Quoc Vu bio photo

Quoc Vu

Son, husband, father, and code addict.

Email LinkedIn Github

In this post, we will learn how to setup a Lambda function on AWS, that is triggered regularly by a CloudWatch event. And as bonus, we will also see how logs produced by the Lambda function are forwarded to CloudWatch. This is rather important because Lambda is an AWS service, thus we do not have access to the console to view our logs.

As a prerequisite, you need package your Lambda function and upload to a S3 bucket. Let say we have the code of the Lambda function at s3://my-lambda-function-bucket/my-function.zip. For this example, let’s pretend our function is written in NodeJS, and the handler (which is the javascript function to execute when the Lambda function is invoked) is in the index.js file and named executeMe().

The following are CloudFormation snippets that you could put in the Resources section of your CFN templates.

First we create a role for our Lambda function to assume. At the minimum, we need to grant permissions to write logs to CloudWatch. If your Lambda function needs access other services such as S3 or SQS, you can add those permissions to this policy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"ExampleRole": {
  "Type": "AWS::IAM::Role",
  "Properties": {
    "Path": "/",
    "AssumeRolePolicyDocument": {
      "Version" : "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": [ "lambda.amazonaws.com" ]
          },
          "Action": [ "sts:AssumeRole" ]
        }
      ]
    },
    "Policies": [
      {
        "PolicyName": "example-policy",
        "PolicyDocument": {
          "Version" : "2012-10-17",
          "Statement": [
            {
              "Action" : [ "logs:PutLogEvents", "logs:DescribeLogStreams", "logs:Create" ],
              "Effect" : "Allow",
              "Resource" : [
                "arn:aws:logs:*:*:*"
              ]
            }
          ]
        }
      }
    ]
  }
},

Now let’s create the log group and stream to use with our function. In this example we choose to keep logs for 90 days. As you can see, a log group may contain multiple log streams (see line 11).

1
2
3
4
5
6
7
8
9
10
11
12
13
"ExampleLogGroup": {
  "Type": "AWS::Logs::LogGroup",
  "Properties": {
    "RetentionInDays": 90
  }
},

"ExampleLogStream": {
  "Type": "AWS::Logs::LogStream",
  "Properties": {
    "LogGroupName": { "Ref": "ExampleLogGroup" }
  }
}

Next we create our Lambda function. Line 4 indicates which javascript function to execute and what file contains it. Line 5 associates the previously created role to this function. And lines 7-8 point to the zip file previously uploaded to S3.

1
2
3
4
5
6
7
8
9
10
11
12
13
"ExampleFunction": {
  "Type": "AWS::Lambda::Function",
  "Properties": {
    "Handler": "index.executeMe",
    "Role": { "Fn::GetAtt" : ["ExampleRole", "Arn"] },
    "Code": {
      "S3Bucket": "my-lambda-function-bucket",
      "S3Key": "my-function.zip"
    },
    "Runtime": "nodejs43",
    "Timeout": "30"
  }
}

Finally, we create a periodic CloudWatch event every 15 minutes and tie it to our function (line 8). We also need to give this event the permission to execute the function (line 14-22).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"ExampleEvent": {
  "Type": "AWS::Events::Rule",
  "Properties": {
    "ScheduleExpression": "rate(15 minutes)",
    "Targets": [
      {
        "Id": "ExampleScheduler",
        "Arn": { "Fn::GetAtt": [ "ExampleFunction", "Arn" ] }
      }
    ]
  }
},

"ExamplePermission": {
  "Type": "AWS::Lambda::Permission",
  "Properties": {
    "FunctionName": { "Fn::GetAtt": [ "ExampleFunction", "Arn" ] },
    "Action": "lambda:InvokeFunction",
    "Principal": "events.amazonaws.com",
    "SourceArn": { "Fn::GetAtt": [ "ExampleEvent", "Arn" ] }
  }
}

I whipped up an simple example to illustrate this. It’s a Lambda function that monitors a website at regular interval and sends SMS messages when things go wrong. You can check it out at https://github.com/quocvu/website-monitor.