DEPLOY AWS CLOUDFORMATION STACK CROSS ACCOUNT — CROSS REGION USING AWS CODEPIPELINE

Atit Shah
6 min readMar 3, 2021

Short description

To deploy an AWS CloudFormation stack in a different account, you must complete the following:

  1. Create a pipeline in one account, account A. This account should include:

a) A customer-managed AWS Key Management Service (AWS KMS) key,

b) Amazon S3 bucket for artifacts, and

c) S3 bucket policy that allows access from the other account, account B.

2. In account B, configure a cross-account service role that allows the following:

a) AWS CloudFormation actions

b) Access to the S3 bucket in account A, and

c) Decryption with the customer-managed KMS key in account A.

3. In account A, allow a pipeline service role to assume a cross-account role (with AssumeRole) in account B.

Now we will implement the above steps in detail.

Step 1: In account A

a) Create a Customer Managed Key

  • Go to AWS KMS Console
  • Choose Customer managed keys
  • Select Create key and then Symmetric. (In the Advanced options, keep the Key Material Origin as KMS)
  • For Alias, enter your Key Name
  • On Define key administrative permissions page, for Key administrators, choose your AWS IAM user and any other users or groups that you want to serve as administrators for the key, and then choose Next.
  • On the Define key usage permissions page, for this account, add the IAM users/role in the account that should have access to s3 and cross-account access (such as the CodePipeline service role). Click here to check how to create AWS CODEPIPELINE SERVICE ROLE.
  • In the Other AWS accounts section, choose Add another AWS account, and then enter the ARN of the IAM role in account B.
  • Choose Next, and then choose Finish.
  • In the Customer managed keys section, choose the key that you just created, and then copy the ARN for that key.

b) Create an S3 bucket as SourceArtifact for Pipeline. The s3 bucket policy in Account A must allow access for Account B

  • In Account A, open the S3 console
  • Choose an existing s3 bucket or create a new s3 bucket to use as ArtifactStore for CodePipeline.

