diff --git a/.changelog/19483.txt b/.changelog/19483.txt new file mode 100644 index 00000000000..be4cf576068 --- /dev/null +++ b/.changelog/19483.txt @@ -0,0 +1,7 @@ +```release-note:bug +resource/aws_apprunner_service: Handle asynchronous IAM eventual consistency error on creation +``` + +```release-note:bug +resource/aws_apprunner_service: Suppress `instance_configuration` `cpu` and `memory` differences +``` diff --git a/aws/resource_aws_apprunner_service.go b/aws/resource_aws_apprunner_service.go index e953e03f08f..7021e786e4c 100644 --- a/aws/resource_aws_apprunner_service.go +++ b/aws/resource_aws_apprunner_service.go @@ -10,10 +10,13 @@ import ( "github.com/aws/aws-sdk-go/service/apprunner" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/apprunner/waiter" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsAppRunnerService() *schema.Resource { @@ -123,6 +126,10 @@ func resourceAwsAppRunnerService() *schema.Resource { Optional: true, Default: "1024", ValidateFunc: validation.StringMatch(regexp.MustCompile(`1024|2048|(1|2) vCPU`), ""), + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // App Runner API always returns the amount in multiples of 1024 units + return (old == "1024" && new == "1 vCPU") || (old == "2048" && new == "2 vCPU") + }, }, "instance_role_arn": { Type: schema.TypeString, @@ -134,6 +141,10 @@ func resourceAwsAppRunnerService() *schema.Resource { Optional: true, Default: "2048", ValidateFunc: validation.StringMatch(regexp.MustCompile(`2048|3072|4096|(2|3|4) GB`), ""), + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // App Runner API always returns the amount in MB + return (old == "2048" && new == "2 GB") || (old == "3072" && new == "3 GB") || (old == "4096" && new == "4 GB") + }, }, }, }, @@ -366,7 +377,26 @@ func resourceAwsAppRunnerServiceCreate(ctx context.Context, d *schema.ResourceDa input.InstanceConfiguration = expandAppRunnerServiceInstanceConfiguration(v.([]interface{})) } - output, err := conn.CreateServiceWithContext(ctx, input) + var output *apprunner.CreateServiceOutput + + err := resource.RetryContext(ctx, iamwaiter.PropagationTimeout, func() *resource.RetryError { + var err error + output, err = conn.CreateServiceWithContext(ctx, input) + + if tfawserr.ErrMessageContains(err, apprunner.ErrCodeInvalidRequestException, "Error in assuming instance role") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + output, err = conn.CreateServiceWithContext(ctx, input) + } if err != nil { return diag.FromErr(fmt.Errorf("error creating App Runner Service (%s): %w", serviceName, err)) diff --git a/aws/resource_aws_apprunner_service_test.go b/aws/resource_aws_apprunner_service_test.go index 434c3692655..aaa707882aa 100644 --- a/aws/resource_aws_apprunner_service_test.go +++ b/aws/resource_aws_apprunner_service_test.go @@ -93,6 +93,21 @@ func TestAccAwsAppRunnerService_ImageRepository_basic(t *testing.T) { testAccCheckAwsAppRunnerServiceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "service_name", rName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "apprunner", regexp.MustCompile(fmt.Sprintf(`service/%s/.+`, rName))), + testAccMatchResourceAttrRegionalARN(resourceName, "auto_scaling_configuration_arn", "apprunner", regexp.MustCompile(`autoscalingconfiguration/DefaultConfiguration/1/.+`)), + resource.TestCheckResourceAttr(resourceName, "health_check_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "health_check_configuration.0.protocol", apprunner.HealthCheckProtocolTcp), + resource.TestCheckResourceAttr(resourceName, "health_check_configuration.0.path", "/"), + // Only check the following attribute values for health_check and instance configurations + // are set as their defaults differ in the API documentation and API itself + resource.TestCheckResourceAttrSet(resourceName, "health_check_configuration.0.interval"), + resource.TestCheckResourceAttrSet(resourceName, "health_check_configuration.0.timeout"), + resource.TestCheckResourceAttrSet(resourceName, "health_check_configuration.0.healthy_threshold"), + resource.TestCheckResourceAttrSet(resourceName, "health_check_configuration.0.unhealthy_threshold"), + resource.TestCheckResourceAttr(resourceName, "instance_configuration.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "instance_configuration.0.cpu"), + resource.TestCheckResourceAttrSet(resourceName, "instance_configuration.0.memory"), + resource.TestCheckResourceAttrSet(resourceName, "service_id"), + resource.TestCheckResourceAttrSet(resourceName, "service_url"), resource.TestCheckResourceAttr(resourceName, "source_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "source_configuration.0.auto_deployments_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "source_configuration.0.image_repository.#", "1"), @@ -100,6 +115,8 @@ func TestAccAwsAppRunnerService_ImageRepository_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "source_configuration.0.image_repository.0.image_configuration.0.port", "8000"), resource.TestCheckResourceAttr(resourceName, "source_configuration.0.image_repository.0.image_identifier", "public.ecr.aws/jg/hello:latest"), resource.TestCheckResourceAttr(resourceName, "source_configuration.0.image_repository.0.image_repository_type", apprunner.ImageRepositoryTypeEcrPublic), + resource.TestCheckResourceAttr(resourceName, "status", apprunner.ServiceStatusRunning), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -245,9 +262,9 @@ func TestAccAwsAppRunnerService_ImageRepository_InstanceConfiguration(t *testing Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppRunnerServiceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "instance_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.cpu", "1 vCPU"), + resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.cpu", "1024"), resource.TestCheckResourceAttrPair(resourceName, "instance_configuration.0.instance_role_arn", roleResourceName, "arn"), - resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.memory", "3 GB"), + resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.memory", "3072"), ), }, { @@ -260,7 +277,7 @@ func TestAccAwsAppRunnerService_ImageRepository_InstanceConfiguration(t *testing Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppRunnerServiceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "instance_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.cpu", "2 vCPU"), + resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.cpu", "2048"), resource.TestCheckResourceAttrPair(resourceName, "instance_configuration.0.instance_role_arn", roleResourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.memory", "4096"), ), @@ -274,7 +291,9 @@ func TestAccAwsAppRunnerService_ImageRepository_InstanceConfiguration(t *testing Config: testAccAppRunnerService_imageRepository(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsAppRunnerServiceExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "instance_configuration.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_configuration.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "instance_configuration.0.cpu"), + resource.TestCheckResourceAttrSet(resourceName, "instance_configuration.0.memory"), ), }, }, @@ -560,48 +579,14 @@ resource "aws_iam_role" "test" { "Sid": "", "Effect": "Allow", "Principal": { - "Service": [ - "apprunner.${data.aws_partition.current.dns_suffix}" - ] + "Service": "tasks.apprunner.${data.aws_partition.current.dns_suffix}" }, - "Action": [ - "sts:AssumeRole" - ] + "Action": "sts:AssumeRole" } ] } EOF } - -resource "aws_iam_policy" "test" { - name = %[1]q - path = "/" - description = "App Runner PassRole Policy" - - policy = <