diff --git a/.changelog/45064.txt b/.changelog/45064.txt new file mode 100644 index 000000000000..07474158130b --- /dev/null +++ b/.changelog/45064.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_ec2_serial_console_access: Add `region` argument +``` \ No newline at end of file diff --git a/internal/generate/servicepackage/service_package_gen.go.gtpl b/internal/generate/servicepackage/service_package_gen.go.gtpl index 33b47627e831..71da93ebe911 100644 --- a/internal/generate/servicepackage/service_package_gen.go.gtpl +++ b/internal/generate/servicepackage/service_package_gen.go.gtpl @@ -32,12 +32,8 @@ inttypes.StringIdentityAttribute( {{- if .IdentityVersion }} inttypes.WithVersion({{ .IdentityVersion }}), {{ end -}} -{{- if gt (len .SDKv2IdentityUpgraders) 0 }} - inttypes.WithSDKv2IdentityUpgraders( - {{- range .SDKv2IdentityUpgraders -}} - {{.}}, - {{- end -}} - ), +{{- if gt (len .SDKv2IdentityUpgraders) 0 -}} + inttypes.WithSDKv2IdentityUpgraders({{- range .SDKv2IdentityUpgraders -}}{{.}},{{- end -}}), {{ end -}} {{- end }} diff --git a/internal/service/ec2/ec2_image_block_public_access.go b/internal/service/ec2/ec2_image_block_public_access.go index b4c2e957a6e5..a9445d0cb4c8 100644 --- a/internal/service/ec2/ec2_image_block_public_access.go +++ b/internal/service/ec2/ec2_image_block_public_access.go @@ -30,6 +30,8 @@ import ( // @Testing(hasExistsFunction=false) // @Testing(generator=false) // @Testing(identityTest=false) +// @Testing(identityVersion="0;v6.0.0") +// @Testing(identityVersion="1;v6.21.0") func resourceImageBlockPublicAccess() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceImageBlockPublicAccessPut, diff --git a/internal/service/ec2/ec2_serial_console_access.go b/internal/service/ec2/ec2_serial_console_access.go index 8ec288695778..b2762e1c2453 100644 --- a/internal/service/ec2/ec2_serial_console_access.go +++ b/internal/service/ec2/ec2_serial_console_access.go @@ -5,21 +5,26 @@ package ec2 import ( "context" + "log" "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_ec2_serial_console_access", name="Serial Console Access") -// @Region(global=true) // @SingletonIdentity +// @IdentityVersion(1, sdkV2IdentityUpgraders="serialConsoleAccessIdentityUpgradeV0") // @V60SDKv2Fix // @Testing(hasExistsFunction=false) // @Testing(generator=false) +// @Testing(identityTest=false) +// @Testing(identityVersion="0;v6.0.0") +// @Testing(identityVersion="1;v6.21.0") func resourceSerialConsoleAccess() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceSerialConsoleAccessCreate, @@ -39,7 +44,6 @@ func resourceSerialConsoleAccess() *schema.Resource { func resourceSerialConsoleAccessCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EC2Client(ctx) enabled := d.Get(names.AttrEnabled).(bool) @@ -47,18 +51,22 @@ func resourceSerialConsoleAccessCreate(ctx context.Context, d *schema.ResourceDa return sdkdiag.AppendErrorf(diags, "setting EC2 Serial Console Access (%t): %s", enabled, err) } - d.SetId(meta.(*conns.AWSClient).AccountID(ctx)) + d.SetId(meta.(*conns.AWSClient).Region(ctx)) return append(diags, resourceSerialConsoleAccessRead(ctx, d, meta)...) } func resourceSerialConsoleAccessRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EC2Client(ctx) - input := ec2.GetSerialConsoleAccessStatusInput{} - output, err := conn.GetSerialConsoleAccessStatus(ctx, &input) + output, err := findSerialConsoleAccessStatus(ctx, conn) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EC2 Serial Console Access %s not found, removing from state", d.Id()) + d.SetId("") + return diags + } if err != nil { return sdkdiag.AppendErrorf(diags, "reading EC2 Serial Console Access: %s", err) @@ -71,7 +79,6 @@ func resourceSerialConsoleAccessRead(ctx context.Context, d *schema.ResourceData func resourceSerialConsoleAccessUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EC2Client(ctx) enabled := d.Get(names.AttrEnabled).(bool) @@ -84,7 +91,6 @@ func resourceSerialConsoleAccessUpdate(ctx context.Context, d *schema.ResourceDa func resourceSerialConsoleAccessDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EC2Client(ctx) // Removing the resource disables serial console access. @@ -99,12 +105,20 @@ func setSerialConsoleAccess(ctx context.Context, conn *ec2.Client, enabled bool) var err error if enabled { - input := ec2.EnableSerialConsoleAccessInput{} + var input ec2.EnableSerialConsoleAccessInput _, err = conn.EnableSerialConsoleAccess(ctx, &input) } else { - input := ec2.DisableSerialConsoleAccessInput{} + var input ec2.DisableSerialConsoleAccessInput _, err = conn.DisableSerialConsoleAccess(ctx, &input) } return err } + +var serialConsoleAccessIdentityUpgradeV0 = schema.IdentityUpgrader{ + Version: 0, + Upgrade: func(ctx context.Context, rawState map[string]any, meta any) (map[string]any, error) { + rawState[names.AttrRegion] = meta.(*conns.AWSClient).Region(ctx) + return rawState, nil + }, +} diff --git a/internal/service/ec2/ec2_serial_console_access_data_source.go b/internal/service/ec2/ec2_serial_console_access_data_source.go index 4053a5168feb..5ec4a8408179 100644 --- a/internal/service/ec2/ec2_serial_console_access_data_source.go +++ b/internal/service/ec2/ec2_serial_console_access_data_source.go @@ -7,7 +7,6 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -34,11 +33,9 @@ func dataSourceSerialConsoleAccess() *schema.Resource { } func dataSourceSerialConsoleAccessRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EC2Client(ctx) - input := ec2.GetSerialConsoleAccessStatusInput{} - output, err := conn.GetSerialConsoleAccessStatus(ctx, &input) + output, err := findSerialConsoleAccessStatus(ctx, conn) if err != nil { return sdkdiag.AppendErrorf(diags, "reading EC2 Serial Console Access: %s", err) diff --git a/internal/service/ec2/ec2_serial_console_access_identity_gen_test.go b/internal/service/ec2/ec2_serial_console_access_identity_gen_test.go deleted file mode 100644 index cc61b3041477..000000000000 --- a/internal/service/ec2/ec2_serial_console_access_identity_gen_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Code generated by internal/generate/identitytests/main.go; DO NOT EDIT. - -package ec2_test - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-testing/config" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/knownvalue" - "github.com/hashicorp/terraform-plugin-testing/plancheck" - "github.com/hashicorp/terraform-plugin-testing/statecheck" - "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" - "github.com/hashicorp/terraform-plugin-testing/tfversion" - "github.com/hashicorp/terraform-provider-aws/internal/acctest" - tfknownvalue "github.com/hashicorp/terraform-provider-aws/internal/acctest/knownvalue" - tfstatecheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/statecheck" - "github.com/hashicorp/terraform-provider-aws/names" -) - -func testAccEC2SerialConsoleAccess_IdentitySerial(t *testing.T) { - t.Helper() - - testCases := map[string]func(t *testing.T){ - acctest.CtBasic: testAccEC2SerialConsoleAccess_Identity_Basic, - "ExistingResource": testAccEC2SerialConsoleAccess_Identity_ExistingResource, - "ExistingResourceNoRefresh": testAccEC2SerialConsoleAccess_Identity_ExistingResource_NoRefresh_NoChange, - } - - acctest.RunSerialTests1Level(t, testCases, 0) -} - -func testAccEC2SerialConsoleAccess_Identity_Basic(t *testing.T) { - ctx := acctest.Context(t) - - resourceName := "aws_ec2_serial_console_access.test" - - acctest.Test(ctx, t, resource.TestCase{ - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(tfversion.Version1_12_0), - }, - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), - CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Steps: []resource.TestStep{ - // Step 1: Setup - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), - ConfigVariables: config.Variables{}, - ConfigStateChecks: []statecheck.StateCheck{ - statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), tfknownvalue.AccountID()), - statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ - names.AttrAccountID: tfknownvalue.AccountID(), - }), - }, - }, - - // Step 2: Import command - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), - ConfigVariables: config.Variables{}, - ImportStateKind: resource.ImportCommandWithID, - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - - // Step 3: Import block with Import ID - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), - ConfigVariables: config.Variables{}, - ResourceName: resourceName, - ImportState: true, - ImportStateKind: resource.ImportBlockWithID, - ImportPlanChecks: resource.ImportPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), tfknownvalue.AccountID()), - }, - }, - }, - - // Step 4: Import block with Resource Identity - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), - ConfigVariables: config.Variables{}, - ResourceName: resourceName, - ImportState: true, - ImportStateKind: resource.ImportBlockWithResourceIdentity, - ImportPlanChecks: resource.ImportPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), tfknownvalue.AccountID()), - }, - }, - }, - }, - }) -} - -func testAccEC2SerialConsoleAccess_Identity_ExistingResource(t *testing.T) { - ctx := acctest.Context(t) - - resourceName := "aws_ec2_serial_console_access.test" - - acctest.Test(ctx, t, resource.TestCase{ - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(tfversion.Version1_12_0), - }, - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), - CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), - Steps: []resource.TestStep{ - // Step 1: Create pre-Identity - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v5.100.0/"), - ConfigVariables: config.Variables{}, - ConfigStateChecks: []statecheck.StateCheck{ - tfstatecheck.ExpectNoIdentity(resourceName), - }, - }, - - // Step 2: v6.0 Identity error - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v6.0.0/"), - ConfigVariables: config.Variables{}, - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), - }, - PostApplyPostRefresh: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), - }, - }, - ConfigStateChecks: []statecheck.StateCheck{ - statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ - names.AttrAccountID: knownvalue.Null(), - }), - }, - }, - - // Step 3: Current version - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), - ConfigVariables: config.Variables{}, - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), - }, - PostApplyPostRefresh: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), - }, - }, - ConfigStateChecks: []statecheck.StateCheck{ - statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ - names.AttrAccountID: tfknownvalue.AccountID(), - }), - }, - }, - }, - }) -} - -func testAccEC2SerialConsoleAccess_Identity_ExistingResource_NoRefresh_NoChange(t *testing.T) { - ctx := acctest.Context(t) - - resourceName := "aws_ec2_serial_console_access.test" - - acctest.Test(ctx, t, resource.TestCase{ - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(tfversion.Version1_12_0), - }, - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), - CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), - AdditionalCLIOptions: &resource.AdditionalCLIOptions{ - Plan: resource.PlanOptions{ - NoRefresh: true, - }, - }, - Steps: []resource.TestStep{ - // Step 1: Create pre-Identity - { - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v5.100.0/"), - ConfigVariables: config.Variables{}, - ConfigStateChecks: []statecheck.StateCheck{ - tfstatecheck.ExpectNoIdentity(resourceName), - }, - }, - - // Step 2: Current version - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), - ConfigVariables: config.Variables{}, - }, - }, - }) -} diff --git a/internal/service/ec2/ec2_serial_console_access_identity_test.go b/internal/service/ec2/ec2_serial_console_access_identity_test.go new file mode 100644 index 000000000000..4096d880a6c4 --- /dev/null +++ b/internal/service/ec2/ec2_serial_console_access_identity_test.go @@ -0,0 +1,422 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/compare" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfknownvalue "github.com/hashicorp/terraform-provider-aws/internal/acctest/knownvalue" + tfstatecheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/statecheck" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// This code was initially generated by internal/generate/identitytests/main.go and then +// modified by hand to deal with resource identity schema migration. + +func testAccEC2SerialConsoleAccess_IdentitySerial(t *testing.T) { + t.Helper() + + testCases := map[string]func(t *testing.T){ + acctest.CtBasic: testAccEC2SerialConsoleAccess_Identity_Basic, + "ExistingResource": testAccEC2SerialConsoleAccess_Identity_ExistingResource, + "ExistingResourceNoRefresh": testAccEC2SerialConsoleAccess_Identity_ExistingResource_NoRefresh_NoChange, + "RegionOverride": testAccEC2SerialConsoleAccess_Identity_RegionOverride, + "Upgrade": testAccEC2SerialConsoleAccess_Identity_Upgrade, + "UpgradeNoRefresh": testAccEC2SerialConsoleAccess_Identity_Upgrade_NoRefresh, + } + + acctest.RunSerialTests1Level(t, testCases, 0) +} + +func testAccEC2SerialConsoleAccess_Identity_Basic(t *testing.T) { + ctx := acctest.Context(t) + + resourceName := "aws_ec2_serial_console_access.test" + + acctest.Test(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + // Step 1: Setup + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.CompareValuePairs(resourceName, tfjsonpath.New(names.AttrID), resourceName, tfjsonpath.New(names.AttrRegion), compare.ValuesSame()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.Region())), + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrRegion: knownvalue.StringExact(acctest.Region()), + }), + }, + }, + + // Step 2: Import command + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ImportStateKind: resource.ImportCommandWithID, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + + // Step 3: Import block with Import ID + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithID, + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), knownvalue.StringExact(acctest.Region())), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.Region())), + }, + }, + }, + + // Step 4: Import block with Resource Identity + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithResourceIdentity, + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), knownvalue.StringExact(acctest.Region())), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.Region())), + }, + }, + }, + }, + }) +} + +func testAccEC2SerialConsoleAccess_Identity_RegionOverride(t *testing.T) { + ctx := acctest.Context(t) + + resourceName := "aws_ec2_serial_console_access.test" + + acctest.Test(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + CheckDestroy: acctest.CheckDestroyNoop, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + // Step 1: Setup + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/region_override/"), + ConfigVariables: config.Variables{ + "region": config.StringVariable(acctest.AlternateRegion()), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.CompareValuePairs(resourceName, tfjsonpath.New(names.AttrID), resourceName, tfjsonpath.New(names.AttrRegion), compare.ValuesSame()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.AlternateRegion())), + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrRegion: knownvalue.StringExact(acctest.AlternateRegion()), + }), + }, + }, + + // Step 2: Import command with appended "@" + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/region_override/"), + ConfigVariables: config.Variables{ + "region": config.StringVariable(acctest.AlternateRegion()), + }, + ImportStateKind: resource.ImportCommandWithID, + ImportStateIdFunc: acctest.CrossRegionImportStateIdFunc(resourceName), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + + // Step 3: Import command without appended "@" + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/region_override/"), + ConfigVariables: config.Variables{ + "region": config.StringVariable(acctest.AlternateRegion()), + }, + ImportStateKind: resource.ImportCommandWithID, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + + // Step 4: Import block with Import ID and appended "@" + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/region_override/"), + ConfigVariables: config.Variables{ + "region": config.StringVariable(acctest.AlternateRegion()), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithID, + ImportStateIdFunc: acctest.CrossRegionImportStateIdFunc(resourceName), + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), knownvalue.StringExact(acctest.AlternateRegion())), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.AlternateRegion())), + }, + }, + }, + + // Step 5: Import block with Import ID and no appended "@" + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/region_override/"), + ConfigVariables: config.Variables{ + "region": config.StringVariable(acctest.AlternateRegion()), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithID, + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), knownvalue.StringExact(acctest.AlternateRegion())), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.AlternateRegion())), + }, + }, + }, + + // Step 6: Import block with Resource Identity + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/region_override/"), + ConfigVariables: config.Variables{ + "region": config.StringVariable(acctest.AlternateRegion()), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithResourceIdentity, + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrID), knownvalue.StringExact(acctest.AlternateRegion())), + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrRegion), knownvalue.StringExact(acctest.AlternateRegion())), + }, + }, + }, + }, + }) +} + +func testAccEC2SerialConsoleAccess_Identity_ExistingResource(t *testing.T) { + ctx := acctest.Context(t) + + resourceName := "aws_ec2_serial_console_access.test" + + acctest.Test(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), + Steps: []resource.TestStep{ + // Step 1: Create pre-Identity + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v5.100.0/"), + ConfigVariables: config.Variables{}, + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectNoIdentity(resourceName), + }, + }, + + // Step 2: v6.0 Identity error + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v6.0.0/"), + ConfigVariables: config.Variables{}, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: knownvalue.Null(), + }), + }, + }, + + // Step 3: Current version + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrRegion: knownvalue.StringExact(acctest.Region()), + }), + }, + }, + }, + }) +} + +func testAccEC2SerialConsoleAccess_Identity_ExistingResource_NoRefresh_NoChange(t *testing.T) { + ctx := acctest.Context(t) + + resourceName := "aws_ec2_serial_console_access.test" + + acctest.Test(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), + AdditionalCLIOptions: &resource.AdditionalCLIOptions{ + Plan: resource.PlanOptions{ + NoRefresh: true, + }, + }, + Steps: []resource.TestStep{ + // Step 1: Create pre-Identity + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v5.100.0/"), + ConfigVariables: config.Variables{}, + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectNoIdentity(resourceName), + }, + }, + + // Step 2: Current version + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + }, + }, + }) +} + +// Resource Identity version 1 was added in version 6.21.0 +func testAccEC2SerialConsoleAccess_Identity_Upgrade(t *testing.T) { + ctx := acctest.Context(t) + + resourceName := "aws_ec2_serial_console_access.test" + + acctest.Test(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), + Steps: []resource.TestStep{ + // Step 1: Create with Identity version 0 + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v6.20.0/"), + ConfigVariables: config.Variables{}, + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectHasIdentity(resourceName), + }, + }, + + // Step 2: Current version + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrRegion: knownvalue.StringExact(acctest.Region()), + }), + }, + }, + }, + }) +} + +// Resource Identity version 1 was added in version 6.21.0 +func testAccEC2SerialConsoleAccess_Identity_Upgrade_NoRefresh(t *testing.T) { + ctx := acctest.Context(t) + + resourceName := "aws_ec2_serial_console_access.test" + + acctest.Test(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID), + CheckDestroy: testAccCheckSerialConsoleAccessDestroy(ctx), + AdditionalCLIOptions: &resource.AdditionalCLIOptions{ + Plan: resource.PlanOptions{ + NoRefresh: true, + }, + }, + Steps: []resource.TestStep{ + // Step 1: Create with Identity version 0 + { + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic_v6.20.0/"), + ConfigVariables: config.Variables{}, + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectHasIdentity(resourceName), + }, + }, + + // Step 2: Current version + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/SerialConsoleAccess/basic/"), + ConfigVariables: config.Variables{}, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + // New "region" attribute. + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrRegion: knownvalue.StringExact(acctest.Region()), + }), + }, + }, + }, + }) +} diff --git a/internal/service/ec2/ec2_serial_console_access_test.go b/internal/service/ec2/ec2_serial_console_access_test.go index 4e50814d5950..8895ba94b98f 100644 --- a/internal/service/ec2/ec2_serial_console_access_test.go +++ b/internal/service/ec2/ec2_serial_console_access_test.go @@ -9,11 +9,12 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -70,14 +71,18 @@ func testAccCheckSerialConsoleAccessDestroy(ctx context.Context) resource.TestCh return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Client(ctx) - input := ec2.GetSerialConsoleAccessStatusInput{} - response, err := conn.GetSerialConsoleAccessStatus(ctx, &input) + output, err := tfec2.FindSerialConsoleAccessStatus(ctx, conn) + + if tfresource.NotFound(err) { + return nil + } + if err != nil { return err } - if aws.ToBool(response.SerialConsoleAccessEnabled) != false { - return fmt.Errorf("Serial console access not disabled on resource removal") + if aws.ToBool(output.SerialConsoleAccessEnabled) != false { + return fmt.Errorf("EC2 Serial Console Access not disabled on resource removal") } return nil @@ -86,25 +91,21 @@ func testAccCheckSerialConsoleAccessDestroy(ctx context.Context) resource.TestCh func testAccCheckSerialConsoleAccess(ctx context.Context, n string, enabled bool) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] + _, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Client(ctx) - input := ec2.GetSerialConsoleAccessStatusInput{} - response, err := conn.GetSerialConsoleAccessStatus(ctx, &input) + output, err := tfec2.FindSerialConsoleAccessStatus(ctx, conn) + if err != nil { return err } - if aws.ToBool(response.SerialConsoleAccessEnabled) != enabled { - return fmt.Errorf("Serial console access is not in expected state (%t)", enabled) + if aws.ToBool(output.SerialConsoleAccessEnabled) != enabled { + return fmt.Errorf("EC2 Serial Console Access is not in expected state (%t)", enabled) } return nil diff --git a/internal/service/ec2/exports_test.go b/internal/service/ec2/exports_test.go index 35c7bff298e1..7ce9fdfd526c 100644 --- a/internal/service/ec2/exports_test.go +++ b/internal/service/ec2/exports_test.go @@ -212,6 +212,7 @@ var ( FindSecurityGroupEgressRuleByID = findSecurityGroupEgressRuleByID FindSecurityGroupIngressRuleByID = findSecurityGroupIngressRuleByID FindSecurityGroupVPCAssociationByTwoPartKey = findSecurityGroupVPCAssociationByTwoPartKey + FindSerialConsoleAccessStatus = findSerialConsoleAccessStatus FindSnapshot = findSnapshot FindSnapshotByID = findSnapshotByID FindSpotDatafeedSubscription = findSpotDatafeedSubscription diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index f23de55d958e..1df0590af62a 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -4636,6 +4636,21 @@ func findImageLaunchPermission(ctx context.Context, conn *ec2.Client, imageID, a return nil, &retry.NotFoundError{} } +func findSerialConsoleAccessStatus(ctx context.Context, conn *ec2.Client) (*ec2.GetSerialConsoleAccessStatusOutput, error) { + input := ec2.GetSerialConsoleAccessStatusInput{} + output, err := conn.GetSerialConsoleAccessStatus(ctx, &input) + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + func findTransitGateway(ctx context.Context, conn *ec2.Client, input *ec2.DescribeTransitGatewaysInput) (*awstypes.TransitGateway, error) { output, err := findTransitGateways(ctx, conn, input) diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index 428523b0cf22..fc613a775b81 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -1041,7 +1041,6 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*inttypes.ServicePa Identity: inttypes.RegionalSingletonIdentity( inttypes.WithV6_0SDKv2Fix(), inttypes.WithVersion(1), - inttypes.WithSDKv2IdentityUpgraders(imageBlockPublicAccessIdentityUpgradeV0), ), }, @@ -1103,9 +1102,11 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*inttypes.ServicePa Factory: resourceSerialConsoleAccess, TypeName: "aws_ec2_serial_console_access", Name: "Serial Console Access", - Region: unique.Make(inttypes.ResourceRegionDisabled()), - Identity: inttypes.GlobalSingletonIdentity( + Region: unique.Make(inttypes.ResourceRegionDefault()), + Identity: inttypes.RegionalSingletonIdentity( inttypes.WithV6_0SDKv2Fix(), + inttypes.WithVersion(1), + inttypes.WithSDKv2IdentityUpgraders(serialConsoleAccessIdentityUpgradeV0), ), Import: inttypes.SDKv2Import{ WrappedImport: true, diff --git a/internal/service/ec2/testdata/SerialConsoleAccess/basic_v6.20.0/main_gen.tf b/internal/service/ec2/testdata/SerialConsoleAccess/basic_v6.20.0/main_gen.tf new file mode 100644 index 000000000000..d02066ba2f54 --- /dev/null +++ b/internal/service/ec2/testdata/SerialConsoleAccess/basic_v6.20.0/main_gen.tf @@ -0,0 +1,17 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_ec2_serial_console_access" "test" { + enabled = true +} + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "6.20.0" + } + } +} + +provider "aws" {} diff --git a/internal/service/ec2/testdata/SerialConsoleAccess/region_override/main_gen.tf b/internal/service/ec2/testdata/SerialConsoleAccess/region_override/main_gen.tf new file mode 100644 index 000000000000..ab68872cde47 --- /dev/null +++ b/internal/service/ec2/testdata/SerialConsoleAccess/region_override/main_gen.tf @@ -0,0 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_ec2_serial_console_access" "test" { + region = var.region + + enabled = true +} + + +variable "region" { + description = "Region to deploy resource in" + type = string + nullable = false +} diff --git a/internal/service/ec2/testdata/tmpl/ec2_serial_console_access_basic.gtpl b/internal/service/ec2/testdata/tmpl/ec2_serial_console_access_basic.gtpl index 994c0ac1dec1..0d884b08d871 100644 --- a/internal/service/ec2/testdata/tmpl/ec2_serial_console_access_basic.gtpl +++ b/internal/service/ec2/testdata/tmpl/ec2_serial_console_access_basic.gtpl @@ -1,3 +1,4 @@ resource "aws_ec2_serial_console_access" "test" { +{{- template "region" }} enabled = true } diff --git a/website/docs/r/ec2_serial_console_access.html.markdown b/website/docs/r/ec2_serial_console_access.html.markdown index 79e28aa28b9e..906d656a6862 100644 --- a/website/docs/r/ec2_serial_console_access.html.markdown +++ b/website/docs/r/ec2_serial_console_access.html.markdown @@ -24,8 +24,8 @@ resource "aws_ec2_serial_console_access" "example" { This resource supports the following arguments: -* `region` - (Optional) Region where this resource will be [managed](https://docs.aws.amazon.com/general/latest/gr/rande.html#regional-endpoints). Defaults to the Region set in the [provider configuration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#aws-configuration-reference). * `enabled` - (Optional) Whether or not serial console access is enabled. Valid values are `true` or `false`. Defaults to `true`. +* `region` - (Optional) Region where this resource will be [managed](https://docs.aws.amazon.com/general/latest/gr/rande.html#regional-endpoints). Defaults to the Region set in the [provider configuration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#aws-configuration-reference). ## Attribute Reference