Search code examples
aws-lambdaterraformaws-event-bridge

AWS Event Scheduler Not Triggering Lambda


I am taking inspiration from this AWS post to start and stop RDS automatically based on a schedule. Instead of using event trigger, i am using event scheduler as you can pass timezone with the cron expression. I see that my event is getting created, but it is not invoking the target lambda. Not able to debug why.

Relevant Code:

Event Bridge:

resource "aws_scheduler_schedule" "lambda-trigger-rule" {
  description = "Eventbridge schedule to trigger lambda that stops/starts RDS"
  flexible_time_window {
    mode = "OFF"
  }
  schedule_expression          = var.schedule_expression
  schedule_expression_timezone = "Europe/Amsterdam"
  target {
    arn      = var.lambda_function_arn
    role_arn = var.iam_role_arn
  }
}

Lambda:

locals {
  name                   = var.rds_shutdown ? "rds-shutdown" : "rds-start"
  lambda_output_path_dir = var.rds_shutdown ? "/tmp/tfartifacts_stp" : "/tmp/tfartifacts_strt"
}

data "aws_region" "current" {}


resource "null_resource" "install_dependencies" {
  provisioner "local-exec" {
    command = "mkdir -p ${local.lambda_output_path_dir} && cp -r ${path.module}/python/${local.name} ${local.lambda_output_path_dir}/python"
  }
  triggers = {
    always_run = timestamp()
  }
}

data "archive_file" "zip_the_python_code" {
  type        = "zip"
  source_dir  = "${local.lambda_output_path_dir}/python"
  output_path = "${local.lambda_output_path_dir}/${local.name}.zip"
  depends_on  = [null_resource.install_dependencies]
}

resource "aws_lambda_function" "this" {
  filename         = data.archive_file.zip_the_python_code.output_path
  source_code_hash = data.archive_file.zip_the_python_code.output_base64sha256
  function_name    = local.name
  role             = var.lambda_stop_start_role_arn
  handler          = "lambda_function.lambda_handler"
  runtime          = "python3.9"
  timeout          = 180
  vpc_config {
    subnet_ids         = var.subnet_ids
    security_group_ids = var.security_group_ids
  }
  environment {
    variables = {
      "REGION" = data.aws_region.current.name
      "KEY"    = "Auto-Start-Shutdown"
      "VALUE"  = true
    }
  }
}

IAM Role:


resource "aws_iam_role" "scheduler_role" {
  name                 = "scheduler_role_to_trigger_lambda"
  permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/AdministratorAccessPermissionBoundary"
  assume_role_policy   = jsonencode({
    "Version" = "2012-10-17"
    "Statement" : [
      {
        "Effect" : "Allow",
        "Principal" : {
          "Service" : "scheduler.amazonaws.com"
        },
        "Action" : "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_policy" "lambda_invoke_policy" {
  name        = "lambda_invoke_policy"
  description = "Permissions required by scheduler to invoke lambda function"
  policy      = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Action" : [
          "lambda:Invoke"
        ],
        "Effect" : "Allow",
        "Resource" : "*"
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "scheduler_lambda_invoke_policy_attachment" {
  role       = aws_iam_role.scheduler_role.name
  policy_arn = aws_iam_policy.lambda_invoke_policy.arn
}

But as seen here: enter image description here, event got scheduled at right time but it never invoked the lambda. How to fix this?


Solution

  • This is not exactly the response you are looking for, but I think it is worth mentioning. You don't need a Lambda function to start and stop an RDS instance. You can do it directly from EventBridge like this:

    resource "aws_scheduler_schedule" "lambda-trigger-rule" {
      description = "Eventbridge schedule to trigger lambda that stops/starts RDS"
      flexible_time_window {
        mode = "OFF"
      }
      schedule_expression          = var.schedule_expression
      schedule_expression_timezone = "Europe/Amsterdam"
      target {
        arn      = "arn:aws:scheduler:::aws-sdk:rds:stopDBInstance"
        role_arn = var.iam_role_arn
    
        input = jsonencode({
          DbInstanceIdentifier = var.rds_instance_identifier // here goes the instance identifier
        })
      }
    }
    

    You can find the complete code, including one to stop EC2 instances, here: https://github.com/andresmiguel/aws-lambda-rds/blob/main/terraform/environment/saver-bot/schedules.tf