Automate your deployments of AWS Lambda functions with CircleCI

Stop wasting your time and money with manual code deploys

Andrea Sonny
A Cloud Guru
7 min readFeb 24, 2018

--

A lot of companies are still struggling to achieve a smooth and efficient deployment workflow. Why? Because teams are overly focused on development activities without regard to maturing their deployment process.

What’s the point of using new serverless technologies like AWS Lambda if you still need to manually run all your tests prior to deploying your functions?

It sounds crazy — but people don’t realize how much money they are loosing just because of the time and resources spent with manual processes. If you can automate your deployment process, then you can focus on developing product features instead of wasting time manually testing and releasing new code.

So why should you build a continuous integration and deployment architecture ? Because you shouldn’t be spending a lot of time and resources on manual and inefficient processes that can be easily automated.

I’m going to show you how easy it is to automate serverless deployments. For this example, we be using a simple “hello world” application I created which returns a random string using an NodeJS module that I published to npm.

Let’s get started!

For this project, we’re going to use NodeJS and the Serverless framework. To begin, create a new folder on your desktop. Then open a terminal to your new project’s directory and type:

$ npm init

Press enter to all the questions to initialize your new Node project. Then install the Serverless Framework with the following command:

$ npm i -D serverless serverless-offline

We’re going to use Serverless to build all the infrastructure needed, and then deploy our application into an AWS Lambda function. Serverless-offline will be used to run and test our function locally.

For this project, we’re going to use one dependency for generating the unique names that will used by our function. We can install the following package as a project dependency using the following command:

$ npm i -S unique-names-generator

Testing our code with Jest

First we’ll automate the testing process to verify any new code being added to our project. Then we’re going to create a continuous integration (CI) system that prevents the deployment of broken code to our live environment.

Let’s install Jest for testing our code. Why Jest? Jest is an solid testing tool developed by Facebook that requires zero configuration — so you can start writing tests out-of-the-box without wasting time on tedious configuration.

$ nmp i -D jest

Now let’s write a simple tests for the new function.

Just create a file called handler.test.js in your project’s folder — assuming that our function will reside in a file called handler.js.

const { handler } = require('./handler');jest.mock('unique-names-generator',
() => ({
generate: jest.fn().mockImplementation(() => 'mocked-name'),
}));
describe('handler', () => {
test('the handler function should work', () => {
handler({}, {}, (err, response) => {
expect(err).toBeNull();
expect(response).toBeDefined();
});
});
test('the response should be successful', () => {
handler({}, {}, (err, response) => {
expect(response.statusCode).toEqual(200);
});
});
test('the response should contain a `name` key', () => {
handler({}, {}, (err, response) => {
expect(response).toMatchSnapshot();
});
});
});

Now create a test script in your package.json file so we can run our tests:

"scripts": {
"test": "jest"
}

Since we haven’t created our handler.js module yet, running npm test is going to fail at this point — so let’s create our module.

Developing the function

For next steps, let’s create our basic handler.js file:

const { generate } = require('unique-names-generator');exports.handler = (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify({
name: generate(),
}),
};
callback(null, response);
};

The code is extremely simple. We’re just returning a JSON response with a random name created by the unique-names-generator module.

Deploying our function

We’re almost ready to deploy our function! If we execute our tests now, Jest should now reward us with a green successful test result.

We can test the logic in our local environment using serverless-offline. For next steps, simply setup the Serverless configuration file required for deploying our Lambda function.

Now just create the following serverless.yml file inside our project folder:

service: serverless-name-generatorprovider:
name: aws
runtime: nodejs6.10
plugins:
- serverless-offline
functions:
generate:
handler: handler.handler # required, handler set in AWS Lambda
events:
- http:
path: generate
method: get

To server the Lambda function using the serverless-offline plugin, we need to implement a new script in our package.json file:

"script": {
"serve": "serverless offline start",
...

Now bootstrap your local server with npm run serve command, and you should receive the following message:

Serverless: Starting Offline: dev/us-east-1.Serverless: Routes for generate:
Serverless: GET /generate
Serverless: Offline listening on http://localhost:3000

This message means that you can now open a browser to http://localhost:3000/generate to enjoy your unique name!

The response generated should look something like this:

{
"name": "angry_green_alpaca"
}

You can see a live version of this code running here: https://gu9294gok9.execute-api.us-east-1.amazonaws.com/dev/generate

Automating deployments with CircleCI

The last step is deploying our code — while ensuring that the newer version is always tested and deployed automatically with CircleCI.

CircleCI is cloud based solution to automate your development process — it’s very quick and efficient. You can register for a new account using your GitHub account — then just click on the Add projects section.

While you’ll be able to see your GitHub repository, you won’t be able to link it to CircleCI at this point. First, you’ll need to push a CircleCI configuration file to your GitHub repository.

  • Inside your project directory, create a new folder called .circleci
  • Now create a config.yml file inside with the following configuration:
version: 2jobs:
build:
docker:
- image: circleci/node:8
working_directory: ~/reposteps:
- checkout
- run:
name: Install Node dependencies
command: npm install
- run:
name: Unit tests
command: npm test
- run:
name: Deploy to AWS
command: npm run deploy

This file informs CircleCI to use NodeJS version 8 for running our Jest tests and, if the tests are passing, deploy our application.

Now let’s push our new file to our Git repo!

$ git add .
$ git commit -m "CircleCI configuration"
$ git push origin master

If you go back to your CircleCI dashboard to the Add projects section — you should be able to click and enter inside your new project repository.

A new build should be automatically be triggered — and it should fail since we’re currently missing a deploy script.

AWS CodeDeploy

CircleCI comes with native AWS support. Everything we need for setup can be found inside our project’s settings — simply click on the cog next to our project’s name.

One the left-side of the navigation bar under the Continuous Deployment section, you’ll find an option for AWS CodeDeploy.

Creating an AWS user for CircleCI
The first step is entering your AWS information in the form. I would recommend logging into the AWS Console and creating a CircleCI user to assign to CircleCI projects. If you don’t have an AWS account, be sure to leverage their free tier.

To create a new user for your CI/CD, access your IAM settings from your AWS dashboard, click Users and then Add User.

  • Tick the “programmatic access” checkbox and create a meaningful name for your new user (eg. circleci-user).
  • On the next screen (permissions), select the “attach existing policies directly” and check the AdministratorAccess from the list.
  • Now you should be able to see your new account Key and Secret for the new user. Paste those keys in the CircleCI settings page then click Save AWS Keys.
AWS CodeDeploy settings page

Your CircleCI job now has all the administrative access required to deploy your AWS Lambda function.

Automated serverless deployments!

The missing piece of the puzzle is the deployment script required for CircleCI to push your code into a new Lambda function hosted on AWS.

For this final piece, just add a new script to your package.json file:

"scripts": {
"deploy": "serverless deploy"
...

That’s it — seriously!

Now just push this small change to your GitHub repository and watch CircleCI deploy your Lambda function for you — after, of course, all the test are finished and green.

Once the job is done, you can copy and paste the endpoint provided under the “Deploy application” task into a new browser tab and enjoy your Lambda function.

Going forward, CircleCI will run the unit tests and then publish a new version of your application in just a couple of minutes — every time any new code is pushed to your Git repository!

Thanks for reading

Now you can just stay focused on developing code for new product features — and stop wasting valuable time and money on manual deployment processes.

For reference, here’s the GitHub repository where I pushed all the code I mentioned in this article — and more. I hope you found this interesting and useful. If you have any comment or have spotted any mistake, please leave a message in the comments.

Thanks for reading!

--

--