Skip to content

Instantly share code, notes, and snippets.

@picadoh
Last active March 20, 2024 12:20
Show Gist options
  • Save picadoh/815c11361d1a88419ea16b14fe044e85 to your computer and use it in GitHub Desktop.
Save picadoh/815c11361d1a88419ea16b14fe044e85 to your computer and use it in GitHub Desktop.
EC2 Instance Scheduling (Stop/Start) with Terraform
### Cloudwatch Events ###
# Event rule: Runs at 8pm during working days
resource "aws_cloudwatch_event_rule" "start_instances_event_rule" {
name = "start_instances_event_rule"
description = "Starts stopped EC2 instances"
schedule_expression = "cron(0 8 ? * MON-FRI *)"
depends_on = ["aws_lambda_function.ec2_start_scheduler_lambda"]
}
# Runs at 8am during working days
resource "aws_cloudwatch_event_rule" "stop_instances_event_rule" {
name = "stop_instances_event_rule"
description = "Stops running EC2 instances"
schedule_expression = "cron(0 20 ? * MON-FRI *)"
depends_on = ["aws_lambda_function.ec2_stop_scheduler_lambda"]
}
# Event target: Associates a rule with a function to run
resource "aws_cloudwatch_event_target" "start_instances_event_target" {
target_id = "start_instances_lambda_target"
rule = "${aws_cloudwatch_event_rule.start_instances_event_rule.name}"
arn = "${aws_lambda_function.ec2_start_scheduler_lambda.arn}"
}
resource "aws_cloudwatch_event_target" "stop_instances_event_target" {
target_id = "stop_instances_lambda_target"
rule = "${aws_cloudwatch_event_rule.stop_instances_event_rule.name}"
arn = "${aws_lambda_function.ec2_stop_scheduler_lambda.arn}"
}
# AWS Lambda Permissions: Allow CloudWatch to execute the Lambda Functions
resource "aws_lambda_permission" "allow_cloudwatch_to_call_start_scheduler" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.ec2_start_scheduler_lambda.function_name}"
principal = "events.amazonaws.com"
source_arn = "${aws_cloudwatch_event_rule.start_instances_event_rule.arn}"
}
resource "aws_lambda_permission" "allow_cloudwatch_to_call_stop_scheduler" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.ec2_stop_scheduler_lambda.function_name}"
principal = "events.amazonaws.com"
source_arn = "${aws_cloudwatch_event_rule.stop_instances_event_rule.arn}"
}
### IAM Role and Policy ###
# Allows Lambda function to describe, stop and start EC2 instances
resource "aws_iam_role" "ec2_start_stop_scheduler" {
name = "ec2_start_stop_scheduler"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
data "aws_iam_policy_document" "ec2_start_stop_scheduler" {
statement = [
{
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = [
"arn:aws:logs:*:*:*",
]
},
{
actions = [
"ec2:Describe*",
"ec2:Stop*",
"ec2:Start*"
]
resources = [
"*",
]
}
]
}
resource "aws_iam_policy" "ec2_start_stop_scheduler" {
name = "ec2_access_scheduler"
path = "/"
policy = "${data.aws_iam_policy_document.ec2_start_stop_scheduler.json}"
}
resource "aws_iam_role_policy_attachment" "ec2_access_scheduler" {
role = "${aws_iam_role.ec2_start_stop_scheduler.name}"
policy_arn = "${aws_iam_policy.ec2_start_stop_scheduler.arn}"
}
### AWS Lambda function ###
# AWS Lambda API requires a ZIP file with the execution code
data "archive_file" "start_scheduler" {
type = "zip"
source_file = "start_instances.py"
output_path = "start_instances.zip"
}
data "archive_file" "stop_scheduler" {
type = "zip"
source_file = "stop_instances.py"
output_path = "stop_instances.zip"
}
# Lambda defined that runs the Python code with the specified IAM role
resource "aws_lambda_function" "ec2_start_scheduler_lambda" {
filename = "${data.archive_file.start_scheduler.output_path}"
function_name = "start_instances"
role = "${aws_iam_role.ec2_start_stop_scheduler.arn}"
handler = "start_instances.lambda_handler"
runtime = "python2.7"
timeout = 300
source_code_hash = "${data.archive_file.start_scheduler.output_base64sha256}"
}
resource "aws_lambda_function" "ec2_stop_scheduler_lambda" {
filename = "${data.archive_file.stop_scheduler.output_path}"
function_name = "stop_instances"
role = "${aws_iam_role.ec2_start_stop_scheduler.arn}"
handler = "stop_instances.lambda_handler"
runtime = "python2.7"
timeout = 300
source_code_hash = "${data.archive_file.stop_scheduler.output_base64sha256}"
}
import boto3
# Boto Connection
ec2 = boto3.resource('ec2', 'eu-west-2')
def lambda_handler(event, context):
# Filters
filters = [{
'Name': 'tag:AutoStop',
'Values': ['true']
},
{
'Name': 'instance-state-name',
'Values': ['stopped']
}
]
# Filter stopped instances that should start
instances = ec2.instances.filter(Filters=filters)
# Retrieve instance IDs
instance_ids = [instance.id for instance in instances]
# starting instances
starting_instances = ec2.instances.filter(Filters=[{'Name': 'instance-id', 'Values': instance_ids}]).start()
import boto3
# Boto Connection
ec2 = boto3.resource('ec2', 'eu-west-2')
def lambda_handler(event, context):
# Filters
filters = [{
'Name': 'tag:AutoStop',
'Values': ['true']
},
{
'Name': 'instance-state-name',
'Values': ['running']
}
]
# Filter running instances that should stop
instances = ec2.instances.filter(Filters=filters)
# Retrieve instance IDs
instance_ids = [instance.id for instance in instances]
# stopping instances
stopping_instances = ec2.instances.filter(Filters=[{'Name': 'instance-id', 'Values': instance_ids}]).stop()
@sslnreddy
Copy link

i have tried same code and did not work for me,i mean could see aws cloudwatch and lambda function in place,
but its not stopping the ec2 instance.

@rhaddadi
Copy link

when i try to run the terraform plan it gives me this error:

on iam.tf line 23, in data "aws_iam_policy_document" "ec2_start_stop_scheduler":
23: statement = [

An argument named "statement" is not expected here. Did you mean to define a
block of type "statement"?

@Nisarg-Satani
Copy link

Nisarg-Satani commented Oct 18, 2021

i have tried same code and did not work for me,i mean could see aws cloudwatch and lambda function in place, but its not stopping the ec2 instance.

same issue, If you have resolved it kindly let me know.

@bharatkarvekar
Copy link

i have tried same code and did not work for me,i mean could see aws cloudwatch and lambda function in place, but its not stopping the ec2 instance.

same issue, If you have resolved it kindly let me know.

Please use block

data "aws_iam_policy_document" "ec2_start_stop_scheduler" {
statement {
actions = ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"]
resources = ["arn:aws:logs:::"]
effect = "Allow"
}
statement {
actions = ["ec2:Describe
","ec2:Stop*","ec2:Start*"]
resources = ["*"]
effect = "Allow"
}
}

@stecog
Copy link

stecog commented Nov 6, 2022

Thank you!

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