CI/CD + AWS CloudFormation + Multi-Account
Tags: aws, cloudformation, cicd, codepipeline
Published: 2022-09-17 19:09:16

I want to share a practical example of how you can deploy and update your infrastructure on AWS with a **CI/CD pipeline**, **AWS CloudFormation**, and from a **root account to multiple accounts**.

If your organization has several AWS accounts, I recommend that the deployment and updating of the infrastructure be done from an account that it delegates and belongs to the organization or from its central account (Root) to the other accounts that they have according to its purpose (production, development, test, etc.) In addition, I recommend using native AWS services such as CodeCommit (private Git repository), CodeBuild (continuous integration service), CodePipeline (continuous delivery service), and CloudFormation (Infrastructure as Code).

![Figure 1: CI/CD pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1m603s2mfuj3qr3c24p.png)

**Pipeline Description:**

1. AWS CloudFormation templates should be stored in a Git repository on CodeCommit, to perform workflows that include code revisions and modifications, pull requests, and merge branches.

2. The CodePipeline service should be used for the CI/CD pipeline and to automate the environment deployment process.

3. The templates will be uploaded to a bucket in S3 so they can be accessed by the CloudFormation stacks in the destination accounts.

4. AWS CloudFormation (Stack Set) service will be used in the central account ID:111111111111 to provision and manage the resources in the target accounts: dev-ID:222222222222, prod-ID:333333333333

5. CodeBuild service must be used to perform a test and thus be able to check the availability of the deployed environment.

---
**AWS CloudFormation templates:**

I make available some AWS CloudFormation templates that I created for this DEMO. You can get them from the following repository on GitHub:

[https://github.com/frankpimienta/DEMO-CICD](https://github.com/frankpimienta/DEMO-CICD)

There is a root template called **kloudpepper-ROOT.yml**, which is used to define the parameters that will be inserted in the infrastructure to be deployed, and in turn, calls the other separate templates by types of resources or services. The templates are made this way to have an organization when modifying values or data in that particular resource. I explain it better in the previous article:

[https://dev.to/frankpimienta/aws-cloudformation-nested-stacks-9k5](https://dev.to/frankpimienta/aws-cloudformation-nested-stacks-9k5)

Also, you're going to look at a JSON file called **parameters-test.json**. This file contains the parameters that will be used in the environment to be deployed. For example, the CIDR of the VPC, the name of the environment, if it is a production or non-production environment type, and others. We will carry out the DEMO simulating a non-productive environment.

And finally, the **buildspec.yml** file, which we will use to define a set of build commands in CodeBuild and check that the environment was deployed correctly.

---
**CI/CD Configuration**

1.In the delegate or Root account that will be use to configure CI/CD pipeline, must be created S3 bucket to deposit the templates that will be called later from the CloudFormation stacks of each destination account. To better understand, you can go back to Figure 1 and look at the pipeline. To create the repository, they must put the same name they defined for the environment. This step is essential since, in the templates, there is a parameter that refers to this name. Therefore, they can't put different names on the templates' parameters and in the bucket.

Example:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xf2xppxhqlrjcp7oyp6s.png)

---

2.Insert the following policy in the bucket created. Have to change the IDs 111111111111 and 222222222222 with the correct IDs that they have in their accounts:

`{
    "Version": "2012-10-17",
    "Id": "Policy1644732224428",
    "Statement": [
        {
            "Sid": "Stmt1644732214596",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::111111111111:root",
                    "arn:aws:iam::222222222222:root"
                ]
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::kloudpepper-test/*"
        }
    ]
}`

---

3.It's necessary to create two (2) roles to delegate the administration permissions of the destination accounts to the account where the CI/CD pipeline will be configured (**Cross-Account IAM Role**) AWS has created two (2) AWS CloudFormation templates to create the roles and permissions in the accounts. I share the templates in the Git repository. So if you know to do the manual configuration, you can do it without problems. There is a lot of documentation that talks about it.

3.1.In the delegate or main (Root) account, you can execute the following template in AWS CloudFormation:
[AWSCloudFormationStackSetAdministrationRole.yml](https://github.com/frankpimienta/DEMO-CICD/blob/main/AWSCloudFormationStackSetAdministrationRole.yml)

You don't have to insert parameters.

3.2.On the target accounts, execute the following template in AWS CloudFormation:
[AWSCloudFormationStackSetExecutionRole.yml](https://github.com/frankpimienta/DEMO-CICD/blob/main/AWSCloudFormationStackSetExecutionRole.yml)

When you execute this template in AWS CloudFormation, it will insert the ID of the parent or delegate account (Root).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vaqolrh7avtoujpsvjg.png)

---

4.Create a **CodeCommit** repository:
I like configuring the repository with approval rules and branches to make pull requests and control the modification of the templates. However, we won't be making those settings for this demo. Create the repository in CodeCommit and upload the templates found on GitHub: [https://github.com/frankpimienta/DEMO-CICD](https://github.com/frankpimienta/DEMO-CICD)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f0xqrl8209uf1rpt5lsk.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5p7jrjzc49deskzyhfu.png)

---

5.Create a building project in **CodeBuild**. Add the following parameters:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31a6kdgazzvss1dpub53.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwxifg014zgli6d2f956.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b976qazxb9etscvy2hqf.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xndyq2e5sfe3ltxtb0hi.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9pyrqhh66blfmifn5a69.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c982dwny2c9waty89r4d.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pewtmlb2xcnexvvwxqzt.png)
Once the project is created, it should look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sm3gssdciqt8ggqwtdwc.png)
5.1.When you finish creating the project, a role is automatically created for its execution. It's necessary to enter the project and search for this role. A policy must then be inserted to assume the role of the target account. Follow the next steps:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1wi8cj2dcpl29uejh6r4.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqj0m1513e3xmv0xrnb8.png)
5.2.Insert the following policy inside the role. Enter the valid ID of the destination account:
`{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:iam::222222222222:role/AWSCloudFormationStackSetExecutionRole"
            ],
            "Action": [
                "sts:AssumeRole"
            ]
        }
    ]
}`

