Creating a scheduled Lambda with SAM

Posted on

In this project we are going to using a Cloud Formation template to orchestrate a AWS::Event::Rule that triggers a lambda execution role, that runs a lambda function every minute.

Scaffolding our project

You will need sam installed on your computer.

We will be using sam to create our project. We will do this by running sam init.

You will be presented with an interactive terminal. I aim to make this post language agnostic but I chose the hello world template, using the go1.* runtime.

You will then have a skeleton project that looks something like;

drwxr-xr-x   9 some_user  some_user_group   288B  3 Jul 11:07 .
drwxr-xr-x  29 some_user  some_user_group   928B  3 Jul 14:19 ..
drwxr-xr-x   3 some_user  some_user_group    96B  3 Jul 11:13 .aws-sam
-rw-r--r--   1 some_user  some_user_group   170B  2 Jul 15:41 Makefile
-rw-r--r--   1 some_user  some_user_group   5.3K  2 Jul 15:27 README.md
-rw-r--r--   1 some_user  some_user_group    55B  2 Jul 15:38 env.json
drwxr-xr-x   5 some_user  some_user_group   160B  3 Jul 11:13 hello-world
-rw-r--r--   1 some_user  some_user_group   1.7K  3 Jul 11:07 template.yaml

The template.yaml file

We will need to make changes to the template.yaml file to create the resources need above.

Resources

We are going to need three resources; the lambda, the role to execute the lambda, and the schedule trigger.

The Lambda function

Nothing much to see here, but suffice to say that this is the lambda that we will be deploying. We will be watching the output in Cloudwatch.

package main

import (
	"fmt"
	"os"

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

func handler() {
	fmt.Printf("hello %s", os.Getenv("LOCATION"))
}

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

The code below will create a lambda function, the function code lives in the ./hello_world dir, the lambda will be created with one environment variable named Location with a value of World.

  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello-world/
      Handler: hello-world
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          LOCATION: World

The Event Trigger

We will need to create the trigger for the lambda. This particular trigger is configured using the cron syntax, and will run every minute. Note the target is the lambda function that we have configured above

  HelloWorldFunctionEventTrigger:
    Type: AWS::Events::Rule
    Properties:
      Description: Trigger lambda ever minute
      ScheduleExpression: 'cron(* * * * ? *)'
      State: ENABLED
      Targets:
        -
          Arn: !GetAtt HelloWorldFunction.Arn
          Id: HelloWorldFunctionEventTrigger
### The Permission

Finally we need to give the AWS::Events::Rule permission to run the AWS::Serverless::Function

PermissionForEventsToInvokeLambda:
  Type: AWS::Lambda::Permission
  Properties:
    FunctionName: !GetAtt HelloWorldFunction.Arn
    Action: "lambda:InvokeFunction"
    Principal: "events.amazonaws.com"
    SourceArn: !GetAtt HelloWorldFunctionEventTrigger.Arn

The full file

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  hello-world-lambda
  
  Sample SAM Template for hello-world-lambda

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 5

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello-world/
      Handler: hello-world
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          LOCATION: World

  PermissionForEventsToInvokeLambda:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt HelloWorldFunction.Arn
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: !GetAtt HelloWorldFunctionEventTrigger.Arn

  HelloWorldFunctionEventTrigger:
    Type: AWS::Events::Rule
    Properties:
      Description: Trigger lambda ever minute
      ScheduleExpression: 'cron(* * * * ? *)'
      State: ENABLED
      Targets:
        -
          Arn: !GetAtt HelloWorldFunction.Arn
          Id: HelloWorldFunctionEventTrigger
Outputs:
  HelloWorldFunction:
    Description: "First Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn

Building…

To build our project run sam build.

Building function 'HelloWorldFunction'
Running GoModulesBuilder:Build

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

Gotcha: You must run the build command to propagate changes to deploy

Deploying…

	sam deploy --region eu-west-1	--capabilities=CAPABILITY_IAM --stack-name hello-world-lambda --s3-bucket lambdas