Skip to content

Continuous Integration and Continuous Delivery (CI/CD) infrastructure built on Amazon Web Services (AWS) using CloudFormation.

License

Notifications You must be signed in to change notification settings

AttaKenn/cicd-on-aws

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI/CD on AWS

Overview

This project demonstrates a Continuous Integration and Continuous Delivery (CI/CD) pipeline built on Amazon Web Services (AWS) using CloudFormation. The pipeline automates the build, test, and deployment process for a simple calculator web application.

Warning: Trying this Project in your own AWS account will incur cost. If you are going to attempt this project in your own AWS account, make sure to delete all the stacks when you are done. The main stack deletion will fail because of non-empty S3 buckets, on the other hand other resources within the stack will be terminated/deleted. Just empty those S3 buckets (CacheBucket & ArtifactBucket) from the S3 console page and delete the stack again. Next, go to CloudWatch > Logs > Log Groups and delete the Log groups. I've got you covered too if you can't try this project in your own AWS account, just keep reading ;)

The project includes two CloudFormation templates:

  • cicd.yaml (main template): Located in the root directory, this template defines the core infrastructure for the CI/CD pipeline. It includes resources like CodeBuild project, CodePipeline for automating the release pipeline, and supporting services (totaling 40 resources). You can view the complete list of resources within the Resources section of the template. Additionally, some resources like the VPC and subnets are configured for sharing and can be reused by the second stack.

  • application.json (Application template): This template defines the infrastructure for the calculator application itself. It includes resources like Auto Scaling Groups (ASG) to deploy and manage instances that will host the application. Similar to the main template, you can view all the resources within the Resources section of the template.

CI/CD Pipeline

Source

The AWS CodePipeline Release pipeline starts on creating the CloudFormation stack. The source is a CodeCommit Repository which contains the calculator directory.

Static Code Analysis

The next stage involves a Lambda function Lambda function that performs static code analysis on the second CloudFormation template. It ensures the template doesn't include security groups allowing:

  • Ingress traffic from anywhere (0.0.0.0/0)
  • SSH access from anywhere on port 22

Build

The following stage is the build stage, utilizing AWS CodeBuild to run tests and build the application for deployment.

Test_Stack_Stage

The subsequent stage, "Test_Stack_Stage," encapsulates three sub-stages:

  1. AWS CloudFormation: test-stack-changeset [CHANGE_SET_REPLACE]: This stage leverages the second CloudFormation Template (application.json) to generate a changeset for the main stack (cicd.yaml).

  2. AWS CloudFormation: test-stack-changeset [CHANGE_SET_EXECUTE]: The generated changeset is executed, creating a new stack with a name containing "-test-stack". The resources created in this stack (including an Application Elastic Load Balancer for the Auto Scaling Groups) can be found in the Resources section of the application.json template.

  3. AWS CodeDeploy: CICD-test-stack-DeploymentGroup: The CodeDeploy agent, automatically installed on ASG instances (see LaunchConfig from the Resources section in cicd.yaml and AutoScalingGroup from the Resources section in application.json), deploys the simple calculator web application to each instances within the ASG as part of Deployment Group.

Application Health Check (Service Status)

The next stage in the pipeline is a Lambda Function that verifies the application's health and responsiveness to user requests. Here's how it works:

  • Retrieve Load Balancer URL: The Lambda function accesses the Application Load Balancer's URL, an output from the test-stack deployment.

  • Send GET Request: The function sends a GET request to the retrieved URL.

  • Validate Response: It scrutinizes the response text for the string "simple calculator service"

  • Signal CodePipeline: If the validation succeeds, signaling CodePipeline that the stage has passed. Otherwise, it fails the stage, indicating a potential issue with the application's deployment or functionality.

Manual Approval Stage

This stage introduces a human review step before deploying the application to production. A reviewer can assess the code changes that triggered the pipeline before authorizing the deployment.

Approval Process

If the reviewer approves the changes, the pipeline proceeds to:

  • Purge Test Stack: CloudFormation deletes the temporary test stack (-test-stack) created during the testing phase.

If the reviewer rejects the changes, the pipeline fails, preventing unintended deployments.

Prod_Stack_Stage (Production Deployment Stage)

