Lambda Cron Example (Terraform)
Tags: aws, terraform, lambda
Published: 2022-10-01 15:59:26

---
title: Lambda Cron Example (Terraform)
published: true
description: Example showing how to configure eventbridge to run your lambda on a cronjob using terraform
tags: aws, terraform, lambda
cover_image: https://rhuaridh.co.uk/img/blog/lambda-cron-example.png
canonical_url: https://rhuaridh.co.uk/blog/aws-terraform-lambda-cron.html
---

## Introduction

A common issue people experience when transitioning to serverless infrastructure is finding where to configure a cron.

During this article we will look at using EventBridge to trigger a lambda on a schedule. We will implement this using Terraform.

## Setup Golang Lambda

I will gloss over the section of creating a lambda, as this is something I have covered in [Golang](https://rhuaridh.co.uk/blog/deploy-golang-lambda-example.html) and [Python](https://rhuaridh.co.uk/blog/deploy-python-lambda-example.html) already.

For this example we will use a simple Golang Hello World example.

Create **main.go**, then add this golang snippet:

```go
package main

import (
    "log"

    "context"

    "github.com/aws/aws-lambda-go/lambda"
)

func handler(ctx context.Context) error {
    log.Println("Golang Lambda executed via Eventbridge Cron")

    return nil
}

func main() {
    lambda.Start(handler)
}
```

Then we will run these CLI commands to create our .zip file:

```bash
# Initialise our golang project
go mod init example.com/demo
go get github.com/aws/aws-lambda-go/lambda

# If you are on a mac, let Go know you want linux
export GOOS=linux
export GOARCH=amd64
export CGO_ENABLED=0

# Build our lambda
go build -o hello

# Zip up our binary ready for terraform
zip -r function.zip hello
```

## Setup Terraform

Create **main.tf**, then add this terraform snippet:

```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

# Configure the AWS Provider

provider "aws" {
  region = "eu-west-1"
}
```

This just lets terraform know that we want to use the AWS provider, and that we'll be working in the **eu-west-1** region.

You can now run this from the CLI to initialise terraform:

```bash
terraform init
```

## Create a Lambda in Terraform

Create **lambda.tf**, then add this terraform snippet:

```
# Create out lambda, using a locally sourced zip file
resource "aws_lambda_function" "demo_lambda_hello_world" {
  function_name = "demo-lambda-hello-world"
  role          = aws_iam_role.demo_lambda_role.arn
  package_type  = "Zip"
  handler       = "hello"
  runtime       = "go1.x"

  filename         = "function.zip"
  source_code_hash = filebase64sha256("function.zip")

  depends_on = [
    aws_iam_role.demo_lambda_role
  ]

  tags = {
    Name = "Demo Lambda Hello World"
  }
}
```

## Create IAM Role for Lambda

Our lambda will need some basic permissions to work, these might look jarring at first but they are fairly straight forward once you read through them.

Essentially, this role will be **assumed** by our Lambda and give it access to write to **Cloudwatch Logs**.

Create **iam.tf**, then add this terraform snippet:

```
# Store the AWS account_id in a variable so we can reference it in our IAM policy
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

locals {
  account_id = data.aws_caller_identity.current.account_id
}

# Lambda IAM Role
resource "aws_iam_role" "demo_lambda_role" {
  name = "demo-lambda-role"

  assume_role_policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Action" : "sts:AssumeRole",
        "Principal" : {
          "Service" : "lambda.amazonaws.com"
        },
        "Effect" : "Allow"
      }
    ]
  })

  inline_policy {
    name = "demo-lambda-policies"
    policy = jsonencode({
      "Version" : "2012-10-17",
      "Statement" : [
        {
          "Effect" : "Allow",
          "Action" : "logs:CreateLogGroup",
          "Resource" : "arn:aws:logs:${data.aws_region.current.name}:${local.account_id}:*"
        },
        {
          "Effect" : "Allow",
          "Action" : [
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource" : [
            "arn:aws:logs:${data.aws_region.current.name}:${local.account_id}:log-group:/aws/lambda/*:*"
          ]
        }
      ]
    })
  }
}
```

## Setup our Cron

Now that we have our lambda setup, we can [set up the cron](https://docs.aws.amazon.com/systems-manager/latest/userguide/reference-cron-and-rate-expressions.html).

 * You can either use a cron, for example: **cron(\*/5 \* \* \* ? \*)** 
 * Or; you can use a rate, for example: **rate(5 minutes)**

Create **eventbridge.tf**, then add this terraform snippet:

```
# Create our schedule
resource "aws_cloudwatch_event_rule" "demo_lambda_every_5_minutes" {
  name                = "demo-lambda-every-5-minutes"
  description         = "Fires every 5 minutes"
  schedule_expression = "rate(5 minutes)"
}

# Trigger our lambda based on the schedule
resource "aws_cloudwatch_event_target" "trigger_lambda_on_schedule" {
  rule      = aws_cloudwatch_event_rule.demo_lambda_every_5_minutes.name
  target_id = "lambda"
  arn       = aws_lambda_function.demo_lambda_hello_world.arn
}
```

## Add Lambda Permission

In order for our cron to work, we need to let our Lambda know that EventBridge is allowed to Invoke it.

Inside **lambda.tf**, add this terraform snippet:

```
resource "aws_lambda_permission" "allow_cloudwatch_to_call_split_lambda" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.demo_lambda_hello_world.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.demo_lambda_every_5_minutes.arn
}
```

## Deploy Terraform

First, run

```
terraform plan
```

Now, once we're confident we can run:

```
terraform apply
```

This will now create the resources from the terraform files.

## Confirm it's running

After it has been running for 5 minutes you can check the Cloudwatch Logs group to confirm it has been triggered.

To confirm it is working find your Lambda in the console, then select "Monitoring". You should now see your lambda running under "Recent invocations":

![Cron Lambda Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3c9y5krhxm8yxn7653z4.png)

## Cleanup

If you were just experimenting, remember you can destroy the resources when you're done by running:

```
terraform destroy
```

While Lambda's are cheap to invoke, it is always good to keep your account clean to avoid any unnecessary billing.

And that's it! You have now configured a Lambda cronjob using EventBridge.