---
6.Create a pipeline in **CodePipeline**. Enter the following parameters:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bcpt52jut96dlz0t16mi.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6a4cdoome2aquklbgn5.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3enm1r2gbty2l2c2jep.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozx5z9shu9iz7xl9trs5.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x7ba7xhzu5enww7nc63g.png)
6.1.Once you finish configuring and saving the creation of the pipeline, it will automatically start its execution. Next, stop execution so you can continue configuring the pipeline:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inzmrcwxwq79f2dishih.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0nvt773dyjw5v78z4tn.png)
6.2.Edit the pipeline:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkwzkdldxjdwj1n87l1w.png)
6.3.After the Source stage, add a new stage and enter a name:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9siafd8jtr2ifh4ch6t.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ffvzroehvj2uanghf1y.png)
6.4.Add an action in the Upload_S3 stage:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67aecshboykk29iqp2kc.png)
6.5.Add the following parameters in the Upload_S3 action:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9g987b2sdjs738fytog.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybrkk4m1vs63pf7121ri.png)
6.6.Once you save the configuration in the Upload_S3 action, you have to save the stage:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4b6m6r9zgpyljcldr21.png)
6.7.After the Deploy stage, add a new stage and enter a name:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zmwwq4nvx9lovingn5q.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4fqyljzilo37plnf5ut.png)
6.8.Add an action in the Test stage:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6tti8j391cg1t4wvan3r.png)
6.9.Enter the following parameters in the Test action:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41f1w3y5sixnen2chopt.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ln26akpl9e05tvtfqhs5.png)
6.10.Once you save the configuration in the Test action, you have to save the stage:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prldosxlu10vge8q1yuu.png)
6.11.Finally, save the pipeline:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pjs48w4jldkio22lm74n.png)

---
**Run CI/CD**
7.Once you have created the pipeline in CodePipeline, the next step is to run it. Remember that we configured the pipeline so that CloudWatch Events automatically run the pipeline every time there is a change in the CodeCommit repository. To disable this automatic process, you can go to EventBridge and disable the rule.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dr1zeq825z2jnikk72o3.png)
7.1.Run the pipeline:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhj1lfc5k0epderpr95o.png)
7.2.Wait for the execution to finish and perform the validations:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tkvw813eborz5jk3c4o1.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ujjr57adggl4txp3h99.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w372gxhu8o8xk8fte845.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u97xksq4wx6e9onay1w6.png)
7.3.In the delegated or root account, review the StackSets operations:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m25ozlnlh72566fwjn8w.png)
7.4.Finally, in the destination account, check the stacks:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gv2cmpojmzlofe9w37tx.png)
I hope that this article will be of great help to you. A big hug and see you another time. Thank you!