This stage orchestrates the deployment of the application to production. It also utilizes three sub-stages:

  1. AWS CloudFormation: prod-stack-changeset [CHANGE_SET_REPLACE]: Similar to the testing stage, this sub-stage leverages the second CloudFormation template (application.json) to generate a changeset for the main stack (cicd.yaml).

  2. AWS CloudFormation: prod-stack-changeset [CHANGE_SET_EXECUTE]: The generated changeset is executed, creating a new stack with a name containing "-prod-stack". The resources created in this stack (including an Application Elastic Load Balancer for the Auto Scaling Groups) can be found in the Resources section of the application.json template.

  3. AWS CodeDeploy: CICD-prod-stack-DeploymentGroup: The CodeDeploy agent, automatically installed on ASG instances (see LaunchConfig from the Resources section in cicd.yaml and AutoScalingGroup from the Resources section in application.json), deploys the simple calculator web application to each instances within the ASG as part of Deployment Group.

Note: If prod-stack already exist, no changes will be made to the resources, the pipeline will quickly jump to the CodeDeploy stage which will deploy the new software update on the instances.

Below is an image depicting the architecture of the Release Pipeline:

CodePipeline Release Pipeline

CodePipeline Release Pipeline

Diving into the Project

CloudShell

I started from my AWS CloudShell where I uploaded these files into my CloudShell instance:

Note: To upload your files into your CloudShell instance, click on the "Actions" button at the top right corner of the CloudShell console and select upload file.

Creating an S3 Bucket from the CloudShell

I then created an S3 Bucket and copied the zip files into it, here are the commands:

  • aws s3 mb s3://attakenn-cicd-codebucket

  • aws s3 sync . s3://attakenn-cicd-codebucket --exclude "cicd.yaml"

The last command will sync the current working directory with the S3 bucket by copying the zip files into the S3 bucket excluding the cicd.yaml file.

Creating the CloudFormation Stack from the CloudShell

aws cloudformation create-stack --stack-name CICD --template-body file://cicd.yaml --parameters ParameterKey=codeBucket,ParameterValue=attakenn-cicd-codebucket --capabilities CAPABILITY_NAMED_IAM --disable-rollback --region us-west-2

The cicd.yaml CloudFormation template requires an S3 bucket name as an input value for the codeBucket parameter.

CAPABILITY_NAMED_IAM is added because CloudFormation will be creating some custom named IAM Resources such as IAM Roles.

See the image below (check response from CloudFormation):

CloudShell

The image below shows the CloudFormation console on creating the CICD stack.

CloudFormation console page - main stack creating

The image below shows the Artifacts and Cache buckets that have been created from the CloudFormation Template including the codebucket.

S3 Console

The image below is from the DynamoDB console displaying the DBRules table and the Rules items that has been inserted by the CFNValidate Lambda Function.

DynamoDB Rules Table

The image below is from the CodeCommit Repository console page that was created as part of the stack.

CodeCommit Repo

The image below shows from the console when the main stack's (CICD) creation is complete, you will also see the test stack (CICD-test-stack) in a CREATE_IN_PROGRESS; this means everything is going on well with our Pipeline and it had reached that stage. We will get to the pipeline soon.

CloudFormation console - main and test stack

Full console image here showing the COMPLETE SUCCESS message of some of the resources within the main stack.

CloudFormation console - main and test stack fullpage

The image below shows the outputs from the main stack (CICD). The outputs will be exported to be used by other stacks such as the test and prod stacks (check the 'Export name' field)

CloudFormation console - CICD stack outputs

The image below shows the stack parameters (Keys and corresponding Values), notice the resolved value for the LatestAmiId parameter.

CICD Stack Parameters

The image below is from the stack's release pipeline console page, it displays the source and Static_Check stages.

CICD CodePipeline console - initial pipeline

The image below displays the Build and Test_Stack stages.

CICD CodePipeline console - Build and Test_Stack Stages

The image below displays the Service_Status stage and the Pending Approval stage of the pipeline.

CICD CodePipeline console - Service_Status and Approval Stages

The image below shows the logs from the Service_Status Lambda Function from the CloudWatch Logs. The logs displays that the test passed and the web application is running fine.

CICD Service Status CloudWatch Logs

When the Test Stack status changes to CREATE_COMPLETE we can go to the Outputs tab to get the ELB Url in order to access our web application. See the image below.

CICD Test Stack 1 Outputs

