DEV Community

Cover image for How to Set Up a Recurring AWS Lambda Function Using Terraform and Golang
Pendar
Pendar

Posted on

How to Set Up a Recurring AWS Lambda Function Using Terraform and Golang

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"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode
  • 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, here provided.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"
        }
      },
    ]
  })
}
Enter fullscreen mode Exit fullscreen mode

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 * * * ? *)"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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()")
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Key Commands

  • build:

    • Cross-compiles the Go application for Linux (GOOS=linux) with architecture amd64.
    • The output is a binary named bootstrap in the bin folder.
    • The binary is zipped into bootstrap.zip.
  • clean: Removes build artifacts.

  • deploy: Initializes and applies the Terraform configuration to deploy resources.

  • terraform-destroy: Destroys the deployed infrastructure.

  • terraform-apply and terraform-plan: Streamlined commands to manage Terraform workflows.


Deployment Workflow

  1. Build the Lambda:
   make build
Enter fullscreen mode Exit fullscreen mode
  1. Deploy Infrastructure:
   make deploy
Enter fullscreen mode Exit fullscreen mode
  1. 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)