Efficiently Managing RDS Shutdown with AWS SAM
Introduction
By default, Amazon RDS instances cannot remain stopped indefinitely, as they are automatically restarted after seven days.
You can stop a DB instance for up to seven days. If you don’t manually start your DB instance after seven days, your DB instance is automatically started so that it doesn’t fall behind any required maintenance updates.
This behavior can lead to unnecessary costs for unused instances. In this blog post, we explore how AWS SAM can help automate the shutdown of RDS instances and ensure they remain stopped.
Prerequisites
Ensure the following are installed on your system:
- AWS SAM
- Python 3.x
Creating an AWS SAM Application
Directory Structure
Below is the directory structure for the SAM application:
/
|-- rds_shutdown/
| |-- app.py
| `-- requirements.txt
|-- samconfig.toml
`-- template.yaml
AWS SAM Template
The template defines a Lambda function triggered by a CloudWatch Events rule using a cron expression (line 23). Alternatively, consider using RDS events with EventBridge for more granular control.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Using a CloudWatch Events rule, RdsShutdown keeps an RDS instance shutdown after 1 week.
Parameters:
RdsDbInstance:
Type: String
Resources:
RdsShutdownFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: rds_shutdown/
Environment:
Variables:
DB_INSTANCE: !Ref RdsDbInstance
Events:
ScheduleEvent:
Type: Schedule
Properties:
Description: An event rule for RdsShutdownFunction
Enabled: True
Schedule: 'cron(* */1 * * ? *)' # Every hour
FunctionName: rds_shutdown
Handler: app.lambda_handler
MemorySize: 128
Role: !GetAtt IamRole.Arn
Runtime: python3.8
Timeout: 10
IamRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: policy1
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:DescribeDBInstances
- rds:StopDBInstance
Resource: !Sub arn:aws:rds:*:${AWS::AccountId}:db:${RdsDbInstance}
RoleName: rds-shutdown
LogsLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${RdsShutdownFunction}
RetentionInDays: 30
Python Script
requirements.txt
No additional dependencies are needed because boto3 is already included in the AWS Lambda runtime.
app.py
This script checks the status of the RDS instance and stops it only when it is available.
import logging
import os
import boto3
# Environment Variables
DB_INSTANCE = os.environ.get('DB_INSTANCE')
logger = logging.getLogger()
logger.setLevel(logging.INFO)
client = boto3.client('rds')
def lambda_handler(event, context):
if not DB_INSTANCE:
# Exit when a DB Instance is not specified.
logger.error('DB_INSTANCE environment variable is not specified.')
return
# Get the DB Instance status.
status = get_db_instance_status(DB_INSTANCE)
if status == 'available':
# Stop when the status is available.
client.stop_db_instance(DBInstanceIdentifier=DB_INSTANCE)
logger.info(f'DB instance - {DB_INSTANCE} - has been stopped.')
def get_db_instance_status(db_instance: str) -> str:
# See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Status.html
response = client.describe_db_instances(DBInstanceIdentifier=db_instance)
return response['DBInstances'][0]['DBInstanceStatus']
samconfig.toml
Replace <YOUR_S3_BUCKET>
and <YOUR_RDS_INSTANCE_NAME>
with your values.
version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "rds-shutdown"
s3_bucket = "<YOUR_S3_BUCKET>"
s3_prefix = "rds-shutdown"
region = "ap-northeast-1"
capabilities = "CAPABILITY_IAM CAPABILITY_NAMED_IAM"
parameter_overrides = "RdsDbInstance=\"<YOUR_RDS_INSTANCE_NAME>\""
Build and Deploy
Use the following commands to build and deploy the application:
sam build
sam deploy
Testing
Stop your RDS instance, leave it for seven days, and check the Lambda logs for the following message:
DB instance - database-1 - has been stopped.
Verify the status of the instance using this AWS CLI command:
aws rds describe-db-instances --db-instance-identifier <YOUR_RDS_INSTANCE_NAME> | grep DBInstanceStatus
You should see the following output:
"DBInstanceStatus": "stopped",
Conclusion
Using AWS SAM and Lambda functions to automate the shutdown of RDS instances is an efficient way to minimize costs and manage resources. However, remember that RDS instances cannot remain stopped indefinitely.
Happy Coding! 🚀