Symptoms
- The console shows Last rotation failed, or an EventBridge rule reports a
RotationFailedevent. - Applications start failing with
password authentication failedorAccess denied for userright after a rotation window. rotate-secretreturnsA previous rotation isn't complete. That rotation will be reattempted.
First: is production broken?
A failed rotation does not always break anything. Secrets Manager only moves the AWSCURRENT label in the final step, so if rotation failed earlier, AWSCURRENT usually still holds the old, working password. Check that before you change anything:
SECRET=prod/app/db
aws secretsmanager describe-secret --secret-id $SECRET \
--query "{enabled:RotationEnabled,lambda:RotationLambdaARN,rules:RotationRules,last:LastRotatedDate,next:NextRotationDate,stages:VersionIdsToStages}"
Then test that AWSCURRENT actually logs in (PostgreSQL example; use mysql or sqlcmd for other engines):
aws secretsmanager get-secret-value --secret-id $SECRET --version-stage AWSCURRENT \
--query SecretString --output text > /tmp/s.json
PGPASSWORD=$(jq -r .password /tmp/s.json) psql -h "$(jq -r .host /tmp/s.json)" \
-U "$(jq -r .username /tmp/s.json)" -d postgres -c "select 1"
rm /tmp/s.json
Caution
If
AWSCURRENTdoes not log in, the database password and the secret no longer match, and applications will fail as their connection pools reconnect. Reset the database password to the value inAWSCURRENT(for RDS:aws rds modify-db-instance --db-instance-identifier <id> --master-user-password '<value>' --apply-immediately), or moveAWSCURRENTback to the version that works (see "Clear a stuck rotation"). Fix the outage first, then the rotation.
How rotation works
The rotation Lambda function is called four times, once per step. Its CloudWatch log shows which step failed:
| Step | What it does | Typical failure |
|---|---|---|
createSecret | Generates a new password and stores it as AWSPENDING | Can't reach the Secrets Manager endpoint; KMS denied |
setSecret | Changes the password in the database | Can't reach the database; user lacks privilege; password breaks a character rule |
testSecret | Logs in with AWSPENDING | Wrong host/port in the secret; TLS required but not configured |
finishSecret | Moves AWSCURRENT to the new version | Rarely fails; check for timeouts |
FN=$(aws secretsmanager describe-secret --secret-id $SECRET --query RotationLambdaARN --output text)
aws logs tail /aws/lambda/${FN##*:} --since 24h --format short
You can also see the failure event in CloudTrail:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=RotationFailed \
--max-results 10 --query "Events[].CloudTrailEvent" --output text
Symptom → cause → fix
| What you see | Cause | Fix |
|---|---|---|
Secrets Manager can't invoke the specified Lambda function | The function's resource policy doesn't allow secretsmanager.amazonaws.com | Add the permission (below) |
Log stops after Found credentials in environment variables, then Task timed out | Function is in a VPC with no route to Secrets Manager | Add a secretsmanager interface endpoint or a NAT route |
Timeout in setSecret or testSecret | Function security group can't reach the database port | Allow the function's SG on the DB SG inbound rule |
AccessDeniedException on GetSecretValue / PutSecretValue | Execution role missing Secrets Manager actions | Grant the actions on the secret ARN |
KMS ... AccessDeniedException | Secret uses a customer managed key the role can't use | Allow kms:Decrypt and kms:GenerateDataKey in the key policy or IAM |
password authentication failed in setSecret | The current password in the secret is already wrong | Fix the mismatch first (see above) |
permission denied on ALTER USER | Alternating-users strategy, but the superuser secret is wrong or lacks privilege | Check the secret referenced by masterarn |
A previous rotation isn't complete | An AWSPENDING version from a failed run is still there | Fix the cause, then clear it (below) |
| Rotation fails only some of the time | Function hits its Lambda timeout, or is throttled by a low concurrency limit | Raise the timeout; check the function's reserved concurrency and account concurrency |
Lambda resource policy
aws lambda add-permission --function-name ${FN##*:} \
--statement-id SecretsManagerInvoke \
--action lambda:InvokeFunction \
--principal secretsmanager.amazonaws.com \
--source-account 111122223333
Network path from a VPC function
aws lambda get-function-configuration --function-name ${FN##*:} \
--query "{vpc:VpcConfig,timeout:Timeout}"
aws ec2 describe-vpc-endpoints \
--filters Name=service-name,Values=com.amazonaws.eu-west-1.secretsmanager \
--query "VpcEndpoints[].{id:VpcEndpointId,vpc:VpcId,dns:PrivateDnsEnabled,sgs:Groups[].GroupId}"
The endpoint needs private DNS turned on, and its security group must allow inbound 443 from the function's subnets or security group. Run the Reachability Analyzer from the function's ENI to the database ENI if the problem is the database path.
Clear a stuck rotation
When the cause is fixed, remove the leftover AWSPENDING label and rotate again. Get the pending version ID first:
aws secretsmanager list-secret-version-ids --secret-id $SECRET \
--query "Versions[].{id:VersionId,stages:VersionStages,created:CreatedDate}" --output table
aws secretsmanager update-secret-version-stage --secret-id $SECRET \
--version-stage AWSPENDING --remove-from-version-id <pending-version-id>
aws secretsmanager rotate-secret --secret-id $SECRET
Warning
If
setSecretsucceeded buttestSecretorfinishSecretfailed, the database may already have theAWSPENDINGpassword. Test theAWSPENDINGvalue (--version-stage AWSPENDING) before you remove it. If it works andAWSCURRENTdoesn't, promote it instead:aws secretsmanager update-secret-version-stage --secret-id $SECRET \ --version-stage AWSCURRENT --move-to-version-id <pending-version-id> \ --remove-from-version-id <current-version-id>
Watch the new run in the function's logs. All four steps should log and finish within a few seconds.
RDS-managed master passwords are different
If the instance uses --manage-master-user-password, RDS owns the secret and its rotation. There is no rotation Lambda for you to fix. Don't edit that secret by hand. Check the RDS events and the secret's OwningService:
aws secretsmanager describe-secret --secret-id $SECRET --query OwningService
aws rds describe-events --source-type db-instance --source-identifier prod-db --duration 1440
Prevent it next time
- Create an EventBridge rule on Secrets Manager
RotationFailedevents (delivered via CloudTrail) and send it to your on-call channel, so you know before the old password ages out. - Use the alternating-users strategy for application credentials, so a failed rotation never invalidates the login that's in use.
- Make applications read the secret on authentication failure and reconnect, rather than caching the password until restart.
- Keep the rotation function's timeout at 30 seconds or more and give it a VPC endpoint rather than relying on a NAT gateway.
- After any change to security groups, KMS key policies or the database user, trigger
rotate-secretin a test environment first.