John Tipper
Navigate back to the homepage

Integrating AWS CDK into GitHub Actions

John Tipper
September 13th, 2020 · 2 min read

Integrating AWS CDK into GitHub Actions

We want to be able to have changes to our infrastructure-as-code be deployed automatically. Specifically, we want changes to our repository to result in the following:

  1. Build and test code our code.
  2. Build our website.
  3. Deploy our website infrastructure, including updating our website content.

Choice of GitHub Actions as CI/CD

Now, one might be forgiven for thinking that if we’re going to be deploying code to AWS that we might make use of AWS features, such as CodePipeline and CodeBuild. However, GitHub Actions are completely free, whereas the AWS services are not. Let’s go with the free option…

AWS Credentials

Create an IAM user with programmatic to AWS and store the access key and secret access key as secrets in the GitHub repository. The permissions associated with the user will be determined by the resources you define within your CDK stack. Let’s assume these secrets are called AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. GitHub Actions will make these secrets available to the actions and we don’t need to store them in source code anywhere. We’ll also assume that there is another secret called AWS_REGION, but clearly neither this nor AWS_ACCESS_KEY_ID actually need to be secret - we’ll just store them in GitHub as secrets to completely decouple our AWS deployment details from the source code.

GitHub Actions Workflow

At its simplest, a GitHub Action comprises a single workflow, which is a sequence of steps to carry out. You can refer to other actions as part of these steps, where those actions may be defined by you or others. For our purposes, we’ll just define a single workflow.

Workflow

Our workflow definition file is inside .github/workflows/build.yml (you’re free to choose whatever workflow name you wish). We define the name of the workflow and when we want it to be triggered (on every push to the repository). We want it to run on a Ubuntu runner and perform a series of steps when it is triggered.

1name: Build
2
3on: [push]
4
5jobs:
6 build:
7 runs-on: ubuntu-latest
8 steps:
9 ... steps go here

Our first steps involve checking out the code from the repository and then configuring Node.js. I need to install the AWS CDK and Gatsby - you may not need Gatsby if you’re building your website some other way. We define some caching of build dependencies to reduce build times for subsequent runs.

1- uses: actions/checkout@v2
2
3 - name: Use Node.js
4 uses: actions/setup-node@v1
5 with:
6 node-version: '14.x'
7
8 - name: Cache Node.js modules
9 uses: actions/cache@v2
10 with:
11 path: ~/.npm
12 key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
13 restore-keys: |
14 ${{ runner.OS }}-node-
15 ${{ runner.OS }}-
16
17 - name: Install CDK and Gatsby
18 run: |
19 npm install -g aws-cdk@1.62.0 gatsby-cli

We then configure Java and Gradle with caching of Gradle dependencies:

1- name: Set up JDK 1.11
2 uses: actions/setup-java@v1
3 with:
4 java-version: 1.11
5
6 - name: Cache Gradle packages
7 uses: actions/cache@v2
8 with:
9 path: ~/.gradle/caches
10 key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
11 restore-keys: ${{ runner.os }}-gradle

Now we’re ready to define some steps which will build our project’s CDK and generate the website. Clearly, you can put in whatever unit tests you wish for your application code: remember, CDK is just developing in your normal coding language.

1- name: Build with Gradle
2 run: ./gradlew build
3
4 - name: Build Gatsby
5 run: |
6 pushd web
7 npm ci
8 gatsby clean
9 gatsby build
10 popd

Next step is to synthesize the CDK stack, which, as you’ll recall, requires AWS credentials to perform a Route53 HostedZone lookup. We want to synthesize the CDK stack every time we push a change, as this is a good way of determining if there’s an issue with our CDK: some errors won’t be apparent until you try to synthesize them.

1- name: Synth CDK
2 run: |
3 ./gradlew cdkPrepare
4 cdk synth \
5 --app 'java -jar ./infrastructure/build/cdk/infrastructure-all.jar -apiLambdaPath ./infrastructure/build/cdk/api-lambdas.zip -webAssets ./infrastructure/build/cdk/web -domainName johntipper.org -region ${{ secrets.AWS_REGION }} -targetAccount ${{ secrets.AWS_TARGET_ACCOUNT }}' \
6 --output build/cdk.out
7 env:
8 AWS_REGION: ${{ secrets.AWS_REGION }}
9 AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
10 AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
11 AWS_TARGET_ACCOUNT: ${{ secrets.AWS_TARGET_ACCOUNT }}

Finally, we want to deploy the synthesized CDK, if we’re on the master branch:

1- name: Deploy CDK
2 run: |
3 cdk deploy --app ./build/cdk.out --require-approval never "*"
4 if: github.ref == 'refs/heads/master'
5 env:
6 AWS_REGION: ${{ secrets.AWS_REGION }}
7 AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
8 AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Status

In our repository README.md, we’ll define an image, which GitHub will update whenever a build occurs. That image will (hopefully) show a green badge showing that our build works. We can refer to that image using this syntax:

1![Build status](https://github.com/<org>/<repo>/workflows/Build/badge.svg "GitHub Actions Build Status")

where Build is the name of the workflow that we define in our workflow file.

More articles from John Tipper

A static website with API backend using AWS CDK and Java

An example of creating a static website using AWS CDK and Java

September 12th, 2020 · 5 min read

Setting up the AWS CDK with Java & Gradle

How the AWS CDK can be used with Java, with an introduction to Gradle en-route.

August 29th, 2020 · 6 min read
© 2018–2021 John Tipper
Link to $https://twitter.com/john_tipperLink to $https://github.com/john-tipperLink to $https://www.linkedin.com/in/john-tipper-5076395