Skip to content

[aws-codepipeline-actions] support cross account/region ecs deployment  #11199

@stocks29

Description

@stocks29

Add account/region params and setup permissions so an EcsDeployAction in a pipeline in Account A can deploy to a different region in Account B.

Use Case

To keep pipeline accounts separate from environment accounts and to support multi-region deployments.

Proposed Solution

I don't have an elegant proposed solution but I was able to achieve a workaround described below.

Other

I was able to cobble together a workaround by:

  1. copy EcsDeployAction to a new module (I called it EcsMultiAccountDeploy in examples below), adding in props for account and region which are passed to super and removing the role code.
  2. granting the deploy role from the cdk boostrap process the ability to deploy to ecs in each environment account via a cdk stack.
  3. passing the deploy role from the cdk bootstrap to the new deploy action described in step 1.

There may be a better approach but this worked for me. The reason for using the deploy role from cdk bootstrap is because it already has access to the build artifacts. Maybe there could be a way to augment the permissions of these roles during bootstrapping.

Below are the snippets of my workaround.

Important pieces from EcsMultiAccountDeploy

export interface EcsMultiAccountDeployActionProps extends EcsDeployActionProps {
  account: string | undefined;
  region: string | undefined;
}

  ...

  constructor(props: EcsMultiAccountDeployActionProps) {
    super({
      ...props,
      category: codepipeline.ActionCategory.DEPLOY,
      provider: 'ECS',
      artifactBounds: deployArtifactBounds(),
      inputs: [determineInputArtifact(props)],
      resource: props.service,
    });

    this.props = props;
  }

In the build stack which contains the pipeline:

// this is the deploy role created by the cdk bootstrap process
const deployRole = iam.Role.fromRoleArn(this, `${stageName}EcsDeployRole`, deployRoleArn);

const action = new EcsMultiAccountDeployAction({
  actionName: `${stageName}EcsDeploy`,
  service: ecsService,
  input: appArtifact,
  runOrder: stage.nextSequentialRunOrder(),
  account: env.account,
  region: env.region,
  role: deployRole,
});

stage.addActions(action);

In the application stack:

  updateDeployRole(props: UpdateDeployRoleProps) {
    const { deployRoleArn } = props;

    // this is the deploy role created by the cdk bootstrap process
    const role = iam.Role.fromRoleArn(this, 'BootstrapDeployRole', deployRoleArn);

    // the below is necessary for the ecs depoly
    role.addToPrincipalPolicy(new iam.PolicyStatement({
      actions: [
        'ecs:DescribeServices',
        'ecs:DescribeTaskDefinition',
        'ecs:DescribeTasks',
        'ecs:ListTasks',
        'ecs:RegisterTaskDefinition',
        'ecs:UpdateService',
      ],
      resources: ['*'],
    }));

    role.addToPrincipalPolicy(new iam.PolicyStatement({
      actions: ['iam:PassRole'],
      resources: ['*'],
      conditions: {
        StringEqualsIfExists: {
          'iam:PassedToService': [
            'ec2.amazonaws.com',
            'ecs-tasks.amazonaws.com',
          ],
        },
      },
    }));
  }
  • 👋 I may be able to implement this feature request
  • ⚠️ This feature might incur a breaking change

This is a 🚀 Feature Request

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions