Simple EC2 Stopinator in Lambda
Tags: aws, lambda, ec2, cloud
Published: 2020-05-25 09:46:23

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](https://aws.amazon.com/solutions/implementations/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](https://stackoverflow.com/q/61587158/174777) 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](https://github.com/aws-john/simple-lambda-stopinator-for-ec2)

(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](https://en.wikipedia.org/wiki/Interrobang).)

To install it:

- Create an **IAM Role** and add an in-line policy with permissions from the [Stopinator IAM Role](https://github.com/aws-john/simple-lambda-stopinator-for-ec2/blob/master/iam_role.json)
- Create a new **AWS Lambda Python function** that uses the IAM Role
- Paste in the [Type 1 Stopinator code](https://github.com/aws-john/simple-lambda-stopinator-for-ec2/blob/master/stopinator_type_1.py)
- Go to **Amazon CloudWatch**, click **Events** and add a Scedule with a [cron expression](https://en.wikipedia.org/wiki/Cron) 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:

```python
# Provide regions here (eg ['us-west-2', 'ap-southeast-2'], or use [] for all regions
regions_to_check = []
```

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](https://stackoverflow.com/q/61587158/174777) 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](https://github.com/aws-john/simple-lambda-stopinator-for-ec2/blob/master/iam_role.json)
- Create a new **AWS Lambda Python function** that uses the IAM Role
- Paste in the [Type 2 Stopinator code](https://github.com/aws-john/simple-lambda-stopinator-for-ec2/blob/master/stopinator_type_2.py)
- 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:

```python
# To send a notification, insert SNS Topic ARN here
SNS_TOPIC_ARN = ''
```

- 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:

```python
# Provide regions here (eg ['us-west-2', 'ap-southeast-2'], or use [] for all regions
regions_to_check = []
```

---

If you find any problems with the Stopinators, feel free to [add an issue](https://github.com/aws-john/simple-lambda-stopinator-for-ec2/issues) to the GitHub repository.

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