The image below shows the calculator web application page.

Calculator Webpage - initial pipeline

The image below shows our Test Stack Auto Scaling Group (ASG) instances from the EC2 console page hosting our calculator application.

CICD Test Stack 1 ASG instances

The image below shows our Test Stack Auto Scaling Group and the Auto Scaling Policies from the management console.

Test Stack ASG & ASP

The image below displays the Amazon EventBridge Rule CI-CD-SourceEvent which starts the CodePipeline release pipeline execution whenever it is triggered by a change (which is an event) in the main branch of our CodeCommit Repo. Take note of the Target Name and the Role which the Rule will assume in order to start the target pipeline.

CICD SourceEvent with Target tab

The image below shows our stack's VPC from the VPC console

CI-CD VPC

Returning to the CodePipeline console, at the Approval Stage and on clicking on the Review, there's going to be a pop-up asking if we would like to Approve or Reject the build. See image below. Take note of the Trigger (user/admin); as mentioned earlier, the pipeline starts during stack creation which I (the admin user) initiated.

CICD CodePipeline Approval Stage

After approving, the pipeline moves to the Delete_Test_Stack stage which will have the Test Stack deleted (This will terminate every resource that was created from the Test Stack including the instances within the Test Stack ASG and the ELB) before moving to the Prod_Stack stage to create the Prod[uction] Stack. See the image below.

CICD CodePipeline After Approval

Now when we check the Test Stack ELB URL (Calculator App Webpage) there will be no response because the resources have been purged. See image below.

Test Stack 1 ELB Url

Moving back to the CloudFormation stacks console page, we will notice that the Test Stack (CICD-test-stack) is nowhere to be found (Delete Completed). The new discovery will be the Prod[uction] Stack (CICD-prod-stack) with the CREATE_IN_PROGRESS status. We have finally arrived the final stage[s] of our initial pipeline execution. See image below

CICD Prod Stack from initial pipeline

The image below shows the terminated instances from the Test Stack ASG and the newly launched Prod Stack ASG instances in a running state from the EC2 console page.

CICD EC2 console page - terminated Test Stack 1 instance

After CloudFormation completes the creation of the CICD-prod-stack, we can then get the Url of our Load Balancer in order to visit our application's page running in the production environment. See image below.

CICD Prod Stack Outputs from initial pipeline

On visiting the page, the Load Balancer returns a "502 Bad Gateway" response. This happened because whiles CloudFormation is done creating the stack, AWS CodeDeploy is yet to Deploy the application to the ASG EC2 instances. The pipeline is yet to be complete. See image below.

CICD Prod Stack ELB Url before CodeDeploy Actions complete

Going back to the CodePipeline console, we can notice that the pipeline execution has completed; CodeDeploy application deployment succeeded. See image below.

CICD Prod_Stack Stage completion from initial pipeline execution

Now returning back to the webpage and refreshing, we see our website live and running in our production environment (stack). See image below.

CICD Prod Stack ELB Url from initial pipeline

Next Step: Updating the Remote CodeCommit Repository

Next I will be pushing some code changes to our remote CodeCommit Repository from my local machine to trigger a new pipeline execution. Follow the steps below to connect your local repository to your remote CodeCommit repository:

  1. In your AWS Account go to IAM.

  2. Go to Users and select the IAM User for which you would like to generate an AWS CodeCommit credentials.

  3. At the selected IAM User page, look for the Security credentials tab and click on it.

  4. Scroll down to HTTPS Git Credentials for AWS CodeCommit and click on Generate credentials.

  5. Download the credentials file and keep it in a secure location.

You can then clone the repository by using the provided Url from the CodeCommit console page.

git remote add origin [URL]

git remote -v

git clone [URL]

Use the downloaded credentials to authenticate when you try to connect to the remote CodeCommit Repository.

See the image below.

CICD Local Git Repo

Next, we make some changes to the index.html file. We simply add "Version 2" to the h1 tag making it "Simple Calculator Service Version 2". See image below.

Index.html first change

Next, the new code change is committed and pushed to the remote CodeCommit Repo to start a new pipeline execution. See the 2 images below.

CICD CodePipeline Console - First trigger

CICD CodePipeline Console - Static Check in progress - First trigger

As the pipeline continues through to the Test_Stack stage and going back to the CloudFormation console page, we will see a new test stack (CICD-test-stack) in a REVIEW_IN_PROGRESS state. See image below.

