Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added remove_current_attachment_association attribute to ec2_transit_gateway_route_table_association #31452

Merged
3 changes: 3 additions & 0 deletions .changelog/31452.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/ec2_transit_gateway_route_table_association: Add `replace_existing_association` argument
```
77 changes: 53 additions & 24 deletions internal/service/ec2/transitgateway_route_table_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ func ResourceTransitGatewayRouteTableAssociation() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceTransitGatewayRouteTableAssociationCreate,
ReadWithoutTimeout: resourceTransitGatewayRouteTableAssociationRead,
UpdateWithoutTimeout: schema.NoopContext,
DeleteWithoutTimeout: resourceTransitGatewayRouteTableAssociationDelete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"replace_existing_association": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"resource_id": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -60,6 +66,28 @@ func resourceTransitGatewayRouteTableAssociationCreate(ctx context.Context, d *s
transitGatewayAttachmentID := d.Get("transit_gateway_attachment_id").(string)
transitGatewayRouteTableID := d.Get("transit_gateway_route_table_id").(string)
id := TransitGatewayRouteTableAssociationCreateResourceID(transitGatewayRouteTableID, transitGatewayAttachmentID)

if d.Get("replace_existing_association").(bool) {
transitGatewayAttachment, err := FindTransitGatewayAttachmentByID(ctx, conn, transitGatewayAttachmentID)

if err != nil {
return sdkdiag.AppendFromErr(diags, err)
}

// If no Association object was found then Gateway Attachment is not linked to a Route Table.
if transitGatewayAttachment.Association != nil {
transitGatewayRouteTableID := aws.StringValue(transitGatewayAttachment.Association.TransitGatewayRouteTableId)

if state := aws.StringValue(transitGatewayAttachment.Association.State); state != ec2.AssociationStatusCodeAssociated {
return sdkdiag.AppendErrorf(diags, "existing EC2 Transit Gateway Route Table (%s) Association (%s) in unexpected state: %s", transitGatewayRouteTableID, transitGatewayAttachmentID, state)
}

if err := disassociateTransitGatewayRouteTable(ctx, conn, transitGatewayRouteTableID, transitGatewayAttachmentID); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
}
}

input := &ec2.AssociateTransitGatewayRouteTableInput{
TransitGatewayAttachmentId: aws.String(transitGatewayAttachmentID),
TransitGatewayRouteTableId: aws.String(transitGatewayRouteTableID),
Expand Down Expand Up @@ -121,21 +149,8 @@ func resourceTransitGatewayRouteTableAssociationDelete(ctx context.Context, d *s
}

log.Printf("[DEBUG] Deleting EC2 Transit Gateway Route Table Association: %s", d.Id())
_, err = conn.DisassociateTransitGatewayRouteTableWithContext(ctx, &ec2.DisassociateTransitGatewayRouteTableInput{
TransitGatewayAttachmentId: aws.String(transitGatewayAttachmentID),
TransitGatewayRouteTableId: aws.String(transitGatewayRouteTableID),
})

if tfawserr.ErrCodeEquals(err, errCodeInvalidRouteTableIDNotFound) {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting EC2 Transit Gateway Route Table Association (%s): %s", d.Id(), err)
}

if _, err := WaitTransitGatewayRouteTableAssociationDeleted(ctx, conn, transitGatewayRouteTableID, transitGatewayAttachmentID); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for EC2 Transit Gateway Route Table Association (%s) delete: %s", d.Id(), err)
if err := disassociateTransitGatewayRouteTable(ctx, conn, transitGatewayRouteTableID, transitGatewayAttachmentID); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}

return diags
Expand Down Expand Up @@ -183,18 +198,32 @@ func transitGatewayRouteTableAssociationUpdate(ctx context.Context, conn *ec2.EC
return fmt.Errorf("waiting for EC2 Transit Gateway Route Table Association (%s) create: %w", id, err)
}

input := &ec2.DisassociateTransitGatewayRouteTableInput{
TransitGatewayAttachmentId: aws.String(transitGatewayAttachmentID),
TransitGatewayRouteTableId: aws.String(transitGatewayRouteTableID),
if err := disassociateTransitGatewayRouteTable(ctx, conn, transitGatewayRouteTableID, transitGatewayAttachmentID); err != nil {
return err
}
}

if _, err := conn.DisassociateTransitGatewayRouteTableWithContext(ctx, input); err != nil {
return fmt.Errorf("deleting EC2 Transit Gateway Route Table Association (%s): %w", id, err)
}
return nil
}

if _, err := WaitTransitGatewayRouteTableAssociationDeleted(ctx, conn, transitGatewayRouteTableID, transitGatewayAttachmentID); err != nil {
return fmt.Errorf("waiting for EC2 Transit Gateway Route Table Association (%s) delete: %w", id, err)
}
func disassociateTransitGatewayRouteTable(ctx context.Context, conn *ec2.EC2, transitGatewayRouteTableID, transitGatewayAttachmentID string) error {
input := &ec2.DisassociateTransitGatewayRouteTableInput{
TransitGatewayAttachmentId: aws.String(transitGatewayAttachmentID),
TransitGatewayRouteTableId: aws.String(transitGatewayRouteTableID),
}

_, err := conn.DisassociateTransitGatewayRouteTableWithContext(ctx, input)

if tfawserr.ErrCodeEquals(err, errCodeInvalidRouteTableIDNotFound) {
return nil
}

if err != nil {
return fmt.Errorf("deleting EC2 Transit Gateway Route Table (%s) Association (%s): %w", transitGatewayRouteTableID, transitGatewayAttachmentID, err)
}

if _, err := WaitTransitGatewayRouteTableAssociationDeleted(ctx, conn, transitGatewayRouteTableID, transitGatewayAttachmentID); err != nil {
return fmt.Errorf("waiting for EC2 Transit Gateway Route Table (%s) Association (%s) delete: %w", transitGatewayRouteTableID, transitGatewayAttachmentID, err)
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ func testAccTransitGatewayRouteTableAssociation_basic(t *testing.T) {
Config: testAccTransitGatewayRouteTableAssociationConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckTransitGatewayRouteTableAssociationExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, "replace_existing_association", "false"),
resource.TestCheckResourceAttrSet(resourceName, "resource_id"),
resource.TestCheckResourceAttrSet(resourceName, "resource_type"),
resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_attachment_id", transitGatewayVpcAttachmentResourceName, "id"),
resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_route_table_id", transitGatewayRouteTableResourceName, "id"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"replace_existing_association"},
},
},
})
Expand Down Expand Up @@ -72,6 +74,43 @@ func testAccTransitGatewayRouteTableAssociation_disappears(t *testing.T) {
})
}

func testAccTransitGatewayRouteTableAssociation_replaceExistingAssociation(t *testing.T) {
ctx := acctest.Context(t)
var v ec2.TransitGatewayRouteTableAssociation
resourceName := "aws_ec2_transit_gateway_route_table_association.test"
transitGatewayRouteTableResourceName := "aws_ec2_transit_gateway_route_table.test"
transitGatewayVpcAttachmentResourceName := "aws_ec2_transit_gateway_vpc_attachment.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckTransitGateway(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckTransitGatewayRouteTableAssociationDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccTransitGatewayRouteTableAssociationConfig_replaceExistingAssociation(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckTransitGatewayRouteTableAssociationExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, "replace_existing_association", "true"),
resource.TestCheckResourceAttrSet(resourceName, "resource_id"),
resource.TestCheckResourceAttrSet(resourceName, "resource_type"),
resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_attachment_id", transitGatewayVpcAttachmentResourceName, "id"),
resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_route_table_id", transitGatewayRouteTableResourceName, "id"),
),
// aws_ec2_transit_gateway_vpc_attachment.test.transit_gateway_default_route_table_association shows diff:
ExpectNonEmptyPlan: true,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"replace_existing_association"},
},
},
})
}

func testAccCheckTransitGatewayRouteTableAssociationExists(ctx context.Context, n string, v *ec2.TransitGatewayRouteTableAssociation) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -136,33 +175,50 @@ func testAccCheckTransitGatewayRouteTableAssociationDestroy(ctx context.Context)
}

func testAccTransitGatewayRouteTableAssociationConfig_basic(rName string) string {
return fmt.Sprintf(`
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(`
resource "aws_ec2_transit_gateway" "test" {
tags = {
Name = %[1]q
}
}

resource "aws_ec2_transit_gateway_vpc_attachment" "test" {
subnet_ids = aws_subnet.test[*].id
transit_gateway_default_route_table_association = false
transit_gateway_id = aws_ec2_transit_gateway.test.id
vpc_id = aws_vpc.test.id

tags = {
Name = %[1]q
}
}

resource "aws_subnet" "test" {
cidr_block = "10.0.0.0/24"
vpc_id = aws_vpc.test.id
resource "aws_ec2_transit_gateway_route_table" "test" {
transit_gateway_id = aws_ec2_transit_gateway.test.id

tags = {
Name = %[1]q
}
}

resource "aws_ec2_transit_gateway_route_table_association" "test" {
transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.test.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.test.id
}
`, rName))
}

