Last updated on May 26, 2023
Every AWS service offering comes with a pay-as-go pricing model, empowering businesses, small or large, to be flexible at experimenting and making changes as they build their products to market. This pricing model is similar to riding an Uber, where you only pay for the distance covered, or to electric bills where you’re charged based on actual energy consumption. Of course, the more devices you have running, the higher your monthly bill will be. Cloud computing, like any other utility, should be treated the same way.
Just as we are told to unplug appliances when not in use, removing/stopping unused resources is a common strategy for reducing expenditures in AWS. While this may not apply to critical resources that must be online 24/7, shutting down non-production resources and restarting them only during work hours is a straightforward yet effective way of avoiding unnecessary charges.
In this article, we’ll look at implementing an automated solution for stopping/starting non-production RDS instances on schedule using the combination of Amazon EventBridge and AWS Lambda. Stopping RDS instances can be done manually through the RDS management console. With that said, you could assign someone in your team to turn off all RDS instances you have in your test environment before clocking out and have them restart once access is needed the following day. This process might be tolerable for a handful of databases; however, with several test databases to manage, clicking each of them through the console can get tedious and become a time-consuming task. It won’t also help if you’re someone like me who tends to forget things once in a while, hence, automation is the way to go.
Before we get into the implementation details, let’s have a quick overview of the general billing concept in Amazon RDS. RDS instances are billed for DB instance hours, provisioned storage, backup storage, I/O requests, provisioned IOPS, and data transfer. Out of these 6, provisioned storage and DB instance hours (compute time) account for most of RDS costs.
Note that we can only control the number of instance hours accrued over time. You’d have to pay for storage regardless of whether your DB instance is stopped or running. The only way to truly eliminate storage costs is to terminate your database instances.
Below is the overview of the solution that we’ll be building. In this post, we’ll assume that all test RDS instances are located in the N. Virginia region. Having a common region for test resources makes management much easier. In addition, we consider the standard working hours (9AM-5PM) during which the instances must remain active.
The solution comprises 5 components, which are briefly described as follows:
-
stop-rds
Lambda function – contains the logic for stopping all RDS instances -
start-rds
Lambda function – contains the logic for starting all RDS instances -
stop-rds
EventBridge Scheduled rule – invokes thestop-rds
Lambda function -
start-rds
EventBridge Scheduled rule – invokes thestart-rds
Lambda function -
rds-stop-start
execution role – Execution roles are used by Lambda functions to interact with other AWS services. In our case, we must grant AWS Lambda the necessary permissions to allow it to stop and start database instances on our behalf.
STEPS:
-
Create an IAM policy for the
rds-stop-start
execution role. On the IAM Console, clickPolicies
thenCreate Policy
.
2. Attach the following IAM permissions in the JSON editor. We’ll skip adding a tag. Click Next:Tags
> Next:Review
to proceed to the final step.
3. Enter rds-stop-start-policy
in the name field. Click Create policy
.
4. Next, let’s create the rds-stop-start
execution role. On the IAM Console, click Roles
then Create role
.
5. Select AWS Lambda as the trusted entity and click Next
.
6. Filter rds-stop-start-policy
and select it. Click Next
.
7. Enter rds-stop-start
as the role’s name. Scroll down to the bottom of the page and click Create role
.
Now that the execution role has been created, head over to the AWS Lambda console and create a Lambda function.
8. Create the Lambda function that will stop the RDS instances. Name the function stop-rds
. Select Python as the runtime. If you wish to write in other supported languages, just google “aws sdk <language> docs“ to get the API reference. Under Execution role, select Use an existing role
and search for the role that we created earlier. Finally, click Create function
.
9. Replace the hello world sample code with the following:
You could create a static list of all non-production RDS instances and loop through it, however, this method requires you to update the list every time an instance is added or terminated. To avoid this, we’ll simply use the describe_db_instances
API. This function returns various details about all existing RDS instances in a region. It’s also important to note that the stop_db_instance
API is incapable of turning off instances that are part of an Aurora cluster, hence, I applied the Filter attribute to exclude them and only grab DB instances that use MySQL. This makes sense for our setup since we’re not using any engine other than MySQL. To stop an Aurora cluster, use describe_db_clusters
to get a list of clusters and then call the stop_db_cluster
API.
10. Create the start-rds
function by using steps 7-9 as a guide. This time, name the function start-rds
and copy-paste the following code:
Our solution is almost complete. The only thing missing now is to run the Lambda functions on schedule. To do this, we’ll create two Amazon EventBridge rules that will invoke the stop-rds
and start-rds
functions.
11. On the Amazon EventBridge console, click Create rule
.
12. Under Rule detail, enter stop-rds
as the rule name and select the Schedule
rule type. Click Next.
13. Write the desired CRON expression. The sample below triggers the stop-rds
Lambda function every 5 PM GMT +8 from Monday to Friday. Click Next.
14. Specify the stop-rds
Lambda function as a target. Click Next
> Next
> Create Rule
15. Create the event rule that will trigger the start-rds
function. Use steps 11-14 as a guide. This time, replace the CRON expression with 0 1 ? * MON-FRI *
to restart the RDS instances at 9 AM every Monday to Friday.