125

I would like to run an AWS lambda function every five minutes. In the AWS Management Console this is easy to set up, under the lambda function's "Event Sources" tab, but how do I set it up with Terraform?

I tried to use an aws_lambda_event_source_mapping resource, but it turns out that the API it uses only supports events from Kinesis and DynamoDB. When I try to use it with a scheduled event source, creation times out.

Vebjorn Ljosa
  • 17,438
  • 13
  • 70
  • 88
  • In the console, I can see the following: [![enter image description here](https://i.stack.imgur.com/Qh0w7.png)](https://i.stack.imgur.com/Qh0w7.png) Even though it says the rule state is "ENABLED", I still have to enable the trigger itself. Not sure why Terraform isn't doing this itself. – yangmillstheory Sep 03 '17 at 15:49

3 Answers3

254

You can use an aws_cloudwatch_event_target resource to tie the scheduled event source (event rule) to your lambda function. You need to grant it permission to invoke your lambda function; you can use an aws_lambda_permission resource for this.

Example:

resource "aws_lambda_function" "check_foo" {
    filename = "check_foo.zip"
    function_name = "checkFoo"
    role = "arn:aws:iam::424242:role/something"
    handler = "index.handler"
}

resource "aws_cloudwatch_event_rule" "every_five_minutes" {
    name = "every-five-minutes"
    description = "Fires every five minutes"
    schedule_expression = "rate(5 minutes)"
}

resource "aws_cloudwatch_event_target" "check_foo_every_five_minutes" {
    rule = aws_cloudwatch_event_rule.every_five_minutes.name
    target_id = "check_foo"
    arn = aws_lambda_function.check_foo.arn
}

resource "aws_lambda_permission" "allow_cloudwatch_to_call_check_foo" {
    statement_id = "AllowExecutionFromCloudWatch"
    action = "lambda:InvokeFunction"
    function_name = aws_lambda_function.check_foo.function_name
    principal = "events.amazonaws.com"
    source_arn = aws_cloudwatch_event_rule.every_five_minutes.arn
}
user1338062
  • 11,939
  • 3
  • 73
  • 67
Vebjorn Ljosa
  • 17,438
  • 13
  • 70
  • 88
  • 4
    Any tips on troubleshooting this? I can see in the console the cloudwatch events rule looks correct and in the lambda console it's listed as a trigger. It seems like the timer is firing but the invocations don't succeed and I see no evidence of the lambda getting invoked in cloudwatch logs. Are there any logs for the cloudwatch events timer itself? – Peter Lyons Mar 17 '17 at 21:26
  • What is the payload the function is invoked with here? – xtra Oct 30 '19 at 06:05
  • 2
    @xtra: example of what the event may look like: https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html#schedule_event_type – Vebjorn Ljosa Oct 30 '19 at 23:41
  • 3
    Worked for me. Actually what I missing was, the aws_lambda_permission. When we create the CW rule manually AWS does this. But for Terraform we need to invoke this as well. – Anuradha Fernando Sep 17 '20 at 04:41
  • I deployed my terraform code using all of the above resources but I can not see logs in cloud watch of the target lambda function. I can see a rule created in Eventbridge and the target is also set to the lambda function but I can not see the trigger in the lambda function. How will I know if it's running at the scheduled time? Any help is appreciated. Thanks – Shubhank Gupta May 20 '21 at 15:31
  • 1
    @ShubhankGupta That's a different story. If you deploy a Lambda like this with terraform, it does not have logging capabilities. Here's an awesome description: https://advancedweb.hu/how-to-manage-lambda-log-groups-with-terraform/ – marcellsimon Jul 31 '21 at 20:35
  • anyone knows how to also pass a simple JSON payload to the function, starting from this sample? I assume it would end-up in "detail" field from the event, but I do not understand from the docs (https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) which parameter could be used to specify it. – Matt-Mac-Muffin Jul 07 '23 at 11:47
6

Verbjorns Ljosa's answer only includes permissions for cloudwatch to invoke the lambda. Have you specified the proper policy and iam role that allows the lambda to perform its actions?

resource "aws_iam_role" "check_foo_role" {
  name="check-foo-assume-role"
  assume_role_policy="assume_role_policy.json"
}

with assume_role_policy.json

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

and a policy referencing the above resource iam role I.e. something like

resource "iam_role_policy" "check-foo-policy" {
  name="check-foo-lambda-policy"
  # referencing the iam role above
  role="${aws_iam_role.check_foo_role.id}"
  policy="check-foo-policy.json"
}

and finally the json specifying the policy, check-foo-policy.json.

{
  "Version": "2012-10-17",
  "Statement": [
    {
  "Effect": "Allow",
  "Action": [
    "logs:CreateLogGroup",
    "logs:CreateLogStream",
    "logs:PutLogEvents"
  ],
  "Resource": ["*"]
},
{
  "Effect": "Allow",
  "Action": [
    "abc:SomeAction",
    "abc:AnotherAction",
  ],
  "Resource": "some-arn-matching-the-actions"
}

Do note that you cannot specify a Resource restriction for the logs-related actions. abc:SomeAction might be ssm:GetParameter with an accompanying resource arn like "arn:aws:ssm:us-east-1:${your-aws-account-id}:parameter/some/parameter/path/*

GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
5

As an addition to the accepted answer. Often times one would want the zip-file for the lambda to be created by terraform as well. To do so one can use the archive_file data source:

data "archive_file" "lambda_zip" {
    type        = "zip"
    source_dir  = "src"
    output_path = "check_foo.zip"
}

resource "aws_lambda_function" "check_foo" {
    filename = "check_foo.zip"
    function_name = "checkFoo"
    role = "arn:aws:iam::424242:role/something"
    handler = "index.handler"
}

# then the rest from the accepted answer to trigger this

That is particularly helpful if the code is under version control, because then you can add the check_foo.zip to the .gitignore and there can never be a missmatch between the zip file and the source code that it is based on.

Falk Tandetzky
  • 5,226
  • 2
  • 15
  • 27