5

I have setup the following in Terraform. So two event rules, start_event at 8am and stop_event at 6pm.

# Create cloudwatch event rules
resource "aws_cloudwatch_event_rule" "stop_ec2_event_rule" {
  name        = "stop-ec2-event-rule"
  description = "Stop EC2 instance at a specified time each day"
  schedule_expression = var.cloudwatch_schedule_stop
}

resource "aws_cloudwatch_event_rule" "start_ec2_event_rule" {
  name        = "start-ec2-event-rule"
  description = "Start EC2 instance at a specified time each day"
  schedule_expression = var.cloudwatch_schedule_start
}

Each event passes an action to the lambda

resource "aws_cloudwatch_event_target" "stop_ec2_event_rule_target" {
  rule      = aws_cloudwatch_event_rule.stop_ec2_event_rule.name
  target_id = "TriggerLambdaFunction"
  arn       = aws_lambda_function.lambda_rscheduler.arn
  input     = "{\"environment\":\"${var.environment}\", \"action\":\"stop\"}"
}

resource "aws_cloudwatch_event_target" "start_ec2_event_rule_target" {
  rule      = aws_cloudwatch_event_rule.start_ec2_event_rule.name
  target_id = "TriggerLambdaFunction"
  arn       = aws_lambda_function.lambda_rscheduler.arn
  input     = "{\"environment\":\"${var.environment}\", \"action\":\"start\"}"
}

This works


resource "aws_lambda_permission" "allow_cloudwatch" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.lambda_rscheduler.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.stop_ec2_event_rule.arn

This issue I'm facing is that I cannot get Terraform to associate the start_event with the lambda function. I go into the AWS console and I can manually add the CloudWatch start_event trigger to the lambda function.

If I have the start_event resources

resource "aws_lambda_permission" "allow_cloudwatch" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.lambda_rscheduler.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.start_ec2_event_rule.arn

It will complain that the statement_id is duplicated.

I needed something like the terraform aws_lambda_event_source_mapping but that only allows Lambda functions to get events from Kinesis, DynamoDB and SQS; and not a CloudWatch event.

How can I tell terraform to associate multiple cloudwatch events to the same lambda function; when I can manually do it from the AWS console?

2 Answers 2

6

statement_id is not compulsory, so you can safely omit it from your aws_lambda_permission and terraform will unique generate id for you automatically. You can also use count or for_each to save you some typing for aws_lambda_permission.

For example, using for_each you could define aws_lambda_permission to be:

resource "aws_lambda_permission" "allow_cloudwatch" {
  
  for_each      = {for idx, v in [
      aws_cloudwatch_event_rule.stop_ec2_event_rule,
      aws_cloudwatch_event_rule.start_ec2_event_rule
  ]: idx => v}
  
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.lambda_rscheduler.function_name
  principal     = "events.amazonaws.com"
  source_arn    = each.value.arn
}

Analogical versions could be written for aws_cloudwatch_event_rule and aws_cloudwatch_event_target so that your code is based on for_each or count without the copy-and-paste repetitions.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Marcin, just letting you know that your solution worked; but without the data module for the function_name function_name = aws_lambda_function.lambda_rscheduler.function_name
@munchine Ah yes. Thanks. I tested the code before making the answer, and forgot to remove data. Question was updated to fixed that now.
0

I faced a similar issue where there were multiple rules created by a for_each.

resource "aws_cloudwatch_event_rule" "eks_pod_scale_up_event" {
  for_each            = { for key, env in local.env_map[var.target_infra] : key => env }
  name                = "${each.value}-pod-scheduler-scale-up-event"
  description         = "Cron trigger lambda to scale up replica count"
  state               = "ENABLED"
  schedule_expression = "cron(30 5 * * ? *)"
}

I handled this with for_each like below. Remember to omit statement_id as same statement_id cannot be used for all of them. OR have dynamic value for statement_id

resource "aws_lambda_permission" "eks_pod_scale_up_event" {
  for_each       = aws_cloudwatch_event_rule.eks_pod_scale_up_event
  action         = "lambda:InvokeFunction"
  function_name  = aws_lambda_function.eks_pod_scheduler.function_name
  principal      = "events.amazonaws.com"
  source_arn     = each.value.arn
  source_account = data.aws_caller_identity.this.account_id
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.