Starting and Stopping EC2 Instances using a Lambda – and cut your AWS bill in half!

aws_simple_icons_compute_awslambda-svg

Cutting your AWS EC2 Bill with Lambda Functions

When running a large training program for an investment bank, we needed over 30 EC2 instances, but only between certain hours of the day. This simple Lambda Function, cut our AWS bill by around 65% on the normal cost of running those instances all day every day.

As a CTO and Cofounder of a food delivery business, I was able to cut our AWS bill substantially by running our servers in the evening when deliveries were taking place. Again, a simple Lambda function could cut the bill as we would no longer be running them all the time.

How many of your servers are really needed all the time? If you want to shave your AWS bill, then Lambda’s make it easy to schedule the starting and stopping of your instances.

How to Create the Lambda Function

Part 1 Create the IAM Role with Permission to access EC2

Any Lambda expression will run with a set of permissions. Those permissions are configured as an IAM role. If you don’t have an IAM role already with permission to access EC2 you will need to create one first.

  1. In the AWS Administration Console, visit the IAM service.
  2. In the left pane of the IAM service, click Roles.
  3. Then click, Create New Role.
  4. At the Set Role Name dialog, enter a name, something like Ec2AccessRole.
  5. At the Select Role Type dialog, click Select by the EC2 Role option.
  6. You are now presented with a list of policies. Locate and select the EC2FullAccess and click Next Step.
  7. At the Review screen, click Create Role.

Part 2 Create the Lambda Function

  1. In the AWS Administration Console, visit the Lambda service.
  2. Click the Create new Lambda Function button.
  3. At the Select Blueprint dialog, select the first option Blank Function.
  4. At the Configure Triggers dialog, click the grey checked box and at the drop down, select CloudWatch Events Schedule.
  5. In the Configure Triggers form, enter a suitable name for your trigger, something like: StartServersAt8AM
  6. In the Configure Triggers form, enter a suitable description, something like: Start instances at 8am.
  7. In the Configure Triggers form, enter a Schedule Expression. These are in the form of Cron, which is a scheduling command found on Unix boxes. It has a standard format for times and dates which is used by AWS. So for example,  to start at 8AM Monday to Friday the expression would be: cron(00 08 ? * MON-FRI *). An excellent utility to help you can be found here: http://www.cronmaker.com/. This simple Web site will give you the required cron expression for the time you require.  IMPORTANT: Note that the time must be in UTC!
  8. Check the Enable trigger checkbox and click Next.

Part 3 Create the Code for the Function

Now you will need to set up the actual Function itself to start the servers. This will be written in Python.

  1. At the Configure Function dialog, enter a name, something like startMyServers.
  2. At the Configure Function dialog, enter a description, something like Start the servers.
  3. At the Configure Function dialog, set the Runtime to Python.
  4. In the code box below, enter the following code. In our example, we are setting it to start servers with a specific Tag on them. You could change this to be anything you like. Some way of identifying the servers you wish to start and stop.

import boto3
import logging

ec2 = boto3.resource('ec2')

def lambda_handler(event, context):

    filters = [{
            'Name': 'tag:Role', // you might change this tag name. Our servers had a tag called Role
            'Values': ['MyRoleTagValue'] // this was the value of the tag called Role. You can change this also. Just make sure you add the Tag called Role to your own instances
        },
        {
            'Name': 'instance-state-name', 
            'Values': ['stopped']
        }
    ]
    
    instances = ec2.instances.filter(Filters=filters)
    
    stoppedInstances = [instance.id for instance in instances]
    
    
    if len(stoppedInstances) > 0:
        startingUp = ec2.instances.filter(InstanceIds=stoppedInstances).start()
    

  1. In the Lambda function handler and role section, select Choose an Existing Role.
  2. In the drop down that appears, select the role created in Part 1. We suggested the name of Ec2AccessRole.
  3. The remaining fields can be left as they are. Click Next.
  4. At the Review dialog, click Create Function.

That’s it! You’re done. To create one that stops the servers, the process is pretty much the same. Create another Lambda, but just change the code slightly to check for started instances, and then call stop() on them instead of start. A simple example of the code is below.


import boto3
ec2 = boto3.resource('ec2')

def lambda_handler(event, context):
    filters = [{
            'Name': 'tag:MyTag',
            'Values': ['MyTagValue']
        },
        {
            'Name': 'instance-state-name', 
            'Values': ['running']
        }
    ]
    
    instances = ec2.instances.filter(Filters=filters)

    runningInstances = [instance.id for instance in instances]
    
    if len(runningInstances) > 0:
        shuttingDown = ec2.instances.filter(InstanceIds=runningInstances).stop()

 

 

3 Comments

Add a Comment

Your email address will not be published. Required fields are marked *