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-rdsLambda function – contains the logic for stopping all RDS instances
start-rdsLambda function – contains the logic for starting all RDS instances
stop-rdsEventBridge Scheduled rule – invokes the
start-rdsEventBridge Scheduled rule – invokes the
rds-stop-startexecution 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.
Create an IAM policy for the
rds-stop-startexecution role. On the IAM Console, click
2. Attach the following IAM permissions in the JSON editor. We’ll skip adding a tag. Click
Next:Review to proceed to the final step.
rds-stop-start-policy in the name field. Click
4. Next, let’s create the
rds-stop-start execution role. On the IAM Console, click
5. Select AWS Lambda as the trusted entity and click
rds-stop-start-policy and select it. Click
rds-stop-start as the role’s name. Scroll down to the bottom of the page and click
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
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
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
11. On the Amazon EventBridge console, click
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
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.