Provisioning applications on AWS is quite easy. For a moderate to high size infrastructure, comprised of Development, Testing, Staging and Production environments, AWS can also become expensive very quickly. A good way to save money is to shutdown instances in the periods they are not being used. This post shows to to create a scheduler to shutdown instances automatically at predefined periods of time such as night-time and weekends.

If you are fortunate enough to have your development instances serving a single timezone, or timezones that are close enough to each other, it’s easy to guess a period of time during the week in which development instances can be stopped. For instance, one could think of having the instances online between 8am and 8pm during working days (Monday to Friday).

AWS already provides great documentation on how to do this using Lambda and CloudWatch services. CloudWatch will have the timing rules for stopping and restarting the instances. Lambda will provide the necessary instructions on how to stop and start instances.

In this post, we learn how to do this with Terraform, so we can build it programatically, immutable and repeatable.

AWS and Terraform

Note: Make sure that your processes start along with your instance. In Linux, this can be done by using chkconfig on for init.d based services, systemctl enable for systemd based services, or by running custom startup instructions at /etc/rc.local.

For complex environments that already count on hundreds of EC2 instances or more, chances are that you are already using Terraform or similar tools to help you quickly build immutable infrastructure and reduce the risk caused by manual environment setup.

So, why not using Terraform to provision an instance scheduler as well, so we can save some money by stopping instances when they are not needed?

CloudWatch Rules

AWS CloudWatch is a monitoring service that provides the ability to setup events that will trigger certain actions. In this case, we intend to trigger the stop and the start of the instances based on a schedule. For that, Scheduled Events can be used.

The Lambda function

AWS Lambda is the serverless service provided by AWS so we can run code without managing servers. The code we will be running on AWS Lambda is simple enough, it consists of two Python functions that will shutdown or start the instances.

I’ll be using the same example provided by AWS documentation, with a tweak. Instead of supplying a list of instance identifiers, which is somewhat static and limited, I’ll be using a tag. Any instance with the tag AutoStop set to true will be subject to the scheduler.

That way, if for some reason you decided (or someone decided for you) that you’ll work some extra hours, you can always remove the tag and the instance will be kept online when the time comes.

Using Python, the Lambda function to stop instances in a given regioin could look something like:

The above code is quite straightforward isn’t it? It filters the instances in a region that are running and stops them. A similar function can be coded for starting the instances.

Terraform

Terraform provides the ability to create Infrastructure as Code (IaC). Although not strictly cloud-agnostic, as some misleading documentation might lead us to think, it does allow to setup IaC for different providers (e.g. AWS, GCP, Azure, OpenStack).

It’s still new, under active development, and subject to a lot of criticism due to the huge amount of weird workarounds that exist for simple tasks. Yet, it’s still a brilliant tool that gets the job done.

The Terraform blueprint is comprised of one or more files with the tf extension. For this particular case, it will contain the definition of the necessary resources that should be created in AWS in order for the scheduler to work properly.

First, we need to define the IAM role that will let our Lambda functions have the necessary permissions to operate over the EC2 instances.

Then we just need the following:

  • CloudWatch Event Rules, that define the triggers to stop and start instances at a specific schedule.
  • CloudWatch targets, that associate a CloudWatch rule with a specific Lambda function to execute.
  • Lambda Permissions, that allow CloudWatch Event Rules to run Lambda Functions.
  • Lambda Functions, that define the code to stop and start the instances.

The below defines the Terraform blueprints that defines the above mentioned resources.

CloudWatch

Lambda

Bake all of the above inside the same directory, and you should be able to run Terraform and create those resources in AWS. Make sure to have Terraform installed and AWS CLI properly configured.

terraform init
terraform apply

Wrapping Up

We’ve seen a simple example on how to setup an automatic stop/start scheduler for AWS using Terraform, a simple change that can help us save a significant amount of money on AWs. This can be further customized to allow for multiple regions or use different criteria.

The same strategy can also be used to achieve different goals, other than stopping or starting instances. The base should be similar to any other behaviour we might need. It may also be useful to create a Terraform reusable module out of it that allows to setup different kinds of functions by simply providing a schedule, the function code and a name.