Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/apprunner_service: fix resource creation with instance role and instance_configuration #19483

Merged
merged 1 commit into from
May 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/19483.txt
Original file line number Diff line number Diff line change
@@ -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
```
32 changes: 31 additions & 1 deletion aws/resource_aws_apprunner_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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")
},
},
},
},
Expand Down Expand Up @@ -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))
Expand Down
67 changes: 26 additions & 41 deletions aws/resource_aws_apprunner_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,30 @@ 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"),
resource.TestCheckResourceAttr(resourceName, "source_configuration.0.image_repository.0.image_configuration.#", "1"),
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"),
),
},
{
Expand Down Expand Up @@ -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"),
),
},
{
Expand All @@ -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"),
),
Expand All @@ -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"),
),
},
},
Expand Down Expand Up @@ -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 = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"apprunner:*"
],
"Resource": [
"*"
]
}
]
}
EOF
}

resource "aws_iam_policy_attachment" "test" {
name = %[1]q
roles = [aws_iam_role.test.name]
policy_arn = aws_iam_policy.test.arn
}
`, rName)
}

Expand Down Expand Up @@ -642,7 +627,7 @@ resource "aws_apprunner_service" "test" {
instance_configuration {
cpu = "2 vCPU"
instance_role_arn = aws_iam_role.test.arn
memory = "4096"
memory = "4 GB"
}

source_configuration {
Expand Down