func testAccTransitGatewayRouteTableAssociationConfig_replaceExistingAssociation(rName string) string {
return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(`
resource "aws_ec2_transit_gateway" "test" {
tags = {
Name = %[1]q
}
}

resource "aws_ec2_transit_gateway_vpc_attachment" "test" {
subnet_ids = [aws_subnet.test.id]
transit_gateway_default_route_table_association = false
subnet_ids = aws_subnet.test[*].id
transit_gateway_default_route_table_association = true
transit_gateway_id = aws_ec2_transit_gateway.test.id
vpc_id = aws_vpc.test.id

Expand All @@ -182,6 +238,8 @@ resource "aws_ec2_transit_gateway_route_table" "test" {
resource "aws_ec2_transit_gateway_route_table_association" "test" {
transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.test.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.test.id

replace_existing_association = true
}
`, rName)
`, rName))
}
5 changes: 3 additions & 2 deletions internal/service/ec2/transitgateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ func TestAccTransitGateway_serial(t *testing.T) {
"tags": testAccTransitGatewayRouteTable_tags,
},
"RouteTableAssociation": {
"basic": testAccTransitGatewayRouteTableAssociation_basic,
"disappears": testAccTransitGatewayRouteTableAssociation_disappears,
"basic": testAccTransitGatewayRouteTableAssociation_basic,
"disappears": testAccTransitGatewayRouteTableAssociation_disappears,
"ReplaceExistingAssociation": testAccTransitGatewayRouteTableAssociation_replaceExistingAssociation,
},
"RouteTablePropagation": {
"basic": testAccTransitGatewayRouteTablePropagation_basic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The following arguments are supported:

* `transit_gateway_attachment_id` - (Required) Identifier of EC2 Transit Gateway Attachment.
* `transit_gateway_route_table_id` - (Required) Identifier of EC2 Transit Gateway Route Table.
* `replace_existing_association` - (Optional) Boolean whether the Gateway Attachment should remove any current Route Table association before associating with the specified Route Table. Default value: `false`. This argument is intended for use with EC2 Transit Gateways shared into the current account, otherwise the `transit_gateway_default_route_table_association` argument of the `aws_ec2_transit_gateway_vpc_attachment` resource should be used.

## Attributes Reference

Expand Down