diff --git a/.changelog/31735.txt b/.changelog/31735.txt new file mode 100644 index 000000000000..c683a3f7a205 --- /dev/null +++ b/.changelog/31735.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_dx_connection: Convert `vlan_id` from [`TypeString`](https://developer.hashicorp.com/terraform/plugin/sdkv2/schemas/schema-types#typestring) to [`TypeInt`](https://developer.hashicorp.com/terraform/plugin/sdkv2/schemas/schema-types#typeint) in [Terraform state](https://developer.hashicorp.com/terraform/language/state) for existing resources. This fixes a regression introduced in [v5.1.0](https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md#510-june--1-2023) causing `a number is required` errors +``` \ No newline at end of file diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 364403dfb3cc..c1e84ddb5d26 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "strconv" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" @@ -23,15 +24,128 @@ import ( // @SDKResource("aws_dx_connection", name="Connection") // @Tags(identifierAttribute="arn") func ResourceConnection() *schema.Resource { + // Resource with v0 schema (provider v5.0.1). + resourceV0 := &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_device": { + Type: schema.TypeString, + Computed: true, + }, + "bandwidth": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validConnectionBandWidth(), + }, + // The MAC Security (MACsec) connection encryption mode. + "encryption_mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"no_encrypt", "should_encrypt", "must_encrypt"}, false), + }, + "has_logical_redundancy": { + Type: schema.TypeString, + Computed: true, + }, + "jumbo_frame_capable": { + Type: schema.TypeBool, + Computed: true, + }, + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + // Indicates whether the connection supports MAC Security (MACsec). + "macsec_capable": { + Type: schema.TypeBool, + Computed: true, + }, + // Enable or disable MAC Security (MACsec) on this connection. + "request_macsec": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "owner_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "partner_name": { + Type: schema.TypeString, + Computed: true, + }, + "port_encryption_status": { + Type: schema.TypeString, + Computed: true, + }, + "provider_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "skip_destroy": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + "vlan_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } + return &schema.Resource{ CreateWithoutTimeout: resourceConnectionCreate, ReadWithoutTimeout: resourceConnectionRead, UpdateWithoutTimeout: resourceConnectionUpdate, DeleteWithoutTimeout: resourceConnectionDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: resourceV0.CoreConfigSchema().ImpliedType(), + Upgrade: func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + // Convert vlan_id from string to int. + if v, ok := rawState["vlan_id"]; ok { + if v, ok := v.(string); ok { + if v == "" { + rawState["vlan_id"] = 0 + } else { + if v, err := strconv.Atoi(v); err == nil { + rawState["vlan_id"] = v + } else { + return nil, err + } + } + } + } + + return rawState, nil + }, + Version: 0, + }, + }, + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index 1d94b7f24f73..3a9022cfe8ab 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -283,6 +283,78 @@ func TestAccDirectConnectConnection_tags(t *testing.T) { }) } +// https://github.com/hashicorp/terraform-provider-aws/issues/31732. +func TestAccDirectConnectConnection_vlanIDMigration501(t *testing.T) { + ctx := acctest.Context(t) + var connection directconnect.Connection + resourceName := "aws_dx_connection.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + CheckDestroy: testAccCheckConnectionDestroy(ctx), + Steps: []resource.TestStep{ + { + // At v5.0.1 the resource's schema is v0 and vlan_id is TypeString. + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.0.1", + }, + }, + Config: testAccConnectionConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckConnectionExists(ctx, resourceName, &connection), + resource.TestCheckResourceAttr(resourceName, "vlan_id", ""), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccConnectionConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckConnectionExists(ctx, resourceName, &connection), + resource.TestCheckResourceAttr(resourceName, "vlan_id", "0"), + ), + }, + }, + }) +} + +func TestAccDirectConnectConnection_vlanIDMigration510(t *testing.T) { + ctx := acctest.Context(t) + var connection directconnect.Connection + resourceName := "aws_dx_connection.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, directconnect.EndpointsID), + CheckDestroy: testAccCheckConnectionDestroy(ctx), + Steps: []resource.TestStep{ + { + // At v5.1.0 the resource's schema is v0 and vlan_id is TypeInt. + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.1.0", + }, + }, + Config: testAccConnectionConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckConnectionExists(ctx, resourceName, &connection), + resource.TestCheckResourceAttr(resourceName, "vlan_id", "0"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccConnectionConfig_basic(rName), + PlanOnly: true, + }, + }, + }) +} + func testAccCheckConnectionDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn()