Schedule EC2 instances in a single-account

Posted: | Updated: | Tags: cloud aws terraform project

This week I open sourced a Terraform project I’ve been using for the past few months. This solution allows the user to schedule the start or stop of EC2 instances in a single AWS account. This schedule is defined through Terraform and created EventBridge Schedulers. This post will be a snapshot in time of how the solutions looks at the time of publishing. An up to date and concise description of the solution can be found on its GitHub page.

Now, the AWS Solutions Library already maintains a feature rich Instance Scheduler with its own CLI, cross-account capabilities, SSM integration, support for EC2 and RDS and automated tagging. That is a more production ready and well-architected solution. Mine is built to be simpler and fit my need to start and stop a few development instances automatically keeping my costs to a minimum. Besides it’s more fun to build stuff.

Solution Overview

ec2-scheduler solution diagram showing the user, EventBridge Scheduler, a Lambda function and a group of tagged EC2 instances.

The solution creates an Amazon EventBridge Scheduler Group, in which you can use the provided Terraform module to create one or more schedulers that manifest itself on AWS as an EventBridge Scheduler. Each scheduler can start or stop one or more EC2 instances that are targetted by a key/value tag. The scheduler triggers a Lambda function when the time is right with all the appropriate details and the deed is done. Simple really.

Usage and examples

Within the repository you will find two modules shared-resources and schedule. The shared-resources module creates the Lambda function, EventBridge Scheduler Group and all IAM roles and policies required, this only needs to be setup once per account. The schedule module creates a new EventBridge Scheduler resource and is where you can define your schedule, key/value tag for the EC2 instances and whether you would like to start or stop those instances.

The main.tf file contains a working example of this solution, I will break it down by section here.

module "shared-resources" {
    source = "./modules/shared-resources"
    scheduler_group_name = var.scheduler_group_name
}

This creates an instance of the shared-resources module and passes in a name for the scheduler group.

module "workday_start" {
    source = "./modules/schedule"
    scheduler_name = "workday-start"
    scheduler_group_name = var.scheduler_group_name
    schedule_expression = "cron(00 9 ? * 2-6 *)"
    scheduler_target_arn = module.shared-resources.start_stop_lambda_function_arn
    scheduler_target_role_arn = module.shared-resources.scheduler_execution_role_arn
    scheduler_target_input = jsonencode({
        start = true
        tag_name = "scheduler"
        tag_value = "dev"
    })
}

This next block creates our first schedule, it’s called “workday-start” and is triggered at 9am on Monday to Friday. As part of the input to the Lambda function it starts EC2 instances with tag key of “scheduler” and value of “dev”. This is all customisable. Other than those values the group name, Lambda function, IAM role details are passed to the module which were outputs from shared-resources.

module "everyday_stop" {
    source = "./modules/schedule"
    scheduler_name = "everyday-stop"
    scheduler_group_name = var.scheduler_group_name
    schedule_expression = "cron(00 22 * * ? *)"
    scheduler_target_arn = module.shared-resources.start_stop_lambda_function_arn
    scheduler_target_role_arn = module.shared-resources.scheduler_execution_role_arn
    scheduler_target_input = jsonencode({
        start = false
        tag_name = "scheduler"
        tag_value = "dev"
    })
}

This next block stops instances everyday of the week at 10pm with the tag key of “scheduler” and the value of “dev”. The name given to this scheduler is “everday-stop” and the same output variables from shared-resources are passed in here as input.

Cost

Amazon EventBridge and AWS Lambda both come with a generous Always Free Tier which this solution fits well within unless you already have high usage on either of these services. EventBridge Scheduler is charged based on number of invocations with the first 14,000,000 invocations for free each month. Lambda is charged based on a invocation (a request) and the duration your code runs. The cost is dependant compute architecture selected and memory allocated. The function created defaults to x86_64 and 128 MB of memory.


Related ramblings