AWS CodePipeline Example which deploys to multiple AWS Accounts - Part2
Tags: aws, codepipeline, cloudformation
Published: 2020-07-01 10:13:33

---
title: AWS CodePipeline Example which deploys to multiple AWS Accounts - Part2
published: true
date: 2020-05-12 13:30:10 UTC
tags: aws, codepipeline, cloudformation
canonical_url: https://kbild.ch/blog/2020-5-8-cf_multiple_accounts_regions_part2/
---

In [Part 1](https://kbild.ch/blog/2020-5-4-CF_multiple_accounts_regions/) you find CloudFormation templates which help you to create an AWS CodePipeline that deploys to multiple AWS Accounts. In this Part 2 we will go into some more details how these CF templates work.

* * *

### cloudformation/01central-prereqs.yaml

Let’s first look at the [01central-prereqs.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/01central-prereqs.yaml) template which creates:

[![01central-prereqs.yaml](https://kbild.ch/202005/arch1.png)](https://kbild.ch/202005/arch1.png)

- 

S3 Artifact Bucket

- 

KMS Key for encryption

- 

IAM Roles/Policies

- 

CodeCommit Repo for the App

- 

CodeBuild Project for App

Looking at the template reveals that the **Dev/Test/Prod accounts** get access to the **KMS Key**.  
In the **Central Account** CodeBuild/CodePipeline roles will create the output artifacts, which will be **encrypted** with the help of the **KMS Key**.  
Later on these artifacts will be used to deploy the application in your **Dev/Test/Prod accounts** , therefore the root in these accounts needs access to the **KMS Key** to **decrypt** the artifacts.

 ![AWS CodePipeline Example](https://kbild.ch/202005/code01.png)

Same is needed for the artifact S3 bucket, but here the roles that will be created by the other template [02prereqs-accounts.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/02prereqs-accounts.yaml) will need access to the bucket.

```
108 S3BucketPolicy:
109 Condition: AddPolicies
110 Type: AWS::S3::BucketPolicy
111 Properties:
112 Bucket: !Ref ArtifactBucket
113 PolicyDocument:
114 Statement:
115 -
116 Action:
117 - s3:GetObject
118 - s3:PutObject
119 Effect: Allow
120 Resource:
121 - !Sub arn:aws:s3:::${ArtifactBucket}
122 - !Sub arn:aws:s3:::${ArtifactBucket}/*
123 Principal:
124 AWS:
125 - !Sub arn:aws:iam::${TestAccount}:role/${Project}-CentralAcctCodePipelineCFRole
126 - !Sub arn:aws:iam::${TestAccount}:role/${Project}-cloudformationdeployer-role
127 - !Sub arn:aws:iam::${ProductionAccount}:role/${Project}-CentralAcctCodePipelineCFRole
128 - !Sub arn:aws:iam::${ProductionAccount}:role/${Project}-cloudformationdeployer-role
129 - !Sub arn:aws:iam::${DevAccount}:role/${Project}-CentralAcctCodePipelineCFRole
130 - !Sub arn:aws:iam::${DevAccount}:role/${Project}-cloudformationdeployer-role
131 - !Sub arn:aws:iam::${AWS::AccountId}:role/${Project}-codepipeline-Role
132 - !Sub arn:aws:iam::${AWS::AccountId}:role/${Project}-codebuild-Role
```

As you can see this policy is only added if the Condition "AddPolicies" is true, so for the first run of [01central-prereqs.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/01central-prereqs.yaml) this S3 bucket policy will NOT be created.  
Reason for this is that [02prereqs-accounts.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/02prereqs-accounts.yaml) has to be deployed on the **Dev/Test/Prod accounts** first to create all these needed roles.  
Right afterwards [01central-prereqs.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/01central-prereqs.yaml) has to be run a second time with "AddPolicies" parameter set to true.

> The template would fail to run if these roles are not present in the other accounts.

There are two roles per Account which will get access to the bucket, the **CentralAcctCodePipelineCFRole** and the **cloudformationdeployer-role**. Let’s switch to the next template [02prereqs-accounts.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/02prereqs-accounts.yaml) and look at these roles.

* * *

### cloudformation/02prereqs-accounts.yaml

Here you find the **CentralAcctCodePipelineCFRole** and as you can see, this will be the role which will be assumed by the **Central Account** CodePipeline to execute the CloudFormation commands.

```
17Resources:
18 CFRole:
19 Type: AWS::IAM::Role
20 Properties:
21 RoleName: !Sub ${Project}-CentralAcctCodePipelineCFRole
22 AssumeRolePolicyDocument:
23 Version: 2012-10-17
24 Statement:
25 -
26 Effect: Allow
27 Principal:
28 AWS:
29 - !Ref CentralAccount
30 Action:
31 - sts:AssumeRole
32 Path: /
```

Looking at the policy used for this role we can see that CloudFormation, S3, IAM and KMS actions are added.

```
33 CFPolicy:
34 Type: AWS::IAM::Policy
35 Properties:
36 PolicyName: !Sub ${Project}-CentralAcctCodePipelineCloudFormationPolicy
37 PolicyDocument:
38 Version: 2012-10-17
39 Statement:
40 -
41 Effect: Allow
42 Action:
43 - cloudformation:CreateStack
44 - cloudformation:DeleteStack
45 - cloudformation:UpdateStack
46 - cloudformation:DescribeStacks
47 - cloudformation:CreateChangeSet
48 - cloudformation:ExecuteChangeSet
49 - cloudformation:ListChangeSets
50 - cloudformation:DescribeChangeSet
51 - cloudformation:DeleteChangeSet
52 Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:*"
53 -
54 Effect: Allow
55 Action:
56 - s3:PutObject
57 - s3:GetObject
58 Resource:
59 - !Sub arn:aws:s3:::${S3Bucket}/*
60 - !Sub arn:aws:s3:::${S3Bucket}
61 -
62 Effect: Allow
63 Action:
64 - iam:PassRole
65 Resource: !Sub "arn:aws:iam::${AWS::AccountId}:*"
66 -
67 Effect: Allow
68 Action:
69 - kms:Decrypt
70 - kms:Encrypt
71 Resource:
72 - !Ref CMKARN
73 Roles:
74 -
75 !Ref CFRole
```

The CF actions are used to deploy the CF templates into this account, S3 actions are needed to get the artifacts from the **Central Account** and KMS actions are needed to decrypt the artifact.

Looking at the **cloudformationdeployer-role** or better the according policy we see that this role gets similar actions like **CentralAcctCodePipelineCFRole**. Supplementary some IAM, Lambda and API Gateway actions are added.

```
 91 CFDeployerPolicy:
 92 Type: AWS::IAM::Policy
 93 Properties:
 94 PolicyName: !Sub ${Project}-cloudformationdeployer-policy
 95 PolicyDocument:
 96 Version: 2012-10-17
 97 Statement:
 98 - Sid: cf
 99 Effect: Allow
100 Action:
101 - cloudformation:CreateStack
102 - cloudformation:DeleteStack
103 - cloudformation:UpdateStack
104 - cloudformation:DescribeStacks
105 - cloudformation:CreateChangeSet
106 - cloudformation:ExecuteChangeSet
107 - cloudformation:ListChangeSets
108 - cloudformation:DescribeChangeSet
109 - cloudformation:DeleteChangeSet
110 Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:*"
111 - Sid: s3
112 Effect: Allow
113 Action:
114 - s3:PutObject
115 - s3:GetBucketPolicy
116 - s3:GetObject
117 - s3:ListBucket
118 Resource:
119 - !Sub "arn:aws:s3:::${S3Bucket}/*"
120 - !Sub "arn:aws:s3:::${S3Bucket}"
121 - Sid: iam
122 Effect: Allow
123 Action:
124 - iam:CreateRole
125 - iam:DeleteRole
126 - iam:AttachRolePolicy
127 - iam:DetachRolePolicy
128 - iam:getRolePolicy
129 - iam:PutRolePolicy
130 - iam:DeleteRolePolicy
131 - iam:GetRole
132 - iam:PassRole
133 - iam:CreateServiceLinkedRole
134 Resource:
135 - !Sub "arn:aws:iam::${AWS::AccountId}:role/*"
136 - Sid: ssm
137 Effect: Allow
138 Action:
139 - ssm:GetParameters
140 Resource:
141 - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*"
142 - Sid: lambda
143 Effect: Allow
144 Action:
145 - lambda:CreateFunction
146 - lambda:DeleteFunction
147 - lambda:GetFunctionConfiguration
148 - lambda:AddPermission
149 - lambda:RemovePermission
150 - lambda:UpdateFunctionConfiguration
151 - lambda:UpdateFunctionCode
152 Resource:
153 - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:*"
154 - Sid: apigw
155 Effect: Allow
156 Action:
157 - apigateway:POST
158 - apigateway:DELETE
159 - apigateway:PATCH
160 - apigateway:GET
161 Resource:
162 - !Sub "arn:aws:apigateway:${AWS::Region}::/*"
163 Roles:
164 -
165 !Ref CFDeployerRole
```

All these actions are needed to create our application:

- 

IAM actions to create all needed roles/policies

- 

Lambda actions to create our serverless functions

- 

API Gateway actions to create endpoints of the application

If you will use this example to deploy your own applications to multiple AWS accounts this role/policy has to be customized to fit your needs.

> All actions which will be needed to deploy your application have to be added here.

Let’s look at the last template,

* * *

### cloudformation/03central-pipeline.yaml

This template which will be used in the **Central account** creates only one resource, the CodePipeline.As you can see this CodePipeline will use the KMS Key as encryption key and will use the S3 bucket as artifact store.

 ![AWS CodePipeline Example](https://kbild.ch/202005/code1.png)

We see 5 stages:

- 

Getting the source from CodeCommit

- 

Build the templates with CodeBuild

- 

Create/deploy change sets to **Dev account**

- 

Create/deploy change sets to **Test account**

- 

Create/deploy change sets to **Prod account**

CodeCommit and CodeBuild stages are easy to understand, no magic there, but let’s look at the create/deploy change sets to the different accounts:

```
 57 - Name: Create_Change_Sets_and_Deploy_to_Dev
 58 Actions:
 59 - Name: CreateChangeSet_Dev
 60 ActionTypeId:
 61 Category: Deploy
 62 Owner: AWS
 63 Version: '1'
 64 Provider: CloudFormation
 65 Configuration:
 66 ChangeSetName: cicd-codepipeline-ChangeSet-Dev
 67 ActionMode: CHANGE_SET_REPLACE
 68 StackName: cicd-codepipeline-Dev
 69 Capabilities: CAPABILITY_IAM
 70 ParameterOverrides: |
 71 {
 72 "Environment" : "dev"
 73 }
 74 TemplatePath: BuildArtifact::packaged.yml
 75 RoleArn:
 76 Fn::ImportValue:
 77 !Sub "${Project}-Dev-cloudformationdeployer-role"
 78 InputArtifacts:
 79 - Name: BuildArtifact
 80 RunOrder: 1
 81 RoleArn:
 82 Fn::ImportValue:
 83 !Sub "${Project}-Dev-centralacctcodepipelineCFRole"
 84 - Name: ExecuteChangeSet_Dev
 85 ActionTypeId:
 86 Category: Deploy
 87 Owner: AWS
 88 Provider: CloudFormation
 89 Version: "1"
 90 Configuration:
 91 ActionMode: CHANGE_SET_EXECUTE
 92 RoleArn:
 93 Fn::ImportValue:
 94 !Sub "${Project}-Dev-cloudformationdeployer-role"
 95 StackName: cicd-codepipeline-Dev
 96 ChangeSetName: cicd-codepipeline-ChangeSet-Dev
 97 OutputArtifacts:
 98 - Name: cicd-codepipeline-ChangeSet-Dev
 99 RunOrder: 2
100 RoleArn:
101 Fn::ImportValue:
102 !Sub "${Project}-Dev-centralacctcodepipelineCFRole"
```

* * *

The key point here are the values used for **RoleArn’s** and that you can define roles for the creation/execution of CloudFormation change sets.

```
81 RoleArn:
82 Fn::ImportValue:
83 !Sub "${Project}-Dev-centralacctcodepipelineCFRole"
```

This is the role ( **CentralAcctCodePipelineCFRole** ) used by the CodePipeline in the **Central account** to execute the CloudFormation template in the **Dev account**.  
  
The role was created on the **Dev account** with the [02prereqs-accounts.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/02prereqs-accounts.yaml) template but the ARN value can be calculated.  
  
This is done in the [01central-prereqs.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/01central-prereqs.yaml) template and imported here in this template [03central-pipeline.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/03central-pipeline.yaml).  
  
Snippet from [01central-prereqs.yaml](https://github.com/kbild/AWS_Cloudformation_Examples/blob/master/multipleAccountPipeline/cloudformation/01central-prereqs.yaml):

```
326 DevCodePipelineCloudFormationRole:
327 Value: !Sub arn:aws:iam::${DevAccount}:role/${Project}-CentralAcctCodePipelineCFRole
328 Export:
329 Name: !Sub ${Project}-Dev-centralacctcodepipelineCFRole
```

* * *

The second role ( **cloudformationdeployer-role** ) is used to deploy the resources inside the **Dev account** which are defined in the CloudFormation template. That’s the same IAM Role setting which you will see if you manually deploy a CloudFormation stack under "Configure stack options" → "Permissions"

```
65 Configuration:
66 ChangeSetName: cicd-codepipeline-ChangeSet-Dev
67 ActionMode: CHANGE_SET_REPLACE
68 StackName: cicd-codepipeline-Dev
69 Capabilities: CAPABILITY_IAM
70 ParameterOverrides: |
71 {
72 "Environment" : "dev"
73 }
74 TemplatePath: BuildArtifact::packaged.yml
75 RoleArn:
76 Fn::ImportValue:
77 !Sub "${Project}-Dev-cloudformationdeployer-role"
```

As you can see the RoleArn is defined inside the **Configuration** part of the Create/Execute change set. This is the role/policy we saw before which has to be customized to fit your needs if you will use this example to deploy your own applications to multiple AWS accounts.

* * *

Hope this Part 2 gave you more details on how this example works & how you can customize it.

> **The magic happens at the RoleArn that are used at the different CodePipeline stages!**

The beauty of this is that if you have a working solution like this you can reuse it almost everywhere.

Comments and questions are welcome or contact me on [Twitter](https://www.twitter.com/kbild).