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"
}
@r24146887
Copy link

Can you also provide the steps to run this? I have a python file with stop and start code, I have downloaded terraform on my machine.. Can you please share the steps to stop or start any instance, please?

@wshihadeh
Copy link
Author

wshihadeh commented May 2, 2020

You need to create a terraform project first and move the above code to main.tf and add also the provider and var files
commands to apply to terraform

terraform init -backend-config="${Your_vars_file}"
terraform plan -var-file="${Your_vars_file}" -lock=false
terraform apply

commands to run the above code

# config
terraform init -backend-config="${Your_vars_file}"; 
terraform init -var-file="${Your_vars_file}"; 
zip ec2_lambda_handler.zip ec2_lambda_handler.py

#apply
terraform apply -var-file="${Your_vars_file}"

#destory
terraform destroy -var-file="${Your_vars_file}"

@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