If you're still rotating database passwords and API keys by hand — or worse, not rotating them at all — you're carrying risk that a single leaked credential can turn into a full breach. AWS Secrets Manager rotation solves this by letting you automate secret rotation AWS-wide, replacing static, long-lived credentials with short-lived ones that change on a schedule without breaking your applications. In this guide, you'll learn how to enable rotation on an existing secret, deploy the Lambda function that actually performs the swap, and configure a schedule that will rotate RDS credentials automatically from now on. By the end, you'll have a working rotation pipeline you can extend to any secret type — API keys, OAuth tokens, third-party service credentials — plus a checklist for verifying it's actually doing its job in production.
Step 1: Inventory the secrets that actually need rotation
Before touching any configuration, list every secret Secrets Manager currently holds and flag which ones are rotation candidates. Database credentials, service account keys, and anything shared across multiple environments should go to the top of the list.
aws secretsmanager list-secrets \
--query 'SecretList[].{Name:Name,LastRotated:LastRotatedDate,RotationEnabled:RotationEnabled}' \
--output table
This gives you a fast view of which secrets have never been rotated. In most environments, the first candidates are RDS, Aurora, Redshift, or DocumentDB master credentials, since AWS ships native rotation templates for all of them. Anything custom — a third-party API key, a Stripe secret, a self-hosted service token — will need a custom Lambda rotation function later, but the workflow is the same.
Step 2: Turn on AWS Secrets Manager rotation for the target secret
For an RDS secret, enabling rotation is a single API call once the secret is already associated with the database. If you created the secret through the RDS console's "manage master credentials" flow, the association already exists.
aws secretsmanager rotate-secret \
--secret-id prod/orders-db/master \
--rotation-lambda-arn arn:aws:lambda:us-east-1:111122223333:function:SecretsManagerRDSRotation \
--rotation-rules AutomaticallyAfterDays=30
If you haven't created the Lambda function yet, don't worry — that's the next step. rotate-secret will still register the rotation configuration; AWS just won't be able to execute a rotation until the function exists and has permission to reach both Secrets Manager and the database.
A few things worth deciding at this point: how long is an acceptable rotation window (30 days is a common default, but 7–14 days is reasonable for high-value secrets), and whether you want rotation to trigger immediately after enabling it. Add --rotate-immediately if you want to validate the pipeline right away instead of waiting for the first scheduled window.
Step 3: Deploy the Secrets Manager Lambda rotation function
AWS publishes ready-made rotation function templates in the Serverless Application Repository for RDS MySQL, PostgreSQL, MariaDB, Aurora, Redshift, and DocumentDB. Rather than writing rotation logic from scratch, deploy the template that matches your engine:
aws serverlessrepo create-cloud-formation-change-set \
--application-id arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSPostgreSQLRotationSingleUser \
--stack-name secrets-rotation-postgres \
--parameter-overrides \
Name=endpoint,Value=secretsmanager.us-east-1.amazonaws.com \
Name=functionName,Value=SecretsManagerRDSRotation \
Name=vpcSubnetIds,Value=subnet-0a1b2c3d,subnet-0e4f5g6h \
Name=vpcSecurityGroupIds,Value=sg-0123456789abcdef0
Execute the resulting change set through CloudFormation to actually create the function. The template wires up the four-step rotation lifecycle Secrets Manager expects from any rotation Lambda — createSecret, setSecret, testSecret, and finishSecret — which stages a new password as AWSPENDING, applies it to the database, verifies the new credential works, then promotes it to AWSCURRENT and demotes the old one to AWSPREVIOUS. If you're rotating something Secrets Manager doesn't have a template for, clone the "single user" template's structure and swap in your own setSecret logic (calling the third-party API instead of a database driver) — the four-step contract stays the same.
Because the function needs to reach both the database and the Secrets Manager VPC endpoint (or the public endpoint over NAT), deploy it into the same VPC and subnets as your RDS instance, with a security group that allows outbound access to the database port.
Step 4: Configure the schedule so RDS credentials rotate automatically going forward
With the function deployed and attached, lock in the recurring schedule so you never have to think about this secret again:
aws secretsmanager rotate-secret \
--secret-id prod/orders-db/master \
--rotation-rules '{"ScheduleExpression": "rate(30 days)", "Duration": "2h"}'
Using a ScheduleExpression instead of AutomaticallyAfterDays gives you finer control — you can express rotation as a cron expression to run during a low-traffic maintenance window, and the Duration field bounds how long Secrets Manager waits for the rotation to complete before marking it stalled. This is the setting that actually delivers on the promise to rotate RDS credentials automatically: once it's in place, Secrets Manager invokes the Lambda function on schedule with no further manual steps, and every application reading the secret via the SDK or a sidecar picks up the new value on its next fetch.
Step 5: Lock down IAM permissions to least privilege
Rotation functions are high-value targets, so scope their permissions tightly. The execution role needs access only to the specific secret ARN and the ability to manage its own database user:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage"
],
"Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:prod/orders-db/master-??????"
},
{
"Effect": "Allow",
"Action": "secretsmanager:GetRandomPassword",
"Resource": "*"
}
]
}
Avoid wildcard resource ARNs across all secrets — a compromised rotation function with broad secretsmanager:* permissions is a far worse outcome than a secret that goes unrotated for a few extra days. Also confirm the database itself grants the rotation user (or the master user the function assumes) only the privileges needed to alter passwords, not full admin rights.
Step 6: Test the rotation function before you trust it
Trigger a manual rotation and confirm the new credential actually works end-to-end before letting the schedule run unattended:
aws secretsmanager rotate-secret --secret-id prod/orders-db/master --rotate-immediately
aws secretsmanager get-secret-value \
--secret-id prod/orders-db/master \
--version-stage AWSCURRENT
Then connect to the database using the returned credentials directly (psql, mysql, or your driver of choice) to confirm they're valid, and check that your application — not just your test script — can still authenticate after the swap. This step catches the most common rotation failure: a security group or network ACL that blocks the Lambda function from reaching the database, which causes rotation to silently fail rather than break anything visibly.
Troubleshooting and verification
Most rotation failures fall into a handful of predictable categories:
- Rotation stuck in progress: Check CloudWatch Logs for the rotation Lambda function. A timeout during
setSecretalmost always means a networking issue — verify the function's VPC subnets have a route to the database and that security groups allow the connection. AWSPENDINGversion never promoted: This meanstestSecretfailed. Pull the exact error from the Lambda logs; it's usually a permissions mismatch between the new password and what the database user is allowed to do, or a connection pool holding onto the old credential.- Applications erroring out after rotation: Confirm your application is reading the secret fresh (via the Secrets Manager SDK, a caching layer with a short TTL, or a sidecar like the Secrets Manager Agent) rather than reading it once at startup and holding it in memory indefinitely.
- Verify end-to-end health on a recurring basis, not just after setup:
aws secretsmanager describe-secret --secret-id prod/orders-db/master \
--query '{RotationEnabled:RotationEnabled,LastRotatedDate:LastRotatedDate,LastRotationSucceeded:RotationRules}'
- Set a CloudWatch alarm on the Lambda function's
Errorsmetric and onRotationLambdaInvocationCountso a failed rotation surfaces as an alert instead of a mystery three months later when the credential expires or an auditor asks when it was last changed.
How Safeguard Helps
Rotation is only half the story — the other half is knowing, at any moment, which secrets exist across your AWS accounts, which ones are actually rotating on schedule, and which ones have quietly drifted out of compliance. Safeguard continuously discovers secrets across your cloud accounts, source repositories, CI/CD pipelines, and container images, then cross-references what it finds against your Secrets Manager inventory to flag credentials that are hardcoded, unmanaged, or stale despite a rotation policy being "enabled" on paper.
For teams building out automatic secret rotation AWS-wide, Safeguard adds the guardrails that raw AWS tooling doesn't provide out of the box: alerts when a secret's LastRotatedDate falls outside policy, detection of the same credential value leaking into a Lambda environment variable or a config file after rotation should have replaced it, and audit-ready evidence for SOC 2 and other compliance frameworks showing exactly when every credential was last rotated and by what mechanism. Instead of trusting that a rotation Lambda ran successfully somewhere in your account, you get a single view across every secret, every environment, and every rotation event — so the automation you just built keeps doing its job long after this setup guide is closed.