Reach Us

CloudifyOps Mini-blog series: Automated RDS DB Instance Start/Stop using AWS Lambda

AWS provides a useful Lambda service to automate code execution without managing the infrastructure. We can supply the code in any supported language such as Python, Node.js, Ruby, or Java, and it executes the code in scalable and highly available systems. AWS does not charge for any resources, and you pay only for the compute time of your code.

Required Resources:

  • AWS Lambda
  • IAM Roles and policies
  • AWS Cloudwatch

Solution Architecture:

Objective:

  • Automated start of DB Instances using the Lambda function
  • Automated stop of the DB Instances using the Lambda function
  • Trigger the Start/Stop Lambda function using CloudWatch event

Prerequisites:

  • IAM Role, Policies (RDS access)
  • Lambda function
  • CloudWatch event (under Amazon Event Bridge)

Steps to be Performed:

Create IAM Role & Policies:

On the IAM console, under Access Management in the navigation pane, choose Policies. Use the below-mentioned policy for IAM role creation.

{
“Version”: “2012-10-17”,
“Statement”: [
{
“Sid”: “VisualEditor0”,
“Effect”: “Allow”,
“Action”: [
“rds:DescribeDBClusterParameters”,
“rds:StartDBCluster”,
“rds:StopDBCluster”,
“rds:DescribeDBEngineVersions”,
“rds:DescribeGlobalClusters”,
“rds:DescribePendingMaintenanceActions”,
“rds:DescribeDBLogFiles”,
“rds:StopDBInstance”,
“rds:StartDBInstance”,
“rds:DescribeReservedDBInstancesOfferings”,
“rds:DescribeReservedDBInstances”,
“rds:ListTagsForResource”,
“rds:DescribeValidDBInstanceModifications”,
“rds:DescribeDBInstances”,
“rds:DescribeSourceRegions”,
“rds:DescribeDBClusterEndpoints”,
“rds:DescribeDBClusters”,
“rds:DescribeDBClusterParameterGroups”,
“rds:DescribeOptionGroups”
],
“Resource”: “*”
}
]
}

Create Lambda Function (start/stop):

Now, let’s create a Lambda function to automate ECR image deletion. We’ll use Python and the Boto3 library for AWS interaction.

The Lambda function needs 3 parameters (REGION, KEY, VALUE) to be passed as environment variables. Navigate to the ‘Configuration’ tab and choose ‘Environment Variables’. Click on the EDIT and add the Environment Variables as shown below.

 

Create one Lambda function for stop and the other for starting the rds instances respectively.

Start Function code:

import boto3
import os
import sys
import time
from datetime import datetime, timezone
from time import gmtime, strftime

def start_rds_all():
region=os.environ[‘REGION’]
key=os.environ[‘KEY’]
value=os.environ[‘VALUE’]
client = boto3.client(‘rds’, region_name=region)
response = client.describe_db_instances()

v_readReplica=[]
for i in response[‘DBInstances’]:
readReplica=i[‘ReadReplicaDBInstanceIdentifiers’]
v_readReplica.extend(readReplica)

for i in response[‘DBInstances’]:
#The if condition below filters aurora clusters from single instance databases as boto3 commands defer to start the aurora clusters.
if i[‘Engine’] not in [‘aurora-mysql’,‘aurora-postgresql’]:
#The if condition below filters Read replicas.
if i[‘DBInstanceIdentifier’] not in v_readReplica and len(i[‘ReadReplicaDBInstanceIdentifiers’]) == 0:
arn=i[‘DBInstanceArn’]
resp2=client.list_tags_for_resource(ResourceName=arn)
#check if the RDS instance is part of the Auto-Shutdown group.
if 0==len(resp2[‘TagList’]):
print(‘DB Instance {0} is not part of autoshutdown’.format(i[‘DBInstanceIdentifier’]))
else:
for tag in resp2[‘TagList’]:
if tag[‘Key’]==key and tag[‘Value’]==value:
if i[‘DBInstanceStatus’] == ‘available’:
print(‘{0} DB instance is already available’.format(i[‘DBInstanceIdentifier’]))
elif i[‘DBInstanceStatus’] == ‘stopped’:
client.start_db_instance(DBInstanceIdentifier = i[‘DBInstanceIdentifier’])
print(‘Started DB Instance {0}’.format(i[‘DBInstanceIdentifier’]))
elif i[‘DBInstanceStatus’]==‘starting’:
print(‘DB Instance {0} is already in starting state’.format(i[‘DBInstanceIdentifier’]))
elif i[‘DBInstanceStatus’]==‘stopping’:
print(‘DB Instance {0} is in stopping state. Please wait before starting’.format(i[‘DBInstanceIdentifier’]))
elif tag[‘Key’]!=key and tag[‘Value’]!=value:
print(‘DB instance {0} is not part of autoshutdown’.format(i[‘DBInstanceIdentifier’]))
elif len(tag[‘Key’]) == 0 or len(tag[‘Value’]) == 0:
print(‘DB Instance {0} is not part of autoShutdown’.format(i[‘DBInstanceIdentifier’]))
elif i[‘DBInstanceIdentifier’] in v_readReplica:
print(‘DB Instance {0} is a Read Replica.’.format(i[‘DBInstanceIdentifier’]))
else:
print(‘DB Instance {0} has a read replica. Cannot shutdown & start a database with Read Replica’.format(i[‘DBInstanceIdentifier’]))

