diff --git a/lib/cloud/aws/policy_statements.go b/lib/cloud/aws/policy_statements.go index 45975cd6a6c..ead15df7700 100644 --- a/lib/cloud/aws/policy_statements.go +++ b/lib/cloud/aws/policy_statements.go @@ -65,6 +65,9 @@ func StatementForECSManageService() *Statement { // EC2 DescribeSecurityGroups is required so that the user can list the SG and then pick which ones they want to apply to the ECS Service. "ec2:DescribeSecurityGroups", + + // IAM CreateServiceLinkedRole is required to ensure the ECS Service linked role exists. + "iam:CreateServiceLinkedRole", }, Resources: allResources, } diff --git a/lib/integrations/awsoidc/deployservice.go b/lib/integrations/awsoidc/deployservice.go index 69a3f409e83..0906b056ee7 100644 --- a/lib/integrations/awsoidc/deployservice.go +++ b/lib/integrations/awsoidc/deployservice.go @@ -26,6 +26,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/retry" "github.com/aws/aws-sdk-go-v2/service/ecs" ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" "github.com/aws/aws-sdk-go-v2/service/sts" @@ -557,6 +558,22 @@ func upsertCluster(ctx context.Context, clt DeployServiceClient, clusterName str ClusterName: aws.String(clusterName), CapacityProviders: requiredCapacityProviders, Tags: resourceCreationTags.ToECSTags(), + }, func(o *ecs.Options) { + o.Retryer = retry.NewStandard(func(so *retry.StandardOptions) { + so.MaxAttempts = 10 + so.MaxBackoff = time.Minute + // Retry if an error is a missing ECS service-linked role. + // This is a retryable error because the ECS service-linked role + // will be created automatically when the caller has + // iam:CreateServiceLinkedRole permission (we should). + // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using-service-linked-roles.html#create-slr + so.Retryables = append(so.Retryables, retry.IsErrorRetryableFunc(func(err error) aws.Ternary { + if err != nil && strings.Contains(err.Error(), "verify that the ECS service linked role exists") { + return aws.TrueTernary + } + return aws.FalseTernary + })) + }) }) if err != nil { return nil, trace.Wrap(err)