From 9c45ddd98c2192e11d5bb2a025446ff4cda73c15 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 17 Dec 2020 13:05:40 -0500 Subject: [PATCH] New Resource: aws_ec2_transit_gateway_prefix_list_reference Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16572 Output from acceptance testing in AWS Commercial: ``` --- PASS: TestAccAwsEc2TransitGatewayPrefixListReference_disappears (214.31s) --- PASS: TestAccAwsEc2TransitGatewayPrefixListReference_basic (217.12s) --- PASS: TestAccAwsEc2TransitGatewayPrefixListReference_disappears_TransitGateway (219.77s) --- PASS: TestAccAwsEc2TransitGatewayPrefixListReference_TransitGatewayAttachmentId (397.05s) ``` --- aws/internal/service/ec2/errors.go | 4 + aws/internal/service/ec2/finder/finder.go | 46 ++++ aws/internal/service/ec2/id.go | 19 ++ aws/internal/service/ec2/waiter/status.go | 16 ++ aws/internal/service/ec2/waiter/waiter.go | 60 +++++ aws/provider.go | 1 + ...2_transit_gateway_prefix_list_reference.go | 206 +++++++++++++++ ...nsit_gateway_prefix_list_reference_test.go | 234 ++++++++++++++++++ ...ateway_prefix_list_reference.html.markdown | 59 +++++ 9 files changed, 645 insertions(+) create mode 100644 aws/resource_aws_ec2_transit_gateway_prefix_list_reference.go create mode 100644 aws/resource_aws_ec2_transit_gateway_prefix_list_reference_test.go create mode 100644 website/docs/r/ec2_transit_gateway_prefix_list_reference.html.markdown diff --git a/aws/internal/service/ec2/errors.go b/aws/internal/service/ec2/errors.go index c63b775edc9..5a95520c06b 100644 --- a/aws/internal/service/ec2/errors.go +++ b/aws/internal/service/ec2/errors.go @@ -20,6 +20,10 @@ const ( ErrCodeInvalidPrefixListIDNotFound = "InvalidPrefixListID.NotFound" ) +const ( + ErrCodeInvalidRouteTableIDNotFound = "InvalidRouteTableID.NotFound" +) + const ( ErrCodeClientVpnEndpointIdNotFound = "InvalidClientVpnEndpointId.NotFound" ErrCodeClientVpnAuthorizationRuleNotFound = "InvalidClientVpnEndpointAuthorizationRuleNotFound" diff --git a/aws/internal/service/ec2/finder/finder.go b/aws/internal/service/ec2/finder/finder.go index 10e42d1faca..121a51a5def 100644 --- a/aws/internal/service/ec2/finder/finder.go +++ b/aws/internal/service/ec2/finder/finder.go @@ -1,6 +1,8 @@ package finder import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" @@ -110,6 +112,50 @@ func SubnetByID(conn *ec2.EC2, id string) (*ec2.Subnet, error) { return output.Subnets[0], nil } +func TransitGatewayPrefixListReference(conn *ec2.EC2, transitGatewayRouteTableID string, prefixListID string) (*ec2.TransitGatewayPrefixListReference, error) { + filters := map[string]string{ + "prefix-list-id": prefixListID, + } + + input := &ec2.GetTransitGatewayPrefixListReferencesInput{ + TransitGatewayRouteTableId: aws.String(transitGatewayRouteTableID), + Filters: tfec2.BuildAttributeFilterList(filters), + } + + var result *ec2.TransitGatewayPrefixListReference + + err := conn.GetTransitGatewayPrefixListReferencesPages(input, func(page *ec2.GetTransitGatewayPrefixListReferencesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, transitGatewayPrefixListReference := range page.TransitGatewayPrefixListReferences { + if transitGatewayPrefixListReference == nil { + continue + } + + if aws.StringValue(transitGatewayPrefixListReference.PrefixListId) == prefixListID { + result = transitGatewayPrefixListReference + return false + } + } + + return !lastPage + }) + + return result, err +} + +func TransitGatewayPrefixListReferenceByID(conn *ec2.EC2, resourceID string) (*ec2.TransitGatewayPrefixListReference, error) { + transitGatewayRouteTableID, prefixListID, err := tfec2.TransitGatewayPrefixListReferenceParseID(resourceID) + + if err != nil { + return nil, fmt.Errorf("error parsing EC2 Transit Gateway Prefix List Reference (%s) identifier: %w", resourceID, err) + } + + return TransitGatewayPrefixListReference(conn, transitGatewayRouteTableID, prefixListID) +} + // VpcPeeringConnectionByID returns the VPC peering connection corresponding to the specified identifier. // Returns nil and potentially an error if no VPC peering connection is found. func VpcPeeringConnectionByID(conn *ec2.EC2, id string) (*ec2.VpcPeeringConnection, error) { diff --git a/aws/internal/service/ec2/id.go b/aws/internal/service/ec2/id.go index c3797b4a2ed..3eb2a65c3e1 100644 --- a/aws/internal/service/ec2/id.go +++ b/aws/internal/service/ec2/id.go @@ -71,6 +71,25 @@ func ClientVpnRouteParseID(id string) (string, string, string, error) { "target-subnet-id"+clientVpnRouteIDSeparator+"destination-cidr-block", id) } +const transitGatewayPrefixListReferenceSeparator = "_" + +func TransitGatewayPrefixListReferenceCreateID(transitGatewayRouteTableID string, prefixListID string) string { + parts := []string{transitGatewayRouteTableID, prefixListID} + id := strings.Join(parts, transitGatewayPrefixListReferenceSeparator) + + return id +} + +func TransitGatewayPrefixListReferenceParseID(id string) (string, string, error) { + parts := strings.Split(id, transitGatewayPrefixListReferenceSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected transit-gateway-route-table-id%[2]sprefix-list-id", id, transitGatewayPrefixListReferenceSeparator) +} + func VpnGatewayVpcAttachmentCreateID(vpnGatewayID, vpcID string) string { return fmt.Sprintf("vpn-attachment-%x", hashcode.String(fmt.Sprintf("%s-%s", vpcID, vpnGatewayID))) } diff --git a/aws/internal/service/ec2/waiter/status.go b/aws/internal/service/ec2/waiter/status.go index 74e0c9308f5..9f00de1e99a 100644 --- a/aws/internal/service/ec2/waiter/status.go +++ b/aws/internal/service/ec2/waiter/status.go @@ -280,6 +280,22 @@ func SubnetMapPublicIpOnLaunch(conn *ec2.EC2, id string) resource.StateRefreshFu } } +func TransitGatewayPrefixListReferenceState(conn *ec2.EC2, transitGatewayRouteTableID string, prefixListID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + transitGatewayPrefixListReference, err := finder.TransitGatewayPrefixListReference(conn, transitGatewayRouteTableID, prefixListID) + + if err != nil { + return nil, "", err + } + + if transitGatewayPrefixListReference == nil { + return nil, "", nil + } + + return transitGatewayPrefixListReference, aws.StringValue(transitGatewayPrefixListReference.State), nil + } +} + const ( vpcPeeringConnectionStatusNotFound = "NotFound" vpcPeeringConnectionStatusUnknown = "Unknown" diff --git a/aws/internal/service/ec2/waiter/waiter.go b/aws/internal/service/ec2/waiter/waiter.go index 0b76dd4f980..44a202f01cd 100644 --- a/aws/internal/service/ec2/waiter/waiter.go +++ b/aws/internal/service/ec2/waiter/waiter.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" ) const ( @@ -290,6 +291,65 @@ func SubnetMapPublicIpOnLaunchUpdated(conn *ec2.EC2, subnetID string, expectedVa return nil, err } +const ( + TransitGatewayPrefixListReferenceTimeout = 5 * time.Minute +) + +func TransitGatewayPrefixListReferenceStateCreated(conn *ec2.EC2, transitGatewayRouteTableID string, prefixListID string) (*ec2.TransitGatewayPrefixListReference, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.TransitGatewayPrefixListReferenceStatePending}, + Target: []string{ec2.TransitGatewayPrefixListReferenceStateAvailable}, + Timeout: TransitGatewayPrefixListReferenceTimeout, + Refresh: TransitGatewayPrefixListReferenceState(conn, transitGatewayRouteTableID, prefixListID), + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*ec2.TransitGatewayPrefixListReference); ok { + return output, err + } + + return nil, err +} + +func TransitGatewayPrefixListReferenceStateDeleted(conn *ec2.EC2, transitGatewayRouteTableID string, prefixListID string) (*ec2.TransitGatewayPrefixListReference, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.TransitGatewayPrefixListReferenceStateDeleting}, + Target: []string{}, + Timeout: TransitGatewayPrefixListReferenceTimeout, + Refresh: TransitGatewayPrefixListReferenceState(conn, transitGatewayRouteTableID, prefixListID), + } + + outputRaw, err := stateConf.WaitForState() + + if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidRouteTableIDNotFound) { + return nil, nil + } + + if output, ok := outputRaw.(*ec2.TransitGatewayPrefixListReference); ok { + return output, err + } + + return nil, err +} + +func TransitGatewayPrefixListReferenceStateUpdated(conn *ec2.EC2, transitGatewayRouteTableID string, prefixListID string) (*ec2.TransitGatewayPrefixListReference, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.TransitGatewayPrefixListReferenceStateModifying}, + Target: []string{ec2.TransitGatewayPrefixListReferenceStateAvailable}, + Timeout: TransitGatewayPrefixListReferenceTimeout, + Refresh: TransitGatewayPrefixListReferenceState(conn, transitGatewayRouteTableID, prefixListID), + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*ec2.TransitGatewayPrefixListReference); ok { + return output, err + } + + return nil, err +} + const ( VpnGatewayVpcAttachmentAttachedTimeout = 15 * time.Minute diff --git a/aws/provider.go b/aws/provider.go index 97bb7538de3..9273c1b4fa4 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -623,6 +623,7 @@ func Provider() *schema.Provider { "aws_ec2_transit_gateway": resourceAwsEc2TransitGateway(), "aws_ec2_transit_gateway_peering_attachment": resourceAwsEc2TransitGatewayPeeringAttachment(), "aws_ec2_transit_gateway_peering_attachment_accepter": resourceAwsEc2TransitGatewayPeeringAttachmentAccepter(), + "aws_ec2_transit_gateway_prefix_list_reference": resourceAwsEc2TransitGatewayPrefixListReference(), "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_prefix_list_reference.go b/aws/resource_aws_ec2_transit_gateway_prefix_list_reference.go new file mode 100644 index 00000000000..c53416bea29 --- /dev/null +++ b/aws/resource_aws_ec2_transit_gateway_prefix_list_reference.go @@ -0,0 +1,206 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/waiter" +) + +func resourceAwsEc2TransitGatewayPrefixListReference() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEc2TransitGatewayPrefixListReferenceCreate, + Read: resourceAwsEc2TransitGatewayPrefixListReferenceRead, + Update: resourceAwsEc2TransitGatewayPrefixListReferenceUpdate, + Delete: resourceAwsEc2TransitGatewayPrefixListReferenceDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "blackhole": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "prefix_list_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "prefix_list_owner_id": { + Type: schema.TypeString, + Computed: true, + }, + "transit_gateway_attachment_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.NoZeroValues, + }, + "transit_gateway_route_table_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + }, + } +} + +func resourceAwsEc2TransitGatewayPrefixListReferenceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + input := &ec2.CreateTransitGatewayPrefixListReferenceInput{} + + if v, ok := d.GetOk("blackhole"); ok { + input.Blackhole = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("prefix_list_id"); ok { + input.PrefixListId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("transit_gateway_attachment_id"); ok { + input.TransitGatewayAttachmentId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("transit_gateway_route_table_id"); ok { + input.TransitGatewayRouteTableId = aws.String(v.(string)) + } + + output, err := conn.CreateTransitGatewayPrefixListReference(input) + + if err != nil { + return fmt.Errorf("error creating EC2 Transit Gateway Prefix List Reference: %w", err) + } + + if output == nil || output.TransitGatewayPrefixListReference == nil { + return fmt.Errorf("error creating EC2 Transit Gateway Prefix List Reference: empty response") + } + + d.SetId(tfec2.TransitGatewayPrefixListReferenceCreateID(aws.StringValue(output.TransitGatewayPrefixListReference.TransitGatewayRouteTableId), aws.StringValue(output.TransitGatewayPrefixListReference.PrefixListId))) + + if _, err := waiter.TransitGatewayPrefixListReferenceStateCreated(conn, aws.StringValue(output.TransitGatewayPrefixListReference.TransitGatewayRouteTableId), aws.StringValue(output.TransitGatewayPrefixListReference.PrefixListId)); err != nil { + return fmt.Errorf("error waiting for EC2 Transit Gateway Prefix List Reference (%s) creation: %w", d.Id(), err) + } + + return resourceAwsEc2TransitGatewayPrefixListReferenceRead(d, meta) +} + +func resourceAwsEc2TransitGatewayPrefixListReferenceRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + transitGatewayPrefixListReference, err := finder.TransitGatewayPrefixListReferenceByID(conn, d.Id()) + + if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidRouteTableIDNotFound) { + log.Printf("[WARN] EC2 Transit Gateway Prefix List Reference (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EC2 Transit Gateway Prefix List Reference (%s): %w", d.Id(), err) + } + + if transitGatewayPrefixListReference == nil { + log.Printf("[WARN] EC2 Transit Gateway Prefix List Reference (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if aws.StringValue(transitGatewayPrefixListReference.State) == ec2.TransitGatewayPrefixListReferenceStateDeleting { + log.Printf("[WARN] EC2 Transit Gateway Prefix List Reference (%s) deleting, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("blackhole", transitGatewayPrefixListReference.Blackhole) + d.Set("prefix_list_id", transitGatewayPrefixListReference.PrefixListId) + d.Set("prefix_list_owner_id", transitGatewayPrefixListReference.PrefixListOwnerId) + + if transitGatewayPrefixListReference.TransitGatewayAttachment == nil { + d.Set("transit_gateway_attachment_id", nil) + } else { + d.Set("transit_gateway_attachment_id", transitGatewayPrefixListReference.TransitGatewayAttachment.TransitGatewayAttachmentId) + } + + d.Set("transit_gateway_route_table_id", transitGatewayPrefixListReference.TransitGatewayRouteTableId) + + return nil +} + +func resourceAwsEc2TransitGatewayPrefixListReferenceUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + input := &ec2.ModifyTransitGatewayPrefixListReferenceInput{} + + if v, ok := d.GetOk("blackhole"); ok { + input.Blackhole = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("prefix_list_id"); ok { + input.PrefixListId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("transit_gateway_attachment_id"); ok { + input.TransitGatewayAttachmentId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("transit_gateway_route_table_id"); ok { + input.TransitGatewayRouteTableId = aws.String(v.(string)) + } + + output, err := conn.ModifyTransitGatewayPrefixListReference(input) + + if err != nil { + return fmt.Errorf("error updating EC2 Transit Gateway Prefix List Reference (%s): %w", d.Id(), err) + } + + if output == nil || output.TransitGatewayPrefixListReference == nil { + return fmt.Errorf("error updating EC2 Transit Gateway Prefix List Reference (%s): empty response", d.Id()) + } + + if _, err := waiter.TransitGatewayPrefixListReferenceStateUpdated(conn, aws.StringValue(output.TransitGatewayPrefixListReference.TransitGatewayRouteTableId), aws.StringValue(output.TransitGatewayPrefixListReference.PrefixListId)); err != nil { + return fmt.Errorf("error waiting for EC2 Transit Gateway Prefix List Reference (%s) update: %w", d.Id(), err) + } + + return resourceAwsEc2TransitGatewayPrefixListReferenceRead(d, meta) +} + +func resourceAwsEc2TransitGatewayPrefixListReferenceDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + transitGatewayRouteTableID, prefixListID, err := tfec2.TransitGatewayPrefixListReferenceParseID(d.Id()) + + if err != nil { + return fmt.Errorf("error parsing EC2 Transit Gateway Prefix List Reference (%s) idenfitier: %w", d.Id(), err) + } + + input := &ec2.DeleteTransitGatewayPrefixListReferenceInput{ + PrefixListId: aws.String(prefixListID), + TransitGatewayRouteTableId: aws.String(transitGatewayRouteTableID), + } + + _, err = conn.DeleteTransitGatewayPrefixListReference(input) + + if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidRouteTableIDNotFound) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting EC2 Transit Gateway Prefix List Reference (%s): %w", d.Id(), err) + } + + if _, err := waiter.TransitGatewayPrefixListReferenceStateDeleted(conn, transitGatewayRouteTableID, prefixListID); err != nil { + return fmt.Errorf("error waiting for EC2 Transit Gateway Prefix List Reference (%s) deletion: %w", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_ec2_transit_gateway_prefix_list_reference_test.go b/aws/resource_aws_ec2_transit_gateway_prefix_list_reference_test.go new file mode 100644 index 00000000000..031c222bc65 --- /dev/null +++ b/aws/resource_aws_ec2_transit_gateway_prefix_list_reference_test.go @@ -0,0 +1,234 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder" +) + +func TestAccAwsEc2TransitGatewayPrefixListReference_basic(t *testing.T) { + managedPrefixListResourceName := "aws_ec2_managed_prefix_list.test" + resourceName := "aws_ec2_transit_gateway_prefix_list_reference.test" + transitGatewayResourceName := "aws_ec2_transit_gateway.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2TransitGateway(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsEc2TransitGatewayPrefixListReferenceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEc2TransitGatewayPrefixListReferenceConfig_Blackhole(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsEc2TransitGatewayPrefixListReferenceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "blackhole", "true"), + resource.TestCheckResourceAttrPair(resourceName, "prefix_list_id", managedPrefixListResourceName, "id"), + testAccCheckResourceAttrAccountID(resourceName, "prefix_list_owner_id"), + resource.TestCheckResourceAttr(resourceName, "transit_gateway_attachment_id", ""), + resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_route_table_id", transitGatewayResourceName, "association_default_route_table_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsEc2TransitGatewayPrefixListReference_disappears(t *testing.T) { + resourceName := "aws_ec2_transit_gateway_prefix_list_reference.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2TransitGateway(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsEc2TransitGatewayPrefixListReferenceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEc2TransitGatewayPrefixListReferenceConfig_Blackhole(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsEc2TransitGatewayPrefixListReferenceExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsEc2TransitGatewayPrefixListReference(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsEc2TransitGatewayPrefixListReference_disappears_TransitGateway(t *testing.T) { + resourceName := "aws_ec2_transit_gateway_prefix_list_reference.test" + transitGatewayResourceName := "aws_ec2_transit_gateway.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2TransitGateway(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsEc2TransitGatewayPrefixListReferenceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEc2TransitGatewayPrefixListReferenceConfig_Blackhole(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsEc2TransitGatewayPrefixListReferenceExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsEc2TransitGateway(), transitGatewayResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsEc2TransitGatewayPrefixListReference_TransitGatewayAttachmentId(t *testing.T) { + resourceName := "aws_ec2_transit_gateway_prefix_list_reference.test" + transitGatewayVpcAttachmentResourceName1 := "aws_ec2_transit_gateway_vpc_attachment.test.0" + transitGatewayVpcAttachmentResourceName2 := "aws_ec2_transit_gateway_vpc_attachment.test.1" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2TransitGateway(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsEc2TransitGatewayPrefixListReferenceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEc2TransitGatewayPrefixListReferenceConfig_TransitGatewayAttachmentId(rName, 0), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsEc2TransitGatewayPrefixListReferenceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "blackhole", "false"), + resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_attachment_id", transitGatewayVpcAttachmentResourceName1, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsEc2TransitGatewayPrefixListReferenceConfig_TransitGatewayAttachmentId(rName, 1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsEc2TransitGatewayPrefixListReferenceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "blackhole", "false"), + resource.TestCheckResourceAttrPair(resourceName, "transit_gateway_attachment_id", transitGatewayVpcAttachmentResourceName2, "id"), + ), + }, + }, + }) +} + +func testAccCheckAwsEc2TransitGatewayPrefixListReferenceDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ec2_transit_gateway_prefix_list_reference" { + continue + } + + transitGatewayPrefixListReference, err := finder.TransitGatewayPrefixListReferenceByID(conn, rs.Primary.ID) + + if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidRouteTableIDNotFound) { + continue + } + + if err != nil { + return fmt.Errorf("error reading EC2 Transit Gateway Prefix List Reference (%s): %w", rs.Primary.ID, err) + } + + if transitGatewayPrefixListReference != nil { + return fmt.Errorf("EC2 Transit Gateway Prefix List Reference (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccAwsEc2TransitGatewayPrefixListReferenceExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + + if !ok { + return fmt.Errorf("resource %s not found", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("resource %s has not set its id", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + transitGatewayPrefixListReference, err := finder.TransitGatewayPrefixListReferenceByID(conn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error reading EC2 Transit Gateway Prefix List Reference (%s): %w", rs.Primary.ID, err) + } + + if transitGatewayPrefixListReference == nil { + return fmt.Errorf("EC2 Transit Gateway Prefix List Reference (%s) not found", rs.Primary.ID) + } + + return nil + } +} + +func testAccAwsEc2TransitGatewayPrefixListReferenceConfig_Blackhole(rName string) string { + return fmt.Sprintf(` +resource "aws_ec2_managed_prefix_list" "test" { + address_family = "IPv4" + max_entries = 1 + name = %[1]q +} + +resource "aws_ec2_transit_gateway" "test" {} + +resource "aws_ec2_transit_gateway_prefix_list_reference" "test" { + blackhole = true + prefix_list_id = aws_ec2_managed_prefix_list.test.id + transit_gateway_route_table_id = aws_ec2_transit_gateway.test.association_default_route_table_id +} +`, rName) +} + +func testAccAwsEc2TransitGatewayPrefixListReferenceConfig_TransitGatewayAttachmentId(rName string, index int) string { + return fmt.Sprintf(` +resource "aws_ec2_managed_prefix_list" "test" { + address_family = "IPv4" + max_entries = 1 + name = %[1]q +} + +resource "aws_vpc" "test" { + count = 2 + + cidr_block = "10.${count.index}.0.0/16" +} + +resource "aws_subnet" "test" { + count = 2 + + cidr_block = cidrsubnet(aws_vpc.test[count.index].cidr_block, 8, 0) + vpc_id = aws_vpc.test[count.index].id +} + +resource "aws_ec2_transit_gateway" "test" {} + +resource "aws_ec2_transit_gateway_vpc_attachment" "test" { + count = 2 + + subnet_ids = [aws_subnet.test[count.index].id] + transit_gateway_id = aws_ec2_transit_gateway.test.id + vpc_id = aws_vpc.test[count.index].id +} + +resource "aws_ec2_transit_gateway_prefix_list_reference" "test" { + prefix_list_id = aws_ec2_managed_prefix_list.test.id + transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.test[%[2]d].id + transit_gateway_route_table_id = aws_ec2_transit_gateway.test.association_default_route_table_id +} +`, rName, index) +} diff --git a/website/docs/r/ec2_transit_gateway_prefix_list_reference.html.markdown b/website/docs/r/ec2_transit_gateway_prefix_list_reference.html.markdown new file mode 100644 index 00000000000..f6d884e8837 --- /dev/null +++ b/website/docs/r/ec2_transit_gateway_prefix_list_reference.html.markdown @@ -0,0 +1,59 @@ +--- +subcategory: "EC2" +layout: "aws" +page_title: "AWS: aws_ec2_transit_gateway_prefix_list_reference" +description: |- + Manages an EC2 Transit Gateway Prefix List Reference +--- + +# Resource: aws_ec2_transit_gateway_prefix_list_reference + +Manages an EC2 Transit Gateway Prefix List Reference. + +## Example Usage + +### Attachment Routing + +```hcl +resource "aws_ec2_transit_gateway_prefix_list_reference" "example" { + prefix_list_id = aws_ec2_managed_prefix_list.example.id + transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.example.id + transit_gateway_route_table_id = aws_ec2_transit_gateway.example.association_default_route_table_id +} +``` + +### Blackhole Routing + +```hcl +resource "aws_ec2_transit_gateway_prefix_list_reference" "example" { + blackhole = true + prefix_list_id = aws_ec2_managed_prefix_list.example.id + transit_gateway_route_table_id = aws_ec2_transit_gateway.example.association_default_route_table_id +} +``` + +## Argument Reference + +The following arguments are required: + +* `prefix_list_id` - (Required) Identifier of EC2 Prefix List. +* `transit_gateway_route_table_id` - (Required) Identifier of EC2 Transit Gateway Route Table. + +The following arguments are optional: + +* `blackhole` - (Optional) Indicates whether to drop traffic that matches the Prefix List. Defaults to `false`. +* `transit_gateway_attachment_id` - (Optional) Identifier of EC2 Transit Gateway Attachment. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - EC2 Transit Gateway Route Table identifier and EC2 Prefix List identifier, separated by an underscore (`_`) + +## Import + +`aws_ec2_transit_gateway_prefix_list_reference` can be imported by using the EC2 Transit Gateway Route Table identifier and EC2 Prefix List identifier, separated by an underscore (`_`), e.g. + +```console +$ terraform import aws_ec2_transit_gateway_prefix_list_reference.example tgw-rtb-12345678_pl-12345678 +```