Back to Checks
aws

AWS Secrets Manager secret is rotated within the configured maximum number of days

secretsmanager_secret_rotated_periodically
Service: secretsmanagerSeverity: medium
OfficialBuilt-inVerified
Categories:secrets
Resource Type:AwsSecretsManagerSecret

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 Cloud
Run
prowler aws --checks secretsmanager_secret_rotated_periodically

Recommendation

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 check

Other

1Open the AWS Console > Secrets Manager
2Select the secret
3If Rotation status is Enabled: click Rotate secret immediately
4If Rotation is Disabled: click Edit rotation, turn on Automatic rotation, choose the rotation Lambda function, Save, then click Rotate secret immediately

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