diff --git a/.changelog/35234.txt b/.changelog/35234.txt new file mode 100644 index 000000000000..46d08ff94d1c --- /dev/null +++ b/.changelog/35234.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_iot_ca_certificate: Change `registration_config.role_arn` from `TypeBool` to `TypeString`, fixing `Inappropriate value for attribute "role_arn": a bool is required` errors +``` \ No newline at end of file diff --git a/internal/service/iot/ca_certificate.go b/internal/service/iot/ca_certificate.go index e5d72c436cb6..d8fbce798528 100644 --- a/internal/service/iot/ca_certificate.go +++ b/internal/service/iot/ca_certificate.go @@ -76,7 +76,7 @@ func ResourceCACertificate() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "role_arn": { - Type: schema.TypeBool, + Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, @@ -159,13 +159,17 @@ func resourceCACertificateCreate(ctx context.Context, d *schema.ResourceData, me input.VerificationCertificate = aws.String(v.(string)) } - output, err := conn.RegisterCACertificateWithContext(ctx, input) + outputRaw, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, + func() (interface{}, error) { + return conn.RegisterCACertificateWithContext(ctx, input) + }, + iot.ErrCodeInvalidRequestException, "included in the RegistrationConfig does not exist or cannot be assumed by AWS IoT") if err != nil { return sdkdiag.AppendErrorf(diags, "registering IoT CA Certificate: %s", err) } - d.SetId(aws.StringValue(output.CertificateId)) + d.SetId(aws.StringValue(outputRaw.(*iot.RegisterCACertificateOutput).CertificateId)) return append(diags, resourceCACertificateRead(ctx, d, meta)...) } @@ -239,7 +243,11 @@ func resourceCACertificateUpdate(ctx context.Context, d *schema.ResourceData, me } } - _, err := conn.UpdateCACertificateWithContext(ctx, input) + _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, + func() (interface{}, error) { + return conn.UpdateCACertificateWithContext(ctx, input) + }, + iot.ErrCodeInvalidRequestException, "included in the RegistrationConfig does not exist or cannot be assumed by AWS IoT") if err != nil { return sdkdiag.AppendErrorf(diags, "updating IoT CA Certificate (%s): %s", d.Id(), err) diff --git a/internal/service/iot/ca_certificate_test.go b/internal/service/iot/ca_certificate_test.go index d588466716d5..f77629d431dc 100644 --- a/internal/service/iot/ca_certificate_test.go +++ b/internal/service/iot/ca_certificate_test.go @@ -163,6 +163,36 @@ func TestAccIoTCACertificate_defaultMode(t *testing.T) { }) } +func TestAccIoTCACertificate_registrationConfig(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_iot_ca_certificate.test" + testExternalProviders := map[string]resource.ExternalProvider{ + "tls": { + Source: "hashicorp/tls", + VersionConstraint: "4.0.4", + }, + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ExternalProviders: testExternalProviders, + CheckDestroy: testAccCheckCACertificateDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCACertificateConfig_registrationConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckCACertificateExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "registration_config.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "registration_config.0.role_arn"), + resource.TestCheckResourceAttrSet(resourceName, "registration_config.0.template_body"), + ), + }, + }, + }) +} + func testAccCheckCACertificateExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -301,3 +331,149 @@ resource "aws_iot_ca_certificate" "test" { data "aws_iot_registration_code" "test" {} `, active, allowAutoRegistration) } + +func testAccCACertificateConfig_registrationConfig_iamRole() string { + return ` +resource "aws_iam_role" "test" { + name = "test_iot_role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Service = "iot.amazonaws.com" + } + Action = "sts:AssumeRole" + } + ] + }) +} +` +} + +func testAccCACertificateConfig_registrationConfig() string { + return acctest.ConfigCompose(testAccCACertificateConfig_registrationConfig_iamRole(), ` +data "aws_caller_identity" "current" {} + +data "aws_partition" "current" {} + +data "aws_region" "current" {} + +resource "tls_self_signed_cert" "ca" { + private_key_pem = tls_private_key.ca.private_key_pem + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + validity_period_hours = 12 + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] + is_ca_certificate = true +} + +resource "tls_private_key" "ca" { + algorithm = "RSA" +} + +resource "tls_cert_request" "verification" { + private_key_pem = tls_private_key.verification.private_key_pem + subject { + common_name = data.aws_iot_registration_code.test.registration_code + } +} + +resource "tls_private_key" "verification" { + algorithm = "RSA" +} + +resource "tls_locally_signed_cert" "verification" { + cert_request_pem = tls_cert_request.verification.cert_request_pem + ca_private_key_pem = tls_private_key.ca.private_key_pem + ca_cert_pem = tls_self_signed_cert.ca.cert_pem + validity_period_hours = 12 + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_iot_ca_certificate" "test" { + active = true + allow_auto_registration = true + ca_certificate_pem = tls_self_signed_cert.ca.cert_pem + certificate_mode = "DEFAULT" + verification_certificate_pem = tls_locally_signed_cert.verification.cert_pem + registration_config { + role_arn = aws_iam_role.test.arn + template_body = <