amazon-iamaws-code-deployaws-codepipelineblue-green-deployment

CodePipeline ECS Blue/Green Deployment cross account fails with PermissionError


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.

Solution

  • 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.