Automate EC2 AMI Builds Triggered by Code Pushed to AWS CodeCommit Repositories

EC2 Image Builder is an AWS service that provides “image pipelines”. Image pipelines are a way to trigger builds of your AMI on a predefined schedule. The pipeline assembles everything you need in the AMI, tests it, and distributes it to different regions and accounts.

Unfortunately, image pipelines cannot be connected to your code repository directly. They can only run on schedules. Wouldn’t it be great if you could trigger an image pipeline as soon as code is pushed to a branch of your repository? This article describes how you can accomplish this.

The overall steps are as follows:

  • Create a component in EC2 image builder for your app.
  • Create a recipe in EC2 image builder using this component.
  • Create an EC2 image pipeline using this recipe.
  • Create a Lambda function to invoke this pipeline.
  • Trigger this Lambda function on code push.

EC2 Image Builder Component

A component in image builder is a piece of software you want to be installed in your final AMI. In our case, we want our application to be installed in the AMI.

For the sake of simplicity, we will assume that ours is a Node.js app with a single App.js file stored in a CodeCommit repository. The “installation” procedure of this app is to simply clone the Git repo into the AMI.

Let’s create a component for this. When this component is run by the pipeline, it will install our app. Go to console.aws.amazon.com/imagebuilder/home#/createComponent. Select “Build” as the component type:

Image builder also supports components of type “Test” which are used to run tests on your AMI before it becomes available.

Select the operating system where your app should run:

Give the component a name and a version:

Provide the following definition document:

schemaVersion: 1.0
phases:
- name: build
  steps:
  - name: DownloadMyApp
    action: ExecuteBash
    inputs:
      commands:
      - git clone https://username:password@git-codecommit.us-east-1.amazonaws.com/v1/repos/my-app /my-app

In this example, the Git credentials are inlined into the repo URL to make things simpler.

As you can see, the CodeCommit repo will be cloned into /my-app when this component is run by the pipeline.

Finish creating the component.

EC2 Image Builder Recipe

Image pipelines don’t run components directly. They run recipes that encapsulate components. So let’s create a recipe for our app.

Go to console.aws.amazon.com/imagebuilder/home#/createImageRecipe. Provide a name and version for the recipe:

Select the operating system your app should run on; Ubuntu 20 in our case:

Next, select the component we built earlier:

Skip the test components and storage sections. Finish creating the recipe.

EC2 Image Pipeline

We can now create a pipeline with this recipe. Go to console.aws.amazon.com/imagebuilder/home#/createPipeline. Provide a name and description:

Set the build schedule to manual because we’ll be triggering this from Lambda:

Select the recipe we created earlier:

Use the defaults for infrastructure configuration and distribution settings and finish creating the pipeline.

Lambda Function

It’s now time to create a Lambda function that will trigger the pipeline. You can use any Lambda runtime of your choice and use the AWS SDK to trigger the pipeline. In this example though, we’ll use the AWS CLI inside a Lambda function to trigger the pipeline.

The AWS CLI is available as a Lambda layer in the serverless app repo. Start by deploying this layer in your account from console.aws.amazon.com/lambda/home#/create/app?applicationId=arn:aws:serverlessrepo:us-east-1:903779448426:applications/lambda-layer-awscli.

Next, create a lambda function using the Amazon Linux default bootstrap as the runtime:

Add the AWS CLI layer to this Lambda function:

Insert the following CLI command into the Lambda function’s hello.sh handler to start the EC2 image pipeline:

/opt/awscli/aws imagebuilder \
  start-image-pipeline-execution \
  --image-pipeline-arn \
    'arn:aws:imagebuilder:us-east-1:213566310128:image-pipeline/my-app-pipeline'

You’ll also need to grant this function permissions to make image builder API calls. For this, add this inline policy to the function’s execution role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "imagebuilder:GetImagePipeline",
                "imagebuilder:StartImagePipelineExecution"
            ],
            "Resource": "arn:aws:imagebuilder:us-east-1:213566310128:image-pipeline/my-app-pipeline"
        }
    ]
}

Now add a trigger to the Lambda function such that it is invoked when any code is pushed to the CodeCommit repo:

Now try pushing a commit to CodeCommit to make the pipeline run. Every run will create an image. All images are listed in the pipeline’s details screen:

Conclusion

In this article, you saw how to build an end-to-end pipeline that will automatically package the latest version of your application code into a ready-to-deploy AMI.

About the Author ✍🏻

Harish KM is a Principal DevOps Engineer at QloudX & a top-ranked APN Ambassador. 👨🏻‍💻

With over a decade of industry experience as everything from a full-stack engineer to a cloud architect, Harish has built many world-class solutions for clients around the world! 👷🏻‍♂️

With over 20 certifications in cloud (AWS, Azure, GCP), containers (Kubernetes, Docker) & DevOps (Terraform, Ansible, Jenkins), Harish is an expert in a multitude of technologies. 📚

These days, his focus is on the fascinating world of DevOps & how it can transform the way we do things! 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *