kddiji
kddiji

Reputation: 225

Terraform with AWS lamda

I couldn't really find anything pertinent my issue. I am creating a Tf script that will trigger a lambda, cloudwatch event and IAM to stop and start some ec2's in my environment. see my code below and please let me know what I am doing wrong!!

main.tf

#
# Test 
#

provider "aws" {
    region = "us-gov-west-1"
    shared_credentials_file = "~/.aws/credentials"
    profile                 = "default"
}


resource "aws_iam_policy" "stop_start_ec2_policy" {
  name = "StopStartEC2Policy"
  path = "/"
  description = "IAM policy for stop and start EC2 from a lambda"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws-us-gov:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Start*",
        "ec2:Stop*",
        "ec2:DescribeInstances*"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role" "stop_start_ec2_role" {
  name = "StopStartEC2Role"

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

resource "aws_iam_role_policy_attachment" "lambda_role_policy" {
  role = "${aws_iam_role.stop_start_ec2_role.name}"
  policy_arn = "${aws_iam_policy.stop_start_ec2_policy.arn}"
}

resource "aws_lambda_function" "stop_ec2_lambda" {
  filename      = "ec2_lambda_handler.zip"
  function_name = "stopEC2Lambda"
  role          = "${aws_iam_role.stop_start_ec2_role.arn}"
  handler       = "ec2_lambda_handler.stop"
  source_code_hash = "${filebase64sha256("ec2_lambda_handler.zip")}"

  runtime = "python3.7"
  memory_size = "250"
  timeout = "60"
}

resource "aws_cloudwatch_event_rule" "ec2_stop_rule" {
  name        = "StopEC2Instances"
  description = "Stop EC2 nodes at 19:00 from Monday to friday"
  schedule_expression = "cron(0 19 ? * 2-6 *)"
}

resource "aws_cloudwatch_event_target" "ec2_stop_rule_target" {
  rule      = "${aws_cloudwatch_event_rule.ec2_stop_rule.name}"
  arn       = "${aws_lambda_function.stop_ec2_lambda.arn}"
}

resource "aws_lambda_permission" "allow_cloudwatch_stop" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.stop_ec2_lambda.function_name}"
  principal     = "events.amazonaws.com"
}

resource "aws_lambda_function" "start_ec2_lambda" {
  filename      = "ec2_lambda_handler.zip"
  function_name = "startEC2Lambda"
  role          = "${aws_iam_role.stop_start_ec2_role.arn}"
  handler       = "ec2_lambda_handler.start"
  source_code_hash = "${filebase64sha256("ec2_lambda_handler.zip")}"

  runtime = "python3.7"
  memory_size = "250"
  timeout = "60"
}

resource "aws_cloudwatch_event_rule" "ec2_start_rule" {
  name        = "StartEC2Instances"
  description = "Start EC2 nodes at 6:30 from Monday to friday"
  schedule_expression = "cron(30 6 ? * 2-6 *)"
}

resource "aws_cloudwatch_event_target" "ec2_start_rule_target" {
  rule      = "${aws_cloudwatch_event_rule.ec2_start_rule.name}"
  arn       = "${aws_lambda_function.start_ec2_lambda.arn}"
}

resource "aws_lambda_permission" "allow_cloudwatch_start" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.start_ec2_lambda.function_name}"
  principal     = "events.amazonaws.com"
}

my ec2_lambda_handler.py

import boto3
region = 'eu-central-1'
ec2 = boto3.client('ec2', region_name=region)
response = ec2.describe_instances(Filters=[
        {
            'Name': 'tag:Auto-Start',
            'Values': [
                'true',
            ]
        },
    ])

instances = []

for reservation in response["Reservations"]:
    for instance in reservation["Instances"]:
        instances.append(instance["InstanceId"])

def stop(event, context):
    ec2.stop_instances(InstanceIds=instances)
    print('stopped instances: ' + str(instances))

def start(event, context):
    ec2.start_instances(InstanceIds=instances)
    print('started  instances: ' + str(instances))

The error I am getting:

