Terraform, Python, AWS Lambda, and EventBridge | Schedule the start and stop of EC2 instances
To save cost, we must start EC2 instances during working hours and stop them during non-working hours. In this blog, we’ll automate it with a Python lambda function, schedule it using AWS EventBridge rules, and create everything with Terraform script.
We’ll create our EventBridge rules with an additional setting at Step — 4 & based on that setting, our Python function will start & stop the EC2. This settings will help you to write a single lambda function to start & stop the EC2 Instance.
Step — 0:
Before writing the terraform script, you need the version & variable files. version.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}# Configure the AWS Provider
provider "aws" {
region = var.aws_region
}
variable.tf
# AWS Region
variable "aws_region" {
description = "Region in which AWS Resources to be created"
type = string
default = "ap-southeast-1"
}
Step — 1:
Create a new policy & attach it with a role for our lambda functions. create a file & named it, policy-lbd-start-stop-ec2.json & paste the following JSON script.
{
"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*"
],
"Resource": "*"
}
]
}
create another file & name it, role-lbd-start-stop-ec2.json & paste the following JSON script.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
}
}
]
}
now write the terraform file to create the policy & role & attach the policy with the role. Named the file, iam-policy-role.tf paste the following terraform script.
resource "aws_iam_policy" "sgp-uat-policy-lbd-start-stop-ec2" {
name = "sgp-uat-policy-lbd-start-stop-ec2"
path = "/"
description = "Policy for Lambda function to control start and stop of EC2 instances"
policy = "${file("policy-lbd-start-stop-ec2.json")}"
}resource "aws_iam_role" "sgp-uat-role-lbd-start-stop-ec2" {
name = "sgp-uat-role-lbd-start-stop-ec2"
path = "/"
description = "Allows Lambda function to control start and stop of EC2 instances."
assume_role_policy = "${file("role-lbd-start-stop-ec2.json")}"
}resource "aws_iam_policy_attachment" "attach_iam_policy_to_iam_role" {
name = "attach_iam_policy_to_iam_role"
roles = ["${aws_iam_role.sgp-uat-role-lbd-start-stop-ec2.name}"]
policy_arn = "${aws_iam_policy.sgp-uat-policy-lbd-start-stop-ec2.arn}"
}
Step — 2:
Create a python function with boto 3, that will run when the lambda function trigger, to start & stop our EC2. Create a folder named, start_stop_lambda_function & name the file, start_stop_lambda_function.py & add the following code.
import boto3region = 'ap-southeast-1'
# instance IDs involved in the operation
instances = ['']
ec2 = boto3.client('ec2', region_name=region)def lambda_handler(event, context):
if 'operation' in event:
if event['operation'] == 'start':
ec2.start_instances(InstanceIds=instances)
print('started your instances: ' + str(instances))
elif event['operation'] == 'stop':
ec2.stop_instances(InstanceIds=instances)
print('stopped your instances: ' + str(instances))
else:
print('No operation matched')
else:
print('No operation detected')
in code line 3, the instance = [‘’] is blank. You can put your instance_id here directly, multiple instance id can be separated by a comma [isntance_1, instance_2] or, you can use lambda environment variable.
Step — 3:
Create the Lambda function with terraform & zip your python code, and name the file start-stop-lambda-function.tf paste the following terraform script.
data "archive_file" "zip_the_python_code" {
type = "zip"
source_dir = "${path.module}/start_stop_lambda_function/"
output_path = "${path.module}/start_stop_lambda_function/start_stop_lambda_function.zip"
}resource "aws_lambda_function" "sgp-uat-lbd-start-stop-ec2" {
filename = "${path.module}/start_stop_lambda_function/start_stop_lambda_function.zip"
function_name = "sgp-uat-lbd-start-stop-ec2"
role = aws_iam_role.sgp-uat-role-lbd-start-stop-ec2.arn
handler = "start_stop_lambda_function.lambda_handler"
runtime = "python3.9"
}
Step — 4:
Now create your EventBridge Rules & attach them with your Lambda function. Name the file, eventbridge-rules.tf & paste the following terraform script.
resource "aws_cloudwatch_event_rule" "sgp-uat-cw-rule-start-ec2" {
name = "sgp-uat-cw-rule-start-ec2"
description = "Trigger Lambda function to start EC2 instances"
schedule_expression = "cron(*/2 * * * ? *)"
}resource "aws_cloudwatch_event_rule" "sgp-uat-cw-rule-stop-ec2" {
name = "sgp-uat-cw-rule-stop-ec2"
description = "Trigger Lambda function to stop EC2 instances"
schedule_expression = "cron(*/10 * * * ? *)"
}resource "aws_cloudwatch_event_target" "sgp-uat-lbd-start-ec2" {
arn = aws_lambda_function.sgp-uat-lbd-start-stop-ec2.arn
rule = aws_cloudwatch_event_rule.sgp-uat-cw-rule-start-ec2.name
input = <<JSON
{
"operation":"start"
}
JSON
}resource "aws_cloudwatch_event_target" "sgp-uat-lbd-stop-ec2" {
arn = aws_lambda_function.sgp-uat-lbd-start-stop-ec2.arn
rule = aws_cloudwatch_event_rule.sgp-uat-cw-rule-stop-ec2.name
input = <<JSON
{
"operation":"stop"
}
JSON
}resource "aws_lambda_permission" "start-permission" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.sgp-uat-lbd-start-stop-ec2.arn
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.sgp-uat-cw-rule-start-ec2.arn
}resource "aws_lambda_permission" "stop-permission" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.sgp-uat-lbd-start-stop-ec2.arn
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.sgp-uat-cw-rule-stop-ec2.arn
}
In schedule_expression, I wrote the cron format which supports by EventBridge rules, & you can change it based on your requirements.
For directory setup & script reference, you can have a look at my GitHub repo.
Step — 5:
We’re now ready to run our terraform script. Make sure you export your aws_access_key_id & aws_secret_access_key in your machine. Run
terraform init
Run the below command to see what the terraform script will create
terraform plan
If you see everything is okay, then run the terraform apply command & put YES argument to create everything on your AWS account.
terraform apply
After successfully finishing the terraform execution, go back to your AWS Console & can see your Lambda function with the Python code, 2 EventBridge rules have been created with the cron job. Now, based on your cron job, the EC2 will run & stop automatically.