Lambda + Terraform + Github Actions

jerome.decoster
7 min readMay 31, 2021

--

Building a continuous deployment for Lambda functions in a multi-stage environment.

The Goal

  • Create a deployment pipeline for a Lambda function with Terraform
  • Use Lambda versions and aliases to define two environments for development and production
  • Use the API Gateway stageVariables to target one of these 2 environments
  • Use Gihub Actions to automatically update the Lambda function
  • A git push on the develop branch will update the dev version of the Lambda function
  • A git push on the master branch will update the prod version of the Lambda function

Install and setup the project

Get the code from this github repository :

To setup the project, run the following command :

# create env + terraform init
$ make setup

This command will :

  • Create an AWS user and put the access keys in the .env file
  • Initialize Terraform from the information defined in the infra directory

Creating the architecture

To create the architecture, run the following command :

# terraform plan + apply
$ make tf-apply

This command will :

The API Gateway is created :

The /hello resource has a GET method that targets our Lambda :

We can see in the Integration Request box that, by targeting the Lambda function, we associate a specific version defined by stageVariable :

Two stages have also been published : dev and prod.

Here is the source code to deploy the stage dev :

The stage dev has a stageVariable called stage whose value is dev :

The stage prod has a stageVariable called stage whose value is prod :

Lambda function is created :

The code is simple :

Here is the source code to create the 2 aliases :

We find these aliases in the web interface :

We can test access to the Lambda function by a curl call :

$ make hello-dev
"Hello from Lambda"

Setup up the Github project

The project uses Github Actions to automatically deploy updates

Lambda function automatic deployment requires AWS credentials

Here is the source code using the accesses :

These accesses were created previously

They are located in the .env file which is based on the .env.tmpl template

We add these secrets in our project :

The secrets have been added :

Creating the develop branch

As a reminder, the deployment of our project is done according to this principle :

  • A git push on the develop branch will update the dev version of the Lambda function
  • A git push on the master branch will update the prod version of the Lambda function

At the moment we only have one master branch. So we create a develop branch from it :

We edit the code of the Lambda function of the develop branch :

We modify the message for Hello from develop :

We commit directly to the develop branch :

The cd.yml workflow starts :

It ends quickly :

To test our deployment we run the following command :

$ make hello-dev

This command executes a simple curl call :

$ curl $(terraform output -raw hello_dev)

It get the URL from a Terraform output :

Our code modification, on the develop branch, returns the correct message :

# successul return !
$ make hello-dev
"Hello from develop"

Updating the master branch

We quickly edit the master branch to test the proper functioning of our continuous deployment :

The message is now Hello from master :

We commit in the master branch :

The cd.yml workflow starts :

It ends quickly. To test our deployment we run the following command :

$ make hello-prod
"Hello from master"

In the Lambda web interface, we can see that aliases now point to the published versions of a Lambda function and no longer to the $LATEST version :

The prod and dev versions have an alias label :

This publication and alias association is performed by this part of the publish.sh script :

Updating with the Github flow

Our project works :

  • Updating the code on the master branch deploys a Lambda accessible via the URL /prod/hello
  • Updating the code on the develop branch deploys a Lambda accessible via the URL /dev/hello

We now want to simulate a real update of our project using the Github flow.

We will create a Pull Request to add a new feature to our function.

We will name this feature feature-1.

We edit our hello.js file from our develop branch :

To simulate the first version of our feature-1, we modify our message to Hello with feature-1-v1 :

By committing our code, we choose to create a new branch and name it feature-1 :

The github web interface offers us to create a Pull Request.

We name it feature-1 and we indicate that this branch should be merged into the develop branch :

The pull request has been created but we do not merge it right away :

We will first update our Lambda function :

It is important to note that the previous commit on the new feature-1 branch did not trigger our continuous deployment workflow.

This is because the workflow is configured to be triggered only after a push action on the master or the develop branches :

We are changing the message to Hello with feature-1-v2 :

We commit this update in the feature-1 branch :

We are now satisfied with all these modifications, we will be able to merge the pull request :

The workflow is started :

He finishes quickly :

Our new version is quickly accessible from the stage dev :

$ make hello-dev
"Hello with feature-1-v2"

If we are satisfied with this version, we can deploy it in production.

We just need to create a new Pull Request to merge the develop branch to the master branch.

The deployment will be automatic.

--

--