Keep Your EC2 Instances in Check and Prevent Over-provisioning with EventBridge

Ghita EL AMLAQUI
6 min readSep 26, 2024

--

Introduction

Managing EC2 instances efficiently is crucial for maintaining cost control and optimizing resource usage in your AWS environment. One of the most common issues teams face is over-provisioning EC2 instances, leading to unnecessary operational expenses and resource sprawl. But what if you could automate the enforcement of instance limits, ensuring that no more than a specified number of EC2 instances are running at any given time?

In this tutorial, we’ll walk you through how to use Amazon EventBridge to automatically detect and prevent over-provisioning of EC2 instances in your account. By setting up EventBridge rules and combining them with AWS Lambda, you’ll be able to enforce smart limits on instance launches, keeping your environment streamlined and cost-effective.

We’ll show you how to do this in the following steps:

  • Step 0: Create an SNS Topic and subscribe to it
  • Step 1: Create a lambda function
  • Step 2: Create an EventBridge rule
  • Step 3: Add the lambda function as a target
  • Step 4: Grant the EventBridge permission to invoke your Lambda function
  • Step 5: Test the EventBridge rule

Architecture

Here’s an architecture diagram of what we’ll implement:

Architecture diagram

Ready to take control of your EC2 provisioning ? Let’s dive in !

Prerequisites

Lambda’s execution role: To allow the Lambda function to manage EC2 instances and send notifications via SNS, you’ll need to create an IAM role with the appropriate permissions.

Here are the necessary permissions:

  • EC2 Permissions: “ec2:DescribeInstances”, “ec2:TerminateInstances
  • SNS Permissions: “sns:Publish

Steps:

Step 0: Create an SNS Topic and subscribe to it

  • Create a new SNS Topic
aws sns create-topic --name EC2StatusChangeTopic

Take note of the TopicArn from the output, we’ll need it in future steps.

> Output:

AWS CloudShell — create sns topic
  • Subscribe to the topic:
aws sns subscribe \
--topic-arn place_SNS_topic_arn_here \
--protocol email \
--notification-endpoint place_your_email_here

> Output:

AWS CloudShell — subscribe to the topic

Note: The email subscription requires the recipient to click a confirmation link. Until confirmed, the subscription remains in a pending state.

Step 1 : Create a lambda function

We need to create a Lambda function that runs every time a new EC2 instance is launched, ensuring that no more than two instances are running in your AWS account.

The Lambda function performs the following actions:

  • Retrieves the instance ID of the newly launched EC2 instance
  • Counts the total number of currently running EC2 instances
  • Terminates the newly launched instance if there are already two running instances
  • Publishes a message to an SNS topic

The flowchart below illustrates this process in detail.

Lambda function flowchart

>>Lambda’s code: (Python)

import json
import boto3

# Initialize clients for EC2 and SNS
ec2_client = boto3.client('ec2')
sns_client = boto3.client('sns')

# SNS topic ARN
SNS_TOPIC_ARN = 'place_sns_topic_arn_here'

def lambda_handler(event, context):
# 1. Get the instance ID from the event
instance_id = event['detail']['instance-id']
print(f"Instance ID from event: {instance_id}")

# 2. Get the count of running EC2 instances
response = ec2_client.describe_instances(
Filters=[
{
'Name': 'instance-state-name',
'Values': ['running']
}
]
)

running_instances = []

for reservation in response['Reservations']:
for instance in reservation['Instances']:
running_instances.append(instance['InstanceId'])

running_count = len(running_instances)
print(f"Number of running instances: {running_count}")

# 3. If running instances >= 2, terminate the new instance
if running_count >= 2:
print(f"Terminating instance: {instance_id}")
ec2_client.terminate_instances(InstanceIds=[instance_id])

message = f"Instance {instance_id} has been terminated because there are more than 2 running instances."
else:
message = f"Instance {instance_id} is running. There are currently {running_count} instances running."

# 4. Send an SNS notification
response = sns_client.publish(
TopicArn=SNS_TOPIC_ARN,
Subject='EC2 Instance Status Alert',
Message=message
)

print(f"SNS notification sent: {response['MessageId']}")

return {
'statusCode': 200,
'body': json.dumps('Lambda executed successfully!')
}

Step 2: Create an EventBridge rule

AWS EventBridge Rules enable you to respond to events from AWS services, custom applications, and SaaS partners by directing them to specified target services. In this tutorial, we’re creating a rule that captures EC2 instance launch events, particularly the ‘pending’ state, as every EC2 instance enters this state during the initial launch process.

aws events put-rule \
--name "monitoring-ec2-launch-state" \
--event-pattern '{
"source": ["aws.ec2"],
"detail-type": ["EC2 Instance State-change Notification"],
"detail": {
"state": ["pending"]
}
}' \
--description "Monitors EC2 instances when they are in a pending state"

> Output:

AWS CloudShell — Eventbridge put-rule command

From the AWS console we can see that our eventBridge rule was successfully created

AWS Console — Eventbridge rules

Step 3: Add the lambda function as a target

With the EventBridge rule now properly defined, we need to set up a target that will be triggered whenever an event matches the rule. To achieve this, we’ll run the following command:

aws events put-targets \
--rule "monitoring-ec2-launch-state" \
--targets "Id"="1","Arn"="place_lambda_arn_here"

> Output:

AWS CloudShell — Eventbridge put target command

From the AWS console, a new target was added to the lambda function:

AWS Console — Lambda function

Step 4: Grant the EventBridge permission to invoke your Lambda function

To allow EventBridge to invoke your Lambda function, you need to grant the necessary permissions by updating the Lambda function’s resource-based policy. This can be done using the aws lambda add-permission command, which specifies the source as EventBridge and allows it to invoke the function when events match the rule.

aws lambda add-permission \
--function-name place_lambda_function_name_here \
--statement-id "monitoring-ec2-launch-state-permission" \
--action "lambda:InvokeFunction" \
--principal events.amazonaws.com \
--source-arn place_eventbridge_rule_arn_here

> Output:

AWS CloudShell — aws lambda add-permission command

Step 5: Test the EventBridge rule

To test this solution, we’ll need to launch a new EC2 instance. This action should trigger the EventBridge rule, which will then invoke the Lambda function. Depending on the number of instances currently running in the account, we will encounter one of two scenarios:

  • Scenario 1: Launch the first EC2 instance in your AWS account

In this case, the Lambda function should allow the instance to remain active since the condition for termination is not met (not more than two instances are running in the account).

> Email notification:

SNS Email — Scenario 1: launching the first ec2 instance
  • Scenario 2: Launch a third EC2 instance in your AWS account

The function will check if there are already two running EC2 instances; if so, it will terminate the newly launched instance and send an SNS email notification.

> Email notification:

SNS Email — Scenario 2: launching a third ec2 instance

Conclusion

In conclusion, using EventBridge rules with a Lambda function provides a robust solution to prevent EC2 over-provisioning in your AWS account. By enforcing a limit of two running instances, this setup ensures that your environment stays within predefined resource limits, helping to control costs and avoid unnecessary usage.

--

--

Ghita EL AMLAQUI
Ghita EL AMLAQUI

Written by Ghita EL AMLAQUI

Software engineer | Data engineer | AWS Certified

No responses yet