Skip to content

Commit c20eb9b

Browse files
authored
Merge pull request #39507 from tcarreira/f-aws_globalaccelerator_endpoint_group-add-attachment_arn
r/aws_globalaccelerator_endpoint_group: add `attachment_arn` to `endpoint_configuration`
2 parents fc016af + 98aa21e commit c20eb9b

File tree

5 files changed

+263
-6
lines changed

5 files changed

+263
-6
lines changed

.changelog/39507.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
resource/aws_globalaccelerator_endpoint_group: Add `endpoint_configuration.attachment_arn` argument
3+
```

internal/service/globalaccelerator/endpoint_group.go

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/aws/aws-sdk-go-v2/aws"
12+
"github.com/aws/aws-sdk-go-v2/aws/arn"
1213
"github.com/aws/aws-sdk-go-v2/service/globalaccelerator"
1314
awstypes "github.com/aws/aws-sdk-go-v2/service/globalaccelerator/types"
1415
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -53,6 +54,11 @@ func resourceEndpointGroup() *schema.Resource {
5354
Optional: true,
5455
Elem: &schema.Resource{
5556
Schema: map[string]*schema.Schema{
57+
"attachment_arn": {
58+
Type: schema.TypeString,
59+
Optional: true,
60+
ValidateFunc: verify.ValidARN,
61+
},
5662
"client_ip_preservation_enabled": {
5763
Type: schema.TypeBool,
5864
Optional: true,
@@ -230,8 +236,13 @@ func resourceEndpointGroupRead(ctx context.Context, d *schema.ResourceData, meta
230236
return sdkdiag.AppendFromErr(diags, err)
231237
}
232238

239+
crossAccountAttachments, err := findCrossAccountAttachments(ctx, conn, endpointGroup.EndpointDescriptions)
240+
if err != nil {
241+
return sdkdiag.AppendErrorf(diags, "reading Global Accelerator Endpoint Group (%s) cross-account attachments: %s", d.Id(), err)
242+
}
243+
233244
d.Set(names.AttrARN, endpointGroup.EndpointGroupArn)
234-
if err := d.Set("endpoint_configuration", flattenEndpointDescriptions(endpointGroup.EndpointDescriptions)); err != nil {
245+
if err := d.Set("endpoint_configuration", flattenEndpointDescriptions(endpointGroup.EndpointDescriptions, crossAccountAttachments)); err != nil {
235246
return sdkdiag.AppendErrorf(diags, "setting endpoint_configuration: %s", err)
236247
}
237248
d.Set("endpoint_group_region", endpointGroup.EndpointGroupRegion)
@@ -372,6 +383,10 @@ func expandEndpointConfiguration(tfMap map[string]interface{}) *awstypes.Endpoin
372383

373384
apiObject := &awstypes.EndpointConfiguration{}
374385

386+
if v, ok := tfMap["attachment_arn"].(string); ok && v != "" {
387+
apiObject.AttachmentArn = aws.String(v)
388+
}
389+
375390
if v, ok := tfMap["client_ip_preservation_enabled"].(bool); ok {
376391
apiObject.ClientIPPreservationEnabled = aws.Bool(v)
377392
}
@@ -457,7 +472,7 @@ func expandPortOverrides(tfList []interface{}) []awstypes.PortOverride {
457472
return apiObjects
458473
}
459474

460-
func flattenEndpointDescription(apiObject *awstypes.EndpointDescription) map[string]interface{} {
475+
func flattenEndpointDescription(apiObject *awstypes.EndpointDescription, crossAccountAttachments map[string]string) map[string]interface{} {
461476
if apiObject == nil {
462477
return nil
463478
}
@@ -469,7 +484,12 @@ func flattenEndpointDescription(apiObject *awstypes.EndpointDescription) map[str
469484
}
470485

471486
if v := apiObject.EndpointId; v != nil {
472-
tfMap["endpoint_id"] = aws.ToString(v)
487+
v := aws.ToString(v)
488+
tfMap["endpoint_id"] = v
489+
490+
if v, ok := crossAccountAttachments[v]; ok {
491+
tfMap["attachment_arn"] = v
492+
}
473493
}
474494

475495
if v := apiObject.Weight; v != nil {
@@ -479,15 +499,15 @@ func flattenEndpointDescription(apiObject *awstypes.EndpointDescription) map[str
479499
return tfMap
480500
}
481501

482-
func flattenEndpointDescriptions(apiObjects []awstypes.EndpointDescription) []interface{} {
502+
func flattenEndpointDescriptions(apiObjects []awstypes.EndpointDescription, crossAccountAttachments map[string]string) []interface{} {
483503
if len(apiObjects) == 0 {
484504
return nil
485505
}
486506

487507
var tfList []interface{}
488508

489509
for _, apiObject := range apiObjects {
490-
tfList = append(tfList, flattenEndpointDescription(&apiObject))
510+
tfList = append(tfList, flattenEndpointDescription(&apiObject, crossAccountAttachments))
491511
}
492512

493513
return tfList
@@ -524,3 +544,52 @@ func flattenPortOverrides(apiObjects []awstypes.PortOverride) []interface{} {
524544

525545
return tfList
526546
}
547+
548+
func findCrossAccountAttachments(ctx context.Context, conn *globalaccelerator.Client, endpointDescriptions []awstypes.EndpointDescription) (map[string]string, error) {
549+
crossAccountAttachments := map[string]string{}
550+
551+
accounts := map[string]bool{}
552+
for _, endpointDescription := range endpointDescriptions {
553+
arn, err := arn.Parse(aws.ToString(endpointDescription.EndpointId))
554+
if err != nil {
555+
continue // Not an ARN => not a cross-account resource.
556+
}
557+
558+
accountID := arn.AccountID
559+
if accounts[accountID] {
560+
continue
561+
}
562+
accounts[accountID] = true
563+
564+
input := &globalaccelerator.ListCrossAccountResourcesInput{
565+
ResourceOwnerAwsAccountId: aws.String(accountID),
566+
}
567+
crossAccountResources, err := findCrossAccountResources(ctx, conn, input)
568+
if err != nil {
569+
return nil, err
570+
}
571+
572+
for _, crossAccountResource := range crossAccountResources {
573+
crossAccountAttachments[aws.ToString(crossAccountResource.EndpointId)] = aws.ToString(crossAccountResource.AttachmentArn)
574+
}
575+
}
576+
577+
return crossAccountAttachments, nil
578+
}
579+
580+
func findCrossAccountResources(ctx context.Context, conn *globalaccelerator.Client, input *globalaccelerator.ListCrossAccountResourcesInput) ([]awstypes.CrossAccountResource, error) {
581+
var output []awstypes.CrossAccountResource
582+
583+
pages := globalaccelerator.NewListCrossAccountResourcesPaginator(conn, input)
584+
for pages.HasMorePages() {
585+
page, err := pages.NextPage(ctx)
586+
587+
if err != nil {
588+
return nil, err
589+
}
590+
591+
output = append(output, page.CrossAccountResources...)
592+
}
593+
594+
return output, nil
595+
}

internal/service/globalaccelerator/endpoint_group_test.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func TestAccGlobalAcceleratorEndpointGroup_ALBEndpoint_clientIP(t *testing.T) {
107107
acctest.MatchResourceAttrGlobalARN(resourceName, names.AttrARN, "globalaccelerator", regexache.MustCompile(`accelerator/[^/]+/listener/[^/]+/endpoint-group/[^/]+`)),
108108
resource.TestCheckResourceAttr(resourceName, "endpoint_configuration.#", acctest.Ct1),
109109
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "endpoint_configuration.*", map[string]string{
110+
"attachment_arn": "",
110111
"client_ip_preservation_enabled": acctest.CtFalse,
111112
names.AttrWeight: "20",
112113
}),
@@ -429,6 +430,47 @@ func TestAccGlobalAcceleratorEndpointGroup_update(t *testing.T) {
429430
})
430431
}
431432

433+
func TestAccGlobalAcceleratorEndpointGroup_crossAccountAttachment(t *testing.T) {
434+
ctx := acctest.Context(t)
435+
var v awstypes.EndpointGroup
436+
resourceName := "aws_globalaccelerator_endpoint_group.test"
437+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
438+
439+
resource.ParallelTest(t, resource.TestCase{
440+
PreCheck: func() {
441+
acctest.PreCheck(ctx, t)
442+
testAccPreCheck(ctx, t)
443+
acctest.PreCheckMultipleRegion(t, 2)
444+
acctest.PreCheckAlternateAccount(t)
445+
},
446+
ErrorCheck: acctest.ErrorCheck(t, names.GlobalAcceleratorServiceID),
447+
ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t),
448+
CheckDestroy: testAccCheckEndpointGroupDestroy(ctx),
449+
Steps: []resource.TestStep{
450+
{
451+
Config: testAccEndpointGroupConfig_crossAccountAttachement(rName),
452+
Check: resource.ComposeTestCheckFunc(
453+
testAccCheckEndpointGroupExists(ctx, resourceName, &v),
454+
acctest.MatchResourceAttrGlobalARN(resourceName, names.AttrARN, "globalaccelerator", regexache.MustCompile(`accelerator/[^/]+/listener/[^/]+/endpoint-group/[^/]+`)),
455+
resource.TestCheckResourceAttr(resourceName, "endpoint_configuration.#", acctest.Ct1),
456+
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "endpoint_configuration.*", map[string]string{
457+
"client_ip_preservation_enabled": acctest.CtFalse,
458+
names.AttrWeight: "20",
459+
}),
460+
resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_configuration.*.attachment_arn", "aws_globalaccelerator_cross_account_attachment.alt_test", names.AttrARN),
461+
resource.TestCheckTypeSetElemAttrPair(resourceName, "endpoint_configuration.*.endpoint_id", "aws_lb.alt_test", names.AttrARN),
462+
resource.TestCheckResourceAttr(resourceName, "endpoint_group_region", acctest.AlternateRegion()),
463+
),
464+
},
465+
{
466+
ResourceName: resourceName,
467+
ImportState: true,
468+
ImportStateVerify: true,
469+
},
470+
},
471+
})
472+
}
473+
432474
func testAccCheckEndpointGroupExists(ctx context.Context, name string, v *awstypes.EndpointGroup) resource.TestCheckFunc {
433475
return func(s *terraform.State) error {
434476
rs, ok := s.RootModule().Resources[name]
@@ -874,3 +916,144 @@ resource "aws_globalaccelerator_endpoint_group" "test" {
874916
}
875917
`, rName)
876918
}
919+
920+
func testAccEndpointGroupConfig_crossAccountAttachement(rName string) string {
921+
return acctest.ConfigCompose(
922+
acctest.ConfigAlternateAccountAlternateRegionProvider(),
923+
fmt.Sprintf(`
924+
###############################################################################
925+
## Alternate account setup.
926+
###############################################################################
927+
data "aws_availability_zones" "alt_available" {
928+
provider = "awsalternate"
929+
930+
exclude_zone_ids = ["usw2-az4", "usgw1-az2"]
931+
state = "available"
932+
933+
filter {
934+
name = "opt-in-status"
935+
values = ["opt-in-not-required"]
936+
}
937+
}
938+
939+
resource "aws_vpc" "alt_test" {
940+
provider = "awsalternate"
941+
942+
cidr_block = "10.0.0.0/16"
943+
944+
tags = {
945+
Name = %[1]q
946+
}
947+
}
948+
949+
resource "aws_subnet" "alt_test" {
950+
provider = "awsalternate"
951+
952+
count = 2
953+
954+
vpc_id = aws_vpc.alt_test.id
955+
availability_zone = data.aws_availability_zones.alt_available.names[count.index]
956+
cidr_block = cidrsubnet(aws_vpc.alt_test.cidr_block, 8, count.index)
957+
958+
tags = {
959+
Name = %[1]q
960+
}
961+
}
962+
963+
resource "aws_lb" "alt_test" {
964+
provider = "awsalternate"
965+
966+
name = %[1]q
967+
internal = false
968+
security_groups = [aws_security_group.alt_test.id]
969+
subnets = aws_subnet.alt_test[*].id
970+
971+
idle_timeout = 30
972+
enable_deletion_protection = false
973+
974+
tags = {
975+
Name = %[1]q
976+
}
977+
}
978+
979+
resource "aws_security_group" "alt_test" {
980+
provider = "awsalternate"
981+
982+
name = %[1]q
983+
vpc_id = aws_vpc.alt_test.id
984+
985+
ingress {
986+
from_port = 0
987+
to_port = 0
988+
protocol = "-1"
989+
cidr_blocks = ["0.0.0.0/0"]
990+
}
991+
992+
egress {
993+
from_port = 0
994+
to_port = 0
995+
protocol = "-1"
996+
cidr_blocks = ["0.0.0.0/0"]
997+
}
998+
999+
tags = {
1000+
Name = %[1]q
1001+
}
1002+
}
1003+
1004+
resource "aws_internet_gateway" "alt_test" {
1005+
provider = "awsalternate"
1006+
1007+
vpc_id = aws_vpc.alt_test.id
1008+
1009+
tags = {
1010+
Name = %[1]q
1011+
}
1012+
}
1013+
1014+
resource "aws_globalaccelerator_cross_account_attachment" "alt_test" {
1015+
provider = "awsalternate"
1016+
1017+
name = %[1]q
1018+
principals = [data.aws_caller_identity.current.account_id]
1019+
1020+
resource {
1021+
endpoint_id = aws_lb.alt_test.arn
1022+
}
1023+
}
1024+
1025+
###############################################################################
1026+
## Main account setup.
1027+
###############################################################################
1028+
data "aws_caller_identity" "current" {}
1029+
1030+
resource "aws_globalaccelerator_accelerator" "test" {
1031+
name = %[1]q
1032+
ip_address_type = "IPV4"
1033+
enabled = false
1034+
}
1035+
1036+
resource "aws_globalaccelerator_listener" "test" {
1037+
accelerator_arn = aws_globalaccelerator_accelerator.test.id
1038+
protocol = "TCP"
1039+
1040+
port_range {
1041+
from_port = 80
1042+
to_port = 80
1043+
}
1044+
}
1045+
1046+
resource "aws_globalaccelerator_endpoint_group" "test" {
1047+
listener_arn = aws_globalaccelerator_listener.test.id
1048+
1049+
endpoint_configuration {
1050+
endpoint_id = aws_lb.alt_test.arn
1051+
attachment_arn = aws_globalaccelerator_cross_account_attachment.alt_test.arn
1052+
weight = 20
1053+
client_ip_preservation_enabled = false
1054+
}
1055+
1056+
endpoint_group_region = %[2]q
1057+
}
1058+
`, rName, acctest.AlternateRegion()))
1059+
}

