AWS DevOps & Developer Productivity Blog
Complete CI/CD with AWS CodeCommit, AWS CodeBuild, AWS CodeDeploy, and AWS CodePipeline
Many organizations have been shifting to DevOps practices, which is the combination of cultural philosophies, practices, and tools that increases your organization’s ability to deliver applications and services at high velocity; for example, evolving and improving products at a faster pace than organizations using traditional software development and infrastructure management processes.
An integral part of DevOps is adopting the culture of continuous integration and continuous delivery/deployment (CI/CD), where a commit or change to code passes through various automated stage gates, all the way from building and testing to deploying applications, from development to production environments.
This post uses the AWS suite of CI/CD services to compile, build, and install a version-controlled Java application onto a set of Amazon Elastic Compute Cloud (Amazon EC2) Linux instances via a fully automated and secure pipeline. The goal is to promote a code commit or change to pass through various automated stage gates all the way from development to production environments, across AWS accounts.
AWS services
This solution uses the following AWS services:
- AWS CodeCommit – A fully-managed source control service that hosts secure Git-based repositories. CodeCommit makes it easy for teams to collaborate on code in a secure and highly scalable ecosystem. This solution uses CodeCommit to create a repository to store the application and deployment codes.
- AWS CodeBuild – A fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy, on a dynamically created build server. This solution uses CodeBuild to build and test the code, which we deploy later.
- AWS CodeDeploy – A fully managed deployment service that automates software deployments to a variety of compute services such as Amazon EC2, AWS Fargate, AWS Lambda, and your on-premises servers. This solution uses CodeDeploy to deploy the code or application onto a set of EC2 instances running CodeDeploy agents.
- AWS CodePipeline – A fully managed continuous delivery service that helps you automate your release pipelines for fast and reliable application and infrastructure updates. This solution uses CodePipeline to create an end-to-end pipeline that fetches the application code from CodeCommit, builds and tests using CodeBuild, and finally deploys using CodeDeploy.
- AWS CloudWatch Events – An AWS CloudWatch Events rule is created to trigger the CodePipeline on a Git commit to the CodeCommit repository.
- Amazon Simple Storage Service (Amazon S3) – An object storage service that offers industry-leading scalability, data availability, security, and performance. This solution uses an S3 bucket to store the build and deployment artifacts created during the pipeline run.
- AWS Key Management Service (AWS KMS) – AWS KMS makes it easy for you to create and manage cryptographic keys and control their use across a wide range of AWS services and in your applications. This solution uses AWS KMS to make sure that the build and deployment artifacts stored on the S3 bucket are encrypted at rest.
Overview of solution
This solution uses two separate AWS accounts: a dev account (111111111111) and a prod account (222222222222) in Region us-east-1.
We use the dev account to deploy and set up the CI/CD pipeline, along with the source code repo. It also builds and tests the code locally and performs a test deploy.
The prod account is any other account where the application is required to be deployed from the pipeline in the dev account.
In summary, the solution has the following workflow:
- A change or commit to the code in the CodeCommit application repository triggers CodePipeline with the help of a CloudWatch event.
- The pipeline downloads the code from the CodeCommit repository, initiates the Build and Test action using CodeBuild, and securely saves the built artifact on the S3 bucket.
- If the preceding step is successful, the pipeline triggers the Deploy in Dev action using CodeDeploy and deploys the app in dev account.
- If successful, the pipeline triggers the Deploy in Prod action using CodeDeploy and deploys the app in the prod account.
The following diagram illustrates the workflow:
Failsafe deployments
This example of CodeDeploy uses the IN_PLACE type of deployment. However, to minimize the downtime, CodeDeploy inherently supports multiple deployment strategies. This example makes use of following features: rolling deployments and automatic rollback.
CodeDeploy provides the following three predefined deployment configurations, to minimize the impact during application upgrades:
- CodeDeployDefault.OneAtATime – Deploys the application revision to only one instance at a time
- CodeDeployDefault.HalfAtATime – Deploys to up to half of the instances at a time (with fractions rounded down)
- CodeDeployDefault.AllAtOnce – Attempts to deploy an application revision to as many instances as possible at once
For OneAtATime and HalfAtATime, CodeDeploy monitors and evaluates instance health during the deployment and only proceeds to the next instance or next half if the previous deployment is healthy. For more information, see Working with deployment configurations in CodeDeploy.
You can also configure a deployment group or deployment to automatically roll back when a deployment fails or when a monitoring threshold you specify is met. In this case, the last known good version of an application revision is automatically redeployed after a failure with the new application version.
How CodePipeline in the dev account deploys apps in the prod account
In this post, the deployment pipeline using CodePipeline is set up in the dev account, but it has permissions to deploy the application in the prod account. We create a special cross-account role in the prod account, which has the following:
- Permission to use fetch artifacts (app) rom Amazon S3 and deploy it locally in the account using CodeDeploy
- Trust with the dev account where the pipeline runs
CodePipeline in the dev account assumes this cross-account role in the prod account to deploy the app.
Do I need multiple accounts?
If you answer “yes” to any of the following questions you should consider creating more AWS accounts:
- Does your business require administrative isolation between workloads? Administrative isolation by account is the most straightforward way to grant independent administrative groups different levels of administrative control over AWS resources based on workload, development lifecycle, business unit (BU), or data sensitivity.
- Does your business require limited visibility and discoverability of workloads? Accounts provide a natural boundary for visibility and discoverability. Workloads cannot be accessed or viewed unless an administrator of the account enables access to users managed in another account.
- Does your business require isolation to minimize blast radius? Separate accounts help define boundaries and provide natural blast-radius isolation to limit the impact of a critical event such as a security breach, an unavailable AWS Region or Availability Zone, account suspensions, and so on.
- Does your business require a particular workload to operate within AWS service limits without impacting the limits of another workload? You can use AWS account service limits to impose restrictions on a business unit, development team, or project. For example, if you create an AWS account for a project group, you can limit the number of Amazon Elastic Compute Cloud (Amazon EC2) or high performance computing (HPC) instances that can be launched by the account.
- Does your business require strong isolation of recovery or auditing data? If regulatory requirements require you to control access and visibility to auditing data, you can isolate the data in an account separate from the one where you run your workloads (for example, by writing AWS CloudTrail logs to a different account).
Prerequisites
For this walkthrough, you should complete the following prerequisites:
- Have access to at least two AWS accounts. For this post, the dev and prod accounts are in us-east-1. You can search and replace the Region and account IDs in all the steps and sample AWS Identity and Access Management (IAM) policies in this post.
- Ensure you have EC2 Linux instances with the CodeDeploy agent installed in all the accounts or VPCs where the sample Java application is to be installed (dev and prod accounts).
- To manually create EC2 instances with CodeDeploy agent, refer Create an Amazon EC2 instance for CodeDeploy (AWS CLI or Amazon EC2 console). Keep in mind the following:
- CodeDeploy uses EC2 instance tags to identify instances to use to deploy the application, so it’s important to set tags appropriately. For this post, we use the tag name Application with the value MyWebApp to identify instances where the sample app is installed.
- Make sure to use an EC2 instance profile (AWS Service Role for EC2 instance) with permissions to read the S3 bucket containing artifacts built by CodeBuild. Refer to the IAM role cicd_ec2_instance_profile in the table Roles-1 below for the set of permissions required. You must update this role later with the actual KMS key and S3 bucket name created as part of the deployment process.
- To create EC2 Linux instances via AWS Cloudformation, download and launch the AWS CloudFormation template from the GitHub repo: cicd-ec2-instance-with-codedeploy.json
- This deploys an EC2 instance with AWS CodeDeploy agent.
- Inputs required:
- AMI : Enter name of the Linux AMI in your region. (This template has been tested with latest Amazon Linux 2 AMI)
- Ec2SshKeyPairName: Name of an existing SSH KeyPair
- Ec2IamInstanceProfile: Name of an existing EC2 instance profile. Note: Use the permissions in the template cicd_ec2_instance_profile_policy.json to create the policy for this EC2 Instance Profile role. You must update this role later with the actual KMS key and S3 bucket name created as part of the deployment process.
- Update the EC2 instance Tags per your need.
- To manually create EC2 instances with CodeDeploy agent, refer Create an Amazon EC2 instance for CodeDeploy (AWS CLI or Amazon EC2 console). Keep in mind the following:
- Ensure required IAM permissions. Have an IAM user with an IAM Group or Role that has the following access levels or permissions:
AWS Service / Components Access Level Accounts Comments AWS CodeCommit Full (admin) Dev Use AWS managed policy AWSCodeCommitFullAccess. AWS CodePipeline Full (admin) Dev Use AWS managed policy AWSCodePipelineFullAccess. AWS CodeBuild Full (admin) Dev Use AWS managed policy AWSCodeBuildAdminAccess. AWS CodeDeploy Full (admin) All Use AWS managed policy
AWSCodeDeployFullAccess.
Create S3 bucket and bucket policies Full (admin) Dev IAM policies can be restricted to specific bucket. Create KMS key and policies Full (admin) Dev IAM policies can be restricted to specific KMS key. AWS CloudFormation Full (admin) Dev Use AWS managed policy
AWSCloudFormationFullAccess.
Create and pass IAM roles Full (admin) All Ability to create IAM roles and policies can be restricted to specific IAM roles or actions. Also, an admin team with IAM privileges could create all the required roles. Refer to the IAM table Roles-1 below. AWS Management Console and AWS CLI As per IAM User permissions All To access suite of Code services. - Create Git credentials for CodeCommit in the pipeline account (dev account). AWS allows you to either use Git credentials or associate SSH public keys with your IAM user. For this post, use Git credentials associated with your IAM user (created in the previous step). For instructions on creating a Git user, see Create Git credentials for HTTPS connections to CodeCommit. Download and save the Git credentials to use later for deploying the application.
- Create all AWS IAM roles as per the following tables (Roles-1). Make sure to update the following references in all the given IAM roles and policies:
- Replace the sample dev account (111111111111) and prod account (222222222222) with actual account IDs
- Replace the S3 bucket mywebapp-codepipeline-bucket-us-east-1-111111111111 with your preferred bucket name.
- Replace the KMS key ID key/82215457-e360-47fc-87dc-a04681c91ce1 with your KMS key ID.
Table: Roles-1
Service | IAM Role Type | Account | IAM Role Name (used for this post) | IAM Role Policy (required for this post) | IAM Role Permissions |
AWS CodePipeline | Service role | Dev (111111111111) | cicd_codepipeline_service_role Select Another AWS Account and use this account as the account ID to create the role. Later update the trust as follows: |
Use the permissions in the template cicd_codepipeline_service_policy.json to create the policy for this role. | This CodePipeline service role has appropriate permissions to the following services in a local account:
|
AWS CodePipeline | IAM role | Dev (111111111111) | cicd_codepipeline_trigger_cwe_role Select Another AWS Account and use this account as the account ID to create the role. Later update the trust as follows: |
Use the permissions in the template cicd_codepipeline_trigger_cwe_policy.json to create the policy for this role. | CodePipeline uses this role to set a CloudWatch event to trigger the pipeline when there is a change or commit made to the code repository. |
AWS CodePipeline | IAM role | Prod (222222222222) | cicd_codepipeline_cross_ac_role Choose Another AWS Account and use the dev account as the trusted account ID to create the role. |
Use the permissions in the template cicd_codepipeline_cross_ac_policy.json to create the policy for this role. | This role is created in the prod account and has permissions to use CodeDeploy and fetch from Amazon S3. The role is assumed by CodePipeline from the dev account to deploy the app in the prod account. Make sure to set up trust with the dev account for this IAM role on the Trust relationships tab. |
AWS CodeBuild | Service role | Dev (111111111111) | cicd_codebuild_service_role Choose CodeBuild as the use case to create the role. |
Use the permissions in the template cicd_codebuild_service_policy.json to create the policy for this role. | This CodeBuild service role has appropriate permissions to:
|
AWS CodeDeploy | Service role | Dev (111111111111) and Prod (222222222222) | cicd_codedeploy_service_role Choose CodeDeploy as the use case to create the role. |
Use the built-in AWS managed policy AWSCodeDeployRole for this role. | This CodeDeploy service role has appropriate permissions to:
|
EC2 Instance | Service role for EC2 instance profile | Dev (111111111111) and Prod (222222222222) | cicd_ec2_instance_profile Choose EC2 as the use case to create the role. |
Use the permissions in the template cicd_ec2_instance_profile_policy.json to create the policy for this role. | This is set as the EC2 instance profile for the EC2 instances where the app is deployed. It has appropriate permissions to fetch artefacts from Amazon S3 and decrypt contents using the KMS key.
You must update this role later with the actual KMS key and S3 bucket name created as part of the deployment process.
|
Setting up the prod account
To set up the prod account, complete the following steps:
- Download and launch the AWS CloudFormation template from the GitHub repo: cicd-codedeploy-prod.json
- This deploys the CodeDeploy app and deployment group.
- Make sure that you already have a set of EC2 Linux instances with the CodeDeploy agent installed in all the accounts where the sample Java application is to be installed (dev and prod accounts). If not, refer back to the Prerequisites section.
- Update the existing EC2 IAM instance profile (cicd_ec2_instance_profile):
- Replace the S3 bucket name mywebapp-codepipeline-bucket-us-east-1-111111111111 with your S3 bucket name (the one used for the CodePipelineArtifactS3Bucket variable when you launched the CloudFormation template in the dev account).
- Replace the KMS key ARN arn:aws:kms:us-east-1:111111111111:key/82215457-e360-47fc-87dc-a04681c91ce1 with your KMS key ARN (the one created as part of the CloudFormation template launch in the dev account).
Setting up the dev account
To set up your dev account, complete the following steps:
- Download and launch the CloudFormation template from the GitHub repo: cicd-aws-code-suite-dev.json
The stack deploys the following services in the dev account:- CodeCommit repository
- CodePipeline
- CodeBuild environment
- CodeDeploy app and deployment group
- CloudWatch event rule
- KMS key (used to encrypt the S3 bucket)
- S3 bucket and bucket policy
- Use following values as inputs to the CloudFormation template. You should have created all the existing resources and roles beforehand as part of the prerequisites.
Key Example Value Comments CodeCommitWebAppRepo MyWebAppRepo Name of the new CodeCommit repository for your web app. CodeCommitMainBranchName master Main branch name on your CodeCommit repository. Default is master (which is pushed to the prod environment). CodeBuildProjectName MyCBWebAppProject Name of the new CodeBuild environment. CodeBuildServiceRole arn:aws:iam::111111111111:role/cicd_codebuild_service_role ARN of an existing IAM service role to be associated with CodeBuild to build web app code. CodeDeployApp MyCDWebApp Name of the new CodeDeploy app to be created for your web app. We assume that the CodeDeploy app name is the same in all accounts where deployment needs to occur (in this case, the prod account). CodeDeployGroupDev MyCICD-Deployment-Group-Dev Name of the new CodeDeploy deployment group to be created in the dev account. CodeDeployGroupProd MyCICD-Deployment-Group-Prod Name of the existing CodeDeploy deployment group in prod account. Created as part of the prod account setup. CodeDeployGroupTagKey
Application Name of the tag key that CodeDeploy uses to identify the existing EC2 fleet for the deployment group to use. CodeDeployGroupTagValue
MyWebApp Value of the tag that CodeDeploy uses to identify the existing EC2 fleet for the deployment group to use. CodeDeployConfigName CodeDeployDefault.OneAtATime Desired Code Deploy config name. Valid options are:
CodeDeployDefault.OneAtATime
CodeDeployDefault.HalfAtATime
CodeDeployDefault.AllAtOnce
For more information, see Deployment configurations on an EC2/on-premises compute platform.
CodeDeployServiceRole arn:aws:iam::111111111111:role/cicd_codedeploy_service_role ARN of an existing IAM service role to be associated with CodeDeploy to deploy web app.
CodePipelineName MyWebAppPipeline Name of the new CodePipeline to be created for your web app. CodePipelineArtifactS3Bucket mywebapp-codepipeline-bucket-us-east-1-111111111111 Name of the new S3 bucket to be created where artifacts for the pipeline are stored for this web app. CodePipelineServiceRole arn:aws:iam::111111111111:role/cicd_codepipeline_service_role ARN of an existing IAM service role to be associated with CodePipeline to deploy web app. CodePipelineCWEventTriggerRole arn:aws:iam::111111111111:role/cicd_codepipeline_trigger_cwe_role ARN of an existing IAM role used to trigger the pipeline you named earlier upon a code push to the CodeCommit repository. CodeDeployRoleXAProd arn:aws:iam::222222222222:role/cicd_codepipeline_cross_ac_role ARN of an existing IAM role in the cross-account for CodePipeline to assume to deploy the app. It should take 5–10 minutes for the CloudFormation stack to complete. When the stack is complete, you can see that CodePipeline has built the pipeline (MyWebAppPipeline) with the CodeCommit repository and CodeBuild environment, along with actions for CodeDeploy in local (dev) and cross-account (prod). CodePipeline should be in a failed state because your CodeCommit repository is empty initially.
- Update the existing Amazon EC2 IAM instance profile (cicd_ec2_instance_profile):
- Replace the S3 bucket name mywebapp-codepipeline-bucket-us-east-1-111111111111 with your S3 bucket name (the one used for the CodePipelineArtifactS3Bucket parameter when launching the CloudFormation template in the dev account).
- Replace the KMS key ARN arn:aws:kms:us-east-1:111111111111:key/82215457-e360-47fc-87dc-a04681c91ce1 with your KMS key ARN (the one created as part of the CloudFormation template launch in the dev account).
Deploying the application
You’re now ready to deploy the application via your desktop or PC.
- Assuming you have the required HTTPS Git credentials for CodeCommit as part of the prerequisites, clone the CodeCommit repo that was created earlier as part of the dev account setup. Obtain the name of the CodeCommit repo to clone, from the CodeCommit console. Enter the Git user name and password when prompted. For example:
- Download the MyWebAppRepo.zip file containing a sample Java application, CodeBuild configuration to build the app, and CodeDeploy config file to deploy the app.
- Copy and unzip the file into the my-web-app-repo Git repository folder created earlier.
- Assuming this is the sample app to be deployed, commit these changes to the Git repo. For example:
For more information, see Tutorial: Create a simple pipeline (CodeCommit repository).
After you commit the code, the CodePipeline will be triggered and all the stages and your application should be built, tested, and deployed all the way to the production environment!
The following screenshot shows the entire pipeline and its latest run:
Troubleshooting
To troubleshoot any service-related issues, see the following:
- Troubleshooting CodePipeline
- Troubleshooting CodeDeploy
- Troubleshooting AWS CodeCommit
- Troubleshooting AWS CodeBuild
Cleaning up
To avoid incurring future charges or to remove any unwanted resources, delete the following:
- EC2 instance used to deploy the application
- CloudFormation template to remove all AWS resources created through this post
- IAM users or roles
Conclusion
Using this solution, you can easily set up and manage an entire CI/CD pipeline in AWS accounts using the native AWS suite of CI/CD services, where a commit or change to code passes through various automated stage gates all the way from building and testing to deploying applications, from development to production environments.
FAQs
In this section, we answer some frequently asked questions:
- Can I expand this deployment to more than two accounts?
- Yes. You can deploy a pipeline in a tooling account and use dev, non-prod, and prod accounts to deploy code on EC2 instances via CodeDeploy. Changes are required to the templates and policies accordingly.
- Can I ensure the application isn’t automatically deployed in the prod account via CodePipeline and needs manual approval?
- Yes. Minor changes are required to the CodePipeline section of the CloudFormation template for the dev environment to add an Approval stage and action. Refer to the following Solution Variations section for more information.
- Can I use a CodeDeploy group with an Auto Scaling group?
- Yes. Minor changes required to the CodeDeploy group creation process. Refer to the following Solution Variations section for more information.
- Can I use this pattern for EC2 Windows instances?
- Not as is. You need to make a few changes to the CloudFormation template for the dev environment for a CodeBuild project. For more information about setting up a CodeBuild environment, see Microsoft Windows samples for CodeBuild.
Solution variations
In this section, we provide a few variations to our solution:
- To associate CodeDeploy to an Auto Scaling group instead of grouping EC2 instances using tags, see Integrating CodeDeploy with Amazon EC2 Auto Scaling.
- To deploy apps to on-premises instances via CodeDeploy, see Working with on-premises instances for CodeDeploy.
- To use GitHub to trigger CodePipeline (instead of CodeCommit), see Use webhooks to start a pipeline (GitHub source).
- To learn about various providers supported by CodePipeline, see Valid action types and providers in CodePipeline.
- To add manual approval before deploying an app to production, see Manage approval actions in CodePipeline.
Author bio
Nitin Verma Nitin is currently a Sr. Cloud Architect in the AWS Managed Services(AMS). He has many years of experience with DevOps-related tools and technologies. Speak to your AWS Managed Services representative to deploy this solution in AMS! |