CICD New Test Stack (2) from 1st Pipeline trigger

After the new test stack achieves a CREATE_COMPLETE status, we then go to the Outputs tab to get the Url provided by our load balancer in order to visit the newly updated webpage. See image below.

CICD Test Stack 2 Outputs from 1st Pipeline trigger

The image below shows the newly updated page on our Test Stack ASG instances. I have highlighted the "Version 2" from the webpage.

CICD Test Stack 2 ELB URL to Webpage

Going back to the EC2 Console page, we can see 6 instances running, 3 from the new Test Stack and 3 from the Prod Stack which was created in the initial pipeline execution. In addition to the 6 running instances, we have the 3 terminated instances from the First Test Stack which was created in the initial pipeline. See image below, as always ;)

Note that instances from the Test Stack have the name simple-calculator-Test and instances from the Prod Stack have the name simple-calculator-Prod.

CICD EC2 console page with 6 running instances

Going back to the release pipeline from the CodePipeline console page, at the Approval stage we click on Review. From the Details section, under the Trigger we can see that CloudWatchEvent - rule/CI-CD-SourceEvent triggered the pipeline execution. Compare with CICD CodePipeline Approval Stage and See image below.

CICD CodePipeline - Approval for Version 2

Clicking on the Revisions tab will provide some details on the Revision along with links to the CodeCommit Repo showcasing where the changes happened within the code. The image below shows the code change in the CodeCommit Repo.

Revision check - First Trigger

After Approving the new Build (code push), the pipeline moves to the Delete_Test_Stack stage prompting CloudFormation to purge (delete) the CICD-Test-Stack before moving on to the Prod_Stack stage. See image below.

Purging Test Stack 2 after Approval

The image below from EC2 Console shows the Test Stack ASG instances in the shutting down state, preparing to be terminated.

Test Stack 2 ASG instances Shutting Down

Once the Pipeline execution is complete, we can visit the production webpage, refresh the browser to see the newly updated application. See image below and compare with CICD Prod Stack ELB Url from initial pipeline.

Note: There has been no change with the production infrastructure. Only the application was updated. Production URL remains the same.

CICD Prod Stack ELB URL after version 2 update

Introducing code error to cause Pipeline Execution Failure

To introduce an error, I changed the value of the variable PORT in the service.js from 8080 to 80, and pushed the committed code to the remote repository. See image below.

Error code in service.js

The new Pipeline execution runs through to the Test_Stack stage where it fails at the Deploy sub-stage handled by AWS CodeDeploy. See image below.

Note: Changeset for the Test Stack was executed, successfully creating resources like the ASG instances and the ELB. You can optionally delete the test stack since the CodeDeploy deployment failed before you trigger the pipeline to start again. When you delete the CICD-test-stack, the pipeline will re-create it when it starts again. You can also leave it there, on starting the pipeline again, when it reaches the Test_Stack stage, it will quickly jump to the Deploy sub-stage. In this instance, I chose to delete the CICD-test-stack after the failed deployment.

CICD CodePipeline Console - Failed Application Deployment

Clicking on the View details will display the Action execution details. See image below.

CICD CodePipeline Console - CodeDeploy details

Under the Execution details in the pop-up, there is a View in CodeDeploy link, clicking on it will send us to CodeDeploy's console page where we can learn more about the error/failure. See image below.

CICD CodeDeploy Concole

Next we click on the CICD-test-stack-DeploymentGroup for more details about the deployment events. From the page, we can see that the Lifecycle event hook (ValidateService) failed. Check appspec.yml & validate.sh. See image below.

CICD CodeDeploy Deployment Events

Let's click on the ScriptFailed link in the Error code column to view the Event Logs. See image below.

CICD CodeDeploy Failed Event Logs

We can see from the image above that there is no running application server listening on port 8080 on the EC2 instance due to the error in the service.js.

Remember also that our Application Load Balancer is configured to forward traffic to our backend service (ASG instances) on port 8080. See the following 2 images below.

CICD-test-stack 3 Outputs from CloudFormation Console

CICD-test-stack 3 URL

Next, the CICD-test-stack is manually deleted from the CloudFormation console, the value of the PORT variable in the service.js is corrected (changed back to 8080 from 80), and the index.html is updated to "...Version 3". See image below.

