From ab13df1ea3d96ea2bf22a44bb628ed82c3157972 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 15:16:35 +0200 Subject: [PATCH 01/22] Add network manager connect attachment --- internal/provider/provider.go | 1 + .../networkmanager/connect_attachment.go | 418 ++++++++++++++++++ 2 files changed, 419 insertions(+) create mode 100644 internal/service/networkmanager/connect_attachment.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 9970d470fa59..5c91586b3324 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1815,6 +1815,7 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_networkmanager_transit_gateway_route_table_attachment": networkmanager.ResourceTransitGatewayRouteTableAttachment(), "aws_networkmanager_vpc_attachment": networkmanager.ResourceVPCAttachment(), "aws_networkmanager_site_to_site_vpn_attachment": networkmanager.ResourceSiteToSiteVPNAttachment(), + "aws_networkmanager_connect_attachment": networkmanager.ResourceConnectAttachment(), "aws_opensearch_domain": opensearch.ResourceDomain(), "aws_opensearch_domain_policy": opensearch.ResourceDomainPolicy(), diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go new file mode 100644 index 000000000000..93e1a727a3a7 --- /dev/null +++ b/internal/service/networkmanager/connect_attachment.go @@ -0,0 +1,418 @@ +package networkmanager + +import ( + "context" + "fmt" + "log" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/networkmanager" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + + // "github.com/hashicorp/terraform-provider-aws/internal/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func ResourceConnectAttachment() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceConnectAttachmentCreate, + ReadWithoutTimeout: resourceConnectAttachmentRead, + UpdateWithoutTimeout: resourceConnectAttachmentUpdate, + DeleteWithoutTimeout: resourceConnectAttachmentDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + CustomizeDiff: verify.SetTagsDiff, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "attachment_id": { + Type: schema.TypeString, + Computed: true, + }, + "attachment_policy_rule_number": { + Type: schema.TypeInt, + Computed: true, + }, + "attachment_type": { + Type: schema.TypeString, + Computed: true, + }, + "core_network_arn": { + Type: schema.TypeString, + Computed: true, + }, + "core_network_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 50), + validation.StringMatch(regexp.MustCompile(`^core-network-([0-9a-f]{8,17})$`), "Must start with core-network and then have 8 to 17 characters"), + ), + }, + "edge_location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 63), + validation.StringMatch(regexp.MustCompile(`[\s\S]*`), "Anything but whitespace"), + ), + }, + "owner_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_arn": { + Type: schema.TypeString, + Computed: true, + }, + "segment_name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + "transport_attachment_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(0, 50), + validation.StringMatch(regexp.MustCompile(`^attachment-([0-9a-f]{8,17})$`), "Must start with attachment- and then have 8 to 17 characters"), + ), + }, + "options": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"GRE"}, false), + }, + }, + }, + }, + }, + } +} + +func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).NetworkManagerConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + + coreNetworkID := d.Get("core_network_id").(string) + edgeLocation := d.Get("edge_location").(string) + transportAttachmentID := d.Get("transport_attachment_id").(string) + options := &networkmanager.ConnectAttachmentOptions{} + + if v, ok := d.GetOk("options"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + options = expandConnectOptions(v.([]interface{})[0].(map[string]interface{})) + } + + input := &networkmanager.CreateConnectAttachmentInput{ + CoreNetworkId: aws.String(coreNetworkID), + EdgeLocation: aws.String(edgeLocation), + TransportAttachmentId: aws.String(transportAttachmentID), + Options: options, + } + + if len(tags) > 0 { + input.Tags = Tags(tags.IgnoreAWS()) + } + + // Connect attachment doesn't have direct dependency to VPC attachment state when using Attachment Accepter. + // Waiting for Create Timeout period for VPC Attachment to come available state. + // Only needed if depends_on statement is not used in Connect attachment + log.Printf("[DEBUG] Creating Network Manager Connect Attachment: %s", input) + var err error + var output *networkmanager.CreateConnectAttachmentOutput + err = resource.RetryContext(ctx, d.Timeout("Create"), func() *resource.RetryError { + output, err = conn.CreateConnectAttachmentWithContext(ctx, input) + if err != nil { + + if validationExceptionMessageContains(err, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) + } + + return nil + }) + + if err != nil { + return diag.Errorf("creating Network Manager Connect Attachment (%s) (%s): %s", transportAttachmentID, coreNetworkID, err) + } + + d.SetId(aws.StringValue(output.ConnectAttachment.Attachment.AttachmentId)) + + if _, err := waitConnectAttachmentCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return diag.Errorf("waiting for Network Manager Connect Attachment (%s) create: %s", d.Id(), err) + } + + return resourceConnectAttachmentRead(ctx, d, meta) +} + +func resourceConnectAttachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).NetworkManagerConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + connectAttachment, err := FindConnectAttachmentByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Network Manager Connect Attachment %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("reading Network Manager Connect Attachment (%s): %s", d.Id(), err) + } + + a := connectAttachment.Attachment + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: "networkmanager", + AccountID: meta.(*conns.AWSClient).AccountID, + Resource: fmt.Sprintf("attachment/%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("attachment_policy_rule_number", a.AttachmentPolicyRuleNumber) + d.Set("attachment_id", a.AttachmentId) + d.Set("attachment_type", a.AttachmentType) + d.Set("core_network_arn", a.CoreNetworkArn) + d.Set("core_network_id", a.CoreNetworkId) + d.Set("owner_account_id", a.OwnerAccountId) + d.Set("resource_arn", a.ResourceArn) + d.Set("segment_name", a.SegmentName) + d.Set("state", a.State) + d.Set("edge_location", a.EdgeLocation) + d.Set("transport_attachment_id", connectAttachment.TransportAttachmentId) + if connectAttachment.Options != nil { + if err := d.Set("options", []interface{}{flattenConnectOptions(connectAttachment.Options)}); err != nil { + return diag.Errorf("setting options: %s", err) + } + } else { + d.Set("options", nil) + } + + tags := KeyValueTags(a.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.Errorf("Setting tags: %s", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.Errorf("setting tags_all: %s", err) + } + + return nil +} + +func resourceConnectAttachmentUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).NetworkManagerConn + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return diag.FromErr(fmt.Errorf("error updating Network Manager Connect Attachment (%s) tags: %s", d.Get("arn").(string), err)) + } + } + + return resourceConnectAttachmentRead(ctx, d, meta) +} + +func resourceConnectAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).NetworkManagerConn + + // If ResourceAttachmentAccepter is used, then VPN Attachment state + // is never updated from StatePendingAttachmentAcceptance and the delete fails + output, sErr := FindConnectAttachmentByID(ctx, conn, d.Id()) + if tfawserr.ErrCodeEquals(sErr, networkmanager.ErrCodeResourceNotFoundException) { + return nil + } + + if sErr != nil { + return diag.Errorf("deleting Network Manager Connect Attachment (%s): %s", d.Id(), sErr) + } + + d.Set("state", output.Attachment.State) + + if state := d.Get("state").(string); state == networkmanager.AttachmentStatePendingAttachmentAcceptance || state == networkmanager.AttachmentStatePendingTagAcceptance { + return diag.Errorf("cannot delete Network Manager Connect Attachment (%s) in %s state", d.Id(), state) + } + + log.Printf("[DEBUG] Deleting Network Manager Connect Attachment: %s", d.Id()) + _, err := conn.DeleteAttachmentWithContext(ctx, &networkmanager.DeleteAttachmentInput{ + AttachmentId: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, networkmanager.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return diag.Errorf("deleting Network Manager Connect Attachment (%s): %s", d.Id(), err) + } + + if _, err := waitConnectAttachmentDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return diag.Errorf("waiting for Network Manager Connect Attachment (%s) delete: %s", d.Id(), err) + } + + return nil +} + +func FindConnectAttachmentByID(ctx context.Context, conn *networkmanager.NetworkManager, id string) (*networkmanager.ConnectAttachment, error) { + input := &networkmanager.GetConnectAttachmentInput{ + AttachmentId: aws.String(id), + } + + output, err := conn.GetConnectAttachmentWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, networkmanager.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.ConnectAttachment == nil || output.ConnectAttachment.Attachment == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.ConnectAttachment, nil +} + +func statusConnectAttachmentState(ctx context.Context, conn *networkmanager.NetworkManager, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindConnectAttachmentByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Attachment.State), nil + } +} + +func waitConnectAttachmentCreated(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { //nolint:unparam + stateConf := &resource.StateChangeConf{ + Pending: []string{networkmanager.AttachmentStateCreating, networkmanager.AttachmentStatePendingNetworkUpdate}, + Target: []string{networkmanager.AttachmentStateAvailable, networkmanager.AttachmentStatePendingAttachmentAcceptance}, + Timeout: timeout, + Refresh: statusConnectAttachmentState(ctx, conn, id), + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*networkmanager.ConnectAttachment); ok { + return output, err + } + + return nil, err +} + +func waitConnectAttachmentDeleted(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{networkmanager.AttachmentStateDeleting}, + Target: []string{}, + Timeout: timeout, + Refresh: statusConnectAttachmentState(ctx, conn, id), + NotFoundChecks: 1, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*networkmanager.ConnectAttachment); ok { + return output, err + } + + return nil, err +} + +func waitConnectAttachmentAvailable(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { //nolint:unparam + stateConf := &resource.StateChangeConf{ + Pending: []string{networkmanager.AttachmentStateCreating, networkmanager.AttachmentStatePendingNetworkUpdate, networkmanager.AttachmentStatePendingAttachmentAcceptance}, + Target: []string{networkmanager.AttachmentStateAvailable}, + Timeout: timeout, + Refresh: statusConnectAttachmentState(ctx, conn, id), + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*networkmanager.ConnectAttachment); ok { + return output, err + } + + return nil, err +} + +func expandConnectOptions(o map[string]interface{}) *networkmanager.ConnectAttachmentOptions { + if o == nil { + return nil + } + + object := &networkmanager.ConnectAttachmentOptions{} + + if v, ok := o["protocol"].(string); ok { + object.Protocol = aws.String(v) + } + + return object +} + +func flattenConnectOptions(apiObject *networkmanager.ConnectAttachmentOptions) map[string]interface{} { // nosemgrep:ci.caps5-in-func-name + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Protocol; v != nil { + tfMap["protocol"] = aws.StringValue(v) + } + + return tfMap +} From 99f22000f4d53ca6b8e751c9b0aeee220312e27a Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 15:17:10 +0200 Subject: [PATCH 02/22] Add network manager connect attachment tests --- .../networkmanager/connect_attachment_test.go | 455 ++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 internal/service/networkmanager/connect_attachment_test.go diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go new file mode 100644 index 000000000000..9e194b344682 --- /dev/null +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -0,0 +1,455 @@ +package networkmanager_test + +import ( + "context" + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/networkmanager" + sdkacctest "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" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfnetworkmanager "github.com/hashicorp/terraform-provider-aws/internal/service/networkmanager" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +func TestAccNetworkManagerConnectAttachment_basic(t *testing.T) { + var v networkmanager.ConnectAttachment + resourceName := "aws_networkmanager_connect_attachment.test" + testExternalProviders := map[string]resource.ExternalProvider{ + "awscc": { + Source: "hashicorp/awscc", + VersionConstraint: "0.29.0", + }, + } + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ExternalProviders: testExternalProviders, + CheckDestroy: testAccCheckConnectAttachmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConnectAttachmentConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckConnectAttachmentExists(resourceName, &v), + acctest.MatchResourceAttrGlobalARN(resourceName, "arn", "networkmanager", regexp.MustCompile(`attachment/.+`)), + resource.TestCheckResourceAttr(resourceName, "attachment_type", "CONNECT"), + resource.TestCheckResourceAttrSet(resourceName, "core_network_id"), + resource.TestCheckResourceAttr(resourceName, "edge_location", acctest.Region()), + acctest.CheckResourceAttrAccountID(resourceName, "owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "segment_name", "shared"), + resource.TestCheckResourceAttrSet(resourceName, "state"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkManagerConnectAttachment_basic_NoDependsOn(t *testing.T) { + var v networkmanager.ConnectAttachment + resourceName := "aws_networkmanager_connect_attachment.test" + testExternalProviders := map[string]resource.ExternalProvider{ + "awscc": { + Source: "hashicorp/awscc", + VersionConstraint: "0.29.0", + }, + } + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ExternalProviders: testExternalProviders, + CheckDestroy: testAccCheckConnectAttachmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConnectAttachmentConfig_basic_NoDependsOn(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckConnectAttachmentExists(resourceName, &v), + acctest.MatchResourceAttrGlobalARN(resourceName, "arn", "networkmanager", regexp.MustCompile(`attachment/.+`)), + resource.TestCheckResourceAttr(resourceName, "attachment_type", "CONNECT"), + resource.TestCheckResourceAttrSet(resourceName, "core_network_id"), + resource.TestCheckResourceAttr(resourceName, "edge_location", acctest.Region()), + acctest.CheckResourceAttrAccountID(resourceName, "owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "segment_name", "shared"), + resource.TestCheckResourceAttrSet(resourceName, "state"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkManagerConnectAttachment_disappears(t *testing.T) { + var v networkmanager.ConnectAttachment + resourceName := "aws_networkmanager_connect_attachment.test" + testExternalProviders := map[string]resource.ExternalProvider{ + "awscc": { + Source: "hashicorp/awscc", + VersionConstraint: "0.29.0", + }, + } + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ExternalProviders: testExternalProviders, + CheckDestroy: testAccCheckConnectAttachmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConnectAttachmentConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectAttachmentExists(resourceName, &v), + acctest.CheckResourceDisappears(acctest.Provider, tfnetworkmanager.ResourceConnectAttachment(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccNetworkManagerConnectAttachment_tags(t *testing.T) { + var v networkmanager.ConnectAttachment + resourceName := "aws_networkmanager_connect_attachment.test" + testExternalProviders := map[string]resource.ExternalProvider{ + "awscc": { + Source: "hashicorp/awscc", + VersionConstraint: "0.29.0", + }, + } + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmanager.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ExternalProviders: testExternalProviders, + CheckDestroy: testAccCheckConnectAttachmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConnectAttachmentConfig_tags1(rName, "segment", "shared"), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectAttachmentExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.segment", "shared"), + ), + }, + { + Config: testAccConnectAttachmentConfig_tags2(rName, "segment", "shared", "Name", "test"), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectAttachmentExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.segment", "shared"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", "test"), + ), + }, + { + Config: testAccConnectAttachmentConfig_tags1(rName, "segment", "shared"), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectAttachmentExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.segment", "shared"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckConnectAttachmentExists(n string, v *networkmanager.ConnectAttachment) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Network Manager Connect Attachment ID is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkManagerConn + + output, err := tfnetworkmanager.FindConnectAttachmentByID(context.Background(), conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccCheckConnectAttachmentDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkManagerConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_networkmanager_connect_attachment" { + continue + } + + _, err := tfnetworkmanager.FindConnectAttachmentByID(context.Background(), conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("Network Manager Connect Attachment %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccConnectAttachmentConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +data "aws_region" "current" {} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + assign_generated_ipv6_cidr_block = true + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + count = 2 + + vpc_id = aws_vpc.test.id + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) + + ipv6_cidr_block = cidrsubnet(aws_vpc.test.ipv6_cidr_block, 8, count.index) + assign_ipv6_address_on_creation = true + + tags = { + Name = %[1]q + } +} + +resource "aws_networkmanager_global_network" "test" { + tags = { + Name = %[1]q + } +} + +resource "awscc_networkmanager_core_network" "test" { + global_network_id = aws_networkmanager_global_network.test.id + policy_document = jsonencode(jsondecode(data.aws_networkmanager_core_network_policy_document.test.json)) +} + +data "aws_networkmanager_core_network_policy_document" "test" { + core_network_configuration { + vpn_ecmp_support = false + asn_ranges = ["64512-64555"] + edge_locations { + location = data.aws_region.current.name + asn = 64512 + } + } + segments { + name = "shared" + description = "SegmentForSharedServices" + require_attachment_acceptance = true + } + segment_actions { + action = "share" + mode = "attachment-route" + segment = "shared" + share_with = ["*"] + } + attachment_policies { + rule_number = 1 + condition_logic = "or" + conditions { + type = "tag-value" + operator = "equals" + key = "segment" + value = "shared" + } + action { + association_method = "constant" + segment = "shared" + } + } +} + +`, rName)) +} + +func testAccConnectAttachmentConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccConnectAttachmentConfig_base(rName), ` + resource "aws_networkmanager_vpc_attachment" "test" { + subnet_arns = aws_subnet.test[*].arn + core_network_id = awscc_networkmanager_core_network.test.id + vpc_arn = aws_vpc.test.arn + tags = { + segment = "shared" + } + } + + resource "aws_networkmanager_attachment_accepter" "test" { + attachment_id = aws_networkmanager_vpc_attachment.test.id + attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type + } + + resource "aws_networkmanager_connect_attachment" "test" { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + tags = { + segment = "shared" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] + } + + resource "aws_networkmanager_attachment_accepter" "test2" { + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type + } +`) +} + +func testAccConnectAttachmentConfig_basic_NoDependsOn(rName string) string { + return acctest.ConfigCompose(testAccConnectAttachmentConfig_base(rName), ` + resource "aws_networkmanager_vpc_attachment" "test" { + subnet_arns = aws_subnet.test[*].arn + core_network_id = awscc_networkmanager_core_network.test.id + vpc_arn = aws_vpc.test.arn + tags = { + segment = "shared" + } + } + + resource "aws_networkmanager_attachment_accepter" "test" { + attachment_id = aws_networkmanager_vpc_attachment.test.id + attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type + } + + resource "aws_networkmanager_connect_attachment" "test" { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + tags = { + segment = "shared" + } + } + + resource "aws_networkmanager_attachment_accepter" "test2" { + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type + } +`) +} + +func testAccConnectAttachmentConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccConnectAttachmentConfig_base(rName), fmt.Sprintf(` +resource "aws_networkmanager_vpc_attachment" "test" { + subnet_arns = [aws_subnet.test[0].arn] + core_network_id = awscc_networkmanager_core_network.test.id + vpc_arn = aws_vpc.test.arn + +} + +resource "aws_networkmanager_attachment_accepter" "test" { + attachment_id = aws_networkmanager_vpc_attachment.test.id + attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type +} + +resource "aws_networkmanager_connect_attachment" "test" { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] + tags = { + %[1]q = %[2]q + } +} + +resource "aws_networkmanager_attachment_accepter" "test2" { + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type +} + +`, tagKey1, tagValue1)) +} + +func testAccConnectAttachmentConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccConnectAttachmentConfig_base(rName), fmt.Sprintf(` +resource "aws_networkmanager_vpc_attachment" "test" { + subnet_arns = [aws_subnet.test[0].arn] + core_network_id = awscc_networkmanager_core_network.test.id + vpc_arn = aws_vpc.test.arn + +} + +resource "aws_networkmanager_attachment_accepter" "test" { + attachment_id = aws_networkmanager_vpc_attachment.test.id + attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type +} + +resource "aws_networkmanager_connect_attachment" "test" { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] + tags = { + %[1]q = %[2]q + } +} + +resource "aws_networkmanager_attachment_accepter" "test2" { + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type +} +`, tagKey1, tagValue1, tagKey2, tagValue2)) +} From c54d7c1dbbc28cc946ec05efe29ca97d9b1a31a1 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 15:30:19 +0200 Subject: [PATCH 03/22] Add connect attachment to attachment accepter --- .../networkmanager/attachment_accepter.go | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/internal/service/networkmanager/attachment_accepter.go b/internal/service/networkmanager/attachment_accepter.go index 8ee07c39baec..058107595475 100644 --- a/internal/service/networkmanager/attachment_accepter.go +++ b/internal/service/networkmanager/attachment_accepter.go @@ -46,6 +46,7 @@ func ResourceAttachmentAccepter() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ networkmanager.AttachmentTypeVpc, networkmanager.AttachmentTypeSiteToSiteVpn, + networkmanager.AttachmentTypeConnect, }, false), }, "core_network_arn": { @@ -110,6 +111,17 @@ func resourceAttachmentAccepterCreate(ctx context.Context, d *schema.ResourceDat d.SetId(attachmentID) + case networkmanager.AttachmentTypeConnect: + connectAttachment, err := FindConnectAttachmentByID(ctx, conn, attachmentID) + + if err != nil { + return diag.Errorf("reading Network Manager Connect Attachment (%s): %s", attachmentID, err) + } + + state = aws.StringValue(connectAttachment.Attachment.State) + + d.SetId(attachmentID) + default: return diag.Errorf("unsupported Network Manager Attachment type: %s", attachmentType) } @@ -135,6 +147,11 @@ func resourceAttachmentAccepterCreate(ctx context.Context, d *schema.ResourceDat if _, err := waitSiteToSiteVPNAttachmentAvailable(ctx, conn, attachmentID, d.Timeout(schema.TimeoutCreate)); err != nil { return diag.Errorf("waiting for Network Manager VPN Attachment (%s) create: %s", attachmentID, err) } + + case networkmanager.AttachmentTypeConnect: + if _, err := waitConnectAttachmentAvailable(ctx, conn, attachmentID, d.Timeout(schema.TimeoutCreate)); err != nil { + return diag.Errorf("waiting for Network Manager Connect Attachment (%s) create: %s", attachmentID, err) + } } } @@ -144,6 +161,8 @@ func resourceAttachmentAccepterCreate(ctx context.Context, d *schema.ResourceDat func resourceAttachmentAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).NetworkManagerConn + var a *networkmanager.Attachment + switch aType := d.Get("attachment_type"); aType { case networkmanager.AttachmentTypeVpc: vpcAttachment, err := FindVPCAttachmentByID(ctx, conn, d.Id()) @@ -158,15 +177,7 @@ func resourceAttachmentAccepterRead(ctx context.Context, d *schema.ResourceData, return diag.Errorf("reading Network Manager VPC Attachment (%s): %s", d.Id(), err) } - a := vpcAttachment.Attachment - d.Set("attachment_policy_rule_number", a.AttachmentPolicyRuleNumber) - d.Set("core_network_arn", a.CoreNetworkArn) - d.Set("core_network_id", a.CoreNetworkId) - d.Set("edge_location", a.EdgeLocation) - d.Set("owner_account_id", a.OwnerAccountId) - d.Set("resource_arn", a.ResourceArn) - d.Set("segment_name", a.SegmentName) - d.Set("state", a.State) + a = vpcAttachment.Attachment case networkmanager.AttachmentTypeSiteToSiteVpn: vpnAttachment, err := FindSiteToSiteVPNAttachmentByID(ctx, conn, d.Id()) @@ -181,16 +192,33 @@ func resourceAttachmentAccepterRead(ctx context.Context, d *schema.ResourceData, return diag.Errorf("reading Network Manager Site To Site VPN Attachment (%s): %s", d.Id(), err) } - a := vpnAttachment.Attachment - d.Set("attachment_policy_rule_number", a.AttachmentPolicyRuleNumber) - d.Set("core_network_arn", a.CoreNetworkArn) - d.Set("core_network_id", a.CoreNetworkId) - d.Set("edge_location", a.EdgeLocation) - d.Set("owner_account_id", a.OwnerAccountId) - d.Set("resource_arn", a.ResourceArn) - d.Set("segment_name", a.SegmentName) - d.Set("state", a.State) + a = vpnAttachment.Attachment + + case networkmanager.AttachmentTypeConnect: + connectAttachment, err := FindConnectAttachmentByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Network Manager Connect Attachment %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("reading Network Manager Connect Attachment (%s): %s", d.Id(), err) + } + + a = connectAttachment.Attachment + } + d.Set("attachment_policy_rule_number", a.AttachmentPolicyRuleNumber) + d.Set("core_network_arn", a.CoreNetworkArn) + d.Set("core_network_id", a.CoreNetworkId) + d.Set("edge_location", a.EdgeLocation) + d.Set("owner_account_id", a.OwnerAccountId) + d.Set("resource_arn", a.ResourceArn) + d.Set("segment_name", a.SegmentName) + d.Set("state", a.State) + return nil } From f8a33c68df19d935b9c906127870046c3b401a34 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 17:07:34 +0200 Subject: [PATCH 04/22] Fix VPC attachment delete --- internal/service/networkmanager/vpc_attachment.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/service/networkmanager/vpc_attachment.go b/internal/service/networkmanager/vpc_attachment.go index a4ca51928c48..8a4482b74ac3 100644 --- a/internal/service/networkmanager/vpc_attachment.go +++ b/internal/service/networkmanager/vpc_attachment.go @@ -269,6 +269,20 @@ func resourceVPCAttachmentUpdate(ctx context.Context, d *schema.ResourceData, me func resourceVPCAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).NetworkManagerConn + // If ResourceAttachmentAccepter is used, then VPC Attachment state + // is not updated from StatePendingAttachmentAcceptance and the delete fails if deleted immediately after create + output, sErr := FindVPCAttachmentByID(ctx, conn, d.Id()) + + if tfawserr.ErrCodeEquals(sErr, networkmanager.ErrCodeResourceNotFoundException) { + return nil + } + + if sErr != nil { + return diag.Errorf("reading Network Manager Site To Site VPN Attachment (%s): %s", d.Id(), sErr) + } + + d.Set("state", output.Attachment.State) + if state := d.Get("state").(string); state == networkmanager.AttachmentStatePendingAttachmentAcceptance || state == networkmanager.AttachmentStatePendingTagAcceptance { return diag.Errorf("cannot delete Network Manager VPC Attachment (%s) in %s state", d.Id(), state) } From 5a61a991d16514f4090ac94b0b829e57500299c8 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 17:07:49 +0200 Subject: [PATCH 05/22] Fix comment --- internal/service/networkmanager/connect_attachment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index 93e1a727a3a7..e07a85bc7b27 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -259,7 +259,7 @@ func resourceConnectAttachmentUpdate(ctx context.Context, d *schema.ResourceData func resourceConnectAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).NetworkManagerConn - // If ResourceAttachmentAccepter is used, then VPN Attachment state + // If ResourceAttachmentAccepter is used, then Connect Attachment state // is never updated from StatePendingAttachmentAcceptance and the delete fails output, sErr := FindConnectAttachmentByID(ctx, conn, d.Id()) if tfawserr.ErrCodeEquals(sErr, networkmanager.ErrCodeResourceNotFoundException) { From 26bbd52fc66adc645ac732af98d6bea108c94f23 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 17:37:46 +0200 Subject: [PATCH 06/22] Add segment tag to vpc attachment --- .../service/networkmanager/connect_attachment_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go index 9e194b344682..d467cf7081c8 100644 --- a/internal/service/networkmanager/connect_attachment_test.go +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -387,7 +387,9 @@ resource "aws_networkmanager_vpc_attachment" "test" { subnet_arns = [aws_subnet.test[0].arn] core_network_id = awscc_networkmanager_core_network.test.id vpc_arn = aws_vpc.test.arn - + tags = { + segment = "shared" + } } resource "aws_networkmanager_attachment_accepter" "test" { @@ -424,7 +426,9 @@ resource "aws_networkmanager_vpc_attachment" "test" { subnet_arns = [aws_subnet.test[0].arn] core_network_id = awscc_networkmanager_core_network.test.id vpc_arn = aws_vpc.test.arn - + tags = { + segment = "shared" + } } resource "aws_networkmanager_attachment_accepter" "test" { From 206d2b89f73848d51e11655b951f7c948a84eaa8 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 17:38:38 +0200 Subject: [PATCH 07/22] Add second tag to tags test --- internal/service/networkmanager/connect_attachment_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go index d467cf7081c8..462c4d8bc5dd 100644 --- a/internal/service/networkmanager/connect_attachment_test.go +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -448,6 +448,7 @@ resource "aws_networkmanager_connect_attachment" "test" { ] tags = { %[1]q = %[2]q + %[3]q = %[4]q } } From 823259651242c0ff07ae4c9b9668b6d6447e50bf Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 11 Nov 2022 17:38:50 +0200 Subject: [PATCH 08/22] Fix expected amount of tags --- internal/service/networkmanager/connect_attachment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go index 462c4d8bc5dd..1ba9f941d5dd 100644 --- a/internal/service/networkmanager/connect_attachment_test.go +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -45,7 +45,7 @@ func TestAccNetworkManagerConnectAttachment_basic(t *testing.T) { acctest.CheckResourceAttrAccountID(resourceName, "owner_account_id"), resource.TestCheckResourceAttr(resourceName, "segment_name", "shared"), resource.TestCheckResourceAttrSet(resourceName, "state"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), ), }, { From a2f849e3000b092e3e6d778bb86dcbb03ec6e67f Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sat, 12 Nov 2022 22:41:14 +0200 Subject: [PATCH 09/22] Add documentation --- ...rkmanager_connect_attachment.html.markdown | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 website/docs/r/networkmanager_connect_attachment.html.markdown diff --git a/website/docs/r/networkmanager_connect_attachment.html.markdown b/website/docs/r/networkmanager_connect_attachment.html.markdown new file mode 100644 index 000000000000..90ac26d731c8 --- /dev/null +++ b/website/docs/r/networkmanager_connect_attachment.html.markdown @@ -0,0 +1,102 @@ +--- +subcategory: "Network Manager" +layout: "aws" +page_title: "AWS: aws_networkmanager_connect_attachment" +description: |- + Terraform resource for managing an AWS NetworkManager ConnectAttachment. +--- + +# Resource: aws_networkmanager_connect_attachment + +Terraform resource for managing an AWS NetworkManager ConnectAttachment. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_networkmanager_vpc_attachment" "example" { + subnet_arns = aws_subnet.example[*].arn + core_network_id = awscc_networkmanager_core_network.example.id + vpc_arn = aws_vpc.example.arn +} + +resource "aws_networkmanager_connect_attachment" "example" { + core_network_id = awscc_networkmanager_core_network.example.id + transport_attachment_id = aws_networkmanager_vpc_attachment.example.id + edge_location = aws_networkmanager_vpc_attachment.example.edge_location + options { + protocol = "GRE" + } +} +``` + +### Usage with attachment accepter + +```terraform +resource "aws_networkmanager_vpc_attachment" "example" { + subnet_arns = aws_subnet.example[*].arn + core_network_id = awscc_networkmanager_core_network.example.id + vpc_arn = aws_vpc.example.arn +} + +resource "aws_networkmanager_attachment_accepter" "example" { + attachment_id = aws_networkmanager_vpc_attachment.example.id + attachment_type = aws_networkmanager_vpc_attachment.example.attachment_type +} + +resource "aws_networkmanager_connect_attachment" "example" { + core_network_id = awscc_networkmanager_core_network.example.id + transport_attachment_id = aws_networkmanager_vpc_attachment.example.id + edge_location = aws_networkmanager_vpc_attachment.example.edge_location + options { + protocol = "GRE" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] +} + +resource "aws_networkmanager_attachment_accepter" "example2" { + attachment_id = aws_networkmanager_connect_attachment.example.id + attachment_type = aws_networkmanager_connect_attachment.example.attachment_type +} +``` + +## Argument Reference + +The following arguments are required: + +- `core_network_id` - (Required) The ID of a core network where you want to create the attachment. +- `transport_attachment_id` - (Required) The ID of the attachment between the two connections. +- `edge_location` - (Required) The Region where the edge is located. +- `options` - (Required) Options for creating an attachment. + +The following arguments are optional: + +- `tags` - (Optional) Key-value tags for the attachment. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `arn` - The ARN of the attachment. +- `attachment_policy_rule_number` - The policy rule number associated with the attachment. +- `attachment_type` - The type of attachment. +- `core_network_arn` - The ARN of a core network. +- `core_network_id` - The ID of a core network +- `edge_location` - The Region where the edge is located. +- `id` - The ID of the attachment. +- `owner_account_id` - The ID of the attachment account owner. +- `resource_arn` - The attachment resource ARN. +- `segment_name` - The name of the segment attachment. +- `state` - The state of the attachment. +- `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Import + +`aws_networkmanager_connect_attachment` can be imported using the attachment ID, e.g. + +``` +$ terraform import aws_networkmanager_connect_attachment.example attachment-0f8fa60d2238d1bd8 +``` From 3c4fbd12a9f81b6859448b4244b993261a0fccef Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sat, 12 Nov 2022 22:41:55 +0200 Subject: [PATCH 10/22] Remove import comment --- internal/service/networkmanager/connect_attachment.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index e07a85bc7b27..abcfc267bc2c 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -16,8 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" - - // "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" From eef0b14301574bc4acab697c2e7e2285549e62ff Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sat, 12 Nov 2022 23:26:45 +0200 Subject: [PATCH 11/22] Fix linting --- .../networkmanager/attachment_accepter.go | 1 - .../networkmanager/connect_attachment.go | 5 +- .../networkmanager/connect_attachment_test.go | 171 +++++++++--------- 3 files changed, 87 insertions(+), 90 deletions(-) diff --git a/internal/service/networkmanager/attachment_accepter.go b/internal/service/networkmanager/attachment_accepter.go index 058107595475..9bba59ebc5ee 100644 --- a/internal/service/networkmanager/attachment_accepter.go +++ b/internal/service/networkmanager/attachment_accepter.go @@ -208,7 +208,6 @@ func resourceAttachmentAccepterRead(ctx context.Context, d *schema.ResourceData, } a = connectAttachment.Attachment - } d.Set("attachment_policy_rule_number", a.AttachmentPolicyRuleNumber) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index abcfc267bc2c..8e520228c94f 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -158,7 +158,6 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData err = resource.RetryContext(ctx, d.Timeout("Create"), func() *resource.RetryError { output, err = conn.CreateConnectAttachmentWithContext(ctx, input) if err != nil { - if validationExceptionMessageContains(err, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") { return resource.RetryableError(err) } @@ -335,7 +334,7 @@ func statusConnectAttachmentState(ctx context.Context, conn *networkmanager.Netw } } -func waitConnectAttachmentCreated(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { //nolint:unparam +func waitConnectAttachmentCreated(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { stateConf := &resource.StateChangeConf{ Pending: []string{networkmanager.AttachmentStateCreating, networkmanager.AttachmentStatePendingNetworkUpdate}, Target: []string{networkmanager.AttachmentStateAvailable, networkmanager.AttachmentStatePendingAttachmentAcceptance}, @@ -370,7 +369,7 @@ func waitConnectAttachmentDeleted(ctx context.Context, conn *networkmanager.Netw return nil, err } -func waitConnectAttachmentAvailable(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { //nolint:unparam +func waitConnectAttachmentAvailable(ctx context.Context, conn *networkmanager.NetworkManager, id string, timeout time.Duration) (*networkmanager.ConnectAttachment, error) { stateConf := &resource.StateChangeConf{ Pending: []string{networkmanager.AttachmentStateCreating, networkmanager.AttachmentStatePendingNetworkUpdate, networkmanager.AttachmentStatePendingAttachmentAcceptance}, Target: []string{networkmanager.AttachmentStateAvailable}, diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go index 1ba9f941d5dd..605230537a12 100644 --- a/internal/service/networkmanager/connect_attachment_test.go +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -310,74 +310,74 @@ data "aws_networkmanager_core_network_policy_document" "test" { func testAccConnectAttachmentConfig_basic(rName string) string { return acctest.ConfigCompose(testAccConnectAttachmentConfig_base(rName), ` - resource "aws_networkmanager_vpc_attachment" "test" { - subnet_arns = aws_subnet.test[*].arn - core_network_id = awscc_networkmanager_core_network.test.id - vpc_arn = aws_vpc.test.arn - tags = { - segment = "shared" - } - } +resource "aws_networkmanager_vpc_attachment" "test" { + subnet_arns = aws_subnet.test[*].arn + core_network_id = awscc_networkmanager_core_network.test.id + vpc_arn = aws_vpc.test.arn + tags = { + segment = "shared" + } +} - resource "aws_networkmanager_attachment_accepter" "test" { - attachment_id = aws_networkmanager_vpc_attachment.test.id - attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type - } +resource "aws_networkmanager_attachment_accepter" "test" { + attachment_id = aws_networkmanager_vpc_attachment.test.id + attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type +} - resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id - transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location - options { - protocol = "GRE" - } - tags = { - segment = "shared" - } - depends_on = [ - "aws_networkmanager_attachment_accepter.test" - ] - } +resource "aws_networkmanager_connect_attachment" "test" { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + tags = { + segment = "shared" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] +} - resource "aws_networkmanager_attachment_accepter" "test2" { - attachment_id = aws_networkmanager_connect_attachment.test.id - attachment_type = aws_networkmanager_connect_attachment.test.attachment_type - } +resource "aws_networkmanager_attachment_accepter" "test2" { + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type +} `) } func testAccConnectAttachmentConfig_basic_NoDependsOn(rName string) string { return acctest.ConfigCompose(testAccConnectAttachmentConfig_base(rName), ` - resource "aws_networkmanager_vpc_attachment" "test" { - subnet_arns = aws_subnet.test[*].arn - core_network_id = awscc_networkmanager_core_network.test.id - vpc_arn = aws_vpc.test.arn - tags = { - segment = "shared" - } - } +resource "aws_networkmanager_vpc_attachment" "test" { + subnet_arns = aws_subnet.test[*].arn + core_network_id = awscc_networkmanager_core_network.test.id + vpc_arn = aws_vpc.test.arn + tags = { + segment = "shared" + } +} - resource "aws_networkmanager_attachment_accepter" "test" { - attachment_id = aws_networkmanager_vpc_attachment.test.id - attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type - } +resource "aws_networkmanager_attachment_accepter" "test" { + attachment_id = aws_networkmanager_vpc_attachment.test.id + attachment_type = aws_networkmanager_vpc_attachment.test.attachment_type +} - resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id - transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location - options { - protocol = "GRE" - } - tags = { - segment = "shared" - } - } +resource "aws_networkmanager_connect_attachment" "test" { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + tags = { + segment = "shared" + } +} - resource "aws_networkmanager_attachment_accepter" "test2" { - attachment_id = aws_networkmanager_connect_attachment.test.id - attachment_type = aws_networkmanager_connect_attachment.test.attachment_type - } +resource "aws_networkmanager_attachment_accepter" "test2" { + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type +} `) } @@ -388,8 +388,8 @@ resource "aws_networkmanager_vpc_attachment" "test" { core_network_id = awscc_networkmanager_core_network.test.id vpc_arn = aws_vpc.test.arn tags = { - segment = "shared" - } + segment = "shared" + } } resource "aws_networkmanager_attachment_accepter" "test" { @@ -398,25 +398,24 @@ resource "aws_networkmanager_attachment_accepter" "test" { } resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id - transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location - options { - protocol = "GRE" - } - depends_on = [ - "aws_networkmanager_attachment_accepter.test" - ] - tags = { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] + tags = { %[1]q = %[2]q } } resource "aws_networkmanager_attachment_accepter" "test2" { - attachment_id = aws_networkmanager_connect_attachment.test.id - attachment_type = aws_networkmanager_connect_attachment.test.attachment_type + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type } - `, tagKey1, tagValue1)) } @@ -427,8 +426,8 @@ resource "aws_networkmanager_vpc_attachment" "test" { core_network_id = awscc_networkmanager_core_network.test.id vpc_arn = aws_vpc.test.arn tags = { - segment = "shared" - } + segment = "shared" + } } resource "aws_networkmanager_attachment_accepter" "test" { @@ -437,24 +436,24 @@ resource "aws_networkmanager_attachment_accepter" "test" { } resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id - transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location - options { - protocol = "GRE" - } - depends_on = [ - "aws_networkmanager_attachment_accepter.test" - ] - tags = { + core_network_id = awscc_networkmanager_core_network.test.id + transport_attachment_id = aws_networkmanager_vpc_attachment.test.id + edge_location = aws_networkmanager_vpc_attachment.test.edge_location + options { + protocol = "GRE" + } + depends_on = [ + "aws_networkmanager_attachment_accepter.test" + ] + tags = { %[1]q = %[2]q %[3]q = %[4]q } } resource "aws_networkmanager_attachment_accepter" "test2" { - attachment_id = aws_networkmanager_connect_attachment.test.id - attachment_type = aws_networkmanager_connect_attachment.test.attachment_type + attachment_id = aws_networkmanager_connect_attachment.test.id + attachment_type = aws_networkmanager_connect_attachment.test.attachment_type } `, tagKey1, tagValue1, tagKey2, tagValue2)) } From d70a6d86d1558886809d46476fbcb0ae18b74e2f Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sat, 12 Nov 2022 23:30:44 +0200 Subject: [PATCH 12/22] Fix linting --- .../networkmanager/connect_attachment_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go index 605230537a12..7730d75917a8 100644 --- a/internal/service/networkmanager/connect_attachment_test.go +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -325,9 +325,9 @@ resource "aws_networkmanager_attachment_accepter" "test" { } resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id + core_network_id = awscc_networkmanager_core_network.test.id transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location + edge_location = aws_networkmanager_vpc_attachment.test.edge_location options { protocol = "GRE" } @@ -363,9 +363,9 @@ resource "aws_networkmanager_attachment_accepter" "test" { } resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id + core_network_id = awscc_networkmanager_core_network.test.id transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location + edge_location = aws_networkmanager_vpc_attachment.test.edge_location options { protocol = "GRE" } @@ -398,9 +398,9 @@ resource "aws_networkmanager_attachment_accepter" "test" { } resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id + core_network_id = awscc_networkmanager_core_network.test.id transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location + edge_location = aws_networkmanager_vpc_attachment.test.edge_location options { protocol = "GRE" } @@ -436,9 +436,9 @@ resource "aws_networkmanager_attachment_accepter" "test" { } resource "aws_networkmanager_connect_attachment" "test" { - core_network_id = awscc_networkmanager_core_network.test.id + core_network_id = awscc_networkmanager_core_network.test.id transport_attachment_id = aws_networkmanager_vpc_attachment.test.id - edge_location = aws_networkmanager_vpc_attachment.test.edge_location + edge_location = aws_networkmanager_vpc_attachment.test.edge_location options { protocol = "GRE" } From b846546ad7f9b242edf47fce0a14d12b782679e7 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sat, 12 Nov 2022 23:37:32 +0200 Subject: [PATCH 13/22] Fix lint --- internal/service/networkmanager/connect_attachment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/networkmanager/connect_attachment_test.go b/internal/service/networkmanager/connect_attachment_test.go index 7730d75917a8..0142240eda74 100644 --- a/internal/service/networkmanager/connect_attachment_test.go +++ b/internal/service/networkmanager/connect_attachment_test.go @@ -387,7 +387,7 @@ resource "aws_networkmanager_vpc_attachment" "test" { subnet_arns = [aws_subnet.test[0].arn] core_network_id = awscc_networkmanager_core_network.test.id vpc_arn = aws_vpc.test.arn - tags = { + tags = { segment = "shared" } } From dba0ecb6e25d1d30fc5121889205de3d5fa3f6c7 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sun, 13 Nov 2022 19:02:44 +0200 Subject: [PATCH 14/22] Fix error message --- internal/service/networkmanager/vpc_attachment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/networkmanager/vpc_attachment.go b/internal/service/networkmanager/vpc_attachment.go index 8a4482b74ac3..1cbc7f01d927 100644 --- a/internal/service/networkmanager/vpc_attachment.go +++ b/internal/service/networkmanager/vpc_attachment.go @@ -278,7 +278,7 @@ func resourceVPCAttachmentDelete(ctx context.Context, d *schema.ResourceData, me } if sErr != nil { - return diag.Errorf("reading Network Manager Site To Site VPN Attachment (%s): %s", d.Id(), sErr) + return diag.Errorf("reading Network Manager VPC Attachment (%s): %s", d.Id(), sErr) } d.Set("state", output.Attachment.State) From ab8597d0358b6bcb17048852dd0834327d378a40 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sun, 13 Nov 2022 20:26:28 +0200 Subject: [PATCH 15/22] Add TimedOut check --- internal/service/networkmanager/connect_attachment.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index 8e520228c94f..a3427c83b2e7 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -155,7 +155,7 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData log.Printf("[DEBUG] Creating Network Manager Connect Attachment: %s", input) var err error var output *networkmanager.CreateConnectAttachmentOutput - err = resource.RetryContext(ctx, d.Timeout("Create"), func() *resource.RetryError { + err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { output, err = conn.CreateConnectAttachmentWithContext(ctx, input) if err != nil { if validationExceptionMessageContains(err, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") { @@ -168,6 +168,10 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData return nil }) + if tfresource.TimedOut(err) { + output, err = conn.CreateConnectAttachmentWithContext(ctx, input) + } + if err != nil { return diag.Errorf("creating Network Manager Connect Attachment (%s) (%s): %s", transportAttachmentID, coreNetworkID, err) } From b76532ac003a88fabfd9f3f2f5905dcbb0f01776 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Mon, 14 Nov 2022 11:19:58 +0200 Subject: [PATCH 16/22] Fix website lint --- .../r/networkmanager_connect_attachment.html.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/docs/r/networkmanager_connect_attachment.html.markdown b/website/docs/r/networkmanager_connect_attachment.html.markdown index 90ac26d731c8..e70c363c2e69 100644 --- a/website/docs/r/networkmanager_connect_attachment.html.markdown +++ b/website/docs/r/networkmanager_connect_attachment.html.markdown @@ -22,9 +22,9 @@ resource "aws_networkmanager_vpc_attachment" "example" { } resource "aws_networkmanager_connect_attachment" "example" { - core_network_id = awscc_networkmanager_core_network.example.id + core_network_id = awscc_networkmanager_core_network.example.id transport_attachment_id = aws_networkmanager_vpc_attachment.example.id - edge_location = aws_networkmanager_vpc_attachment.example.edge_location + edge_location = aws_networkmanager_vpc_attachment.example.edge_location options { protocol = "GRE" } @@ -46,9 +46,9 @@ resource "aws_networkmanager_attachment_accepter" "example" { } resource "aws_networkmanager_connect_attachment" "example" { - core_network_id = awscc_networkmanager_core_network.example.id + core_network_id = awscc_networkmanager_core_network.example.id transport_attachment_id = aws_networkmanager_vpc_attachment.example.id - edge_location = aws_networkmanager_vpc_attachment.example.edge_location + edge_location = aws_networkmanager_vpc_attachment.example.edge_location options { protocol = "GRE" } From 2a107b96bed3b2748c4a2b6f94d3f82d87e7b989 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 14 Nov 2022 14:01:06 -0500 Subject: [PATCH 17/22] Add CHANGELOG entry. --- .changelog/27787.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/27787.txt diff --git a/.changelog/27787.txt b/.changelog/27787.txt new file mode 100644 index 000000000000..86c0e3b517cc --- /dev/null +++ b/.changelog/27787.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_networkmanager_connect_attachment +``` \ No newline at end of file From 9e56e616d7b458ab2b8a219379d176023c00393e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 14 Nov 2022 14:01:39 -0500 Subject: [PATCH 18/22] Alphabetic order for resources. --- internal/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 5c91586b3324..7bb4e77b13c0 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1802,6 +1802,7 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_networkfirewall_rule_group": networkfirewall.ResourceRuleGroup(), "aws_networkmanager_attachment_accepter": networkmanager.ResourceAttachmentAccepter(), + "aws_networkmanager_connect_attachment": networkmanager.ResourceConnectAttachment(), "aws_networkmanager_connection": networkmanager.ResourceConnection(), "aws_networkmanager_customer_gateway_association": networkmanager.ResourceCustomerGatewayAssociation(), "aws_networkmanager_device": networkmanager.ResourceDevice(), @@ -1815,7 +1816,6 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_networkmanager_transit_gateway_route_table_attachment": networkmanager.ResourceTransitGatewayRouteTableAttachment(), "aws_networkmanager_vpc_attachment": networkmanager.ResourceVPCAttachment(), "aws_networkmanager_site_to_site_vpn_attachment": networkmanager.ResourceSiteToSiteVPNAttachment(), - "aws_networkmanager_connect_attachment": networkmanager.ResourceConnectAttachment(), "aws_opensearch_domain": opensearch.ResourceDomain(), "aws_opensearch_domain_policy": opensearch.ResourceDomainPolicy(), From 526a9bbbd64fae4b9a74c7267597c91510cd8cd6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 14 Nov 2022 14:05:35 -0500 Subject: [PATCH 19/22] r/aws_networkmanager_connect_attachment: Alphabetize attributes. --- .../networkmanager/connect_attachment.go | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index a3427c83b2e7..9082467d1803 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -79,6 +79,20 @@ func ResourceConnectAttachment() *schema.Resource { validation.StringMatch(regexp.MustCompile(`[\s\S]*`), "Anything but whitespace"), ), }, + "options": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"GRE"}, false), + }, + }, + }, + }, "owner_account_id": { Type: schema.TypeString, Computed: true, @@ -106,20 +120,6 @@ func ResourceConnectAttachment() *schema.Resource { validation.StringMatch(regexp.MustCompile(`^attachment-([0-9a-f]{8,17})$`), "Must start with attachment- and then have 8 to 17 characters"), ), }, - "options": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "protocol": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"GRE"}, false), - }, - }, - }, - }, }, } } From 4cf588277134a13ac0020167eabdfa087513c6a5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 14 Nov 2022 14:11:19 -0500 Subject: [PATCH 20/22] r/aws_networkmanager_connect_attachment: Cosmetics. --- .../networkmanager/connect_attachment.go | 56 +++++++------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index 9082467d1803..f5ae963d0586 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -133,7 +133,6 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData edgeLocation := d.Get("edge_location").(string) transportAttachmentID := d.Get("transport_attachment_id").(string) options := &networkmanager.ConnectAttachmentOptions{} - if v, ok := d.GetOk("options"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { options = expandConnectOptions(v.([]interface{})[0].(map[string]interface{})) } @@ -141,8 +140,8 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData input := &networkmanager.CreateConnectAttachmentInput{ CoreNetworkId: aws.String(coreNetworkID), EdgeLocation: aws.String(edgeLocation), - TransportAttachmentId: aws.String(transportAttachmentID), Options: options, + TransportAttachmentId: aws.String(transportAttachmentID), } if len(tags) > 0 { @@ -153,30 +152,16 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData // Waiting for Create Timeout period for VPC Attachment to come available state. // Only needed if depends_on statement is not used in Connect attachment log.Printf("[DEBUG] Creating Network Manager Connect Attachment: %s", input) - var err error - var output *networkmanager.CreateConnectAttachmentOutput - err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { - output, err = conn.CreateConnectAttachmentWithContext(ctx, input) - if err != nil { - if validationExceptionMessageContains(err, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - output, err = conn.CreateConnectAttachmentWithContext(ctx, input) - } + outputRaw, err := tfresource.RetryWhenAWSErrMessageContainsContext(ctx, d.Timeout(schema.TimeoutCreate), func() (interface{}, error) { + return conn.CreateConnectAttachmentWithContext(ctx, input) + }, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") if err != nil { return diag.Errorf("creating Network Manager Connect Attachment (%s) (%s): %s", transportAttachmentID, coreNetworkID, err) } - d.SetId(aws.StringValue(output.ConnectAttachment.Attachment.AttachmentId)) + d.SetId(aws.StringValue(outputRaw.(*networkmanager.CreateConnectAttachmentOutput).ConnectAttachment.Attachment.AttachmentId)) if _, err := waitConnectAttachmentCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return diag.Errorf("waiting for Network Manager Connect Attachment (%s) create: %s", d.Id(), err) @@ -215,12 +200,7 @@ func resourceConnectAttachmentRead(ctx context.Context, d *schema.ResourceData, d.Set("attachment_type", a.AttachmentType) d.Set("core_network_arn", a.CoreNetworkArn) d.Set("core_network_id", a.CoreNetworkId) - d.Set("owner_account_id", a.OwnerAccountId) - d.Set("resource_arn", a.ResourceArn) - d.Set("segment_name", a.SegmentName) - d.Set("state", a.State) d.Set("edge_location", a.EdgeLocation) - d.Set("transport_attachment_id", connectAttachment.TransportAttachmentId) if connectAttachment.Options != nil { if err := d.Set("options", []interface{}{flattenConnectOptions(connectAttachment.Options)}); err != nil { return diag.Errorf("setting options: %s", err) @@ -228,12 +208,17 @@ func resourceConnectAttachmentRead(ctx context.Context, d *schema.ResourceData, } else { d.Set("options", nil) } + d.Set("owner_account_id", a.OwnerAccountId) + d.Set("resource_arn", a.ResourceArn) + d.Set("segment_name", a.SegmentName) + d.Set("state", a.State) + d.Set("transport_attachment_id", connectAttachment.TransportAttachmentId) tags := KeyValueTags(a.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return diag.Errorf("Setting tags: %s", err) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { @@ -250,7 +235,7 @@ func resourceConnectAttachmentUpdate(ctx context.Context, d *schema.ResourceData o, n := d.GetChange("tags_all") if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return diag.FromErr(fmt.Errorf("error updating Network Manager Connect Attachment (%s) tags: %s", d.Get("arn").(string), err)) + return diag.FromErr(fmt.Errorf("updating Network Manager Connect Attachment (%s) tags: %s", d.Get("arn").(string), err)) } } @@ -262,23 +247,22 @@ func resourceConnectAttachmentDelete(ctx context.Context, d *schema.ResourceData // If ResourceAttachmentAccepter is used, then Connect Attachment state // is never updated from StatePendingAttachmentAcceptance and the delete fails - output, sErr := FindConnectAttachmentByID(ctx, conn, d.Id()) - if tfawserr.ErrCodeEquals(sErr, networkmanager.ErrCodeResourceNotFoundException) { + output, err := FindConnectAttachmentByID(ctx, conn, d.Id()) + + if tfawserr.ErrCodeEquals(err, networkmanager.ErrCodeResourceNotFoundException) { return nil } - if sErr != nil { - return diag.Errorf("deleting Network Manager Connect Attachment (%s): %s", d.Id(), sErr) + if err != nil { + return diag.Errorf("reading Network Manager Connect Attachment (%s): %s", d.Id(), err) } - d.Set("state", output.Attachment.State) - - if state := d.Get("state").(string); state == networkmanager.AttachmentStatePendingAttachmentAcceptance || state == networkmanager.AttachmentStatePendingTagAcceptance { - return diag.Errorf("cannot delete Network Manager Connect Attachment (%s) in %s state", d.Id(), state) + if state := aws.StringValue(output.Attachment.State); state == networkmanager.AttachmentStatePendingAttachmentAcceptance || state == networkmanager.AttachmentStatePendingTagAcceptance { + return diag.Errorf("cannot delete Network Manager Connect Attachment (%s) in state: %s", d.Id(), state) } log.Printf("[DEBUG] Deleting Network Manager Connect Attachment: %s", d.Id()) - _, err := conn.DeleteAttachmentWithContext(ctx, &networkmanager.DeleteAttachmentInput{ + _, err = conn.DeleteAttachmentWithContext(ctx, &networkmanager.DeleteAttachmentInput{ AttachmentId: aws.String(d.Id()), }) From b3fc1e662248075a919f13aae6c108f5566f97ef Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 14 Nov 2022 17:38:26 -0500 Subject: [PATCH 21/22] r/aws_networkmanager_connect_attachment: Tweak timeout values. --- internal/service/networkmanager/connect_attachment.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index f5ae963d0586..def6d793e7e3 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -35,8 +35,7 @@ func ResourceConnectAttachment() *schema.Resource { CustomizeDiff: verify.SetTagsDiff, Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(10 * time.Minute), - Update: schema.DefaultTimeout(10 * time.Minute), + Create: schema.DefaultTimeout(15 * time.Minute), Delete: schema.DefaultTimeout(10 * time.Minute), }, From 2c4353c7fc5d452ca6cabdeeee1bd13bb48dacc8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 14 Nov 2022 18:06:08 -0500 Subject: [PATCH 22/22] r/aws_networkmanager_connect_attachment: Use 'validationExceptionMessageContains' when retrying resource creation. --- .../networkmanager/connect_attachment.go | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/internal/service/networkmanager/connect_attachment.go b/internal/service/networkmanager/connect_attachment.go index def6d793e7e3..0607c8d0c043 100644 --- a/internal/service/networkmanager/connect_attachment.go +++ b/internal/service/networkmanager/connect_attachment.go @@ -35,7 +35,7 @@ func ResourceConnectAttachment() *schema.Resource { CustomizeDiff: verify.SetTagsDiff, Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(15 * time.Minute), + Create: schema.DefaultTimeout(10 * time.Minute), Delete: schema.DefaultTimeout(10 * time.Minute), }, @@ -147,14 +147,34 @@ func resourceConnectAttachmentCreate(ctx context.Context, d *schema.ResourceData input.Tags = Tags(tags.IgnoreAWS()) } - // Connect attachment doesn't have direct dependency to VPC attachment state when using Attachment Accepter. - // Waiting for Create Timeout period for VPC Attachment to come available state. - // Only needed if depends_on statement is not used in Connect attachment - log.Printf("[DEBUG] Creating Network Manager Connect Attachment: %s", input) - - outputRaw, err := tfresource.RetryWhenAWSErrMessageContainsContext(ctx, d.Timeout(schema.TimeoutCreate), func() (interface{}, error) { - return conn.CreateConnectAttachmentWithContext(ctx, input) - }, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") + outputRaw, err := tfresource.RetryWhenContext(ctx, d.Timeout(schema.TimeoutCreate), + func() (interface{}, error) { + return conn.CreateConnectAttachmentWithContext(ctx, input) + }, + func(err error) (bool, error) { + // Connect attachment doesn't have direct dependency to VPC attachment state when using Attachment Accepter. + // Waiting for Create Timeout period for VPC Attachment to come available state. + // Only needed if depends_on statement is not used in Connect attachment. + // + // ValidationException: Incorrect input. + // { + // RespMetadata: { + // StatusCode: 400, + // RequestID: "0a711cf7-2210-40c9-a170-a4c42134e195" + // }, + // Fields: [{ + // Message: "Transport attachment state is invalid.", + // Name: "transportAttachmentId" + // }], + // Message_: "Incorrect input.", + // Reason: "FieldValidationFailed" + // } + if validationExceptionMessageContains(err, networkmanager.ValidationExceptionReasonFieldValidationFailed, "Transport attachment state is invalid.") { + return true, err + } + + return false, err + }) if err != nil { return diag.Errorf("creating Network Manager Connect Attachment (%s) (%s): %s", transportAttachmentID, coreNetworkID, err)