0
votes

This is the resource for anybody trying to test it https://cloudywithachanceofbigdata.com/really-simple-terraform-infrastructure-automation-using-aws-lambda/.

My tf scripts create:

  • an IAM Role and associated policy for the Lambda Function
  • the Lambda function
  • a Cloudwatch event rule and trigger

my terraform version:

Terraform v0.13.4

my main.tf is below:

#
# Module Provider
#

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

#
# Create IAM Role and Policy for Lambda Function
#

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

resource "aws_iam_role_policy" "lamdba_ec2_shutdown_policy" {
  name = "lamdba_ec2_shutdown_policy"
  role = "${aws_iam_role.lambda_stop_ec2.id}"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Stop*",
        "ec2:DescribeInstances"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}

#
# Create ZIP Archive for Function Source Code
#

data "archive_file" "lambda_stop_ec2_zip" {
  type = "zip"
  output_path = "/home/test/scheduler/repo/simple-lambda-ec2-scheduler/lambda_stop_ec2_zip"
  source_dir = "/home/test/scheduler/repo/simple-lambda-ec2-scheduler/function_source_code/"
}

#
# Create Lambda Function
#
 
resource "aws_lambda_function" "lambda_stop_ec2" {
  filename = "lambda_stop_ec2_zip"
  function_name    = "lambda_stop_ec2"
  timeout          = 10  
  role             = "${aws_iam_role.lambda_stop_ec2.arn}"
  handler          = "lambda_stop_ec2.lambda_handler"
  runtime          = "python3.8"
}

#
# Create CloudWatch Event Rule
#

resource "aws_cloudwatch_event_rule" "stop_ec2_event_rule" {
  name        = "stop-ec2-event-rule"
  description = "Stop running EC2 instance at a specified time each day"
  schedule_expression = "${var.schedule_expression}"
}

#
# Create CloudWatch Event Target
#

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_stop_ec2.arn}"
  input     = "{\"name\":\"${var.name}\"}"
}

#
# Add Lamdba Permission
#

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

my terraform.tfvars:

schedule_expression = "cron(0 17 * * ? *)"
name = "instanceScheduler"

my variables.tf:

variable "schedule_expression" {}
variable "name" {}

my function_source_code/lambda_stop_ec2.py

import boto3
region = 'us-west-1'

def lambda_handler(event, context):
    name = event["name"]
    print("stopping all instances in the %s name" % (name))
    ec2 = boto3.client('ec2', region_name=region)
    response = ec2.describe_instances(
        Filters=[
            {
                'Name': 'tag:Name',
                'Values': [name]
            }
        ]
    )
    for reservation in response["Reservations"]:
        for instance in reservation["Instances"]:
            print("instance [%s] is in [%s] state" % (instance["InstanceId"], instance["State"]["Name"]))
            if instance["State"]["Name"] == "running":
                print("stopping instance [%s]" % (instance["InstanceId"]))
                ec2.stop_instances(InstanceIds=[instance["InstanceId"]])
                print("instance [%s] stopped" % (instance["InstanceId"]))

my running commands:

terraform init
terraform apply

what I get back:

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


Warning: Interpolation-only expressions are deprecated

  on main.tf line 38, in resource "aws_iam_role_policy" "lamdba_ec2_shutdown_policy":
  38:   role = "${aws_iam_role.lambda_stop_ec2.id}"

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 6 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_cloudwatch_event_rule.stop_ec2_event_rule: Creating...
aws_iam_role.lambda_stop_ec2: Creating...
aws_iam_role.lambda_stop_ec2: Creation complete after 1s [id=lambda_stop_ec2]
aws_iam_role_policy.lamdba_ec2_shutdown_policy: Creating...
aws_lambda_function.lambda_stop_ec2: Creating...
aws_cloudwatch_event_rule.stop_ec2_event_rule: Creation complete after 2s [id=stop-ec2-event-rule]
aws_lambda_function.lambda_stop_ec2: Still creating... [10s elapsed]
aws_lambda_function.lambda_stop_ec2: Creation complete after 16s [id=lambda_stop_ec2]
aws_lambda_permission.allow_cloudwatch: Creating...
aws_cloudwatch_event_target.stop_ec2_event_rule_target: Creating...
aws_lambda_permission.allow_cloudwatch: Creation complete after 1s [id=AllowExecutionFromCloudWatch]
aws_cloudwatch_event_target.stop_ec2_event_rule_target: Creation complete after 1s [id=stop-ec2-event-rule-TriggerLambdaFunction]

Error: Error putting IAM role policy lamdba_ec2_shutdown_policy: MalformedPolicyDocument: Partition "aws" is not valid for resource "arn:aws:logs:*:*:*".
        status code: 400, request id: b2e4b11e-da82-4b1d-b482-8cc2a3afd242

Please help!!

1

1 Answers

1
votes

In the policy example shown at https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html

The resource is "arn:aws:logs:*:*:*"

See the 3 asterisks. Yours only have 1. I have tested your policy at IAM Policy Simulator (https://policysim.aws.amazon.com/) and it is invalid due to the missing asterisks

Full policy sample:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "logs:DescribeLogStreams"
    ],
      "Resource": [
        "arn:aws:logs:*:*:*"
    ]
  }
 ]
}