From 44291e0aed4408d71f7172871165086a7fc8fbd3 Mon Sep 17 00:00:00 2001 From: Joe Horner Date: Thu, 5 Dec 2019 19:20:51 +0000 Subject: [PATCH 1/3] Resource aws_ec2_transit_gateway_peering_attachment Create and read functions implemented to interact with the AWS GO SDK for transit gateway peering attachments. waitFor functions added for Create/Read terraform operations. Describe function added for transit gateway peering attachments. --- aws/ec2_transit_gateway.go | 74 ++++++++++ ..._ec2_transit_gateway_peering_attachment.go | 127 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 aws/resource_aws_ec2_transit_gateway_peering_attachment.go diff --git a/aws/ec2_transit_gateway.go b/aws/ec2_transit_gateway.go index 740eccf1f8d..465ebc59333 100644 --- a/aws/ec2_transit_gateway.go +++ b/aws/ec2_transit_gateway.go @@ -211,6 +211,43 @@ func ec2DescribeTransitGatewayRouteTablePropagation(conn *ec2.EC2, transitGatewa return output.TransitGatewayRouteTablePropagations[0], nil } +func ec2DescribeTransitGatewayPeeringAttachment(conn *ec2.EC2, transitGatewayAttachmentID string) (*ec2.TransitGatewayPeeringAttachment, error) { + input := &ec2.DescribeTransitGatewayPeeringAttachmentsInput{ + TransitGatewayAttachmentIds: []*string{aws.String(transitGatewayAttachmentID)}, + } + + log.Printf("[DEBUG] Reading EC2 Transit Gateway Peering Attachment (%s): %s", transitGatewayAttachmentID, input) + for { + output, err := conn.DescribeTransitGatewayPeeringAttachments(input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.TransitGatewayPeeringAttachments) == 0 { + return nil, nil + } + + for _, transitGatewayPeeringAttachment := range output.TransitGatewayPeeringAttachments { + if transitGatewayPeeringAttachment == nil { + continue + } + + if aws.StringValue(transitGatewayPeeringAttachment.TransitGatewayAttachmentId) == transitGatewayAttachmentID { + return transitGatewayPeeringAttachment, nil + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + + input.NextToken = output.NextToken + } + + return nil, nil +} + func ec2DescribeTransitGatewayVpcAttachment(conn *ec2.EC2, transitGatewayAttachmentID string) (*ec2.TransitGatewayVpcAttachment, error) { input := &ec2.DescribeTransitGatewayVpcAttachmentsInput{ TransitGatewayAttachmentIds: []*string{aws.String(transitGatewayAttachmentID)}, @@ -541,6 +578,43 @@ func waitForEc2TransitGatewayRouteTableAssociationDeletion(conn *ec2.EC2, transi return err } +func waitForEc2TransitGatewayPeeringAttachmentAcceptance(conn *ec2.EC2, transitGatewayAttachmentID string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + ec2.TransitGatewayAttachmentStatePending, + ec2.TransitGatewayAttachmentStatePendingAcceptance, + }, + Target: []string{ec2.TransitGatewayAttachmentStateAvailable}, + Refresh: ec2TransitGatewayVpcAttachmentRefreshFunc(conn, transitGatewayAttachmentID), + Timeout: 10 * time.Minute, + } + + log.Printf("[DEBUG] Waiting for EC2 Transit Gateway Peering Attachment (%s) availability", transitGatewayAttachmentID) + _, err := stateConf.WaitForState() + + return err +} + +func waitForEc2TransitGatewayPeeringAttachmentCreation(conn *ec2.EC2, transitGatewayAttachmentID string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + ec2.TransitGatewayAttachmentStatePending, + "initiatingRequest", + }, + Target: []string{ + ec2.TransitGatewayAttachmentStatePendingAcceptance, + ec2.TransitGatewayAttachmentStateAvailable, + }, + Refresh: ec2TransitGatewayPeeringAttachmentRefreshFunc(conn, transitGatewayAttachmentID), + Timeout: 10 * time.Minute, + } + + log.Printf("[DEBUG] Waiting for EC2 Transit Gateway Peering Attachment (%s) availability", transitGatewayAttachmentID) + _, err := stateConf.WaitForState() + + return err +} + func waitForEc2TransitGatewayVpcAttachmentAcceptance(conn *ec2.EC2, transitGatewayAttachmentID string) error { stateConf := &resource.StateChangeConf{ Pending: []string{ diff --git a/aws/resource_aws_ec2_transit_gateway_peering_attachment.go b/aws/resource_aws_ec2_transit_gateway_peering_attachment.go new file mode 100644 index 00000000000..d543bcf2051 --- /dev/null +++ b/aws/resource_aws_ec2_transit_gateway_peering_attachment.go @@ -0,0 +1,127 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceAwsEc2TransitGatewayPeeringAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEc2TransitGatewayPeeringAttachmentCreate, + Read: resourceAwsEc2TransitGatewayPeeringAttachmentRead, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "peer_account_id": { + Type: schema.TypeString, + Required: true, + }, + "peer_region": { + Type: schema.TypeString, + Required: true, + }, + "peer_transit_gateway_id": { + Type: schema.TypeString, + Required: true, + }, + "tags": tagsSchema(), + "transit_gateway_id": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsEc2TransitGatewayPeeringAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + transitGatewayID := d.Get("transit_gateway_id").(string) + + input := &ec2.CreateTransitGatewayPeeringAttachmentInput{ + PeerAccountId: aws.String(d.Get("peer_account_id").(string)), + PeerRegion: aws.String(d.Get("peer_region").(string)), + PeerTransitGatewayId: aws.String(d.Get("peer_transit_gateway_id").(string)), + TagSpecifications: expandEc2TransitGatewayAttachmentTagSpecifications(d.Get("tags").(map[string]interface{})), + TransitGatewayId: aws.String(transitGatewayID), + } + + log.Printf("[DEBUG] Creating EC2 Transit Gateway Peering Attachment: %s", input) + output, err := conn.CreateTransitGatewayPeeringAttachment(input) + if err != nil { + return fmt.Errorf("error creating EC2 Transit Gateway Peering Attachment: %s", err) + } + + d.SetId(aws.StringValue(output.TransitGatewayPeeringAttachment.TransitGatewayAttachmentId)) + + if err := waitForEc2TransitGatewayPeeringAttachmentCreation(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for EC2 Transit Gateway Peering Attachment (%s) availability: %s", d.Id(), err) + } + + transitGateway, err := ec2DescribeTransitGateway(conn, transitGatewayID) + if err != nil { + return fmt.Errorf("error describing EC2 Transit Gateway (%s): %s", transitGatewayID, err) + } + + if transitGateway.Options == nil { + return fmt.Errorf("error describing EC2 Transit Gateway (%s): missing options", transitGatewayID) + } + + return resourceAwsEc2TransitGatewayPeeringAttachmentRead(d, meta) +} + +func resourceAwsEc2TransitGatewayPeeringAttachmentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + transitGatewayPeeringAttachment, err := ec2DescribeTransitGatewayPeeringAttachment(conn, d.Id()) + + if isAWSErr(err, "InvalidTransitGatewayAttachmentID.NotFound", "") { + log.Printf("[WARN] EC2 Transit Gateway Peering Attachment (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EC2 Transit Gateway Peering Attachment: %s", err) + } + + if transitGatewayPeeringAttachment == nil { + log.Printf("[WARN] EC2 Transit Gateway Peering Attachment (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if aws.StringValue(transitGatewayPeeringAttachment.State) == ec2.TransitGatewayAttachmentStateDeleting || aws.StringValue(transitGatewayPeeringAttachment.State) == ec2.TransitGatewayAttachmentStateDeleted { + log.Printf("[WARN] EC2 Transit Gateway Peering Attachment (%s) in deleted state (%s), removing from state", d.Id(), aws.StringValue(transitGatewayPeeringAttachment.State)) + d.SetId("") + return nil + } + + transitGatewayID := aws.StringValue(transitGatewayPeeringAttachment.RequesterTgwInfo.TransitGatewayId) + transitGateway, err := ec2DescribeTransitGateway(conn, transitGatewayID) + if err != nil { + return fmt.Errorf("error describing EC2 Transit Gateway (%s): %s", transitGatewayID, err) + } + + if transitGateway.Options == nil { + return fmt.Errorf("error describing EC2 Transit Gateway (%s): missing options", transitGatewayID) + } + + if err := d.Set("tags", tagsToMap(transitGatewayPeeringAttachment.Tags)); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + d.Set("peer_account_id", (transitGatewayPeeringAttachment.AccepterTgwInfo.OwnerId != nil)) + d.Set("peer_region", (transitGatewayPeeringAttachment.AccepterTgwInfo.Region != nil)) + d.Set("peer_transit_gateway_id", (transitGatewayPeeringAttachment.AccepterTgwInfo.TransitGatewayId != nil)) + d.Set("tags", (transitGatewayPeeringAttachment.Tags)) + d.Set("transit_gateway_id", (transitGatewayPeeringAttachment.RequesterTgwInfo.TransitGatewayId)) + + return nil +} From 6b2b24a3854d9b996ef5f83f95c0a3c257fd584f Mon Sep 17 00:00:00 2001 From: Omarimcblack Date: Thu, 5 Dec 2019 19:37:02 +0000 Subject: [PATCH 2/3] Resource aws_ec2_transit_gateway_peering_attachment Create Update/Delete/refresh functions implemented to interact with the AWS GO SDK for transit gateway peering attachments waitFor functions added for Update/Delete terraform operations. Add provider resource mapping to provider.go --- aws/ec2_transit_gateway.go | 56 ++++++++++++++++++ aws/provider.go | 1 + ..._ec2_transit_gateway_peering_attachment.go | 57 +++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/aws/ec2_transit_gateway.go b/aws/ec2_transit_gateway.go index 465ebc59333..6fe91c3ce52 100644 --- a/aws/ec2_transit_gateway.go +++ b/aws/ec2_transit_gateway.go @@ -411,6 +411,26 @@ func ec2TransitGatewayRouteTableAssociationRefreshFunc(conn *ec2.EC2, transitGat } } +func ec2TransitGatewayPeeringAttachmentRefreshFunc(conn *ec2.EC2, transitGatewayAttachmentID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + transitGatewayPeeringAttachment, err := ec2DescribeTransitGatewayPeeringAttachment(conn, transitGatewayAttachmentID) + + if isAWSErr(err, "InvalidTransitGatewayAttachmentID.NotFound", "") { + return nil, ec2.TransitGatewayAttachmentStateDeleted, nil + } + + if err != nil { + return nil, "", fmt.Errorf("error reading EC2 Transit Gateway Peering Attachment (%s): %s", transitGatewayAttachmentID, err) + } + + if transitGatewayPeeringAttachment == nil { + return nil, ec2.TransitGatewayAttachmentStateDeleted, nil + } + + return transitGatewayPeeringAttachment, aws.StringValue(transitGatewayPeeringAttachment.State), nil + } +} + func ec2TransitGatewayVpcAttachmentRefreshFunc(conn *ec2.EC2, transitGatewayAttachmentID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { transitGatewayVpcAttachment, err := ec2DescribeTransitGatewayVpcAttachment(conn, transitGatewayAttachmentID) @@ -615,6 +635,42 @@ func waitForEc2TransitGatewayPeeringAttachmentCreation(conn *ec2.EC2, transitGat return err } +func waitForEc2TransitGatewayPeeringAttachmentDeletion(conn *ec2.EC2, transitGatewayAttachmentID string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + ec2.TransitGatewayAttachmentStateAvailable, + ec2.TransitGatewayAttachmentStateDeleting, + }, + Target: []string{ec2.TransitGatewayAttachmentStateDeleted}, + Refresh: ec2TransitGatewayVpcAttachmentRefreshFunc(conn, transitGatewayAttachmentID), + Timeout: 10 * time.Minute, + NotFoundChecks: 1, + } + + log.Printf("[DEBUG] Waiting for EC2 Transit Gateway Peering Attachment (%s) deletion", transitGatewayAttachmentID) + _, err := stateConf.WaitForState() + + if isResourceNotFoundError(err) { + return nil + } + + return err +} + +func waitForEc2TransitGatewayPeeringAttachmentUpdate(conn *ec2.EC2, transitGatewayAttachmentID string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.TransitGatewayAttachmentStateModifying}, + Target: []string{ec2.TransitGatewayAttachmentStateAvailable}, + Refresh: ec2TransitGatewayVpcAttachmentRefreshFunc(conn, transitGatewayAttachmentID), + Timeout: 10 * time.Minute, + } + + log.Printf("[DEBUG] Waiting for EC2 Transit Gateway Peering Attachment (%s) availability", transitGatewayAttachmentID) + _, err := stateConf.WaitForState() + + return err +} + func waitForEc2TransitGatewayVpcAttachmentAcceptance(conn *ec2.EC2, transitGatewayAttachmentID string) error { stateConf := &resource.StateChangeConf{ Pending: []string{ diff --git a/aws/provider.go b/aws/provider.go index ea4bc1a74bc..d8c6687f134 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -484,6 +484,7 @@ func Provider() terraform.ResourceProvider { "aws_ec2_client_vpn_network_association": resourceAwsEc2ClientVpnNetworkAssociation(), "aws_ec2_fleet": resourceAwsEc2Fleet(), "aws_ec2_transit_gateway": resourceAwsEc2TransitGateway(), + "aws_ec2_transit_gateway_peering_attachment": resourceAwsEc2TransitGatewayPeeringAttachment(), "aws_ec2_transit_gateway_route": resourceAwsEc2TransitGatewayRoute(), "aws_ec2_transit_gateway_route_table": resourceAwsEc2TransitGatewayRouteTable(), "aws_ec2_transit_gateway_route_table_association": resourceAwsEc2TransitGatewayRouteTableAssociation(), diff --git a/aws/resource_aws_ec2_transit_gateway_peering_attachment.go b/aws/resource_aws_ec2_transit_gateway_peering_attachment.go index d543bcf2051..b9b14aa94df 100644 --- a/aws/resource_aws_ec2_transit_gateway_peering_attachment.go +++ b/aws/resource_aws_ec2_transit_gateway_peering_attachment.go @@ -13,6 +13,8 @@ func resourceAwsEc2TransitGatewayPeeringAttachment() *schema.Resource { return &schema.Resource{ Create: resourceAwsEc2TransitGatewayPeeringAttachmentCreate, Read: resourceAwsEc2TransitGatewayPeeringAttachmentRead, + Update: resourceAwsEc2TransitGatewayPeeringAttachmentUpdate, + Delete: resourceAwsEc2TransitGatewayPeeringAttachmentDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -125,3 +127,58 @@ func resourceAwsEc2TransitGatewayPeeringAttachmentRead(d *schema.ResourceData, m return nil } + +func resourceAwsEc2TransitGatewayPeeringAttachmentUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + if d.HasChange("peer_account_id") || d.HasChange("peer_region") || d.HasChange("peer_transit_gateway_id") || d.HasChange("transit_gateway_id") { + transitGatewayID := d.Get("transit_gateway_id").(string) + + transitGateway, err := ec2DescribeTransitGateway(conn, transitGatewayID) + if err != nil { + return fmt.Errorf("error describing EC2 Transit Gateway (%s): %s", transitGatewayID, err) + } + + if transitGateway.Options == nil { + return fmt.Errorf("error describing EC2 Transit Gateway (%s): missing options", transitGatewayID) + } + + if err := waitForEc2TransitGatewayPeeringAttachmentUpdate(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for EC2 Transit Gateway Peering Attachment (%s) update: %s", d.Id(), err) + } + + } + + if d.HasChange("tags") { + if err := setTags(conn, d); err != nil { + return fmt.Errorf("error updating EC2 Transit Gateway Peering Attachment (%s) tags: %s", d.Id(), err) + } + } + + return nil +} + +func resourceAwsEc2TransitGatewayPeeringAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + input := &ec2.DeleteTransitGatewayPeeringAttachmentInput{ + TransitGatewayAttachmentId: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Deleting EC2 Transit Gateway Peering Attachment (%s): %s", d.Id(), input) + _, err := conn.DeleteTransitGatewayPeeringAttachment(input) + + if isAWSErr(err, "InvalidTransitGatewayAttachmentID.NotFound", "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting EC2 Transit Gateway Peering Attachment: %s", err) + } + + if err := waitForEc2TransitGatewayPeeringAttachmentDeletion(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for EC2 Transit Gateway Peering Attachment (%s) deletion: %s", d.Id(), err) + } + + return nil +} From 54b21e58e6c590b9ffcb266300fdc75047398fa7 Mon Sep 17 00:00:00 2001 From: Joe Horner Date: Fri, 6 Dec 2019 19:49:16 +0000 Subject: [PATCH 3/3] Remove references to tgw peering attachment acceptance Work for transit_gateway_peering_attachment_accepter has now been moved into its own pr #11185. --- aws/ec2_transit_gateway.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/aws/ec2_transit_gateway.go b/aws/ec2_transit_gateway.go index 6fe91c3ce52..636e7b06d7e 100644 --- a/aws/ec2_transit_gateway.go +++ b/aws/ec2_transit_gateway.go @@ -598,23 +598,6 @@ func waitForEc2TransitGatewayRouteTableAssociationDeletion(conn *ec2.EC2, transi return err } -func waitForEc2TransitGatewayPeeringAttachmentAcceptance(conn *ec2.EC2, transitGatewayAttachmentID string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{ - ec2.TransitGatewayAttachmentStatePending, - ec2.TransitGatewayAttachmentStatePendingAcceptance, - }, - Target: []string{ec2.TransitGatewayAttachmentStateAvailable}, - Refresh: ec2TransitGatewayVpcAttachmentRefreshFunc(conn, transitGatewayAttachmentID), - Timeout: 10 * time.Minute, - } - - log.Printf("[DEBUG] Waiting for EC2 Transit Gateway Peering Attachment (%s) availability", transitGatewayAttachmentID) - _, err := stateConf.WaitForState() - - return err -} - func waitForEc2TransitGatewayPeeringAttachmentCreation(conn *ec2.EC2, transitGatewayAttachmentID string) error { stateConf := &resource.StateChangeConf{ Pending: []string{