response=client.describe_db_clusters()
for i in response[‘DBClusters’]:
cluarn=i[‘DBClusterArn’]
resp2=client.list_tags_for_resource(ResourceName=cluarn)
if 0==len(resp2[‘TagList’]):
print(‘DB Cluster {0} is not part of autoshutdown’.format(i[‘DBClusterIdentifier’]))
else:
for tag in resp2[‘TagList’]:
if tag[‘Key’]==key and tag[‘Value’]==value:
if i[‘Status’] == ‘available’:
print(‘{0} DB Cluster is already available’.format(i[‘DBClusterIdentifier’]))
elif i[‘Status’] == ‘stopped’:
client.start_db_cluster(DBClusterIdentifier=i[‘DBClusterIdentifier’])
print(‘Started Cluster {0}’.format(i[‘DBClusterIdentifier’]))
elif i[‘Status’]==‘starting’:
print(‘cluster {0} is already in starting state.’.format(i[‘DBClusterIdentifier’]))
elif i[‘Status’]==‘stopping’:
print(‘cluster {0} is in stopping state. Please wait before starting’.format(i[‘DBClusterIdentifier’]))
elif tag[‘Key’] != key and tag[‘Value’] != value:
print(‘DB Cluster {0} is not part of autoshutdown’.format(i[‘DBClusterIdentifier’]))
else:
print(‘DB Instance {0} is not part of autoShutdown’.format(i[‘DBClusterIdentifier’]))

def lambda_handler(event, context):
start_rds_all()

 

Stop Function code:

import boto3
import os
import sys
import time
from datetime import datetime, timezone
from time import gmtime, strftime

def shut_rds_all():
region=os.environ['REGION']
key=os.environ['KEY']
value=os.environ['VALUE']


client = boto3.client('rds', region_name=region)
response = client.describe_db_instances()
v_readReplica=[]
for i in response['DBInstances']:
readReplica=i['ReadReplicaDBInstanceIdentifiers']
v_readReplica.extend(readReplica)

for i in response['DBInstances']:
#The if condition below filters aurora clusters from single instance databases as boto3 commands defer to stop the aurora clusters.
if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
#The if condition below filters Read replicas.
if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
arn=i['DBInstanceArn']
resp2=client.list_tags_for_resource(ResourceName=arn)
#check if the RDS instance is part of the Auto-Shutdown group.
if 0==len(resp2['TagList']):
print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
else:
for tag in resp2['TagList']:
#If the tags match, then stop the instances by validating the current status.
if tag['Key']==key and tag['Value']==value:
if i['DBInstanceStatus'] == 'available':
client.stop_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
print('stopping DB instance {0}'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus'] == 'stopped':
print('DB Instance {0} is already stopped'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus']=='starting':
print('DB Instance {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus']=='stopping':
print('DB Instance {0} is already in stopping state.'.format(i['DBInstanceIdentifier']))
elif tag['Key']!=key and tag['Value']!=value:
print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
print('DB Instance {0} is not part of auroShutdown'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceIdentifier'] in v_readReplica:
print('DB Instance {0} is a Read Replica. Cannot shutdown a Read Replica instance'.format(i['DBInstanceIdentifier']))
else:
print('DB Instance {0} has a read replica. Cannot shutdown a database with Read Replica'.format(i['DBInstanceIdentifier']))

response=client.describe_db_clusters()
for i in response['DBClusters']:
cluarn=i['DBClusterArn']
resp2=client.list_tags_for_resource(ResourceName=cluarn)
if 0==len(resp2['TagList']):
print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
else:
for tag in resp2['TagList']:
if tag['Key']==key and tag['Value']==value:
if i['Status'] == 'available':
client.stop_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
print('stopping DB cluster {0}'.format(i['DBClusterIdentifier']))
elif i['Status'] == 'stopped':
print('DB Cluster {0} is already stopped'.format(i['DBClusterIdentifier']))
elif i['Status']=='starting':
print('DB Cluster {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBClusterIdentifier']))
elif i['Status']=='stopping':
print('DB Cluster {0} is already in stopping state.'.format(i['DBClusterIdentifier']))
elif tag['Key'] != key and tag['Value'] != value:
print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
else:
print('DB Instance {0} is not part of auroShutdown'.format(i['DBClusterIdentifier']))

def lambda_handler(event, context):
shut_rds_all()

Create Amazon EventBridge Rule:

In the EventBridge console, navigate to the Rule and create a rule. Provide the name, description, and the eventbus. Choose the rule type as ‘scheduled’ and click ‘continue’ to create the rule.

Under the schedule pattern, choose a specific time and enter the Cron expression. Click ‘next’. On the next page, select the target types as AWS Service. Choose your Lambda function (start/stop) that you want to trigger and enter your Lambda functions ARN. Select ‘Create’ at the bottom of the page to create the Rule. Now the EventBridge Rule will trigger your Lambda functions (start/stop).

Write to us at sales@cloudifyops.com if you are looking for Cost Optimization solutions for your AWS cloud infrastructure.

Privacy Settings
We use cookies to enhance your experience while using our website. If you are using our Services via a browser you can restrict, block or remove cookies through your web browser settings. We also use content and scripts from third parties that may use tracking technologies. You can selectively provide your consent below to allow such third party embeds. For complete information about the cookies we use, data we collect and how we process them, please check our Privacy Policy
Youtube
Consent to display content from - Youtube
Vimeo
Consent to display content from - Vimeo
Google Maps
Consent to display content from - Google
Spotify
Consent to display content from - Spotify
Sound Cloud
Consent to display content from - Sound
Contact Us