Note: Artifacts can include the stack template file, template configuration file or both. CodePipeline uses these artifacts to work with AWS CloudFormation stacks and changesets. In your template configuration file, you must specify template parameter values, a stack policy, and tags

  • Select Permissions
  • Choose Bucket Policy. Add the below policy in the editor
{
"Id": "Policy1553183091390",
"Version": "2012-10-17",
"Statement": [{
"Sid": "",
"Action": [
"s3:Get*",
"s3:Put*"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::source-artifacts-cross-account-codepipeline/*",
"Principal": {
"AWS": [
"arn:aws:iam::AccountB:root"
]
}
},
{
"Sid": "",
"Action": [
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::source-artifacts-cross-account-codepipeline",
"Principal": {
"AWS": [
"arn:aws:iam::AccountB:root"
]
}
}
]
}

In the above code replace source-artifacts-cross-account-codepipeline with s3 bucket having your SourceArtifact and AccountB with AWS account no.

  • Click Save.

Step 2: In account B

Create a cross-account role that allows actions related to s3 and KMS in the SourceArtifcat for Account A (CROSS_ACCOUNT_ROLE)

The cross-account role policy allows the pipeline in Account A to assume a role in Account B. This policy also enables the AWS Cloudformation actions and access to perform operations related to AWS KMS

  • Open IAM console.
  • Select Policies, in the left navigation pane
  • Click Create Policy.
  • Choose JSON, and then enter the following policy into the editor.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudformation:*",
"iam:PassRole"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:Put*",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::source-artifacts-cross-account-codepipeline/*"
]
}
]
}

In the above code replace source-artifacts-cross-account-codepipeline with an s3 bucket having your SourceArtifact

  • Choose Review and then create policy.

Now we need to create the Policy that will give access to perform related KMS

  • Open IAM console.
  • Select Policies, in the left navigation pane
  • Click Create Policy.
  • Choose JSON, and then enter the following policy into the editor.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:GenerateDataKey*",
"kms:Encrypt",
"kms:ReEncrypt*",
"kms:Decrypt"
],
"Resource": [
"arn:aws:kms:Region:AccountA:key/key-id"
]
}
]
}

In the above code replace the AWS KMS ARN for the highlighted part.

  • Choose Review and then create policy.

Step 3: In account A

We need to Add the AssumeRole permission to the CodePipeline service role.

  • Open IAM console.
  • Select Roles, in the left navigation pane
  • Click IAM Service Role that we have created previously for Codepipeline
  • Choose Add inline policy.
  • Choose JSON, and then enter the following policy into the editor.
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::AccountB:role/*"
]
}
}

In the above code replace the AccountB with AWS account no

  • Choose Review and then create policy.

Step 4: In account B

Create a role for Cloudformation Stack to deploy the services on your behalf. (CFN_STACK_ROLE)

  • Open IAM console.
  • Select Roles, in the left navigation pane
  • Create a role for AWS CloudFormation to use when launching services on your behalf
  • Apply permissions to your role based on your needs.

Note: You can’t use the CodePipeline console to create or edit a pipeline that uses resources associated with another account. However, you can use the console to create the general structure of the pipeline. You can then use the AWS CLI to edit the pipeline and add the resources associated with the other account. Or, you can update a current pipeline with the resources for the new pipeline.

  • In AWS CLI run the below command
aws codepipeline get-pipeline --name MyFirstPipeline >pipeline.json
  • In your local pipeline.json file, confirm that the encryptionKey ID contains the ID with the ARN of the key that you created earlier.
  • The RoleArn inside the action configuration your SJSON structure is the role for the AWS CloudFormation stack (CFN_STACK_ROLE). The roleArn outside the action configuration JSON structure is the cross-account role that the pipeline assumes to operate an AWS CloudFormation stack (CROSS_ACCOUNT_ROLE). Verify both the roleArn.
{
"pipeline": {
"name": "CROSS-ACCT-PIPELINE-DEMO",
"roleArn": "arn:aws:iam::AccountA:role/CROSS-ACCOUNT-PIPELINE-SERVICE-ROLE",
"artifactStores": {
"Region1": {
"type": "S3",
"location": "artifact-replication-bucket",
"encryptionKey": {
"id": "arn:aws:kms:Region1:AccountA:key/key-id",
"type": "KMS"
}
},
"Region2": {
"type": "S3",
"location": "source-artifacts-cross-account-codepipeline",
"encryptionKey": {
"id": "arn:aws:kms:Region2:AccountA:key/key-id",
"type": "KMS"
}
}
},
"stages": [
{
"name": "Source",
"actions": [
{
"name": "Source",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1"
},
"runOrder": 1,
"configuration": {
"PollForSourceChanges": "false",
"S3Bucket": "codepipeline-src",
"S3ObjectKey": "cf-s3-basic-json.zip"
},
"outputArtifacts": [
{
"name": "SourceArtifact"
}
],
"inputArtifacts": [],
"region": "Region"
}
]
},
{
"name": "Deploy",
"actions": [
{
"name": "Create_Change_Set",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CloudFormation",
"version": "1"
},
"runOrder": 1,
"configuration": {
"ActionMode": "CREATE_UPDATE",
"Capabilities": "CAPABILITY_IAM",
"RoleArn": "arn:aws:iam::AccountB:role/CFN_STACK_ROLE",
"StackName": "DEMO-CROSS-ACC-S3-STACK",
"TemplatePath": "SourceArtifact::cf-s3-basic.json"
},
"outputArtifacts": [],
"inputArtifacts": [
{
"name": "SourceArtifact"
}
],
"roleArn": "arn:aws:iam::AccountB:role/CROSS_ACCOUNT_ROLE",
"region": "Region"
}
]
}
]
}
}

Remove the metadata from pipeline.json

Since we have to deploy the cross-region/cross-account CFT, the s3 Bucket must be present in the region where you wish to deploy CFT, with bucket encryption enabled using KMS. The replication of the artifacts is taken care of by AWS.

You must include either artifactStore or artifactStores in your pipeline, but you cannot use both. If you create a cross-region action in your pipeline, you must use artifactStores. Ref this for more details.

To create a pipeline and update the JSON structure, run the following command to update the pipeline with the new configuration file:

aws codepipeline update-pipeline --cli-input-json file://pipeline.json

References:

--

--