index.html second change - version 3

The change is committed and then pushed to the remote CodeCommit repository to start a new pipeline execution. See the image below which shows the CICD release pipeline's executions including the just started one with the "In progress" status. Check the Triggers.

CICD CodePipeline history

The image below shows the from the console the build history for the CICD CodeBuild Project.

CICD CodeBuild History from console

The image below shows the updated calculator application webpage from the test stack, soon to be deployed to the prod stack in the pipeline.

CICD Test Stack 4 ELB URL

The image below shows the commit history of the AWS CodeCommit repository.

Repository commit history

The image below shows Prod_Stack stage from the CodePipeline console after approving the push update (build). CodeDeploy prepares to deploy the update into the CICD-prod-stack ASG intances.

CICD CodePipeline Prod_Stage in progress

The image below shows the CodeDeploy deployment history. Take note of the Initiating event field where all are User actions.

Note: Under the deployment group field/column, there are no test-stack related deployments because all CICD-test-stack-DeploymentGroups resources are deleted on deleting the CICD-test-stack stacks.

CICD CodeDeploy deployment history

The image below shows the updated webpage in the prod stack. The pipeline execution has completed.

CICD Prod Stack calculator application webpage

The image below shows the Execution history of the CI-CD-releasePipeline.

CICD CodePipeline detailed Exectution history

Terminating the Prod Stack EC2 instances

Everything is working perfectly now, Let's see what happens if the Prod[uction] Stack ASG EC2 instances are terminated from the EC2 console. See image below.

Terminating Prod Stack ASG instances

The image below shows the response after terminating all the instances hosting the calculator application.

Note: This response is from the Application ELB. It forwards the traffic to the ASG, and now there are no instances in the ASG to handle the requests.

Page Response after terminating Prod Stack ASG instances

Note that after deleting the ASG instances, CloudFormation does not detect any drift in the CICD-prod-stack. The instances are managed by the Auto Scaling Group and not CloudFormation; they are not defined in the Resources section in the CloudFormation template. See image below

CICD-prod-stack drift results

After about a minute or 2, the Auto Scaling kicks in launching a new instance. See image below

EC2 console page - newly launched EC2 vm instance

Now unlike the previously terminated Prod Stack instances, the newly launched instance by the Auto Scaling Group does not have the calculator application installed and running. So one might ask, how do we get the application installed and started on the newly launched instance? Are we to trigger the pipeline again? The simple answer is there's no need for all that. In the Resources section of the application.json CloudFormation template there is a CodeDeploy DeploymentGroup and the ASG is attached to it. On launching a new instance, the ASG will automatically initiate a CodeDeploy deployment event. See image below and take note of the Initiating event field.

CICD CodeDeploy deployment history

On returning to the production page and refreshing the browser, we have the application running; backed by a single ASG instance for now. See image below.

Page response after ASG first action

SInce the ASG was configured to have a minimum instance number of 3, by default, after the first instance was launched, a new instance is launched in a 2 minute interval until minimum instance number (3) is achieved. See image below.

EC2 Console with 3 new running instances

The image below shows the CodeDeploy deployment history after the 3 ASG actions.

CICD CodeDeploy deployment history after 3rd ASG action

Deleting Auto Scaling Group

Let's see what happens when the ASG is deleted. See image below.

CICD-prod-stack ASG deletion

The image below shows the ELB response from the calculator page after deleting the ASG.

Page response after ASG deletion

Note: Although the instances in the Auto Scaling Group remain in a running state, with the application also running in them, the application cannot serve any requests from the ELB because it is the ASG itself that has been configured as the ELB backend target group, not the individual ASG EC2 instances.

The image below shows the Auto Scaling Group in Deleting state whiles the number of running instances remains 3.

CICD-prod-stack ASG console page with deleting status

The image below shows from the EC2 console the number of running instances.

EC2 console page

The image below shows the drift results of the CICD-prod-stack from the CloudFormation console page. From the image we can see the drift status of AutoScalingGroup is DELETED.

CICD-prod-stack CFN console drift results

The image below shows the Log groups created by the Lambda Functions and AWS CodeBuild which provide details about the operations of these services.

CICD CloudWatch Log Groups

Please do not forget to delete the stack(s) upon completion

Deleting main stack - CICD