diff --git a/.changelog/37213.txt b/.changelog/37213.txt new file mode 100644 index 00000000000..d8893505576 --- /dev/null +++ b/.changelog/37213.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_dx_macsec_key_association: Add plan-time validation of `secret_arn` +``` \ No newline at end of file diff --git a/.ci/semgrep/migrate/context.yml b/.ci/semgrep/migrate/context.yml index 8bdc830bc96..be3699fc7da 100644 --- a/.ci/semgrep/migrate/context.yml +++ b/.ci/semgrep/migrate/context.yml @@ -30,6 +30,7 @@ rules: - pattern-not: conn.Options() - pattern-not: codestarconnections_sdkv2.$API() - pattern-not: connectcases_sdkv2.$API() + - pattern-not: directconnect_sdkv2.$API() - pattern-not: kafkaconnect_sdkv2.$API() - pattern-not: mediaconnect_sdkv2.$API() - pattern-not: pcaconnectorad_sdkv2.$API() diff --git a/go.mod b/go.mod index 233ec3d5470..a6448347a23 100644 --- a/go.mod +++ b/go.mod @@ -87,6 +87,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/detective v1.29.3 github.com/aws/aws-sdk-go-v2/service/devicefarm v1.25.2 github.com/aws/aws-sdk-go-v2/service/devopsguru v1.32.3 + github.com/aws/aws-sdk-go-v2/service/directconnect v1.27.5 github.com/aws/aws-sdk-go-v2/service/directoryservice v1.27.3 github.com/aws/aws-sdk-go-v2/service/dlm v1.26.3 github.com/aws/aws-sdk-go-v2/service/docdb v1.36.3 diff --git a/go.sum b/go.sum index fa0f849dc30..cfc330752b6 100644 --- a/go.sum +++ b/go.sum @@ -194,6 +194,8 @@ github.com/aws/aws-sdk-go-v2/service/devicefarm v1.25.2 h1:DSv0r8nKo8+ix2h5Rz/Zl github.com/aws/aws-sdk-go-v2/service/devicefarm v1.25.2/go.mod h1:7Ev/BlW5/zbURomHu/2Ay8l/HAgoQAbaSP2XlMUED9I= github.com/aws/aws-sdk-go-v2/service/devopsguru v1.32.3 h1:dVk+ogfz83rhZLaWSwSbgTQnxno+DIhZ3Q3KFdxTVmA= github.com/aws/aws-sdk-go-v2/service/devopsguru v1.32.3/go.mod h1:Rbgi0LKyAIyWHlqVtgU5wy39omdfHHvlGjrl+Vg41us= +github.com/aws/aws-sdk-go-v2/service/directconnect v1.27.5 h1:waoTf1hh2njzovjQm35YI/NgzBJ6MFh38V1D06m3ZSw= +github.com/aws/aws-sdk-go-v2/service/directconnect v1.27.5/go.mod h1:pPYhajQqE5gRnRVzTE6ptT0BCl603tkBc8YgLxsHMSo= github.com/aws/aws-sdk-go-v2/service/directoryservice v1.27.3 h1:Ua8NLsRNDm/HSotawG9MjeUEdo88uuTsEJ+EQB99G7c= github.com/aws/aws-sdk-go-v2/service/directoryservice v1.27.3/go.mod h1:DeGGGnrVVVNQlfMpAqmIiEndGTlDVbUIzNI4MbyyH68= github.com/aws/aws-sdk-go-v2/service/dlm v1.26.3 h1:LAZoBLsYn4eSTzJlfIu+v/+EHzqLqkPlIIc+y36HgEA= diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index d167702d9a0..243e720adb5 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -79,6 +79,7 @@ import ( detective_sdkv2 "github.com/aws/aws-sdk-go-v2/service/detective" devicefarm_sdkv2 "github.com/aws/aws-sdk-go-v2/service/devicefarm" devopsguru_sdkv2 "github.com/aws/aws-sdk-go-v2/service/devopsguru" + directconnect_sdkv2 "github.com/aws/aws-sdk-go-v2/service/directconnect" directoryservice_sdkv2 "github.com/aws/aws-sdk-go-v2/service/directoryservice" dlm_sdkv2 "github.com/aws/aws-sdk-go-v2/service/dlm" docdb_sdkv2 "github.com/aws/aws-sdk-go-v2/service/docdb" @@ -219,7 +220,6 @@ import ( xray_sdkv2 "github.com/aws/aws-sdk-go-v2/service/xray" batch_sdkv1 "github.com/aws/aws-sdk-go/service/batch" connect_sdkv1 "github.com/aws/aws-sdk-go/service/connect" - directconnect_sdkv1 "github.com/aws/aws-sdk-go/service/directconnect" elasticsearchservice_sdkv1 "github.com/aws/aws-sdk-go/service/elasticsearchservice" emr_sdkv1 "github.com/aws/aws-sdk-go/service/emr" gamelift_sdkv1 "github.com/aws/aws-sdk-go/service/gamelift" @@ -570,8 +570,8 @@ func (c *AWSClient) DeviceFarmClient(ctx context.Context) *devicefarm_sdkv2.Clie return errs.Must(client[*devicefarm_sdkv2.Client](ctx, c, names.DeviceFarm, make(map[string]any))) } -func (c *AWSClient) DirectConnectConn(ctx context.Context) *directconnect_sdkv1.DirectConnect { - return errs.Must(conn[*directconnect_sdkv1.DirectConnect](ctx, c, names.DirectConnect, make(map[string]any))) +func (c *AWSClient) DirectConnectClient(ctx context.Context) *directconnect_sdkv2.Client { + return errs.Must(client[*directconnect_sdkv2.Client](ctx, c, names.DirectConnect, make(map[string]any))) } func (c *AWSClient) DocDBClient(ctx context.Context) *docdb_sdkv2.Client { diff --git a/internal/service/apigatewayv2/integration_response_test.go b/internal/service/apigatewayv2/integration_response_test.go index 0d8ece594a3..9255021c055 100644 --- a/internal/service/apigatewayv2/integration_response_test.go +++ b/internal/service/apigatewayv2/integration_response_test.go @@ -189,17 +189,17 @@ func testAccIntegrationResponseImportStateIdFunc(resourceName string) resource.I } func testAccIntegrationResponseConfig_basic(rName string) string { - return testAccIntegrationConfig_basic(rName) + ` + return acctest.ConfigCompose(testAccIntegrationConfig_basic(rName), ` resource "aws_apigatewayv2_integration_response" "test" { api_id = aws_apigatewayv2_api.test.id integration_id = aws_apigatewayv2_integration.test.id integration_response_key = "/200/" } -` +`) } func testAccIntegrationResponseConfig_allAttributes(rName string) string { - return testAccIntegrationConfig_basic(rName) + ` + return acctest.ConfigCompose(testAccIntegrationConfig_basic(rName), ` resource "aws_apigatewayv2_integration_response" "test" { api_id = aws_apigatewayv2_api.test.id integration_id = aws_apigatewayv2_integration.test.id @@ -212,11 +212,11 @@ resource "aws_apigatewayv2_integration_response" "test" { "application/json" = "" } } -` +`) } func testAccIntegrationResponseConfig_allAttributesUpdated(rName string) string { - return testAccIntegrationConfig_basic(rName) + ` + return acctest.ConfigCompose(testAccIntegrationConfig_basic(rName), ` resource "aws_apigatewayv2_integration_response" "test" { api_id = aws_apigatewayv2_api.test.id integration_id = aws_apigatewayv2_integration.test.id @@ -230,5 +230,5 @@ resource "aws_apigatewayv2_integration_response" "test" { "application/xml" = "#set($percent=$number/100)" } } -` +`) } diff --git a/internal/service/apigatewayv2/model_test.go b/internal/service/apigatewayv2/model_test.go index 82a5703b446..b656ef90088 100644 --- a/internal/service/apigatewayv2/model_test.go +++ b/internal/service/apigatewayv2/model_test.go @@ -255,18 +255,18 @@ resource "aws_apigatewayv2_api" "test" { } func testAccModelConfig_basic(rName, schema string) string { - return testAccModelConfig_api(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccModelConfig_api(rName), fmt.Sprintf(` resource "aws_apigatewayv2_model" "test" { api_id = aws_apigatewayv2_api.test.id content_type = "application/json" name = %[1]q schema = %[2]q } -`, rName, schema) +`, rName, schema)) } func testAccModelConfig_allAttributes(rName, schema string) string { - return testAccModelConfig_api(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccModelConfig_api(rName), fmt.Sprintf(` resource "aws_apigatewayv2_model" "test" { api_id = aws_apigatewayv2_api.test.id content_type = "text/x-json" @@ -274,5 +274,5 @@ resource "aws_apigatewayv2_model" "test" { description = "test" schema = %[2]q } -`, rName, schema) +`, rName, schema)) } diff --git a/internal/service/appautoscaling/policy_test.go b/internal/service/appautoscaling/policy_test.go index 61d0b17e8e5..ad996edb2e5 100644 --- a/internal/service/appautoscaling/policy_test.go +++ b/internal/service/appautoscaling/policy_test.go @@ -1145,7 +1145,7 @@ resource "aws_ecs_service" "test2" { } func testAccPolicyConfig_resourceIDForceNew1(rName string) string { - return testAccPolicyConfig_resourceIDForceNewBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccPolicyConfig_resourceIDForceNewBase(rName), fmt.Sprintf(` resource "aws_appautoscaling_target" "test" { max_capacity = 4 min_capacity = 0 @@ -1190,11 +1190,11 @@ resource "aws_cloudwatch_metric_alarm" "test" { ClusterName = aws_ecs_cluster.test.name } } -`, rName) +`, rName)) } func testAccPolicyConfig_resourceIDForceNew2(rName string) string { - return testAccPolicyConfig_resourceIDForceNewBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccPolicyConfig_resourceIDForceNewBase(rName), fmt.Sprintf(` resource "aws_appautoscaling_target" "test" { max_capacity = 4 min_capacity = 0 @@ -1239,7 +1239,7 @@ resource "aws_cloudwatch_metric_alarm" "test" { ClusterName = aws_ecs_cluster.test.name } } -`, rName) +`, rName)) } func testAccPolicyImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { diff --git a/internal/service/appsync/resolver_test.go b/internal/service/appsync/resolver_test.go index 300dcef8cbe..dc657363e69 100644 --- a/internal/service/appsync/resolver_test.go +++ b/internal/service/appsync/resolver_test.go @@ -797,7 +797,7 @@ EOF } func testAccResolverConfig_sync(rName string) string { - return acctest.ConfigCompose(testAccDatasourceConfig_baseDynamoDB(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccDatasourceConfig_baseDynamoDB(rName), fmt.Sprintf(` resource "aws_appsync_graphql_api" "test" { authentication_type = "API_KEY" name = %[1]q diff --git a/internal/service/backup/plan_test.go b/internal/service/backup/plan_test.go index 192d0a2e17b..7b6989e4980 100644 --- a/internal/service/backup/plan_test.go +++ b/internal/service/backup/plan_test.go @@ -1062,7 +1062,7 @@ resource "aws_backup_plan" "test" { } func testAccPlanConfig_ruleCopyActionCrossRegion(rName string) string { - return acctest.ConfigAlternateRegionProvider() + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAlternateRegionProvider(), fmt.Sprintf(` resource "aws_backup_vault" "test" { name = "%[1]s-1" } @@ -1095,7 +1095,7 @@ resource "aws_backup_plan" "test" { } } } -`, rName) +`, rName)) } func testAccPlanConfig_ruleCopyActionNoLifecycle(rName string) string { diff --git a/internal/service/batch/scheduling_policy_data_source_test.go b/internal/service/batch/scheduling_policy_data_source_test.go index 2e661daed42..9c02ea4727f 100644 --- a/internal/service/batch/scheduling_policy_data_source_test.go +++ b/internal/service/batch/scheduling_policy_data_source_test.go @@ -45,7 +45,7 @@ func TestAccBatchSchedulingPolicyDataSource_basic(t *testing.T) { }) } -func testAccSchedulingPolicyDataSourceConfig(rName string) string { +func testAccSchedulingPolicyDataSourceConfig_base(rName string) string { return fmt.Sprintf(` resource "aws_batch_scheduling_policy" "test" { name = %[1]q @@ -69,7 +69,7 @@ resource "aws_batch_scheduling_policy" "test" { } func testAccSchedulingPolicyDataSourceConfig_basic(rName string) string { - return fmt.Sprintf(testAccSchedulingPolicyDataSourceConfig(rName) + ` + return acctest.ConfigCompose(testAccSchedulingPolicyDataSourceConfig_base(rName), ` data "aws_batch_scheduling_policy" "test" { arn = aws_batch_scheduling_policy.test.arn } diff --git a/internal/service/codeartifact/repository_test.go b/internal/service/codeartifact/repository_test.go index f5252b19bb7..bf9ba771308 100644 --- a/internal/service/codeartifact/repository_test.go +++ b/internal/service/codeartifact/repository_test.go @@ -384,7 +384,7 @@ resource "aws_codeartifact_repository" "test" { } func testAccRepositoryConfig_upstreams2(rName string) string { - return testAccRepositoryConfig_base(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccRepositoryConfig_base(rName), fmt.Sprintf(` resource "aws_codeartifact_repository" "upstream1" { repository = "%[1]s-upstream1" domain = aws_codeartifact_domain.test.domain @@ -407,11 +407,11 @@ resource "aws_codeartifact_repository" "test" { repository_name = aws_codeartifact_repository.upstream2.repository } } -`, rName) +`, rName)) } func testAccRepositoryConfig_externalConnection(rName string) string { - return testAccRepositoryConfig_base(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccRepositoryConfig_base(rName), fmt.Sprintf(` resource "aws_codeartifact_repository" "test" { repository = %[1]q domain = aws_codeartifact_domain.test.domain @@ -420,11 +420,11 @@ resource "aws_codeartifact_repository" "test" { external_connection_name = "public:npmjs" } } -`, rName) +`, rName)) } func testAccRepositoryConfig_tags1(rName, tagKey1, tagValue1 string) string { - return testAccRepositoryConfig_base(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccRepositoryConfig_base(rName), fmt.Sprintf(` resource "aws_codeartifact_repository" "test" { repository = %[1]q domain = aws_codeartifact_domain.test.domain @@ -433,11 +433,11 @@ resource "aws_codeartifact_repository" "test" { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccRepositoryConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccRepositoryConfig_base(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccRepositoryConfig_base(rName), fmt.Sprintf(` resource "aws_codeartifact_repository" "test" { repository = %[1]q domain = aws_codeartifact_domain.test.domain @@ -447,5 +447,5 @@ resource "aws_codeartifact_repository" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } diff --git a/internal/service/cognitoidentity/pool_roles_attachment_test.go b/internal/service/cognitoidentity/pool_roles_attachment_test.go index 82854d54ed6..704d84677f3 100644 --- a/internal/service/cognitoidentity/pool_roles_attachment_test.go +++ b/internal/service/cognitoidentity/pool_roles_attachment_test.go @@ -239,7 +239,7 @@ func testAccCheckPoolRolesAttachmentDestroy(ctx context.Context) resource.TestCh } } -func testAccPoolRolesAttachmentConfig(name string) string { +func testAccPoolRolesAttachmentConfig_base(name string) string { return fmt.Sprintf(` resource "aws_cognito_identity_pool" "main" { identity_pool_name = "identity pool %[1]s" @@ -356,7 +356,7 @@ EOF } func testAccPoolRolesAttachmentConfig_basic(name string) string { - return fmt.Sprintf(testAccPoolRolesAttachmentConfig(name) + ` + return acctest.ConfigCompose(testAccPoolRolesAttachmentConfig_base(name), ` resource "aws_cognito_identity_pool_roles_attachment" "test" { identity_pool_id = aws_cognito_identity_pool.main.id @@ -368,7 +368,7 @@ resource "aws_cognito_identity_pool_roles_attachment" "test" { } func testAccPoolRolesAttachmentConfig_roleMappings(name string) string { - return fmt.Sprintf(testAccPoolRolesAttachmentConfig(name) + ` + return acctest.ConfigCompose(testAccPoolRolesAttachmentConfig_base(name), ` resource "aws_cognito_identity_pool_roles_attachment" "test" { identity_pool_id = aws_cognito_identity_pool.main.id @@ -393,7 +393,7 @@ resource "aws_cognito_identity_pool_roles_attachment" "test" { } func testAccPoolRolesAttachmentConfig_roleMappingsUpdated(name string) string { - return fmt.Sprintf(testAccPoolRolesAttachmentConfig(name) + ` + return acctest.ConfigCompose(testAccPoolRolesAttachmentConfig_base(name), ` resource "aws_cognito_identity_pool_roles_attachment" "test" { identity_pool_id = aws_cognito_identity_pool.main.id @@ -425,7 +425,7 @@ resource "aws_cognito_identity_pool_roles_attachment" "test" { } func testAccPoolRolesAttachmentConfig_roleMappingsWithAmbiguousRoleResolutionError(name string) string { - return fmt.Sprintf(testAccPoolRolesAttachmentConfig(name) + ` + return acctest.ConfigCompose(testAccPoolRolesAttachmentConfig_base(name), ` resource "aws_cognito_identity_pool_roles_attachment" "test" { identity_pool_id = aws_cognito_identity_pool.main.id @@ -449,7 +449,7 @@ resource "aws_cognito_identity_pool_roles_attachment" "test" { } func testAccPoolRolesAttachmentConfig_roleMappingsWithRulesTypeError(name string) string { - return fmt.Sprintf(testAccPoolRolesAttachmentConfig(name) + ` + return acctest.ConfigCompose(testAccPoolRolesAttachmentConfig_base(name), ` resource "aws_cognito_identity_pool_roles_attachment" "test" { identity_pool_id = aws_cognito_identity_pool.main.id @@ -467,7 +467,7 @@ resource "aws_cognito_identity_pool_roles_attachment" "test" { } func testAccPoolRolesAttachmentConfig_roleMappingsWithTokenTypeError(name string) string { - return fmt.Sprintf(testAccPoolRolesAttachmentConfig(name) + ` + return acctest.ConfigCompose(testAccPoolRolesAttachmentConfig_base(name), ` resource "aws_cognito_identity_pool_roles_attachment" "test" { identity_pool_id = aws_cognito_identity_pool.main.id diff --git a/internal/service/configservice/organization_custom_rule_test.go b/internal/service/configservice/organization_custom_rule_test.go index 7f759fbcb71..426ea10ff05 100644 --- a/internal/service/configservice/organization_custom_rule_test.go +++ b/internal/service/configservice/organization_custom_rule_test.go @@ -505,7 +505,7 @@ func testAccCheckOrganizationCustomRuleDestroy(ctx context.Context) resource.Tes } } -func testAccOrganizationCustomRuleConfigBase(rName string) string { +func testAccOrganizationCustomRuleConfig_base(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" { } @@ -590,7 +590,7 @@ resource "aws_organizations_organization" "test" { } func testAccOrganizationCustomRuleConfig_description(rName, description string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -599,7 +599,7 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName, description) +`, rName, description)) } func testAccOrganizationCustomRuleConfig_errorHandling(rName string) string { @@ -656,7 +656,7 @@ resource "aws_config_organization_custom_rule" "test" { } func testAccOrganizationCustomRuleConfig_excludedAccounts1(rName string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -665,11 +665,11 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName) +`, rName)) } func testAccOrganizationCustomRuleConfig_excludedAccounts2(rName string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -678,11 +678,11 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName) +`, rName)) } func testAccOrganizationCustomRuleConfig_inputParameters(rName, inputParameters string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -694,11 +694,11 @@ PARAMS name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName, inputParameters) +`, rName, inputParameters)) } func testAccOrganizationCustomRuleConfig_lambdaFunctionARN1(rName string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -706,11 +706,11 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName) +`, rName)) } func testAccOrganizationCustomRuleConfig_lambdaFunctionARN2(rName string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_lambda_function" "test2" { filename = "test-fixtures/lambdatest.zip" function_name = "%[1]s2" @@ -733,11 +733,11 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName) +`, rName)) } func testAccOrganizationCustomRuleConfig_maximumExecutionFrequency(rName, maximumExecutionFrequency string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -746,11 +746,11 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = ["ScheduledNotification"] } -`, rName, maximumExecutionFrequency) +`, rName, maximumExecutionFrequency)) } func testAccOrganizationCustomRuleConfig_resourceIdScope(rName, resourceIdScope string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -760,11 +760,11 @@ resource "aws_config_organization_custom_rule" "test" { resource_types_scope = ["AWS::EC2::Instance"] trigger_types = ["ScheduledNotification"] } -`, rName, resourceIdScope) +`, rName, resourceIdScope)) } func testAccOrganizationCustomRuleConfig_resourceTypesScope1(rName string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -773,11 +773,11 @@ resource "aws_config_organization_custom_rule" "test" { resource_types_scope = ["AWS::EC2::Instance"] trigger_types = ["ScheduledNotification"] } -`, rName) +`, rName)) } func testAccOrganizationCustomRuleConfig_resourceTypesScope2(rName string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -786,11 +786,11 @@ resource "aws_config_organization_custom_rule" "test" { resource_types_scope = ["AWS::EC2::Instance", "AWS::EC2::VPC"] trigger_types = ["ScheduledNotification"] } -`, rName) +`, rName)) } func testAccOrganizationCustomRuleConfig_tagKeyScope(rName, tagKeyScope string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -799,11 +799,11 @@ resource "aws_config_organization_custom_rule" "test" { tag_key_scope = %[2]q trigger_types = ["ScheduledNotification"] } -`, rName, tagKeyScope) +`, rName, tagKeyScope)) } func testAccOrganizationCustomRuleConfig_tagValueScope(rName, tagValueScope string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -813,11 +813,11 @@ resource "aws_config_organization_custom_rule" "test" { tag_value_scope = %[2]q trigger_types = ["ScheduledNotification"] } -`, rName, tagValueScope) +`, rName, tagValueScope)) } func testAccOrganizationCustomRuleConfig_triggerTypes1(rName, triggerType1 string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -825,11 +825,11 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = [%[2]q] } -`, rName, triggerType1) +`, rName, triggerType1)) } func testAccOrganizationCustomRuleConfig_triggerTypes2(rName, triggerType1, triggerType2 string) string { - return testAccOrganizationCustomRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationCustomRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_custom_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_lambda_permission.test, aws_organizations_organization.test] @@ -837,5 +837,5 @@ resource "aws_config_organization_custom_rule" "test" { name = %[1]q trigger_types = [%[2]q, %[3]q] } -`, rName, triggerType1, triggerType2) +`, rName, triggerType1, triggerType2)) } diff --git a/internal/service/configservice/organization_managed_rule_test.go b/internal/service/configservice/organization_managed_rule_test.go index 4905e1375cc..d9bcfb9e3a8 100644 --- a/internal/service/configservice/organization_managed_rule_test.go +++ b/internal/service/configservice/organization_managed_rule_test.go @@ -466,7 +466,7 @@ func testAccCheckOrganizationManagedRuleDestroy(ctx context.Context) resource.Te } } -func testAccOrganizationManagedRuleConfigBase(rName string) string { +func testAccOrganizationManagedRuleConfig_base(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" { } @@ -511,7 +511,7 @@ resource "aws_organizations_organization" "test" { } func testAccOrganizationManagedRuleConfig_description(rName, description string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -519,7 +519,7 @@ resource "aws_config_organization_managed_rule" "test" { name = %[1]q rule_identifier = "IAM_PASSWORD_POLICY" } -`, rName, description) +`, rName, description)) } func testAccOrganizationManagedRuleConfig_errorHandling(rName string) string { @@ -539,7 +539,7 @@ resource "aws_config_organization_managed_rule" "test" { } func testAccOrganizationManagedRuleConfig_excludedAccounts1(rName string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -547,11 +547,11 @@ resource "aws_config_organization_managed_rule" "test" { name = %[1]q rule_identifier = "IAM_PASSWORD_POLICY" } -`, rName) +`, rName)) } func testAccOrganizationManagedRuleConfig_excludedAccounts2(rName string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -559,11 +559,11 @@ resource "aws_config_organization_managed_rule" "test" { name = %[1]q rule_identifier = "IAM_PASSWORD_POLICY" } -`, rName) +`, rName)) } func testAccOrganizationManagedRuleConfig_inputParameters(rName, inputParameters string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -574,11 +574,11 @@ PARAMS name = %[1]q rule_identifier = "REQUIRED_TAGS" } -`, rName, inputParameters) +`, rName, inputParameters)) } func testAccOrganizationManagedRuleConfig_maximumExecutionFrequency(rName, maximumExecutionFrequency string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -586,11 +586,11 @@ resource "aws_config_organization_managed_rule" "test" { name = %[1]q rule_identifier = "IAM_PASSWORD_POLICY" } -`, rName, maximumExecutionFrequency) +`, rName, maximumExecutionFrequency)) } func testAccOrganizationManagedRuleConfig_resourceIdScope(rName, resourceIdScope string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -599,11 +599,11 @@ resource "aws_config_organization_managed_rule" "test" { resource_types_scope = ["AWS::EC2::Instance"] rule_identifier = "EC2_INSTANCE_DETAILED_MONITORING_ENABLED" } -`, rName, resourceIdScope) +`, rName, resourceIdScope)) } func testAccOrganizationManagedRuleConfig_resourceTypesScope1(rName string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -618,11 +618,11 @@ EOF resource_types_scope = ["AWS::EC2::Instance"] rule_identifier = "REQUIRED_TAGS" } -`, rName) +`, rName)) } func testAccOrganizationManagedRuleConfig_resourceTypesScope2(rName string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -637,22 +637,22 @@ EOF resource_types_scope = ["AWS::EC2::Instance", "AWS::EC2::VPC"] rule_identifier = "REQUIRED_TAGS" } -`, rName) +`, rName)) } func testAccOrganizationManagedRuleConfig_identifier(rName, ruleIdentifier string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] name = %[1]q rule_identifier = %[2]q } -`, rName, ruleIdentifier) +`, rName, ruleIdentifier)) } func testAccOrganizationManagedRuleConfig_tagKeyScope(rName, tagKeyScope string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -660,11 +660,11 @@ resource "aws_config_organization_managed_rule" "test" { rule_identifier = "EC2_INSTANCE_DETAILED_MONITORING_ENABLED" tag_key_scope = %[2]q } -`, rName, tagKeyScope) +`, rName, tagKeyScope)) } func testAccOrganizationManagedRuleConfig_tagValueScope(rName, tagValueScope string) string { - return testAccOrganizationManagedRuleConfigBase(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationManagedRuleConfig_base(rName), fmt.Sprintf(` resource "aws_config_organization_managed_rule" "test" { depends_on = [aws_config_configuration_recorder.test, aws_organizations_organization.test] @@ -673,5 +673,5 @@ resource "aws_config_organization_managed_rule" "test" { tag_key_scope = "key1" tag_value_scope = %[2]q } -`, rName, tagValueScope) +`, rName, tagValueScope)) } diff --git a/internal/service/devicefarm/device_pool_test.go b/internal/service/devicefarm/device_pool_test.go index c512424c392..c406e3d74cd 100644 --- a/internal/service/devicefarm/device_pool_test.go +++ b/internal/service/devicefarm/device_pool_test.go @@ -235,7 +235,7 @@ func testAccCheckDevicePoolDestroy(ctx context.Context) resource.TestCheckFunc { } func testAccDevicePoolConfig_basic(rName string) string { - return testAccProjectConfig_basic(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(` resource "aws_devicefarm_device_pool" "test" { name = %[1]q project_arn = aws_devicefarm_project.test.arn @@ -245,11 +245,11 @@ resource "aws_devicefarm_device_pool" "test" { value = "\"AVAILABLE\"" } } -`, rName) +`, rName)) } func testAccDevicePoolConfig_tags1(rName, tagKey1, tagValue1 string) string { - return testAccProjectConfig_basic(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(` resource "aws_devicefarm_device_pool" "test" { name = %[1]q project_arn = aws_devicefarm_project.test.arn @@ -262,11 +262,11 @@ resource "aws_devicefarm_device_pool" "test" { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccDevicePoolConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccProjectConfig_basic(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(` resource "aws_devicefarm_device_pool" "test" { name = %[1]q project_arn = aws_devicefarm_project.test.arn @@ -280,5 +280,5 @@ resource "aws_devicefarm_device_pool" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } diff --git a/internal/service/devicefarm/network_profile_test.go b/internal/service/devicefarm/network_profile_test.go index df565390d43..e4f493f0590 100644 --- a/internal/service/devicefarm/network_profile_test.go +++ b/internal/service/devicefarm/network_profile_test.go @@ -242,16 +242,16 @@ func testAccCheckNetworkProfileDestroy(ctx context.Context) resource.TestCheckFu } func testAccNetworkProfileConfig_basic(rName string) string { - return testAccProjectConfig_basic(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(` resource "aws_devicefarm_network_profile" "test" { name = %[1]q project_arn = aws_devicefarm_project.test.arn } -`, rName) +`, rName)) } func testAccNetworkProfileConfig_tags1(rName, tagKey1, tagValue1 string) string { - return testAccProjectConfig_basic(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(` resource "aws_devicefarm_network_profile" "test" { name = %[1]q project_arn = aws_devicefarm_project.test.arn @@ -260,11 +260,11 @@ resource "aws_devicefarm_network_profile" "test" { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccNetworkProfileConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccProjectConfig_basic(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(` resource "aws_devicefarm_network_profile" "test" { name = %[1]q project_arn = aws_devicefarm_project.test.arn @@ -274,5 +274,5 @@ resource "aws_devicefarm_network_profile" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } diff --git a/internal/service/directconnect/acc_test.go b/internal/service/directconnect/acc_test.go deleted file mode 100644 index 0886a423d3b..00000000000 --- a/internal/service/directconnect/acc_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect_test - -import ( - "context" - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" - "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" -) - -func testAccCheckVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) - - rs, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - resp, err := conn.DescribeVirtualInterfacesWithContext(ctx, &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - }) - if err != nil { - return err - } - - for _, v := range resp.VirtualInterfaces { - if aws.StringValue(v.VirtualInterfaceId) == rs.Primary.ID { - *vif = *v - - return nil - } - } - - return fmt.Errorf("Direct Connect virtual interface (%s) not found", rs.Primary.ID) - } -} - -func testAccCheckVirtualInterfaceDestroy(ctx context.Context, s *terraform.State, t string) error { // nosemgrep:ci.semgrep.acctest.naming.destroy-check-signature - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) - - for _, rs := range s.RootModule().Resources { - if rs.Type != t { - continue - } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - resp, err := conn.DescribeVirtualInterfacesWithContext(ctx, &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - }) - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "does not exist") { - continue - } - if err != nil { - return err - } - - for _, v := range resp.VirtualInterfaces { - if aws.StringValue(v.VirtualInterfaceId) == rs.Primary.ID && aws.StringValue(v.VirtualInterfaceState) != directconnect.VirtualInterfaceStateDeleted { - return fmt.Errorf("[DESTROY ERROR] Direct Connect virtual interface (%s) not deleted", rs.Primary.ID) - } - } - } - - return nil -} diff --git a/internal/service/directconnect/bgp_peer.go b/internal/service/directconnect/bgp_peer.go index 4d48c9cb8bd..8e226ad2769 100644 --- a/internal/service/directconnect/bgp_peer.go +++ b/internal/service/directconnect/bgp_peer.go @@ -9,19 +9,22 @@ import ( "log" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -// @SDKResource("aws_dx_bgp_peer") -func ResourceBGPPeer() *schema.Resource { +// @SDKResource("aws_dx_bgp_peer", name="BGP Peer") +func resourceBGPPeer() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceBGPPeerCreate, ReadWithoutTimeout: resourceBGPPeerRead, @@ -29,25 +32,24 @@ func ResourceBGPPeer() *schema.Resource { Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, - "bgp_asn": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "virtual_interface_id": { + "amazon_address": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, - "amazon_address": { + "aws_device": { Type: schema.TypeString, - Optional: true, Computed: true, + }, + "bgp_asn": { + Type: schema.TypeInt, + Required: true, ForceNew: true, }, "bgp_auth_key": { @@ -56,23 +58,24 @@ func ResourceBGPPeer() *schema.Resource { Computed: true, ForceNew: true, }, - "customer_address": { + "bgp_peer_id": { Type: schema.TypeString, - Optional: true, Computed: true, - ForceNew: true, }, "bgp_status": { Type: schema.TypeString, Computed: true, }, - "bgp_peer_id": { + "customer_address": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, - "aws_device": { + "virtual_interface_id": { Type: schema.TypeString, - Computed: true, + Required: true, + ForceNew: true, }, }, @@ -85,53 +88,39 @@ func ResourceBGPPeer() *schema.Resource { func resourceBGPPeerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) - - vifId := d.Get("virtual_interface_id").(string) - addrFamily := d.Get("address_family").(string) - asn := int64(d.Get("bgp_asn").(int)) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - req := &directconnect.CreateBGPPeerInput{ - VirtualInterfaceId: aws.String(vifId), - NewBGPPeer: &directconnect.NewBGPPeer{ - AddressFamily: aws.String(addrFamily), - Asn: aws.Int64(asn), + vifID := d.Get("virtual_interface_id").(string) + addrFamily := awstypes.AddressFamily(d.Get("address_family").(string)) + asn := int32(d.Get("bgp_asn").(int)) + input := &directconnect.CreateBGPPeerInput{ + NewBGPPeer: &awstypes.NewBGPPeer{ + AddressFamily: addrFamily, + Asn: asn, }, + VirtualInterfaceId: aws.String(vifID), } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewBGPPeer.AmazonAddress = aws.String(v.(string)) + input.NewBGPPeer.AmazonAddress = aws.String(v.(string)) } if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewBGPPeer.AuthKey = aws.String(v.(string)) + input.NewBGPPeer.AuthKey = aws.String(v.(string)) } if v, ok := d.GetOk("customer_address"); ok { - req.NewBGPPeer.CustomerAddress = aws.String(v.(string)) + input.NewBGPPeer.CustomerAddress = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating Direct Connect BGP peer: %#v", req) - _, err := conn.CreateBGPPeerWithContext(ctx, req) + _, err := conn.CreateBGPPeer(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Direct Connect BGP peer: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect BGP Peer: %s", err) } - d.SetId(fmt.Sprintf("%s-%s-%d", vifId, addrFamily, asn)) + d.SetId(fmt.Sprintf("%s-%s-%d", vifID, addrFamily, asn)) - stateConf := &retry.StateChangeConf{ - Pending: []string{ - directconnect.BGPPeerStatePending, - }, - Target: []string{ - directconnect.BGPPeerStateAvailable, - directconnect.BGPPeerStateVerifying, - }, - Refresh: bgpPeerStateRefresh(ctx, conn, vifId, addrFamily, asn), - Timeout: d.Timeout(schema.TimeoutDelete), - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, - } - _, err = stateConf.WaitForStateContext(ctx) - if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect BGP peer (%s) to be available: %s", d.Id(), err) + if _, err = waitBGPPeerCreated(ctx, conn, vifID, addrFamily, asn, d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect BGP Peer (%s) create: %s", d.Id(), err) } return append(diags, resourceBGPPeerRead(ctx, d, meta)...) @@ -139,94 +128,137 @@ func resourceBGPPeerCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceBGPPeerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vifId := d.Get("virtual_interface_id").(string) - addrFamily := d.Get("address_family").(string) - asn := int64(d.Get("bgp_asn").(int)) + vifID := d.Get("virtual_interface_id").(string) + addrFamily := awstypes.AddressFamily(d.Get("address_family").(string)) + asn := int32(d.Get("bgp_asn").(int)) + bgpPeer, err := findBGPPeerByThreePartKey(ctx, conn, vifID, addrFamily, asn) - bgpPeerRaw, state, err := bgpPeerStateRefresh(ctx, conn, vifId, addrFamily, asn)() - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Direct Connect BGP peer: %s", err) - } - if state == directconnect.BGPPeerStateDeleted { - log.Printf("[WARN] Direct Connect BGP peer (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect BGP Peer (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - bgpPeer := bgpPeerRaw.(*directconnect.BGPPeer) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect BGP Peer (%s): %s", d.Id(), err) + } + d.Set("amazon_address", bgpPeer.AmazonAddress) + d.Set("aws_device", bgpPeer.AwsDeviceV2) d.Set("bgp_auth_key", bgpPeer.AuthKey) - d.Set("customer_address", bgpPeer.CustomerAddress) - d.Set("bgp_status", bgpPeer.BgpStatus) d.Set("bgp_peer_id", bgpPeer.BgpPeerId) - d.Set("aws_device", bgpPeer.AwsDeviceV2) + d.Set("bgp_status", bgpPeer.BgpStatus) + d.Set("customer_address", bgpPeer.CustomerAddress) return diags } func resourceBGPPeerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vifId := d.Get("virtual_interface_id").(string) - addrFamily := d.Get("address_family").(string) - asn := int64(d.Get("bgp_asn").(int)) + vifID := d.Get("virtual_interface_id").(string) + addrFamily := awstypes.AddressFamily(d.Get("address_family").(string)) + asn := int32(d.Get("bgp_asn").(int)) log.Printf("[DEBUG] Deleting Direct Connect BGP peer: %s", d.Id()) - _, err := conn.DeleteBGPPeerWithContext(ctx, &directconnect.DeleteBGPPeerInput{ - Asn: aws.Int64(asn), + _, err := conn.DeleteBGPPeer(ctx, &directconnect.DeleteBGPPeerInput{ + Asn: asn, CustomerAddress: aws.String(d.Get("customer_address").(string)), - VirtualInterfaceId: aws.String(vifId), + VirtualInterfaceId: aws.String(vifID), }) + + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "The last BGP Peer on a Virtual Interface cannot be deleted") { + return diags + } + if err != nil { - // This is the error returned if the BGP peering has already gone. - if tfawserr.ErrMessageContains(err, "DirectConnectClientException", "The last BGP Peer on a Virtual Interface cannot be deleted") { - return diags - } return sdkdiag.AppendErrorf(diags, "deleting Direct Connect BGP Peer (%s): %s", d.Id(), err) } - stateConf := &retry.StateChangeConf{ - Pending: []string{ - directconnect.BGPPeerStateAvailable, - directconnect.BGPPeerStateDeleting, - directconnect.BGPPeerStatePending, - directconnect.BGPPeerStateVerifying, - }, - Target: []string{ - directconnect.BGPPeerStateDeleted, - }, - Refresh: bgpPeerStateRefresh(ctx, conn, vifId, addrFamily, asn), - Timeout: d.Timeout(schema.TimeoutDelete), - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, + if _, err = waitBGPPeerDeleted(ctx, conn, vifID, addrFamily, asn, d.Timeout(schema.TimeoutDelete)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect BGP Peer (%s) delete: %s", d.Id(), err) } - _, err = stateConf.WaitForStateContext(ctx) + + return diags +} + +func findBGPPeerByThreePartKey(ctx context.Context, conn *directconnect.Client, vifID string, addrFamily awstypes.AddressFamily, asn int32) (*awstypes.BGPPeer, error) { + vif, err := findVirtualInterfaceByID(ctx, conn, vifID) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting Direct Connect BGP Peer (%s): waiting for completion: %s", d.Id(), err) + return nil, err } - return diags + output, err := tfresource.AssertSingleValueResult(tfslices.Filter(vif.BgpPeers, func(v awstypes.BGPPeer) bool { + return v.AddressFamily == addrFamily && v.Asn == asn + })) + + if err != nil { + return nil, err + } + + if state := output.BgpPeerState; state == awstypes.BGPPeerStateDeleted { + return nil, &retry.NotFoundError{ + Message: string(state), + } + } + + return output, nil } -func bgpPeerStateRefresh(ctx context.Context, conn *directconnect.DirectConnect, vifId, addrFamily string, asn int64) retry.StateRefreshFunc { +func statusBGPPeer(ctx context.Context, conn *directconnect.Client, vifID string, addrFamily awstypes.AddressFamily, asn int32) retry.StateRefreshFunc { return func() (interface{}, string, error) { - vif, err := virtualInterfaceRead(ctx, vifId, conn) + output, err := findBGPPeerByThreePartKey(ctx, conn, vifID, addrFamily, asn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + if err != nil { return nil, "", err } - if vif == nil { - return "", directconnect.BGPPeerStateDeleted, nil - } - for _, bgpPeer := range vif.BgpPeers { - if aws.StringValue(bgpPeer.AddressFamily) == addrFamily && aws.Int64Value(bgpPeer.Asn) == asn { - return bgpPeer, aws.StringValue(bgpPeer.BgpPeerState), nil - } - } + return output, string(output.BgpPeerState), nil + } +} + +func waitBGPPeerCreated(ctx context.Context, conn *directconnect.Client, vifID string, addrFamily awstypes.AddressFamily, asn int32, timeout time.Duration) (*awstypes.BGPPeer, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.BGPPeerStatePending), + Target: enum.Slice(awstypes.BGPPeerStateAvailable, awstypes.BGPPeerStateVerifying), + Refresh: statusBGPPeer(ctx, conn, vifID, addrFamily, asn), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } - return "", directconnect.BGPPeerStateDeleted, nil + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.BGPPeer); ok { + return output, err + } + + return nil, err +} + +func waitBGPPeerDeleted(ctx context.Context, conn *directconnect.Client, vifID string, addrFamily awstypes.AddressFamily, asn int32, timeout time.Duration) (*awstypes.BGPPeer, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.BGPPeerStateAvailable, awstypes.BGPPeerStateDeleting, awstypes.BGPPeerStatePending, awstypes.BGPPeerStateVerifying), + Target: []string{}, + Refresh: statusBGPPeer(ctx, conn, vifID, addrFamily, asn), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.BGPPeer); ok { + return output, err } + + return nil, err } diff --git a/internal/service/directconnect/bgp_peer_test.go b/internal/service/directconnect/bgp_peer_test.go index 90384a05d80..bd765db9025 100644 --- a/internal/service/directconnect/bgp_peer_test.go +++ b/internal/service/directconnect/bgp_peer_test.go @@ -6,28 +6,25 @@ package directconnect_test import ( "context" "fmt" - "os" - "strconv" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "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" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfdirectconnect "github.com/hashicorp/terraform-provider-aws/internal/service/directconnect" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccDirectConnectBGPPeer_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_VIRTUAL_INTERFACE_ID" - vifId := os.Getenv(key) - if vifId == "" { - t.Skipf("Environment variable %s is not set", key) - } + vifID := acctest.SkipIfEnvVarNotSet(t, "DX_VIRTUAL_INTERFACE_ID") bgpAsn := sdkacctest.RandIntRange(64512, 65534) + resourceName := "aws_dx_bgp_peer.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -36,62 +33,88 @@ func TestAccDirectConnectBGPPeer_basic(t *testing.T) { CheckDestroy: testAccCheckBGPPeerDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccBGPPeerConfig_basic(vifId, bgpAsn), + Config: testAccBGPPeerConfig_basic(vifID, bgpAsn), Check: resource.ComposeTestCheckFunc( - testAccCheckBGPPeerExists("aws_dx_bgp_peer.foo"), - resource.TestCheckResourceAttr("aws_dx_bgp_peer.foo", "address_family", "ipv6"), + testAccCheckBGPPeerExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv6"), ), }, }, }) } +func TestAccDirectConnectBGPPeer_disappears(t *testing.T) { + ctx := acctest.Context(t) + vifID := acctest.SkipIfEnvVarNotSet(t, "DX_VIRTUAL_INTERFACE_ID") + bgpAsn := sdkacctest.RandIntRange(64512, 65534) + resourceName := "aws_dx_bgp_peer.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckBGPPeerDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccBGPPeerConfig_basic(vifID, bgpAsn), + Check: resource.ComposeTestCheckFunc( + testAccCheckBGPPeerExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfdirectconnect.ResourceBGPPeer(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckBGPPeerDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_bgp_peer" { continue } - input := &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.Attributes["virtual_interface_id"]), + + _, err := tfdirectconnect.FindBGPPeerByThreePartKey(ctx, conn, rs.Primary.Attributes["virtual_interface_id"], awstypes.AddressFamily(rs.Primary.Attributes["address_family"]), flex.StringValueToInt32Value(rs.Primary.Attributes["bgp_asn"])) + + if tfresource.NotFound(err) { + continue } - resp, err := conn.DescribeVirtualInterfacesWithContext(ctx, input) if err != nil { return err } - for _, peer := range resp.VirtualInterfaces[0].BgpPeers { - if aws.StringValue(peer.AddressFamily) == rs.Primary.Attributes["address_family"] && - strconv.Itoa(int(aws.Int64Value(peer.Asn))) == rs.Primary.Attributes["bgp_asn"] && - aws.StringValue(peer.BgpPeerState) != directconnect.BGPPeerStateDeleted { - return fmt.Errorf("[DESTROY ERROR] Dx BGP peer (%s) not deleted", rs.Primary.ID) - } - } + + return fmt.Errorf("Direct Connect BGP Peer %s still exists", rs.Primary.ID) } + return nil } } -func testAccCheckBGPPeerExists(name string) resource.TestCheckFunc { +func testAccCheckBGPPeerExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } - return nil + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) + + _, err := tfdirectconnect.FindBGPPeerByThreePartKey(ctx, conn, rs.Primary.Attributes["virtual_interface_id"], awstypes.AddressFamily(rs.Primary.Attributes["address_family"]), flex.StringValueToInt32Value(rs.Primary.Attributes["bgp_asn"])) + + return err } } -func testAccBGPPeerConfig_basic(vifId string, bgpAsn int) string { +func testAccBGPPeerConfig_basic(vifID string, bgpAsn int) string { return fmt.Sprintf(` -resource "aws_dx_bgp_peer" "foo" { - virtual_interface_id = "%s" +resource "aws_dx_bgp_peer" "test" { + virtual_interface_id = %[1]q address_family = "ipv6" - bgp_asn = %d + bgp_asn = %[2]d } -`, vifId, bgpAsn) +`, vifID, bgpAsn) } diff --git a/internal/service/directconnect/connection.go b/internal/service/directconnect/connection.go index 9872015ae82..9d039ec3d4b 100644 --- a/internal/service/directconnect/connection.go +++ b/internal/service/directconnect/connection.go @@ -8,16 +8,21 @@ import ( "fmt" "log" "strconv" + "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" 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" @@ -26,7 +31,7 @@ import ( // @SDKResource("aws_dx_connection", name="Connection") // @Tags(identifierAttribute="arn") -func ResourceConnection() *schema.Resource { +func resourceConnection() *schema.Resource { // Resource with v0 schema (provider v5.0.1). resourceV0 := &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -39,17 +44,14 @@ func ResourceConnection() *schema.Resource { Computed: true, }, "bandwidth": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validConnectionBandWidth(), + Type: schema.TypeString, + Required: true, + ForceNew: true, }, - // 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), + Type: schema.TypeString, + Computed: true, + Optional: true, }, "has_logical_redundancy": { Type: schema.TypeString, @@ -64,12 +66,10 @@ func ResourceConnection() *schema.Resource { 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, @@ -238,7 +238,7 @@ func ResourceConnection() *schema.Resource { func resourceConnectionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) name := d.Get(names.AttrName).(string) input := &directconnect.CreateConnectionInput{ @@ -253,22 +253,22 @@ func resourceConnectionCreate(ctx context.Context, d *schema.ResourceData, meta input.ProviderName = aws.String(v.(string)) } - output, err := conn.CreateConnectionWithContext(ctx, input) + output, err := conn.CreateConnection(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect Connection (%s): %s", name, err) } - d.SetId(aws.StringValue(output.ConnectionId)) + d.SetId(aws.ToString(output.ConnectionId)) return append(diags, resourceConnectionRead(ctx, d, meta)...) } func resourceConnectionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - connection, err := FindConnectionByID(ctx, conn, d.Id()) + connection, err := findConnectionByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Connection (%s) not found, removing from state", d.Id()) @@ -282,9 +282,9 @@ func resourceConnectionRead(ctx context.Context, d *schema.ResourceData, meta in arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, - Region: aws.StringValue(connection.Region), + Region: aws.ToString(connection.Region), Service: "directconnect", - AccountID: aws.StringValue(connection.OwnerAccount), + AccountID: aws.ToString(connection.OwnerAccount), Resource: fmt.Sprintf("dxcon/%s", d.Id()), }.String() d.Set(names.AttrARN, arn) @@ -300,20 +300,17 @@ func resourceConnectionRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("partner_name", connection.PartnerName) d.Set("port_encryption_status", connection.PortEncryptionStatus) d.Set(names.AttrProviderName, connection.ProviderName) - d.Set("vlan_id", connection.Vlan) - - // d.Set("request_macsec", d.Get("request_macsec").(bool)) - if !d.IsNewResource() && !d.Get("request_macsec").(bool) { d.Set("request_macsec", aws.Bool(false)) } + d.Set("vlan_id", connection.Vlan) return diags } func resourceConnectionUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if d.HasChange("encryption_mode") { input := &directconnect.UpdateConnectionInput{ @@ -321,7 +318,7 @@ func resourceConnectionUpdate(ctx context.Context, d *schema.ResourceData, meta EncryptionMode: aws.String(d.Get("encryption_mode").(string)), } - _, err := conn.UpdateConnectionWithContext(ctx, input) + _, err := conn.UpdateConnection(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Direct Connect Connection (%s): %s", d.Id(), err) @@ -337,7 +334,7 @@ func resourceConnectionUpdate(ctx context.Context, d *schema.ResourceData, meta func resourceConnectionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if _, ok := d.GetOk(names.AttrSkipDestroy); ok { return diags @@ -350,13 +347,13 @@ func resourceConnectionDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func deleteConnection(ctx context.Context, conn *directconnect.DirectConnect, connectionID string, waiter func(context.Context, *directconnect.DirectConnect, string) (*directconnect.Connection, error)) error { +func deleteConnection(ctx context.Context, conn *directconnect.Client, connectionID string, waiter func(context.Context, *directconnect.Client, string) (*awstypes.Connection, error)) error { log.Printf("[DEBUG] Deleting Direct Connect Connection: %s", connectionID) - _, err := conn.DeleteConnectionWithContext(ctx, &directconnect.DeleteConnectionInput{ + _, err := conn.DeleteConnection(ctx, &directconnect.DeleteConnectionInput{ ConnectionId: aws.String(connectionID), }) - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Could not find Connection with ID") { + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Could not find Connection with ID") { return nil } @@ -364,11 +361,96 @@ func deleteConnection(ctx context.Context, conn *directconnect.DirectConnect, co return fmt.Errorf("deleting Direct Connect Connection (%s): %w", connectionID, err) } - _, err = waiter(ctx, conn, connectionID) - - if err != nil { + if _, err := waiter(ctx, conn, connectionID); err != nil { return fmt.Errorf("waiting for Direct Connect Connection (%s): %w", connectionID, err) } return nil } + +func findConnectionByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Connection, error) { + input := &directconnect.DescribeConnectionsInput{ + ConnectionId: aws.String(id), + } + output, err := findConnection(ctx, conn, input, tfslices.PredicateTrue[*awstypes.Connection]()) + + if err != nil { + return nil, err + } + + if state := output.ConnectionState; state == awstypes.ConnectionStateDeleted || state == awstypes.ConnectionStateRejected { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, + } + } + + return output, nil +} + +func findConnection(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeConnectionsInput, filter tfslices.Predicate[*awstypes.Connection]) (*awstypes.Connection, error) { + output, err := findConnections(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findConnections(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeConnectionsInput, filter tfslices.Predicate[*awstypes.Connection]) ([]awstypes.Connection, error) { + output, err := conn.DescribeConnections(ctx, input) + + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Could not find Connection with ID") { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return tfslices.Filter(output.Connections, tfslices.PredicateValue(filter)), nil +} + +func statusConnection(ctx context.Context, conn *directconnect.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findConnectionByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.ConnectionState), nil + } +} + +func waitConnectionDeleted(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Connection, error) { + const ( + timeout = 10 * time.Minute + ) + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.ConnectionStatePending, awstypes.ConnectionStateOrdering, awstypes.ConnectionStateAvailable, awstypes.ConnectionStateRequested, awstypes.ConnectionStateDeleting), + Target: []string{}, + Refresh: statusConnection(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Connection); ok { + return output, err + } + + return nil, err +} diff --git a/internal/service/directconnect/connection_association.go b/internal/service/directconnect/connection_association.go index 2454f8caebe..47ebbbba36e 100644 --- a/internal/service/directconnect/connection_association.go +++ b/internal/service/directconnect/connection_association.go @@ -7,20 +7,23 @@ import ( "context" "fmt" "log" + "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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" "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_dx_connection_association") -func ResourceConnectionAssociation() *schema.Resource { +// @SDKResource("aws_dx_connection_association", name="Connection LAG Association") +func resourceConnectionAssociation() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceConnectionAssociationCreate, ReadWithoutTimeout: resourceConnectionAssociationRead, @@ -43,7 +46,7 @@ func ResourceConnectionAssociation() *schema.Resource { func resourceConnectionAssociationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) connectionID := d.Get(names.AttrConnectionID).(string) lagID := d.Get("lag_id").(string) @@ -52,24 +55,23 @@ func resourceConnectionAssociationCreate(ctx context.Context, d *schema.Resource LagId: aws.String(lagID), } - log.Printf("[DEBUG] Creating Direct Connect Connection LAG Association: %s", input) - output, err := conn.AssociateConnectionWithLagWithContext(ctx, input) + output, err := conn.AssociateConnectionWithLag(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect Connection (%s) LAG (%s) Association: %s", connectionID, lagID, err) } - d.SetId(aws.StringValue(output.ConnectionId)) + d.SetId(aws.ToString(output.ConnectionId)) - return diags + return append(diags, resourceConnectionAssociationRead(ctx, d, meta)...) } func resourceConnectionAssociationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) lagID := d.Get("lag_id").(string) - err := FindConnectionAssociationExists(ctx, conn, d.Id(), lagID) + err := findConnectionLAGAssociation(ctx, conn, d.Id(), lagID) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Connection (%s) LAG (%s) Association not found, removing from state", d.Id(), lagID) @@ -86,41 +88,51 @@ func resourceConnectionAssociationRead(ctx context.Context, d *schema.ResourceDa func resourceConnectionAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if err := deleteConnectionLAGAssociation(ctx, conn, d.Id(), d.Get("lag_id").(string)); err != nil { return sdkdiag.AppendFromErr(diags, err) } + return diags } -func deleteConnectionLAGAssociation(ctx context.Context, conn *directconnect.DirectConnect, connectionID, lagID string) error { +func deleteConnectionLAGAssociation(ctx context.Context, conn *directconnect.Client, connectionID, lagID string) error { input := &directconnect.DisassociateConnectionFromLagInput{ ConnectionId: aws.String(connectionID), LagId: aws.String(lagID), } - _, err := tfresource.RetryWhen(ctx, connectionDisassociatedTimeout, + const ( + timeout = 1 * time.Minute + ) + _, err := tfresource.RetryWhenIsAErrorMessageContains[*awstypes.DirectConnectClientException](ctx, timeout, func() (interface{}, error) { - return conn.DisassociateConnectionFromLagWithContext(ctx, input) - }, - func(err error) (bool, error) { - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Connection does not exist") || - tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Lag does not exist") { - return false, nil - } + return conn.DisassociateConnectionFromLag(ctx, input) + }, "is in a transitioning state") - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "is in a transitioning state") { - return true, err - } - - return false, err - }, - ) + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Connection does not exist") || + errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Lag does not exist") { + return nil + } if err != nil { return fmt.Errorf("deleting Direct Connect Connection (%s) LAG (%s) Association: %w", connectionID, lagID, err) } - return err + return nil +} + +func findConnectionLAGAssociation(ctx context.Context, conn *directconnect.Client, connectionID, lagID string) error { + connection, err := findConnectionByID(ctx, conn, connectionID) + + if err != nil { + return err + } + + if lagID != aws.ToString(connection.LagId) { + return &retry.NotFoundError{} + } + + return nil } diff --git a/internal/service/directconnect/connection_association_test.go b/internal/service/directconnect/connection_association_test.go index e8d95ad5075..69d99859595 100644 --- a/internal/service/directconnect/connection_association_test.go +++ b/internal/service/directconnect/connection_association_test.go @@ -39,6 +39,29 @@ func TestAccDirectConnectConnectionAssociation_basic(t *testing.T) { }) } +func TestAccDirectConnectConnectionAssociation_disappears(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_dx_connection_association.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConnectionAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccConnectionAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConnectionAssociationExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfdirectconnect.ResourceConnectionAssociation(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccDirectConnectConnectionAssociation_lagOnConnection(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_dx_connection_association.test" @@ -85,14 +108,14 @@ func TestAccDirectConnectConnectionAssociation_multiple(t *testing.T) { func testAccCheckConnectionAssociationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_connection_association" { continue } - err := tfdirectconnect.FindConnectionAssociationExists(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["lag_id"]) + err := tfdirectconnect.FindConnectionLAGAssociation(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["lag_id"]) if tfresource.NotFound(err) { continue @@ -109,20 +132,16 @@ func testAccCheckConnectionAssociationDestroy(ctx context.Context) resource.Test } } -func testAccCheckConnectionAssociationExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckConnectionAssociationExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) - - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) - return tfdirectconnect.FindConnectionAssociationExists(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["lag_id"]) + return tfdirectconnect.FindConnectionLAGAssociation(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["lag_id"]) } } diff --git a/internal/service/directconnect/connection_confirmation.go b/internal/service/directconnect/connection_confirmation.go index 7e20e98ad39..fe9de3c39f4 100644 --- a/internal/service/directconnect/connection_confirmation.go +++ b/internal/service/directconnect/connection_confirmation.go @@ -6,23 +6,27 @@ package directconnect import ( "context" "log" + "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "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_dx_connection_confirmation") -func ResourceConnectionConfirmation() *schema.Resource { +// @SDKResource("aws_dx_connection_confirmation", name="Connection Confirmation") +func resourceConnectionConfirmation() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceConnectionConfirmationCreate, ReadWithoutTimeout: resourceConnectionConfirmationRead, - DeleteWithoutTimeout: resourceConnectionConfirmationDelete, + DeleteWithoutTimeout: schema.NoopContext, Schema: map[string]*schema.Schema{ names.AttrConnectionID: { @@ -36,24 +40,23 @@ func ResourceConnectionConfirmation() *schema.Resource { func resourceConnectionConfirmationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) connectionID := d.Get(names.AttrConnectionID).(string) input := &directconnect.ConfirmConnectionInput{ ConnectionId: aws.String(connectionID), } - log.Printf("[DEBUG] Confirming Direct Connect Connection: %s", input) - _, err := conn.ConfirmConnectionWithContext(ctx, input) + _, err := conn.ConfirmConnection(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "confirming Direct Connection Connection (%s): %s", connectionID, err) + return sdkdiag.AppendErrorf(diags, "confirming Direct Connect Connection (%s): %s", connectionID, err) } d.SetId(connectionID) if _, err := waitConnectionConfirmed(ctx, conn, d.Id()); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Direct Connection Connection (%s) confirm: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Connection (%s) confirm: %s", d.Id(), err) } return diags @@ -61,9 +64,9 @@ func resourceConnectionConfirmationCreate(ctx context.Context, d *schema.Resourc func resourceConnectionConfirmationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - _, err := FindConnectionByID(ctx, conn, d.Id()) + _, err := findConnectionByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Connection (%s) not found, removing from state", d.Id()) @@ -78,8 +81,22 @@ func resourceConnectionConfirmationRead(ctx context.Context, d *schema.ResourceD return diags } -func resourceConnectionConfirmationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - log.Printf("[WARN] Will not delete Direct Connect connection. Terraform will remove this resource from the state file, however resources may remain.") - return diags +func waitConnectionConfirmed(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Connection, error) { //nolint:unparam + const ( + timeout = 10 * time.Minute + ) + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.ConnectionStatePending, awstypes.ConnectionStateOrdering, awstypes.ConnectionStateRequested), + Target: enum.Slice(awstypes.ConnectionStateAvailable), + Refresh: statusConnection(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Connection); ok { + return output, err + } + + return nil, err } diff --git a/internal/service/directconnect/connection_confirmation_test.go b/internal/service/directconnect/connection_confirmation_test.go index a3c89c1a21b..65280d0d928 100644 --- a/internal/service/directconnect/connection_confirmation_test.go +++ b/internal/service/directconnect/connection_confirmation_test.go @@ -5,12 +5,10 @@ package directconnect_test import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -23,13 +21,10 @@ import ( func TestAccDirectConnectConnectionConfirmation_basic(t *testing.T) { ctx := acctest.Context(t) - env, err := testAccCheckHostedConnectionEnv() - if err != nil { - acctest.Skip(t, err.Error()) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "TEST_AWS_DX_CONNECTION_ID") + ownerAccountID := acctest.SkipIfEnvVarNotSet(t, "TEST_AWS_DX_OWNER_ACCOUNT_ID") var providers []*schema.Provider - connectionName := fmt.Sprintf("tf-dx-%s", sdkacctest.RandString(5)) resourceName := "aws_dx_connection_confirmation.test" providerFunc := testAccConnectionConfirmationProvider(&providers, 0) @@ -45,26 +40,22 @@ func TestAccDirectConnectConnectionConfirmation_basic(t *testing.T) { CheckDestroy: testAccCheckHostedConnectionDestroy(ctx, altProviderFunc), Steps: []resource.TestStep{ { - Config: testAccConnectionConfirmationConfig_basic(connectionName, env.ConnectionId, env.OwnerAccountId), + Config: testAccConnectionConfirmationConfig_basic(connectionName, connectionID, ownerAccountID), Check: testAccCheckConnectionConfirmationExists(ctx, resourceName, providerFunc), }, }, }) } -func testAccCheckConnectionConfirmationExists(ctx context.Context, name string, providerFunc func() *schema.Provider) resource.TestCheckFunc { +func testAccCheckConnectionConfirmationExists(ctx context.Context, n string, providerFunc func() *schema.Provider) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.ID == "" { - return errors.New("No Direct Connect Connection ID is set") + return fmt.Errorf("Not found: %s", n) } provider := providerFunc() - conn := provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) connection, err := tfdirectconnect.FindConnectionByID(ctx, conn, rs.Primary.ID) @@ -72,24 +63,24 @@ func testAccCheckConnectionConfirmationExists(ctx context.Context, name string, return err } - if state := aws.StringValue(connection.ConnectionState); state != directconnect.ConnectionStateAvailable { - return fmt.Errorf("Direct Connect Connection %s in unexpected state: %s", rs.Primary.ID, state) + if connection.ConnectionState != awstypes.ConnectionStateAvailable { + return fmt.Errorf("Direct Connect Connection %s in unexpected state: %s", rs.Primary.ID, string(connection.ConnectionState)) } return nil } } -func testAccConnectionConfirmationConfig_basic(name, connectionId, ownerAccountId string) string { +func testAccConnectionConfirmationConfig_basic(name, connectionID, ownerAccountID string) string { return acctest.ConfigCompose( acctest.ConfigAlternateAccountProvider(), fmt.Sprintf(` resource "aws_dx_hosted_connection" "connection" { provider = "awsalternate" - name = "%s" - connection_id = "%s" - owner_account_id = "%s" + name = %[1]q + connection_id = %[2]q + owner_account_id = %[3]q bandwidth = "100Mbps" vlan = 4092 } @@ -97,7 +88,7 @@ resource "aws_dx_hosted_connection" "connection" { resource "aws_dx_connection_confirmation" "test" { connection_id = aws_dx_hosted_connection.connection.id } -`, name, connectionId, ownerAccountId)) +`, name, connectionID, ownerAccountID)) } func testAccConnectionConfirmationProvider(providers *[]*schema.Provider, index int) func() *schema.Provider { diff --git a/internal/service/directconnect/connection_data_source.go b/internal/service/directconnect/connection_data_source.go index 7f8253b2184..54ffd79ff06 100644 --- a/internal/service/directconnect/connection_data_source.go +++ b/internal/service/directconnect/connection_data_source.go @@ -7,19 +7,21 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "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" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_dx_connection") -func DataSourceConnection() *schema.Resource { +// @SDKDataSource("aws_dx_connection", name="Connection") +func dataSourceConnection() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceConnectionRead, @@ -67,43 +69,26 @@ func DataSourceConnection() *schema.Resource { func dataSourceConnectionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - var connections []*directconnect.Connection - input := &directconnect.DescribeConnectionsInput{} name := d.Get(names.AttrName).(string) + input := &directconnect.DescribeConnectionsInput{} - // DescribeConnections is not paginated. - output, err := conn.DescribeConnectionsWithContext(ctx, input) + connection, err := findConnection(ctx, conn, input, func(v *awstypes.Connection) bool { + return aws.ToString(v.ConnectionName) == name + }) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Direct Connect Connections: %s", err) - } - - for _, connection := range output.Connections { - if aws.StringValue(connection.ConnectionName) == name { - connections = append(connections, connection) - } + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("Direct Connect Connection", err)) } - switch count := len(connections); count { - case 0: - return sdkdiag.AppendErrorf(diags, "no matching Direct Connect Connection found") - case 1: - default: - return sdkdiag.AppendErrorf(diags, "%d Direct Connect Connections matched; use additional constraints to reduce matches to a single Direct Connect Connection", count) - } - - connection := connections[0] - - d.SetId(aws.StringValue(connection.ConnectionId)) - + d.SetId(aws.ToString(connection.ConnectionId)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, - Region: aws.StringValue(connection.Region), + Region: aws.ToString(connection.Region), Service: "directconnect", - AccountID: aws.StringValue(connection.OwnerAccount), + AccountID: aws.ToString(connection.OwnerAccount), Resource: fmt.Sprintf("dxcon/%s", d.Id()), }.String() d.Set(names.AttrARN, arn) diff --git a/internal/service/directconnect/connection_test.go b/internal/service/directconnect/connection_test.go index f3194f7b451..a0bfb205051 100644 --- a/internal/service/directconnect/connection_test.go +++ b/internal/service/directconnect/connection_test.go @@ -6,11 +6,10 @@ package directconnect_test import ( "context" "fmt" - "os" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -23,7 +22,7 @@ import ( func TestAccDirectConnectConnection_basic(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -48,7 +47,6 @@ func TestAccDirectConnectConnection_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan_id", acctest.Ct0), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -61,7 +59,7 @@ func TestAccDirectConnectConnection_basic(t *testing.T) { func TestAccDirectConnectConnection_disappears(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -85,22 +83,13 @@ func TestAccDirectConnectConnection_disappears(t *testing.T) { func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { ctx := acctest.Context(t) - dxKey := "DX_CONNECTION_ID" - connectionId := os.Getenv(dxKey) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", dxKey) - } - - dxName := "DX_CONNECTION_NAME" - connectionName := os.Getenv(dxName) - if connectionName == "" { - t.Skipf("Environment variable %s is not set", dxName) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") + connectionName := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_NAME") - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" - ckn := testAccDirecConnectMacSecGenerateHex() - cak := testAccDirecConnectMacSecGenerateHex() + ckn := testAccMacSecGenerateHex() + cak := testAccMacSecGenerateHex() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -112,7 +101,7 @@ func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { Config: testAccConnectionConfig_encryptionModeShouldEncrypt(connectionName, ckn, cak), ResourceName: resourceName, ImportState: true, - ImportStateId: connectionId, + ImportStateId: connectionID, ImportStatePersist: true, }, { @@ -143,7 +132,7 @@ func TestAccDirectConnectConnection_encryptionMode(t *testing.T) { func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -179,7 +168,7 @@ func TestAccDirectConnectConnection_macsecRequested(t *testing.T) { func TestAccDirectConnectConnection_providerName(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -202,7 +191,6 @@ func TestAccDirectConnectConnection_providerName(t *testing.T) { resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -215,7 +203,7 @@ func TestAccDirectConnectConnection_providerName(t *testing.T) { func TestAccDirectConnectConnection_skipDestroy(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -238,7 +226,7 @@ func TestAccDirectConnectConnection_skipDestroy(t *testing.T) { func TestAccDirectConnectConnection_tags(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -257,7 +245,6 @@ func TestAccDirectConnectConnection_tags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -290,7 +277,7 @@ 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 + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -327,7 +314,7 @@ func TestAccDirectConnectConnection_vlanIDMigration501(t *testing.T) { func TestAccDirectConnectConnection_vlanIDMigration510(t *testing.T) { ctx := acctest.Context(t) - var connection directconnect.Connection + var connection awstypes.Connection resourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -353,7 +340,10 @@ func TestAccDirectConnectConnection_vlanIDMigration510(t *testing.T) { { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Config: testAccConnectionConfig_basic(rName), - PlanOnly: true, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckConnectionExists(ctx, resourceName, &connection), + resource.TestCheckResourceAttr(resourceName, "vlan_id", acctest.Ct0), + ), }, }, }) @@ -361,7 +351,7 @@ func TestAccDirectConnectConnection_vlanIDMigration510(t *testing.T) { func testAccCheckConnectionDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_connection" { @@ -385,26 +375,22 @@ func testAccCheckConnectionDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckConnectionExists(ctx context.Context, name string, v *directconnect.Connection) resource.TestCheckFunc { +func testAccCheckConnectionExists(ctx context.Context, n string, v *awstypes.Connection) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) - - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No Direct Connect Connection ID is set") - } + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) - connection, err := tfdirectconnect.FindConnectionByID(ctx, conn, rs.Primary.ID) + output, err := tfdirectconnect.FindConnectionByID(ctx, conn, rs.Primary.ID) if err != nil { return err } - *v = *connection + *v = *output return nil } @@ -412,7 +398,7 @@ func testAccCheckConnectionExists(ctx context.Context, name string, v *directcon func testAccCheckConnectionNoDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_connection" { diff --git a/internal/service/directconnect/exports_test.go b/internal/service/directconnect/exports_test.go index c8eb889439a..51b9e0438af 100644 --- a/internal/service/directconnect/exports_test.go +++ b/internal/service/directconnect/exports_test.go @@ -5,5 +5,35 @@ package directconnect // Exports for use in tests only. var ( - ValidConnectionBandWidth = validConnectionBandWidth + ResourceBGPPeer = resourceBGPPeer + ResourceConnection = resourceConnection + ResourceConnectionAssociation = resourceConnectionAssociation + ResourceConnectionConfirmation = resourceConnectionConfirmation + ResourceGateway = resourceGateway + ResourceGatewayAssociation = resourceGatewayAssociation + ResourceGatewayAssociationProposal = resourceGatewayAssociationProposal + ResourceHostedConnection = resourceHostedConnection + ResourceHostedPrivateVirtualInterface = resourceHostedPrivateVirtualInterface + ResourceHostedPrivateVirtualInterfaceAccepter = resourceHostedPrivateVirtualInterfaceAccepter + ResourceHostedPublicVirtualInterface = resourceHostedPublicVirtualInterface + ResourceHostedPublicVirtualInterfaceAccepter = resourceHostedPublicVirtualInterfaceAccepter + ResourceHostedTransitVirtualInterface = resourceHostedTransitVirtualInterface + ResourceHostedTransitVirtualInterfaceAccepter = resourceHostedTransitVirtualInterfaceAccepter + ResourceLag = resourceLag + ResourceMacSecKeyAssociation = resourceMacSecKeyAssociation + ResourcePrivateVirtualInterface = resourcePrivateVirtualInterface + ResourcePublicVirtualInterface = resourcePublicVirtualInterface + ResourceTransitVirtualInterface = resourceTransitVirtualInterface + + FindBGPPeerByThreePartKey = findBGPPeerByThreePartKey + FindConnectionByID = findConnectionByID + FindConnectionLAGAssociation = findConnectionLAGAssociation + FindGatewayAssociationByID = findGatewayAssociationByID + FindGatewayAssociationProposalByID = findGatewayAssociationProposalByID + FindGatewayByID = findGatewayByID + FindHostedConnectionByID = findHostedConnectionByID + FindLagByID = findLagByID + FindMacSecKeyByTwoPartKey = findMacSecKeyByTwoPartKey + FindVirtualInterfaceByID = findVirtualInterfaceByID + GatewayAssociationStateUpgradeV0 = gatewayAssociationStateUpgradeV0 ) diff --git a/internal/service/directconnect/find.go b/internal/service/directconnect/find.go deleted file mode 100644 index a9bd6ce7a18..00000000000 --- a/internal/service/directconnect/find.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect - -import ( - "context" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" -) - -func FindConnectionByID(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { - input := &directconnect.DescribeConnectionsInput{ - ConnectionId: aws.String(id), - } - - output, err := conn.DescribeConnectionsWithContext(ctx, input) - - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Could not find Connection with ID") { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil || len(output.Connections) == 0 || output.Connections[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.Connections); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - connection := output.Connections[0] - - if state := aws.StringValue(connection.ConnectionState); state == directconnect.ConnectionStateDeleted || state == directconnect.ConnectionStateRejected { - return nil, &retry.NotFoundError{ - Message: state, - LastRequest: input, - } - } - - return connection, nil -} - -func FindConnectionAssociationExists(ctx context.Context, conn *directconnect.DirectConnect, connectionID, lagID string) error { - connection, err := FindConnectionByID(ctx, conn, connectionID) - - if err != nil { - return err - } - - if lagID != aws.StringValue(connection.LagId) { - return &retry.NotFoundError{} - } - - return nil -} - -func FindGatewayByID(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Gateway, error) { - input := &directconnect.DescribeDirectConnectGatewaysInput{ - DirectConnectGatewayId: aws.String(id), - } - - output, err := conn.DescribeDirectConnectGatewaysWithContext(ctx, input) - - if err != nil { - return nil, err - } - - if output == nil || len(output.DirectConnectGateways) == 0 || output.DirectConnectGateways[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.DirectConnectGateways); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - gateway := output.DirectConnectGateways[0] - - if state := aws.StringValue(gateway.DirectConnectGatewayState); state == directconnect.GatewayStateDeleted { - return nil, &retry.NotFoundError{ - Message: state, - LastRequest: input, - } - } - - return gateway, nil -} - -func FindGatewayAssociationByID(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.GatewayAssociation, error) { - input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociationId: aws.String(id), - } - - return FindGatewayAssociation(ctx, conn, input) -} - -func FindGatewayAssociationByGatewayIDAndAssociatedGatewayID(ctx context.Context, conn *directconnect.DirectConnect, directConnectGatewayID, associatedGatewayID string) (*directconnect.GatewayAssociation, error) { - input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ - AssociatedGatewayId: aws.String(associatedGatewayID), - DirectConnectGatewayId: aws.String(directConnectGatewayID), - } - - return FindGatewayAssociation(ctx, conn, input) -} - -func FindGatewayAssociationByGatewayIDAndVirtualGatewayID(ctx context.Context, conn *directconnect.DirectConnect, directConnectGatewayID, virtualGatewayID string) (*directconnect.GatewayAssociation, error) { - input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ - DirectConnectGatewayId: aws.String(directConnectGatewayID), - VirtualGatewayId: aws.String(virtualGatewayID), - } - - return FindGatewayAssociation(ctx, conn, input) -} - -func FindGatewayAssociation(ctx context.Context, conn *directconnect.DirectConnect, input *directconnect.DescribeDirectConnectGatewayAssociationsInput) (*directconnect.GatewayAssociation, error) { - output, err := conn.DescribeDirectConnectGatewayAssociationsWithContext(ctx, input) - - if err != nil { - return nil, err - } - - if output == nil || len(output.DirectConnectGatewayAssociations) == 0 || output.DirectConnectGatewayAssociations[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.DirectConnectGatewayAssociations); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - association := output.DirectConnectGatewayAssociations[0] - - if state := aws.StringValue(association.AssociationState); state == directconnect.GatewayAssociationStateDisassociated { - return nil, &retry.NotFoundError{ - Message: state, - LastRequest: input, - } - } - - if association.AssociatedGateway == nil { - return nil, &retry.NotFoundError{ - Message: "Empty AssociatedGateway", - LastRequest: input, - } - } - - return association, nil -} - -func FindGatewayAssociationProposalByID(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.GatewayAssociationProposal, error) { - input := &directconnect.DescribeDirectConnectGatewayAssociationProposalsInput{ - ProposalId: aws.String(id), - } - - output, err := conn.DescribeDirectConnectGatewayAssociationProposalsWithContext(ctx, input) - - if err != nil { - return nil, err - } - - if output == nil || len(output.DirectConnectGatewayAssociationProposals) == 0 || output.DirectConnectGatewayAssociationProposals[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.DirectConnectGatewayAssociationProposals); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - proposal := output.DirectConnectGatewayAssociationProposals[0] - - if state := aws.StringValue(proposal.ProposalState); state == directconnect.GatewayAssociationProposalStateDeleted { - return nil, &retry.NotFoundError{ - Message: state, - LastRequest: input, - } - } - - if proposal.AssociatedGateway == nil { - return nil, &retry.NotFoundError{ - Message: "Empty AssociatedGateway", - LastRequest: input, - } - } - - return proposal, nil -} - -func FindHostedConnectionByID(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { - input := &directconnect.DescribeHostedConnectionsInput{ - ConnectionId: aws.String(id), - } - - output, err := conn.DescribeHostedConnectionsWithContext(ctx, input) - - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Could not find Connection with ID") { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil || len(output.Connections) == 0 || output.Connections[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.Connections); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - connection := output.Connections[0] - - if state := aws.StringValue(connection.ConnectionState); state == directconnect.ConnectionStateDeleted || state == directconnect.ConnectionStateRejected { - return nil, &retry.NotFoundError{ - Message: state, - LastRequest: input, - } - } - - return connection, nil -} - -func FindLagByID(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Lag, error) { - input := &directconnect.DescribeLagsInput{ - LagId: aws.String(id), - } - - output, err := conn.DescribeLagsWithContext(ctx, input) - - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Could not find Lag with ID") { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil || len(output.Lags) == 0 || output.Lags[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.Lags); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - lag := output.Lags[0] - - if state := aws.StringValue(lag.LagState); state == directconnect.LagStateDeleted { - return nil, &retry.NotFoundError{ - Message: state, - LastRequest: input, - } - } - - return lag, nil -} - -func FindLocationByCode(ctx context.Context, conn *directconnect.DirectConnect, code string) (*directconnect.Location, error) { - input := &directconnect.DescribeLocationsInput{} - - locations, err := FindLocations(ctx, conn, input) - - if err != nil { - return nil, err - } - - for _, location := range locations { - if aws.StringValue(location.LocationCode) == code { - return location, nil - } - } - - return nil, tfresource.NewEmptyResultError(input) -} - -func FindLocations(ctx context.Context, conn *directconnect.DirectConnect, input *directconnect.DescribeLocationsInput) ([]*directconnect.Location, error) { - output, err := conn.DescribeLocationsWithContext(ctx, input) - - if err != nil { - return nil, err - } - - if output == nil || len(output.Locations) == 0 { - return nil, tfresource.NewEmptyResultError(input) - } - - return output.Locations, nil -} diff --git a/internal/service/directconnect/gateway.go b/internal/service/directconnect/gateway.go index 3939ca2c45f..02404897d86 100644 --- a/internal/service/directconnect/gateway.go +++ b/internal/service/directconnect/gateway.go @@ -5,24 +5,29 @@ package directconnect import ( "context" + "errors" "log" - "strconv" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_gateway") -func ResourceGateway() *schema.Resource { +// @SDKResource("aws_dx_gateway", name="Gateway") +func resourceGateway() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceGatewayCreate, ReadWithoutTimeout: resourceGatewayRead, @@ -59,7 +64,7 @@ func ResourceGateway() *schema.Resource { func resourceGatewayCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) name := d.Get(names.AttrName).(string) input := &directconnect.CreateDirectConnectGatewayInput{ @@ -67,17 +72,16 @@ func resourceGatewayCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.Get("amazon_side_asn").(string); ok && v != "" { - v, _ := strconv.ParseInt(v, 10, 64) - input.AmazonSideAsn = aws.Int64(v) + input.AmazonSideAsn = flex.StringValueToInt64(v) } - output, err := conn.CreateDirectConnectGatewayWithContext(ctx, input) + output, err := conn.CreateDirectConnectGateway(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect Gateway (%s): %s", name, err) } - d.SetId(aws.StringValue(output.DirectConnectGateway.DirectConnectGatewayId)) + d.SetId(aws.ToString(output.DirectConnectGateway.DirectConnectGatewayId)) if _, err := waitGatewayCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway (%s) create: %s", d.Id(), err) @@ -88,9 +92,9 @@ func resourceGatewayCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceGatewayRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - output, err := FindGatewayByID(ctx, conn, d.Id()) + output, err := findGatewayByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Gateway (%s) not found, removing from state", d.Id()) @@ -102,7 +106,7 @@ func resourceGatewayRead(ctx context.Context, d *schema.ResourceData, meta inter return sdkdiag.AppendErrorf(diags, "reading Direct Connect Gateway (%s): %s", d.Id(), err) } - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(output.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", flex.Int64ToStringValue(output.AmazonSideAsn)) d.Set(names.AttrName, output.DirectConnectGatewayName) d.Set(names.AttrOwnerAccountID, output.OwnerAccount) @@ -111,7 +115,7 @@ func resourceGatewayRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceGatewayUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if d.HasChange(names.AttrName) { input := &directconnect.UpdateDirectConnectGatewayInput{ @@ -119,7 +123,7 @@ func resourceGatewayUpdate(ctx context.Context, d *schema.ResourceData, meta int NewDirectConnectGatewayName: aws.String(d.Get(names.AttrName).(string)), } - _, err := conn.UpdateDirectConnectGatewayWithContext(ctx, input) + _, err := conn.UpdateDirectConnectGateway(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Direct Connect Gateway (%s): %s", d.Id(), err) @@ -131,14 +135,14 @@ func resourceGatewayUpdate(ctx context.Context, d *schema.ResourceData, meta int func resourceGatewayDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) log.Printf("[DEBUG] Deleting Direct Connect Gateway: %s", d.Id()) - _, err := conn.DeleteDirectConnectGatewayWithContext(ctx, &directconnect.DeleteDirectConnectGatewayInput{ + _, err := conn.DeleteDirectConnectGateway(ctx, &directconnect.DeleteDirectConnectGatewayInput{ DirectConnectGatewayId: aws.String(d.Id()), }) - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "does not exist") { + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "does not exist") { return diags } @@ -152,3 +156,111 @@ func resourceGatewayDelete(ctx context.Context, d *schema.ResourceData, meta int return diags } + +func findGatewayByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.DirectConnectGateway, error) { + input := &directconnect.DescribeDirectConnectGatewaysInput{ + DirectConnectGatewayId: aws.String(id), + } + output, err := findGateway(ctx, conn, input, tfslices.PredicateTrue[*awstypes.DirectConnectGateway]()) + + if err != nil { + return nil, err + } + + if state := output.DirectConnectGatewayState; state == awstypes.DirectConnectGatewayStateDeleted { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, + } + } + + return output, nil +} + +func findGateway(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewaysInput, filter tfslices.Predicate[*awstypes.DirectConnectGateway]) (*awstypes.DirectConnectGateway, error) { + output, err := findGateways(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findGateways(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewaysInput, filter tfslices.Predicate[*awstypes.DirectConnectGateway]) ([]awstypes.DirectConnectGateway, error) { + var output []awstypes.DirectConnectGateway + + err := describeDirectConnectGatewaysPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DirectConnectGateways { + if filter(&v) { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} + +func statusGateway(ctx context.Context, conn *directconnect.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findGatewayByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.DirectConnectGatewayState), nil + } +} + +func waitGatewayCreated(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.DirectConnectGateway, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.DirectConnectGatewayStatePending), + Target: enum.Slice(awstypes.DirectConnectGatewayStateAvailable), + Refresh: statusGateway(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.DirectConnectGateway); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateChangeError))) + + return output, err + } + + return nil, err +} + +func waitGatewayDeleted(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.DirectConnectGateway, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.DirectConnectGatewayStatePending, awstypes.DirectConnectGatewayStateAvailable, awstypes.DirectConnectGatewayStateDeleting), + Target: []string{}, + Refresh: statusGateway(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.DirectConnectGateway); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateChangeError))) + + return output, err + } + + return nil, err +} diff --git a/internal/service/directconnect/gateway_association.go b/internal/service/directconnect/gateway_association.go index 5f2b9012615..d734cd74783 100644 --- a/internal/service/directconnect/gateway_association.go +++ b/internal/service/directconnect/gateway_association.go @@ -5,24 +5,29 @@ package directconnect import ( "context" + "errors" "fmt" "log" "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -// @SDKResource("aws_dx_gateway_association") -func ResourceGatewayAssociation() *schema.Resource { +// @SDKResource("aws_dx_gateway_association", name="Gateway Association") +func resourceGatewayAssociation() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceGatewayAssociationCreate, ReadWithoutTimeout: resourceGatewayAssociationRead, @@ -37,7 +42,7 @@ func ResourceGatewayAssociation() *schema.Resource { StateUpgraders: []schema.StateUpgrader{ { Type: resourceGatewayAssociationResourceV0().CoreConfigSchema().ImpliedType(), - Upgrade: GatewayAssociationStateUpgradeV0, + Upgrade: gatewayAssociationStateUpgradeV0, Version: 0, }, }, @@ -49,7 +54,6 @@ func ResourceGatewayAssociation() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "associated_gateway_id": { Type: schema.TypeString, Optional: true, @@ -58,7 +62,6 @@ func ResourceGatewayAssociation() *schema.Resource { ConflictsWith: []string{"associated_gateway_owner_account_id", "proposal_id"}, AtLeastOneOf: []string{"associated_gateway_id", "associated_gateway_owner_account_id", "proposal_id"}, }, - "associated_gateway_owner_account_id": { Type: schema.TypeString, Optional: true, @@ -69,35 +72,29 @@ func ResourceGatewayAssociation() *schema.Resource { RequiredWith: []string{"proposal_id"}, AtLeastOneOf: []string{"associated_gateway_id", "associated_gateway_owner_account_id", "proposal_id"}, }, - "associated_gateway_type": { Type: schema.TypeString, Computed: true, }, - "dx_gateway_association_id": { Type: schema.TypeString, Computed: true, }, - "dx_gateway_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "dx_gateway_owner_account_id": { Type: schema.TypeString, Computed: true, }, - "proposal_id": { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"associated_gateway_id", "vpn_gateway_id"}, AtLeastOneOf: []string{"associated_gateway_id", "associated_gateway_owner_account_id", "proposal_id"}, }, - "vpn_gateway_id": { Type: schema.TypeString, Optional: true, @@ -117,7 +114,7 @@ func ResourceGatewayAssociation() *schema.Resource { func resourceGatewayAssociationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) var associationID string directConnectGatewayID := d.Get("dx_gateway_id").(string) @@ -134,16 +131,15 @@ func resourceGatewayAssociationCreate(ctx context.Context, d *schema.ResourceDat input.OverrideAllowedPrefixesToDirectConnectGateway = expandRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Accepting Direct Connect Gateway Association Proposal: %s", input) - output, err := conn.AcceptDirectConnectGatewayAssociationProposalWithContext(ctx, input) + output, err := conn.AcceptDirectConnectGatewayAssociationProposal(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "accepting Direct Connect Gateway Association Proposal (%s): %s", proposalID, err) } // For historical reasons the resource ID isn't set to the association ID returned from the API. - associationID = aws.StringValue(output.DirectConnectGatewayAssociation.AssociationId) - d.SetId(GatewayAssociationCreateResourceID(directConnectGatewayID, aws.StringValue(output.DirectConnectGatewayAssociation.AssociatedGateway.Id))) + associationID = aws.ToString(output.DirectConnectGatewayAssociation.AssociationId) + d.SetId(gatewayAssociationCreateResourceID(directConnectGatewayID, aws.ToString(output.DirectConnectGatewayAssociation.AssociatedGateway.Id))) } else { associatedGatewayID := d.Get("associated_gateway_id").(string) input := &directconnect.CreateDirectConnectGatewayAssociationInput{ @@ -155,22 +151,21 @@ func resourceGatewayAssociationCreate(ctx context.Context, d *schema.ResourceDat input.AddAllowedPrefixesToDirectConnectGateway = expandRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Creating Direct Connect Gateway Association: %s", input) - output, err := conn.CreateDirectConnectGatewayAssociationWithContext(ctx, input) + output, err := conn.CreateDirectConnectGatewayAssociation(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect Gateway Association (%s/%s): %s", directConnectGatewayID, associatedGatewayID, err) } // For historical reasons the resource ID isn't set to the association ID returned from the API. - associationID = aws.StringValue(output.DirectConnectGatewayAssociation.AssociationId) - d.SetId(GatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID)) + associationID = aws.ToString(output.DirectConnectGatewayAssociation.AssociationId) + d.SetId(gatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID)) } d.Set("dx_gateway_association_id", associationID) if _, err := waitGatewayAssociationCreated(ctx, conn, associationID, d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway Association (%s) to create: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway Association (%s) create: %s", d.Id(), err) } return append(diags, resourceGatewayAssociationRead(ctx, d, meta)...) @@ -178,11 +173,11 @@ func resourceGatewayAssociationCreate(ctx context.Context, d *schema.ResourceDat func resourceGatewayAssociationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) associationID := d.Get("dx_gateway_association_id").(string) - output, err := FindGatewayAssociationByID(ctx, conn, associationID) + output, err := findGatewayAssociationByID(ctx, conn, associationID) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Gateway Association (%s) not found, removing from state", d.Id()) @@ -197,7 +192,6 @@ func resourceGatewayAssociationRead(ctx context.Context, d *schema.ResourceData, if err := d.Set("allowed_prefixes", flattenRouteFilterPrefixes(output.AllowedPrefixesToDirectConnectGateway)); err != nil { return sdkdiag.AppendErrorf(diags, "setting allowed_prefixes: %s", err) } - d.Set("associated_gateway_id", output.AssociatedGateway.Id) d.Set("associated_gateway_owner_account_id", output.AssociatedGateway.OwnerAccount) d.Set("associated_gateway_type", output.AssociatedGateway.Type) @@ -210,33 +204,32 @@ func resourceGatewayAssociationRead(ctx context.Context, d *schema.ResourceData, func resourceGatewayAssociationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) associationID := d.Get("dx_gateway_association_id").(string) input := &directconnect.UpdateDirectConnectGatewayAssociationInput{ AssociationId: aws.String(associationID), } - oraw, nraw := d.GetChange("allowed_prefixes") - o, n := oraw.(*schema.Set), nraw.(*schema.Set) + o, n := d.GetChange("allowed_prefixes") + os, ns := o.(*schema.Set), n.(*schema.Set) - if add := n.Difference(o); add.Len() > 0 { + if add := ns.Difference(os); add.Len() > 0 { input.AddAllowedPrefixesToDirectConnectGateway = expandRouteFilterPrefixes(add.List()) } - if del := o.Difference(n); del.Len() > 0 { + if del := os.Difference(ns); del.Len() > 0 { input.RemoveAllowedPrefixesToDirectConnectGateway = expandRouteFilterPrefixes(del.List()) } - log.Printf("[DEBUG] Updating Direct Connect Gateway Association: %s", input) - _, err := conn.UpdateDirectConnectGatewayAssociationWithContext(ctx, input) + _, err := conn.UpdateDirectConnectGatewayAssociation(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Direct Connect Gateway Association (%s): %s", d.Id(), err) } if _, err := waitGatewayAssociationUpdated(ctx, conn, associationID, d.Timeout(schema.TimeoutUpdate)); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway Association (%s) to update: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway Association (%s) update: %s", d.Id(), err) } return append(diags, resourceGatewayAssociationRead(ctx, d, meta)...) @@ -244,16 +237,16 @@ func resourceGatewayAssociationUpdate(ctx context.Context, d *schema.ResourceDat func resourceGatewayAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) associationID := d.Get("dx_gateway_association_id").(string) log.Printf("[DEBUG] Deleting Direct Connect Gateway Association: %s", d.Id()) - _, err := conn.DeleteDirectConnectGatewayAssociationWithContext(ctx, &directconnect.DeleteDirectConnectGatewayAssociationInput{ + _, err := conn.DeleteDirectConnectGatewayAssociation(ctx, &directconnect.DeleteDirectConnectGatewayAssociationInput{ AssociationId: aws.String(associationID), }) - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "does not exist") { + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "does not exist") { return diags } @@ -262,14 +255,14 @@ func resourceGatewayAssociationDelete(ctx context.Context, d *schema.ResourceDat } if _, err := waitGatewayAssociationDeleted(ctx, conn, associationID, d.Timeout(schema.TimeoutDelete)); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway Association (%s) to delete: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Gateway Association (%s) delete: %s", d.Id(), err) } return diags } func resourceGatewayAssociationImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) parts := strings.Split(d.Id(), "/") @@ -280,15 +273,169 @@ func resourceGatewayAssociationImport(ctx context.Context, d *schema.ResourceDat directConnectGatewayID := parts[0] associatedGatewayID := parts[1] - output, err := FindGatewayAssociationByGatewayIDAndAssociatedGatewayID(ctx, conn, directConnectGatewayID, associatedGatewayID) + output, err := findGatewayAssociationByGatewayIDAndAssociatedGatewayID(ctx, conn, directConnectGatewayID, associatedGatewayID) if err != nil { return nil, err } - d.SetId(GatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID)) + d.SetId(gatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID)) d.Set("dx_gateway_id", output.DirectConnectGatewayId) d.Set("dx_gateway_association_id", output.AssociationId) return []*schema.ResourceData{d}, nil } + +func gatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID string) string { + return fmt.Sprintf("ga-%s%s", directConnectGatewayID, associatedGatewayID) +} + +func findGatewayAssociationByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.DirectConnectGatewayAssociation, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociationId: aws.String(id), + } + + return findNonDisassociatedGatewayAssociation(ctx, conn, input, tfslices.PredicateTrue[*awstypes.DirectConnectGatewayAssociation]()) +} + +func findGatewayAssociationByGatewayIDAndAssociatedGatewayID(ctx context.Context, conn *directconnect.Client, directConnectGatewayID, associatedGatewayID string) (*awstypes.DirectConnectGatewayAssociation, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + AssociatedGatewayId: aws.String(associatedGatewayID), + DirectConnectGatewayId: aws.String(directConnectGatewayID), + } + + return findNonDisassociatedGatewayAssociation(ctx, conn, input, tfslices.PredicateTrue[*awstypes.DirectConnectGatewayAssociation]()) +} + +func findGatewayAssociationByGatewayIDAndVirtualGatewayID(ctx context.Context, conn *directconnect.Client, directConnectGatewayID, virtualGatewayID string) (*awstypes.DirectConnectGatewayAssociation, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ + DirectConnectGatewayId: aws.String(directConnectGatewayID), + VirtualGatewayId: aws.String(virtualGatewayID), + } + + return findNonDisassociatedGatewayAssociation(ctx, conn, input, tfslices.PredicateTrue[*awstypes.DirectConnectGatewayAssociation]()) +} + +func findNonDisassociatedGatewayAssociation(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, filter tfslices.Predicate[*awstypes.DirectConnectGatewayAssociation]) (*awstypes.DirectConnectGatewayAssociation, error) { + output, err := findGatewayAssociation(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + if state := output.AssociationState; state == awstypes.DirectConnectGatewayAssociationStateDisassociated { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, + } + } + + return output, nil +} + +func findGatewayAssociation(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, filter tfslices.Predicate[*awstypes.DirectConnectGatewayAssociation]) (*awstypes.DirectConnectGatewayAssociation, error) { + output, err := findGatewayAssociations(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findGatewayAssociations(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, filter tfslices.Predicate[*awstypes.DirectConnectGatewayAssociation]) ([]awstypes.DirectConnectGatewayAssociation, error) { + var output []awstypes.DirectConnectGatewayAssociation + + err := describeDirectConnectGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DirectConnectGatewayAssociations { + if filter(&v) { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} + +func statusGatewayAssociation(ctx context.Context, conn *directconnect.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findGatewayAssociationByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.AssociationState), nil + } +} + +func waitGatewayAssociationCreated(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.DirectConnectGatewayAssociation, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.DirectConnectGatewayAssociationStateAssociating), + Target: enum.Slice(awstypes.DirectConnectGatewayAssociationStateAssociated), + Refresh: statusGatewayAssociation(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.DirectConnectGatewayAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateChangeError))) + + return output, err + } + + return nil, err +} + +func waitGatewayAssociationUpdated(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.DirectConnectGatewayAssociation, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.DirectConnectGatewayAssociationStateUpdating), + Target: enum.Slice(awstypes.DirectConnectGatewayAssociationStateAssociated), + Refresh: statusGatewayAssociation(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.DirectConnectGatewayAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateChangeError))) + + return output, err + } + + return nil, err +} + +func waitGatewayAssociationDeleted(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.DirectConnectGatewayAssociation, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.DirectConnectGatewayAssociationStateDisassociating), + Target: []string{}, + Refresh: statusGatewayAssociation(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.DirectConnectGatewayAssociation); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StateChangeError))) + + return output, err + } + + return nil, err +} diff --git a/internal/service/directconnect/gateway_association_migrate.go b/internal/service/directconnect/gateway_association_migrate.go index 6b655bb2816..407d528e654 100644 --- a/internal/service/directconnect/gateway_association_migrate.go +++ b/internal/service/directconnect/gateway_association_migrate.go @@ -7,7 +7,7 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -78,20 +78,20 @@ func resourceGatewayAssociationResourceV0() *schema.Resource { } } -func GatewayAssociationStateUpgradeV0(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) +func gatewayAssociationStateUpgradeV0(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) log.Println("[INFO] Found Direct Connect Gateway Association state v0; migrating to v1") // dx_gateway_association_id was introduced in v2.8.0. Handle the case where it's not yet present. if v, ok := rawState["dx_gateway_association_id"]; !ok || v == nil { - output, err := FindGatewayAssociationByGatewayIDAndVirtualGatewayID(ctx, conn, rawState["dx_gateway_id"].(string), rawState["vpn_gateway_id"].(string)) + output, err := findGatewayAssociationByGatewayIDAndVirtualGatewayID(ctx, conn, rawState["dx_gateway_id"].(string), rawState["vpn_gateway_id"].(string)) if err != nil { return nil, err } - rawState["dx_gateway_association_id"] = aws.StringValue(output.AssociationId) + rawState["dx_gateway_association_id"] = aws.ToString(output.AssociationId) } return rawState, nil diff --git a/internal/service/directconnect/gateway_association_proposal.go b/internal/service/directconnect/gateway_association_proposal.go index f64345ececd..4b3ba458514 100644 --- a/internal/service/directconnect/gateway_association_proposal.go +++ b/internal/service/directconnect/gateway_association_proposal.go @@ -9,20 +9,23 @@ import ( "log" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -// @SDKResource("aws_dx_gateway_association_proposal") -func ResourceGatewayAssociationProposal() *schema.Resource { +// @SDKResource("aws_dx_gateway_association_proposal", name="Gateway Association Proposal") +func resourceGatewayAssociationProposal() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceGatewayAssociationProposalCreate, ReadWithoutTimeout: resourceGatewayAssociationProposalRead, @@ -36,11 +39,11 @@ func ResourceGatewayAssociationProposal() *schema.Resource { // Accepting the proposal with overridden prefixes changes the returned RequestedAllowedPrefixesToDirectConnectGateway value (allowed_prefixes attribute). // We only want to force a new resource if this value changes and the current proposal state is "requested". customdiff.ForceNewIf("allowed_prefixes", func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) log.Printf("[DEBUG] CustomizeDiff for Direct Connect Gateway Association Proposal (%s) allowed_prefixes", d.Id()) - output, err := FindGatewayAssociationProposalByID(ctx, conn, d.Id()) + output, err := findGatewayAssociationProposalByID(ctx, conn, d.Id()) if tfresource.NotFound(err) { // Proposal may be end-of-life and removed by AWS. @@ -52,7 +55,7 @@ func ResourceGatewayAssociationProposal() *schema.Resource { return false } - return aws.StringValue(output.ProposalState) == directconnect.GatewayAssociationProposalStateRequested + return output.ProposalState == awstypes.DirectConnectGatewayAssociationProposalStateRequested }), ), @@ -63,29 +66,24 @@ func ResourceGatewayAssociationProposal() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "associated_gateway_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "associated_gateway_owner_account_id": { Type: schema.TypeString, Computed: true, }, - "associated_gateway_type": { Type: schema.TypeString, Computed: true, }, - "dx_gateway_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "dx_gateway_owner_account_id": { Type: schema.TypeString, Required: true, @@ -98,7 +96,7 @@ func ResourceGatewayAssociationProposal() *schema.Resource { func resourceGatewayAssociationProposalCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) directConnectGatewayID := d.Get("dx_gateway_id").(string) associatedGatewayID := d.Get("associated_gateway_id").(string) @@ -112,31 +110,30 @@ func resourceGatewayAssociationProposalCreate(ctx context.Context, d *schema.Res input.AddAllowedPrefixesToDirectConnectGateway = expandRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Creating Direct Connect Gateway Association Proposal: %s", input) - output, err := conn.CreateDirectConnectGatewayAssociationProposalWithContext(ctx, input) + output, err := conn.CreateDirectConnectGatewayAssociationProposal(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect Gateway Association Proposal (%s/%s): %s", directConnectGatewayID, associatedGatewayID, err) } - d.SetId(aws.StringValue(output.DirectConnectGatewayAssociationProposal.ProposalId)) + d.SetId(aws.ToString(output.DirectConnectGatewayAssociationProposal.ProposalId)) return append(diags, resourceGatewayAssociationProposalRead(ctx, d, meta)...) } func resourceGatewayAssociationProposalRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) // First attempt to find by proposal ID. - output, err := FindGatewayAssociationProposalByID(ctx, conn, d.Id()) + output, err := findGatewayAssociationProposalByID(ctx, conn, d.Id()) if tfresource.NotFound(err) { // Attempt to find an existing association. directConnectGatewayID := d.Get("dx_gateway_id").(string) associatedGatewayID := d.Get("associated_gateway_id").(string) - output, err := FindGatewayAssociationByGatewayIDAndAssociatedGatewayID(ctx, conn, directConnectGatewayID, associatedGatewayID) + output, err := findGatewayAssociationByGatewayIDAndAssociatedGatewayID(ctx, conn, directConnectGatewayID, associatedGatewayID) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Gateway Association Proposal (%s) not found, removing from state", d.Id()) @@ -181,14 +178,14 @@ func resourceGatewayAssociationProposalRead(ctx context.Context, d *schema.Resou func resourceGatewayAssociationProposalDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) log.Printf("[DEBUG] Deleting Direct Connect Gateway Association Proposal: %s", d.Id()) - _, err := conn.DeleteDirectConnectGatewayAssociationProposalWithContext(ctx, &directconnect.DeleteDirectConnectGatewayAssociationProposalInput{ + _, err := conn.DeleteDirectConnectGatewayAssociationProposal(ctx, &directconnect.DeleteDirectConnectGatewayAssociationProposalInput{ ProposalId: aws.String(d.Id()), }) - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "is not found") { + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "is not found") { return diags } @@ -199,21 +196,105 @@ func resourceGatewayAssociationProposalDelete(ctx context.Context, d *schema.Res return diags } -func expandRouteFilterPrefixes(tfList []interface{}) []*directconnect.RouteFilterPrefix { +func resourceGatewayAssociationProposalImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + switch parts := strings.Split(strings.ToLower(d.Id()), "/"); len(parts) { + case 1: + break + + case 3: + proposalID := parts[0] + directConnectGatewayID := parts[1] + associatedGatewayID := parts[2] + + if proposalID == "" || directConnectGatewayID == "" || associatedGatewayID == "" { + return nil, fmt.Errorf("Incorrect resource ID format: %q. PROPOSALID, DXGATEWAYID and ASSOCIATEDGATEWAYID must not be empty strings", d.Id()) + } + + // Use pseudo-proposal ID and actual DirectConnectGatewayId and AssociatedGatewayId. + d.SetId(proposalID) + d.Set("associated_gateway_id", associatedGatewayID) + d.Set("dx_gateway_id", directConnectGatewayID) + + default: + return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected PROPOSALID or PROPOSALID/DXGATEWAYID/ASSOCIATEDGATEWAYID", d.Id()) + } + + return []*schema.ResourceData{d}, nil +} + +func findGatewayAssociationProposalByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.DirectConnectGatewayAssociationProposal, error) { + input := &directconnect.DescribeDirectConnectGatewayAssociationProposalsInput{ + ProposalId: aws.String(id), + } + + output, err := findGatewayAssociationProposal(ctx, conn, input, tfslices.PredicateTrue[*awstypes.DirectConnectGatewayAssociationProposal]()) + + if err != nil { + return nil, err + } + + if state := output.ProposalState; state == awstypes.DirectConnectGatewayAssociationProposalStateDeleted { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, + } + } + + if output.AssociatedGateway == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + +func findGatewayAssociationProposal(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationProposalsInput, filter tfslices.Predicate[*awstypes.DirectConnectGatewayAssociationProposal]) (*awstypes.DirectConnectGatewayAssociationProposal, error) { + output, err := findGatewayAssociationProposals(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findGatewayAssociationProposals(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationProposalsInput, filter tfslices.Predicate[*awstypes.DirectConnectGatewayAssociationProposal]) ([]awstypes.DirectConnectGatewayAssociationProposal, error) { + var output []awstypes.DirectConnectGatewayAssociationProposal + + err := describeDirectConnectGatewayAssociationProposalsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DirectConnectGatewayAssociationProposals { + if filter(&v) { + output = append(output, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} + +func expandRouteFilterPrefixes(tfList []interface{}) []awstypes.RouteFilterPrefix { if len(tfList) == 0 { return nil } - var apiObjects []*directconnect.RouteFilterPrefix + var apiObjects []awstypes.RouteFilterPrefix for _, tfStringRaw := range tfList { tfString, ok := tfStringRaw.(string) - if !ok { continue } - apiObject := &directconnect.RouteFilterPrefix{ + apiObject := awstypes.RouteFilterPrefix{ Cidr: aws.String(tfString), } @@ -223,7 +304,7 @@ func expandRouteFilterPrefixes(tfList []interface{}) []*directconnect.RouteFilte return apiObjects } -func flattenRouteFilterPrefixes(apiObjects []*directconnect.RouteFilterPrefix) []interface{} { +func flattenRouteFilterPrefixes(apiObjects []awstypes.RouteFilterPrefix) []interface{} { if len(apiObjects) == 0 { return nil } @@ -231,38 +312,8 @@ func flattenRouteFilterPrefixes(apiObjects []*directconnect.RouteFilterPrefix) [ var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - - tfList = append(tfList, aws.StringValue(apiObject.Cidr)) + tfList = append(tfList, aws.ToString(apiObject.Cidr)) } return tfList } - -func resourceGatewayAssociationProposalImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - switch parts := strings.Split(strings.ToLower(d.Id()), "/"); len(parts) { - case 1: - break - - case 3: - proposalID := parts[0] - directConnectGatewayID := parts[1] - associatedGatewayID := parts[2] - - if proposalID == "" || directConnectGatewayID == "" || associatedGatewayID == "" { - return nil, fmt.Errorf("Incorrect resource ID format: %q. PROPOSALID, DXGATEWAYID and ASSOCIATEDGATEWAYID must not be empty strings", d.Id()) - } - - // Use pseudo-proposal ID and actual DirectConnectGatewayId and AssociatedGatewayId. - d.SetId(proposalID) - d.Set("associated_gateway_id", associatedGatewayID) - d.Set("dx_gateway_id", directConnectGatewayID) - - default: - return nil, fmt.Errorf("Incorrect resource ID format: %q. Expected PROPOSALID or PROPOSALID/DXGATEWAYID/ASSOCIATEDGATEWAYID", d.Id()) - } - - return []*schema.ResourceData{d}, nil -} diff --git a/internal/service/directconnect/gateway_association_proposal_test.go b/internal/service/directconnect/gateway_association_proposal_test.go index addbd621fd1..659e3076315 100644 --- a/internal/service/directconnect/gateway_association_proposal_test.go +++ b/internal/service/directconnect/gateway_association_proposal_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -23,7 +23,7 @@ import ( func TestAccDirectConnectGatewayAssociationProposal_basicVPNGateway(t *testing.T) { ctx := acctest.Context(t) - var proposal directconnect.GatewayAssociationProposal + var proposal awstypes.DirectConnectGatewayAssociationProposal rBgpAsn := sdkacctest.RandIntRange(64512, 65534) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_dx_gateway_association_proposal.test" @@ -59,7 +59,7 @@ func TestAccDirectConnectGatewayAssociationProposal_basicVPNGateway(t *testing.T func TestAccDirectConnectGatewayAssociationProposal_basicTransitGateway(t *testing.T) { ctx := acctest.Context(t) - var proposal directconnect.GatewayAssociationProposal + var proposal awstypes.DirectConnectGatewayAssociationProposal rBgpAsn := sdkacctest.RandIntRange(64512, 65534) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_dx_gateway_association_proposal.test" @@ -97,7 +97,7 @@ func TestAccDirectConnectGatewayAssociationProposal_basicTransitGateway(t *testi func TestAccDirectConnectGatewayAssociationProposal_disappears(t *testing.T) { ctx := acctest.Context(t) - var proposal directconnect.GatewayAssociationProposal + var proposal awstypes.DirectConnectGatewayAssociationProposal rBgpAsn := sdkacctest.RandIntRange(64512, 65534) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_dx_gateway_association_proposal.test" @@ -122,7 +122,7 @@ func TestAccDirectConnectGatewayAssociationProposal_disappears(t *testing.T) { func TestAccDirectConnectGatewayAssociationProposal_endOfLifeVPN(t *testing.T) { ctx := acctest.Context(t) - var proposal directconnect.GatewayAssociationProposal + var proposal awstypes.DirectConnectGatewayAssociationProposal rBgpAsn := sdkacctest.RandIntRange(64512, 65534) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_dx_gateway_association_proposal.test" @@ -145,9 +145,9 @@ func TestAccDirectConnectGatewayAssociationProposal_endOfLifeVPN(t *testing.T) { ResourceName: resourceName, ImportStateIdFunc: func(s *terraform.State) (string, error) { return strings.Join([]string{ - aws.StringValue(proposal.ProposalId), - aws.StringValue(proposal.DirectConnectGatewayId), - aws.StringValue(proposal.AssociatedGateway.Id), + aws.ToString(proposal.ProposalId), + aws.ToString(proposal.DirectConnectGatewayId), + aws.ToString(proposal.AssociatedGateway.Id), }, "/"), nil }, ImportState: true, @@ -159,7 +159,7 @@ func TestAccDirectConnectGatewayAssociationProposal_endOfLifeVPN(t *testing.T) { func TestAccDirectConnectGatewayAssociationProposal_endOfLifeTgw(t *testing.T) { ctx := acctest.Context(t) - var proposal directconnect.GatewayAssociationProposal + var proposal awstypes.DirectConnectGatewayAssociationProposal rBgpAsn := sdkacctest.RandIntRange(64512, 65534) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_dx_gateway_association_proposal.test" @@ -182,9 +182,9 @@ func TestAccDirectConnectGatewayAssociationProposal_endOfLifeTgw(t *testing.T) { ResourceName: resourceName, ImportStateIdFunc: func(s *terraform.State) (string, error) { return strings.Join([]string{ - aws.StringValue(proposal.ProposalId), - aws.StringValue(proposal.DirectConnectGatewayId), - aws.StringValue(proposal.AssociatedGateway.Id), + aws.ToString(proposal.ProposalId), + aws.ToString(proposal.DirectConnectGatewayId), + aws.ToString(proposal.AssociatedGateway.Id), }, "/"), nil }, ImportState: true, @@ -196,7 +196,7 @@ func TestAccDirectConnectGatewayAssociationProposal_endOfLifeTgw(t *testing.T) { func TestAccDirectConnectGatewayAssociationProposal_allowedPrefixes(t *testing.T) { ctx := acctest.Context(t) - var proposal1, proposal2 directconnect.GatewayAssociationProposal + var proposal1, proposal2 awstypes.DirectConnectGatewayAssociationProposal rBgpAsn := sdkacctest.RandIntRange(64512, 65534) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_dx_gateway_association_proposal.test" @@ -234,7 +234,7 @@ func TestAccDirectConnectGatewayAssociationProposal_allowedPrefixes(t *testing.T func testAccCheckGatewayAssociationProposalDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_gateway_association_proposal" { @@ -258,18 +258,14 @@ func testAccCheckGatewayAssociationProposalDestroy(ctx context.Context) resource } } -func testAccCheckGatewayAssociationProposalExists(ctx context.Context, resourceName string, gatewayAssociationProposal *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { +func testAccCheckGatewayAssociationProposalExists(ctx context.Context, n string, v *awstypes.DirectConnectGatewayAssociationProposal) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("Not found: %s", n) } - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) output, err := tfdirectconnect.FindGatewayAssociationProposalByID(ctx, conn, rs.Primary.ID) @@ -277,15 +273,15 @@ func testAccCheckGatewayAssociationProposalExists(ctx context.Context, resourceN return err } - *gatewayAssociationProposal = *output + *v = *output return nil } } -func testAccCheckGatewayAssociationProposalRecreated(old, new *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { +func testAccCheckGatewayAssociationProposalRecreated(old, new *awstypes.DirectConnectGatewayAssociationProposal) resource.TestCheckFunc { return func(s *terraform.State) error { - if old, new := aws.StringValue(old.ProposalId), aws.StringValue(new.ProposalId); old == new { + if old, new := aws.ToString(old.ProposalId), aws.ToString(new.ProposalId); old == new { return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not recreated", old) } @@ -300,7 +296,7 @@ func testAccCheckGatewayAssociationProposalAccepted(ctx context.Context, resourc return fmt.Errorf("Not found: %s", resourceName) } - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) output, err := tfdirectconnect.FindGatewayAssociationProposalByID(ctx, conn, rs.Primary.ID) @@ -308,8 +304,8 @@ func testAccCheckGatewayAssociationProposalAccepted(ctx context.Context, resourc return err } - if state := aws.StringValue(output.ProposalState); state != directconnect.GatewayAssociationProposalStateAccepted { - return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted (%s)", rs.Primary.ID, state) + if output.ProposalState != awstypes.DirectConnectGatewayAssociationProposalStateAccepted { + return fmt.Errorf("Direct Connect Gateway Association Proposal (%s) not accepted (%s)", rs.Primary.ID, string(output.ProposalState)) } return nil diff --git a/internal/service/directconnect/gateway_association_test.go b/internal/service/directconnect/gateway_association_test.go index 4d57406eb8f..da210a5171d 100644 --- a/internal/service/directconnect/gateway_association_test.go +++ b/internal/service/directconnect/gateway_association_test.go @@ -8,8 +8,8 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -26,8 +26,8 @@ func TestAccDirectConnectGatewayAssociation_v0StateUpgrade(t *testing.T) { resourceName := "aws_dx_gateway_association.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -53,8 +53,8 @@ func TestAccDirectConnectGatewayAssociation_basicVPNGatewaySingleAccount(t *test resourceNameVgw := "aws_vpn_gateway.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -93,8 +93,8 @@ func TestAccDirectConnectGatewayAssociation_basicVPNGatewayCrossAccount(t *testi resourceNameVgw := "aws_vpn_gateway.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckAlternateAccount(t) }, @@ -128,8 +128,8 @@ func TestAccDirectConnectGatewayAssociation_basicTransitGatewaySingleAccount(t * resourceNameTgw := "aws_ec2_transit_gateway.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -169,8 +169,8 @@ func TestAccDirectConnectGatewayAssociation_basicTransitGatewayCrossAccount(t *t resourceNameTgw := "aws_ec2_transit_gateway.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckAlternateAccount(t) }, @@ -204,8 +204,8 @@ func TestAccDirectConnectGatewayAssociation_multiVPNGatewaysSingleAccount(t *tes resourceName2 := "aws_dx_gateway_association.test.1" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -237,8 +237,8 @@ func TestAccDirectConnectGatewayAssociation_allowedPrefixesVPNGatewaySingleAccou resourceNameVgw := "aws_vpn_gateway.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -283,8 +283,8 @@ func TestAccDirectConnectGatewayAssociation_allowedPrefixesVPNGatewayCrossAccoun resourceNameVgw := "aws_vpn_gateway.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga directconnect.GatewayAssociation - var gap directconnect.GatewayAssociationProposal + var ga awstypes.DirectConnectGatewayAssociation + var gap awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckAlternateAccount(t) }, @@ -326,8 +326,8 @@ func TestAccDirectConnectGatewayAssociation_recreateProposal(t *testing.T) { resourceName := "aws_dx_gateway_association.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) - var ga1, ga2 directconnect.GatewayAssociation - var gap1, gap2 directconnect.GatewayAssociationProposal + var ga1, ga2 awstypes.DirectConnectGatewayAssociation + var gap1, gap2 awstypes.DirectConnectGatewayAssociationProposal resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckAlternateAccount(t) }, @@ -366,7 +366,7 @@ func testAccGatewayAssociationImportStateIdFunc(resourceName string) resource.Im func testAccCheckGatewayAssociationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_gateway_association" { @@ -389,18 +389,14 @@ func testAccCheckGatewayAssociationDestroy(ctx context.Context) resource.TestChe } } -func testAccCheckGatewayAssociationExists(ctx context.Context, name string, ga *directconnect.GatewayAssociation, gap *directconnect.GatewayAssociationProposal) resource.TestCheckFunc { +func testAccCheckGatewayAssociationExists(ctx context.Context, n string, v *awstypes.DirectConnectGatewayAssociation, gap *awstypes.DirectConnectGatewayAssociationProposal) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.Attributes["dx_gateway_association_id"] == "" { - return fmt.Errorf("No Direct Connect Gateway Association ID is set") + return fmt.Errorf("Not found: %s", n) } - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) output, err := tfdirectconnect.FindGatewayAssociationByID(ctx, conn, rs.Primary.Attributes["dx_gateway_association_id"]) @@ -418,15 +414,15 @@ func testAccCheckGatewayAssociationExists(ctx context.Context, name string, ga * *gap = *output } - *ga = *output + *v = *output return nil } } -func testAccCheckGatewayAssociationNotRecreated(old, new *directconnect.GatewayAssociation) resource.TestCheckFunc { +func testAccCheckGatewayAssociationNotRecreated(old, new *awstypes.DirectConnectGatewayAssociation) resource.TestCheckFunc { return func(s *terraform.State) error { - if old, new := aws.StringValue(old.AssociationId), aws.StringValue(new.AssociationId); old != new { + if old, new := aws.ToString(old.AssociationId), aws.ToString(new.AssociationId); old != new { return fmt.Errorf("Direct Connect Gateway Association (%s) recreated (%s)", old, new) } @@ -442,10 +438,6 @@ func testAccCheckGatewayAssociationStateUpgradeV0(ctx context.Context, name stri return fmt.Errorf("Not found: %s", name) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - rawState := map[string]interface{}{ "dx_gateway_id": rs.Primary.Attributes["dx_gateway_id"], "vpn_gateway_id": rs.Primary.Attributes["associated_gateway_id"], // vpn_gateway_id was removed in 3.0, but older state still has it diff --git a/internal/service/directconnect/gateway_data_source.go b/internal/service/directconnect/gateway_data_source.go index 867669e9896..3357253b127 100644 --- a/internal/service/directconnect/gateway_data_source.go +++ b/internal/service/directconnect/gateway_data_source.go @@ -7,17 +7,19 @@ import ( "context" "strconv" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "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" ) -// @SDKDataSource("aws_dx_gateway") -func DataSourceGateway() *schema.Resource { +// @SDKDataSource("aws_dx_gateway", name="Gateway") +func dataSourceGateway() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceGatewayRead, @@ -40,40 +42,21 @@ func DataSourceGateway() *schema.Resource { func dataSourceGatewayRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) - name := d.Get(names.AttrName).(string) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - gateways := make([]*directconnect.Gateway, 0) - // DescribeDirectConnectGatewaysInput does not have a name parameter for filtering + name := d.Get(names.AttrName).(string) input := &directconnect.DescribeDirectConnectGatewaysInput{} - for { - output, err := conn.DescribeDirectConnectGatewaysWithContext(ctx, input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Direct Connect Gateway: %s", err) - } - for _, gateway := range output.DirectConnectGateways { - if aws.StringValue(gateway.DirectConnectGatewayName) == name { - gateways = append(gateways, gateway) - } - } - if output.NextToken == nil { - break - } - input.NextToken = output.NextToken - } - if len(gateways) == 0 { - return sdkdiag.AppendErrorf(diags, "Direct Connect Gateway not found for name: %s", name) - } + gateway, err := findGateway(ctx, conn, input, func(v *awstypes.DirectConnectGateway) bool { + return aws.ToString(v.DirectConnectGatewayName) == name + }) - if len(gateways) > 1 { - return sdkdiag.AppendErrorf(diags, "Multiple Direct Connect Gateways found for name: %s", name) + if err != nil { + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("Direct Connect Gateway", err)) } - gateway := gateways[0] - - d.SetId(aws.StringValue(gateway.DirectConnectGatewayId)) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(gateway.AmazonSideAsn), 10)) + d.SetId(aws.ToString(gateway.DirectConnectGatewayId)) + d.Set("amazon_side_asn", strconv.FormatInt(aws.ToInt64(gateway.AmazonSideAsn), 10)) d.Set(names.AttrOwnerAccountID, gateway.OwnerAccount) return diags diff --git a/internal/service/directconnect/gateway_data_source_test.go b/internal/service/directconnect/gateway_data_source_test.go index 9ace8ed0a6b..f2580ebeb76 100644 --- a/internal/service/directconnect/gateway_data_source_test.go +++ b/internal/service/directconnect/gateway_data_source_test.go @@ -7,7 +7,6 @@ import ( "fmt" "testing" - "github.com/YakDriver/regexache" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -25,10 +24,6 @@ func TestAccDirectConnectGatewayDataSource_basic(t *testing.T) { ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ - { - Config: testAccGatewayDataSourceConfig_nonExistent, - ExpectError: regexache.MustCompile(`Direct Connect Gateway not found`), - }, { Config: testAccGatewayDataSourceConfig_name(rName, sdkacctest.RandIntRange(64512, 65534)), Check: resource.ComposeTestCheckFunc( @@ -45,13 +40,13 @@ func TestAccDirectConnectGatewayDataSource_basic(t *testing.T) { func testAccGatewayDataSourceConfig_name(rName string, rBgpAsn int) string { return fmt.Sprintf(` resource "aws_dx_gateway" "wrong" { - amazon_side_asn = "%d" - name = "%s-wrong" + amazon_side_asn = %[1]d + name = "%[2]s-wrong" } resource "aws_dx_gateway" "test" { - amazon_side_asn = "%d" - name = "%s" + amazon_side_asn = %[3]d + name = %[4]q } data "aws_dx_gateway" "test" { @@ -59,9 +54,3 @@ data "aws_dx_gateway" "test" { } `, rBgpAsn+1, rName, rBgpAsn, rName) } - -const testAccGatewayDataSourceConfig_nonExistent = ` -data "aws_dx_gateway" "test" { - name = "tf-acc-test-does-not-exist" -} -` diff --git a/internal/service/directconnect/gateway_test.go b/internal/service/directconnect/gateway_test.go index 3bd31a930e5..58d7c8fb14d 100644 --- a/internal/service/directconnect/gateway_test.go +++ b/internal/service/directconnect/gateway_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -21,7 +21,7 @@ import ( func TestAccDirectConnectGateway_basic(t *testing.T) { ctx := acctest.Context(t) - var v directconnect.Gateway + var v awstypes.DirectConnectGateway rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) resourceName := "aws_dx_gateway.test" @@ -50,7 +50,7 @@ func TestAccDirectConnectGateway_basic(t *testing.T) { func TestAccDirectConnectGateway_disappears(t *testing.T) { ctx := acctest.Context(t) - var v directconnect.Gateway + var v awstypes.DirectConnectGateway rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) resourceName := "aws_dx_gateway.test" @@ -75,7 +75,7 @@ func TestAccDirectConnectGateway_disappears(t *testing.T) { func TestAccDirectConnectGateway_complex(t *testing.T) { ctx := acctest.Context(t) - var v directconnect.Gateway + var v awstypes.DirectConnectGateway rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) resourceName := "aws_dx_gateway.test" @@ -104,7 +104,7 @@ func TestAccDirectConnectGateway_complex(t *testing.T) { func TestAccDirectConnectGateway_update(t *testing.T) { ctx := acctest.Context(t) - var v directconnect.Gateway + var v awstypes.DirectConnectGateway rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rBgpAsn := sdkacctest.RandIntRange(64512, 65534) @@ -136,7 +136,7 @@ func TestAccDirectConnectGateway_update(t *testing.T) { func testAccCheckGatewayDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_gateway" { @@ -159,18 +159,14 @@ func testAccCheckGatewayDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckGatewayExists(ctx context.Context, name string, v *directconnect.Gateway) resource.TestCheckFunc { +func testAccCheckGatewayExists(ctx context.Context, n string, v *awstypes.DirectConnectGateway) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) output, err := tfdirectconnect.FindGatewayByID(ctx, conn, rs.Primary.ID) diff --git a/internal/service/directconnect/generate.go b/internal/service/directconnect/generate.go index 13a0f277c61..d6180ff44f6 100644 --- a/internal/service/directconnect/generate.go +++ b/internal/service/directconnect/generate.go @@ -1,8 +1,8 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeDirectConnectGateways,DescribeDirectConnectGatewayAssociations,DescribeDirectConnectGatewayAssociationProposals -//go:generate go run ../../generate/tags/main.go -ListTags -ListTagsOp=DescribeTags -ListTagsInIDElem=ResourceArns -ListTagsInIDNeedSlice=yes -ListTagsOutTagsElem=ResourceTags[0].Tags -ServiceTagsSlice -UpdateTags -CreateTags +//go:generate go run ../../generate/listpages/main.go -AWSSDKVersion=2 -ListOps=DescribeDirectConnectGateways,DescribeDirectConnectGatewayAssociations,DescribeDirectConnectGatewayAssociationProposals +//go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -ListTags -ListTagsOp=DescribeTags -ListTagsInIDElem=ResourceArns -ListTagsInIDNeedValueSlice=yes -ListTagsOutTagsElem=ResourceTags[0].Tags -ServiceTagsSlice -UpdateTags -CreateTags //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/directconnect/hosted_connection.go b/internal/service/directconnect/hosted_connection.go index a8cbb87611f..e79ad56191c 100644 --- a/internal/service/directconnect/hosted_connection.go +++ b/internal/service/directconnect/hosted_connection.go @@ -8,20 +8,25 @@ import ( "log" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_connection") -func ResourceHostedConnection() *schema.Resource { +// @SDKResource("aws_dx_hosted_connection", name="Hosted Connection") +func resourceHostedConnection() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedConnectionCreate, ReadWithoutTimeout: resourceHostedConnectionRead, @@ -102,7 +107,7 @@ func ResourceHostedConnection() *schema.Resource { func resourceHostedConnectionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) name := d.Get(names.AttrName).(string) input := &directconnect.AllocateHostedConnectionInput{ @@ -110,26 +115,25 @@ func resourceHostedConnectionCreate(ctx context.Context, d *schema.ResourceData, ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), ConnectionName: aws.String(name), OwnerAccount: aws.String(d.Get(names.AttrOwnerAccountID).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), } - log.Printf("[DEBUG] Creating Direct Connect Hosted Connection: %s", input) - output, err := conn.AllocateHostedConnectionWithContext(ctx, input) + output, err := conn.AllocateHostedConnection(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect Hosted Connection (%s): %s", name, err) } - d.SetId(aws.StringValue(output.ConnectionId)) + d.SetId(aws.ToString(output.ConnectionId)) return append(diags, resourceHostedConnectionRead(ctx, d, meta)...) } func resourceHostedConnectionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - connection, err := FindHostedConnectionByID(ctx, conn, d.Id()) + connection, err := findHostedConnectionByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect Hosted Connection (%s) not found, removing from state", d.Id()) @@ -148,7 +152,7 @@ func resourceHostedConnectionRead(ctx context.Context, d *schema.ResourceData, m d.Set("has_logical_redundancy", connection.HasLogicalRedundancy) d.Set("jumbo_frame_capable", connection.JumboFrameCapable) d.Set("lag_id", connection.LagId) - d.Set("loa_issue_time", aws.TimeValue(connection.LoaIssueTime).Format(time.RFC3339)) + d.Set("loa_issue_time", aws.ToTime(connection.LoaIssueTime).Format(time.RFC3339)) d.Set(names.AttrLocation, connection.Location) d.Set(names.AttrName, connection.ConnectionName) d.Set(names.AttrOwnerAccountID, connection.OwnerAccount) @@ -163,7 +167,7 @@ func resourceHostedConnectionRead(ctx context.Context, d *schema.ResourceData, m func resourceHostedConnectionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if err := deleteConnection(ctx, conn, d.Id(), waitHostedConnectionDeleted); err != nil { return sdkdiag.AppendFromErr(diags, err) @@ -171,3 +175,90 @@ func resourceHostedConnectionDelete(ctx context.Context, d *schema.ResourceData, return diags } + +func findHostedConnectionByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Connection, error) { + input := &directconnect.DescribeHostedConnectionsInput{ + ConnectionId: aws.String(id), + } + output, err := findHostedConnection(ctx, conn, input, tfslices.PredicateTrue[*awstypes.Connection]()) + + if err != nil { + return nil, err + } + + if state := output.ConnectionState; state == awstypes.ConnectionStateDeleted || state == awstypes.ConnectionStateRejected { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, + } + } + + return output, nil +} + +func findHostedConnection(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeHostedConnectionsInput, filter tfslices.Predicate[*awstypes.Connection]) (*awstypes.Connection, error) { + output, err := findHostedConnections(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findHostedConnections(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeHostedConnectionsInput, filter tfslices.Predicate[*awstypes.Connection]) ([]awstypes.Connection, error) { + output, err := conn.DescribeHostedConnections(ctx, input) + + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Could not find Connection with ID") { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return tfslices.Filter(output.Connections, tfslices.PredicateValue(filter)), nil +} + +func statusHostedConnection(ctx context.Context, conn *directconnect.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findHostedConnectionByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.ConnectionState), nil + } +} + +func waitHostedConnectionDeleted(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Connection, error) { + const ( + timeout = 10 * time.Minute + ) + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.ConnectionStatePending, awstypes.ConnectionStateOrdering, awstypes.ConnectionStateAvailable, awstypes.ConnectionStateRequested, awstypes.ConnectionStateDeleting), + Target: []string{}, + Refresh: statusHostedConnection(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Connection); ok { + return output, err + } + + return nil, err +} diff --git a/internal/service/directconnect/hosted_connection_test.go b/internal/service/directconnect/hosted_connection_test.go index bcd96076dce..32e0d2edff5 100644 --- a/internal/service/directconnect/hosted_connection_test.go +++ b/internal/service/directconnect/hosted_connection_test.go @@ -5,9 +5,7 @@ package directconnect_test import ( "context" - "errors" "fmt" - "os" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -21,17 +19,10 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -type testAccDxHostedConnectionEnv struct { - ConnectionId string - OwnerAccountId string -} - func TestAccDirectConnectHostedConnection_basic(t *testing.T) { ctx := acctest.Context(t) - env, err := testAccCheckHostedConnectionEnv() - if err != nil { - acctest.Skip(t, err.Error()) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "TEST_AWS_DX_CONNECTION_ID") + ownerAccountID := acctest.SkipIfEnvVarNotSet(t, "TEST_AWS_DX_OWNER_ACCOUNT_ID") connectionName := fmt.Sprintf("tf-dx-%s", sdkacctest.RandString(5)) resourceName := "aws_dx_hosted_connection.test" @@ -40,15 +31,15 @@ func TestAccDirectConnectHostedConnection_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckHostedConnectionDestroy(ctx, testAccHostedConnectionProvider), + CheckDestroy: testAccCheckHostedConnectionDestroy(ctx, func() *schema.Provider { return acctest.Provider }), Steps: []resource.TestStep{ { - Config: testAccHostedConnectionConfig_basic(connectionName, env.ConnectionId, env.OwnerAccountId), + Config: testAccHostedConnectionConfig_basic(connectionName, connectionID, ownerAccountID), Check: resource.ComposeTestCheckFunc( testAccCheckHostedConnectionExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, names.AttrName, connectionName), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, env.ConnectionId), - resource.TestCheckResourceAttr(resourceName, names.AttrOwnerAccountID, env.OwnerAccountId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), + resource.TestCheckResourceAttr(resourceName, names.AttrOwnerAccountID, ownerAccountID), resource.TestCheckResourceAttr(resourceName, "bandwidth", "100Mbps"), resource.TestCheckResourceAttr(resourceName, "vlan", "4094"), ), @@ -57,23 +48,10 @@ func TestAccDirectConnectHostedConnection_basic(t *testing.T) { }) } -func testAccCheckHostedConnectionEnv() (*testAccDxHostedConnectionEnv, error) { - result := &testAccDxHostedConnectionEnv{ - ConnectionId: os.Getenv("TEST_AWS_DX_CONNECTION_ID"), - OwnerAccountId: os.Getenv("TEST_AWS_DX_OWNER_ACCOUNT_ID"), - } - - if result.ConnectionId == "" || result.OwnerAccountId == "" { - return nil, errors.New("TEST_AWS_DX_CONNECTION_ID and TEST_AWS_DX_OWNER_ACCOUNT_ID must be set for tests involving hosted connections") - } - - return result, nil -} - func testAccCheckHostedConnectionDestroy(ctx context.Context, providerFunc func() *schema.Provider) resource.TestCheckFunc { return func(s *terraform.State) error { provider := providerFunc() - conn := provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_hosted_connection" { @@ -97,17 +75,13 @@ func testAccCheckHostedConnectionDestroy(ctx context.Context, providerFunc func( } } -func testAccCheckHostedConnectionExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckHostedConnectionExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.ID == "" { - return errors.New("No Direct Connect Hosted Connection ID is set") + return fmt.Errorf("Not found: %s", n) } _, err := tfdirectconnect.FindHostedConnectionByID(ctx, conn, rs.Primary.ID) @@ -116,18 +90,14 @@ func testAccCheckHostedConnectionExists(ctx context.Context, name string) resour } } -func testAccHostedConnectionConfig_basic(name, connectionId, ownerAccountId string) string { +func testAccHostedConnectionConfig_basic(name, connectionID, ownerAccountID string) string { return fmt.Sprintf(` resource "aws_dx_hosted_connection" "test" { - name = "%s" - connection_id = "%s" - owner_account_id = "%s" + name = %[1]q + connection_id = %[2]q + owner_account_id = %[3]q bandwidth = "100Mbps" vlan = 4094 } -`, name, connectionId, ownerAccountId) -} - -func testAccHostedConnectionProvider() *schema.Provider { - return acctest.Provider +`, name, connectionID, ownerAccountID) } diff --git a/internal/service/directconnect/hosted_private_virtual_interface.go b/internal/service/directconnect/hosted_private_virtual_interface.go index f3fcc25aee5..68ff4098555 100644 --- a/internal/service/directconnect/hosted_private_virtual_interface.go +++ b/internal/service/directconnect/hosted_private_virtual_interface.go @@ -7,40 +7,41 @@ import ( "context" "fmt" "log" - "strconv" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_private_virtual_interface") -func ResourceHostedPrivateVirtualInterface() *schema.Resource { +// @SDKResource("aws_dx_hosted_private_virtual_interface", name="Hosted Private Virtual Interface") +func resourceHostedPrivateVirtualInterface() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedPrivateVirtualInterfaceCreate, ReadWithoutTimeout: resourceHostedPrivateVirtualInterfaceRead, DeleteWithoutTimeout: resourceHostedPrivateVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ StateContext: resourceHostedPrivateVirtualInterfaceImport, }, Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - directconnect.AddressFamilyIpv4, - directconnect.AddressFamilyIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, "amazon_address": { Type: schema.TypeString, @@ -121,42 +122,46 @@ func ResourceHostedPrivateVirtualInterface() *schema.Resource { func resourceHostedPrivateVirtualInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - req := &directconnect.AllocatePrivateVirtualInterfaceInput{ + input := &directconnect.AllocatePrivateVirtualInterfaceInput{ ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - NewPrivateVirtualInterfaceAllocation: &directconnect.NewPrivateVirtualInterfaceAllocation{ - AddressFamily: aws.String(d.Get("address_family").(string)), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), - Mtu: aws.Int64(int64(d.Get("mtu").(int))), + NewPrivateVirtualInterfaceAllocation: &awstypes.NewPrivateVirtualInterfaceAllocation{ + AddressFamily: awstypes.AddressFamily(d.Get("address_family").(string)), + Asn: int32(d.Get("bgp_asn").(int)), + Mtu: aws.Int32(int32(d.Get("mtu").(int))), VirtualInterfaceName: aws.String(d.Get(names.AttrName).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), }, OwnerAccount: aws.String(d.Get(names.AttrOwnerAccountID).(string)), } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewPrivateVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) + input.NewPrivateVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewPrivateVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) + input.NewPrivateVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) } + if v, ok := d.GetOk("customer_address"); ok { - req.NewPrivateVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) + input.NewPrivateVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("mtu"); ok { - req.NewPrivateVirtualInterfaceAllocation.Mtu = aws.Int64(int64(v.(int))) + input.NewPrivateVirtualInterfaceAllocation.Mtu = aws.Int32(int32(v.(int))) } - log.Printf("[DEBUG] Creating Direct Connect hosted private virtual interface: %s", req) - resp, err := conn.AllocatePrivateVirtualInterfaceWithContext(ctx, req) + output, err := conn.AllocatePrivateVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Direct Connect hosted private virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect Hosted Private Virtual Interface: %s", err) } - d.SetId(aws.StringValue(resp.VirtualInterfaceId)) + d.SetId(aws.ToString(output.VirtualInterfaceId)) - if err := hostedPrivateVirtualInterfaceWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitHostedPrivateVirtualInterfaceAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Hosted Private Virtual Interface (%s) create: %s", d.Id(), err) } return append(diags, resourceHostedPrivateVirtualInterfaceRead(ctx, d, meta)...) @@ -164,21 +169,23 @@ func resourceHostedPrivateVirtualInterfaceCreate(ctx context.Context, d *schema. func resourceHostedPrivateVirtualInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect hosted private virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Hosted Private Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Hosted Private Virtual Interface (%s): %s", d.Id(), err) + } + d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(vif.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", flex.Int64ToStringValue(vif.AmazonSideAsn)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -206,33 +213,28 @@ func resourceHostedPrivateVirtualInterfaceDelete(ctx context.Context, d *schema. } func resourceHostedPrivateVirtualInterfaceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "private" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "private" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } return []*schema.ResourceData{d}, nil } -func hostedPrivateVirtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitHostedPrivateVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateConfirming, awstypes.VirtualInterfaceStateDown), timeout, - []string{ - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStateDown, - }) + ) } diff --git a/internal/service/directconnect/hosted_private_virtual_interface_accepter.go b/internal/service/directconnect/hosted_private_virtual_interface_accepter.go index 8fd4b981724..9ca1521deb6 100644 --- a/internal/service/directconnect/hosted_private_virtual_interface_accepter.go +++ b/internal/service/directconnect/hosted_private_virtual_interface_accepter.go @@ -9,26 +9,30 @@ import ( "log" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_private_virtual_interface_accepter", name="Hosted Private Virtual Interface") +// @SDKResource("aws_dx_hosted_private_virtual_interface_accepter", name="Hosted Private Virtual Interface Accepter") // @Tags(identifierAttribute="arn") -func ResourceHostedPrivateVirtualInterfaceAccepter() *schema.Resource { +func resourceHostedPrivateVirtualInterfaceAccepter() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedPrivateVirtualInterfaceAccepterCreate, ReadWithoutTimeout: resourceHostedPrivateVirtualInterfaceAccepterRead, UpdateWithoutTimeout: resourceHostedPrivateVirtualInterfaceAccepterUpdate, - DeleteWithoutTimeout: resourceHostedPrivateVirtualInterfaceAccepterDelete, + DeleteWithoutTimeout: schema.NoopContext, + Importer: &schema.ResourceImporter{ StateContext: resourceHostedPrivateVirtualInterfaceAccepterImport, }, @@ -39,10 +43,10 @@ func ResourceHostedPrivateVirtualInterfaceAccepter() *schema.Resource { Computed: true, }, "dx_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"vpn_gateway_id"}, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"dx_gateway_id", "vpn_gateway_id"}, }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), @@ -52,10 +56,10 @@ func ResourceHostedPrivateVirtualInterfaceAccepter() *schema.Resource { ForceNew: true, }, "vpn_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"dx_gateway_id"}, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"dx_gateway_id", "vpn_gateway_id"}, }, }, @@ -70,32 +74,28 @@ func ResourceHostedPrivateVirtualInterfaceAccepter() *schema.Resource { func resourceHostedPrivateVirtualInterfaceAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vgwIdRaw, vgwOk := d.GetOk("vpn_gateway_id") - dxgwIdRaw, dxgwOk := d.GetOk("dx_gateway_id") - if vgwOk == dxgwOk { - return sdkdiag.AppendErrorf(diags, "One of ['vpn_gateway_id', 'dx_gateway_id'] must be set to create a Direct Connect private virtual interface accepter") + vifID := d.Get("virtual_interface_id").(string) + input := &directconnect.ConfirmPrivateVirtualInterfaceInput{ + VirtualInterfaceId: aws.String(vifID), } - vifId := d.Get("virtual_interface_id").(string) - req := &directconnect.ConfirmPrivateVirtualInterfaceInput{ - VirtualInterfaceId: aws.String(vifId), - } - if dxgwOk && dxgwIdRaw.(string) != "" { - req.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) + if v, ok := d.GetOk("dx_gateway_id"); ok { + input.DirectConnectGatewayId = aws.String(v.(string)) } - if vgwOk && vgwIdRaw.(string) != "" { - req.VirtualGatewayId = aws.String(vgwIdRaw.(string)) + + if v, ok := d.GetOk("vpn_gateway_id"); ok { + input.VirtualGatewayId = aws.String(v.(string)) } - log.Printf("[DEBUG] Accepting Direct Connect hosted private virtual interface: %s", req) - _, err := conn.ConfirmPrivateVirtualInterfaceWithContext(ctx, req) + _, err := conn.ConfirmPrivateVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "accepting Direct Connect hosted private virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "accepting Direct Connect Hosted Private Virtual Interface (%s): %s", vifID, err) } - d.SetId(vifId) + d.SetId(vifID) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -105,12 +105,12 @@ func resourceHostedPrivateVirtualInterfaceAccepterCreate(ctx context.Context, d }.String() d.Set(names.AttrARN, arn) - if err := hostedPrivateVirtualInterfaceAccepterWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitHostedPrivateVirtualInterfaceAccepterAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Hosted Private Virtual Interface Accepter (%s) create: %s", d.Id(), err) } if err := createTags(ctx, conn, arn, getTagsIn(ctx)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting Direct Connect hosted private virtual interface (%s) tags: %s", arn, err) + return sdkdiag.AppendErrorf(diags, "setting Direct Connect Hosted Private Virtual Interface (%s) tags: %s", arn, err) } return append(diags, resourceHostedPrivateVirtualInterfaceAccepterUpdate(ctx, d, meta)...) @@ -118,21 +118,22 @@ func resourceHostedPrivateVirtualInterfaceAccepterCreate(ctx context.Context, d func resourceHostedPrivateVirtualInterfaceAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect hosted private virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Hosted Private Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - vifState := aws.StringValue(vif.VirtualInterfaceState) - if vifState != directconnect.VirtualInterfaceStateAvailable && - vifState != directconnect.VirtualInterfaceStateDown { - log.Printf("[WARN] Direct Connect hosted private virtual interface (%s) is '%s', removing from state", vifState, d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Hosted Private Virtual Interface (%s): %s", d.Id(), err) + } + + if state := vif.VirtualInterfaceState; state != awstypes.VirtualInterfaceStateAvailable && state != awstypes.VirtualInterfaceStateDown { + log.Printf("[WARN] Direct Connect Hosted Private Virtual Interface (%s) is '%s', removing from state", d.Id(), state) d.SetId("") return diags } @@ -155,24 +156,16 @@ func resourceHostedPrivateVirtualInterfaceAccepterUpdate(ctx context.Context, d return append(diags, resourceHostedPrivateVirtualInterfaceAccepterRead(ctx, d, meta)...) } -func resourceHostedPrivateVirtualInterfaceAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - log.Printf("[WARN] Will not delete Direct Connect virtual interface. Terraform will remove this resource from the state file, however resources may remain.") - return diags -} - func resourceHostedPrivateVirtualInterfaceAccepterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "private" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "private" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } @@ -188,16 +181,13 @@ func resourceHostedPrivateVirtualInterfaceAccepterImport(ctx context.Context, d return []*schema.ResourceData{d}, nil } -func hostedPrivateVirtualInterfaceAccepterWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitHostedPrivateVirtualInterfaceAccepterAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStateConfirming, awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateDown), timeout, - []string{ - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateDown, - }) + ) } diff --git a/internal/service/directconnect/hosted_private_virtual_interface_test.go b/internal/service/directconnect/hosted_private_virtual_interface_test.go index ab62fa32f35..3415949b58c 100644 --- a/internal/service/directconnect/hosted_private_virtual_interface_test.go +++ b/internal/service/directconnect/hosted_private_virtual_interface_test.go @@ -6,13 +6,12 @@ package directconnect_test import ( "context" "fmt" - "os" "strconv" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,13 +21,9 @@ import ( func TestAccDirectConnectHostedPrivateVirtualInterface_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_hosted_private_virtual_interface.test" accepterResourceName := "aws_dx_hosted_private_virtual_interface_accepter.test" vpnGatewayResourceName := "aws_vpn_gateway.test" @@ -46,16 +41,16 @@ func TestAccDirectConnectHostedPrivateVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckHostedPrivateVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccHostedPrivateVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), - Check: resource.ComposeTestCheckFunc( + Config: testAccHostedPrivateVirtualInterfaceConfig_basic(connectionID, rName, bgpAsn, vlan), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckHostedPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -68,9 +63,8 @@ func TestAccDirectConnectHostedPrivateVirtualInterface_basic(t *testing.T) { resource.TestCheckResourceAttrPair(accepterResourceName, "vpn_gateway_id", vpnGatewayResourceName, names.AttrID), ), }, - // Test import. { - Config: testAccHostedPrivateVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), + Config: testAccHostedPrivateVirtualInterfaceConfig_basic(connectionID, rName, bgpAsn, vlan), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -81,13 +75,9 @@ func TestAccDirectConnectHostedPrivateVirtualInterface_basic(t *testing.T) { func TestAccDirectConnectHostedPrivateVirtualInterface_accepterTags(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_hosted_private_virtual_interface.test" accepterResourceName := "aws_dx_hosted_private_virtual_interface_accepter.test" vpnGatewayResourceName := "aws_vpn_gateway.test" @@ -105,16 +95,16 @@ func TestAccDirectConnectHostedPrivateVirtualInterface_accepterTags(t *testing.T CheckDestroy: testAccCheckHostedPrivateVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccHostedPrivateVirtualInterfaceConfig_accepterTags(connectionId, rName, bgpAsn, vlan), + Config: testAccHostedPrivateVirtualInterfaceConfig_accepterTags(connectionID, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -131,16 +121,16 @@ func TestAccDirectConnectHostedPrivateVirtualInterface_accepterTags(t *testing.T ), }, { - Config: testAccHostedPrivateVirtualInterfaceConfig_accepterTagsUpdated(connectionId, rName, bgpAsn, vlan), + Config: testAccHostedPrivateVirtualInterfaceConfig_accepterTagsUpdated(connectionID, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -166,12 +156,12 @@ func testAccCheckHostedPrivateVirtualInterfaceDestroy(ctx context.Context) resou } } -func testAccCheckHostedPrivateVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { +func testAccCheckHostedPrivateVirtualInterfaceExists(ctx context.Context, name string, vif *awstypes.VirtualInterface) resource.TestCheckFunc { return testAccCheckVirtualInterfaceExists(ctx, name, vif) } func testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName string, bgpAsn, vlan int) string { - return acctest.ConfigAlternateAccountProvider() + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAlternateAccountProvider(), fmt.Sprintf(` # Creator resource "aws_dx_hosted_private_virtual_interface" "test" { address_family = "ipv4" @@ -198,22 +188,22 @@ resource "aws_vpn_gateway" "test" { Name = %[2]q } } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccHostedPrivateVirtualInterfaceConfig_basic(cid, rName string, bgpAsn, vlan int) string { - return testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + ` + return acctest.ConfigCompose(testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan), ` resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { provider = "awsalternate" virtual_interface_id = aws_dx_hosted_private_virtual_interface.test.id vpn_gateway_id = aws_vpn_gateway.test.id } -` +`) } func testAccHostedPrivateVirtualInterfaceConfig_accepterTags(cid, rName string, bgpAsn, vlan int) string { - return testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan), fmt.Sprintf(` resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { provider = "awsalternate" @@ -226,11 +216,11 @@ resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { Key2 = "Value2a" } } -`, rName) +`, rName)) } func testAccHostedPrivateVirtualInterfaceConfig_accepterTagsUpdated(cid, rName string, bgpAsn, vlan int) string { - return testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan), fmt.Sprintf(` resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { provider = "awsalternate" @@ -243,5 +233,5 @@ resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { Key3 = "Value3" } } -`, rName) +`, rName)) } diff --git a/internal/service/directconnect/hosted_public_virtual_interface.go b/internal/service/directconnect/hosted_public_virtual_interface.go index a464e8da8e1..f03144f8d84 100644 --- a/internal/service/directconnect/hosted_public_virtual_interface.go +++ b/internal/service/directconnect/hosted_public_virtual_interface.go @@ -10,38 +10,40 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_public_virtual_interface") -func ResourceHostedPublicVirtualInterface() *schema.Resource { +// @SDKResource("aws_dx_hosted_public_virtual_interface", name="Hosted Public Virtual Interface") +func resourceHostedPublicVirtualInterface() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedPublicVirtualInterfaceCreate, ReadWithoutTimeout: resourceHostedPublicVirtualInterfaceRead, DeleteWithoutTimeout: resourceHostedPublicVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ StateContext: resourceHostedPublicVirtualInterfaceImport, }, + CustomizeDiff: resourceHostedPublicVirtualInterfaceCustomizeDiff, Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - directconnect.AddressFamilyIpv4, - directconnect.AddressFamilyIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, "amazon_address": { Type: schema.TypeString, @@ -118,40 +120,44 @@ func ResourceHostedPublicVirtualInterface() *schema.Resource { func resourceHostedPublicVirtualInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - req := &directconnect.AllocatePublicVirtualInterfaceInput{ + input := &directconnect.AllocatePublicVirtualInterfaceInput{ ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - NewPublicVirtualInterfaceAllocation: &directconnect.NewPublicVirtualInterfaceAllocation{ - AddressFamily: aws.String(d.Get("address_family").(string)), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), + NewPublicVirtualInterfaceAllocation: &awstypes.NewPublicVirtualInterfaceAllocation{ + AddressFamily: awstypes.AddressFamily(d.Get("address_family").(string)), + Asn: int32(d.Get("bgp_asn").(int)), VirtualInterfaceName: aws.String(d.Get(names.AttrName).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), }, OwnerAccount: aws.String(d.Get(names.AttrOwnerAccountID).(string)), } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewPublicVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) + input.NewPublicVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewPublicVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) + input.NewPublicVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) } + if v, ok := d.GetOk("customer_address"); ok { - req.NewPublicVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) + input.NewPublicVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("route_filter_prefixes"); ok { - req.NewPublicVirtualInterfaceAllocation.RouteFilterPrefixes = expandRouteFilterPrefixes(v.(*schema.Set).List()) + input.NewPublicVirtualInterfaceAllocation.RouteFilterPrefixes = expandRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Allocating Direct Connect hosted public virtual interface: %s", req) - resp, err := conn.AllocatePublicVirtualInterfaceWithContext(ctx, req) + output, err := conn.AllocatePublicVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "allocating Direct Connect hosted public virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect Hosted Public Virtual Interface: %s", err) } - d.SetId(aws.StringValue(resp.VirtualInterfaceId)) + d.SetId(aws.ToString(output.VirtualInterfaceId)) - if err := hostedPublicVirtualInterfaceWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := waitHostedPublicVirtualInterfaceAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendFromErr(diags, err) } @@ -160,21 +166,23 @@ func resourceHostedPublicVirtualInterfaceCreate(ctx context.Context, d *schema.R func resourceHostedPublicVirtualInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Hosted Public Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Hosted Public Virtual Interface (%s): %s", d.Id(), err) + } + d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(vif.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", strconv.FormatInt(aws.ToInt64(vif.AmazonSideAsn), 10)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -203,17 +211,15 @@ func resourceHostedPublicVirtualInterfaceDelete(ctx context.Context, d *schema.R } func resourceHostedPublicVirtualInterfaceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "public" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "public" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } @@ -223,7 +229,7 @@ func resourceHostedPublicVirtualInterfaceImport(ctx context.Context, d *schema.R func resourceHostedPublicVirtualInterfaceCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { if diff.Id() == "" { // New resource. - if addressFamily := diff.Get("address_family").(string); addressFamily == directconnect.AddressFamilyIpv4 { + if addressFamily := diff.Get("address_family").(string); addressFamily == string(awstypes.AddressFamilyIPv4) { if _, ok := diff.GetOk("customer_address"); !ok { return fmt.Errorf("'customer_address' must be set when 'address_family' is '%s'", addressFamily) } @@ -236,17 +242,13 @@ func resourceHostedPublicVirtualInterfaceCustomizeDiff(_ context.Context, diff * return nil } -func hostedPublicVirtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitHostedPublicVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateConfirming, awstypes.VirtualInterfaceStateDown, awstypes.VirtualInterfaceStateVerifying), timeout, - []string{ - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStateDown, - directconnect.VirtualInterfaceStateVerifying, - }) + ) } diff --git a/internal/service/directconnect/hosted_public_virtual_interface_accepter.go b/internal/service/directconnect/hosted_public_virtual_interface_accepter.go index bf32996fff8..9210bf14633 100644 --- a/internal/service/directconnect/hosted_public_virtual_interface_accepter.go +++ b/internal/service/directconnect/hosted_public_virtual_interface_accepter.go @@ -9,26 +9,30 @@ import ( "log" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_public_virtual_interface_accepter", name="Hosted Public Virtual Interface") +// @SDKResource("aws_dx_hosted_public_virtual_interface_accepter", name="Hosted Public Virtual Interface Accepter") // @Tags(identifierAttribute="arn") -func ResourceHostedPublicVirtualInterfaceAccepter() *schema.Resource { +func resourceHostedPublicVirtualInterfaceAccepter() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedPublicVirtualInterfaceAccepterCreate, ReadWithoutTimeout: resourceHostedPublicVirtualInterfaceAccepterRead, UpdateWithoutTimeout: resourceHostedPublicVirtualInterfaceAccepterUpdate, - DeleteWithoutTimeout: resourceHostedPublicVirtualInterfaceAccepterDelete, + DeleteWithoutTimeout: schema.NoopContext, + Importer: &schema.ResourceImporter{ StateContext: resourceHostedPublicVirtualInterfaceAccepterImport, }, @@ -58,20 +62,20 @@ func ResourceHostedPublicVirtualInterfaceAccepter() *schema.Resource { func resourceHostedPublicVirtualInterfaceAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vifId := d.Get("virtual_interface_id").(string) - req := &directconnect.ConfirmPublicVirtualInterfaceInput{ - VirtualInterfaceId: aws.String(vifId), + vifID := d.Get("virtual_interface_id").(string) + input := &directconnect.ConfirmPublicVirtualInterfaceInput{ + VirtualInterfaceId: aws.String(vifID), } - log.Printf("[DEBUG] Accepting Direct Connect hosted public virtual interface: %s", req) - _, err := conn.ConfirmPublicVirtualInterfaceWithContext(ctx, req) + _, err := conn.ConfirmPublicVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "accepting Direct Connect hosted public virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "accepting Direct Connect Hosted Public Virtual Interface (%s): %s", vifID, err) } - d.SetId(vifId) + d.SetId(vifID) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -81,12 +85,12 @@ func resourceHostedPublicVirtualInterfaceAccepterCreate(ctx context.Context, d * }.String() d.Set(names.AttrARN, arn) - if err := hostedPublicVirtualInterfaceAccepterWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitHostedPublicVirtualInterfaceAccepterAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Hosted Public Virtual Interface Accepter (%s) create: %s", d.Id(), err) } if err := createTags(ctx, conn, arn, getTagsIn(ctx)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting Direct Connect hosted public virtual interface (%s) tags: %s", arn, err) + return sdkdiag.AppendErrorf(diags, "setting Direct Connect Hosted Public Virtual Interface (%s) tags: %s", arn, err) } return append(diags, resourceHostedPublicVirtualInterfaceAccepterUpdate(ctx, d, meta)...) @@ -94,22 +98,22 @@ func resourceHostedPublicVirtualInterfaceAccepterCreate(ctx context.Context, d * func resourceHostedPublicVirtualInterfaceAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect hosted public virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Hosted Public Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - vifState := aws.StringValue(vif.VirtualInterfaceState) - if vifState != directconnect.VirtualInterfaceStateAvailable && - vifState != directconnect.VirtualInterfaceStateDown && - vifState != directconnect.VirtualInterfaceStateVerifying { - log.Printf("[WARN] Direct Connect hosted public virtual interface (%s) is '%s', removing from state", vifState, d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Hosted Public Virtual Interface (%s): %s", d.Id(), err) + } + + if state := vif.VirtualInterfaceState; state != awstypes.VirtualInterfaceStateAvailable && state != awstypes.VirtualInterfaceStateDown && state != awstypes.VirtualInterfaceStateVerifying { + log.Printf("[WARN] Direct Connect Hosted Public Virtual Interface (%s) is '%s', removing from state", d.Id(), state) d.SetId("") return diags } @@ -130,24 +134,16 @@ func resourceHostedPublicVirtualInterfaceAccepterUpdate(ctx context.Context, d * return append(diags, resourceHostedPublicVirtualInterfaceAccepterRead(ctx, d, meta)...) } -func resourceHostedPublicVirtualInterfaceAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - log.Printf("[WARN] Will not delete Direct Connect virtual interface. Terraform will remove this resource from the state file, however resources may remain.") - return diags -} - func resourceHostedPublicVirtualInterfaceAccepterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "public" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "public" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } @@ -163,17 +159,13 @@ func resourceHostedPublicVirtualInterfaceAccepterImport(ctx context.Context, d * return []*schema.ResourceData{d}, nil } -func hostedPublicVirtualInterfaceAccepterWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, +func waitHostedPublicVirtualInterfaceAccepterAvailable(ctx context.Context, conn *directconnect.Client, vifId string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, vifId, + enum.Slice(awstypes.VirtualInterfaceStateConfirming, awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateDown, awstypes.VirtualInterfaceStateVerifying), timeout, - []string{ - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateDown, - directconnect.VirtualInterfaceStateVerifying, - }) + ) } diff --git a/internal/service/directconnect/hosted_public_virtual_interface_test.go b/internal/service/directconnect/hosted_public_virtual_interface_test.go index 40ba3497332..e52ad94602b 100644 --- a/internal/service/directconnect/hosted_public_virtual_interface_test.go +++ b/internal/service/directconnect/hosted_public_virtual_interface_test.go @@ -6,13 +6,12 @@ package directconnect_test import ( "context" "fmt" - "os" "strconv" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,13 +21,9 @@ import ( func TestAccDirectConnectHostedPublicVirtualInterface_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_hosted_public_virtual_interface.test" accepterResourceName := "aws_dx_hosted_public_virtual_interface_accepter.test" rName := fmt.Sprintf("tf-testacc-public-vif-%s", sdkacctest.RandString(10)) @@ -47,17 +42,17 @@ func TestAccDirectConnectHostedPublicVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckHostedPublicVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccHostedPublicVirtualInterfaceConfig_basic(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccHostedPublicVirtualInterfaceConfig_basic(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedPublicVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", acctest.Ct2), @@ -70,9 +65,8 @@ func TestAccDirectConnectHostedPublicVirtualInterface_basic(t *testing.T) { resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, names.AttrID), ), }, - // Test import. { - Config: testAccHostedPublicVirtualInterfaceConfig_basic(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccHostedPublicVirtualInterfaceConfig_basic(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -83,13 +77,9 @@ func TestAccDirectConnectHostedPublicVirtualInterface_basic(t *testing.T) { func TestAccDirectConnectHostedPublicVirtualInterface_accepterTags(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_hosted_public_virtual_interface.test" accepterResourceName := "aws_dx_hosted_public_virtual_interface_accepter.test" rName := fmt.Sprintf("tf-testacc-public-vif-%s", sdkacctest.RandString(10)) @@ -108,17 +98,17 @@ func TestAccDirectConnectHostedPublicVirtualInterface_accepterTags(t *testing.T) CheckDestroy: testAccCheckHostedPublicVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccHostedPublicVirtualInterfaceConfig_accepterTags(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccHostedPublicVirtualInterfaceConfig_accepterTags(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedPublicVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", acctest.Ct2), @@ -135,17 +125,17 @@ func TestAccDirectConnectHostedPublicVirtualInterface_accepterTags(t *testing.T) ), }, { - Config: testAccHostedPublicVirtualInterfaceConfig_accepterTagsUpdated(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccHostedPublicVirtualInterfaceConfig_accepterTagsUpdated(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedPublicVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", acctest.Ct2), @@ -171,12 +161,12 @@ func testAccCheckHostedPublicVirtualInterfaceDestroy(ctx context.Context) resour } } -func testAccCheckHostedPublicVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { +func testAccCheckHostedPublicVirtualInterfaceExists(ctx context.Context, name string, vif *awstypes.VirtualInterface) resource.TestCheckFunc { return testAccCheckVirtualInterfaceExists(ctx, name, vif) } func testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { - return acctest.ConfigAlternateAccountProvider() + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAlternateAccountProvider(), fmt.Sprintf(` # Creator resource "aws_dx_hosted_public_virtual_interface" "test" { address_family = "ipv4" @@ -198,21 +188,21 @@ resource "aws_dx_hosted_public_virtual_interface" "test" { data "aws_caller_identity" "accepter" { provider = "awsalternate" } -`, cid, rName, amzAddr, custAddr, bgpAsn, vlan) +`, cid, rName, amzAddr, custAddr, bgpAsn, vlan)) } func testAccHostedPublicVirtualInterfaceConfig_basic(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { - return testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan) + ` + return acctest.ConfigCompose(testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan), ` resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { provider = "awsalternate" virtual_interface_id = aws_dx_hosted_public_virtual_interface.test.id } -` +`) } func testAccHostedPublicVirtualInterfaceConfig_accepterTags(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { - return testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan), fmt.Sprintf(` resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { provider = "awsalternate" @@ -224,11 +214,11 @@ resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { Key2 = "Value2a" } } -`, rName) +`, rName)) } func testAccHostedPublicVirtualInterfaceConfig_accepterTagsUpdated(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { - return testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan), fmt.Sprintf(` resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { provider = "awsalternate" @@ -240,5 +230,5 @@ resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { Key3 = "Value3" } } -`, rName) +`, rName)) } diff --git a/internal/service/directconnect/hosted_transit_virtual_interface.go b/internal/service/directconnect/hosted_transit_virtual_interface.go index 1f95e21b861..78bbe396cc0 100644 --- a/internal/service/directconnect/hosted_transit_virtual_interface.go +++ b/internal/service/directconnect/hosted_transit_virtual_interface.go @@ -7,40 +7,41 @@ import ( "context" "fmt" "log" - "strconv" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_transit_virtual_interface") -func ResourceHostedTransitVirtualInterface() *schema.Resource { +// @SDKResource("aws_dx_hosted_transit_virtual_interface", name="Hosted Transit Virtual Interface") +func resourceHostedTransitVirtualInterface() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedTransitVirtualInterfaceCreate, ReadWithoutTimeout: resourceHostedTransitVirtualInterfaceRead, DeleteWithoutTimeout: resourceHostedTransitVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ StateContext: resourceHostedTransitVirtualInterfaceImport, }, Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - directconnect.AddressFamilyIpv4, - directconnect.AddressFamilyIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, "amazon_address": { Type: schema.TypeString, @@ -121,39 +122,42 @@ func ResourceHostedTransitVirtualInterface() *schema.Resource { func resourceHostedTransitVirtualInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - req := &directconnect.AllocateTransitVirtualInterfaceInput{ + input := &directconnect.AllocateTransitVirtualInterfaceInput{ ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - OwnerAccount: aws.String(d.Get(names.AttrOwnerAccountID).(string)), - NewTransitVirtualInterfaceAllocation: &directconnect.NewTransitVirtualInterfaceAllocation{ - AddressFamily: aws.String(d.Get("address_family").(string)), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), - Mtu: aws.Int64(int64(d.Get("mtu").(int))), + NewTransitVirtualInterfaceAllocation: &awstypes.NewTransitVirtualInterfaceAllocation{ + AddressFamily: awstypes.AddressFamily(d.Get("address_family").(string)), + Asn: int32(d.Get("bgp_asn").(int)), + Mtu: aws.Int32(int32(d.Get("mtu").(int))), VirtualInterfaceName: aws.String(d.Get(names.AttrName).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), }, + OwnerAccount: aws.String(d.Get(names.AttrOwnerAccountID).(string)), } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewTransitVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) + input.NewTransitVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewTransitVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) + input.NewTransitVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) } + if v, ok := d.GetOk("customer_address"); ok { - req.NewTransitVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) + input.NewTransitVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating Direct Connect hosted transit virtual interface: %s", req) - resp, err := conn.AllocateTransitVirtualInterfaceWithContext(ctx, req) + output, err := conn.AllocateTransitVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Direct Connect hosted transit virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect Hosted Transit Virtual Interface: %s", err) } - d.SetId(aws.StringValue(resp.VirtualInterface.VirtualInterfaceId)) + d.SetId(aws.ToString(output.VirtualInterface.VirtualInterfaceId)) - if err := hostedTransitVirtualInterfaceWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitHostedTransitVirtualInterfaceAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Hosted Transit Virtual Interface (%s) create: %s", d.Id(), err) } return append(diags, resourceHostedTransitVirtualInterfaceRead(ctx, d, meta)...) @@ -161,21 +165,23 @@ func resourceHostedTransitVirtualInterfaceCreate(ctx context.Context, d *schema. func resourceHostedTransitVirtualInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect hosted transit virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Hosted Transit Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Hosted Transit Virtual Interface (%s): %s", d.Id(), err) + } + d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(vif.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", flex.Int64ToStringValue(vif.AmazonSideAsn)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -203,33 +209,28 @@ func resourceHostedTransitVirtualInterfaceDelete(ctx context.Context, d *schema. } func resourceHostedTransitVirtualInterfaceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "transit" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "transit" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } return []*schema.ResourceData{d}, nil } -func hostedTransitVirtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitHostedTransitVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateConfirming, awstypes.VirtualInterfaceStateDown), timeout, - []string{ - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStateDown, - }) + ) } diff --git a/internal/service/directconnect/hosted_transit_virtual_interface_accepter.go b/internal/service/directconnect/hosted_transit_virtual_interface_accepter.go index fba63d61272..76e81e6cb7e 100644 --- a/internal/service/directconnect/hosted_transit_virtual_interface_accepter.go +++ b/internal/service/directconnect/hosted_transit_virtual_interface_accepter.go @@ -9,26 +9,30 @@ import ( "log" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_dx_hosted_transit_virtual_interface_accepter", name="Hosted Transit Virtual Interface") +// @SDKResource("aws_dx_hosted_transit_virtual_interface_accepter", name="Hosted Transit Virtual Interface Accepter") // @Tags(identifierAttribute="arn") -func ResourceHostedTransitVirtualInterfaceAccepter() *schema.Resource { +func resourceHostedTransitVirtualInterfaceAccepter() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceHostedTransitVirtualInterfaceAccepterCreate, ReadWithoutTimeout: resourceHostedTransitVirtualInterfaceAccepterRead, UpdateWithoutTimeout: resourceHostedTransitVirtualInterfaceAccepterUpdate, - DeleteWithoutTimeout: resourceHostedTransitVirtualInterfaceAccepterDelete, + DeleteWithoutTimeout: schema.NoopContext, + Importer: &schema.ResourceImporter{ StateContext: resourceHostedTransitVirtualInterfaceAccepterImport, }, @@ -63,21 +67,21 @@ func ResourceHostedTransitVirtualInterfaceAccepter() *schema.Resource { func resourceHostedTransitVirtualInterfaceAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vifId := d.Get("virtual_interface_id").(string) - req := &directconnect.ConfirmTransitVirtualInterfaceInput{ + vifID := d.Get("virtual_interface_id").(string) + input := &directconnect.ConfirmTransitVirtualInterfaceInput{ DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), - VirtualInterfaceId: aws.String(vifId), + VirtualInterfaceId: aws.String(vifID), } - log.Printf("[DEBUG] Accepting Direct Connect hosted transit virtual interface: %s", req) - _, err := conn.ConfirmTransitVirtualInterfaceWithContext(ctx, req) + _, err := conn.ConfirmTransitVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "accepting Direct Connect hosted transit virtual interface (%s): %s", vifId, err) + return sdkdiag.AppendErrorf(diags, "accepting Direct Connect Hosted Transit Virtual Interface (%s): %s", vifID, err) } - d.SetId(vifId) + d.SetId(vifID) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -87,12 +91,12 @@ func resourceHostedTransitVirtualInterfaceAccepterCreate(ctx context.Context, d }.String() d.Set(names.AttrARN, arn) - if err := hostedTransitVirtualInterfaceAccepterWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitHostedTransitVirtualInterfaceAccepterAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Hosted Transit Virtual Interface Accepter (%s) create: %s", d.Id(), err) } if err := createTags(ctx, conn, arn, getTagsIn(ctx)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting Direct Connect hosted transit virtual interface (%s) tags: %s", arn, err) + return sdkdiag.AppendErrorf(diags, "setting Direct Connect Hosted Transit Virtual Interface (%s) tags: %s", arn, err) } return append(diags, resourceHostedTransitVirtualInterfaceAccepterUpdate(ctx, d, meta)...) @@ -100,20 +104,22 @@ func resourceHostedTransitVirtualInterfaceAccepterCreate(ctx context.Context, d func resourceHostedTransitVirtualInterfaceAccepterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect transit virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Hosted Transit Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - vifState := aws.StringValue(vif.VirtualInterfaceState) - if vifState != directconnect.VirtualInterfaceStateAvailable && vifState != directconnect.VirtualInterfaceStateDown { - log.Printf("[WARN] Direct Connect virtual interface (%s) is '%s', removing from state", vifState, d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Hosted Transit Virtual Interface (%s): %s", d.Id(), err) + } + + if state := vif.VirtualInterfaceState; state != awstypes.VirtualInterfaceStateAvailable && state != awstypes.VirtualInterfaceStateDown { + log.Printf("[WARN] Direct Connect virtual interface (%s) is '%s', removing from state", d.Id(), state) d.SetId("") return diags } @@ -135,24 +141,16 @@ func resourceHostedTransitVirtualInterfaceAccepterUpdate(ctx context.Context, d return append(diags, resourceHostedTransitVirtualInterfaceAccepterRead(ctx, d, meta)...) } -func resourceHostedTransitVirtualInterfaceAccepterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - log.Printf("[WARN] Will not delete Direct Connect virtual interface. Terraform will remove this resource from the state file, however resources may remain.") - return diags -} - func resourceHostedTransitVirtualInterfaceAccepterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "transit" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "transit" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } @@ -168,16 +166,13 @@ func resourceHostedTransitVirtualInterfaceAccepterImport(ctx context.Context, d return []*schema.ResourceData{d}, nil } -func hostedTransitVirtualInterfaceAccepterWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitHostedTransitVirtualInterfaceAccepterAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStateConfirming, awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateDown), timeout, - []string{ - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateDown, - }) + ) } diff --git a/internal/service/directconnect/hosted_transit_virtual_interface_test.go b/internal/service/directconnect/hosted_transit_virtual_interface_test.go index 12e6d28c2d6..53b05668e08 100644 --- a/internal/service/directconnect/hosted_transit_virtual_interface_test.go +++ b/internal/service/directconnect/hosted_transit_virtual_interface_test.go @@ -6,13 +6,12 @@ package directconnect_test import ( "context" "fmt" - "os" "strconv" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -33,13 +32,9 @@ func TestAccDirectConnectHostedTransitVirtualInterface_serial(t *testing.T) { func testAccHostedTransitVirtualInterface_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_hosted_transit_virtual_interface.test" accepterResourceName := "aws_dx_hosted_transit_virtual_interface_accepter.test" dxGatewayResourceName := "aws_dx_gateway.test" @@ -58,16 +53,16 @@ func testAccHostedTransitVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckHostedTransitVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccHostedTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccHostedTransitVirtualInterfaceConfig_basic(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -80,9 +75,8 @@ func testAccHostedTransitVirtualInterface_basic(t *testing.T) { resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, names.AttrID), ), }, - // Test import. { - Config: testAccHostedTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccHostedTransitVirtualInterfaceConfig_basic(connectionID, rName, amzAsn, bgpAsn, vlan), ResourceName: resourceName, ImportState: true, ImportStateVerify: true, @@ -93,13 +87,9 @@ func testAccHostedTransitVirtualInterface_basic(t *testing.T) { func testAccHostedTransitVirtualInterface_accepterTags(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_hosted_transit_virtual_interface.test" accepterResourceName := "aws_dx_hosted_transit_virtual_interface_accepter.test" dxGatewayResourceName := "aws_dx_gateway.test" @@ -118,16 +108,16 @@ func testAccHostedTransitVirtualInterface_accepterTags(t *testing.T) { CheckDestroy: testAccCheckHostedTransitVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccHostedTransitVirtualInterfaceConfig_accepterTags(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccHostedTransitVirtualInterfaceConfig_accepterTags(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -144,16 +134,16 @@ func testAccHostedTransitVirtualInterface_accepterTags(t *testing.T) { ), }, { - Config: testAccHostedTransitVirtualInterfaceConfig_accepterTagsUpdated(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccHostedTransitVirtualInterfaceConfig_accepterTagsUpdated(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckHostedTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), @@ -173,7 +163,7 @@ func testAccHostedTransitVirtualInterface_accepterTags(t *testing.T) { }) } -func testAccCheckHostedTransitVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { +func testAccCheckHostedTransitVirtualInterfaceExists(ctx context.Context, name string, vif *awstypes.VirtualInterface) resource.TestCheckFunc { return testAccCheckVirtualInterfaceExists(ctx, name, vif) } @@ -184,7 +174,7 @@ func testAccCheckHostedTransitVirtualInterfaceDestroy(ctx context.Context) resou } func testAccHostedTransitVirtualInterfaceConfig_base(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return acctest.ConfigAlternateAccountProvider() + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAlternateAccountProvider(), fmt.Sprintf(` # Creator resource "aws_dx_hosted_transit_virtual_interface" "test" { address_family = "ipv4" @@ -210,22 +200,22 @@ resource "aws_dx_gateway" "test" { amazon_side_asn = %[3]d name = %[2]q } -`, cid, rName, amzAsn, bgpAsn, vlan) +`, cid, rName, amzAsn, bgpAsn, vlan)) } func testAccHostedTransitVirtualInterfaceConfig_basic(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan) + ` + return acctest.ConfigCompose(testAccHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan), ` resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { provider = "awsalternate" dx_gateway_id = aws_dx_gateway.test.id virtual_interface_id = aws_dx_hosted_transit_virtual_interface.test.id } -` +`) } func testAccHostedTransitVirtualInterfaceConfig_accepterTags(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan), fmt.Sprintf(` resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { provider = "awsalternate" @@ -238,11 +228,11 @@ resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { Key2 = "Value2a" } } -`, rName) +`, rName)) } func testAccHostedTransitVirtualInterfaceConfig_accepterTagsUpdated(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan), fmt.Sprintf(` resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { provider = "awsalternate" @@ -255,5 +245,5 @@ resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { Key3 = "Value3" } } -`, rName) +`, rName)) } diff --git a/internal/service/directconnect/id.go b/internal/service/directconnect/id.go deleted file mode 100644 index 9bef8c3a844..00000000000 --- a/internal/service/directconnect/id.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect - -import ( - "fmt" -) - -func GatewayAssociationCreateResourceID(directConnectGatewayID, associatedGatewayID string) string { - return fmt.Sprintf("ga-%s%s", directConnectGatewayID, associatedGatewayID) -} diff --git a/internal/service/directconnect/lag.go b/internal/service/directconnect/lag.go index aceea99bf8f..36265c03a3f 100644 --- a/internal/service/directconnect/lag.go +++ b/internal/service/directconnect/lag.go @@ -7,15 +7,20 @@ import ( "context" "fmt" "log" + "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" 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" @@ -24,12 +29,13 @@ import ( // @SDKResource("aws_dx_lag", name="LAG") // @Tags(identifierAttribute="arn") -func ResourceLag() *schema.Resource { +func resourceLag() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceLagCreate, ReadWithoutTimeout: resourceLagRead, UpdateWithoutTimeout: resourceLagUpdate, DeleteWithoutTimeout: resourceLagDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -92,7 +98,7 @@ func ResourceLag() *schema.Resource { func resourceLagCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) name := d.Get(names.AttrName).(string) input := &directconnect.CreateLagInput{ @@ -106,27 +112,26 @@ func resourceLagCreate(ctx context.Context, d *schema.ResourceData, meta interfa if v, ok := d.GetOk(names.AttrConnectionID); ok { connectionIDSpecified = true input.ConnectionId = aws.String(v.(string)) - input.NumberOfConnections = aws.Int64(1) + input.NumberOfConnections = int32(1) } else { - input.NumberOfConnections = aws.Int64(1) + input.NumberOfConnections = int32(1) } if v, ok := d.GetOk(names.AttrProviderName); ok { input.ProviderName = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating Direct Connect LAG: %s", input) - output, err := conn.CreateLagWithContext(ctx, input) + output, err := conn.CreateLag(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Direct Connect LAG (%s): %s", name, err) } - d.SetId(aws.StringValue(output.LagId)) + d.SetId(aws.ToString(output.LagId)) // Delete unmanaged connection. if !connectionIDSpecified { - if err := deleteConnection(ctx, conn, aws.StringValue(output.Connections[0].ConnectionId), waitConnectionDeleted); err != nil { + if err := deleteConnection(ctx, conn, aws.ToString(output.Connections[0].ConnectionId), waitConnectionDeleted); err != nil { return sdkdiag.AppendFromErr(diags, err) } } @@ -136,9 +141,9 @@ func resourceLagCreate(ctx context.Context, d *schema.ResourceData, meta interfa func resourceLagRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - lag, err := FindLagByID(ctx, conn, d.Id()) + lag, err := findLagByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id()) @@ -152,9 +157,9 @@ func resourceLagRead(ctx context.Context, d *schema.ResourceData, meta interface arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, - Region: aws.StringValue(lag.Region), + Region: aws.ToString(lag.Region), Service: "directconnect", - AccountID: aws.StringValue(lag.OwnerAccount), + AccountID: aws.ToString(lag.OwnerAccount), Resource: fmt.Sprintf("dxlag/%s", d.Id()), }.String() d.Set(names.AttrARN, arn) @@ -171,7 +176,7 @@ func resourceLagRead(ctx context.Context, d *schema.ResourceData, meta interface func resourceLagUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if d.HasChange(names.AttrName) { input := &directconnect.UpdateLagInput{ @@ -179,8 +184,7 @@ func resourceLagUpdate(ctx context.Context, d *schema.ResourceData, meta interfa LagName: aws.String(d.Get(names.AttrName).(string)), } - log.Printf("[DEBUG] Updating Direct Connect LAG: %s", input) - _, err := conn.UpdateLagWithContext(ctx, input) + _, err := conn.UpdateLag(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Direct Connect LAG (%s): %s", d.Id(), err) @@ -192,21 +196,21 @@ func resourceLagUpdate(ctx context.Context, d *schema.ResourceData, meta interfa func resourceLagDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if d.Get(names.AttrForceDestroy).(bool) { - lag, err := FindLagByID(ctx, conn, d.Id()) + lag, err := findLagByID(ctx, conn, d.Id()) if tfresource.NotFound(err) { return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting Direct Connect LAG (%s): listing connections: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading Direct Connect LAG (%s): %s", d.Id(), err) } for _, connection := range lag.Connections { - if err := deleteConnection(ctx, conn, aws.StringValue(connection.ConnectionId), waitConnectionDeleted); err != nil { + if err := deleteConnection(ctx, conn, aws.ToString(connection.ConnectionId), waitConnectionDeleted); err != nil { return sdkdiag.AppendFromErr(diags, err) } } @@ -217,11 +221,11 @@ func resourceLagDelete(ctx context.Context, d *schema.ResourceData, meta interfa } log.Printf("[DEBUG] Deleting Direct Connect LAG: %s", d.Id()) - _, err := conn.DeleteLagWithContext(ctx, &directconnect.DeleteLagInput{ + _, err := conn.DeleteLag(ctx, &directconnect.DeleteLagInput{ LagId: aws.String(d.Id()), }) - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "Could not find Lag with ID") { + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Could not find Lag with ID") { return diags } @@ -229,11 +233,96 @@ func resourceLagDelete(ctx context.Context, d *schema.ResourceData, meta interfa return sdkdiag.AppendErrorf(diags, "deleting Direct Connect LAG (%s): %s", d.Id(), err) } - _, err = waitLagDeleted(ctx, conn, d.Id()) - - if err != nil { + if _, err := waitLagDeleted(ctx, conn, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect LAG (%s) delete: %s", d.Id(), err) } return diags } + +func findLagByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Lag, error) { + input := &directconnect.DescribeLagsInput{ + LagId: aws.String(id), + } + output, err := findLag(ctx, conn, input, tfslices.PredicateTrue[*awstypes.Lag]()) + + if err != nil { + return nil, err + } + + if state := output.LagState; state == awstypes.LagStateDeleted { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, + } + } + + return output, nil +} + +func findLag(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeLagsInput, filter tfslices.Predicate[*awstypes.Lag]) (*awstypes.Lag, error) { + output, err := findLags(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findLags(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeLagsInput, filter tfslices.Predicate[*awstypes.Lag]) ([]awstypes.Lag, error) { + output, err := conn.DescribeLags(ctx, input) + + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "Could not find Lag with ID") { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return tfslices.Filter(output.Lags, tfslices.PredicateValue(filter)), nil +} + +func statusLag(ctx context.Context, conn *directconnect.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findLagByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.LagState), nil + } +} + +func waitLagDeleted(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.Lag, error) { + const ( + timeout = 10 * time.Minute + ) + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.LagStateAvailable, awstypes.LagStateRequested, awstypes.LagStatePending, awstypes.LagStateDeleting), + Target: []string{}, + Refresh: statusLag(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Lag); ok { + return output, err + } + + return nil, err +} diff --git a/internal/service/directconnect/lag_test.go b/internal/service/directconnect/lag_test.go index 5197e251568..38b03430280 100644 --- a/internal/service/directconnect/lag_test.go +++ b/internal/service/directconnect/lag_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,7 +22,7 @@ import ( func TestAccDirectConnectLag_basic(t *testing.T) { ctx := acctest.Context(t) - var lag directconnect.Lag + var lag awstypes.Lag resourceName := "aws_dx_lag.test" rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -39,7 +39,7 @@ func TestAccDirectConnectLag_basic(t *testing.T) { testAccCheckLagExists(ctx, resourceName, &lag), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(`dxlag/.+`)), resource.TestCheckNoResourceAttr(resourceName, names.AttrConnectionID), - resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "1Gbps"), + resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "10Gbps"), resource.TestCheckResourceAttr(resourceName, names.AttrForceDestroy, acctest.CtFalse), resource.TestCheckResourceAttrSet(resourceName, "has_logical_redundancy"), resource.TestCheckResourceAttrSet(resourceName, "jumbo_frame_capable"), @@ -56,7 +56,7 @@ func TestAccDirectConnectLag_basic(t *testing.T) { testAccCheckLagExists(ctx, resourceName, &lag), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(`dxlag/.+`)), resource.TestCheckNoResourceAttr(resourceName, names.AttrConnectionID), - resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "1Gbps"), + resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "10Gbps"), resource.TestCheckResourceAttr(resourceName, names.AttrForceDestroy, acctest.CtFalse), resource.TestCheckResourceAttrSet(resourceName, "has_logical_redundancy"), resource.TestCheckResourceAttrSet(resourceName, "jumbo_frame_capable"), @@ -79,7 +79,7 @@ func TestAccDirectConnectLag_basic(t *testing.T) { func TestAccDirectConnectLag_disappears(t *testing.T) { ctx := acctest.Context(t) - var lag directconnect.Lag + var lag awstypes.Lag resourceName := "aws_dx_lag.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -103,7 +103,7 @@ func TestAccDirectConnectLag_disappears(t *testing.T) { func TestAccDirectConnectLag_connectionID(t *testing.T) { ctx := acctest.Context(t) - var lag directconnect.Lag + var lag awstypes.Lag resourceName := "aws_dx_lag.test" connectionResourceName := "aws_dx_connection.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -120,7 +120,7 @@ func TestAccDirectConnectLag_connectionID(t *testing.T) { testAccCheckLagExists(ctx, resourceName, &lag), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(`dxlag/.+`)), resource.TestCheckResourceAttrPair(resourceName, names.AttrConnectionID, connectionResourceName, names.AttrID), - resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "1Gbps"), + resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "10Gbps"), resource.TestCheckResourceAttr(resourceName, names.AttrForceDestroy, acctest.CtFalse), resource.TestCheckResourceAttrSet(resourceName, "has_logical_redundancy"), resource.TestCheckResourceAttrSet(resourceName, "jumbo_frame_capable"), @@ -143,7 +143,7 @@ func TestAccDirectConnectLag_connectionID(t *testing.T) { func TestAccDirectConnectLag_providerName(t *testing.T) { ctx := acctest.Context(t) - var lag directconnect.Lag + var lag awstypes.Lag resourceName := "aws_dx_lag.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -159,7 +159,7 @@ func TestAccDirectConnectLag_providerName(t *testing.T) { testAccCheckLagExists(ctx, resourceName, &lag), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(`dxlag/.+`)), resource.TestCheckNoResourceAttr(resourceName, names.AttrConnectionID), - resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "1Gbps"), + resource.TestCheckResourceAttr(resourceName, "connections_bandwidth", "10Gbps"), resource.TestCheckResourceAttr(resourceName, names.AttrForceDestroy, acctest.CtFalse), resource.TestCheckResourceAttrSet(resourceName, "has_logical_redundancy"), resource.TestCheckResourceAttrSet(resourceName, "jumbo_frame_capable"), @@ -182,7 +182,7 @@ func TestAccDirectConnectLag_providerName(t *testing.T) { func TestAccDirectConnectLag_tags(t *testing.T) { ctx := acctest.Context(t) - var lag directconnect.Lag + var lag awstypes.Lag resourceName := "aws_dx_lag.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -232,7 +232,7 @@ func TestAccDirectConnectLag_tags(t *testing.T) { func testAccCheckLagDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_dx_lag" { @@ -256,26 +256,22 @@ func testAccCheckLagDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckLagExists(ctx context.Context, name string, v *directconnect.Lag) resource.TestCheckFunc { +func testAccCheckLagExists(ctx context.Context, name string, v *awstypes.Lag) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectConn(ctx) - rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("Not found: %s", name) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) - lag, err := tfdirectconnect.FindLagByID(ctx, conn, rs.Primary.ID) + output, err := tfdirectconnect.FindLagByID(ctx, conn, rs.Primary.ID) if err != nil { return err } - *v = *lag + *v = *output return nil } @@ -287,7 +283,7 @@ data "aws_dx_locations" "test" {} resource "aws_dx_lag" "test" { name = %[1]q - connections_bandwidth = "1Gbps" + connections_bandwidth = "10Gbps" location = tolist(data.aws_dx_locations.test.location_codes)[0] } `, rName) @@ -306,7 +302,7 @@ resource "aws_dx_lag" "test" { resource "aws_dx_connection" "test" { name = %[1]q - bandwidth = "1Gbps" + bandwidth = "10Gbps" location = tolist(data.aws_dx_locations.test.location_codes)[1] } `, rName) @@ -322,7 +318,7 @@ data "aws_dx_location" "test" { resource "aws_dx_lag" "test" { name = %[1]q - connections_bandwidth = "1Gbps" + connections_bandwidth = "10Gbps" location = data.aws_dx_location.test.location_code provider_name = data.aws_dx_location.test.available_providers[0] @@ -336,7 +332,7 @@ data "aws_dx_locations" "test" {} resource "aws_dx_lag" "test" { name = %[1]q - connections_bandwidth = "1Gbps" + connections_bandwidth = "10Gbps" location = tolist(data.aws_dx_locations.test.location_codes)[0] force_destroy = true @@ -353,7 +349,7 @@ data "aws_dx_locations" "test" {} resource "aws_dx_lag" "test" { name = %[1]q - connections_bandwidth = "1Gbps" + connections_bandwidth = "10Gbps" location = tolist(data.aws_dx_locations.test.location_codes)[0] force_destroy = true diff --git a/internal/service/directconnect/list_pages_gen.go b/internal/service/directconnect/list_pages_gen.go index bfe7fbc49fd..a6ea2a72ee5 100644 --- a/internal/service/directconnect/list_pages_gen.go +++ b/internal/service/directconnect/list_pages_gen.go @@ -1,23 +1,22 @@ -// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeDirectConnectGateways,DescribeDirectConnectGatewayAssociations,DescribeDirectConnectGatewayAssociationProposals"; DO NOT EDIT. +// Code generated by "internal/generate/listpages/main.go -AWSSDKVersion=2 -ListOps=DescribeDirectConnectGateways,DescribeDirectConnectGatewayAssociations,DescribeDirectConnectGatewayAssociationProposals"; DO NOT EDIT. package directconnect import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/aws/aws-sdk-go/service/directconnect/directconnectiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" ) -func describeGatewayAssociationProposalsPages(ctx context.Context, conn directconnectiface.DirectConnectAPI, input *directconnect.DescribeDirectConnectGatewayAssociationProposalsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, bool) bool) error { +func describeDirectConnectGatewayAssociationProposalsPages(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationProposalsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, bool) bool) error { for { - output, err := conn.DescribeDirectConnectGatewayAssociationProposalsWithContext(ctx, input) + output, err := conn.DescribeDirectConnectGatewayAssociationProposals(ctx, input) if err != nil { return err } - lastPage := aws.StringValue(output.NextToken) == "" + lastPage := aws.ToString(output.NextToken) == "" if !fn(output, lastPage) || lastPage { break } @@ -26,14 +25,14 @@ func describeGatewayAssociationProposalsPages(ctx context.Context, conn directco } return nil } -func describeGatewayAssociationsPages(ctx context.Context, conn directconnectiface.DirectConnectAPI, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationsOutput, bool) bool) error { +func describeDirectConnectGatewayAssociationsPages(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewayAssociationsInput, fn func(*directconnect.DescribeDirectConnectGatewayAssociationsOutput, bool) bool) error { for { - output, err := conn.DescribeDirectConnectGatewayAssociationsWithContext(ctx, input) + output, err := conn.DescribeDirectConnectGatewayAssociations(ctx, input) if err != nil { return err } - lastPage := aws.StringValue(output.NextToken) == "" + lastPage := aws.ToString(output.NextToken) == "" if !fn(output, lastPage) || lastPage { break } @@ -42,14 +41,14 @@ func describeGatewayAssociationsPages(ctx context.Context, conn directconnectifa } return nil } -func describeGatewaysPages(ctx context.Context, conn directconnectiface.DirectConnectAPI, input *directconnect.DescribeDirectConnectGatewaysInput, fn func(*directconnect.DescribeDirectConnectGatewaysOutput, bool) bool) error { +func describeDirectConnectGatewaysPages(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeDirectConnectGatewaysInput, fn func(*directconnect.DescribeDirectConnectGatewaysOutput, bool) bool) error { for { - output, err := conn.DescribeDirectConnectGatewaysWithContext(ctx, input) + output, err := conn.DescribeDirectConnectGateways(ctx, input) if err != nil { return err } - lastPage := aws.StringValue(output.NextToken) == "" + lastPage := aws.ToString(output.NextToken) == "" if !fn(output, lastPage) || lastPage { break } diff --git a/internal/service/directconnect/location_data_source.go b/internal/service/directconnect/location_data_source.go index d865335e036..a2fbfb7be0a 100644 --- a/internal/service/directconnect/location_data_source.go +++ b/internal/service/directconnect/location_data_source.go @@ -6,16 +6,19 @@ package directconnect import ( "context" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "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" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -// @SDKDataSource("aws_dx_location") -func DataSourceLocation() *schema.Resource { +// @SDKDataSource("aws_dx_location", name="Location") +func dataSourceLocation() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceLocationRead, @@ -25,24 +28,20 @@ func DataSourceLocation() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "available_port_speeds": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "available_providers": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "location_code": { Type: schema.TypeString, Required: true, }, - "location_name": { Type: schema.TypeString, Computed: true, @@ -53,25 +52,48 @@ func DataSourceLocation() *schema.Resource { func dataSourceLocationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) - locationCode := d.Get("location_code").(string) - - location, err := FindLocationByCode(ctx, conn, locationCode) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - if tfresource.NotFound(err) { - return sdkdiag.AppendErrorf(diags, "no Direct Connect location matched; change the search criteria and try again") - } + input := &directconnect.DescribeLocationsInput{} + locationCode := d.Get("location_code").(string) + location, err := findLocation(ctx, conn, input, func(v *awstypes.Location) bool { + return aws.ToString(v.LocationCode) == locationCode + }) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Direct Connect location (%s): %s", locationCode, err) + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("Direct Connect Location", err)) } d.SetId(locationCode) - d.Set("available_macsec_port_speeds", aws.StringValueSlice(location.AvailableMacSecPortSpeeds)) - d.Set("available_port_speeds", aws.StringValueSlice(location.AvailablePortSpeeds)) - d.Set("available_providers", aws.StringValueSlice(location.AvailableProviders)) + d.Set("available_macsec_port_speeds", location.AvailableMacSecPortSpeeds) + d.Set("available_port_speeds", location.AvailablePortSpeeds) + d.Set("available_providers", location.AvailableProviders) d.Set("location_code", location.LocationCode) d.Set("location_name", location.LocationName) return diags } + +func findLocation(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeLocationsInput, filter tfslices.Predicate[*awstypes.Location]) (*awstypes.Location, error) { + output, err := findLocations(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findLocations(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeLocationsInput, filter tfslices.Predicate[*awstypes.Location]) ([]awstypes.Location, error) { + output, err := conn.DescribeLocations(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return tfslices.Filter(output.Locations, tfslices.PredicateValue(filter)), nil +} diff --git a/internal/service/directconnect/locations_data_source.go b/internal/service/directconnect/locations_data_source.go index 78bfc6437c6..123baa2fd3b 100644 --- a/internal/service/directconnect/locations_data_source.go +++ b/internal/service/directconnect/locations_data_source.go @@ -6,16 +6,18 @@ package directconnect import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "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" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" ) -// @SDKDataSource("aws_dx_locations") -func DataSourceLocations() *schema.Resource { +// @SDKDataSource("aws_dx_locations", name="Locations") +func dataSourceLocations() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceLocationsRead, @@ -31,22 +33,19 @@ func DataSourceLocations() *schema.Resource { func dataSourceLocationsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - locations, err := FindLocations(ctx, conn, &directconnect.DescribeLocationsInput{}) + input := &directconnect.DescribeLocationsInput{} + locations, err := findLocations(ctx, conn, input, tfslices.PredicateTrue[*awstypes.Location]()) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Direct Connect locations: %s", err) - } - - var locationCodes []*string - - for _, location := range locations { - locationCodes = append(locationCodes, location.LocationCode) + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Locations: %s", err) } d.SetId(meta.(*conns.AWSClient).Region) - d.Set("location_codes", aws.StringValueSlice(locationCodes)) + d.Set("location_codes", tfslices.ApplyToAll(locations, func(v awstypes.Location) string { + return aws.ToString(v.LocationCode) + })) return diags } diff --git a/internal/service/directconnect/macsec_key.go b/internal/service/directconnect/macsec_key.go deleted file mode 100644 index 7503324efea..00000000000 --- a/internal/service/directconnect/macsec_key.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect - -import ( - "context" - "fmt" - "log" - "strings" - - "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "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/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/names" -) - -// @SDKResource("aws_dx_macsec_key_association") -func ResourceMacSecKeyAssociation() *schema.Resource { - return &schema.Resource{ - // MacSecKey resource only supports create (Associate), read (Describe) and delete (Disassociate) - CreateWithoutTimeout: resourceMacSecKeyCreate, - ReadWithoutTimeout: resourceMacSecKeyRead, - DeleteWithoutTimeout: resourceMacSecKeyDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - - Schema: map[string]*schema.Schema{ - "cak": { - Type: schema.TypeString, - Optional: true, - // CAK requires CKN - RequiredWith: []string{"ckn"}, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`[0-9A-Fa-f]{64}$`), "Must be 64-character hex code string"), - ForceNew: true, - }, - "ckn": { - Type: schema.TypeString, - Computed: true, - Optional: true, - AtLeastOneOf: []string{"ckn", "secret_arn"}, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`[0-9A-Fa-f]{64}$`), "Must be 64-character hex code string"), - ForceNew: true, - }, - names.AttrConnectionID: { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "secret_arn": { - Type: schema.TypeString, - Optional: true, - Computed: true, - AtLeastOneOf: []string{"ckn", "secret_arn"}, - ForceNew: true, - }, - "start_on": { - Type: schema.TypeString, - Computed: true, - }, - names.AttrState: { - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func resourceMacSecKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) - - input := &directconnect.AssociateMacSecKeyInput{ - ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - } - - if d.Get("ckn").(string) != "" { - input.Cak = aws.String(d.Get("cak").(string)) - input.Ckn = aws.String(d.Get("ckn").(string)) - } - - if d.Get("secret_arn").(string) != "" { - input.SecretARN = aws.String(d.Get("secret_arn").(string)) - } - - log.Printf("[DEBUG] Creating MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) - output, err := conn.AssociateMacSecKeyWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "creating MACSec secret key on Direct Connect Connection (%s): %s", *input.ConnectionId, err) - } - - secret_arn := MacSecKeyParseSecretARN(output) - - // Create a composite ID based on connection ID and secret ARN - d.SetId(fmt.Sprintf("%s_%s", secret_arn, aws.StringValue(output.ConnectionId))) - - d.Set("secret_arn", secret_arn) - - return append(diags, resourceMacSecKeyRead(ctx, d, meta)...) -} - -func resourceMacSecKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) - - secretArn, connId, err := MacSecKeyParseID(d.Id()) - if err != nil { - return sdkdiag.AppendErrorf(diags, "unexpected format of ID (%s), expected secretArn_connectionId", d.Id()) - } - - connection, err := FindConnectionByID(ctx, conn, connId) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Direct Connect Connection (%s): %s", d.Id(), err) - } - - if connection.MacSecKeys == nil { - return sdkdiag.AppendErrorf(diags, "no MACSec keys found on Direct Connect Connection (%s)", d.Id()) - } - - for _, key := range connection.MacSecKeys { - if aws.StringValue(key.SecretARN) == aws.StringValue(&secretArn) { - d.Set("ckn", key.Ckn) - d.Set(names.AttrConnectionID, connId) - d.Set("secret_arn", key.SecretARN) - d.Set("start_on", key.StartOn) - d.Set(names.AttrState, key.State) - } - } - - return diags -} - -func resourceMacSecKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) - - input := &directconnect.DisassociateMacSecKeyInput{ - ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - SecretARN: aws.String(d.Get("secret_arn").(string)), - } - - log.Printf("[DEBUG] Disassociating MACSec secret key on Direct Connect Connection: %s", *input.ConnectionId) - _, err := conn.DisassociateMacSecKeyWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "Unable to disassociate MACSec secret key on Direct Connect Connection (%s): %s", *input.ConnectionId, err) - } - - return diags -} - -// MacSecKeyParseSecretARN parses the secret ARN returned from a CMK or secret_arn -func MacSecKeyParseSecretARN(output *directconnect.AssociateMacSecKeyOutput) string { - var result string - - for _, key := range output.MacSecKeys { - if key != nil { - result = aws.StringValue(key.SecretARN) - } - } - - return result -} - -// MacSecKeyParseID parses the resource ID and returns the secret ARN and connection ID -func MacSecKeyParseID(id string) (string, string, error) { - parts := strings.SplitN(id, "_", 2) - - if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", &retry.NotFoundError{} - } - - return parts[0], parts[1], nil -} diff --git a/internal/service/directconnect/macsec_key_association.go b/internal/service/directconnect/macsec_key_association.go new file mode 100644 index 00000000000..753614436d1 --- /dev/null +++ b/internal/service/directconnect/macsec_key_association.go @@ -0,0 +1,194 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package directconnect + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "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/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKResource("aws_dx_macsec_key_association", name="MACSec Key Association") +func resourceMacSecKeyAssociation() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceMacSecKeyAssociatioCreate, + ReadWithoutTimeout: resourceMacSecKeyAssociationRead, + DeleteWithoutTimeout: resourceMacSecKeyAssociationDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "cak": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + RequiredWith: []string{"ckn"}, + ValidateFunc: validation.StringMatch(regexache.MustCompile(`[0-9A-Fa-f]{64}$`), "Must be 64-character hex code string"), + }, + "ckn": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + AtLeastOneOf: []string{"ckn", "secret_arn"}, + ValidateFunc: validation.StringMatch(regexache.MustCompile(`[0-9A-Fa-f]{64}$`), "Must be 64-character hex code string"), + }, + names.AttrConnectionID: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "secret_arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + AtLeastOneOf: []string{"ckn", "secret_arn"}, + ValidateFunc: verify.ValidARN, + }, + "start_on": { + Type: schema.TypeString, + Computed: true, + }, + names.AttrState: { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceMacSecKeyAssociatioCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + connectionID := d.Get(names.AttrConnectionID).(string) + input := &directconnect.AssociateMacSecKeyInput{ + ConnectionId: aws.String(connectionID), + } + + if v, ok := d.GetOk("ckn"); ok { + input.Cak = aws.String(d.Get("cak").(string)) + input.Ckn = aws.String(v.(string)) + } + + if v, ok := d.GetOk("secret_arn"); ok { + input.SecretARN = aws.String(v.(string)) + } + + output, err := conn.AssociateMacSecKey(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating MACSec Key Association with Direct Connect Connection (%s): %s", connectionID, err) + } + + var secretARN string + for _, key := range output.MacSecKeys { + secretARN = aws.ToString(key.SecretARN) + } + + d.SetId(macSecKeyAssociationCreateResourceID(secretARN, connectionID)) + + return append(diags, resourceMacSecKeyAssociationRead(ctx, d, meta)...) +} + +func resourceMacSecKeyAssociationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + secretARN, connectionID, err := macSecKeyAssociationParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + key, err := findMacSecKeyByTwoPartKey(ctx, conn, connectionID, secretARN) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect MACSec Key Association (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect MACSec Key Association (%s): %s", d.Id(), err) + } + + d.Set("ckn", key.Ckn) + d.Set(names.AttrConnectionID, connectionID) + d.Set("secret_arn", key.SecretARN) + d.Set("start_on", key.StartOn) + d.Set(names.AttrState, key.State) + + return diags +} + +func resourceMacSecKeyAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + secretARN, connectionID, err := macSecKeyAssociationParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + log.Printf("[DEBUG] Deleting Direct Connect MACSec Key Association: %s", d.Id()) + _, err = conn.DisassociateMacSecKey(ctx, &directconnect.DisassociateMacSecKeyInput{ + ConnectionId: aws.String(connectionID), + SecretARN: aws.String(secretARN), + }) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting MACSec Key Association (%s): %s", d.Id(), err) + } + + return diags +} + +const macSecKeyAssociationResourceIDSeparator = "_" + +func macSecKeyAssociationCreateResourceID(secretARN, connectionID string) string { + parts := []string{secretARN, connectionID} + id := strings.Join(parts, macSecKeyAssociationResourceIDSeparator) + + return id +} + +func macSecKeyAssociationParseResourceID(id string) (string, string, error) { + parts := strings.SplitN(id, macSecKeyAssociationResourceIDSeparator, 2) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected secretArn%[2]sconnectionId", id, macSecKeyAssociationResourceIDSeparator) +} + +func findMacSecKeyByTwoPartKey(ctx context.Context, conn *directconnect.Client, connectionID, secretARN string) (*awstypes.MacSecKey, error) { + output, err := findConnectionByID(ctx, conn, connectionID) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(tfslices.Filter(output.MacSecKeys, func(v awstypes.MacSecKey) bool { + return aws.ToString(v.SecretARN) == secretARN + })) +} diff --git a/internal/service/directconnect/macsec_key_association_test.go b/internal/service/directconnect/macsec_key_association_test.go new file mode 100644 index 00000000000..063d6f8a12b --- /dev/null +++ b/internal/service/directconnect/macsec_key_association_test.go @@ -0,0 +1,155 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package directconnect_test + +import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "testing" + + "github.com/YakDriver/regexache" + "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" + tfdirectconnect "github.com/hashicorp/terraform-provider-aws/internal/service/directconnect" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccDirectConnectMacSecKeyAssociation_withCkn(t *testing.T) { + ctx := acctest.Context(t) + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") + resourceName := "aws_dx_macsec_key_association.test" + ckn := testAccMacSecGenerateHex() + cak := testAccMacSecGenerateHex() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckMacSecKeyAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccMacSecKeyAssociationConfig_withCkn(ckn, cak, connectionID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMacSecKeyAssociationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), + resource.TestMatchResourceAttr(resourceName, "ckn", regexache.MustCompile(ckn)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + // Ignore the "cak" attribute as isn't returned by the API during read/refresh + ImportStateVerifyIgnore: []string{"cak"}, + }, + }, + }) +} + +func TestAccDirectConnectMacSecKeyAssociation_withSecret(t *testing.T) { + ctx := acctest.Context(t) + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") + secretARN := acctest.SkipIfEnvVarNotSet(t, "SECRET_ARN") + resourceName := "aws_dx_macsec_key_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckMacSecKeyAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccMacSecKeyAssociationConfig_withSecret(secretARN, connectionID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMacSecKeyAssociationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), + resource.TestCheckResourceAttr(resourceName, "secret_arn", secretARN), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccMacSecGenerateHex() string { + s := make([]byte, 32) + if _, err := rand.Read(s); err != nil { + return "" + } + return hex.EncodeToString(s) +} + +func testAccCheckMacSecKeyAssociationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_dx_macsec_key_association" { + continue + } + + _, err := tfdirectconnect.FindMacSecKeyByTwoPartKey(ctx, conn, rs.Primary.Attributes[names.AttrConnectionID], rs.Primary.Attributes["secret_arn"]) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("Direct Connect MACSec Key Association %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckMacSecKeyAssociationExists(ctx context.Context, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) + + _, err := tfdirectconnect.FindMacSecKeyByTwoPartKey(ctx, conn, rs.Primary.Attributes[names.AttrConnectionID], rs.Primary.Attributes["secret_arn"]) + + return err + } +} + +func testAccMacSecKeyAssociationConfig_withCkn(ckn, cak, connectionID string) string { + return fmt.Sprintf(` +resource "aws_dx_macsec_key_association" "test" { + connection_id = %[3]q + ckn = %[1]q + cak = %[2]q +} +`, ckn, cak, connectionID) +} + +// Can only be used with an EXISTING secrets created by previous association - cannot create secrets from scratch. +func testAccMacSecKeyAssociationConfig_withSecret(secretARN, connectionID string) string { + return fmt.Sprintf(` +data "aws_secretsmanager_secret" "test" { + arn = %[1]q +} + +resource "aws_dx_macsec_key_association" "test" { + connection_id = %[2]q + secret_arn = data.aws_secretsmanager_secret.test.arn +} +`, secretARN, connectionID) +} diff --git a/internal/service/directconnect/macsec_key_test.go b/internal/service/directconnect/macsec_key_test.go deleted file mode 100644 index 7770bfa57ae..00000000000 --- a/internal/service/directconnect/macsec_key_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect_test - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "os" - "testing" - - "github.com/YakDriver/regexache" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-provider-aws/internal/acctest" - "github.com/hashicorp/terraform-provider-aws/names" -) - -func TestAccDirectConnectMacSecKey_withCkn(t *testing.T) { - ctx := acctest.Context(t) - // Requires an existing MACsec-capable DX connection set as environmental variable - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } - resourceName := "aws_dx_macsec_key_association.test" - ckn := testAccDirecConnectMacSecGenerateHex() - cak := testAccDirecConnectMacSecGenerateHex() - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: nil, - Steps: []resource.TestStep{ - { - Config: testAccMacSecConfig_withCkn(ckn, cak, connectionId), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), - resource.TestMatchResourceAttr(resourceName, "ckn", regexache.MustCompile(ckn)), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - // Ignore the "cak" attribute as isn't returned by the API during read/refresh - ImportStateVerifyIgnore: []string{"cak"}, - }, - }, - }) -} - -func TestAccDirectConnectMacSecKey_withSecret(t *testing.T) { - ctx := acctest.Context(t) - // Requires an existing MACsec-capable DX connection set as environmental variable - dxKey := "DX_CONNECTION_ID" - connectionId := os.Getenv(dxKey) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", dxKey) - } - - secretKey := "SECRET_ARN" - secretArn := os.Getenv(secretKey) - if secretArn == "" { - t.Skipf("Environment variable %s is not set", secretKey) - } - - resourceName := "aws_dx_macsec_key_association.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: nil, - Steps: []resource.TestStep{ - { - Config: testAccMacSecConfig_withSecret(secretArn, connectionId), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), - resource.TestCheckResourceAttr(resourceName, "secret_arn", secretArn), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -// testAccDirecConnectMacSecGenerateKey generates a 64-character hex string to be used as CKN or CAK -func testAccDirecConnectMacSecGenerateHex() string { - s := make([]byte, 32) - if _, err := rand.Read(s); err != nil { - return "" - } - return hex.EncodeToString(s) -} - -func testAccMacSecConfig_withCkn(ckn, cak, connectionId string) string { - return fmt.Sprintf(` -resource "aws_dx_macsec_key_association" "test" { - connection_id = %[3]q - ckn = %[1]q - cak = %[2]q -} - - -`, ckn, cak, connectionId) -} - -// Can only be used with an EXISTING secrets created by previous association - cannot create secrets from scratch -func testAccMacSecConfig_withSecret(secretArn, connectionId string) string { - return fmt.Sprintf(` -data "aws_secretsmanager_secret" "test" { - arn = %[1]q -} - -resource "aws_dx_macsec_key_association" "test" { - connection_id = %[2]q - secret_arn = data.aws_secretsmanager_secret.test.arn -} - - -`, secretArn, connectionId) -} diff --git a/internal/service/directconnect/private_virtual_interface.go b/internal/service/directconnect/private_virtual_interface.go index 71efc7bf66f..8f60ee7a27b 100644 --- a/internal/service/directconnect/private_virtual_interface.go +++ b/internal/service/directconnect/private_virtual_interface.go @@ -10,40 +10,41 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_dx_private_virtual_interface", name="Private Virtual Interface") // @Tags(identifierAttribute="arn") -func ResourcePrivateVirtualInterface() *schema.Resource { +func resourcePrivateVirtualInterface() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourcePrivateVirtualInterfaceCreate, ReadWithoutTimeout: resourcePrivateVirtualInterfaceRead, UpdateWithoutTimeout: resourcePrivateVirtualInterfaceUpdate, DeleteWithoutTimeout: resourcePrivateVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ StateContext: resourcePrivateVirtualInterfaceImport, }, Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - directconnect.AddressFamilyIpv4, - directconnect.AddressFamilyIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, "amazon_address": { Type: schema.TypeString, @@ -86,10 +87,10 @@ func ResourcePrivateVirtualInterface() *schema.Resource { ForceNew: true, }, "dx_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"vpn_gateway_id"}, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"dx_gateway_id", "vpn_gateway_id"}, }, "jumbo_frame_capable": { Type: schema.TypeBool, @@ -119,10 +120,10 @@ func ResourcePrivateVirtualInterface() *schema.Resource { ValidateFunc: validation.IntBetween(1, 4094), }, "vpn_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"dx_gateway_id"}, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"dx_gateway_id", "vpn_gateway_id"}, }, }, @@ -138,52 +139,51 @@ func ResourcePrivateVirtualInterface() *schema.Resource { func resourcePrivateVirtualInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vgwIdRaw, vgwOk := d.GetOk("vpn_gateway_id") - dxgwIdRaw, dxgwOk := d.GetOk("dx_gateway_id") - if vgwOk == dxgwOk { - return sdkdiag.AppendErrorf(diags, "One of ['vpn_gateway_id', 'dx_gateway_id'] must be set to create a Direct Connect private virtual interface") - } - - req := &directconnect.CreatePrivateVirtualInterfaceInput{ + input := &directconnect.CreatePrivateVirtualInterfaceInput{ ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - NewPrivateVirtualInterface: &directconnect.NewPrivateVirtualInterface{ - AddressFamily: aws.String(d.Get("address_family").(string)), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), + NewPrivateVirtualInterface: &awstypes.NewPrivateVirtualInterface{ + AddressFamily: awstypes.AddressFamily(d.Get("address_family").(string)), + Asn: int32(d.Get("bgp_asn").(int)), EnableSiteLink: aws.Bool(d.Get("sitelink_enabled").(bool)), - Mtu: aws.Int64(int64(d.Get("mtu").(int))), + Mtu: aws.Int32(int32(d.Get("mtu").(int))), Tags: getTagsIn(ctx), VirtualInterfaceName: aws.String(d.Get(names.AttrName).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), }, } - if vgwOk && vgwIdRaw.(string) != "" { - req.NewPrivateVirtualInterface.VirtualGatewayId = aws.String(vgwIdRaw.(string)) - } - if dxgwOk && dxgwIdRaw.(string) != "" { - req.NewPrivateVirtualInterface.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) - } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewPrivateVirtualInterface.AmazonAddress = aws.String(v.(string)) + input.NewPrivateVirtualInterface.AmazonAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewPrivateVirtualInterface.AuthKey = aws.String(v.(string)) + input.NewPrivateVirtualInterface.AuthKey = aws.String(v.(string)) } + if v, ok := d.GetOk("customer_address"); ok { - req.NewPrivateVirtualInterface.CustomerAddress = aws.String(v.(string)) + input.NewPrivateVirtualInterface.CustomerAddress = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating Direct Connect private virtual interface: %s", req) - resp, err := conn.CreatePrivateVirtualInterfaceWithContext(ctx, req) + if v, ok := d.GetOk("dx_gateway_id"); ok { + input.NewPrivateVirtualInterface.DirectConnectGatewayId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("vpn_gateway_id"); ok { + input.NewPrivateVirtualInterface.VirtualGatewayId = aws.String(v.(string)) + } + + output, err := conn.CreatePrivateVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Direct Connect private virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect Private Virtual Interface: %s", err) } - d.SetId(aws.StringValue(resp.VirtualInterfaceId)) + d.SetId(aws.ToString(output.VirtualInterfaceId)) - if err := privateVirtualInterfaceWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitPrivateVirtualInterfaceAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Private Virtual Interface (%s) create: %s", d.Id(), err) } return append(diags, resourcePrivateVirtualInterfaceRead(ctx, d, meta)...) @@ -191,21 +191,23 @@ func resourcePrivateVirtualInterfaceCreate(ctx context.Context, d *schema.Resour func resourcePrivateVirtualInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect private virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Private Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Private Virtual Interface (%s): %s", d.Id(), err) + } + d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(vif.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", strconv.FormatInt(aws.ToInt64(vif.AmazonSideAsn), 10)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -238,8 +240,8 @@ func resourcePrivateVirtualInterfaceUpdate(ctx context.Context, d *schema.Resour return diags } - if err := privateVirtualInterfaceWaitUntilAvailable(ctx, meta.(*conns.AWSClient).DirectConnectConn(ctx), d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitPrivateVirtualInterfaceAvailable(ctx, meta.(*conns.AWSClient).DirectConnectClient(ctx), d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Private Virtual Interface (%s) update: %s", d.Id(), err) } return append(diags, resourcePrivateVirtualInterfaceRead(ctx, d, meta)...) @@ -250,32 +252,28 @@ func resourcePrivateVirtualInterfaceDelete(ctx context.Context, d *schema.Resour } func resourcePrivateVirtualInterfaceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "private" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "private" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } return []*schema.ResourceData{d}, nil } -func privateVirtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitPrivateVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateDown), timeout, - []string{ - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateDown, - }) + ) } diff --git a/internal/service/directconnect/private_virtual_interface_test.go b/internal/service/directconnect/private_virtual_interface_test.go index 940c861dfa8..8c057ed7dd3 100644 --- a/internal/service/directconnect/private_virtual_interface_test.go +++ b/internal/service/directconnect/private_virtual_interface_test.go @@ -6,13 +6,12 @@ package directconnect_test import ( "context" "fmt" - "os" "strconv" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,13 +21,9 @@ import ( func TestAccDirectConnectPrivateVirtualInterface_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_private_virtual_interface.test" vpnGatewayResourceName := "aws_vpn_gateway.test" rName := fmt.Sprintf("tf-testacc-private-vif-%s", sdkacctest.RandString(9)) @@ -42,17 +37,17 @@ func TestAccDirectConnectPrivateVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckPrivateVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPrivateVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), + Config: testAccPrivateVirtualInterfaceConfig_basic(connectionID, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), @@ -63,17 +58,17 @@ func TestAccDirectConnectPrivateVirtualInterface_basic(t *testing.T) { ), }, { - Config: testAccPrivateVirtualInterfaceConfig_updated(connectionId, rName, bgpAsn, vlan), + Config: testAccPrivateVirtualInterfaceConfig_updated(connectionID, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "9001"), @@ -83,7 +78,6 @@ func TestAccDirectConnectPrivateVirtualInterface_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", vpnGatewayResourceName, names.AttrID), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -95,13 +89,9 @@ func TestAccDirectConnectPrivateVirtualInterface_basic(t *testing.T) { func TestAccDirectConnectPrivateVirtualInterface_tags(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_private_virtual_interface.test" vpnGatewayResourceName := "aws_vpn_gateway.test" rName := fmt.Sprintf("tf-testacc-private-vif-%s", sdkacctest.RandString(9)) @@ -115,17 +105,17 @@ func TestAccDirectConnectPrivateVirtualInterface_tags(t *testing.T) { CheckDestroy: testAccCheckPrivateVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPrivateVirtualInterfaceConfig_tags(connectionId, rName, bgpAsn, vlan), + Config: testAccPrivateVirtualInterfaceConfig_tags(connectionID, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), @@ -139,17 +129,17 @@ func TestAccDirectConnectPrivateVirtualInterface_tags(t *testing.T) { ), }, { - Config: testAccPrivateVirtualInterfaceConfig_tagsUpdated(connectionId, rName, bgpAsn, vlan), + Config: testAccPrivateVirtualInterfaceConfig_tagsUpdated(connectionID, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), @@ -162,7 +152,6 @@ func TestAccDirectConnectPrivateVirtualInterface_tags(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", vpnGatewayResourceName, names.AttrID), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -174,13 +163,9 @@ func TestAccDirectConnectPrivateVirtualInterface_tags(t *testing.T) { func TestAccDirectConnectPrivateVirtualInterface_dxGateway(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_private_virtual_interface.test" dxGatewayResourceName := "aws_dx_gateway.test" rName := fmt.Sprintf("tf-testacc-private-vif-%s", sdkacctest.RandString(9)) @@ -195,17 +180,17 @@ func TestAccDirectConnectPrivateVirtualInterface_dxGateway(t *testing.T) { CheckDestroy: testAccCheckPrivateVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPrivateVirtualInterfaceConfig_gateway(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccPrivateVirtualInterfaceConfig_gateway(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -215,7 +200,6 @@ func TestAccDirectConnectPrivateVirtualInterface_dxGateway(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -227,13 +211,9 @@ func TestAccDirectConnectPrivateVirtualInterface_dxGateway(t *testing.T) { func TestAccDirectConnectPrivateVirtualInterface_siteLink(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_private_virtual_interface.test" dxGatewayResourceName := "aws_dx_gateway.test" rName := fmt.Sprintf("tf-testacc-private-vif-%s", sdkacctest.RandString(9)) @@ -248,17 +228,17 @@ func TestAccDirectConnectPrivateVirtualInterface_siteLink(t *testing.T) { CheckDestroy: testAccCheckPrivateVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPrivateVirtualInterfaceConfig_siteLinkBasic(connectionId, rName, amzAsn, bgpAsn, vlan, true), + Config: testAccPrivateVirtualInterfaceConfig_siteLinkBasic(connectionID, rName, amzAsn, bgpAsn, vlan, true), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -270,17 +250,17 @@ func TestAccDirectConnectPrivateVirtualInterface_siteLink(t *testing.T) { ), }, { - Config: testAccPrivateVirtualInterfaceConfig_siteLinkUpdated(connectionId, rName, amzAsn, bgpAsn, vlan, false), + Config: testAccPrivateVirtualInterfaceConfig_siteLinkUpdated(connectionID, rName, amzAsn, bgpAsn, vlan, false), Check: resource.ComposeTestCheckFunc( testAccCheckPrivateVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -291,7 +271,6 @@ func TestAccDirectConnectPrivateVirtualInterface_siteLink(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "sitelink_enabled", acctest.CtFalse), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -307,7 +286,7 @@ func testAccCheckPrivateVirtualInterfaceDestroy(ctx context.Context) resource.Te } } -func testAccCheckPrivateVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { +func testAccCheckPrivateVirtualInterfaceExists(ctx context.Context, name string, vif *awstypes.VirtualInterface) resource.TestCheckFunc { return testAccCheckVirtualInterfaceExists(ctx, name, vif) } @@ -321,7 +300,7 @@ resource "aws_vpn_gateway" "test" { } func testAccPrivateVirtualInterfaceConfig_basic(cid, rName string, bgpAsn, vlan int) string { - return testAccPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccPrivateVirtualInterfaceConfig_vpnGateway(rName), fmt.Sprintf(` resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -330,11 +309,11 @@ resource "aws_dx_private_virtual_interface" "test" { vlan = %[4]d vpn_gateway_id = aws_vpn_gateway.test.id } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccPrivateVirtualInterfaceConfig_updated(cid, rName string, bgpAsn, vlan int) string { - return testAccPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccPrivateVirtualInterfaceConfig_vpnGateway(rName), fmt.Sprintf(` resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -344,11 +323,11 @@ resource "aws_dx_private_virtual_interface" "test" { vlan = %[4]d vpn_gateway_id = aws_vpn_gateway.test.id } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccPrivateVirtualInterfaceConfig_tags(cid, rName string, bgpAsn, vlan int) string { - return testAccPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccPrivateVirtualInterfaceConfig_vpnGateway(rName), fmt.Sprintf(` resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -363,11 +342,11 @@ resource "aws_dx_private_virtual_interface" "test" { Key2 = "Value2a" } } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccPrivateVirtualInterfaceConfig_tagsUpdated(cid, rName string, bgpAsn, vlan int) string { - return testAccPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccPrivateVirtualInterfaceConfig_vpnGateway(rName), fmt.Sprintf(` resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -382,7 +361,7 @@ resource "aws_dx_private_virtual_interface" "test" { Key3 = "Value3" } } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccPrivateVirtualInterfaceConfig_gateway(cid, rName string, amzAsn, bgpAsn, vlan int) string { diff --git a/internal/service/directconnect/public_virtual_interface.go b/internal/service/directconnect/public_virtual_interface.go index f21663390f7..e1b30e3a385 100644 --- a/internal/service/directconnect/public_virtual_interface.go +++ b/internal/service/directconnect/public_virtual_interface.go @@ -10,31 +10,36 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_dx_public_virtual_interface", name="Public Virtual Interface") // @Tags(identifierAttribute="arn") -func ResourcePublicVirtualInterface() *schema.Resource { +func resourcePublicVirtualInterface() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourcePublicVirtualInterfaceCreate, ReadWithoutTimeout: resourcePublicVirtualInterfaceRead, UpdateWithoutTimeout: resourcePublicVirtualInterfaceUpdate, DeleteWithoutTimeout: resourcePublicVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ StateContext: resourcePublicVirtualInterfaceImport, }, + CustomizeDiff: customdiff.Sequence( resourcePublicVirtualInterfaceCustomizeDiff, verify.SetTagsDiff, @@ -42,13 +47,10 @@ func ResourcePublicVirtualInterface() *schema.Resource { Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - directconnect.AddressFamilyIpv4, - directconnect.AddressFamilyIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, "amazon_address": { Type: schema.TypeString, @@ -121,41 +123,45 @@ func ResourcePublicVirtualInterface() *schema.Resource { func resourcePublicVirtualInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - req := &directconnect.CreatePublicVirtualInterfaceInput{ + input := &directconnect.CreatePublicVirtualInterfaceInput{ ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - NewPublicVirtualInterface: &directconnect.NewPublicVirtualInterface{ - AddressFamily: aws.String(d.Get("address_family").(string)), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), + NewPublicVirtualInterface: &awstypes.NewPublicVirtualInterface{ + AddressFamily: awstypes.AddressFamily(d.Get("address_family").(string)), + Asn: int32(d.Get("bgp_asn").(int)), Tags: getTagsIn(ctx), VirtualInterfaceName: aws.String(d.Get(names.AttrName).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), }, } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewPublicVirtualInterface.AmazonAddress = aws.String(v.(string)) + input.NewPublicVirtualInterface.AmazonAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewPublicVirtualInterface.AuthKey = aws.String(v.(string)) + input.NewPublicVirtualInterface.AuthKey = aws.String(v.(string)) } + if v, ok := d.GetOk("customer_address"); ok { - req.NewPublicVirtualInterface.CustomerAddress = aws.String(v.(string)) + input.NewPublicVirtualInterface.CustomerAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("route_filter_prefixes"); ok { - req.NewPublicVirtualInterface.RouteFilterPrefixes = expandRouteFilterPrefixes(v.(*schema.Set).List()) + input.NewPublicVirtualInterface.RouteFilterPrefixes = expandRouteFilterPrefixes(v.(*schema.Set).List()) } - log.Printf("[DEBUG] Creating Direct Connect public virtual interface: %s", req) - resp, err := conn.CreatePublicVirtualInterfaceWithContext(ctx, req) + output, err := conn.CreatePublicVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Direct Connect public virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect Public Virtual Interface: %s", err) } - d.SetId(aws.StringValue(resp.VirtualInterfaceId)) + d.SetId(aws.ToString(output.VirtualInterfaceId)) - if err := publicVirtualInterfaceWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitPublicVirtualInterfaceAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Public Virtual Interface (%s) create: %s", d.Id(), err) } return append(diags, resourcePublicVirtualInterfaceRead(ctx, d, meta)...) @@ -163,21 +169,23 @@ func resourcePublicVirtualInterfaceCreate(ctx context.Context, d *schema.Resourc func resourcePublicVirtualInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Public Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Public Virtual Interface (%s): %s", d.Id(), err) + } + d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(vif.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", strconv.FormatInt(aws.ToInt64(vif.AmazonSideAsn), 10)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -216,17 +224,15 @@ func resourcePublicVirtualInterfaceDelete(ctx context.Context, d *schema.Resourc } func resourcePublicVirtualInterfaceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "public" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "public" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } @@ -236,7 +242,7 @@ func resourcePublicVirtualInterfaceImport(ctx context.Context, d *schema.Resourc func resourcePublicVirtualInterfaceCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { if diff.Id() == "" { // New resource. - if addressFamily := diff.Get("address_family").(string); addressFamily == directconnect.AddressFamilyIpv4 { + if addressFamily := diff.Get("address_family").(string); addressFamily == string(awstypes.AddressFamilyIPv4) { if _, ok := diff.GetOk("customer_address"); !ok { return fmt.Errorf("'customer_address' must be set when 'address_family' is '%s'", addressFamily) } @@ -249,16 +255,13 @@ func resourcePublicVirtualInterfaceCustomizeDiff(_ context.Context, diff *schema return nil } -func publicVirtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitPublicVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateDown, awstypes.VirtualInterfaceStateVerifying), timeout, - []string{ - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateDown, - directconnect.VirtualInterfaceStateVerifying, - }) + ) } diff --git a/internal/service/directconnect/public_virtual_interface_test.go b/internal/service/directconnect/public_virtual_interface_test.go index c45e4c9d235..f9539cbcb4c 100644 --- a/internal/service/directconnect/public_virtual_interface_test.go +++ b/internal/service/directconnect/public_virtual_interface_test.go @@ -6,13 +6,12 @@ package directconnect_test import ( "context" "fmt" - "os" "strconv" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,13 +21,9 @@ import ( func TestAccDirectConnectPublicVirtualInterface_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_public_virtual_interface.test" rName := fmt.Sprintf("tf-testacc-public-vif-%s", sdkacctest.RandString(10)) // DirectConnectClientException: Amazon Address is not allowed to contain a private IP @@ -47,17 +42,17 @@ func TestAccDirectConnectPublicVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckPublicVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPublicVirtualInterfaceConfig_basic(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccPublicVirtualInterfaceConfig_basic(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPublicVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", acctest.Ct2), @@ -67,7 +62,6 @@ func TestAccDirectConnectPublicVirtualInterface_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -79,13 +73,9 @@ func TestAccDirectConnectPublicVirtualInterface_basic(t *testing.T) { func TestAccDirectConnectPublicVirtualInterface_tags(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_public_virtual_interface.test" rName := fmt.Sprintf("tf-testacc-public-vif-%s", sdkacctest.RandString(10)) amazonAddress := "175.45.176.3/28" @@ -100,17 +90,17 @@ func TestAccDirectConnectPublicVirtualInterface_tags(t *testing.T) { CheckDestroy: testAccCheckPublicVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPublicVirtualInterfaceConfig_tags(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccPublicVirtualInterfaceConfig_tags(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPublicVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", acctest.Ct2), @@ -124,17 +114,17 @@ func TestAccDirectConnectPublicVirtualInterface_tags(t *testing.T) { ), }, { - Config: testAccPublicVirtualInterfaceConfig_tagsUpdated(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Config: testAccPublicVirtualInterfaceConfig_tagsUpdated(connectionID, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckPublicVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", acctest.Ct2), @@ -147,7 +137,6 @@ func TestAccDirectConnectPublicVirtualInterface_tags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -163,7 +152,7 @@ func testAccCheckPublicVirtualInterfaceDestroy(ctx context.Context) resource.Tes } } -func testAccCheckPublicVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { +func testAccCheckPublicVirtualInterfaceExists(ctx context.Context, name string, vif *awstypes.VirtualInterface) resource.TestCheckFunc { return testAccCheckVirtualInterfaceExists(ctx, name, vif) } diff --git a/internal/service/directconnect/router_configuration_data_source.go b/internal/service/directconnect/router_configuration_data_source.go index 4a776ae330d..647fd32cb02 100644 --- a/internal/service/directconnect/router_configuration_data_source.go +++ b/internal/service/directconnect/router_configuration_data_source.go @@ -7,18 +7,18 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "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/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_dx_router_configuration") -func DataSourceRouterConfiguration() *schema.Resource { +// @SDKDataSource("aws_dx_router_configuration", name="Router Configuration") +func dataSourceRouterConfiguration() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceRouterConfigurationRead, @@ -76,81 +76,75 @@ func DataSourceRouterConfiguration() *schema.Resource { } } -const ( - DSNameRouterConfiguration = "Router Configuration Data Source" -) - func dataSourceRouterConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) routerTypeIdentifier := d.Get("router_type_identifier").(string) - virtualInterfaceId := d.Get("virtual_interface_id").(string) + vifID := d.Get("virtual_interface_id").(string) + id := fmt.Sprintf("%s:%s", vifID, routerTypeIdentifier) + output, err := findRouterConfigurationByTwoPartKey(ctx, conn, routerTypeIdentifier, vifID) - out, err := findRouterConfigurationByTypeAndVif(ctx, conn, routerTypeIdentifier, virtualInterfaceId) if err != nil { - return create.AppendDiagError(diags, names.DirectConnect, create.ErrActionReading, DSNameRouterConfiguration, virtualInterfaceId, err) + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Router Configuration (%s): %s", id, err) } - d.SetId(fmt.Sprintf("%s:%s", virtualInterfaceId, routerTypeIdentifier)) - - d.Set("customer_router_config", out.CustomerRouterConfig) - d.Set("router_type_identifier", out.Router.RouterTypeIdentifier) - d.Set("virtual_interface_id", out.VirtualInterfaceId) - d.Set("virtual_interface_name", out.VirtualInterfaceName) - - if err := d.Set("router", flattenRouter(out.Router)); err != nil { - return create.AppendDiagError(diags, names.DirectConnect, create.ErrActionSetting, DSNameRouterConfiguration, d.Id(), err) + d.SetId(id) + d.Set("customer_router_config", output.CustomerRouterConfig) + if err := d.Set("router", flattenRouter(output.Router)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting router: %s", err) } + d.Set("router_type_identifier", output.Router.RouterTypeIdentifier) + d.Set("virtual_interface_id", output.VirtualInterfaceId) + d.Set("virtual_interface_name", output.VirtualInterfaceName) return diags } -func findRouterConfigurationByTypeAndVif(ctx context.Context, conn *directconnect.DirectConnect, routerTypeIdentifier string, virtualInterfaceId string) (*directconnect.DescribeRouterConfigurationOutput, error) { +func findRouterConfigurationByTwoPartKey(ctx context.Context, conn *directconnect.Client, routerTypeIdentifier, vifID string) (*directconnect.DescribeRouterConfigurationOutput, error) { input := &directconnect.DescribeRouterConfigurationInput{ RouterTypeIdentifier: aws.String(routerTypeIdentifier), - VirtualInterfaceId: aws.String(virtualInterfaceId), + VirtualInterfaceId: aws.String(vifID), } - output, err := conn.DescribeRouterConfigurationWithContext(ctx, input) + output, err := conn.DescribeRouterConfiguration(ctx, input) if err != nil { return nil, err } - if output == nil { + if output == nil || output.Router == nil { return nil, tfresource.NewEmptyResultError(input) } return output, nil } -func flattenRouter(apiObject *directconnect.RouterType) []interface{} { +func flattenRouter(apiObject *awstypes.RouterType) []interface{} { tfMap := map[string]interface{}{} if v := apiObject.Platform; v != nil { - tfMap["platform"] = aws.StringValue(v) + tfMap["platform"] = aws.ToString(v) } if v := apiObject.RouterTypeIdentifier; v != nil { - tfMap["router_type_identifier"] = aws.StringValue(v) + tfMap["router_type_identifier"] = aws.ToString(v) } if v := apiObject.Software; v != nil { - tfMap["software"] = aws.StringValue(v) + tfMap["software"] = aws.ToString(v) } if v := apiObject.Vendor; v != nil { - tfMap["vendor"] = aws.StringValue(v) + tfMap["vendor"] = aws.ToString(v) } if v := apiObject.XsltTemplateName; v != nil { - tfMap["xslt_template_name"] = aws.StringValue(v) + tfMap["xslt_template_name"] = aws.ToString(v) } if v := apiObject.XsltTemplateNameForMacSec; v != nil { - tfMap["xslt_template_name_for_mac_sec"] = aws.StringValue(v) + tfMap["xslt_template_name_for_mac_sec"] = aws.ToString(v) } return []interface{}{tfMap} diff --git a/internal/service/directconnect/router_configuration_data_source_test.go b/internal/service/directconnect/router_configuration_data_source_test.go index e7294b5b549..12211bf6952 100644 --- a/internal/service/directconnect/router_configuration_data_source_test.go +++ b/internal/service/directconnect/router_configuration_data_source_test.go @@ -5,10 +5,8 @@ package directconnect_test import ( "fmt" - "os" "testing" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/names" @@ -16,11 +14,7 @@ import ( func TestAccDirectConnectRouterConfigurationDataSource_basic(t *testing.T) { ctx := acctest.Context(t) - key := "VIRTUAL_INTERFACE_ID" - virtualInterfaceId := os.Getenv(key) - if virtualInterfaceId == "" { - t.Skipf("Environment variable %s is not set", key) - } + vifID := acctest.SkipIfEnvVarNotSet(t, "VIRTUAL_INTERFACE_ID") dataSourceName := "data.aws_dx_router_configuration.test" routerTypeIdentifier := "CiscoSystemsInc-2900SeriesRouters-IOS124" @@ -28,15 +22,15 @@ func TestAccDirectConnectRouterConfigurationDataSource_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, directconnect.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.DirectConnectEndpointID) }, ErrorCheck: acctest.ErrorCheck(t, names.DirectConnectServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccRouterConfigurationDataSourceConfig_basic(virtualInterfaceId, routerTypeIdentifier), + Config: testAccRouterConfigurationDataSourceConfig_basic(vifID, routerTypeIdentifier), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "virtual_interface_id", virtualInterfaceId), + resource.TestCheckResourceAttr(dataSourceName, "virtual_interface_id", vifID), resource.TestCheckResourceAttr(dataSourceName, "router_type_identifier", routerTypeIdentifier), resource.TestCheckResourceAttrSet(dataSourceName, "virtual_interface_name"), resource.TestCheckResourceAttr(dataSourceName, "router.0.platform", "2900 Series Routers"), diff --git a/internal/service/directconnect/service_endpoint_resolver_gen.go b/internal/service/directconnect/service_endpoint_resolver_gen.go index 8445f971931..b7f99a26cf3 100644 --- a/internal/service/directconnect/service_endpoint_resolver_gen.go +++ b/internal/service/directconnect/service_endpoint_resolver_gen.go @@ -6,65 +6,63 @@ import ( "context" "fmt" "net" - "net/url" - endpoints_sdkv1 "github.com/aws/aws-sdk-go/aws/endpoints" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + directconnect_sdkv2 "github.com/aws/aws-sdk-go-v2/service/directconnect" + smithyendpoints "github.com/aws/smithy-go/endpoints" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/errs" ) -var _ endpoints_sdkv1.Resolver = resolverSDKv1{} +var _ directconnect_sdkv2.EndpointResolverV2 = resolverSDKv2{} -type resolverSDKv1 struct { - ctx context.Context +type resolverSDKv2 struct { + defaultResolver directconnect_sdkv2.EndpointResolverV2 } -func newEndpointResolverSDKv1(ctx context.Context) resolverSDKv1 { - return resolverSDKv1{ - ctx: ctx, +func newEndpointResolverSDKv2() resolverSDKv2 { + return resolverSDKv2{ + defaultResolver: directconnect_sdkv2.NewDefaultEndpointResolverV2(), } } -func (r resolverSDKv1) EndpointFor(service, region string, opts ...func(*endpoints_sdkv1.Options)) (endpoint endpoints_sdkv1.ResolvedEndpoint, err error) { - ctx := r.ctx +func (r resolverSDKv2) ResolveEndpoint(ctx context.Context, params directconnect_sdkv2.EndpointParameters) (endpoint smithyendpoints.Endpoint, err error) { + params = params.WithDefaults() + useFIPS := aws_sdkv2.ToBool(params.UseFIPS) - var opt endpoints_sdkv1.Options - opt.Set(opts...) - - useFIPS := opt.UseFIPSEndpoint == endpoints_sdkv1.FIPSEndpointStateEnabled + if eps := params.Endpoint; aws_sdkv2.ToString(eps) != "" { + tflog.Debug(ctx, "setting endpoint", map[string]any{ + "tf_aws.endpoint": endpoint, + }) - defaultResolver := endpoints_sdkv1.DefaultResolver() + if useFIPS { + tflog.Debug(ctx, "endpoint set, ignoring UseFIPSEndpoint setting") + params.UseFIPS = aws_sdkv2.Bool(false) + } - if useFIPS { + return r.defaultResolver.ResolveEndpoint(ctx, params) + } else if useFIPS { ctx = tflog.SetField(ctx, "tf_aws.use_fips", useFIPS) - endpoint, err = defaultResolver.EndpointFor(service, region, opts...) + endpoint, err = r.defaultResolver.ResolveEndpoint(ctx, params) if err != nil { return endpoint, err } tflog.Debug(ctx, "endpoint resolved", map[string]any{ - "tf_aws.endpoint": endpoint.URL, + "tf_aws.endpoint": endpoint.URI.String(), }) - var endpointURL *url.URL - endpointURL, err = url.Parse(endpoint.URL) - if err != nil { - return endpoint, err - } - - hostname := endpointURL.Hostname() + hostname := endpoint.URI.Hostname() _, err = net.LookupHost(hostname) if err != nil { if dnsErr, ok := errs.As[*net.DNSError](err); ok && dnsErr.IsNotFound { tflog.Debug(ctx, "default endpoint host not found, disabling FIPS", map[string]any{ "tf_aws.hostname": hostname, }) - opts = append(opts, func(o *endpoints_sdkv1.Options) { - o.UseFIPSEndpoint = endpoints_sdkv1.FIPSEndpointStateDisabled - }) + params.UseFIPS = aws_sdkv2.Bool(false) } else { - err = fmt.Errorf("looking up accessanalyzer endpoint %q: %s", hostname, err) + err = fmt.Errorf("looking up directconnect endpoint %q: %s", hostname, err) return } } else { @@ -72,5 +70,13 @@ func (r resolverSDKv1) EndpointFor(service, region string, opts ...func(*endpoin } } - return defaultResolver.EndpointFor(service, region, opts...) + return r.defaultResolver.ResolveEndpoint(ctx, params) +} + +func withBaseEndpoint(endpoint string) func(*directconnect_sdkv2.Options) { + return func(o *directconnect_sdkv2.Options) { + if endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + } } diff --git a/internal/service/directconnect/service_endpoints_gen_test.go b/internal/service/directconnect/service_endpoints_gen_test.go index 910a8e7822f..80bf5ee2038 100644 --- a/internal/service/directconnect/service_endpoints_gen_test.go +++ b/internal/service/directconnect/service_endpoints_gen_test.go @@ -4,18 +4,22 @@ package directconnect_test import ( "context" + "errors" "fmt" "maps" "net" "net/url" "os" "path/filepath" + "reflect" "strings" "testing" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/endpoints" - directconnect_sdkv1 "github.com/aws/aws-sdk-go/service/directconnect" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + directconnect_sdkv2 "github.com/aws/aws-sdk-go-v2/service/directconnect" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" "github.com/google/go-cmp/cmp" "github.com/hashicorp/aws-sdk-go-base/v2/servicemocks" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -240,54 +244,63 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S } func defaultEndpoint(region string) (url.URL, error) { - r := endpoints.DefaultResolver() + r := directconnect_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(directconnect_sdkv1.EndpointsID, region) + ep, err := r.ResolveEndpoint(context.Background(), directconnect_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + }) if err != nil { return url.URL{}, err } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return *url, nil + return ep.URI, nil } func defaultFIPSEndpoint(region string) (url.URL, error) { - r := endpoints.DefaultResolver() + r := directconnect_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(directconnect_sdkv1.EndpointsID, region, func(opt *endpoints.Options) { - opt.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled + ep, err := r.ResolveEndpoint(context.Background(), directconnect_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + UseFIPS: aws_sdkv2.Bool(true), }) if err != nil { return url.URL{}, err } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return *url, nil + return ep.URI, nil } func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { t.Helper() - client := meta.DirectConnectConn(ctx) + client := meta.DirectConnectClient(ctx) - req, _ := client.DescribeConnectionsRequest(&directconnect_sdkv1.DescribeConnectionsInput{}) + var result apiCallParams - req.HTTPRequest.URL.Path = "/" - - return apiCallParams{ - endpoint: req.HTTPRequest.URL.String(), - region: aws_sdkv1.StringValue(client.Config.Region), + _, err := client.DescribeConnections(ctx, &directconnect_sdkv2.DescribeConnectionsInput{}, + func(opts *directconnect_sdkv2.Options) { + opts.APIOptions = append(opts.APIOptions, + addRetrieveEndpointURLMiddleware(t, &result.endpoint), + addRetrieveRegionMiddleware(&result.region), + addCancelRequestMiddleware(), + ) + }, + ) + if err == nil { + t.Fatal("Expected an error, got none") + } else if !errors.Is(err, errCancelOperation) { + t.Fatalf("Unexpected error: %s", err) } + + return result } func withNoConfig(_ *caseSetup) { @@ -466,6 +479,89 @@ func testEndpointCase(t *testing.T, region string, testcase endpointTestCase, ca } } +func addRetrieveEndpointURLMiddleware(t *testing.T, endpoint *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + retrieveEndpointURLMiddleware(t, endpoint), + middleware.After, + ) + } +} + +func retrieveEndpointURLMiddleware(t *testing.T, endpoint *string) middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Retrieve Endpoint", + func(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + t.Helper() + + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + t.Fatalf("Expected *github.com/aws/smithy-go/transport/http.Request, got %s", fullTypeName(in.Request)) + } + + url := request.URL + url.RawQuery = "" + url.Path = "/" + + *endpoint = url.String() + + return next.HandleFinalize(ctx, in) + }) +} + +func addRetrieveRegionMiddleware(region *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Serialize.Add( + retrieveRegionMiddleware(region), + middleware.After, + ) + } +} + +func retrieveRegionMiddleware(region *string) middleware.SerializeMiddleware { + return middleware.SerializeMiddlewareFunc( + "Test: Retrieve Region", + func(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (middleware.SerializeOutput, middleware.Metadata, error) { + *region = awsmiddleware.GetRegion(ctx) + + return next.HandleSerialize(ctx, in) + }, + ) +} + +var errCancelOperation = fmt.Errorf("Test: Canceling request") + +func addCancelRequestMiddleware() func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + cancelRequestMiddleware(), + middleware.After, + ) + } +} + +// cancelRequestMiddleware creates a Smithy middleware that intercepts the request before sending and cancels it +func cancelRequestMiddleware() middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Cancel Requests", + func(_ context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + return middleware.FinalizeOutput{}, middleware.Metadata{}, errCancelOperation + }) +} + +func fullTypeName(i interface{}) string { + return fullValueTypeName(reflect.ValueOf(i)) +} + +func fullValueTypeName(v reflect.Value) string { + if v.Kind() == reflect.Ptr { + return "*" + fullValueTypeName(reflect.Indirect(v)) + } + + requestType := v.Type() + return fmt.Sprintf("%s.%s", requestType.PkgPath(), requestType.Name()) +} + func generateSharedConfigFile(config configFile) string { var buf strings.Builder diff --git a/internal/service/directconnect/service_package_gen.go b/internal/service/directconnect/service_package_gen.go index aa7c0014dfc..c8565890e3f 100644 --- a/internal/service/directconnect/service_package_gen.go +++ b/internal/service/directconnect/service_package_gen.go @@ -5,10 +5,8 @@ package directconnect import ( "context" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - directconnect_sdkv1 "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/terraform-plugin-log/tflog" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + directconnect_sdkv2 "github.com/aws/aws-sdk-go-v2/service/directconnect" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" @@ -27,24 +25,29 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { return []*types.ServicePackageSDKDataSource{ { - Factory: DataSourceConnection, + Factory: dataSourceConnection, TypeName: "aws_dx_connection", + Name: "Connection", }, { - Factory: DataSourceGateway, + Factory: dataSourceGateway, TypeName: "aws_dx_gateway", + Name: "Gateway", }, { - Factory: DataSourceLocation, + Factory: dataSourceLocation, TypeName: "aws_dx_location", + Name: "Location", }, { - Factory: DataSourceLocations, + Factory: dataSourceLocations, TypeName: "aws_dx_locations", + Name: "Locations", }, { - Factory: DataSourceRouterConfiguration, + Factory: dataSourceRouterConfiguration, TypeName: "aws_dx_router_configuration", + Name: "Router Configuration", }, } } @@ -52,11 +55,12 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ { - Factory: ResourceBGPPeer, + Factory: resourceBGPPeer, TypeName: "aws_dx_bgp_peer", + Name: "BGP Peer", }, { - Factory: ResourceConnection, + Factory: resourceConnection, TypeName: "aws_dx_connection", Name: "Connection", Tags: &types.ServicePackageResourceTags{ @@ -64,67 +68,76 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceConnectionAssociation, + Factory: resourceConnectionAssociation, TypeName: "aws_dx_connection_association", + Name: "Connection LAG Association", }, { - Factory: ResourceConnectionConfirmation, + Factory: resourceConnectionConfirmation, TypeName: "aws_dx_connection_confirmation", + Name: "Connection Confirmation", }, { - Factory: ResourceGateway, + Factory: resourceGateway, TypeName: "aws_dx_gateway", + Name: "Gateway", }, { - Factory: ResourceGatewayAssociation, + Factory: resourceGatewayAssociation, TypeName: "aws_dx_gateway_association", + Name: "Gateway Association", }, { - Factory: ResourceGatewayAssociationProposal, + Factory: resourceGatewayAssociationProposal, TypeName: "aws_dx_gateway_association_proposal", + Name: "Gateway Association Proposal", }, { - Factory: ResourceHostedConnection, + Factory: resourceHostedConnection, TypeName: "aws_dx_hosted_connection", + Name: "Hosted Connection", }, { - Factory: ResourceHostedPrivateVirtualInterface, + Factory: resourceHostedPrivateVirtualInterface, TypeName: "aws_dx_hosted_private_virtual_interface", + Name: "Hosted Private Virtual Interface", }, { - Factory: ResourceHostedPrivateVirtualInterfaceAccepter, + Factory: resourceHostedPrivateVirtualInterfaceAccepter, TypeName: "aws_dx_hosted_private_virtual_interface_accepter", - Name: "Hosted Private Virtual Interface", + Name: "Hosted Private Virtual Interface Accepter", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, { - Factory: ResourceHostedPublicVirtualInterface, + Factory: resourceHostedPublicVirtualInterface, TypeName: "aws_dx_hosted_public_virtual_interface", + Name: "Hosted Public Virtual Interface", }, { - Factory: ResourceHostedPublicVirtualInterfaceAccepter, + Factory: resourceHostedPublicVirtualInterfaceAccepter, TypeName: "aws_dx_hosted_public_virtual_interface_accepter", - Name: "Hosted Public Virtual Interface", + Name: "Hosted Public Virtual Interface Accepter", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, { - Factory: ResourceHostedTransitVirtualInterface, + Factory: resourceHostedTransitVirtualInterface, TypeName: "aws_dx_hosted_transit_virtual_interface", + Name: "Hosted Transit Virtual Interface", }, { - Factory: ResourceHostedTransitVirtualInterfaceAccepter, + Factory: resourceHostedTransitVirtualInterfaceAccepter, TypeName: "aws_dx_hosted_transit_virtual_interface_accepter", - Name: "Hosted Transit Virtual Interface", + Name: "Hosted Transit Virtual Interface Accepter", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, { - Factory: ResourceLag, + Factory: resourceLag, TypeName: "aws_dx_lag", Name: "LAG", Tags: &types.ServicePackageResourceTags{ @@ -132,11 +145,12 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceMacSecKeyAssociation, + Factory: resourceMacSecKeyAssociation, TypeName: "aws_dx_macsec_key_association", + Name: "MACSec Key Association", }, { - Factory: ResourcePrivateVirtualInterface, + Factory: resourcePrivateVirtualInterface, TypeName: "aws_dx_private_virtual_interface", Name: "Private Virtual Interface", Tags: &types.ServicePackageResourceTags{ @@ -144,7 +158,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourcePublicVirtualInterface, + Factory: resourcePublicVirtualInterface, TypeName: "aws_dx_public_virtual_interface", Name: "Public Virtual Interface", Tags: &types.ServicePackageResourceTags{ @@ -152,7 +166,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceTransitVirtualInterface, + Factory: resourceTransitVirtualInterface, TypeName: "aws_dx_transit_virtual_interface", Name: "Transit Virtual Interface", Tags: &types.ServicePackageResourceTags{ @@ -166,22 +180,14 @@ func (p *servicePackage) ServicePackageName() string { return names.DirectConnect } -// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*directconnect_sdkv1.DirectConnect, error) { - sess := config[names.AttrSession].(*session_sdkv1.Session) - - cfg := aws_sdkv1.Config{} - - if endpoint := config[names.AttrEndpoint].(string); endpoint != "" { - tflog.Debug(ctx, "setting endpoint", map[string]any{ - "tf_aws.endpoint": endpoint, - }) - cfg.Endpoint = aws_sdkv1.String(endpoint) - } else { - cfg.EndpointResolver = newEndpointResolverSDKv1(ctx) - } +// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API. +func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*directconnect_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) - return directconnect_sdkv1.New(sess.Copy(&cfg)), nil + return directconnect_sdkv2.NewFromConfig(cfg, + directconnect_sdkv2.WithEndpointResolverV2(newEndpointResolverSDKv2()), + withBaseEndpoint(config[names.AttrEndpoint].(string)), + ), nil } func ServicePackage(ctx context.Context) conns.ServicePackage { diff --git a/internal/service/directconnect/status.go b/internal/service/directconnect/status.go deleted file mode 100644 index 45c3e9b06f4..00000000000 --- a/internal/service/directconnect/status.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect - -import ( - "context" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" -) - -func statusConnectionState(ctx context.Context, conn *directconnect.DirectConnect, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindConnectionByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.ConnectionState), nil - } -} - -func statusGatewayState(ctx context.Context, conn *directconnect.DirectConnect, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindGatewayByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.DirectConnectGatewayState), nil - } -} - -func statusGatewayAssociationState(ctx context.Context, conn *directconnect.DirectConnect, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindGatewayAssociationByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.AssociationState), nil - } -} - -func statusHostedConnectionState(ctx context.Context, conn *directconnect.DirectConnect, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindHostedConnectionByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.ConnectionState), nil - } -} - -func statusLagState(ctx context.Context, conn *directconnect.DirectConnect, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindLagByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.LagState), nil - } -} diff --git a/internal/service/directconnect/sweep.go b/internal/service/directconnect/sweep.go index 984968c62b3..05e8540561a 100644 --- a/internal/service/directconnect/sweep.go +++ b/internal/service/directconnect/sweep.go @@ -7,15 +7,15 @@ import ( "fmt" "log" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" - "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" ) func RegisterSweepers() { @@ -64,53 +64,36 @@ func sweepConnections(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - - conn := client.DirectConnectConn(ctx) - - sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &directconnect.DescribeConnectionsInput{} + conn := client.DirectConnectClient(ctx) + sweepResources := make([]sweep.Sweepable, 0) - // DescribeConnections has no pagination support - output, err := conn.DescribeConnectionsWithContext(ctx, input) + output, err := conn.DescribeConnections(ctx, input) - if awsv1.SkipSweepError(err) { + if awsv2.SkipSweepError(err) { log.Printf("[WARN] Skipping Direct Connect Connection sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() + return nil } if err != nil { - sweeperErr := fmt.Errorf("error listing Direct Connect Connections for %s: %w", region, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - return sweeperErrs.ErrorOrNil() + return fmt.Errorf("error listing Direct Connect Connections (%s): %w", region, err) } - if output == nil { - log.Printf("[WARN] Skipping Direct Connect Connection sweep for %s: empty response", region) - return sweeperErrs.ErrorOrNil() - } - - for _, connection := range output.Connections { - if connection == nil { - continue - } - - id := aws.StringValue(connection.ConnectionId) - - r := ResourceConnection() + for _, v := range output.Connections { + r := resourceConnection() d := r.Data(nil) - d.SetId(id) + d.SetId(aws.ToString(v.ConnectionId)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect Connection: %w", err)) + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping Direct Connect Connections (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepGatewayAssociationProposals(region string) error { @@ -119,31 +102,31 @@ func sweepGatewayAssociationProposals(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.DirectConnectConn(ctx) input := &directconnect.DescribeDirectConnectGatewayAssociationProposalsInput{} + conn := client.DirectConnectClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = describeGatewayAssociationProposalsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, lastPage bool) bool { + err = describeDirectConnectGatewayAssociationProposalsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationProposalsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, proposal := range page.DirectConnectGatewayAssociationProposals { - proposalID := aws.StringValue(proposal.ProposalId) + for _, v := range page.DirectConnectGatewayAssociationProposals { + id := aws.ToString(v.ProposalId) - if proposalRegion := aws.StringValue(proposal.AssociatedGateway.Region); proposalRegion != region { - log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal (%s) in different home region: %s", proposalID, proposalRegion) + if proposalRegion := aws.ToString(v.AssociatedGateway.Region); proposalRegion != region { + log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal %s: AssociatedGateway.Region=%s", id, proposalRegion) continue } - if state := aws.StringValue(proposal.ProposalState); state != directconnect.GatewayAssociationProposalStateAccepted { - log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal (%s) in non-accepted (%s) state", proposalID, state) + if state := v.ProposalState; state != awstypes.DirectConnectGatewayAssociationProposalStateAccepted { + log.Printf("[INFO] Skipping Direct Connect Gateway Association Proposal %s: ProposalState=%s", id, state) continue } - r := ResourceGatewayAssociationProposal() + r := resourceGatewayAssociationProposal() d := r.Data(nil) - d.SetId(proposalID) + d.SetId(id) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -151,8 +134,8 @@ func sweepGatewayAssociationProposals(region string) error { return !lastPage }) - if awsv1.SkipSweepError(err) { - log.Print(fmt.Errorf("[WARN] Skipping Direct Connect Gateway Association Proposal sweep for %s: %w", region, err)) + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Direct Connect Gateway Association Proposal sweep for %s: %s", region, err) return nil } @@ -175,45 +158,44 @@ func sweepGatewayAssociations(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.DirectConnectConn(ctx) input := &directconnect.DescribeDirectConnectGatewaysInput{} - var sweeperErrs *multierror.Error + conn := client.DirectConnectClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = describeGatewaysPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { + err = describeDirectConnectGatewaysPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, gateway := range page.DirectConnectGateways { - directConnectGatewayID := aws.StringValue(gateway.DirectConnectGatewayId) + for _, v := range page.DirectConnectGateways { + directConnectGatewayID := aws.ToString(v.DirectConnectGatewayId) input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ DirectConnectGatewayId: aws.String(directConnectGatewayID), } - err := describeGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + err := describeDirectConnectGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, association := range page.DirectConnectGatewayAssociations { - gatewayID := aws.StringValue(association.AssociatedGateway.Id) + for _, v := range page.DirectConnectGatewayAssociations { + gatewayID := aws.ToString(v.AssociatedGateway.Id) - if aws.StringValue(association.AssociatedGateway.Region) != region { - log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association (%s) in different home region: %s", directConnectGatewayID, gatewayID, aws.StringValue(association.AssociatedGateway.Region)) + if gatewayRegion := aws.ToString(v.AssociatedGateway.Region); gatewayRegion != region { + log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association (%s): AssociatedGateway.Region=%s", directConnectGatewayID, gatewayID, gatewayRegion) continue } - if state := aws.StringValue(association.AssociationState); state != directconnect.GatewayAssociationStateAssociated { - log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association in non-available (%s) state: %s", directConnectGatewayID, state, gatewayID) + if state := v.AssociationState; state != awstypes.DirectConnectGatewayAssociationStateAssociated { + log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association (%s): AssociationState=%s", directConnectGatewayID, gatewayID, state) continue } - r := ResourceGatewayAssociation() + r := resourceGatewayAssociation() d := r.Data(nil) - d.SetId(GatewayAssociationCreateResourceID(directConnectGatewayID, gatewayID)) - d.Set("dx_gateway_association_id", association.AssociationId) + d.SetId(gatewayAssociationCreateResourceID(directConnectGatewayID, gatewayID)) + d.Set("dx_gateway_association_id", v.AssociationId) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -222,20 +204,20 @@ func sweepGatewayAssociations(region string) error { }) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Associations (%s): %w", region, err)) + continue } } return !lastPage }) - if awsv1.SkipSweepError(err) { - log.Print(fmt.Errorf("[WARN] Skipping Direct Connect Gateway Association sweep for %s: %w", region, err)) - return sweeperErrs // In case we have completed some pages, but had errors + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Direct Connect Gateway Association sweep for %s: %s", region, err) + return nil } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateways (%s): %w", region, err)) + return fmt.Errorf("error listing Direct Connect Gateways (%s): %w", region, err) } // Handle cross-account EC2 Transit Gateway associations. @@ -250,37 +232,37 @@ func sweepGatewayAssociations(region string) error { page, err := pages.NextPage(ctx) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 Transit Gateways (%s): %w", region, err)) + return fmt.Errorf("error listing EC2 Transit Gateways (%s): %w", region, err) } - for _, transitGateway := range page.TransitGateways { - if transitGateway.State == ec2types.TransitGatewayStateDeleted { + for _, v := range page.TransitGateways { + if v.State == ec2types.TransitGatewayStateDeleted { continue } - transitGatewayID := aws.StringValue(transitGateway.TransitGatewayId) + transitGatewayID := aws.ToString(v.TransitGatewayId) input := &directconnect.DescribeDirectConnectGatewayAssociationsInput{ AssociatedGatewayId: aws.String(transitGatewayID), } - err := describeGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + err := describeDirectConnectGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, association := range page.DirectConnectGatewayAssociations { - directConnectGatewayID := aws.StringValue(association.DirectConnectGatewayId) + for _, v := range page.DirectConnectGatewayAssociations { + directConnectGatewayID := aws.ToString(v.DirectConnectGatewayId) - if state := aws.StringValue(association.AssociationState); state != directconnect.GatewayAssociationStateAssociated { - log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association in non-available (%s) state: %s", directConnectGatewayID, state, transitGatewayID) + if state := v.AssociationState; state != awstypes.DirectConnectGatewayAssociationStateAssociated { + log.Printf("[INFO] Skipping Direct Connect Gateway (%s) Association (%s): %s", directConnectGatewayID, transitGatewayID, state) continue } - r := ResourceGatewayAssociation() + r := resourceGatewayAssociation() d := r.Data(nil) - d.SetId(GatewayAssociationCreateResourceID(directConnectGatewayID, transitGatewayID)) - d.Set("dx_gateway_association_id", association.AssociationId) + d.SetId(gatewayAssociationCreateResourceID(directConnectGatewayID, transitGatewayID)) + d.Set("dx_gateway_association_id", v.AssociationId) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -289,7 +271,7 @@ func sweepGatewayAssociations(region string) error { }) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Associations (%s): %w", region, err)) + continue } } } @@ -297,10 +279,10 @@ func sweepGatewayAssociations(region string) error { err = sweep.SweepOrchestrator(ctx, sweepResources) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect Gateway Associations (%s): %w", region, err)) + return fmt.Errorf("error sweeping Direct Connect Gateway Associations (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepGateways(region string) error { @@ -309,21 +291,20 @@ func sweepGateways(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.DirectConnectConn(ctx) input := &directconnect.DescribeDirectConnectGatewaysInput{} - var sweeperErrs *multierror.Error + conn := client.DirectConnectClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = describeGatewaysPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { + err = describeDirectConnectGatewaysPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewaysOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, gateway := range page.DirectConnectGateways { - directConnectGatewayID := aws.StringValue(gateway.DirectConnectGatewayId) + for _, v := range page.DirectConnectGateways { + directConnectGatewayID := aws.ToString(v.DirectConnectGatewayId) - if state := aws.StringValue(gateway.DirectConnectGatewayState); state != directconnect.GatewayStateAvailable { - log.Printf("[INFO] Skipping Direct Connect Gateway in non-available (%s) state: %s", state, directConnectGatewayID) + if state := v.DirectConnectGatewayState; state != awstypes.DirectConnectGatewayStateAvailable { + log.Printf("[INFO] Skipping Direct Connect Gateway (%s): DirectConnectGatewayState=%s", directConnectGatewayID, state) continue } @@ -333,7 +314,7 @@ func sweepGateways(region string) error { var associations bool - err := describeGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { + err := describeDirectConnectGatewayAssociationsPages(ctx, conn, input, func(page *directconnect.DescribeDirectConnectGatewayAssociationsOutput, lastPage bool) bool { if page == nil { return !lastPage } @@ -352,7 +333,7 @@ func sweepGateways(region string) error { }) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateway Associations (%s): %w", region, err)) + continue } if associations { @@ -360,7 +341,7 @@ func sweepGateways(region string) error { continue } - r := ResourceGateway() + r := resourceGateway() d := r.Data(nil) d.SetId(directConnectGatewayID) @@ -370,22 +351,22 @@ func sweepGateways(region string) error { return !lastPage }) - if awsv1.SkipSweepError(err) { - log.Print(fmt.Errorf("[WARN] Skipping Direct Connect Gateway sweep for %s: %w", region, err)) - return sweeperErrs // In case we have completed some pages, but had errors + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Direct Connect Gateway sweep for %s: %s", region, err) + return nil } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Direct Connect Gateways (%s): %w", region, err)) + return fmt.Errorf("error listing Direct Connect Gateways (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect Gateways (%s): %w", region, err)) + return fmt.Errorf("error sweeping Direct Connect Gateways (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepLags(region string) error { @@ -394,53 +375,36 @@ func sweepLags(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - - conn := client.DirectConnectConn(ctx) - - sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &directconnect.DescribeLagsInput{} + conn := client.DirectConnectClient(ctx) + sweepResources := make([]sweep.Sweepable, 0) - // DescribeLags has no pagination support - output, err := conn.DescribeLagsWithContext(ctx, input) + output, err := conn.DescribeLags(ctx, input) - if awsv1.SkipSweepError(err) { + if awsv2.SkipSweepError(err) { log.Printf("[WARN] Skipping Direct Connect LAG sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() + return nil } if err != nil { - sweeperErr := fmt.Errorf("error listing Direct Connect LAGs for %s: %w", region, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - return sweeperErrs.ErrorOrNil() - } - - if output == nil { - log.Printf("[WARN] Skipping Direct Connect LAG sweep for %s: empty response", region) - return sweeperErrs.ErrorOrNil() + return fmt.Errorf("error listing Direct Connect LAGs for %s: %w", region, err) } - for _, lag := range output.Lags { - if lag == nil { - continue - } - - id := aws.StringValue(lag.LagId) - - r := ResourceLag() + for _, v := range output.Lags { + r := resourceLag() d := r.Data(nil) - d.SetId(id) + d.SetId(aws.ToString(v.LagId)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Direct Connect LAG: %w", err)) + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping Direct Connect LAGs (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepMacSecKeys(region string) error { @@ -449,60 +413,42 @@ func sweepMacSecKeys(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - - dxConn := client.DirectConnectConn(ctx) - + input := &directconnect.DescribeConnectionsInput{} + dxConn := client.DirectConnectClient(ctx) // Clean up leaked Secrets Manager resources created by Direct Connect. // Direct Connect does not remove the corresponding Secrets Manager // key when deleting the MACsec key association. The only option to // clean up the dangling resource is to use Secrets Manager to delete // the MACsec key secret. smConn := client.SecretsManagerClient(ctx) - dxInput := &directconnect.DescribeConnectionsInput{} - var sweeperErrs *multierror.Error - output, err := dxConn.DescribeConnectionsWithContext(ctx, dxInput) + output, err := dxConn.DescribeConnections(ctx, input) - if awsv1.SkipSweepError(err) { + if awsv2.SkipSweepError(err) { log.Printf("[WARN] Skipping Direct Connect MACsec Keys sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() + return nil } if err != nil { - sweeperErr := fmt.Errorf(" listing Direct Connect Connections for %s: %w", region, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - return sweeperErrs.ErrorOrNil() + return fmt.Errorf("error listing Direct Connect Connections (%s): %w", region, err) } - if output == nil { - log.Printf("[WARN] Skipping Direct Connect MACsec Keys sweep for %s: empty response", region) - return sweeperErrs.ErrorOrNil() - } - - for _, connection := range output.Connections { - if connection.MacSecKeys == nil { - continue - } - - for _, key := range connection.MacSecKeys { - arn := aws.StringValue(key.SecretARN) + for _, v := range output.Connections { + for _, v := range v.MacSecKeys { + arn := aws.ToString(v.SecretARN) input := &secretsmanager.DeleteSecretInput{ SecretId: aws.String(arn), } - log.Printf("[DEBUG] Deleting MACSec secret key: %s", *input.SecretId) + log.Printf("[DEBUG] Deleting MACSec secret key: %s", arn) _, err := smConn.DeleteSecret(ctx, input) if err != nil { - sweeperErr := fmt.Errorf("error deleting MACsec Secret (%s): %w", arn, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) continue } } } - return sweeperErrs.ErrorOrNil() + return nil } diff --git a/internal/service/directconnect/tags_gen.go b/internal/service/directconnect/tags_gen.go index 36eff3c345e..1ee0a5de99c 100644 --- a/internal/service/directconnect/tags_gen.go +++ b/internal/service/directconnect/tags_gen.go @@ -5,9 +5,9 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/aws/aws-sdk-go/service/directconnect/directconnectiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/logging" @@ -19,12 +19,12 @@ import ( // listTags lists directconnect service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func listTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, identifier string) (tftags.KeyValueTags, error) { +func listTags(ctx context.Context, conn *directconnect.Client, identifier string, optFns ...func(*directconnect.Options)) (tftags.KeyValueTags, error) { input := &directconnect.DescribeTagsInput{ - ResourceArns: aws.StringSlice([]string{identifier}), + ResourceArns: []string{identifier}, } - output, err := conn.DescribeTagsWithContext(ctx, input) + output, err := conn.DescribeTags(ctx, input, optFns...) if err != nil { return tftags.New(ctx, nil), err @@ -36,7 +36,7 @@ func listTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, ide // ListTags lists directconnect service tags and set them in Context. // It is called from outside this package. func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier string) error { - tags, err := listTags(ctx, meta.(*conns.AWSClient).DirectConnectConn(ctx), identifier) + tags, err := listTags(ctx, meta.(*conns.AWSClient).DirectConnectClient(ctx), identifier) if err != nil { return err @@ -52,11 +52,11 @@ func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier stri // []*SERVICE.Tag handling // Tags returns directconnect service tags. -func Tags(tags tftags.KeyValueTags) []*directconnect.Tag { - result := make([]*directconnect.Tag, 0, len(tags)) +func Tags(tags tftags.KeyValueTags) []awstypes.Tag { + result := make([]awstypes.Tag, 0, len(tags)) for k, v := range tags.Map() { - tag := &directconnect.Tag{ + tag := awstypes.Tag{ Key: aws.String(k), Value: aws.String(v), } @@ -68,11 +68,11 @@ func Tags(tags tftags.KeyValueTags) []*directconnect.Tag { } // KeyValueTags creates tftags.KeyValueTags from directconnect service tags. -func KeyValueTags(ctx context.Context, tags []*directconnect.Tag) tftags.KeyValueTags { +func KeyValueTags(ctx context.Context, tags []awstypes.Tag) tftags.KeyValueTags { m := make(map[string]*string, len(tags)) for _, tag := range tags { - m[aws.StringValue(tag.Key)] = tag.Value + m[aws.ToString(tag.Key)] = tag.Value } return tftags.New(ctx, m) @@ -80,7 +80,7 @@ func KeyValueTags(ctx context.Context, tags []*directconnect.Tag) tftags.KeyValu // getTagsIn returns directconnect service tags from Context. // nil is returned if there are no input tags. -func getTagsIn(ctx context.Context) []*directconnect.Tag { +func getTagsIn(ctx context.Context) []awstypes.Tag { if inContext, ok := tftags.FromContext(ctx); ok { if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { return tags @@ -91,25 +91,25 @@ func getTagsIn(ctx context.Context) []*directconnect.Tag { } // setTagsOut sets directconnect service tags in Context. -func setTagsOut(ctx context.Context, tags []*directconnect.Tag) { +func setTagsOut(ctx context.Context, tags []awstypes.Tag) { if inContext, ok := tftags.FromContext(ctx); ok { inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) } } // createTags creates directconnect service tags for new resources. -func createTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, identifier string, tags []*directconnect.Tag) error { +func createTags(ctx context.Context, conn *directconnect.Client, identifier string, tags []awstypes.Tag, optFns ...func(*directconnect.Options)) error { if len(tags) == 0 { return nil } - return updateTags(ctx, conn, identifier, nil, KeyValueTags(ctx, tags)) + return updateTags(ctx, conn, identifier, nil, KeyValueTags(ctx, tags), optFns...) } // updateTags updates directconnect service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func updateTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, identifier string, oldTagsMap, newTagsMap any) error { +func updateTags(ctx context.Context, conn *directconnect.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*directconnect.Options)) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) @@ -120,10 +120,10 @@ func updateTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, i if len(removedTags) > 0 { input := &directconnect.UntagResourceInput{ ResourceArn: aws.String(identifier), - TagKeys: aws.StringSlice(removedTags.Keys()), + TagKeys: removedTags.Keys(), } - _, err := conn.UntagResourceWithContext(ctx, input) + _, err := conn.UntagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("untagging resource (%s): %w", identifier, err) @@ -138,7 +138,7 @@ func updateTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, i Tags: Tags(updatedTags), } - _, err := conn.TagResourceWithContext(ctx, input) + _, err := conn.TagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("tagging resource (%s): %w", identifier, err) @@ -151,5 +151,5 @@ func updateTags(ctx context.Context, conn directconnectiface.DirectConnectAPI, i // UpdateTags updates directconnect service tags. // It is called from outside this package. func (p *servicePackage) UpdateTags(ctx context.Context, meta any, identifier string, oldTags, newTags any) error { - return updateTags(ctx, meta.(*conns.AWSClient).DirectConnectConn(ctx), identifier, oldTags, newTags) + return updateTags(ctx, meta.(*conns.AWSClient).DirectConnectClient(ctx), identifier, oldTags, newTags) } diff --git a/internal/service/directconnect/transit_virtual_interface.go b/internal/service/directconnect/transit_virtual_interface.go index ad70a3f29eb..c0821c3a9ce 100644 --- a/internal/service/directconnect/transit_virtual_interface.go +++ b/internal/service/directconnect/transit_virtual_interface.go @@ -10,40 +10,41 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" 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" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_dx_transit_virtual_interface", name="Transit Virtual Interface") // @Tags(identifierAttribute="arn") -func ResourceTransitVirtualInterface() *schema.Resource { +func resourceTransitVirtualInterface() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceTransitVirtualInterfaceCreate, ReadWithoutTimeout: resourceTransitVirtualInterfaceRead, UpdateWithoutTimeout: resourceTransitVirtualInterfaceUpdate, DeleteWithoutTimeout: resourceTransitVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ StateContext: resourceTransitVirtualInterfaceImport, }, Schema: map[string]*schema.Schema{ "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - directconnect.AddressFamilyIpv4, - directconnect.AddressFamilyIpv6, - }, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.AddressFamily](), }, "amazon_address": { Type: schema.TypeString, @@ -131,41 +132,44 @@ func ResourceTransitVirtualInterface() *schema.Resource { func resourceTransitVirtualInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - req := &directconnect.CreateTransitVirtualInterfaceInput{ + input := &directconnect.CreateTransitVirtualInterfaceInput{ ConnectionId: aws.String(d.Get(names.AttrConnectionID).(string)), - NewTransitVirtualInterface: &directconnect.NewTransitVirtualInterface{ - AddressFamily: aws.String(d.Get("address_family").(string)), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), + NewTransitVirtualInterface: &awstypes.NewTransitVirtualInterface{ + AddressFamily: awstypes.AddressFamily(d.Get("address_family").(string)), + Asn: int32(d.Get("bgp_asn").(int)), DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), EnableSiteLink: aws.Bool(d.Get("sitelink_enabled").(bool)), - Mtu: aws.Int64(int64(d.Get("mtu").(int))), + Mtu: aws.Int32(int32(d.Get("mtu").(int))), Tags: getTagsIn(ctx), VirtualInterfaceName: aws.String(d.Get(names.AttrName).(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Vlan: int32(d.Get("vlan").(int)), }, } + if v, ok := d.GetOk("amazon_address"); ok { - req.NewTransitVirtualInterface.AmazonAddress = aws.String(v.(string)) + input.NewTransitVirtualInterface.AmazonAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("bgp_auth_key"); ok { - req.NewTransitVirtualInterface.AuthKey = aws.String(v.(string)) + input.NewTransitVirtualInterface.AuthKey = aws.String(v.(string)) } + if v, ok := d.GetOk("customer_address"); ok { - req.NewTransitVirtualInterface.CustomerAddress = aws.String(v.(string)) + input.NewTransitVirtualInterface.CustomerAddress = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating Direct Connect transit virtual interface: %s", req) - resp, err := conn.CreateTransitVirtualInterfaceWithContext(ctx, req) + output, err := conn.CreateTransitVirtualInterface(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Direct Connect transit virtual interface: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Direct Connect Transit Virtual Interface: %s", err) } - d.SetId(aws.StringValue(resp.VirtualInterface.VirtualInterfaceId)) + d.SetId(aws.ToString(output.VirtualInterface.VirtualInterfaceId)) - if err := transitVirtualInterfaceWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitTransitVirtualInterfaceAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Transit Virtual Interface (%s) create: %s", d.Id(), err) } return append(diags, resourceTransitVirtualInterfaceRead(ctx, d, meta)...) @@ -173,21 +177,23 @@ func resourceTransitVirtualInterfaceCreate(ctx context.Context, d *schema.Resour func resourceTransitVirtualInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - if vif == nil { - log.Printf("[WARN] Direct Connect transit virtual interface (%s) not found, removing from state", d.Id()) + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Direct Connect Transit Virtual Interface (%s) not found, removing from state", d.Id()) d.SetId("") return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Direct Connect Transit Virtual Interface (%s): %s", d.Id(), err) + } + d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) - d.Set("amazon_side_asn", strconv.FormatInt(aws.Int64Value(vif.AmazonSideAsn), 10)) + d.Set("amazon_side_asn", strconv.FormatInt(aws.ToInt64(vif.AmazonSideAsn), 10)) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -219,8 +225,8 @@ func resourceTransitVirtualInterfaceUpdate(ctx context.Context, d *schema.Resour return diags } - if err := transitVirtualInterfaceWaitUntilAvailable(ctx, meta.(*conns.AWSClient).DirectConnectConn(ctx), d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { - return sdkdiag.AppendFromErr(diags, err) + if _, err := waitTransitVirtualInterfaceAvailable(ctx, meta.(*conns.AWSClient).DirectConnectClient(ctx), d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Transit Virtual Interface (%s) update: %s", d.Id(), err) } return append(diags, resourceTransitVirtualInterfaceRead(ctx, d, meta)...) @@ -231,32 +237,28 @@ func resourceTransitVirtualInterfaceDelete(ctx context.Context, d *schema.Resour } func resourceTransitVirtualInterfaceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) + + vif, err := findVirtualInterfaceByID(ctx, conn, d.Id()) - vif, err := virtualInterfaceRead(ctx, d.Id(), conn) if err != nil { return nil, err } - if vif == nil { - return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) - } - if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "transit" { + if vifType := aws.ToString(vif.VirtualInterfaceType); vifType != "transit" { return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } return []*schema.ResourceData{d}, nil } -func transitVirtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { - return virtualInterfaceWaitUntilAvailable(ctx, conn, - vifId, +func waitTransitVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + return waitVirtualInterfaceAvailable( + ctx, + conn, + id, + enum.Slice(awstypes.VirtualInterfaceStatePending), + enum.Slice(awstypes.VirtualInterfaceStateAvailable, awstypes.VirtualInterfaceStateDown), timeout, - []string{ - directconnect.VirtualInterfaceStatePending, - }, - []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateDown, - }) + ) } diff --git a/internal/service/directconnect/transit_virtual_interface_test.go b/internal/service/directconnect/transit_virtual_interface_test.go index f562571ce8e..0f78cc194b4 100644 --- a/internal/service/directconnect/transit_virtual_interface_test.go +++ b/internal/service/directconnect/transit_virtual_interface_test.go @@ -6,13 +6,12 @@ package directconnect_test import ( "context" "fmt" - "os" "strconv" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -34,13 +33,9 @@ func TestAccDirectConnectTransitVirtualInterface_serial(t *testing.T) { func testAccTransitVirtualInterface_basic(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_transit_virtual_interface.test" dxGatewayResourceName := "aws_dx_gateway.test" rName := fmt.Sprintf("tf-testacc-transit-vif-%s", sdkacctest.RandString(9)) @@ -55,17 +50,17 @@ func testAccTransitVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckTransitVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccTransitVirtualInterfaceConfig_basic(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -76,17 +71,17 @@ func testAccTransitVirtualInterface_basic(t *testing.T) { ), }, { - Config: testAccTransitVirtualInterfaceConfig_updated(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccTransitVirtualInterfaceConfig_updated(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -96,7 +91,6 @@ func testAccTransitVirtualInterface_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -108,13 +102,9 @@ func testAccTransitVirtualInterface_basic(t *testing.T) { func testAccTransitVirtualInterface_tags(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_transit_virtual_interface.test" dxGatewayResourceName := "aws_dx_gateway.test" rName := fmt.Sprintf("tf-testacc-transit-vif-%s", sdkacctest.RandString(9)) @@ -129,17 +119,17 @@ func testAccTransitVirtualInterface_tags(t *testing.T) { CheckDestroy: testAccCheckTransitVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccTransitVirtualInterfaceConfig_tags(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccTransitVirtualInterfaceConfig_tags(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -153,17 +143,17 @@ func testAccTransitVirtualInterface_tags(t *testing.T) { ), }, { - Config: testAccTransitVirtualInterfaceConfig_tagsUpdated(connectionId, rName, amzAsn, bgpAsn, vlan), + Config: testAccTransitVirtualInterfaceConfig_tagsUpdated(connectionID, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( testAccCheckTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -176,7 +166,6 @@ func testAccTransitVirtualInterface_tags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -188,13 +177,9 @@ func testAccTransitVirtualInterface_tags(t *testing.T) { func testAccTransitVirtualInterface_siteLink(t *testing.T) { ctx := acctest.Context(t) - key := "DX_CONNECTION_ID" - connectionId := os.Getenv(key) - if connectionId == "" { - t.Skipf("Environment variable %s is not set", key) - } + connectionID := acctest.SkipIfEnvVarNotSet(t, "DX_CONNECTION_ID") - var vif directconnect.VirtualInterface + var vif awstypes.VirtualInterface resourceName := "aws_dx_transit_virtual_interface.test" dxGatewayResourceName := "aws_dx_gateway.test" rName := fmt.Sprintf("tf-testacc-transit-vif-%s", sdkacctest.RandString(9)) @@ -209,17 +194,17 @@ func testAccTransitVirtualInterface_siteLink(t *testing.T) { CheckDestroy: testAccCheckTransitVirtualInterfaceDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccTransitVirtualInterfaceConfig_siteLinkBasic(connectionId, rName, amzAsn, bgpAsn, vlan, true), + Config: testAccTransitVirtualInterfaceConfig_siteLinkBasic(connectionID, rName, amzAsn, bgpAsn, vlan, true), Check: resource.ComposeTestCheckFunc( testAccCheckTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -231,17 +216,17 @@ func testAccTransitVirtualInterface_siteLink(t *testing.T) { ), }, { - Config: testAccTransitVirtualInterfaceConfig_siteLinkUpdated(connectionId, rName, amzAsn, bgpAsn, vlan, false), + Config: testAccTransitVirtualInterfaceConfig_siteLinkUpdated(connectionID, rName, amzAsn, bgpAsn, vlan, false), Check: resource.ComposeTestCheckFunc( testAccCheckTransitVirtualInterfaceExists(ctx, resourceName, &vif), resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), resource.TestCheckResourceAttrSet(resourceName, "amazon_side_asn"), - acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "directconnect", regexache.MustCompile(fmt.Sprintf("dxvif/%s", aws.ToString(vif.VirtualInterfaceId)))), resource.TestCheckResourceAttrSet(resourceName, "aws_device"), resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), - resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionId), + resource.TestCheckResourceAttr(resourceName, names.AttrConnectionID, connectionID), resource.TestCheckResourceAttrSet(resourceName, "customer_address"), resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, names.AttrID), resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", acctest.CtTrue), @@ -252,7 +237,6 @@ func testAccTransitVirtualInterface_siteLink(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, - // Test import. { ResourceName: resourceName, ImportState: true, @@ -262,7 +246,7 @@ func testAccTransitVirtualInterface_siteLink(t *testing.T) { }) } -func testAccCheckTransitVirtualInterfaceExists(ctx context.Context, name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { +func testAccCheckTransitVirtualInterfaceExists(ctx context.Context, name string, vif *awstypes.VirtualInterface) resource.TestCheckFunc { return testAccCheckVirtualInterfaceExists(ctx, name, vif) } @@ -282,7 +266,7 @@ resource "aws_dx_gateway" "test" { } func testAccTransitVirtualInterfaceConfig_basic(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccTransitVirtualInterfaceConfig_base(rName, amzAsn), fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -291,11 +275,11 @@ resource "aws_dx_transit_virtual_interface" "test" { name = %[2]q vlan = %[4]d } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccTransitVirtualInterfaceConfig_updated(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccTransitVirtualInterfaceConfig_base(rName, amzAsn), fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -305,11 +289,11 @@ resource "aws_dx_transit_virtual_interface" "test" { name = %[2]q vlan = %[4]d } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccTransitVirtualInterfaceConfig_tags(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccTransitVirtualInterfaceConfig_base(rName, amzAsn), fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -324,11 +308,11 @@ resource "aws_dx_transit_virtual_interface" "test" { Key2 = "Value2a" } } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccTransitVirtualInterfaceConfig_tagsUpdated(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return testAccTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccTransitVirtualInterfaceConfig_base(rName, amzAsn), fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -343,11 +327,11 @@ resource "aws_dx_transit_virtual_interface" "test" { Key3 = "Value3" } } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan)) } func testAccTransitVirtualInterfaceConfig_siteLinkBasic(cid, rName string, amzAsn, bgpAsn, vlan int, sitelink_enabled bool) string { - return testAccTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccTransitVirtualInterfaceConfig_base(rName, amzAsn), fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -358,11 +342,11 @@ resource "aws_dx_transit_virtual_interface" "test" { sitelink_enabled = %[5]t vlan = %[4]d } -`, cid, rName, bgpAsn, vlan, sitelink_enabled) +`, cid, rName, bgpAsn, vlan, sitelink_enabled)) } func testAccTransitVirtualInterfaceConfig_siteLinkUpdated(cid, rName string, amzAsn, bgpAsn, vlan int, sitelink_enabled bool) string { - return testAccTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccTransitVirtualInterfaceConfig_base(rName, amzAsn), fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" bgp_asn = %[3]d @@ -373,5 +357,5 @@ resource "aws_dx_transit_virtual_interface" "test" { sitelink_enabled = %[5]t vlan = %[4]d } -`, cid, rName, bgpAsn, vlan, sitelink_enabled) +`, cid, rName, bgpAsn, vlan, sitelink_enabled)) } diff --git a/internal/service/directconnect/validate_test.go b/internal/service/directconnect/validate_test.go deleted file mode 100644 index 79bea3a65f7..00000000000 --- a/internal/service/directconnect/validate_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect_test - -import ( - "testing" - - "github.com/hashicorp/terraform-provider-aws/internal/acctest" - tfdirectconnect "github.com/hashicorp/terraform-provider-aws/internal/service/directconnect" -) - -func TestValidConnectionBandWidth(t *testing.T) { - t.Parallel() - - validBandwidths := []string{ - "1Gbps", - "2Gbps", - "5Gbps", - "10Gbps", - "100Gbps", - "50Mbps", - "100Mbps", - "200Mbps", - "300Mbps", - "400Mbps", - "500Mbps", - } - for _, v := range validBandwidths { - _, errors := tfdirectconnect.ValidConnectionBandWidth()(v, "bandwidth") - if len(errors) != 0 { - t.Fatalf("%q should be a valid bandwidth: %q", v, errors) - } - } - - invalidBandwidths := []string{ - "1Tbps", - "10GBpS", - "42Mbps", - acctest.Ct0, - "???", - "a lot", - } - for _, v := range invalidBandwidths { - _, errors := tfdirectconnect.ValidConnectionBandWidth()(v, "bandwidth") - if len(errors) == 0 { - t.Fatalf("%q should be an invalid bandwidth", v) - } - } -} diff --git a/internal/service/directconnect/vif.go b/internal/service/directconnect/vif.go index 6c1648a64d1..10f26bc01ff 100644 --- a/internal/service/directconnect/vif.go +++ b/internal/service/directconnect/vif.go @@ -5,56 +5,50 @@ package directconnect import ( "context" - "fmt" "log" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/directconnect" + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func virtualInterfaceRead(ctx context.Context, id string, conn *directconnect.DirectConnect) (*directconnect.VirtualInterface, error) { - resp, state, err := virtualInterfaceStateRefresh(ctx, conn, id)() - if err != nil { - return nil, fmt.Errorf("reading Direct Connect virtual interface (%s): %s", id, err) - } - if state == directconnect.VirtualInterfaceStateDeleted { - return nil, nil - } - - return resp.(*directconnect.VirtualInterface), nil -} - func virtualInterfaceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) if d.HasChange("mtu") { - req := &directconnect.UpdateVirtualInterfaceAttributesInput{ - Mtu: aws.Int64(int64(d.Get("mtu").(int))), + input := &directconnect.UpdateVirtualInterfaceAttributesInput{ + Mtu: aws.Int32(int32(d.Get("mtu").(int))), VirtualInterfaceId: aws.String(d.Id()), } - log.Printf("[DEBUG] Modifying Direct Connect virtual interface attributes: %s", req) - _, err := conn.UpdateVirtualInterfaceAttributesWithContext(ctx, req) + + _, err := conn.UpdateVirtualInterfaceAttributes(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying Direct Connect virtual interface (%s) attributes: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating Direct Connect Virtual Interface (%s) Mtu attribute: %s", d.Id(), err) } } + if d.HasChange("sitelink_enabled") { - req := &directconnect.UpdateVirtualInterfaceAttributesInput{ + input := &directconnect.UpdateVirtualInterfaceAttributesInput{ EnableSiteLink: aws.Bool(d.Get("sitelink_enabled").(bool)), VirtualInterfaceId: aws.String(d.Id()), } - log.Printf("[DEBUG] Modifying Direct Connect virtual interface attributes: %s", req) - _, err := conn.UpdateVirtualInterfaceAttributesWithContext(ctx, req) + + _, err := conn.UpdateVirtualInterfaceAttributes(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying Direct Connect virtual interface (%s) attributes: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "modifying Direct Connect Virtual Interface (%s) EnableSiteLink attribute: %s", d.Id(), err) } } @@ -63,81 +57,130 @@ func virtualInterfaceUpdate(ctx context.Context, d *schema.ResourceData, meta in func virtualInterfaceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).DirectConnectConn(ctx) + conn := meta.(*conns.AWSClient).DirectConnectClient(ctx) - log.Printf("[DEBUG] Deleting Direct Connect virtual interface: %s", d.Id()) - _, err := conn.DeleteVirtualInterfaceWithContext(ctx, &directconnect.DeleteVirtualInterfaceInput{ + log.Printf("[DEBUG] Deleting Direct Connect Virtual Interface: %s", d.Id()) + _, err := conn.DeleteVirtualInterface(ctx, &directconnect.DeleteVirtualInterfaceInput{ VirtualInterfaceId: aws.String(d.Id()), }) + + if errs.IsAErrorMessageContains[*awstypes.DirectConnectClientException](err, "does not exist") { + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting Direct Connect Virtual Interface (%s): %s", d.Id(), err) + } + + if _, err := waitVirtualInterfaceDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect Virtual Interface (%s) delete: %s", d.Id(), err) + } + + return diags +} + +func findVirtualInterfaceByID(ctx context.Context, conn *directconnect.Client, id string) (*awstypes.VirtualInterface, error) { + input := &directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(id), + } + output, err := findVirtualInterface(ctx, conn, input, tfslices.PredicateTrue[*awstypes.VirtualInterface]()) + if err != nil { - if tfawserr.ErrMessageContains(err, directconnect.ErrCodeClientException, "does not exist") { - return diags + return nil, err + } + + if state := output.VirtualInterfaceState; state == awstypes.VirtualInterfaceStateDeleted { + return nil, &retry.NotFoundError{ + Message: string(state), + LastRequest: input, } - return sdkdiag.AppendErrorf(diags, "deleting Direct Connect virtual interface (%s): %s", d.Id(), err) - } - - deleteStateConf := &retry.StateChangeConf{ - Pending: []string{ - directconnect.VirtualInterfaceStateAvailable, - directconnect.VirtualInterfaceStateConfirming, - directconnect.VirtualInterfaceStateDeleting, - directconnect.VirtualInterfaceStateDown, - directconnect.VirtualInterfaceStatePending, - directconnect.VirtualInterfaceStateRejected, - directconnect.VirtualInterfaceStateVerifying, - }, - Target: []string{ - directconnect.VirtualInterfaceStateDeleted, - }, - Refresh: virtualInterfaceStateRefresh(ctx, conn, d.Id()), - Timeout: d.Timeout(schema.TimeoutDelete), - Delay: 10 * time.Second, - MinTimeout: 5 * time.Second, } - _, err = deleteStateConf.WaitForStateContext(ctx) + + return output, nil +} + +func findVirtualInterface(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeVirtualInterfacesInput, filter tfslices.Predicate[*awstypes.VirtualInterface]) (*awstypes.VirtualInterface, error) { + output, err := findVirtualInterfaces(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findVirtualInterfaces(ctx context.Context, conn *directconnect.Client, input *directconnect.DescribeVirtualInterfacesInput, filter tfslices.Predicate[*awstypes.VirtualInterface]) ([]awstypes.VirtualInterface, error) { + output, err := conn.DescribeVirtualInterfaces(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Direct Connect virtual interface (%s) to be deleted: %s", d.Id(), err) + return nil, err } - return diags + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return tfslices.Filter(output.VirtualInterfaces, tfslices.PredicateValue(filter)), nil } -func virtualInterfaceStateRefresh(ctx context.Context, conn *directconnect.DirectConnect, vifId string) retry.StateRefreshFunc { +func statusVirtualInterface(ctx context.Context, conn *directconnect.Client, id string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.DescribeVirtualInterfacesWithContext(ctx, &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(vifId), - }) + output, err := findVirtualInterfaceByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + if err != nil { return nil, "", err } - n := len(resp.VirtualInterfaces) - switch n { - case 0: - return "", directconnect.VirtualInterfaceStateDeleted, nil - - case 1: - vif := resp.VirtualInterfaces[0] - return vif, aws.StringValue(vif.VirtualInterfaceState), nil - - default: - return nil, "", fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, vifId) - } + return output, string(output.VirtualInterfaceState), nil } } -func virtualInterfaceWaitUntilAvailable(ctx context.Context, conn *directconnect.DirectConnect, vifId string, timeout time.Duration, pending, target []string) error { +func waitVirtualInterfaceAvailable(ctx context.Context, conn *directconnect.Client, id string, pending, target []string, timeout time.Duration) (*awstypes.VirtualInterface, error) { stateConf := &retry.StateChangeConf{ Pending: pending, Target: target, - Refresh: virtualInterfaceStateRefresh(ctx, conn, vifId), + Refresh: statusVirtualInterface(ctx, conn, id), Timeout: timeout, Delay: 10 * time.Second, MinTimeout: 5 * time.Second, } - if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for Direct Connect virtual interface (%s) to become available: %s", vifId, err) + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.VirtualInterface); ok { + return output, err + } + + return nil, err +} + +func waitVirtualInterfaceDeleted(ctx context.Context, conn *directconnect.Client, id string, timeout time.Duration) (*awstypes.VirtualInterface, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice( + awstypes.VirtualInterfaceStateAvailable, + awstypes.VirtualInterfaceStateConfirming, + awstypes.VirtualInterfaceStateDeleting, + awstypes.VirtualInterfaceStateDown, + awstypes.VirtualInterfaceStatePending, + awstypes.VirtualInterfaceStateRejected, + awstypes.VirtualInterfaceStateVerifying, + ), + Target: []string{}, + Refresh: statusVirtualInterface(ctx, conn, id), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.VirtualInterface); ok { + return output, err } - return nil + return nil, err } diff --git a/internal/service/directconnect/vif_test.go b/internal/service/directconnect/vif_test.go new file mode 100644 index 00000000000..1f49e7137ea --- /dev/null +++ b/internal/service/directconnect/vif_test.go @@ -0,0 +1,62 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package directconnect_test + +import ( + "context" + "fmt" + + awstypes "github.com/aws/aws-sdk-go-v2/service/directconnect/types" + "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" + tfdirectconnect "github.com/hashicorp/terraform-provider-aws/internal/service/directconnect" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +func testAccCheckVirtualInterfaceExists(ctx context.Context, n string, v *awstypes.VirtualInterface) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) + + output, err := tfdirectconnect.FindVirtualInterfaceByID(ctx, conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccCheckVirtualInterfaceDestroy(ctx context.Context, s *terraform.State, typ string) error { // nosemgrep:ci.semgrep.acctest.naming.destroy-check-signature + conn := acctest.Provider.Meta().(*conns.AWSClient).DirectConnectClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != typ { + continue + } + + _, err := tfdirectconnect.FindVirtualInterfaceByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("Direct Connect Virtual Interface (%s) %s still exists", typ, rs.Primary.ID) + } + + return nil +} diff --git a/internal/service/directconnect/wait.go b/internal/service/directconnect/wait.go deleted file mode 100644 index 98f6d84dc58..00000000000 --- a/internal/service/directconnect/wait.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package directconnect - -import ( - "context" - "errors" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/directconnect" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" -) - -const ( - connectionConfirmedTimeout = 10 * time.Minute - connectionDeletedTimeout = 10 * time.Minute - connectionDisassociatedTimeout = 1 * time.Minute - hostedConnectionDeletedTimeout = 10 * time.Minute - lagDeletedTimeout = 10 * time.Minute -) - -func waitConnectionConfirmed(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { //nolint:unparam - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.ConnectionStatePending, directconnect.ConnectionStateOrdering, directconnect.ConnectionStateRequested}, - Target: []string{directconnect.ConnectionStateAvailable}, - Refresh: statusConnectionState(ctx, conn, id), - Timeout: connectionConfirmedTimeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.Connection); ok { - return output, err - } - - return nil, err -} - -func waitConnectionDeleted(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.ConnectionStatePending, directconnect.ConnectionStateOrdering, directconnect.ConnectionStateAvailable, directconnect.ConnectionStateRequested, directconnect.ConnectionStateDeleting}, - Target: []string{}, - Refresh: statusConnectionState(ctx, conn, id), - Timeout: connectionDeletedTimeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.Connection); ok { - return output, err - } - - return nil, err -} - -func waitGatewayCreated(ctx context.Context, conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.Gateway, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.GatewayStatePending}, - Target: []string{directconnect.GatewayStateAvailable}, - Refresh: statusGatewayState(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.Gateway); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) - - return output, err - } - - return nil, err -} - -func waitGatewayDeleted(ctx context.Context, conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.Gateway, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.GatewayStatePending, directconnect.GatewayStateAvailable, directconnect.GatewayStateDeleting}, - Target: []string{}, - Refresh: statusGatewayState(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.Gateway); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) - - return output, err - } - - return nil, err -} - -func waitGatewayAssociationCreated(ctx context.Context, conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.GatewayAssociationStateAssociating}, - Target: []string{directconnect.GatewayAssociationStateAssociated}, - Refresh: statusGatewayAssociationState(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.GatewayAssociation); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) - - return output, err - } - - return nil, err -} - -func waitGatewayAssociationUpdated(ctx context.Context, conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.GatewayAssociationStateUpdating}, - Target: []string{directconnect.GatewayAssociationStateAssociated}, - Refresh: statusGatewayAssociationState(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.GatewayAssociation); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) - - return output, err - } - - return nil, err -} - -func waitGatewayAssociationDeleted(ctx context.Context, conn *directconnect.DirectConnect, id string, timeout time.Duration) (*directconnect.GatewayAssociation, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.GatewayAssociationStateDisassociating}, - Target: []string{}, - Refresh: statusGatewayAssociationState(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.GatewayAssociation); ok { - tfresource.SetLastError(err, errors.New(aws.StringValue(output.StateChangeError))) - - return output, err - } - - return nil, err -} - -func waitHostedConnectionDeleted(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Connection, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.ConnectionStatePending, directconnect.ConnectionStateOrdering, directconnect.ConnectionStateAvailable, directconnect.ConnectionStateRequested, directconnect.ConnectionStateDeleting}, - Target: []string{}, - Refresh: statusHostedConnectionState(ctx, conn, id), - Timeout: hostedConnectionDeletedTimeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.Connection); ok { - return output, err - } - - return nil, err -} - -func waitLagDeleted(ctx context.Context, conn *directconnect.DirectConnect, id string) (*directconnect.Lag, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{directconnect.LagStateAvailable, directconnect.LagStateRequested, directconnect.LagStatePending, directconnect.LagStateDeleting}, - Target: []string{}, - Refresh: statusLagState(ctx, conn, id), - Timeout: lagDeletedTimeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*directconnect.Lag); ok { - return output, err - } - - return nil, err -} diff --git a/internal/service/ec2/ec2_instance_type_offerings_data_source_test.go b/internal/service/ec2/ec2_instance_type_offerings_data_source_test.go index be1d36c26a4..893f14be9f1 100644 --- a/internal/service/ec2/ec2_instance_type_offerings_data_source_test.go +++ b/internal/service/ec2/ec2_instance_type_offerings_data_source_test.go @@ -89,7 +89,7 @@ data "aws_ec2_instance_type_offerings" "test" { } func testAccInstanceTypeOfferingsDataSourceConfig_location() string { - return acctest.ConfigAvailableAZsNoOptIn() + ` + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), ` data "aws_ec2_instance_type_offerings" "test" { filter { name = "location" @@ -98,5 +98,5 @@ data "aws_ec2_instance_type_offerings" "test" { location_type = "availability-zone" } -` +`) } diff --git a/internal/service/ec2/ipam_preview_next_cidr_data_source_test.go b/internal/service/ec2/ipam_preview_next_cidr_data_source_test.go index 4c5d9c781a8..b3ce2ff73a4 100644 --- a/internal/service/ec2/ipam_preview_next_cidr_data_source_test.go +++ b/internal/service/ec2/ipam_preview_next_cidr_data_source_test.go @@ -157,7 +157,7 @@ resource "aws_vpc_ipam_pool_cidr_allocation" "test" { } func testAccIPAMPreviewNextCIDRDataSourceConfig_ipv4Disallowed(netmaskLength, disallowedCidr string) string { - return testAccIPAMPreviewNextCIDRDataSourceConfig_base + fmt.Sprintf(` + return acctest.ConfigCompose(testAccIPAMPreviewNextCIDRDataSourceConfig_base, fmt.Sprintf(` data "aws_vpc_ipam_preview_next_cidr" "test" { ipam_pool_id = aws_vpc_ipam_pool.test.id netmask_length = %[1]q @@ -170,5 +170,5 @@ data "aws_vpc_ipam_preview_next_cidr" "test" { aws_vpc_ipam_pool_cidr.test ] } -`, netmaskLength, disallowedCidr) +`, netmaskLength, disallowedCidr)) } diff --git a/internal/service/ec2/ipam_preview_next_cidr_test.go b/internal/service/ec2/ipam_preview_next_cidr_test.go index c9cf3750234..afec7ab7fe6 100644 --- a/internal/service/ec2/ipam_preview_next_cidr_test.go +++ b/internal/service/ec2/ipam_preview_next_cidr_test.go @@ -157,7 +157,7 @@ resource "aws_vpc_ipam_pool_cidr_allocation" "test" { } func testAccIPAMPreviewNextCIDRConfig_ipv4Disallowed(netmaskLength, disallowedCidr string) string { - return testAccIPAMPreviewNextCIDRConfig_ipv4Base + fmt.Sprintf(` + return acctest.ConfigCompose(testAccIPAMPreviewNextCIDRConfig_ipv4Base, fmt.Sprintf(` resource "aws_vpc_ipam_preview_next_cidr" "test" { ipam_pool_id = aws_vpc_ipam_pool.test.id netmask_length = %[1]q @@ -170,5 +170,5 @@ resource "aws_vpc_ipam_preview_next_cidr" "test" { aws_vpc_ipam_pool_cidr.test ] } -`, netmaskLength, disallowedCidr) +`, netmaskLength, disallowedCidr)) } diff --git a/internal/service/elasticache/replication_group_data_source_test.go b/internal/service/elasticache/replication_group_data_source_test.go index 8ed72bcd723..fd1107ff4b6 100644 --- a/internal/service/elasticache/replication_group_data_source_test.go +++ b/internal/service/elasticache/replication_group_data_source_test.go @@ -144,7 +144,7 @@ func TestAccElastiCacheReplicationGroupDataSource_Engine_Redis_LogDeliveryConfig } func testAccReplicationGroupDataSourceConfig_basic(rName string) string { - return acctest.ConfigAvailableAZsNoOptIn() + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_elasticache_replication_group" "test" { replication_group_id = %[1]q description = "test description" @@ -159,7 +159,7 @@ resource "aws_elasticache_replication_group" "test" { data "aws_elasticache_replication_group" "test" { replication_group_id = aws_elasticache_replication_group.test.replication_group_id } -`, rName) +`, rName)) } func testAccReplicationGroupDataSourceConfig_clusterMode(rName string) string { diff --git a/internal/service/elbv2/listener_certificate_test.go b/internal/service/elbv2/listener_certificate_test.go index 06c19cf55e0..cda26656fd9 100644 --- a/internal/service/elbv2/listener_certificate_test.go +++ b/internal/service/elbv2/listener_certificate_test.go @@ -324,12 +324,12 @@ resource "aws_lb_listener" "test" { } func testAccListenerCertificateConfig_basic(rName, key, certificate string) string { - return testAccListenerCertificateConfig_base(rName, key, certificate) + ` + return acctest.ConfigCompose(testAccListenerCertificateConfig_base(rName, key, certificate), ` resource "aws_lb_listener_certificate" "test" { certificate_arn = aws_iam_server_certificate.test.arn listener_arn = aws_lb_listener.test.arn } -` +`) } func testAccListenerCertificateConfig_arnUnderscores(rName, key, certificate string) string { @@ -402,7 +402,7 @@ resource "aws_lb_listener_certificate" "test" { } func testAccListenerCertificateConfig_multiple(rName string, keys, certificates []string) string { - return testAccListenerCertificateConfig_base(rName, keys[0], certificates[0]) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccListenerCertificateConfig_base(rName, keys[0], certificates[0]), fmt.Sprintf(` resource "aws_lb_listener_certificate" "default" { listener_arn = aws_lb_listener.test.arn certificate_arn = aws_iam_server_certificate.test.arn @@ -429,11 +429,11 @@ resource "aws_iam_server_certificate" "additional_2" { certificate_body = "%[4]s" private_key = "%[5]s" } -`, rName, acctest.TLSPEMEscapeNewlines(certificates[1]), acctest.TLSPEMEscapeNewlines(keys[1]), acctest.TLSPEMEscapeNewlines(certificates[2]), acctest.TLSPEMEscapeNewlines(keys[2])) +`, rName, acctest.TLSPEMEscapeNewlines(certificates[1]), acctest.TLSPEMEscapeNewlines(keys[1]), acctest.TLSPEMEscapeNewlines(certificates[2]), acctest.TLSPEMEscapeNewlines(keys[2]))) } func testAccListenerCertificateConfig_multipleAddNew(rName string, keys, certificates []string) string { - return testAccListenerCertificateConfig_multiple(rName, keys, certificates) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccListenerCertificateConfig_multiple(rName, keys, certificates), fmt.Sprintf(` resource "aws_iam_server_certificate" "additional_3" { name = "%[1]s-additional-3" certificate_body = "%[2]s" @@ -444,5 +444,5 @@ resource "aws_lb_listener_certificate" "additional_3" { listener_arn = aws_lb_listener.test.arn certificate_arn = aws_iam_server_certificate.additional_3.arn } -`, rName, acctest.TLSPEMEscapeNewlines(certificates[3]), acctest.TLSPEMEscapeNewlines(keys[3])) +`, rName, acctest.TLSPEMEscapeNewlines(certificates[3]), acctest.TLSPEMEscapeNewlines(keys[3]))) } diff --git a/internal/service/events/target_test.go b/internal/service/events/target_test.go index c0b23f46abf..38b784f64b4 100644 --- a/internal/service/events/target_test.go +++ b/internal/service/events/target_test.go @@ -1656,7 +1656,7 @@ data "aws_partition" "current" {} `, rName) } -func testAccTargetHTTPConfigBase(rName string) string { +func testAccTargetConfig_baseHTTP(rName string) string { return fmt.Sprintf(` resource "aws_cloudwatch_event_rule" "test" { name = %[1]q @@ -1711,7 +1711,7 @@ data "aws_partition" "current" {} } func testAccTargetConfig_http(rName string) string { - return testAccTargetHTTPConfigBase(rName) + ` + return acctest.ConfigCompose(testAccTargetConfig_baseHTTP(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = "${aws_api_gateway_stage.test.execution_arn}/GET" rule = aws_cloudwatch_event_rule.test.id @@ -1727,11 +1727,11 @@ resource "aws_cloudwatch_event_target" "test" { } } } -` +`) } func testAccTargetConfig_httpParameter(rName string) string { - return testAccTargetHTTPConfigBase(rName) + ` + return acctest.ConfigCompose(testAccTargetConfig_baseHTTP(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = "${aws_api_gateway_stage.test.execution_arn}/*/*/GET" rule = aws_cloudwatch_event_rule.test.id @@ -1747,11 +1747,11 @@ resource "aws_cloudwatch_event_target" "test" { } } } -` +`) } func testAccTargetConfig_httpParameterUpdated(rName string) string { - return testAccTargetHTTPConfigBase(rName) + ` + return acctest.ConfigCompose(testAccTargetConfig_baseHTTP(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = "${aws_api_gateway_stage.test.execution_arn}/*/*/*/GET" rule = aws_cloudwatch_event_rule.test.id @@ -1767,10 +1767,10 @@ resource "aws_cloudwatch_event_target" "test" { } } } -` +`) } -func testAccTargetConfig_ecsBase(rName string) string { +func testAccTargetConfig_baseECS(rName string) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` resource "aws_iam_role" "test" { name = %[1]q @@ -1841,7 +1841,7 @@ resource "aws_cloudwatch_event_rule" "test" { } func testAccTargetConfig_ecs(rName string) string { - return acctest.ConfigCompose(testAccTargetConfig_ecsBase(rName), ` + return acctest.ConfigCompose(testAccTargetConfig_baseECS(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id @@ -1932,7 +1932,7 @@ resource "aws_redshift_cluster" "test" { } func testAccTargetConfig_ecsNoLaunchType(rName string) string { - return acctest.ConfigCompose(testAccTargetConfig_ecsBase(rName), ` + return acctest.ConfigCompose(testAccTargetConfig_baseECS(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id @@ -1951,7 +1951,7 @@ resource "aws_cloudwatch_event_target" "test" { } func testAccTargetConfig_ecsBlankLaunchType(rName string) string { - return acctest.ConfigCompose(testAccTargetConfig_ecsBase(rName), ` + return acctest.ConfigCompose(testAccTargetConfig_baseECS(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id @@ -1971,7 +1971,7 @@ resource "aws_cloudwatch_event_target" "test" { } func testAccTargetConfig_ecsBlankTaskCount(rName string) string { - return acctest.ConfigCompose(testAccTargetConfig_ecsBase(rName), ` + return acctest.ConfigCompose(testAccTargetConfig_baseECS(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id @@ -1990,7 +1990,7 @@ resource "aws_cloudwatch_event_target" "test" { } func testAccTargetConfig_ecsBlankTaskCountFull(rName string) string { - return acctest.ConfigCompose(testAccTargetConfig_ecsBase(rName), ` + return acctest.ConfigCompose(testAccTargetConfig_baseECS(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id @@ -2022,7 +2022,7 @@ resource "aws_cloudwatch_event_target" "test" { func testAccTargetConfig_ecsCapacityProvider(rName string) string { return acctest.ConfigCompose( acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(), - testAccTargetConfig_ecsBase(rName), + testAccTargetConfig_baseECS(rName), fmt.Sprintf(` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id @@ -2088,7 +2088,7 @@ resource "aws_ecs_capacity_provider" "test" { func testAccTargetConfig_ecsPlacementStrategy(rName string) string { return acctest.ConfigCompose( acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(), - testAccTargetConfig_ecsBase(rName), + testAccTargetConfig_baseECS(rName), fmt.Sprintf(` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id @@ -2629,7 +2629,7 @@ resource "aws_sns_topic" "test" { } func testAccTargetConfig_ecsNoPropagateTags(rName string) string { - return acctest.ConfigCompose(testAccTargetConfig_ecsBase(rName), ` + return acctest.ConfigCompose(testAccTargetConfig_baseECS(rName), ` resource "aws_cloudwatch_event_target" "test" { arn = aws_ecs_cluster.test.id rule = aws_cloudwatch_event_rule.test.id diff --git a/internal/service/rds/proxy_test.go b/internal/service/rds/proxy_test.go index 77f58e1e4e1..92e434b6073 100644 --- a/internal/service/rds/proxy_test.go +++ b/internal/service/rds/proxy_test.go @@ -956,7 +956,7 @@ resource "aws_secretsmanager_secret_version" "test2" { } func testAccProxyConfig_tags1(rName, tagKey1, tagValue1 string) string { - return testAccProxyConfig_base(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProxyConfig_base(rName), fmt.Sprintf(` resource "aws_db_proxy" "test" { depends_on = [ aws_secretsmanager_secret_version.test, @@ -980,11 +980,11 @@ resource "aws_db_proxy" "test" { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccProxyConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccProxyConfig_base(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccProxyConfig_base(rName), fmt.Sprintf(` resource "aws_db_proxy" "test" { depends_on = [ aws_secretsmanager_secret_version.test, @@ -1009,5 +1009,5 @@ resource "aws_db_proxy" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } diff --git a/internal/service/s3/bucket_object_test.go b/internal/service/s3/bucket_object_test.go index a8ccc10d0df..a8ef9a9b4a8 100644 --- a/internal/service/s3/bucket_object_test.go +++ b/internal/service/s3/bucket_object_test.go @@ -1849,7 +1849,7 @@ func testAccBucketObjectConfig_nonVersioned(rName string, source string) string ] }` - return acctest.ConfigAssumeRolePolicy(policy) + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAssumeRolePolicy(policy), fmt.Sprintf(` resource "aws_s3_bucket" "object_bucket_3" { bucket = %[1]q } @@ -1860,7 +1860,7 @@ resource "aws_s3_bucket_object" "object" { source = %[2]q etag = filemd5(%[2]q) } -`, rName, source) +`, rName, source)) } func testAccBucketObjectConfig_objectKeyEnabled(rName string, content string) string { diff --git a/internal/service/s3/bucket_objects_data_source_test.go b/internal/service/s3/bucket_objects_data_source_test.go index 08967d751fd..75ee0790d9e 100644 --- a/internal/service/s3/bucket_objects_data_source_test.go +++ b/internal/service/s3/bucket_objects_data_source_test.go @@ -283,17 +283,17 @@ resource "aws_s3_object" "object7" { } func testAccBucketObjectsDataSourceConfig_resourcesPlusAccessPoint(randInt int) string { - return testAccBucketObjectsDataSourceConfig_resources(randInt) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccBucketObjectsDataSourceConfig_resources(randInt), fmt.Sprintf(` resource "aws_s3_access_point" "test" { bucket = aws_s3_bucket.objects_bucket.bucket name = "tf-objects-test-access-point-%[1]d" } -`, randInt) +`, randInt)) } func testAccBucketObjectsDataSourceConfig_basic(randInt int) string { return fmt.Sprintf(` -%s +%[1]s data "aws_s3_objects" "yesh" { bucket = aws_s3_bucket.objects_bucket.id @@ -304,13 +304,13 @@ data "aws_s3_objects" "yesh" { } func testAccBucketObjectsDataSourceConfig_basicViaAccessPoint(randInt int) string { - return testAccBucketObjectsDataSourceConfig_resourcesPlusAccessPoint(randInt) + ` + return acctest.ConfigCompose(testAccBucketObjectsDataSourceConfig_resourcesPlusAccessPoint(randInt), ` data "aws_s3_objects" "yesh" { bucket = aws_s3_access_point.test.arn prefix = "arch/navajo/" delimiter = "/" } -` +`) } func testAccBucketObjectsDataSourceConfig_all(randInt int) string { diff --git a/internal/service/s3/object_test.go b/internal/service/s3/object_test.go index 036ae3f5379..d638d307865 100644 --- a/internal/service/s3/object_test.go +++ b/internal/service/s3/object_test.go @@ -2790,7 +2790,7 @@ func testAccObjectConfig_nonVersioned(rName string, source string) string { ] }` - return acctest.ConfigAssumeRolePolicy(policy) + fmt.Sprintf(` + return acctest.ConfigCompose(acctest.ConfigAssumeRolePolicy(policy), fmt.Sprintf(` resource "aws_s3_bucket" "object_bucket_3" { bucket = %[1]q } @@ -2801,7 +2801,7 @@ resource "aws_s3_object" "object" { source = %[2]q etag = filemd5(%[2]q) } -`, rName, source) +`, rName, source)) } func testAccObjectConfig_bucketKeyEnabled(rName string, content string) string { diff --git a/internal/service/ssm/maintenance_window_task_test.go b/internal/service/ssm/maintenance_window_task_test.go index d05fff898cd..bface56b2f4 100644 --- a/internal/service/ssm/maintenance_window_task_test.go +++ b/internal/service/ssm/maintenance_window_task_test.go @@ -545,7 +545,7 @@ func testAccMaintenanceWindowTaskImportStateIdFunc(resourceName string) resource } } -func testAccMaintenanceWindowTaskBaseConfig(rName string) string { +func testAccMaintenanceWindowTaskConfig_base(rName string) string { return fmt.Sprintf(` resource "aws_ssm_maintenance_window" "test" { cutoff = 1 @@ -604,7 +604,7 @@ POLICY } func testAccMaintenanceWindowTaskConfig_basic(rName string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName) + ` + return acctest.ConfigCompose(testAccMaintenanceWindowTaskConfig_base(rName), ` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -633,7 +633,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_noTarget(rName string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName) + ` + return acctest.ConfigCompose(testAccMaintenanceWindowTaskConfig_base(rName), ` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -646,7 +646,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_cutoff(rName, cutoff string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -660,7 +660,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_basicUpdate(rName, description, taskType, taskArn string, priority, maxConcurrency, maxErrors int) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -727,7 +727,7 @@ EOF } func testAccMaintenanceWindowTaskConfig_basicUpdated(rName string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName) + ` + return acctest.ConfigCompose(testAccMaintenanceWindowTaskConfig_base(rName), ` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -759,7 +759,7 @@ resource "aws_ssm_maintenance_window_task" "test" { func testAccMaintenanceWindowTaskConfig_description(rName string, description string) string { return acctest.ConfigCompose( - testAccMaintenanceWindowTaskBaseConfig(rName), + testAccMaintenanceWindowTaskConfig_base(rName), fmt.Sprintf(` resource "aws_ssm_maintenance_window_task" "test" { description = %[1]q @@ -787,7 +787,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_emptyNotifcation(rName string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName) + ` + return acctest.ConfigCompose(testAccMaintenanceWindowTaskConfig_base(rName), ` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -820,7 +820,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_noRole(rName string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName) + ` + return acctest.ConfigCompose(testAccMaintenanceWindowTaskConfig_base(rName), ` resource "aws_ssm_maintenance_window_task" "test" { description = "This resource is for test purpose only" max_concurrency = 2 @@ -849,7 +849,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_automation(rName, version string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -885,7 +885,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_automationUpdate(rName, version string) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -926,7 +926,7 @@ resource "aws_ssm_maintenance_window_task" "test" { func testAccMaintenanceWindowTaskConfig_lambda(funcName, policyName, roleName, sgName, rName string, rInt int) string { return fmt.Sprintf(testAccLambdaBasicConfig(funcName, policyName, roleName, sgName)+ - testAccMaintenanceWindowTaskBaseConfig(rName)+` + testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -959,7 +959,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_runCommand(rName, comment string, timeoutSeconds int) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_ssm_maintenance_window_task" "test" { window_id = aws_ssm_maintenance_window.test.id @@ -994,7 +994,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_runCommandUpdate(rName, comment string, timeoutSeconds int) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_s3_bucket" "test" { bucket = %[1]q force_destroy = true @@ -1035,7 +1035,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_runCommandCloudWatch(rName string, enabled bool) string { - return fmt.Sprintf(testAccMaintenanceWindowTaskBaseConfig(rName)+` + return fmt.Sprintf(testAccMaintenanceWindowTaskConfig_base(rName)+` resource "aws_cloudwatch_log_group" "test" { name = %[1]q } @@ -1076,7 +1076,7 @@ resource "aws_ssm_maintenance_window_task" "test" { } func testAccMaintenanceWindowTaskConfig_stepFunction(rName string) string { - return testAccMaintenanceWindowTaskBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccMaintenanceWindowTaskConfig_base(rName), fmt.Sprintf(` resource "aws_sfn_activity" "test" { name = %[1]q } @@ -1106,17 +1106,17 @@ resource "aws_ssm_maintenance_window_task" "test" { } } } -`, rName) +`, rName)) } func testAccLambdaBasicConfig(funcName, policyName, roleName, sgName string) string { - return fmt.Sprintf(acctest.ConfigLambdaBase(policyName, roleName, sgName)+` + return acctest.ConfigCompose(acctest.ConfigLambdaBase(policyName, roleName, sgName), fmt.Sprintf(` resource "aws_lambda_function" "test" { filename = "test-fixtures/lambdatest.zip" - function_name = "%s" + function_name = %[1]q role = aws_iam_role.iam_for_lambda.arn handler = "exports.example" runtime = "nodejs16.x" } -`, funcName) +`, funcName)) } diff --git a/internal/service/storagegateway/cached_iscsi_volume_test.go b/internal/service/storagegateway/cached_iscsi_volume_test.go index ac44748848b..9d0922df9aa 100644 --- a/internal/service/storagegateway/cached_iscsi_volume_test.go +++ b/internal/service/storagegateway/cached_iscsi_volume_test.go @@ -347,7 +347,7 @@ func testAccCheckCachediSCSIVolumeDestroy(ctx context.Context) resource.TestChec } } -func testAccCachediSCSIVolumeBaseConfig(rName string) string { +func testAccCachediSCSIVolumeConfig_base(rName string) string { return acctest.ConfigCompose( testAccGatewayConfig_typeCached(rName), fmt.Sprintf(` @@ -392,7 +392,7 @@ resource "aws_storagegateway_cache" "test" { func testAccCachediSCSIVolumeConfig_basic(rName string) string { return acctest.ConfigCompose( - testAccCachediSCSIVolumeBaseConfig(rName), + testAccCachediSCSIVolumeConfig_base(rName), fmt.Sprintf(` resource "aws_storagegateway_cached_iscsi_volume" "test" { gateway_arn = aws_storagegateway_cache.test.gateway_arn @@ -404,7 +404,9 @@ resource "aws_storagegateway_cached_iscsi_volume" "test" { } func testAccCachediSCSIVolumeConfig_kmsEncrypted(rName string) string { - return testAccCachediSCSIVolumeBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCachediSCSIVolumeConfig_base(rName), + fmt.Sprintf(` resource "aws_kms_key" "test" { description = "Terraform acc test %[1]s" policy = <