Deploy a lambda function using terraform

AWS Lambda terraform aws_lambda_function archive_file

4 min read | by Jordi Prats

To be able to deploy a lambda function there are several pieces that need to be deployed:

  • IAM roles: It might need to access to the AWS API, so we need to create an IAM role to control it's privileges
  • Triggering: We might want the function to get triggered with some event (that's what we are going to do on this post) or a load balancer
  • Code: What we want to execute
  • Lambda function itself

First we are going to create the IAM role that's going to use out lambda function. We'll have to take into account that the call will come form the lambda service so the IAM role needs to be set accordingly:

data "aws_iam_policy_document" "lambda_assume_role_policy_doc" {

  statement {
    actions = ["sts:AssumeRole"]
    effect  = "Allow"

    principals {
      type = "Service"
      identifiers = [ "lambda.amazonaws.com" ]
    }
  }
}

resource "aws_iam_role" "example_lambda" {
  name                  = "${var.appname}-packer-cleanup-lambda"
  path                  = "/cl/app/${var.appname}/"
  assume_role_policy    = data.aws_iam_policy_document.lambda_assume_role_policy_doc.json
  force_detach_policies = true
}

resource "aws_iam_policy" "example_ec2_operations" {
  name   = "${var.appname}-packer-cleanup-ec2-operations-policy"
  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:*",
            ],
            "Resource": [
                "*",
            ]
        }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "attach_iam_policy_to_iam_role" {
  count = try(var.config.example, false) ? 1 : 0

  role       = aws_iam_role.example_lambda.name
  policy_arn = aws_iam_policy.example_ec2_operations.arn
}

To trigger this function we are going to use a CloudWatch event that's going to be triggered everyday at midnight (UTC):

resource "aws_cloudwatch_event_rule" "every_day" {
  name                = "every-day"
  description         = "Fires every day at 8:00 UTC"
  schedule_expression = "cron(0 0 * * ? *)"
}

resource "aws_cloudwatch_event_target" "example_lambda_every_five_minutes" {
  rule = aws_cloudwatch_event_rule.every_day.name
  arn  = aws_lambda_function.example_lambda.arn
}

resource "aws_lambda_permission" "allow_cloudwatch_to_call_example_lambda" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.example_lambda.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.every_day.arn
}

To use a simple example for a lambda we are going to use the following python code that will just return a list of instance-id and it's state. We'll need the name of the function and it's file name:

def lambda_handler(event, context):
  ret_val = []
  try:
    ec2 = boto3.client('ec2', region_name='eu-west-1')
    response = ec2.describe_instances(Filters=instance_filter)

    for reservation in response['Reservations']:
        for instance in reservation["Instances"]:
          ret_val.append({'instance_id': instance["InstanceId"], 'state': instance["State"]["Name"]})
  except:
    pass

  return ret_val

Let's assume we have the code on a folder witting our terraform code called lambda/simple_example. We are going to name our file handler. The name of the handler (handler option) is going to be the file name and the name of the function we want to call. So, for this example it's going to be handler.lambda_handler

Then, we are going to tell terraform to generate a zip file on lambda/simple_example.zip (notice how it is outside of the code itself)

To finally, put all the pieces together with the aws_lambda_function resource:

data "archive_file" "zip_example" {
  type        = "zip"
  source_dir  = "${path.module}/lambda/simple_example/"
  output_path = "${path.module}/lambda/simple_example.zip"
}

resource "aws_lambda_function" "example_lambda" {
  filename         = data.archive_file.zip_example.output_path
  source_code_hash = data.archive_file.zip_example.output_base64sha256

  role          = aws_iam_role.example_lambda.arn
  function_name = "example_lambda_fx"
  handler       = "handler.lambda_handler"
  runtime       = "python3.8"
  timeout       = "600"

  reserved_concurrent_executions = 1
}

We can now wait for the trigger to execute it, or use the console to test the function: We can find it clicking on the function and then on the Test tab there is a Test button


Posted on 05/05/2022

Categories