> ## 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.

# OCI IAM API Keys Should Be Rotated Every 90 Days

### More Info:

API signing keys should be rotated every 90 days. Regular rotation limits the window of exposure if a key is compromised and ensures cryptographic material stays current

### Risk Level

High

### Address

Compliance, Security

### Compliance Standards

* APRA CPS 234 (Australia)
* BSI C5 (Germany)
* Brazil LGPD
* CCPA / CPRA (California)
* CIS AWS
* CIS Critical Security Controls v8
* CMMC 2.0
* CSA Cloud Controls Matrix v4
* Cloudanix Best Practice
* DPDPA
* Digital Operational Resilience Act (EU)
* Essential 8
* GDPR
* HIPAA
* HITRUST CSF
* ISO/IEC 27017
* ISO/IEC 27018
* ISO/IEC 27701
* KSA PDPL
* MAS Technology Risk Management (Singapore)
* MITRE ATT\&CK (Cloud)
* NIS2 Directive
* NIST
* NIST SP 800-171
* NYDFS 23 NYCRR 500
* Reserve Bank of India (RBI) Master Direction – Information Technology Framework
* SWIFT Customer Security Controls Framework
* Sarbanes-Oxley IT General Controls
* UK NCSC Cyber Assessment Framework

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are the console-based steps to remediate **“OCI IAM API Keys Should Be Rotated Every 90 Days”** by rotating a user’s API key and aligning with monitoring.

        ***

        ## 1. Identify which API keys need rotation

        1. Sign in to the **OCI Console**.
        2. Go to **Identity & Security** → **Identity** → **Users**.
        3. Click the specific **user**.
        4. In the user details, go to the **API Keys** tab.
        5. Note the **Created** date of each key. Any older than 90 days should be rotated.

        ***

        ## 2. Create a new API key for the user

        1. Stay on the same user’s **API Keys** tab.
        2. Click **Add API Key**.
        3. Choose **Generate API Key Pair** (recommended for simplicity):
           * The console generates a public/private key pair.
           * Click **Download Private Key** and save it securely (this is your only chance).
        4. Click **Add** to register the new key.
        5. Copy and save:
           * **Fingerprint**
           * **Tenancy OCID**
           * **User OCID**
           * API endpoint region (for CLI/SDK config later).

        > If you already have your own key pair, choose **Upload Public Key** and upload the public key instead.

        ***

        ## 3. Update applications / tools to use the new key

        Wherever the old key is used (CLI config, SDKs, automation scripts, CI/CD):

        1. Update the configuration with:
           * New **private key file path**
           * New **fingerprint**
           * Same **user OCID** and **tenancy OCID** (unless you changed the user/tenancy)
           * Same **region**
        2. Test that:
           * `oci iam compartment list` (or another simple call) works for that user.
           * Applications using the key can still access needed resources.

        Ensure everything is working before deleting the old key.

        ***

        ## 4. Delete the old API key

        1. Back in **Identity & Security** → **Identity** → **Users** → *user* → **API Keys**.
        2. Identify the **old** key (by **Created** date or fingerprint).
        3. Click the **three dots (⋯)** next to the old key.
        4. Click **Delete** and confirm.

        That fully rotates the key: new key in use; old key removed.

        ***

        ## 5. Set up / confirm monitoring for 90‑day rotation

        To continuously detect this issue:

        1. Go to **Cloud Guard** (Identity & Security → **Cloud Guard**).
        2. Make sure **Cloud Guard status** is **Enabled**.
        3. Under **Detector Recipes**, open your active recipe (often the default Oracle-managed one, or your clone).
        4. Find the detector for **IAM API key age / rotation** (name varies but is typically under IAM or Identity security checks).
        5. Ensure it is:
           * **Enabled**
           * Severity set appropriately (e.g., High/Medium)
        6. (Optional) Under **Responder Recipes**, configure responders/notifications (email, pager, etc.) for this detector so you are alerted when a key is older than 90 days.

        ***

        **Ongoing practice:** Repeat this process for all users with API keys; integrate it into a 90‑day rotation schedule, or rely on Cloud Guard alerts to trigger rotation.
      </Accordion>

      <Accordion title="Using CLI">
        Below are the minimal, CLI-focused steps to remediate “OCI IAM API Keys Should Be Rotated Every 90 Days” by rotating the keys and (optionally) setting up monitoring.

        ***

        ## 1. Prerequisites

        * OCI CLI installed and configured (`oci setup config` already done).
        * You know the **User OCID** whose API keys you want to rotate (could be yourself or a service user).

        If you don’t know the user OCID (for your own account):

        ```bash theme={null}
        oci iam user list --all --compartment-id <tenancy_ocid> \
          --query "data[?\"name\"=='<your_username>'].id | [0]" --raw-output
        ```

        ***

        ## 2. List Existing API Keys and Check Age

        ```bash theme={null}
        oci iam user api-key list \
          --user-id <user_ocid> \
          --query 'data[].{"keyId": "key-id", "timeCreated": "time-created"}' \
          --output table
        ```

        If any `timeCreated` is older than 90 days, you should rotate that key.

        ***

        ## 3. Generate a New RSA Key Pair Locally

        ```bash theme={null}
        mkdir -p ~/.oci/keys
        cd ~/.oci/keys

        # Generate new key pair (adjust file names as needed)
        openssl genrsa -out new_api_key.pem 2048
        openssl rsa -pubout -in new_api_key.pem -out new_api_key_public.pem
        ```

        ***

        ## 4. Upload the New Public Key to OCI (Create New API Key)

        ```bash theme={null}
        oci iam user api-key upload \
          --user-id <user_ocid> \
          --key-file new_api_key_public.pem \
          --query 'data."key-id"' \
          --raw-output
        ```

        This returns the new `key-id` which you can record for reference.

        ***

        ## 5. Update Your OCI CLI Config to Use the New Private Key

        Edit your OCI config file (typically `~/.oci/config`):

        ```bash theme={null}
        nano ~/.oci/config
        ```

        Update (or add) the profile you use to include the new key:

        ```ini theme={null}
        [DEFAULT]
        user=<user_ocid>
        fingerprint=<new_key_fingerprint_from_console_or_list>
        tenancy=<tenancy_ocid>
        region=<region_identifier>
        key_file=~/.oci/keys/new_api_key.pem
        ```

        You can get fingerprints for keys via:

        ```bash theme={null}
        oci iam user api-key list --user-id <user_ocid> \
          --query 'data[].{"keyId":"key-id","fingerprint":"fingerprint"}' \
          --output table
        ```

        Test that CLI works with the new key:

        ```bash theme={null}
        oci iam compartment list --compartment-id <tenancy_ocid> --limit 1
        ```

        ***

        ## 6. Delete the Old API Key

        List keys again to find the old `key-id`:

        ```bash theme={null}
        oci iam user api-key list --user-id <user_ocid> \
          --query 'data[].{"keyId":"key-id","timeCreated":"time-created"}' \
          --output table
        ```

        Delete the old key:

        ```bash theme={null}
        oci iam user api-key delete \
          --user-id <user_ocid> \
          --fingerprint <old_key_fingerprint> \
          --force
        ```

        Or using `key-id`:

        ```bash theme={null}
        oci iam user api-key delete \
          --user-id <user_ocid> \
          --key-id <old_key_id> \
          --force
        ```

        ***

        ## 7. (Optional) Monitoring: Detect Keys Older Than 90 Days

        You can periodically run a script using OCI CLI to detect keys older than 90 days and send alerts via Monitoring/Notifications.

        Example: list keys with creation time filter using JMESPath and `date` comparison in a shell script:

        ```bash theme={null}
        THRESHOLD_DAYS=90
        CUTOFF_DATE=$(date -u -d "-${THRESHOLD_DAYS} days" +%s)

        oci iam user api-key list --user-id <user_ocid> \
          --query 'data[].{"keyId":"key-id","timeCreated":"time-created"}' \
          --raw-output | jq -r '.[] | @base64' | while read row; do
          _jq() { echo ${row} | base64 --decode | jq -r ${1}; }
          KEY_ID=$(_jq '.keyId')
          TIME_CREATED=$(_jq '.timeCreated')
          CREATED_EPOCH=$(date -u -d "$TIME_CREATED" +%s)

          if [ "$CREATED_EPOCH" -lt "$CUTOFF_DATE" ]; then
            echo "API key $KEY_ID is older than ${THRESHOLD_DAYS} days."
            # Integrate here with OCI Notifications / email / Slack etc.
          fi
        done
        ```

        You can run this via a scheduled job (cron, OCI DevOps pipeline, or external scheduler) and alert when old keys are found.

        ***

        These steps remediate the finding by rotating OCI IAM API keys via OCI CLI and optionally allow you to monitor and alert on keys older than 90 days.
      </Accordion>

      <Accordion title="Using Python">
        Below is a simple, practical way to **monitor** and enforce a “rotate every 90 days” policy for OCI IAM API keys using Python and the OCI SDK.

        You’ll:

        1. Use the OCI Python SDK to list users and their API keys
        2. Calculate key age
        3. Flag keys older than 90 days (log, email, or push metrics)
        4. Optionally, auto-delete old keys (if your process supports it)

        ***

        ## 1. Prerequisites

        1. Install the OCI Python SDK:

        ```bash theme={null}
        pip install oci
        ```

        2. Create/verify an OCI config file (usually `~/.oci/config`) with a profile:

        ```ini theme={null}
        [DEFAULT]
        user=ocid1.user.oc1..aaaa...
        fingerprint=xx:xx:xx:...
        key_file=/path/to/oci_api_key.pem
        tenancy=ocid1.tenancy.oc1..aaaa...
        region=us-ashburn-1
        ```

        3. Make sure the user or instance principal running the script has these IAM permissions in a policy (at least at tenancy or compartment scope you care about):

        ```text theme={null}
        Allow group SecurityAdmins to inspect users in tenancy
        Allow group SecurityAdmins to inspect api-keys in tenancy
        Allow group SecurityAdmins to manage api-keys in tenancy  # only if you want auto-delete
        ```

        ***

        ## 2. Python Script: Detect API Keys Older Than 90 Days

        This script:

        * Lists all IAM users
        * Lists each user’s API keys
        * Flags keys older than 90 days
        * (Optional) deletes them if `AUTO_DELETE_OLD_KEYS = True`

        ```python theme={null}
        import oci
        from datetime import datetime, timezone, timedelta

        # === CONFIG ===
        PROFILE_NAME = "DEFAULT"             # profile from ~/.oci/config
        MAX_AGE_DAYS = 90
        AUTO_DELETE_OLD_KEYS = False         # set True if you want to auto-delete
        DRY_RUN = True                       # if deleting, keep True to test first

        def main():
            # Load config
            config = oci.config.from_file("~/.oci/config", PROFILE_NAME)
            identity_client = oci.identity.IdentityClient(config)

            tenancy_id = config["tenancy"]
            cutoff = datetime.now(timezone.utc) - timedelta(days=MAX_AGE_DAYS)

            print(f"Checking API keys older than {MAX_AGE_DAYS} days (before {cutoff.isoformat()})")

            # List all users in the tenancy
            users = oci.pagination.list_call_get_all_results(
                identity_client.list_users,
                compartment_id=tenancy_id
            ).data

            for user in users:
                user_ocid = user.id
                user_name = user.name

                # List API keys for each user
                api_keys = identity_client.list_api_keys(user_ocid).data

                for key in api_keys:
                    key_id = key.key_id
                    time_created = key.time_created  # datetime with timezone
                    age_days = (datetime.now(timezone.utc) - time_created).days

                    if time_created < cutoff:
                        print(
                            f"[STALE] User: {user_name} ({user_ocid}), "
                            f"Key: {key_id}, Created: {time_created}, Age: {age_days} days"
                        )

                        if AUTO_DELETE_OLD_KEYS:
                            if DRY_RUN:
                                print(f"  -> DRY RUN: would delete key {key_id}")
                            else:
                                delete_api_key(identity_client, user_ocid, key_id)

                    else:
                        # Uncomment if you want to log all keys
                        # print(f"[OK] User: {user_name}, Key: {key_id}, Age: {age_days} days")
                        pass


        def delete_api_key(identity_client, user_ocid, key_id):
            print(f"  -> Deleting key {key_id} for user {user_ocid}")
            identity_client.delete_api_key(
                user_id=user_ocid,
                fingerprint=key_id  # for API keys, key_id is the fingerprint
            )


        if __name__ == "__main__":
            main()
        ```

        **Notes:**

        * `time_created` is returned in UTC with timezone.
        * For IAM API keys, `key_id` is the fingerprint and is used as `fingerprint` in `delete_api_key`.
        * Keep `DRY_RUN=True` while testing to avoid accidental deletion.

        ***

        ## 3. Integrating with “Monitoring”

        Pick how you want to consume this check:

        ### Option A: Run on a Schedule (cron / CI)

        Run the script daily via cron on a bastion server or in a CI pipeline (GitHub Actions, Jenkins, etc.):

        Example cron (run every night at 01:00):

        ```bash theme={null}
        0 1 * * * /usr/bin/python3 /path/to/check_oci_api_keys.py >> /var/log/oci_api_key_check.log 2>&1
        ```

        You can then:

        * Send email/Slack when `[STALE]` appears in log.
        * Or export output as JSON and push to a logging system.

        ***

        ### Option B: Use an OCI Function + Logging / Notifications

        1. Package this script logic as an OCI Function (Python runtime).
        2. Write stale key findings to:
           * OCI Logging (using `logging` module)
           * or push notifications via OCI Notifications to email/Slack.

        High-level steps:

        * Create function application in your compartment.
        * Deploy Python function with above logic (adapt to use instance principal or resource principal).
        * Create a scheduled job using **OCI Events + Functions** (Event rule with schedule, e.g., `cron(0 1 * * ? *)`).
        * Function writes to Logging or calls Notifications.

        ***

        ## 4. (Optional) Enforcing Rotation, Not Just Deletion

        To truly “rotate” instead of just delete, you need a process:

        1. Notify user whose keys are > N days old.
        2. User (or automation) creates a new key pair and uploads public key or uses CLI to create new API key.
        3. Verify new key works.
        4. Script deletes old key once new is in place and younger than threshold.

        You can enhance the script to:

        * Only delete keys if user has at least one “young” key.
        * Or send email only, and have manual rotation performed.

        ***

        ## 5. Summary

        * Use the OCI Python SDK (`oci.identity.IdentityClient`) to list users and `list_api_keys`.
        * Compare `time_created` to `now - 90 days`.
        * Log/alert or auto-delete keys older than 90 days.
        * Schedule the script via cron, CI, or OCI Functions/Events to continuously monitor and enforce your rotation policy.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # OCI IAM API keys cannot be rotated or time-limited via Terraform.
        # The oci/oci provider does not expose a resource or argument that:
        # - Sets an expiry/rotation interval for API keys, or
        # - Rotates an existing API key on a schedule.

        # API keys are created/managed as user credentials outside Terraform,
        # so rotation every 90 days must be handled operationally:
        #
        # 1. In the OCI Console:
        #    - Go to Identity & Security -> Users.
        #    - Select the user -> API Keys.
        #    - Add a new API key (download private key & config).
        #    - Update any workloads using the old key to use the new key.
        #    - Delete the old API key.
        #
        # 2. Or with OCI CLI or automation (scripts, CI pipelines, etc.)
        #    to:
        #    - Create new API keys,
        #    - Update consuming apps’ configs,
        #    - Delete keys older than 90 days.
        #
        # For “OCI IAM Monitoring”, Terraform can at most:
        # - Tag users or keys (where supported) and you then monitor age externally,
        # - Or wire up alarms/logging that detect keys older than 90 days.
        # But the actual key rotation remains a runtime/manual process.

        # Because Terraform cannot manage or rotate oci-identitymanagement-iam-apikey
        # credentials directly, `terraform plan` will show **no changes** related to
        # key rotation; all rotation behavior must be implemented outside Terraform.
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
