Skip to content

Instantly share code, notes, and snippets.

@ozgurgul
Forked from picadoh/cloudwatch.tf
Created March 25, 2021 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ozgurgul/47dc57fa0ccf131557cb23c44d06bb11 to your computer and use it in GitHub Desktop.
Save ozgurgul/47dc57fa0ccf131557cb23c44d06bb11 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment