This check flags AWS Secrets Manager secrets that have no automatic rotation configured, which means stale credentials sit around indefinitely waiting to be leaked or abused. Enable rotation with a Lambda rotation function and a sensible rotation window to keep secrets short-lived.
Static secrets are one of the quietest risks in a cloud environment. A database password or API key that was set once during launch and never changed will keep working for years, which is exactly what an attacker wants. AWS Secrets Manager gives you a built-in way to fix this with automatic rotation, but it is off by default. The secretsmanager_norotation check catches secrets that are missing this protection.
What this check detects
The check inspects every secret in AWS Secrets Manager and reports any secret where automatic rotation is not enabled. Concretely, it looks at the RotationEnabled attribute on the secret. If that flag is false or absent, the secret is marked as failing.
A passing secret has three things wired up:
- A rotation Lambda function (the
RotationLambdaARN) - A rotation schedule, either a fixed number of days or a cron/rate expression
RotationEnabledset totrue
Note: Secrets Manager rotation works by running a Lambda function that knows how to generate a new credential, update the target service (for example a database user), and then store the new value as a fresh secret version. AWS provides ready-made rotation function templates for RDS, Redshift, DocumentDB, and generic two-user schemes.
Why it matters
A secret that never rotates is a long-lived credential, and long-lived credentials are a recurring theme in real breaches. The longer a secret stays valid, the more time it has to leak through a log file, a committed .env, a misconfigured backup, or a compromised CI runner. Once it leaks, it keeps working because nothing ever invalidates it.
Consider a few concrete scenarios:
- Leaked database password. A developer pastes a connection string into a support ticket. Without rotation, that password is valid until someone manually remembers to change it, which usually means never.
- Departed employee. A contractor had read access to a secret during their engagement. After they leave, the secret value they saw is still live months later.
- Compromised build pipeline. An attacker who gains brief access to a CI job can exfiltrate every secret it reads. Frequent rotation shrinks the window in which stolen values are useful.
There is also a compliance angle. Frameworks like PCI DSS, SOC 2, and HIPAA expect credential rotation as a control. An unrotated secret is an easy finding for an auditor, and a hard one to explain away.
Warning: Rotation does not retroactively protect a secret that has already leaked, but it does cap the blast radius. A 30-day rotation means a leaked value is useless after at most 30 days, instead of forever.
How to fix it
The fix depends on what the secret protects. RDS, Aurora, Redshift, and DocumentDB have managed rotation that AWS can set up for you. Other secrets need a custom rotation Lambda. Here are the common paths.
Option 1: Console (RDS-backed secret)
- Open the Secrets Manager console and select the secret.
- Scroll to Rotation configuration and choose Edit rotation.
- Toggle Automatic rotation on.
- Set a schedule, for example every 30 days.
- Choose Use a rotation function from the serverless application repository and pick the matching template (single user or alternating users).
- Confirm the database connection details so the Lambda can reach the target, then save.
Option 2: AWS CLI
If you already have a rotation Lambda deployed, enabling rotation on an existing secret is a single call:
aws secretsmanager rotate-secret \
--secret-id prod/app/db-credentials \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSRotation \
--rotation-rules '{"ScheduleExpression": "rate(30 days)"}'
For RDS, Aurora, Redshift, or DocumentDB secrets where you want AWS to manage the rotation Lambda, use the managed option instead:
aws secretsmanager rotate-secret \
--secret-id prod/app/db-credentials \
--rotation-rules '{"ScheduleExpression": "rate(30 days)"}' \
--rotate-immediately
Warning: The rotate-secret call triggers an immediate rotation unless you pass --no-rotate-immediately. If your application caches the old secret value, a sudden rotation can cause auth failures until it picks up the new version. Test in a non-production account first and make sure clients fetch secrets fresh rather than caching them indefinitely.
Option 3: Terraform
For infrastructure as code, attach a rotation block to the secret resource:
resource "aws_secretsmanager_secret" "db" {
name = "prod/app/db-credentials"
}
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotation.arn
rotation_rules {
automatically_after_days = 30
}
}
For RDS secrets managed by AWS, you can skip the custom Lambda and let the aws_rds_cluster or aws_db_instance managed master password feature handle rotation, which provisions and rotates the secret automatically.
Tip: The rotation Lambda needs network access to the target. If your database lives in a private subnet, deploy the rotation function inside the same VPC with a security group that allows it to reach the database port. Most failed rotations come down to a missing VPC config or a security group rule, not the rotation logic itself.
Verify the fix
aws secretsmanager describe-secret \
--secret-id prod/app/db-credentials \
--query '{Rotation: RotationEnabled, Lambda: RotationLambdaARN, Rules: RotationRules}'
You want RotationEnabled to come back as true with a populated Lambda ARN and rules.
How to prevent it from happening again
Fixing the secrets you have today is the easy part. Stopping new unrotated secrets from appearing is what keeps the finding from coming back.
Enforce rotation in IaC review
Use a policy-as-code tool to reject any aws_secretsmanager_secret that does not have a matching rotation resource. Here is a Checkov-style approach using a custom policy, but Open Policy Agent and Sentinel can express the same rule:
# conftest / OPA rego sketch
deny[msg] {
resource := input.resource.aws_secretsmanager_secret[name]
not has_rotation(name)
msg := sprintf("Secret '%s' has no rotation configured", [name])
}
Wire that into a CI step so the pipeline fails before an unrotated secret reaches an account.
Add an AWS Config rule
AWS Config ships a managed rule, secretsmanager-rotation-enabled-check, that continuously evaluates every secret and flags any without rotation. Deploy it organization-wide so coverage does not depend on someone remembering to run a scan:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "secretsmanager-rotation-enabled-check",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "SECRETSMANAGER_ROTATION_ENABLED_CHECK"
},
"Scope": {
"ComplianceResourceTypes": ["AWS::SecretsManager::Secret"]
}
}'
Tip: Run the Lensix secretsmanager_norotation check on a schedule across all your accounts so you catch drift between IaC and reality. A secret created by hand during an incident is exactly the kind of thing that slips past pipeline gates.
Best practices
- Pick a rotation interval that matches sensitivity. Thirty days is a reasonable default. For high-value credentials such as production database masters, shorter is better. Going below a few days rarely helps and increases the chance of rotation-related outages.
- Use alternating-user rotation for databases. The two-user strategy keeps one credential live while the other rotates, which avoids the brief window where a single-user rotation can interrupt active connections.
- Never cache secrets forever in your apps. Use the AWS Secrets Manager caching library with a sensible TTL so applications pick up rotated values automatically instead of holding a stale password.
- Scope read access tightly. Rotation reduces the value of a leaked secret, but least-privilege IAM policies on
secretsmanager:GetSecretValuereduce who can leak it in the first place. - Alarm on rotation failures. A rotation Lambda that quietly fails leaves you thinking you are protected when you are not. Send rotation failures to CloudWatch alarms and route them to your on-call channel.
- Encrypt with a customer-managed KMS key. Pairing rotation with a CMK gives you an additional access boundary and a clear audit trail of who decrypted what.
Danger: Do not enable immediate rotation on a production secret without confirming every consumer can handle a credential change. If a critical service caches the old value and cannot refresh, you can lock yourself out of your own database the moment rotation runs. Stage it, watch the first rotation closely, and keep a break-glass credential path documented.
Automatic rotation is a small amount of setup that turns every secret from a permanent liability into a short-lived one. Enable it everywhere, enforce it in your pipeline, and let AWS Config and Lensix keep you honest as your environment grows.

