DEV Community

Cover image for Simple EC2 Stopinator in Lambda
John Rotenstein for AWS

Posted on

Simple EC2 Stopinator in Lambda

A Stopinator is the general term applied to a utility that can start/stop Amazon EC2 instances on a particular schedule or in particular circumstances.

There are quite a few Stopinators available, such as the Instance Scheduler from AWS. However these Stopinators tend to be quite complex, involving CloudFormation templates and databases.

Therefore, I set myself a challenge to create a Simple Lambda Stopinator to help people in situations such as this StackOverflow question asking how to send a notification when an EC2 instance is still running and then eventually terminate it after a given period. How simple? Just paste some code into a Lambda function and you're done!

I have, in fact, written two stopinators:

  • Type 1: Run this Lambda function once per night to stop/terminate tagged Amazon EC2 instances
  • Type 2: Run this Lambda function throughout the day to notify/stop/terminate instances after a given period of time

You can find them in: GitHub: Simple Lambda Stopinators

(I hope to also write another Stopinator with start/stop scheduling, but that gets a bit more complicated with weekdays/weekends and timezones.)

Here's an explanation of the Stopinators and how to use them.


Type 1 Stopinator: Stop/Terminate instances at night

I originally wrote this Stopinator when I was delivering AWS training courses. I would launch Amazon EC2 instances for demonstrations during the day and would often forget to terminate them. So, I wanted a script that could identify instances tagged as 'temporary' and terminate them at night, so things were clean for teaching the next day.

However, I didn't always remember to tag the temporary instances, so I also added functionality that would stop unidentified instances at night rather than terminating them (just in case I still needed them). This then also required the ability to mark instances that the Stopinator should 'ignore' because I wanted them to keep running.

This is how the Type 1 Stopinator works:

  • The Lambda function should be scheduled to run each night (eg 7pm) using Amazon CloudWatch Events
  • It looks at every running instance in every region (or just the regions you specify)
  • If the instance has an Auto-Stop = Terminate tag, it terminates the instance
  • If the instance has an Auto-Stop = Ignore tag, it does nothing
  • Otherwise, it stops the instance

That's it — simple, eh‽ (Yes, that's an interrobang.)

To install it:

  • Create an IAM Role and add an in-line policy with permissions from the Stopinator IAM Role
  • Create a new AWS Lambda Python function that uses the IAM Role
  • Paste in the Type 1 Stopinator code
  • Go to Amazon CloudWatch, click Events and add a Scedule with a cron expression that triggers the Lambda function each night. Be careful — AWS uses the UTC timezone, so you'll need to adjust for your particular timezone.

By default, it will automatically stop any untagged instances. If you want to terminate instances instead of stopping them, add an Auto-Stop = Terminate tag (that is, Key = Auto-Stop, Value = Terminate). If there are instances you don't want it to touch, use Auto-Stop = Ignore.

You can also edit this section to control which regions are checked:

# Provide regions here (eg ['us-west-2', 'ap-southeast-2'], or use [] for all regions
regions_to_check = []
Enter fullscreen mode Exit fullscreen mode

That's it. So simple! Enjoy.


Type 2 Stopinator: Stop/Terminate/Notify instances after a given duration

This Stopinator was inspired by a StackOverflow question that was asking for a way to send notifications about a running instance, and then eventually turn it off.

This AWS Lambda function needs to be scheduled to run at regular intervals, so that it continually checks whether instances have exceeded their duration. The more often it runs, the closer it will match the desired running duration.

This is how the Type 2 Stopinator works:

  • The Lambda function should be scheduled to run throughout the day (eg every 5 minutes) using Amazon CloudWatch Events
  • It looks at every running instance in every region (or just the regions you specify) and checks for one of these tags (in this order):
    • Terminate-After
    • Stop-After
    • Notify-After (or any tag that starts with Notify-After, such as Notify-After1, Notify-After2, etc)
  • It checks the duration supplied in the tag value (eg 30m, 1.5h, 24h)
  • If the instance has been running longer than the given duration, it will terminate/stop the instance or send a notification to an Amazon SNS topic.

To install it:

  • Create an IAM Role and add an in-line policy with permissions from the Stopinator IAM Role
  • Create a new AWS Lambda Python function that uses the IAM Role
  • Paste in the Type 2 Stopinator code
  • Go to Amazon CloudWatch, click Events and add a Schedule at a "Fixed rate of 5 minutes" (or whatever frequency you wish) that triggers the Lambda function
  • Optional: Create an Amazon SNS topic and insert the ARN in this line at the top of the Lambda function:
# To send a notification, insert SNS Topic ARN here
SNS_TOPIC_ARN = ''
Enter fullscreen mode Exit fullscreen mode
  • Then, subscribe to the Amazon SNS topic to receive notifications

To remember whether a notification has been sent, the Stopinator will remove the Notify-After tag once it has been used. This allows multiple notifications to be tracked by supplying multiple tags that start with Notify-After in the tag key.

You can also edit this section to control which regions are checked:

# Provide regions here (eg ['us-west-2', 'ap-southeast-2'], or use [] for all regions
regions_to_check = []
Enter fullscreen mode Exit fullscreen mode

If you find any problems with the Stopinators, feel free to add an issue to the GitHub repository.

If they have been useful for you, feel free to add a comment below. Enjoy!

Top comments (3)

Collapse
 
qfarhanm profile image
Mohammed Farhan

Hi,

Hoping you can provide a solution to my ask.

So, I have an ask to terminate all the EC2 instances that haven't been started in the past 30 days. Basically all it is that if an EC2 instance is in a stopped state for 30 days, it needs terminating.

I have gone through your stopinator article (which is cool by the way).

How can I proceed with my ask?

One solution that I have is scheduling an even in AWS Eventbridge using the cron job to trigger a lambda function (python code) to check the instance state of the ec2s and terminate any that have been in a stopped state for 30 days.

One drawback that I have is that these instances are not tagged and even if they are, the tags make no sense.

I have found a code but unsure if it will work?

import boto3
import re
from datetime import datetime

TERMINATION_AGE = 30

ec2_client = boto3.client('ec2', region_name='ap-southeast-2')

Get a list of stopped instances

instances = ec2_client.describe_instances(Filters=[{'Name': 'instance-state-name', 'Values': ['stopped']}])

for reservation in instances['Reservations']:
for instance in reservation['Instances']:

    # StateTransitionReason might be like "i-xxxxxxxx User initiated (2016-05-23 17:27:19 GMT)"
    reason = instance['StateTransitionReason']
    date_string = re.search('User initiated \(([\d-]*)', reason).group(1)
    if len(date_string) == 10:
        date = datetime.strptime(date_string, '%Y-%m-%d')

        # Terminate if older than TERMINATION_AGE
        if (datetime.today() - date).days > TERMINATION_AGE:
            ec2_client.terminate_instance
Enter fullscreen mode Exit fullscreen mode
Collapse
 
rosswilliams profile image
rosswilliams

I’d scope down the IAM role in Type 1. Adding a condition to only allow termination of instances with certain tags makes this a lot safer.

Collapse
 
aws_john profile image
John Rotenstein

Yes, that's an interesting idea. Effectively it's limiting the Lambda function to only do what it's meant to do, so the role can't be used to stop/terminate other instances. Of course, this then needs limitations on who can edit Tags.

I'll admit I was lazy — I made one IAM Role that would work with both Stopinators, but it should be scoped-down to be safer.