AWS, GO

How to Invoke AWS Lambda by a Scheduled Event

User Icon ,   Calendar Icon
aws-lambda-cron-go-v1

The AWS Lambda function is a serverless event-driven compute service that can receive triggers from various sources, such as file arrival at an S3 bucket or an event published on an SNS topic, etc. But in some use cases we would like to run lambda functions periodically or at specific times of the day, week or month. This blog will explain how to invoke AWS Lambda by a scheduled event.

Context

We are going to write a very simple lambda function in GO language. The function will only print the incoming event. A scheduled event will trigger this lambda function after every 1 minute. We will also learn how to build and deploy all the AWS resources required for this example using Cloudformation.

Before proceeding, I would suggest reading my previous post, which explains how to create and deploy the AWS Lambda function using cloudformation.

Cloudwatch Schedule Events

AWS CloudWatch Schedule Events allow for configuring triggers that run at specific days, times, or intervals. These triggers are useful for automating various AWS tasks and performing routine operations on a schedule. Schedule Events can initiate actions such as running an AWS Lambda function, or starting or stopping an EC2 instance.

The Schedule Expression used in CloudWatch Schedule Events is similar to a UNIX cron expression, allowing us to specify a fixed rate (e.g. every 10 minutes), a specific date and time, or a combination of both.

AWS Lambda supports cron and rate expressions, with a maximum frequency of 1 minute. This means that a Lambda function triggers after every 1 minute.

Rate and Cron Expression

You can find the details of cron and rate expression on this page. As a summary I am posting a screenshot from the mentioned page,

cron-and-rate-expression
cron-and-rate-expression [1]

GO function


The main function initiates the AWS Lambda runtime by invoking lambda.Start(HandleRequest). This triggers the execution of the HandleRequest function when the Lambda function is invoke

The HandleRequest function takes two arguments: a context.Context and an Event struct. The context.Context provides information about the runtime environment of the Lambda function, such as the deadline for executing the function. The Event struct represents the event that triggered the Lambda function.

The HandleRequest function logs the context and incoming event.

package main

import (
	"context"
	"log"
	"github.com/aws/aws-lambda-go/lambda"
)

type Event struct {
	Source string `json:"source,omitempty"`
	Action string `json:"action,omitempty"`
}

func HandleRequest(ctx context.Context, event Event) {
	log.Println("Context: ", ctx)
	log.Println("Event received: ", event)
}

func main() {
	lambda.Start(HandleRequest)
}

Dependency

We use Go version 1.19 in this example. You can learn how to setup GO Language development environment in this post. We also need to add aws-lambda-go library, which provides go libraries and tools needed to write lambda code.

go 1.19
require github.com/aws/aws-lambda-go v1.37.0

Resolve dependencies and identify potential problems,

go mod tidy
go mod vendor
go vet ./...

Build application,

GOOS=linux go build main.go

Build a zip file,

build-lambda-zip.exe -o aws-lambda-cron-go.zip main

Upload zip file to s3 bucket

aws s3 cp aws-lambda-cron-go.zip s3://azam-demo-s3-bucket/demo-lambda/aws-lambda-cron-go.zip

Cloudformation stack

This CloudFormation template creates a serverless infrastructure with a Lambda function, an IAM role, and a CloudWatch event rule.

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "A CF template to create a lambda functions triggered by cloudwatch scheduled event"
Parameters:
  pLambdaCodeBucket:
    Type: "String"
  pLambdaCodeS3KeyPath:
    Type: "String"
Resources:
  lfnLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: "Allow"
          Principal:
            Service: "lambda.amazonaws.com"
          Action: "sts:AssumeRole"
      Policies:
      - PolicyName: "lambdaCloudWatchPolicy"
        PolicyDocument:
          Statement:
          - Effect: "Allow"
            Action:
            - "logs:CreateLogGroup"
            - "logs:CreateLogStream"
            - "logs:PutLogEvents"
            Resource: "*"
  cwEventRuleLmabdaInvoke:
    Type: "AWS::Events::Rule"
    Properties:
      Description: "Cloud watch event rule to invoke lambda function"
      State: "ENABLED"
      ScheduleExpression: "rate(1 minute)"
      Targets:
      - Arn:
          Fn::GetAtt:
          - "lfnScheduleEventLambda"
          - "Arn"
        Id: "event_rule_schedule_lambda"
        Input: "{\"source\":\"cloud watch\", \"action\":\"invoke lambda\"}"
  cwEventPermInvokeLambda:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName:
        Ref: "lfnScheduleEventLambda"
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn:
        Fn::GetAtt:
        - "cwEventRuleLmabdaInvoke"
        - "Arn"
  lfnScheduleEventLambda:
    Type: "AWS::Lambda::Function"
    DependsOn:
    - "lfnLambdaRole"
    Properties:
      Architectures:
      - "x86_64"
      Runtime: "go1.x"
      Handler: "main"
      Code:
        S3Bucket:
          Ref: "pLambdaCodeBucket"
        S3Key:
          Ref: "pLambdaCodeS3KeyPath"
      Description: "This lambda function triggered by a cron job"
      FunctionName: "cron-job-function"
      Role:
        Fn::Sub: "${lfnLambdaRole.Arn}"
      Timeout: "120"
  • The IAM role named "lfnLambdaRole" allows the Lambda function to access CloudWatch logs.
  • The CloudWatch event rule, "cwEventRuleLmabdaInvoke", is created with a schedule expression of "rate(1 minute)" to trigger the Lambda function once per minute. The CloudWatch Schedule Event also contains a target for the Lambda function that includes an input JSON string.
  • The Lambda function, "lfnScheduleEventLambda", is defined with the code located in the S3 bucket and path specified in the "pLambdaCodeBucket" and "pLambdaCodeS3KeyPath" parameters, respectively. The function's handler is set to "main", with a runtime of "go1.x", and is assigned the previously defined IAM role, i.e. "lfnLambdaRole".
  • The "cwEventPermInvokeLambda" resource is a Lambda permission granting CloudWatch events the ability to invoke the Lambda function.

It's time to deploy cloudformation stack,

aws cloudformation deploy \
--template-file ./deploy/cf.yml \
--stack-name my-demo-cron-lambda-stack \
--capabilities CAPABILITY_IAM \
--parameter-overrides \
pLambdaCodeBucket=azam-demo-s3-bucket \
pLambdaCodeS3KeyPath=demo-lambda/aws-lambda-cron-go.zip

Cloudformation has deployed all the defined resources,

cloudformation-stack-deployed
cloudformation-stack-deployed

Just deployed lambda function,

lambda-function-created
lambda-function-created

Cloudwatch Event Rule, which triggers the lambda function after every minute.

cloudwatch-event-rule-view

Cloudwatch logs

Cloudwatch logs show schedule events called lambda function after every minute,

cloudwatch-logs
cloudwatch-logs

Conclusion

AWS Cloudwatch provides cron and rate expressions to generate events after specific intervals or at specific date and time. These events can be used for different purposes; running lambda functions is one of them. We saw how to invoke AWS Lambda function by a scheduled event. This is a very useful feature for periodically running a piece of code.