From 94ede2699d3e8f63f5fd01b7af6a504e50bb8a46 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 2 Oct 2020 12:44:18 -0400 Subject: [PATCH] resource/aws_ecs_cluster: Retry CreateCluster for IAM Service Linked Role eventual consistency Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/15452 Output from acceptance testing: ``` --- PASS: TestAccAWSEcsCluster_disappears (22.48s) --- PASS: TestAccAWSEcsCluster_basic (27.36s) --- PASS: TestAccAWSEcsCluster_Tags (46.73s) --- PASS: TestAccAWSEcsCluster_CapacityProviders (49.40s) --- PASS: TestAccAWSEcsCluster_CapacityProvidersNoStrategy (53.36s) --- PASS: TestAccAWSEcsCluster_containerInsights (65.48s) --- PASS: TestAccAWSEcsCluster_CapacityProvidersUpdate (83.87s) --- PASS: TestAccAWSEcsCluster_SingleCapacityProvider (90.92s) ``` --- aws/resource_aws_ecs_cluster.go | 41 +++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_ecs_cluster.go b/aws/resource_aws_ecs_cluster.go index 26ba03dd53c..eeeb5d32939 100644 --- a/aws/resource_aws_ecs_cluster.go +++ b/aws/resource_aws_ecs_cluster.go @@ -8,10 +8,12 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ecs" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "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" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" ) const ( @@ -114,25 +116,46 @@ func resourceAwsEcsClusterCreate(d *schema.ResourceData, meta interface{}) error clusterName := d.Get("name").(string) log.Printf("[DEBUG] Creating ECS cluster %s", clusterName) - input := ecs.CreateClusterInput{ - ClusterName: aws.String(clusterName), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().EcsTags(), + input := &ecs.CreateClusterInput{ + ClusterName: aws.String(clusterName), + DefaultCapacityProviderStrategy: expandEcsCapacityProviderStrategy(d.Get("default_capacity_provider_strategy").(*schema.Set)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().EcsTags(), + } + + if v, ok := d.GetOk("capacity_providers"); ok { + input.CapacityProviders = expandStringSet(v.(*schema.Set)) } if v, ok := d.GetOk("setting"); ok { input.Settings = expandEcsSettings(v.(*schema.Set)) } - if v, ok := d.GetOk("capacity_providers"); ok { - input.CapacityProviders = expandStringSet(v.(*schema.Set)) - } + // CreateCluster will create the ECS IAM Service Linked Role on first ECS provision + // This process does not complete before the initial API call finishes. + var out *ecs.CreateClusterOutput + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + var err error + out, err = conn.CreateCluster(input) - input.DefaultCapacityProviderStrategy = expandEcsCapacityProviderStrategy(d.Get("default_capacity_provider_strategy").(*schema.Set)) + if tfawserr.ErrMessageContains(err, ecs.ErrCodeInvalidParameterException, "Unable to assume the service linked role") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + out, err = conn.CreateCluster(input) + } - out, err := conn.CreateCluster(&input) if err != nil { - return err + return fmt.Errorf("error creating ECS Cluster (%s): %w", clusterName, err) } + log.Printf("[DEBUG] ECS cluster %s created", aws.StringValue(out.Cluster.ClusterArn)) d.SetId(aws.StringValue(out.Cluster.ClusterArn))