I'm trying to set up CodePipeline with an ECS blue/green deployment where the deployment is in a different AWS account.
I've been using the two guides for ECS Blue/Green and CodePipeline cross-account deployments. CodePipeline lives in Account A along with its KMS Key, S3 artifact bucket and ECR repository. The ECS cluster lives in Account B with the CodeDeploy setup.
The ECR, KMS key and S3 buckets have cross-account permissions (these give a different error when wrong). The cluster starts up and runs, and CodeDeploy works correctly when invoked inside Account B.
A role in Account B has been created for CodePipeline to assume and it has granted Account A permission to assume the role. This role currently has the AWSCodeDeployRoleForECS policy (I intend to reduce this once it works)
CodePipeline fails with an unhelpful message of
"code": "PermissionError",
"message": "The provided role does not have sufficient permissions to access CodeDeploy"
}```
The codepipeline role does have permission to access codedeploy as it's in the canned AWS policy. I can only assume there's some missing permission but I cannot find out what from this message.
I've discovered the answer after tracing through CloudTrail. There were two permissions missing from the CodePipeline deployment role which I can't find documented, they are ecs:RegisterTaskDefinition
and iam:PassRole
for the ECS Container role. CodeDeploy assumes a different role during deployment that also needs these permissions, but it looks like CodePipeline needs them to start the deployment.
The documentation I was working off had an example for CodeDeploy cross-account, but this was CodeDeploy to EC2 rather than to ECS.
My final permissions for the role assumed in Account B by CodePipeline looks like:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig",
"codedeploy:GetApplicationRevision",
"codedeploy:RegisterApplicationRevision",
"codedeploy:GetApplication",
"ecs:RegisterTaskDefinition"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"s3:GetObject*",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::deployment_intermediate_bucket/*",
"Effect": "Allow"
},
{
"Action": [ "s3:ListBucket"],
"Resource": "arn:aws:s3:::deployment_intermediate_bucket",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:GenerateDataKey*",
"kms:Encrypt",
"kms:ReEncrypt*",
"kms:Decrypt"
],
"Resource": [
"deployment_kms_key_arn"
]
},
{
"Action": [
"iam:PassRole"
],
"Effect": "Allow",
"Resource": "ecs_container_role_arn"
}
]
}
I'm going to reduce this down to the minimum required.