Automating EC2 Deployment with Terraform and Lambda: A Guide to Stop Instances and Send Notification with SNS.
I’d like to demonstrate how you can deploy EC2 instances using Terraform. And create a lambda function to stop these instances and trigger relative notification using SNS.
What is EC2?
- Amazon EC2 (Elastic Compute Cloud) is a foundational service within Amazon Web Services (AWS) that offers resizable compute capacity in the cloud. Users deploy virtual servers (instances) easily, scaling capacity as needed. It provides diverse instance types and pricing options, integrates with other AWS services securely, and supports auto-scaling and load balancing. In essence, EC2 enables flexible, efficient cloud computing.
What is AWS Lambda?
- AWS Lambda is a serverless computing service by Amazon Web Services. It lets you run code without managing servers. Lambda automatically scales based on triggers like HTTP requests or data changes. You're charged only for the compute time used.
What is AWS SNS?
- AWS SNS (Simple Notification Service) is a fully managed messaging service by Amazon Web Services. It enables you to send notifications to distributed systems and applications through various communication protocols such as email, SMS, HTTP, or even to other AWS services like Lambda functions or SQS queues. It simplifies the process of sending notifications to a large number of subscribers or endpoints with high reliability and scalability.
What is Terraform and why use it?
Terraform is an open-source infrastructure as code (IaC) tool developed by HashiCorp. It allows users to define and provision infrastructure resources in a declarative configuration language.
There are several reasons to use Terraform:
Infrastructure as Code (IaC): Simplifies infrastructure management through code.
Multi-Cloud Support: Manages infrastructure across various cloud providers.
Automation: Automates provisioning and lifecycle management of resources.
State Management: Tracks and manages infrastructure state for efficient changes.
Scalability: Scales from small environments to large deployments.
Community and Ecosystem: Benefits from a robust community and ecosystem of reusable components and best practices.
Let's start:
Write a code to deploy ec2 instances:
- Here I used a module, which is a self-contained collection of Terraform configurations, that allows you to encapsulate and reuse infrastructure components, making it easier to manage and maintain your Terraform configurations. (module/ec2/main.tf)
provider "aws" {
region = "us-east-1"
}
#ec2 creation
resource "aws_instance" "windows_instances" {
count = var.count_value
ami = var.ami_value
instance_type = var.instance_type_value
tags = {
Name = "windowsec2-${count.index + 1}"
}
}
output "instance_ids" {
value = aws_instance.windows_instances[*].id
}
variable.tf under module/ec2
variable "ami_value" {
description = "This is ami value of windows instance"
}
variable "instance_type_value" {
description = "This is instance type"
}
variable "count_value" {
description = "Number of ec2 instance creation"
}
Create a lambda function to stop these EC2 instances. And trigger a relative notification.
import boto3 def lambda_handler(event, context): # Initialize EC2 client ec2_client = boto3.client('ec2') # Get list of running instances instances = ec2_client.describe_instances( Filters=[ { 'Name': 'instance-state-name', 'Values': ['running'] } ] )['Reservations'] # Stop each running instance for reservation in instances: for instance in reservation['Instances']: instance_id = instance['InstanceId'] ec2_client.stop_instances(InstanceIds=[instance_id]) print(f"Stopped instance {instance_id}") # Send notification sns_client = boto3.client('sns') sns_client.publish( TopicArn='your_sns_topic_are', Message='EC2 instances have been stopped successfully' )
Write code to trigger a lambda function using AWS cloud watch (main.tf).
provider "aws" { region = "us-east-1" } # EC2 Instance Creation module "ec2_instance" { source = "./module/ec2" count_value = 10 ami_value = "ami-0f9c44e98edf38a2b" instance_type_value = "t2.micro" } # IAM Role for Lambda Execution resource "aws_iam_role" "lambda_role" { name = "stopec2" assume_role_policy = jsonencode({ "Version" : "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" }] }) tags = { tag-key = "lambda-stopec2" } } # IAM Policy for Lambda Execution resource "aws_iam_policy" "lambda_policy" { name = "STOPEC2" description = "Policy for Lambda to stop EC2 instances and send SNS notification" policy = jsonencode({ "Version" : "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "ec2:StopInstances", "sns:Publish", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }] }) } # Attach Policy to Role resource "aws_iam_user_policy_attachment" "lambda_policy_attachment" { user = "your_iam_user_name" policy_arn = aws_iam_policy.lambda_policy.arn } # Lambda Function resource "aws_lambda_function" "stop_ec2_lambda" { filename = "lambda.zip" # Path to your Lambda function code function_name = "StopEC2Instances" handler = "lambda.lambda_handler" runtime = "python3.8" timeout = 900 role = aws_iam_role.lambda_role.arn source_code_hash = filebase64sha256("lambda.zip") } # Trigger for Lambda Function resource "aws_cloudwatch_event_rule" "trigger_lambda_rule" { name = "TriggerLambdaOnSchedule" description = "Trigger Lambda function to stop EC2 instances" schedule_expression = "cron(5 18 4 3 ? *)" #mention schedule time } resource "aws_cloudwatch_event_target" "lambda_target" { rule = aws_cloudwatch_event_rule.trigger_lambda_rule.name target_id = "lambda" arn = aws_lambda_function.stop_ec2_lambda.arn } # SNS Topic resource "aws_sns_topic" "ec2_stop_notification" { name = "EC2_Stop_Notification" } #SNS Subscription resource "aws_sns_topic_subscription" "snssubscription" { topic_arn = aws_sns_topic.ec2_stop_notification.arn protocol = "email" endpoint = "Enter email address" } resource "aws_sns_topic_subscription" "snssubscription_lambda" { topic_arn = aws_sns_topic.ec2_stop_notification.arn protocol = "lambda" endpoint = aws_lambda_function.stop_ec2_lambda.arn } # Lambda Permission to Execute from CloudWatch Events resource "aws_lambda_permission" "cloudwatch_permission" { statement_id = "AllowExecutionFromCloudWatch" action = "lambda:InvokeFunction" function_name = aws_lambda_function.stop_ec2_lambda.function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.trigger_lambda_rule.arn } # Lambda Permission to Publish SNS resource "aws_lambda_permission" "sns_publish_permission" { statement_id = "AllowExecutionToSNS" action = "lambda:InvokeFunction" function_name = aws_lambda_function.stop_ec2_lambda.function_name principal = "sns.amazonaws.com" source_arn = aws_sns_topic.ec2_stop_notification.arn }
Save the above code and run the command "terraform init" to initialize the terraform configuration directory then "terraform plan" to preview the changes and finally "terraform apply" to apply the changes described by the execution plan.
You will receive a notification on the mentioned email.