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.
In this article
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.
Now that everything is set up, we can start working in our default project directory.
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:
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:
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”.
Let’s take a deeper look at Terraform Lambda resources and how they provision Lambda functions and other resources.
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 } } }
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" }
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.
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 }
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 } } }
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"] }
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 }
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 }
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.
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.
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.