How to schedule EC2 instances managed by Auto Scaling Groups (start/stop)
Tags: ec2, aws, python, lambda
Published: 2022-03-01 11:56:29

Many times, we have a lot of **cloud waste** because we never shut down our services/EC2 instances. One of the trickiest cases to control is the **Auto Scaling Groups**.

Now, this tutorial that is based on [Vicent's one](https://gist.github.com/veuncent), I'm going to help you automate this task using _AWS EventBridge_ and _Lambda_ functions.

It's important to highlight that you must create two Lambda functions:

1. One for **stopping** that is going to have a **0** as the environment variables (`MAX_SIZE` and `DESIRED_CAPACITY`).
2. One for **starting** that is going to have **1+** as the environment variables (`MAX_SIZE` and `DESIRED_CAPACITY`).

Now, the steps:

### Create new Lambda function and Start Event
1. Open AWS Lambda in the console and click `Create a Lambda function`.
2. Choose `Author from Scratch`.
3. Give a name like `ScheduleASStart`.
4. For `Runtime`, select `Python 3.9`.
5. Click on `Add Trigger`.
6. Choose `EventBridge (CloudWatch Events)`.
7. Click on `Create a new rule`.
8. Enter a `Rule name` and `Rule description`.
9. For `Rule type`, select `Schedule expression`.
10. For `Schedule expression`, enter a Cron expression to specify at which day(s) and time this event should trigger.  
For now, only specify the event to start servers (E.g., at the beginning of the weekday). We will configure the second event later in this tutorial.  
E.g., `cron(00 06 ? * MON-FRI *)` fires every weekday (Monday to Friday) at 6:00 AM UTC. See [this](http://www.cronmaker.com) website for a handy Cron calculator.
11. Click on `Add`.

### Configure function
10. On the `Configure function` page, enter a `Name` and `Description` for the function.
11. For `Code entry type` make sure `Edit code inline` is selected.
12. Delete the existing code and paste the following code into the code field:

	```json
	import os
	import boto3

	client = boto3.client('autoscaling')

	def get_env_variable(var_name):
		msg = "Set the %s environment variable"
		try:
			return os.environ[var_name]
		except KeyError:
			error_msg = msg % var_name

	def lambda_handler(event, context):
		auto_scaling_groups = get_env_variable('NAMES').split()

		for group in auto_scaling_groups:
			if servers_need_to_be_started(group):
				action = "Starting"
				min_size = int(get_env_variable('MIN_SIZE'))
				max_size = int(get_env_variable('MAX_SIZE'))
				desired_capacity = int(get_env_variable('DESIRED_CAPACITY'))
			else:
				action = "Stopping"
				min_size = 0
				max_size = 0
				desired_capacity = 0

			print (action + ": " + group)
			response = client.update_auto_scaling_group(
				AutoScalingGroupName=group,
				MinSize=min_size,
				MaxSize=max_size,
				DesiredCapacity=desired_capacity,
			)

			print (response)

	def servers_need_to_be_started(group_name):
		min_group_size = get_current_min_group_size(group_name)
		if min_group_size == 0:
			return True
		else:
			return False
		

	def get_current_min_group_size(group_name):
		response = client.describe_auto_scaling_groups(
			AutoScalingGroupNames=[ group_name ],
		)
		return response["AutoScalingGroups"][0]["MinSize"]
	```
    
13. For `Environment variables` add the following:
  `NAMES` - Space separated list of the Auto Scaling Groups you want to manage with this function 
  `MIN_SIZE` - Minimum size of the Auto Scaling Group(s) when EC2 instances are started. e.g., 0
  `MAX_SIZE` - Maximum size of the Auto Scaling Group(s) when EC2 instances are started. e.g., 1
  `DESIRED_CAPACITY` - Desired capacity of the Auto Scaling Group(s) when EC2 instances are started. e.g., 1

14. For `Handler`, make sure the value is `lambda_function.lambda_handler`.
15. For `Role`, select `Create a custom role`. An IAM wizard will open a new tab.

16. For `IAM Role`, select `Create a new IAM Role`.
17. Enter a `Role Name`.
18. Click on `View Policy Document` and click `Edit`. 
19. A warning popup will appear. Click `Ok`.
20. Add the following statement right after the closing `}` of `"Resource": "arn:aws:logs:*:*:*"
    }`

	```json
	,
	{
		  "Effect": "Allow",
		  "Action": "autoscaling:*",
		  "Resource": "*"
		}
	```

21. Click on `Allow`.

22. Back on the `Configure function` screen, make sure your new Role is selected under `Existing role`.
24. Click on `Next` and `Create Function`.

### Create Stop Event

1. Create a new function like the first section.
2. Configure the `Environment variables`:
  `NAMES` - Space separated list of the Auto Scaling Groups you want to manage with this function 
  `MIN_SIZE` - Minimum size of the Auto Scaling Group(s) when EC2 instances are started. e.g., 0
  `MAX_SIZE` - Maximum size of the Auto Scaling Group(s) when EC2 instances are started. e.g., 0
  `DESIRED_CAPACITY` - Desired capacity of the Auto Scaling Group(s) when EC2 instances are started. e.g., 0

3. Click on Add Trigger.
4. Choose `EventBridge (CloudWatch Events)`.
5. Click on `Create a new rule`.
6. Enter a `Rule name` and `Rule description`.
7. For `Rule type`, select `Schedule expression`.
8. For `Schedule expression`, enter a Cron expression to specify at which day(s) and time this event should trigger.  
E.g., `cron(00 20 ? * MON-FRI *)` fires every weekday (Monday to Friday) at 20:00 PM UTC. See [this](http://www.cronmaker.com) website for a handy Cron calculator.
9. Click on `Add`.

That's it, all done!  
You can test the function by hitting the `Test` button. The first time an `Input test event` popup will appear.  
For the `Sample event template` select `Scheduled event` and click `Save and test`.

# Acknowledgement
[This](http://blog.conygre.com/2016/11/18/starting-and-stopping-ec2-instances-using-a-lambda-and-cut-your-aws-bill-in-half/) tutorial on Nick Todd's blog helped me setting up this tutorial.

### Follow me on:

|  Personal  |  LinkedIn  |YouTube|Instagram|Cyber Prophets|Sharing Your Stories|
|:----------|:----------|:------------:|:------------:|:------------:|:------------:|
|[![Personal](https://raw.githubusercontent.com/FANMixco/federiconavarrete/master/img/favicons/favicon.png)](https://federiconavarrete.com)|[![LinkedIn](https://i.stack.imgur.com/idQWu.png)](https://www.linkedin.com/in/federiconavarrete)|[![YouTube](https://i.stack.imgur.com/CFPMR.png)](https://youtube.com/c/FedericoNavarrete)|[![Instagram](https://i.stack.imgur.com/PIfqY.png)](https://www.instagram.com/federico_the_consultant)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/cyber-prophets)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/sharing-your-stories)|

[![sponsor me](https://raw.githubusercontent.com/FANMixco/Xamarin-SearchBar/master/bmc-rezr5vpd.gif)](https://www.buymeacoffee.com/fanmixco)