Skip to content

Instantly share code, notes, and snippets.

@wshihadeh
Last active February 5, 2024 09:36
Show Gist options
  • Save wshihadeh/e13ed919e11031ae9c2e417da2ffa904 to your computer and use it in GitHub Desktop.
Save wshihadeh/e13ed919e11031ae9c2e417da2ffa904 to your computer and use it in GitHub Desktop.
Terraform script for defining stop and start functionality for EC2 instances
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: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"
}
@kdiji
Copy link

kdiji commented Feb 11, 2021

Thank you so much for providing this. I am new to terraform can you please share your variables.tf?

@wshihadeh
Copy link
Author

it could be something like

variable "region" {
  type = "string"
  description = "Create an ec2 instacne in a given region"
}
variable "ami" {
  type = "string"
  description = "Image ami id"
}
variable "instance_type" {
  type = "string"
  description = "Instance type"
}
variable "instance_name" {
  type = "string"
  description = "Name to be used on all resources as prefix"
}
variable "name" {
  type = "string"
  description = "Name of all the resources"
}
variable "instance_count" {
  description = "Number of instances to launch"
  type        = number
  default     = 1
}
variable "profile" {
  default     = "terraform"
  description = "Using terraform for deployment"
}

and you may need another file for the values of these vars

region="eu-central-1"
ami="ami-dd3c0f36"
instance_type="t2.xlarge"
instance_name="dev01-docker01"
instance_count=1
instance_size="100"

@kdiji
Copy link

kdiji commented Feb 16, 2021

You are a life saver!! We are implementing it in our dev environment. Let say if I would like to use different tags based on the region to make more functional. Obviously I would have to add the necessary code to the main.tf by updating the time but I am scratching my head on the lambda part a I am new to it. I would appreciate any feedback you have hero. See my code below :

import boto3
region = 'us-gov-west-1'
ec2 = boto3.client('ec2', region_name=region)
response = ec2.describe_instances(Filters=[
        {
            'Name': 'tag:Auto-Start-EST',
            'Values': [
                'true',
            'Name': 'tag:Auto-Start-MST',
            'Values': [
                'true',
            'Name': 'tag:Auto-Start-PST',
            '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))

@kdiji
Copy link

kdiji commented Feb 17, 2021

Never mind my question. To make things simpler I will just create a module for each region (EST, PST, MST) with their respective variables and output tfs. Thoughts!!! ;)

@wshihadeh
Copy link
Author

it seems that you have a syntax issue here, you are missing couple of ]

response = ec2.describe_instances(Filters=[
        {
            'Name': 'tag:Auto-Start-EST',
            'Values': [
                'true',
            'Name': 'tag:Auto-Start-MST',
            'Values': [
                'true',
            'Name': 'tag:Auto-Start-PST',
            'Values': [
                'true',
            ]
        },
    ])

@swaroopsai999
Copy link

Can you please provide for the Azure too to Start and stop the VM's

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment