> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Kms secrets rotation frequency remediation

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are concise, step-by-step AWS Console instructions to remediate “Secrets Manager Secrets Should Be Rotated Frequently” for secrets encrypted with AWS KMS.

        ***

        ## 1. Identify Non-Rotating Secrets

        1. Sign in to the **AWS Management Console**.
        2. Go to **Secrets Manager**:
           * Services → **Secrets Manager**.
        3. In **Secrets**, look for secrets where:
           * **Rotation configuration** column is **“Disabled”** or rotation interval is longer than your policy (e.g., >30 days).

        You’ll remediate each of these secrets.

        ***

        ## 2. Enable Rotation for a Secret

        Perform this for each non-compliant secret.

        1. In **Secrets Manager**, click the **secret name**.
        2. In the secret’s detail page, choose **Rotate secret** (or **Edit rotation** if already enabled but not compliant).

        ### 2.1 Choose Rotation Strategy

        3. On the **Rotation configuration** page:
           * Check **“Enable automatic rotation”**.
           * Set **Rotation schedule**:
             * Choose **“Every X days”** and enter your required frequency\
               (e.g., **30** for monthly rotation).

        ### 2.2 Choose or Create Rotation Lambda Function

        4. Under **Rotation function**, choose one:
           * If you already have a rotation Lambda for this type (e.g., RDS, DocumentDB, Redshift, etc.):
             * Select **“Use an existing Lambda function”** and pick the function.
           * If you don’t have one:
             1. Select **“Create a new Lambda function”**.
             2. Choose the **secret type / database type** (e.g., RDS, other database, custom).
             3. Follow the wizard to create the Lambda:
                * Select the **VPC**, **subnets**, and **security groups** if the target is in a private network.
                * Secrets Manager will create a template Lambda function with the correct rotation logic for that backend.

        5. Click **Next**, then **Save** / **Enable rotation**.

        Secrets Manager will now:

        * Use your **KMS key** (already associated with the secret) to encrypt the new versions.
        * Automatically invoke the Lambda on your schedule to rotate the secret.

        ***

        ## 3. Verify KMS Key Permissions (If Rotation Fails)

        If rotation errors occur, you may need to adjust the KMS CMK policy:

        1. Go to **AWS KMS**:
           * Services → **Key Management Service** → **Customer managed keys**.
        2. Click the **CMK** used by the secret (shown on the secret’s detail page under **Encryption key**).
        3. Under **Key policy**:
           * Ensure the IAM role used by the **rotation Lambda function** has:
             * `kms:Encrypt`
             * `kms:Decrypt`
             * `kms:GenerateDataKey`
             * `kms:DescribeKey`
        4. Save the key policy if edited.

        ***

        ## 4. Confirm Rotation Is Working

        1. Back in **Secrets Manager**, open the secret.
        2. Confirm:
           * **Rotation configuration** shows **Enabled** and the correct interval.
           * **Secret versions** show multiple versions over time (after the first rotation occurs).
        3. Optionally, click **Rotate secret now** to test the configuration immediately.

        ***

        Repeat for all flagged secrets until all have automatic rotation enabled at the desired frequency.
      </Accordion>

      <Accordion title="Using CLI">
        Below is how to remediate “Secrets Manager Secrets Should Be Rotated Frequently” for AWS using the AWS CLI. This covers enabling automatic rotation for an AWS Secrets Manager secret (which is encrypted with KMS).

        ***

        ## 1. Identify the secret(s) that need rotation

        ```bash theme={null}
        aws secretsmanager list-secrets --query 'SecretList[].Name'
        ```

        Or filter by tag / name pattern as needed.

        ***

        ## 2. (One-time) Create a rotation Lambda function

        If you don’t already have a rotation Lambda, you need one. For RDS or other standard engines, use an AWS-provided template from the console or AWS docs; for CLI-only, this is the basic outline:

        1. Create an IAM role for the Lambda with permissions to:
           * `secretsmanager:GetSecretValue`
           * `secretsmanager:PutSecretValue`
           * `secretsmanager:UpdateSecretVersionStage`
           * Any permissions needed to update the target (e.g., RDS, API key, etc.)

        Example trust policy file `lambda-trust-policy.json`:

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": { "Service": "lambda.amazonaws.com" },
              "Action": "sts:AssumeRole"
            }
          ]
        }
        ```

        Create role:

        ```bash theme={null}
        aws iam create-role \
          --role-name SecretsRotationRole \
          --assume-role-policy-document file://lambda-trust-policy.json
        ```

        Attach basic Lambda logging and Secrets Manager permissions (adjust ARNs/permissions as needed):

        ```bash theme={null}
        aws iam attach-role-policy \
          --role-name SecretsRotationRole \
          --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

        aws iam put-role-policy \
          --role-name SecretsRotationRole \
          --policy-name SecretsRotationPolicy \
          --policy-document file://secrets-rotation-inline-policy.json
        ```

        Your `secrets-rotation-inline-policy.json` should at minimum include:

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:PutSecretValue",
                "secretsmanager:UpdateSecretVersionStage"
              ],
              "Resource": "arn:aws:secretsmanager:REGION:ACCOUNT_ID:secret:*"
            },
            {
              "Effect": "Allow",
              "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:GenerateDataKey"
              ],
              "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID"
            }
          ]
        }
        ```

        Then create the Lambda function (ZIP file must contain your rotation code):

        ```bash theme={null}
        aws lambda create-function \
          --function-name my-secret-rotation-fn \
          --runtime python3.11 \
          --role arn:aws:iam::ACCOUNT_ID:role/SecretsRotationRole \
          --handler lambda_function.lambda_handler \
          --zip-file fileb://rotation_function.zip \
          --timeout 300
        ```

        You can reuse this Lambda for multiple secrets of the same type.

        ***

        ## 3. Enable automatic rotation on a secret

        Pick a rotation interval (e.g., 30 days). Example for `my-app-secret`:

        ```bash theme={null}
        SECRET_ARN=$(aws secretsmanager describe-secret \
          --secret-id my-app-secret \
          --query 'ARN' --output text)
        ```

        Enable rotation:

        ```bash theme={null}
        aws secretsmanager rotate-secret \
          --secret-id "$SECRET_ARN" \
          --rotation-lambda-arn arn:aws:lambda:REGION:ACCOUNT_ID:function:my-secret-rotation-fn \
          --rotation-rules AutomaticallyAfterDays=30
        ```

        If rotation Lambda is already attached and you only need to adjust frequency:

        ```bash theme={null}
        aws secretsmanager update-secret-rotation \
          --secret-id "$SECRET_ARN" \
          --rotation-rules AutomaticallyAfterDays=30
        ```

        ***

        ## 4. Verify rotation configuration

        ```bash theme={null}
        aws secretsmanager describe-secret \
          --secret-id "$SECRET_ARN" \
          --query '{RotationEnabled:RotationEnabled,Rules:RotationRules}'
        ```

        You should see `RotationEnabled: true` and `AutomaticallyAfterDays: 30` (or your chosen value).

        ***

        ## 5. (Optional) Enable KMS key rotation (separate from secret rotation)

        If your secret uses a customer-managed KMS key and you also want that key to rotate annually:

        ```bash theme={null}
        aws kms enable-key-rotation --key-id YOUR_KMS_KEY_ID
        ```

        Check status:

        ```bash theme={null}
        aws kms get-key-rotation-status --key-id YOUR_KMS_KEY_ID
        ```

        ***

        This configuration ensures Secrets Manager secrets are automatically rotated on the schedule you define, with encryption handled by KMS.
      </Accordion>

      <Accordion title="Using Python">
        Below is a concise step‑by‑step guide to remediate “Secrets Manager secrets should be rotated frequently” for an AWS KMS‑encrypted secret using Python. This covers:

        1. What you need set up
        2. Creating a Python rotation function (Lambda)
        3. Attaching it to the secret with a rotation schedule

        ***

        ## 1. Prerequisites

        1. **Existing secret in AWS Secrets Manager**, encrypted with a **KMS CMK** (customer‑managed key or AWS managed key).
        2. **IAM role for Lambda** with at least:
           * `secretsmanager:GetSecretValue`
           * `secretsmanager:PutSecretValue`
           * `secretsmanager:UpdateSecretVersionStage`
           * Any permissions required to change the underlying credentials (e.g., RDS, user store, etc.).
        3. **Python 3.x Lambda runtime**.

        ***

        ## 2. Rotation Model to Use

        Decide how new credentials are generated. Example scenarios:

        * **RDS or other DB password**: You call the DB to change password and then update the secret.
        * **API keys**: You call the provider to issue a new key, then update the secret.
        * **KMS key material** itself is not “rotated” by Secrets Manager. Instead, you rotate:
          * Either the **credentials that are stored in the secret** (such as DB creds, API keys that are encrypted with KMS),
          * Or you enable **KMS key rotation** directly on CMKs separately (outside of Secrets Manager):
            ```bash theme={null}
            aws kms enable-key-rotation --key-id <your-key-id>
            ```

        Below assumes we are rotating **credentials stored in the secret that are KMS‑encrypted**, which is what the misconfiguration usually refers to.

        ***

        ## 3. Python Rotation Lambda – Core Template

        AWS Secrets Manager expects the Lambda to implement a `lambda_handler` that supports these steps:

        * `createSecret`
        * `setSecret`
        * `testSecret`
        * `finishSecret`

        ### 3.1. Basic Lambda Skeleton (Python)

        Create a new Lambda function with Python 3.x and paste:

        ```python theme={null}
        import boto3
        import json
        import os
        import logging

        logger = logging.getLogger()
        logger.setLevel(logging.INFO)

        secrets_client = boto3.client('secretsmanager')

        def lambda_handler(event, context):
            """Entry point for Secrets Manager rotation."""
            logger.info(f"Event: {json.dumps(event)}")
            
            step = event['Step']
            secret_arn = event['SecretId']
            token = event['ClientRequestToken']
            
            # Validate version and stages
            metadata = secrets_client.describe_secret(SecretId=secret_arn)
            if 'RotationEnabled' in metadata and not metadata['RotationEnabled']:
                raise ValueError("Secret rotation not enabled for this secret.")

            versions = metadata['VersionIdsToStages']
            if token not in versions:
                raise ValueError("Secret version not set as part of rotation.")
            if 'AWSCURRENT' in versions[token]:
                logger.info("Version already AWSCURRENT, nothing to do.")
                return
            elif 'AWSPENDING' not in versions[token]:
                raise ValueError("Secret version not set to AWSPENDING for rotation.")

            if step == 'createSecret':
                create_secret(secret_arn, token)
            elif step == 'setSecret':
                set_secret(secret_arn, token)
            elif step == 'testSecret':
                test_secret(secret_arn, token)
            elif step == 'finishSecret':
                finish_secret(secret_arn, token)
            else:
                raise ValueError("Invalid step parameter")

        def create_secret(secret_arn, token):
            """
            Generate a new secret value and store it as the AWSPENDING version.
            This is where you actually *generate* the new credentials.
            """
            logger.info("createSecret step")
            
            # Get current secret as baseline, if needed
            current = secrets_client.get_secret_value(
                SecretId=secret_arn,
                VersionStage='AWSCURRENT'
            )
            current_secret_str = current['SecretString']
            current_secret = json.loads(current_secret_str)

            # Example: rotate a password field
            new_password = generate_password()
            new_secret = current_secret.copy()
            new_secret['password'] = new_password

            # Store AWSPENDING version
            secrets_client.put_secret_value(
                SecretId=secret_arn,
                ClientRequestToken=token,
                SecretString=json.dumps(new_secret),
                VersionStages=['AWSPENDING']
            )

        def set_secret(secret_arn, token):
            """
            Apply the pending credentials to the target resource (DB, service, etc.).
            For example, update DB user password here.
            """
            logger.info("setSecret step")

            pending = secrets_client.get_secret_value(
                SecretId=secret_arn,
                VersionId=token,
                VersionStage='AWSPENDING'
            )
            pending_secret = json.loads(pending['SecretString'])

            # Example: apply to a DB; replace with your logic
            # update_database_password(
            #     username=pending_secret['username'],
            #     password=pending_secret['password'],
            #     host=pending_secret['host'],
            #     ...
            # )

        def test_secret(secret_arn, token):
            """
            Verify the pending credentials actually work against the target resource.
            """
            logger.info("testSecret step")

            pending = secrets_client.get_secret_value(
                SecretId=secret_arn,
                VersionId=token,
                VersionStage='AWSPENDING'
            )
            pending_secret = json.loads(pending['SecretString'])

            # Example: try login/connection using pending_secret
            # if not test_database_connection(pending_secret):
            #     raise ValueError("Pending secret failed validation")

        def finish_secret(secret_arn, token):
            """
            Mark the pending version as current (AWSCURRENT) and demote old versions.
            """
            logger.info("finishSecret step")

            metadata = secrets_client.describe_secret(SecretId=secret_arn)
            versions = metadata['VersionIdsToStages']

            current_version = None
            for version, stages in versions.items():
                if 'AWSCURRENT' in stages:
                    current_version = version
                    break

            # Set AWSCURRENT to the new version
            secrets_client.update_secret_version_stage(
                SecretId=secret_arn,
                VersionStage='AWSCURRENT',
                MoveToVersionId=token,
                RemoveFromVersionId=current_version
            )

        def generate_password(length=32):
            """
            Helper: generate a strong password. Customize policy as needed.
            """
            import string
            import secrets as sec

            alphabet = string.ascii_letters + string.digits + string.punctuation
            return ''.join(sec.choice(alphabet) for _ in range(length))
        ```

        Replace the commented `update_database_password` / `test_database_connection` bits with the actual logic for your use case (API, DB, etc.).

        ***

        ## 4. Configure the Lambda for Rotation

        1. Create the Lambda (console or CLI) with the above code.
        2. Attach an IAM role allowing:
           * `secretsmanager:GetSecretValue`, `PutSecretValue`, `DescribeSecret`, `UpdateSecretVersionStage`
           * Any service‑specific actions (e.g., `rds:ModifyDBInstance`, or API client permissions).
        3. Ensure Lambda environment has necessary config values (e.g., DB endpoint, user, etc.) if not stored in the secret.

        ***

        ## 5. Enable Rotation on the Secret (via Console or Python)

        ### 5.1. Using AWS Console

        1. Go to **Secrets Manager** → select the secret.
        2. Click **Rotate secret**.
        3. Choose **Use an existing Lambda function** and select the Lambda created above.
        4. Set rotation schedule (e.g., **every 30 days**).
        5. Save.

        ### 5.2. Using Python (boto3)

        ```python theme={null}
        import boto3

        secrets_client = boto3.client('secretsmanager')

        secret_arn = "<your-secret-arn>"
        lambda_arn = "<your-lambda-arn>"

        response = secrets_client.rotate_secret(
            SecretId=secret_arn,
            RotationLambdaARN=lambda_arn,
            RotationRules={
                'AutomaticallyAfterDays': 30   # adjust as needed
            },
            RotateImmediately=True            # perform an immediate rotation
        )
        print(response)
        ```

        This:

        * Attaches the rotation Lambda.
        * Sets rotation interval (e.g., 30 days).
        * Triggers an immediate rotation cycle if `RotateImmediately=True`.

        ***

        ## 6. Ensure KMS Key Rotation (Optional but Recommended)

        If you use a **customer‑managed KMS key** to encrypt the secret, enable key rotation:

        ```bash theme={null}
        aws kms enable-key-rotation --key-id <your-kms-key-id>
        ```

        Or via boto3:

        ```python theme={null}
        import boto3
        kms = boto3.client('kms')

        kms.enable_key_rotation(
            KeyId='<your-kms-key-id>'
        )
        ```

        This rotates the key’s cryptographic material annually, complementing secret rotation.

        ***

        If you share what type of credential is stored in the secret (RDS, API key, etc.), I can adapt the `setSecret` and `testSecret` functions with concrete code.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "aws_kms_key" "SECRET_KMS_KEY" {
          description             = "KMS key used to encrypt Secrets Manager secrets"
          deletion_window_in_days = 30
          # ...any additional required arguments (e.g., policy) ...
        }

        # Enable automatic annual rotation on the CMK used for Secrets Manager
        resource "aws_kms_key_rotation" "SECRET_KMS_KEY_ROTATION" {
          key_id  = aws_kms_key.SECRET_KMS_KEY.key_id
          enabled = true
        }
        ```

        Substitute:

        * `SECRET_KMS_KEY` with the Terraform name you use for the KMS key that encrypts your Secrets Manager secrets.

        This change does not force replacement of the KMS key; it toggles rotation in place.

        Verification: `terraform plan` should show an `aws_kms_key_rotation` resource being created (or updated) with `enabled = true` for the target key and no replacement of the `aws_kms_key` itself.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