Plan: 11 to add, 0 to change, 0 to destroy.


Warning: Interpolation-only expressions are deprecated

  on main.tf line 65, in resource "aws_iam_role_policy_attachment" "lambda_role_policy":
  65:   role = "${aws_iam_role.stop_start_ec2_role.name}"

Terraform 0.11 and earlier required all non-constant expressions to be
provided via interpolation syntax, but this pattern is now deprecated. To
silence this warning, remove the "${ sequence from the start and the }"
sequence from the end of this expression, leaving just the inner expression.

Template interpolation syntax is still used to construct strings from
expressions when the template includes multiple interpolation sequences or a
mixture of literal strings and interpolations. This deprecation applies only
to templates that consist entirely of a single interpolation sequence.

(and 9 more similar warnings elsewhere)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_policy.stop_start_ec2_policy: Creating...
aws_cloudwatch_event_rule.ec2_start_rule: Creating...
aws_iam_role.stop_start_ec2_role: Creating...
aws_cloudwatch_event_rule.ec2_stop_rule: Creating...
aws_iam_role.stop_start_ec2_role: Creation complete after 1s [id=StopStartEC2Role]
aws_lambda_function.stop_ec2_lambda: Creating...
aws_lambda_function.start_ec2_lambda: Creating...
aws_cloudwatch_event_rule.ec2_start_rule: Creation complete after 2s [id=StartEC2Instances]
aws_cloudwatch_event_rule.ec2_stop_rule: Creation complete after 2s [id=StopEC2Instances]
aws_iam_policy.stop_start_ec2_policy: Creation complete after 2s [id=arn:aws-us-gov:iam::235856440647:policy/StopStartEC2Policy]
aws_iam_role_policy_attachment.lambda_role_policy: Creating...
aws_iam_role_policy_attachment.lambda_role_policy: Creation complete after 0s [id=StopStartEC2Role-20210211225204032900000001]

Error: Unable to load "ec2_lambda_handler.zip": open ec2_lambda_handler.zip: no such file or directory

My directory:

$ ls 
ec2_lambda_handler.py  main.tf

I have a feeling that the zip file is not there before Terraform runs but not sure how to fix that!! Please help!!!! Here the source code : https://medium.com/better-programming/minimize-the-costs-of-running-aws-ec2-instances-using-terraform-3999c5141830

Upvotes: 1

Views: 500

Answers (2)

tomarv2
tomarv2

Reputation: 833

this is what I use:

resource "aws_lambda_function" "lambda" {
  filename                      = "deployment-files/lambda_logs/file.zip"
  function_name                 = "${var.teamid}-${var.prjid}"
  role                          = var.role
  handler                       = var.handler
  source_code_hash              = base64sha256("file.zip")

  runtime                       = var.runtime == "" ? "null" :  var.runtime
  memory_size                   = var.memory_size == "" ? "null" :  var.memory_size
  timeout                       = var.timeout == "" ? "null" :  var.timeout
  description                   = var.description == "" ? "null" :  var.description

  tags                          = merge(local.shared_tags)

  environment {
    variables                   = var.environment_vars
  }
}

where payload_file is the local zipfile path and source_code_hash is the filename inside the lambda

Upvotes: 0

Marcin
Marcin

Reputation: 238517

You are not creating the zip at all. You have only ec2_lambda_handler.py. Normally you would use archive_file to zip it before you use it the lambda function:

data "archive_file" "zip" {
       type        = "zip"
       source_file = "ec2_lambda_handler.py"
       output_path = "ec2_lambda_handler.zip"
}

Then in your lambda:

resource "aws_lambda_function" "stop_ec2_lambda" {
  filename         = data.archive_file.zip.output_path
  function_name    = "stopEC2Lambda"
  role             = "${aws_iam_role.stop_start_ec2_role.arn}"
  handler          = "ec2_lambda_handler.stop"
  source_code_hash =  data.archive_file.zip.output_base64sha256

  runtime = "python3.7"
  memory_size = "250"
  timeout = "60"
}

Upvotes: 2

Related Questions