website/docs/cdktf/python/r/globalaccelerator_endpoint_group.html.markdown

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Terraform will only perform drift detection of its value when present in a confi
5858
**Note:** When client IP address preservation is enabled, the Global Accelerator service creates an EC2 Security Group in the VPC named `GlobalAccelerator` that must be deleted (potentially outside of Terraform) before the VPC will successfully delete. If this EC2 Security Group is not deleted, Terraform will retry the VPC deletion for a few minutes before reporting a `DependencyViolation` error. This cannot be resolved by re-running Terraform.
5959
* `endpoint_id` - (Optional) An ID for the endpoint. If the endpoint is a Network Load Balancer or Application Load Balancer, this is the Amazon Resource Name (ARN) of the resource. If the endpoint is an Elastic IP address, this is the Elastic IP address allocation ID.
6060
* `weight` - (Optional) The weight associated with the endpoint. When you add weights to endpoints, you configure AWS Global Accelerator to route traffic based on proportions that you specify.
61+
* `attachment_arn` - (Optional) An ARN of an exposed cross-account attachment. See the [AWS documentation](https://docs.aws.amazon.com/global-accelerator/latest/dg/cross-account-resources.html) for more details.
6162

6263
`port_override` supports the following arguments:
6364

@@ -104,4 +105,4 @@ Using `terraform import`, import Global Accelerator endpoint groups using the `i
104105
% terraform import aws_globalaccelerator_endpoint_group.example arn:aws:globalaccelerator::111111111111:accelerator/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/listener/xxxxxxx/endpoint-group/xxxxxxxx
105106
```
106107

107-
<!-- cache-key: cdktf-0.20.1 input-a99822bb8d802bdee38671c5875d85175be0aff6331a6b380641cb861eac7402 -->
108+
<!-- cache-key: cdktf-0.20.1 input-a99822bb8d802bdee38671c5875d85175be0aff6331a6b380641cb861eac7402 -->

website/docs/r/globalaccelerator_endpoint_group.html.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Terraform will only perform drift detection of its value when present in a confi
4141

4242
`endpoint_configuration` supports the following arguments:
4343

44+
* `attachment_arn` - (Optional) An ARN of an exposed cross-account attachment. See the [AWS documentation](https://docs.aws.amazon.com/global-accelerator/latest/dg/cross-account-resources.html) for more details.
4445
* `client_ip_preservation_enabled` - (Optional) Indicates whether client IP address preservation is enabled for an Application Load Balancer endpoint. See the [AWS documentation](https://docs.aws.amazon.com/global-accelerator/latest/dg/preserve-client-ip-address.html) for more details. The default value is `false`.
4546
**Note:** When client IP address preservation is enabled, the Global Accelerator service creates an EC2 Security Group in the VPC named `GlobalAccelerator` that must be deleted (potentially outside of Terraform) before the VPC will successfully delete. If this EC2 Security Group is not deleted, Terraform will retry the VPC deletion for a few minutes before reporting a `DependencyViolation` error. This cannot be resolved by re-running Terraform.
4647
* `endpoint_id` - (Optional) An ID for the endpoint. If the endpoint is a Network Load Balancer or Application Load Balancer, this is the Amazon Resource Name (ARN) of the resource. If the endpoint is an Elastic IP address, this is the Elastic IP address allocation ID.

0 commit comments

Comments
 (0)