Deploying AWS Lambda with Terraform
Pinned note: Writing this post does not mean I enjoy using Terraform.
This section is a bit verbose — feel free to skip straight to my GitHub.
Deploy with local Terraform: https://github.com/chengqing-su/lambda-deployment-via-terraform
Deploy with Docker: https://github.com/chengqing-su/lambda-deployment-via-dockerized-terraform
Deploying with Local Terraform
The minimal components of an AWS Lambda setup:
- Lambda code: defines what the Lambda does and how it does it
- AWS Lambda function’s execution role: defines what AWS resources and services the Lambda function is allowed to access
- AWS Lambda function resource
- AWS CloudWatch Log Group (optional): stores execution logs
I wrote a very simple demo that just prints log output. It is written in Node.js 12 and TypeScript.
1 | ├── README.md |
If you find this section too verbose, you can go straight to the code on GitHub: https://github.com/chengqing-su/lambda-deployment-via-terraform
If you’re interested in how to deploy with Docker and why you’d want to, skip ahead to the “Containerized Deployment” section, or check out my code: https://github.com/chengqing-su/lambda-deployment-via-dockerized-terraform
Lambda Code
The application code lives under src. This is an extremely simple demo with no tests. If tests were included, they would go in a tests directory at the same level as src (a bit verbose, I know).
1 | import { |
Next is the infrastructure code, which lives under deployment.
Typically, you need to package the Lambda application code first. The code below builds the final deployable artifact and packages it into a zip archive.
1 | resource "null_resource" "package" { |
AWS Lambda function’s execution role
The Lambda function’s execution role defines the permissions the function has to access AWS services and resources.
1 | # lambda execution role |
AWS Lambda function resource
1 | resource "aws_lambda_function" "function" { |
AWS CloudWatch Log Group
Create a Log Group to store the Lambda execution logs.
1 | resource "aws_cloudwatch_log_group" "logs" { |
Deploying with Local Terraform
After all that explanation, here is how to actually deploy — which turns out to be straightforward.
1 | cd deployment/ |
Containerized Deployment
The pain points of local deployment are:
- If the required tooling is not installed, you have to set everything up from scratch. Even if the environment exists, you cannot be sure it is clean, or that other team members are using the correct versions. In this demo alone, you need Node.js 12 (the latest LTS at the time was 14), Yarn, and Terraform 0.14.4 — using a higher or lower version can cause unpredictable issues.
- In real projects, you often work with multiple Lambda functions that may use different runtimes — some in Python, some in Ruby, some in Node.js. Installing all of those environments locally becomes a significant burden.
Containerization is therefore essential.
First, introduce a docker-compose.yaml at the project root. Here is an example for a Node.js project:
1 | version: "2" |
Next, add an auto directory at the same level as deployment to hold automation scripts. Here is an example Terraform wrapper script at auto/terraform:
1 | !/bin/bash -e |
With auto/terraform in place, how do you deploy? Add one more automation script, auto/deploy, like this:
1 | !/bin/bash -e |
Deployment then becomes extremely simple:
1 | export AWS_ACCESS_KEY_ID=<YOUR_AWS_ACCESS_KEY_ID> |