In this guide, we’ll walk through how to set up a recurring AWS Lambda function written in Golang using Terraform for infrastructure as code. We'll also demonstrate how to bundle and deploy the Lambda function using a Makefile
.
You can find the code for this tutorial in recurring-lambda-go github repository.
Prerequesites
You will need to have an AWS account and have AWS CLI and Terraform installed.
Step 1: Infrastructure Setup
Let’s start with the Terraform configuration file for setting up the infrastructure. Below, we’ll break down each resource and explain its purpose.
AWS Provider
provider "aws" {
region = "us-west-2"
}
This block specifies the AWS provider and sets the region to us-west-2
. It’s necessary to interact with AWS resources. If you are using a different region as your default you will need to change this.
Creating the Lambda Function
resource "aws_lambda_function" "my_recurring_lambda" {
filename = "../bin/bootstrap.zip"
function_name = "my_recurring_lambda"
handler = "lambda_function_payload"
source_code_hash = filebase64sha256("../bin/bootstrap.zip")
runtime = "provided.al2" // golang
role = aws_iam_role.my_recurring_lambda_iam_role.arn
}
-
filename
: Path to the zipped Lambda code (bootstrap.zip
). Note that this file will be created as a result of the build (make build
) command which is explained later. -
function_name
: Name of the Lambda function. -
handler
: Specifies the entry point; in this case, "lambda_function_payload" for custom runtimes like Go. -
source_code_hash
: Ensures Lambda is redeployed if the code changes. -
runtime
: The runtime environment, hereprovided.al2
for custom AWS Lambda runtimes like Golang. -
role
: IAM Role ARN that the Lambda will assume for execution.
IAM Role for Lambda
resource "aws_iam_role" "my_recurring_lambda_iam_role" {
name = "my_recurring_lambda_iam_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
},
]
})
}
The IAM role allows Lambda to assume the necessary permissions to run. It specifies that the Lambda service (lambda.amazonaws.com
) can assume this role.
Scheduled CloudWatch Event Rule
resource "aws_cloudwatch_event_rule" "my_recurring_lambda_schedule" {
name = "run_every_2_minutes"
schedule_expression = "cron(0/2 * * * ? *)"
}
This rule triggers the Lambda function every 2 minutes using a cron expression.
-
cron(0/2 * * * ? *)
: Runs every 2 minutes. -
name
: Friendly name for the schedule.
CloudWatch Event Target
resource "aws_cloudwatch_event_target" "my_recurring_lambda_target" {
rule = aws_cloudwatch_event_rule.my_recurring_lambda_schedule.name
target_id = "lambda"
arn = aws_lambda_function.my_recurring_lambda.arn
}
The target specifies that the Lambda function should be invoked when the scheduled rule is triggered.
Lambda Invocation Permission
resource "aws_lambda_permission" "my_recurring_lambda_permission" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.my_recurring_lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.my_recurring_lambda_schedule.arn
}
This resource grants CloudWatch Events permission to invoke the Lambda function. This and the following resources that concern cloudwatch logs are optional as you can also use other logging options.
IAM Policies for Logging
data "aws_iam_policy_document" "lambda_logging" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
]
resources = ["arn:aws:logs:*:*:*"]
}
}
resource "aws_iam_policy" "lambda_logging" {
name = "lambda_logging"
description = "IAM policy for logging from a lambda"
policy = data.aws_iam_policy_document.lambda_logging.json
}
This allows the Lambda to create and write logs in CloudWatch.
IAM Policy Attachment
resource "aws_iam_role_policy_attachment" "my_recurring_lambda_iam_role" {
role = aws_iam_role.my_recurring_lambda_iam_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
Attaches the basic execution role to the Lambda IAM role for logging permissions.
CloudWatch Log Group
resource "aws_cloudwatch_log_group" "my_recurring_lambda_log_group" {
name = "/aws/lambda/${aws_lambda_function.my_recurring_lambda.function_name}"
retention_in_days = 14
}
Creates a log group in CloudWatch for the Lambda function with logs retained for 14 days. Note that the longer you retain the logs the more you higher your AWS bill will be.
Step 2: Writing the Go Lambda Function
Here's the Go file:
package main
import "fmt"
func main() {
fmt.Println("Running main.go")
}
func Execute() {
fmt.Println("Running Execute()")
}
This minimal Go Lambda prints basic messages. In a real-world scenario, you'd replace Execute()
with your custom logic.
Step 3: Bundling and Deploying the Lambda
The Makefile
simplifies the process:
Makefile Overview
build:
GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o ./bin/bootstrap src/main.go
zip -j ./bin/bootstrap.zip ./bin/bootstrap
clean:
rm -rf ./bin/bootstrap ./bin/bootstrap.zip
deploy:
cd infra && terraform init && terraform apply
terraform-destroy:
cd infra && terraform init && terraform destroy
terraform-apply:
make deploy
terraform-plan:
cd infra && terraform init && terraform plan
Key Commands
-
build
:- Cross-compiles the Go application for Linux (
GOOS=linux
) with architectureamd64
. - The output is a binary named
bootstrap
in thebin
folder. - The binary is zipped into
bootstrap.zip
.
- Cross-compiles the Go application for Linux (
clean
: Removes build artifacts.deploy
: Initializes and applies the Terraform configuration to deploy resources.terraform-destroy
: Destroys the deployed infrastructure.terraform-apply
andterraform-plan
: Streamlined commands to manage Terraform workflows.
Deployment Workflow
- Build the Lambda:
make build
- Deploy Infrastructure:
make deploy
- Validate Execution: Check the CloudWatch logs to confirm the Lambda function runs every 2 minutes.
This approach combines Terraform for infrastructure as code and Makefile for a simplified deployment pipeline. With this setup, you can easily deploy, test, and manage your recurring Golang-based Lambda function.
Top comments (0)