DEV Community

Cover image for How to schedule EC2 instances managed by Auto Scaling Groups (start/stop)
Federico Navarrete
Federico Navarrete

Posted on • Updated on • Originally published at gist.github.com

How to schedule EC2 instances managed by Auto Scaling Groups (start/stop)

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, 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 website for a handy Cron calculator.
  11. Click on Add.

Configure function

  1. On the Configure function page, enter a Name and Description for the function.
  2. For Code entry type make sure Edit code inline is selected.
  3. Delete the existing code and paste the following code into the code field:

    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"]
    
  4. 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

  5. For Handler, make sure the value is lambda_function.lambda_handler.

  6. For Role, select Create a custom role. An IAM wizard will open a new tab.

  7. For IAM Role, select Create a new IAM Role.

  8. Enter a Role Name.

  9. Click on View Policy Document and click Edit.

  10. A warning popup will appear. Click Ok.

  11. Add the following statement right after the closing } of "Resource": "arn:aws:logs:*:*:*"
    }

    ,
    {
          "Effect": "Allow",
          "Action": "autoscaling:*",
          "Resource": "*"
        }
    
  12. Click on Allow.

  13. Back on the Configure function screen, make sure your new Role is selected under Existing role.

  14. 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 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 tutorial on Nick Todd's blog helped me setting up this tutorial.

Discussion (2)

Collapse
vikasj003 profile image
vikasj003

How to do it for multiple asg with different min max and desired capacity

Collapse
fanmixco profile image
Federico Navarrete Author • Edited on

You will need to modify the lambda for that and create your custom script. Most likely you will need to pass JSON values and cast them instead of env variables.