I am working on a small project around automating OWASP ZAP scans so we can automatically test our SaaS service. Ultimately, we decided to run the scanner from an EC2 instance (the reason it’s not a container is beyond the scope of this article). The plan was to have EC2 in a stopped state and start it automatically once a week to execute the scan.
I started looking into how to automate and codify it since we are heavy on the Infrastructure-as-Code approach.
Approach #1
My colleague advised the first approach. It boils down to
- A Lambda function to start an EC2 instance,
- An IAM role and policy to allow the Lambda function to start the instance,
- A CloudWatch event to trigger a lambda function.
It was pretty straightforward. However, I wondered what other options I had. Most of the time, you can do the same thing in several ways in AWS.
If I couldn’t find a better solution over the next couple of hours, this approach was my fallback plan.
Approach #2
I immediately found an AWS document on how to do it. It ended up being remarkably similar to what my colleague suggested.
- A Lambda function to start an EC2 instance,
- An IAM role and policy to allow the Lambda function to start the instance,
- EventBridge to trigger the lambda function.
That’s the first time I have come across EventBridge. It’s a serverless event bus that allows you to receive and send events. Amazon recommends using EventBridge over CloudWatch Events. Per AWS documentation: “Amazon EventBridge is the preferred way to manage your events. CloudWatch Events and EventBridge are the same underlying service and API, but EventBridge provides more features.”
However, the overall complexity is the same as the first approach.
Approach #3
The next thing I stumbled upon is Instance Scheduler. It looked like a match made in heaven based on the description “The Instance Scheduler on AWS solution automates the starting and stopping of Amazon Elastic Compute Cloud (Amazon EC2) and Amazon Relational Database Service (Amazon RDS) instances.”
However, as I read through the first page, I realized it’s just a Cloud Formation template that wraps approaches 1 and 2. On top of that, our tool of choice is Terraform. So, CloudFormation references weren’t helpful.
Approach #4
I checked out the possible targets for CloudWatch Events. There were a couple of EC2 actions (like StopInstances and TerminateInstances). However, there were no StartInstances.
Close, but no cigar.
Approach #5
Finally, I looked in EventBridge Scheduler (via AWS Console UI) and found that it provides a richer set of targets (including starting an EC2 instance).
I started to dig into how to configure it via Terraform, and I was puzzled by how few links there are for “aws_scheduler_schedule.” Google returned half a dozen links, half pointing to the GitHub issue at the Hashicorp Terraform repo where it was implemented. Looking back, I realized I should have clicked “If you like, you can repeat the search with the omitted results included.”) to display all mentions.
It took some time to piece things together.
- How it is used in Terraform (thanks to a website with examples and an explanation in Japanese).
- Additionally, I found this Terraform README file documenting this new feature.
- And finally, the last piece of the puzzle is EventBridge Universal Target. It can send events to most AWS APIs, which is fantastic!
I end up with the following Terraform script.
resource “aws_scheduler_schedule“ “my_scheduler“ { | |
name = “my-scheduler“ | |
group_name = “default“ | |
flexible_time_window { | |
mode = “OFF“ | |
} | |
# Run it each Monday | |
schedule_expression = “cron(0 9 ? * MON *)“ | |
target { | |
# This indicates that the event should be send to EC2 API and startInstances action should be triggered | |
arn = “arn:aws:scheduler:::aws-sdk:ec2:startInstances“ | |
role_arn = aws_iam_role.my_role.arn | |
# And this block will be passed to startInstances API | |
input = jsonencode({ | |
InstanceIds = [ | |
aws_instance.my_ec2.id | |
] | |
}) | |
} |
A small note. I didn’t include the definition of an EC2 instance (“my_ec2”) and a role (“my_role”) to keep it brief.
The result ended up straightforward and even simpler than the original solution.
That’s all, folks. Happy terraforming.
Aembit is the Identity Platform that lets DevOps and Security manage, enforce, and audit access between federated workloads.
We invite you to try it today!