Help us improve the AWS re:Post Knowledge Center by sharing your feedback in a brief survey. Your input can influence how we create and update our content to better support your AWS journey.
How do I use Step Functions to stop an Amazon RDS instance for more than 7 days?
I want to use AWS Step Functions to stop an Amazon Relational Database Service (Amazon RDS) for more than the 7-day duration.
Short description
By default, you can stop an Amazon RDS database instance for up to 7 days at a time. After 7 days, the instance restarts so that it doesn't miss any maintenance updates.
To stop your instance for more than 7 days, you can use Step Functions to automate the workflow so that you don't miss maintenance windows.
Note: For an alternative resolution, see How can I use an AWS Lambda function to stop an Amazon RDS instance for longer than seven days?
Resolution
Configure IAM permissions
To create an AWS Identity and Access Management (IAM) policy that allows Step Functions to start and stop an RDS instance, complete the following steps:
-
Open the IAM console.
-
In the navigation pane, choose Policies, and then choose Create policy.
-
Choose the JSON tab, and then enter the following statement:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "rds:DescribeDBInstances", "rds:StopDBInstance", "rds:StartDBInstance" ], "Resource": "*" } ] } -
Choose Next.
-
For Policy name, enter the name for your policy. For example, enter step-functions-start-stop-rds-policy.
-
(Optional) To add a tag, choose Add new tag, and then enter the values for the Key and Value fields.
-
Choose Create policy.
For more information, see Creating policies using the JSON editor.
Create an IAM role and attach the required policies
Complete the following steps:
- Open the IAM console.
- In the navigation pane, choose Roles, and then choose Create role.
- For Select trusted entity, choose AWS service.
- In the Use cases for other AWS service dropdown list, choose Step Functions.
- Choose Next.
Note: Don't take any actions on the Add permissions page. Create the role first, and then edit the default permissions. - Choose Next.
- For Role name, enter the name for the role. For example, enter step-functions-start-stop-rds-role.
(Optional) Enter the role description.
(Optional) To add a tag, choose Add new tag and enter the values for the Key and Value fields. - Choose Create role. This returns you to the Roles page.
- In the search box, enter the name of the role that you created, then select that role to view the details.
- In the Permissions tab, choose the Add Permissions dropdown list, and then choose Attach policies.
- In the search box, enter the name of the policy that you created in the Configure IAM permissions section. For example, enter step-functions-start-stop-rds-policy and then select the policy.
- In the Permissions tab, select the AWSLambdaRole AWS managed policy, and then choose Remove.
For more information, see Creating a role for an AWS service (console).
Add tags for database instances
Complete the following steps:
- Open the Amazon RDS console.
- In the navigation pane, choose Databases.
- Select the database instance that you want to start and stop automatically.
- Choose the Tags tab.
- Choose Add. For Tag key, enter autostart. For Value, enter yes.
- Choose Add another Tag. For Tag key, enter autostop. For Value, enter yes.
- Choose Add.
For more information, see Adding and deleting tags in Amazon RDS.
Create a state machine to start and stop the tagged database instances
Complete the following steps:
-
Open the Step Functions console, and then choose Get started.
-
From Create your state machine, choose Create your own.
-
Enter a state machine name. For example, enter step-functions-start-stop-rds-state-machine.
-
For State machine type, choose Standard, and then choose Continue.
-
Choose the Code tab, then enter the following state machine definition:
{ "Comment": "State Machine Definition to start and stop RDS DB instances", "StartAt": "Describe DBInstances to Start", "States": { "Describe DBInstances to Start": { "Type": "Task", "Parameters": {}, "Resource": "arn:aws:states:::aws-sdk:rds:describeDBInstances", "Next": "Iterate on Instances to Start", "Retry": [ { "ErrorEquals": [ "Rds.InternalFailure", "Rds.ServiceUnavailable", "Rds.ThrottlingException", "Rds.SdkClientException" ], "BackoffRate": 2, "IntervalSeconds": 1, "MaxAttempts": 2 } ] }, "Iterate on Instances to Start": { "Type": "Map", "ItemProcessor": { "ProcessorConfig": { "Mode": "INLINE" }, "StartAt": "Format Array before Start", "States": { "Format Array before Start": { "Type": "Pass", "Next": "Check If Instance stopped, if no Tags or if Tags contains 'autostart=yes'", "Parameters": { "DbInstanceStatus.$": "$.DBInstance.DbInstanceStatus", "DbInstanceIdentifier.$": "$.DBInstance.DbInstanceIdentifier", "TagList.$": "$.DBInstance.TagList", "TagsArrayLength.$": "States.ArrayLength($.DBInstance.TagList)", "TagContainsKey.$": "States.ArrayContains($.DBInstance.TagList,$.LookingFor)" } }, "Check If Instance stopped, if no Tags or if Tags contains 'autostart=yes'": { "Type": "Choice", "Choices": [ { "Not": { "Variable": "$.DbInstanceStatus", "StringEquals": "stopped" }, "Next": "Instance is not in 'stopped' status" }, { "Variable": "$.TagsArrayLength", "NumericEquals": 0, "Next": "No Tags found to Start" }, { "Variable": "$.TagContainsKey", "BooleanEquals": true, "Next": "Tags found Start DBInstance" } ], "Default": "No Tags found to Start" }, "Tags found Start DBInstance": { "Type": "Task", "Parameters": { "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" }, "Resource": "arn:aws:states:::aws-sdk:rds:startDBInstance", "Retry": [ { "ErrorEquals": [ "Rds.InternalFailure", "Rds.ServiceUnavailable", "Rds.ThrottlingException", "Rds.SdkClientException" ], "BackoffRate": 2, "IntervalSeconds": 1, "MaxAttempts": 2 } ], "Catch": [ { "ErrorEquals": [ "States.ALL" ], "Next": "Failed to Start DBInstance" } ], "ResultSelector": { "message": "Instance Started", "DbInstanceIdentifier.$": "$.DbInstance.DbInstanceIdentifier" }, "End": true }, "Failed to Start DBInstance": { "Type": "Pass", "Parameters": { "message": "Failed to start instance", "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" }, "End": true }, "No Tags found to Start": { "Type": "Pass", "End": true, "Parameters": { "message": "No Tags found to Start", "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" } }, "Instance is not in 'stopped' status": { "Type": "Pass", "End": true, "Parameters": { "message": "Instance is not in 'stopped' status", "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" } } } }, "InputPath": "$.DbInstances", "Next": "Wait for 1 hour and 30 minutes", "ItemSelector": { "LookingFor": { "Key": "autostart", "Value": "yes" }, "DBInstance.$": "$$.Map.Item.Value" } }, "Wait for 1 hour and 30 minutes": { "Type": "Wait", "Seconds": 5400, "Next": "Describe DBInstances to Stop" }, "Describe DBInstances to Stop": { "Type": "Task", "Parameters": {}, "Resource": "arn:aws:states:::aws-sdk:rds:describeDBInstances", "Retry": [ { "ErrorEquals": [ "Rds.InternalFailure", "Rds.ServiceUnavailable", "Rds.ThrottlingException", "Rds.SdkClientException" ], "BackoffRate": 2, "IntervalSeconds": 1, "MaxAttempts": 2 } ], "Next": "Iterate on Instances to Stop" }, "Iterate on Instances to Stop": { "Type": "Map", "ItemProcessor": { "ProcessorConfig": { "Mode": "INLINE" }, "StartAt": "Format Array before Stop", "States": { "Format Array before Stop": { "Type": "Pass", "Next": "Check If Instance available, if no Tags or if Tags contains 'autostop=yes'", "Parameters": { "DbInstanceStatus.$": "$.DBInstance.DbInstanceStatus", "DbInstanceIdentifier.$": "$.DBInstance.DbInstanceIdentifier", "TagList.$": "$.DBInstance.TagList", "TagsArrayLength.$": "States.ArrayLength($.DBInstance.TagList)", "TagContainsKey.$": "States.ArrayContains($.DBInstance.TagList,$.LookingFor)" } }, "Check If Instance available, if no Tags or if Tags contains 'autostop=yes'": { "Type": "Choice", "Choices": [ { "Not": { "Variable": "$.DbInstanceStatus", "StringEquals": "available" }, "Next": "Instance is not in 'available' status" }, { "Variable": "$.TagsArrayLength", "NumericEquals": 0, "Next": "No Tags found to Stop" }, { "Variable": "$.TagContainsKey", "BooleanEquals": true, "Next": "Tags found Stop DBInstance" } ], "Default": "No Tags found to Stop" }, "Tags found Stop DBInstance": { "Type": "Task", "Parameters": { "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" }, "Resource": "arn:aws:states:::aws-sdk:rds:stopDBInstance", "Retry": [ { "ErrorEquals": [ "Rds.InternalFailure", "Rds.ServiceUnavailable", "Rds.ThrottlingException", "Rds.SdkClientException" ], "BackoffRate": 2, "IntervalSeconds": 1, "MaxAttempts": 2 } ], "Catch": [ { "ErrorEquals": [ "States.ALL" ], "Next": "Failed to Stop DBInstance" } ], "ResultSelector": { "message": "Instance Stopped", "DbInstanceIdentifier.$": "$.DbInstance.DbInstanceIdentifier" }, "End": true }, "Failed to Stop DBInstance": { "Type": "Pass", "Parameters": { "message": "Failed to stop instance", "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" }, "End": true }, "No Tags found to Stop": { "Type": "Pass", "End": true, "Parameters": { "message": "No Tags found to Stop", "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" } }, "Instance is not in 'available' status": { "Type": "Pass", "End": true, "Parameters": { "message": "Instance is not in 'available' status", "DbInstanceIdentifier.$": "$.DbInstanceIdentifier" } } } }, "InputPath": "$.DbInstances", "Next": "Workflow Finished", "ItemSelector": { "LookingFor": { "Key": "autostop", "Value": "yes" }, "DBInstance.$": "$$.Map.Item.Value" } }, "Workflow Finished": { "Type": "Succeed" } } }Note: In the Wait for 1 hour and 30 minutes parameter, you can change the Seconds field value for testing purposes. For example, you can change the Seconds field value to a longer or shorter maintenance window.
-
Choose Config, and then choose Create.
-
For Confirm role creation, choose Confirm.
-
Choose Start execution.
Run a function test for tagged database instances in the Stopped state
Complete the following steps:
- Open the Step Functions console.
- In the navigation pane, choose State machines.
- Select the state machine that you created to start your database instances. For example, step-functions-start-stop-rds-state-machine.
- Choose Start execution.
Note: This resolution doesn't require an event payload. Under the Start execution dialog, you can remove the event payload or leave the default event. - Choose Start execution.
Create the schedule
To schedule a weekly maintenance window for the tagged database instances, create an Amazon EventBridge rule. This rule automatically starts the database instance 30 minutes before the maintenance window.
In the following example, the maintenance window occurs during 22:00-22:30 on Sunday. The example rule starts the database instance at 21:30 every Sunday.
To create the schedule, complete the following steps:
- Open the EventBridge console.
- Choose EventBridge Schedule, and then choose Create schedule.
- Enter a schedule name. For example, step-function-start-stop-schedule. Leave Schedule group as default.
- For Schedule pattern, choose Recurring schedule.
- For Schedule type, choose Cron-based schedule.
- Add a cron expression for the automated schedule. For example, enter cron(30 21 ? * SUN *).
- For the Flexible time window, choose Off.
- Choose Next.
- For Target detail, choose Templated targets, and then choose StartExecution.
- Under StartExecution, select the state machine that you created in the section Create a state machine to start and stop the tagged database instances. For example, select step-functions-start-stop-rds-state-machine. Keep the input's default value of {}.
- Choose Next.
- Under Retry policy and dead-letter queue (DLQ), set the Retry policy switch to off.
- For Dead-letter queue (DLQ), choose None.
- Under Permissions, choose: Create new role for this schedule, and then choose Next.
Note: EventBridge creates a new role to assume and initiates the StartExecution API for your workflow. - Under Schedule detail, verify that the next 10 invocation dates match the dates of your expected schedule, and then choose Create schedule.
The Step Function workflow begins when EventBridge invokes the rule. This starts the database instances 30 minutes before the maintenance window. The workflow then stops the database instance 30 minutes after the maintenance window completes. For example, the workflow starts your database instance at 21:30. The maintenance window occurs at 22:00–22:30. Then, the workflow stops your instance at 23:00.
- Language
- English

in the example, the cron schedule is hardcoded but it looks like every RDS can have their own maintenance window, how would that work?
I want to share github.com/sqlxpert/stay-stopped-aws-rds-aurora , which handles this more reliably.
The present solution is among the better ones on re:Post, for its use of Step Function task states with AWS SDK integrations and for the awareness that the DescribeDBInstances response now includes tags, which saves needless rds:ListTagsForResource calls.
Nevertheless, like most solutions, it has a latent bug, a race condition.
-
Status can change between
DescribeDBInstancesandStopDBInstance. Faced with this non-atomic pair, stop first and check status later. -
availablestatus should not be the only error-free way forward.stopping,stoppedanddeletingwould be fine, too. A human might notice that the database has become available and stop it manually before the 1 hour and 30 minute wait is up.
{ "Not": { "Variable": "$.DbInstanceStatus", "StringEquals": "available" }, "Next": "Instance is not in 'available' status" },
I hope that this information will be helpful.
I have also added a Step Function solution, github.com/sqlxpert/step-stay-stopped-aws-rds-aurora
Relevant content
- Accepted Answerasked 3 years ago
AWS OFFICIALUpdated 3 months ago