• Guide Content

Deploying AWS Lambda with Terraform Quick Tutorial and Basic Concepts

Serverless is a popular cloud computing architecture for applications in the AWS cloud. Typically, you create one or more Lambda functions and then expose them using AWS API Gateway. These Lambda functions connect to other AWS services to serve the requests with data and other information.

You can build this serverless application flow manually using AWS Console or AWS CLI, however, that will be very painful to maintain for the enhancements, rollbacks, and when you need to recreate new environments for the same workload. Hashicorp’s Terraform is a popular automation scripting tool for building, changing, and versioning infrastructure safely and efficiently.

Terraform AWS modules can help automate and provision serverless application resources much more efficiently.

serverless automation workflow with terraform

Image Source

TL;DR: Terraform AWS Lambda Example—How to Deploy a Lambda Function with Terraform

This tutorial will show you how to deploy a Lambda function to Terraform from a local machine. It is abbreviated from the full tutorial by Souvik Paul.

Prerequisites: Download and install Terraform on the local machine. The folder in which you installed Terraform will be referred to as {Terraform-folder}

Warning: In the interest of simplicity, this tutorial grants broad permissions via IAM, which can create security issues in a production environment. Please carry out the tutorial on a test AWS account – not on your production account.

Step 1: Create an IAM User

  1. Sign into the AWS Console, navigate to Amazon Identity and Access Management (IAM).
  2. Click Create IAM User and create a user with administrative privileges on both Amazon console and API.
  3. Create a folder named {Terraform-folder}\.aws\
  4. Inside it, create a text file named credentials that looks like this:

Now that everything is set up, we can start working in our default project directory.

Step 2: Create Lambda function and upload to S3

Create a directory for your project, for example {Terraform-folder}\lambda-test.

Create a simple Hello World Lambda function with the following code, and name it hello.js.

Compress the Lambda function as a hello.zip, create a new Amazon S3 bucket, and upload the ZIP to S3 (see documentation here).

Step 3: Create IAM Policy

Create a directory named {Terraform-folder}\lambda-test\iam .

Automatically create a policy file using the AWS policy generator:

  • Under Type of Policy select IAM policy
  • Under AWS Service select AWS CloudWatch Logs
  • Check the All Actions checkbox
  • Enter * as the Amazon resource name
  • Click Create Policy

Copy the JSON code and create a file named lambda_policy.json .

In the \iam folder, Add another file named lambda_assume_role_policy.json, with this code:

Step 4: Create Terraform Resources

To enable TerraForm to deploy Lambda functions, you need to create three .tf files in the \lambda-test\ project folder:

  • iam-lambda.tf – defines two TerraForm resources and assigns the IAM policies to them
  • provider.tf – defines AWS as a TerraForm provider
  • lambda.tf – defines the Lambda function as a TerraForm resource

Below is sample code for each of these files.

Step 6: Deploy the Lambda function to AWS

Run the following command in the {TerraForm-directory} to initialize TerraForm and download plugins:

terraforminit

Then run this command to deploy all the resources in the project folder:

terraform apply -auto-approve

That’s it! TerraForm will automatically deploy your Lambda function to AWS.

To test that the Lambda function is really working, go to the Amazon Lambda console, find the hello function, open it and click Test. Provide a test event and check log output to see that your new Lambda logs the words “Hello World”.

Understanding Terraform Lambda Resources

Let’s take a deeper look at Terraform Lambda resources and how they provision Lambda functions and other resources.

Aws_lambda_alias

The aws_lambda_alias resource creates a virtual identifier for a specific version of a Lambda function. It helps to upgrade the version of a function without asking the client to modify code at their end. You can also implement canary deployment by pointing the alias to multiple versions using routing config.

Below is a Terraform AWS example with the Lambda alias feature:

resource "aws_lambda_alias" "prod_lambda_alias" { name             = "your_alias" description      = "production version" function_name    = aws_lambda_function.lambda_function_prod.arn function_version = "1" routing_config {    additional_version_weights = {      "2" = 0.4    } } }

aws_lambda_event_source_mapping

The aws_lambda_event_source_mapping resource creates a mapping between an event source and a Lambda function. It configures the ARN of the event source that triggers the Lambda function. It also defines the properties to control the behaviour to trigger the function.

Below is a Terraform AWS example of a DynamoDB event source:

resource "aws_lambda_event_source_mapping" "DynamoDBExample" { event_source_arn  = aws_dynamodb_table.dynamodbexample.stream_arn function_name     = aws_lambda_function.dynamodbexample.arn starting_position = "LATEST" }

aws_lambda_function

A Lambda function needs code and an IAM role to run a function. Code is deployed on an S3 bucket as a deployment package (zip file).

Below is a sample Terraform AWS Lambda function:

