Cross account deployment

Allow pulling Docker images from other AWS accounts

Update the cloudformation template in cloudformation/ecr.yml and define a policy for your ECR repository:

---
AWSTemplateFormatVersion: 2010-09-09
Description: ECR repository for '...'

Parameters:
  RepositoryName:
    Type: String
    Description: 'Name of the ECR repository'

Resources:
  Repository:
    Type: 'AWS::ECR::Repository'
    Properties:
      RepositoryName: !Ref RepositoryName
      RepositoryPolicyText:
        Version: "2012-10-17"
        Statement:
          - Sid: AllowDockerPull
            Effect: Allow
            Principal:
              AWS:
                - "arn:aws:iam::AWS_ACCOUNT1:root"
                - "arn:aws:iam::AWS_ACCOUNT2:root"
            Action:
              - "ecr:GetDownloadUrlForLayer"
              - "ecr:BatchGetImage"
              - "ecr:BatchCheckLayerAvailability"

Make sure to replace AWS_ACCOUNT1, AWS_ACCOUNT2, etc. with the correct account numbers.

Update the Cloudformation stack with the following command in the ci AWS account

AWS_PROFILE=myorg-ci make ecr

Creating new roles for cross account access in the Pipeline

Create a new Cloudformation template cloudformation/deployment-role.yml with the following content:

---
AWSTemplateFormatVersion: 2010-09-09
Description: Deployment IAM role for '...'

Parameters:
  CiAwsAccountId:
    Type: String
    AllowedPattern: '^[0-9]*$'
    Description: ID of the CI AWS account

Resources:
  RoleAdministrator:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref AWS::StackName
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${CiAwsAccountId}:root'
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess

Enhance your project Makefile in the project directory with the following content:


.PHONY: deployment-role
deployment-role: guard-STAGE
	aws cloudformation deploy \
		--no-fail-on-empty-changeset \
		--template-file cloudformation/deployment-role.yml \
		--stack-name $(SERVICE)-$(STAGE)-deployment-role \
		--parameter-overrides CiAwsAccountId=$(CI_AWS_ACCOUNT_ID) \
		--region $(AWS_REGION)

Create the Cloudformation stack with the following command in the app-staging AWS account

AWS_PROFILE=myorg-app-staging make deployment-role STAGE=dev

Assuming the roles in the Pipeline

Create a new file assume-role.sh in your project:

#!/usr/bin/env bash -e

if [ $# -le 2 ]; then
    echo "Error: invalid number of parameters"
    exit 1
fi

ROLE_TO_ASSUME=$1
shift;

ROLE=(`aws sts assume-role --role-arn "${ROLE_TO_ASSUME}" --role-session-name "${ROLE_TO_ASSUME}-$(date +%s)" \
         --output text --query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]'`)

export AWS_ACCESS_KEY_ID="${ROLE[0]}"
export AWS_SECRET_ACCESS_KEY="${ROLE[1]}"
export AWS_SECURITY_TOKEN="${ROLE[2]}"
export AWS_SESSION_TOKEN="${ROLE[2]}"

exec $@

Set the executable bit on the file:

chmod +x ./assume-role.sh

Change your buildspec_deploy.yml as follows:

version: 0.2

env:
  variables:
    ROLE_NAME: "java-docker-aws-example-fstehle-dev-deployment-role"

phases:
  install:
    runtime-versions:
      java: openjdk8
      docker: 18
    commands:
      - pip install awsebcli
  build:
    commands:
      - echo "Setting up the database and the Elastic Beanstalk app"
      - ./assume-role.sh arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME} make database app STAGE=dev
      - echo "Deploying Docker container to the Elastic Beanstalk app"
      - ./assume-role.sh arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME} make deploy STAGE=dev

Make sure to commit and push everything to the repository:

git add buildspec_deploy.yml
git commit -m 'Enhance pipeline by setting up the database and the Elastic Beanstalk app'

Then push to your branch

git push ...

Log in to the AWS Console, switch to the CI account and trigger your pipeline.