AWS Secrets Manager secret is rotated within the configured maximum number of days
AWS Secrets Manager secrets are evaluated for periodic rotation within a configured window (default 90 days).
Secrets with no recorded rotation, or with rotation older than the allowed window, are identified for review.
Risk
Long-lived or never-rotated secrets widen the attack window. Leaked or brute-forced credentials stay valid, enabling unauthorized access to databases and APIs, data exfiltration, and unauthorized changes-compromising confidentiality and integrity.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks secretsmanager_secret_rotated_periodicallyRecommendation
Enable automatic rotation for all secrets with intervals aligned to sensitivity (**90 days or more frequent). Ensure apps retrieve secrets at runtime. Apply least privilege to rotation roles and KMS keys, use separation of duties, and monitor rotation health with alerts. Avoid hard-coded credentials and retire unused secrets.
Remediation
CLI
aws secretsmanager rotate-secret --secret-id <secret-id>Terraform
# Enable rotation for the secret
resource "aws_secretsmanager_secret_rotation" "<example_resource_name>" {
secret_id = "<example_resource_id>" # CRITICAL: target secret
rotation_lambda_arn = "<example_resource_id>" # CRITICAL: Lambda ARN used to rotate
rotation_rules {
automatically_after_days = 30 # CRITICAL: rotate within allowed days
}
}NativeIaC
# CloudFormation: enable rotation and rotate now
Resources:
<example_resource_name>:
Type: AWS::SecretsManager::RotationSchedule
Properties:
SecretId: <example_resource_id> # CRITICAL: target secret to rotate
RotationLambdaARN: <example_resource_id> # CRITICAL: Lambda ARN used to perform rotation
ScheduleExpression: rate(30 days) # CRITICAL: ensures rotation occurs within max allowed days
RotateImmediatelyOnUpdate: true # CRITICAL: triggers an immediate rotation to pass the checkOther
Check Code
from datetime import datetime, timezone
from typing import List
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.secretsmanager.secretsmanager_client import (
secretsmanager_client,
)
class secretsmanager_secret_rotated_periodically(Check):
"""Check if AWS Secret Manager secrets are rotated periodically.
This class checks if each secret in AWS Secret Manager has been rotated periodically
the maximum number of days allowed could be configured in the audit_config file as max_days_secret_unrotated.
"""
def execute(self) -> List[Check_Report_AWS]:
"""Execute secretsmanager_secret_rotated_periodically check.
Iterate over all secrets in AWS Secret Manager and check if each secret has been rotated in the past
max_days_secret_unrotated days.
Returns:
List of reports objects for each secret in AWS Secret Manager.
"""
findings = []
for secret in secretsmanager_client.secrets.values():
report = Check_Report_AWS(metadata=self.metadata(), resource=secret)
report.status = "PASS"
report.status_extended = f"Secret {secret.name} was last rotated on {secret.last_rotated_date.strftime('%B %d, %Y')}."
if secret.last_rotated_date == datetime.min.replace(tzinfo=timezone.utc):
report.status = "FAIL"
report.status_extended = f"Secret {secret.name} has never been rotated."
else:
days_since_last_rotation = (
datetime.now(timezone.utc) - secret.last_rotated_date
).days
if days_since_last_rotation > secretsmanager_client.audit_config.get(
"max_days_secret_unrotated", 90
):
report.status = "FAIL"
report.status_extended = f"Secret {secret.name} has not been rotated in {days_since_last_rotation} days, which is more than the maximum allowed of {secretsmanager_client.audit_config.get('max_days_secret_unrotated', 90)} days."
findings.append(report)
return findings