Schedule EC2 Start/Stop and Save Money

Schedule EC2 Start/Stop and Save Money

You may have heard you could schedule your instances to be stopped after hours or on weekends to save money but don't know how to. Don't worry, that's exactly what I am going to demonstrate in this article, I will be using AWS lambda to stop the instances at 6 pm and start them in the morning at 6 am.

Summary

Set a cron job to trigger lambda functions that will shutdown/Start the instances based on their selection criteria. I am using tags as my criteria to select instances. You could select instances based on their architecture, instance type, kernel ID, etc.

Why would you implement this solution?

It is a simple, serverless, and AWS native solution, plus it is very cheap(lambda pricing). Additionally, depending on how many instances you stop using this solution, you will be reducing your AWS monthly bill significantly

Solution Setup Steps

1. Instances Selection criteria: Tags

  • Search AWS Resource Group --> in the left pane, select tag editor.
  • Select a region, and AWS::EC2::Instance resource type --> click on search resources.
  • All the instances should prompt up at the bottom --> select the instances you want to apply tags to --> click Manage tags of selected resources
  • Click Add tag, enter in hoursas Tag Key and 9-6 as Tag Value --> click review and apply tag changes --> click on apply changes to all selected
  • Tags have been applied to the instances. Lambda functions will look for instances with these tags

2. IAM Role

A role for lambda functions to execute and stop/start EC2 instances

  • Search IAM --> click Roles in the left pane --> click on Create role
  • Select AWS Service as Trusted entity type, lambda as Use case --> click Next
  • Search and select AWSLambdaBasicExecutionRole type -AWS managed policy
  • Search and select AmazonEC2FullAccess type -AWS managed policy --> click Next
  • Enter in role name - EC2Scdeduler--> click Create role

3. Lambda Functions

I'll create two functions, one of them will stop the instances and the other will start them based on the schedules.

I. Instance Stop Function

  • Search AWS Lambda --> select functions --> click Create function
  • Enter in function name stopfunction --> select Python 3.9 Runtime and x86_64 Architecture
  • Expand Change default execution role --> select Use an existing role
  • Search and select EC2Scdeduler role and click create function
  • In Code Tab, select lambda_function --> remove everything and enter in the code below --> click Deploy and save
import json
import boto3
client = boto3.client('ec2')
def lambda_handler(event, context):
    response = client.describe_instances(
    Filters=[
        {
            'Name': 'tag:hours',
            'Values': [
                '9-6',
            ] 
        },
    ],
    )
    print(response)
    instances = []
    length = len(response['Reservations'])
    for i in range(length):
        instanceID = response['Reservations'][i]['Instances'][0]['InstanceId']
        instances.append(instanceID)
    if not instances:
        print("No instance found")
    else:
        response = client.stop_instances(
        InstanceIds=instances,
    )

ii. Instance Start Function

Almost the same as the stop function, but this starts the instances.

  • Search AWS Lambda --> select functions and click Create function
  • Enter in function name startfunction --> select Python 3.9 Runtime and x86_64 Architecture
  • Expand Change default execution role --> select Use an existing role
  • search and select EC2Scdedulerrole and click Create function
  • In Code Tab, select lambda_function --> remove everything and enter in the code below --> click Deploy and save
    import json
    import boto3
    client = boto3.client('ec2')
    def lambda_handler(event, context):
      response = client.describe_instances(
      Filters=[
          {
              'Name': 'tag:hours',
              'Values': [
                  '9-6',           
              ] 
          },
      ],
      )
      print(response)
      instances = []
      length = len(response['Reservations'])
      for i in range(length):
          instanceID = response['Reservations'][i]['Instances'][0]['InstanceId']
          instances.append(instanceID)
      if not instances:
          print("No instance found")
      else:
          response = client.start_instances(
          InstanceIds=instances,
      )
    

4. Set schedules for lambda functions

I'll use EventBridge to create schedules that will trigger lambda functions daily.

i. Instance Stop Schedule

  • Search Amazon EventBridge --> select Rules under Events in the left pane.
  • Click Create rule --> Name StopInstances, Event Bus default --> Rule type select Schedule --> click Next
  • Enter in cron expression 0,18,*,*,?,* for Minutes, Hour, Day of Month, Day of Week and Year. This schedule will run every day at 6pm UTC*
  • Select Target Lambda function as a target, stopfunctionas Function --> click Next
  • Add tags, enter in ec2scheduleras Key --> click Next
  • Review the rule --> click Create rule

ii. Instance Start Schedule

  • Search Amazon EventBridge --> select Rules under Events in the left pane
  • Click Create rule --> Name - StopInstances --> Event Bus - default --> Rule type - select Schedule--> click Next
  • Enter in cron expression 0,6,*,*,?,* for Minutes, Hour, Day of Month, Day of Week and Year.This schedule will run everyday at 6 am UTC*
  • Select Target Lambda function --> stopfunctionas Function --> click *Next
  • Add tags, enter in ec2scheduleras Key --> click Next
  • Review the rule --> click Create rule

All the resources have been created, instances will be stopped/started based on the schedules set in EventBridge.

Cleanup

If you have created these resources in your lab environment, don't forget to delete them to avoid getting billed.