resource "aws_iam_role" "iam_role_for_lambda" { name = "iam_role_for_lambda" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [    {      "Action": "sts:AssumeRole",      "Principal": {        "Service": "lambda.amazonaws.com"      },      "Effect": "Allow",      "Sid": ""    } ] } EOF } resource "aws_lambda_function" "sample_lambda" { filename      = "lambda_function_payload.zip" function_name = "lambda_terraform_function_name" role          = aws_iam_role.iam_role_for_lambda.arn   handler       = "data.test" # The filebase64sha256() function is available in Terraform 0.11.12 and later # For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function: # source_code_hash = "${base64sha256(file("lambda_function_payload.zip"))}"   source_code_hash = filebase64sha256("lambda_function_payload.zip")   runtime = "nodejs12.x" environment {    variables = {      foo = "bar"    } } } }

It has several other properties such as vpc_config, file_system_config, and more.

The function might need several other optional resources such as aws_efs_file_system, aws_efs_mount_target, and aws_efs_access_point.

aws_cloudwatch_log_group

This resource is required to create a log group for a function and configure a retention policy for it. You need this for logging and monitoring the Lambda function.

resource "aws_cloudwatch_log_group" "example" {
  name              = "/aws/lambda/${var.lambda_function_name}"
  retention_in_days = 14
}

aws_lambda_function_event_invoke_config

This resource is required to invoke a Lambda function for asynchronous flow.

resource "aws_lambda_function_event_invoke_config" "lambdaexample" { function_name = aws_lambda_alias.example.function_name maximum_event_age_in_seconds = 60 maximum_retry_attempts       = 0 qualifier     = aws_lambda_alias.example.name destination_config {    on_failure {      destination = aws_sqs_queue.example.arn    }    on_success {      destination = aws_sns_topic.example.arn    } } }

aws_lambda_layer_version

This provides a Lambda Layer Version resource. Lambda Layers allow you share the reusable code through layers across multiple Lambda functions.

resource "aws_lambda_layer_version" "lambda_nodejs_layer" { filename   = "lambda_nodejs_layer_payload.zip" layer_name = "lambda_layer_nodejs" compatible_runtimes = ["nodejs12.0"] }

aws_lambda_permission

This resource provides other AWS services, such as S3 and DynamoDB, access to the Lambda function.

resource "aws_lambda_permission" "allow_s3" { statement_id  = "AllowExecutionFromS3" action        = "lambda:InvokeFunction" function_name = aws_lambda_function.s3_lambda.function_name principal     = "events.amazonaws.com" source_arn    = "arn:aws:events:ap-east-2:121112424343:rule/RunDaily" qualifier     = aws_lambda_alias.s3_alias.name }

aws_api_gateway

API Gateway is the most important resource for synchronous flow. Terraform can be used to create and integrate it with a Lambda function.

Below are the four resources required to create API Gateway and it’s integration with a function:

resource "aws_api_gateway_rest_api" "APIexample" { name        = "ServerlessAppExample" description = "Terraform Serverless Application Example" } resource "aws_api_gateway_resource" "apiproxy" {   rest_api_id = aws_api_gateway_rest_api.example.id   parent_id   = aws_api_gateway_rest_api.example.root_resource_id   path_part   = "{proxy+}" } resource "aws_api_gateway_method" "methodproxy" {   rest_api_id   = aws_api_gateway_rest_api.example.id   resource_id   = aws_api_gateway_resource.proxy.id   http_method   = "ANY"   authorization = "NONE"  } resource "aws_api_gateway_integration" "apilambda" {   rest_api_id = aws_api_gateway_rest_api.example.id   resource_id = aws_api_gateway_method.proxy.resource_id    http_method = aws_api_gateway_method.proxy.http_method   integration_http_method = "POST"   type                    = "AWS_PROXY"   uri                     = aws_lambda_function.example.invoke_arn }

Terraform vs. CloudFormation

So far we discussed how Terraform works and how it can help automate the provisioning of serverless application resources. Now, let’s compare it with a native alternative solution: automating Lambda with CloudFormation.

  • CloudFormation is an AWS managed service so it does the state management by itself. With Terraform, you can store the state in AWS S3 but you have to configure it manually.
  • Terraform is agnostic to the cloud platform vs CloudFormation that is specific to AWS. You cannot easily provision or coordinate non-AWS resources with CloudFormation.
  • Terraform uses data sources supplied by the specific cloud provider that enables it to fetch data defined outside of Terraform.
  • CloudFormation uses parameters, and has a maximum of 60 parameters per template. Each parameter must be declared and referenced from within the same template.
  • Terraform uses the HashiCorp Configuration Language (HCL) and is compatible with JSON. CloudFormation supports JSON and YAML.

Both are very powerful tools for infrastructure provisioning. If you are using AWS tools and don’t plan to have a hybrid/multi-cloud environment, CloudFormation is a better option as it is always in sync with all the latest AWS releases.

Conclusion

In this article, we reviewed Terraform and the Terraform Lambda modules in detail. Terraform is a good option when using multiple cloud providers, as it supports other clouds such as GCP and Azure. CloudFormation is an alternative that can be used for AWS Lambda resource provisioning and is limited to the AWS ecosystem.