Search code examples
amazon-web-servicesamazon-iamamazon-ecsaws-cdkaws-codepipeline

How to create a IAM role for cross account deployment of ECS services via Code Pipeline?


I'm trying to use the EcsDeployAction and I understand for a cross account deployment I must provide a role that's created in the account with the ECS cluster:

const service = ecs.BaseService.fromServiceArnWithCluster(this, 'Service', `arn:aws:ecs:${this.region}:${ACCOUNTS.dev}:service/${CLUSTER_NAME}/${SERVICE_NAME}`);
const deploymentRole  = iam.Role.fromRoleArn(this, 'DeploymentRole', `arn:aws:iam::${ACCOUNTS.dev}:role/${DEPLOYMENT_ROLE_NAME}`)
const deploymentAction = new codepipelineActions.EcsDeployAction({
          actionName: 'deploy',
          service,
          input: buildOutputFromDockerBuildInCodeBuild,
          role: deploymentRole,
        });
  • How can I create this role(deploymentRole) with CDK(or CLI or other IAAC but not manually through console) that Codepipeline can do a deployment in another account?
  • What permissions are required for this role?
  • What should be trust/assume-role policy of the role?
  • After creating this role am I missing anything else to get this Codepipeline action working?

I found a few examples of cross account deployments on the web but couldn't find one with ECS deployment in this way that documents the IAM role creation. Codepipeline own documentaion and a few other examples of cross account deployments show a role that only has access to S3 which not what I'm deploying to.


Solution

  • I did this a few weeks ago in Terraform so happy to assist. I found this article on codepipeline the most comprehensive example I could find on how to do this. You need both a codepipeline role that can be assumed by the account your codepipeline is located in, as well as a codedeploy role that can be assumed by the codedeploy service. I am not using S3 to deploy either but the permissions are there nonetheless.

    Below is my Terraform code:

    resource "aws_iam_role" "codepipeline_cross_account" {
      name = "codepipeline-cross-account"
    
      assume_role_policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Action = "sts:AssumeRole"
            Effect = "Allow"
            Sid    = ""
            Principal = {
              AWS = "arn:aws:iam::<account_id_of_codepipeline_acct>:root"
            }
          }
        ]
      })
    
      inline_policy {
        name = "codedeploy"
    
        policy = jsonencode({
          Version = "2012-10-17"
          Statement = [
            {
              Effect : "Allow",
              Action : [
                "codedeploy:GetDeploymentConfig",
                "codedeploy:GetApplicationRevision",
                "codedeploy:RegisterApplicationRevision",
                "codedeploy:GetApplication",
                "codedeploy:GetDeployment",
                "codedeploy:CreateDeployment"
              ],
              Resource : "*"
            },
            {
              Effect : "Allow",
              Action : [
                "kms:DescribeKey",
                "kms:GenerateDataKey*",
                "kms:Encrypt",
                "kms:ReEncrypt*",
                "kms:Decrypt"
              ],
              Resource : "<kms_arn>"
            },
            {
              Effect : "Allow",
              Action : [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetObjectTagging",
                "s3:GetObjectVersionTagging"
              ],
              Resource : "<s3_arn>/*"
            },
            {
              Effect : "Allow",
              Action : [
                "ecs:RegisterTaskDefinition"
              ],
              Resource : "*"
            },
            {
              Effect : "Allow",
              Action : [
                "iam:PassRole"
              ],
              Resource : <ecs_task_execution_role_arn_goes_here>
            }
          ]
        })
      }
    }
    
    resource "aws_iam_role" "ecs_deploy" {
      name = "ecs-deploy"
    
      managed_policy_arns = ["arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS"]
    
      assume_role_policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Action = "sts:AssumeRole"
            Effect = "Allow"
            Sid    = ""
            Principal = {
              Service = "codedeploy.amazonaws.com"
            }
          }
        ]
      })
    }
    
    resource "aws_codedeploy_app" "app" {
      compute_platform = "ECS"
      name             = "app"
    }
    
    resource "aws_codedeploy_deployment_group" "example" {
      app_name               = aws_codedeploy_app.app.name
      deployment_config_name = terraform.workspace == "prod" ? "CodeDeployDefault.ECSLinear10PercentEvery1Minutes" : "CodeDeployDefault.ECSAllAtOnce"
      deployment_group_name  = "example"
      service_role_arn       = aws_iam_role.ecs_deploy.arn
    
       ..rest of code
    }
    

    Then when you create your deployment group in your application account, you will reference the ECS deployment role above as the service role. That will allow your pipeline to deploy to it.