> ## 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 Users Should Have Only One Active API Key

### More Info:

Each IAM user should have only one active API signing key. Multiple active keys increase the attack surface and make key rotation and compromise detection more difficult

### Risk Level

Medium

### 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
* 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 SP 800-171
* NYDFS 23 NYCRR 500
* 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">
        To remediate “OCI IAM Users Should Have Only One Active API Key” using the OCI Console, you need to remove extra API keys from each affected user so that only one remains.

        Follow these steps for each flagged IAM user:

        1. **Sign in to OCI Console**
           * Log in to the OCI Console with an account that has IAM administrator privileges.

        2. **Go to the Users page**
           * Open the main navigation menu (☰).
           * Navigate to: **Identity & Security** → **Identity** → **Users**.

        3. **Select the affected user**
           * In the user list, find and click the name of the user that has more than one API key (as indicated by your monitoring/detector).

        4. **Open the API Keys tab**
           * Inside the user details page, click the **API Keys** tab.
           * You’ll see a list of all API keys associated with this user.

        5. **Determine which key to keep**
           * Identify the key currently used by applications/automation (use:
             * the **Fingerprint**, and
             * the **Created** timestamp,
             * any internal documentation or configuration files where the key is referenced).
           * Decide **one** key to keep active. If you’re unsure, coordinate with the team using the key before deleting any.

        6. **Delete extra API keys**
           * For each **unneeded** API key:
             * Click the **Actions** (⋮) menu next to that key.
             * Select **Delete** (or **Remove**).
             * Confirm the deletion when prompted.
           * Repeat until the user has **only one** remaining API key.

        7. **Verify compliance**
           * Confirm under the **API Keys** tab that only one key remains.
           * If you use **Cloud Guard / IAM Monitoring**, allow a few minutes and then:
             * Go to **Cloud Guard** → **Detections** (or **Dashboard**),
             * Confirm that the “user has multiple API keys” problem is cleared or closed for that user.

        8. **(Optional) Standardize going forward**
           * Update your internal process to:
             * Issue only one active API key per IAM user.
             * Rotate by **replacing** a key (create new, update workloads, then delete old) so that you never leave more than one active longer than necessary.

        Repeat these steps for every user detected as having multiple active API keys.
      </Accordion>

      <Accordion title="Using CLI">
        Below is a practical, CLI‑only way to enforce “OCI IAM Users Should Have Only One Active API Key”:

        **Goal:** For each IAM user, keep **only one** active API key, and delete all others.

        ***

        ### 1. Prerequisites

        * OCI CLI installed and configured (`oci setup config`)
        * You’re using a tenancy admin or IAM user with permissions to manage users’ API keys:
          * `inspect users`, `read users`, `manage api-keys` on IAM users (or equivalent policy).

        ***

        ### 2. List Users You Want to Check

        Example: list **all** users in the tenancy:

        ```bash theme={null}
        oci iam user list \
          --all \
          --query "data[].{id:id, name:name}" \
          --output table
        ```

        If you want to restrict to a specific group:

        ```bash theme={null}
        GROUP_OCID="<group_ocid>"

        oci iam group list-users \
          --group-id "$GROUP_OCID" \
          --all \
          --query "data[].{id:id, name:name}" \
          --output table
        ```

        ***

        ### 3. For Each User, List Their API Keys

        For each user OCID (say `$USER_OCID`):

        ```bash theme={null}
        oci iam user api-key list \
          --user-id "$USER_OCID" \
          --all \
          --query "data[].{id:id, fingerprint:fingerprint, timeCreated:\"time-created\"}" \
          --output table
        ```

        This shows all active keys (OCI doesn’t support “disabled” API keys; they’re either present or deleted).

        ***

        ### 4. Decide Which API Key to Keep

        Typical rules:

        * Keep the **newest** key (largest `time-created`).
        * Or keep a specific fingerprint you know is used by automation.

        To find the newest key via CLI:

        ```bash theme={null}
        oci iam user api-key list \
          --user-id "$USER_OCID" \
          --all \
          --query "data | sort_by(@, &\"time-created\")[-1]" \
          --output json
        ```

        Extract the key ID you want to **keep**:

        ```bash theme={null}
        KEEP_KEY_ID=$(oci iam user api-key list \
          --user-id "$USER_OCID" \
          --all \
          --query "data | sort_by(@, &\"time-created\")[-1].id" \
          --raw-output)
        ```

        ***

        ### 5. Delete All Other API Keys (Leave Only One)

        List all key IDs for the user:

        ```bash theme={null}
        oci iam user api-key list \
          --user-id "$USER_OCID" \
          --all \
          --query "data[].id" \
          --raw-output
        ```

        Example Bash loop to delete all except `$KEEP_KEY_ID`:

        ```bash theme={null}
        USER_OCID="<user_ocid>"

        KEEP_KEY_ID=$(oci iam user api-key list \
          --user-id "$USER_OCID" \
          --all \
          --query "data | sort_by(@, &\"time-created\")[-1].id" \
          --raw-output)

        for KEY_ID in $(oci iam user api-key list \
                          --user-id "$USER_OCID" \
                          --all \
                          --query "data[].id" \
                          --raw-output); do
          if [ "$KEY_ID" != "$KEEP_KEY_ID" ]; then
            echo "Deleting extra API key $KEY_ID for user $USER_OCID"
            oci iam user api-key delete \
              --user-id "$USER_OCID" \
              --fingerprint "$KEY_ID" \
              --force
          fi
        done
        ```

        > Note: In the API & CLI, the **API key is deleted using its fingerprint**, but the CLI returns it as `id` which equals the fingerprint.

        ***

        ### 6. Automate for All Users (Tenancy‑Wide)

        Example Bash script (run from an admin machine):

        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        # List all users in the tenancy
        USER_LIST=$(oci iam user list --all --query "data[].id" --raw-output)

        for USER_OCID in $USER_LIST; do
          echo "Processing user: $USER_OCID"

          KEY_COUNT=$(oci iam user api-key list \
            --user-id "$USER_OCID" \
            --all \
            --query "length(data)" \
            --raw-output)

          # Skip users with 0 or 1 key
          if [ "$KEY_COUNT" -le 1 ]; then
            echo "  User has $KEY_COUNT API key(s); no action."
            continue
          fi

          # Determine the key to KEEP (newest)
          KEEP_KEY_ID=$(oci iam user api-key list \
            --user-id "$USER_OCID" \
            --all \
            --query "data | sort_by(@, &\"time-created\")[-1].id" \
            --raw-output)

          echo "  Keeping key: $KEEP_KEY_ID"

          # Delete all other keys
          for KEY_ID in $(oci iam user api-key list \
                            --user-id "$USER_OCID" \
                            --all \
                            --query "data[].id" \
                            --raw-output); do
            if [ "$KEY_ID" != "$KEEP_KEY_ID" ]; then
              echo "  Deleting extra key: $KEY_ID"
              oci iam user api-key delete \
                --user-id "$USER_OCID" \
                --fingerprint "$KEY_ID" \
                --force
            fi
          done

        done
        ```

        Run:

        ```bash theme={null}
        chmod +x enforce_single_api_key.sh
        ./enforce_single_api_key.sh
        ```

        ***

        ### 7. Integrate With IAM Monitoring / Cloud Guard

        To monitor this condition rather than only remediate:

        1. **Enable Cloud Guard** in the tenancy and choose the “IAM” target.
        2. Use or create a detector recipe that checks for:
           * “Users with more than one API key” (OCI typically provides this as a managed detector).
        3. Configure this script as:
           * A **remediation function** (OCI Functions) called by a Cloud Guard responder, or
           * A scheduled job (OCI DevOps/cron) that runs periodically using the CLI.

        ***

        If you tell me whether you prefer newest key, oldest key, or a specific naming/fingerprint rule, I can adapt the loop/query exactly to that policy.
      </Accordion>

      <Accordion title="Using Python">
        Below is a simple, practical way to enforce “OCI IAM Users Should Have Only One Active API Key” using Python and the OCI Python SDK. The idea:

        1. List all IAM users (or a subset you care about).
        2. For each user, list API keys.
        3. If a user has more than one active key, keep only one and delete the rest.

        You can then run this script periodically (e.g., via a scheduled job) as your “IAM monitoring/remediation.”

        ***

        ## 1. Prerequisites

        1. **Install OCI Python SDK**
           ```bash theme={null}
           pip install oci
           ```

        2. **Configure OCI credentials** (for the script to talk to OCI):\
           Create/verify `~/.oci/config` with at least:

           ```ini theme={null}
           [DEFAULT]
           user=ocid1.user.oc1..aaaa...
           fingerprint=aa:bb:cc:dd:...
           key_file=/path/to/your/private_key.pem
           tenancy=ocid1.tenancy.oc1..aaaa...
           region=eu-frankfurt-1
           ```

           Make sure this user has enough IAM permissions to:

           * List and manage users
           * List and manage API keys

        ***

        ## 2. High-Level Strategy

        * Get the **compartment/tenancy** where IAM users live (IAM is global, but you use tenancy OCID).
        * For each IAM user (or filtered ones):
          * Use `list_api_keys(user_id)` to get keys.
          * If `len(keys) > 1`, decide a rule:
            * Keep newest, delete older ones **OR**
            * Keep oldest, delete others.
          * Use `delete_api_key(user_id, fingerprint)` to remove extra keys.

        ***

        ## 3. Example Python Script (Remediate to One Active Key per User)

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

        # ---- CONFIG ----
        PROFILE = "DEFAULT"  # Profile name from ~/.oci/config
        TENANCY_OCID = "ocid1.tenancy.oc1..example"  # Your tenancy OCID
        DRY_RUN = True  # Set to False to actually delete extra keys

        # Policy for which key to keep: "newest" or "oldest"
        KEEP_POLICY = "newest"

        def get_iam_client(profile=PROFILE):
            config = oci.config.from_file(profile_name=profile)
            return oci.identity.IdentityClient(config), config["tenancy"]

        def list_all_users(identity_client, tenancy_ocid):
            users = []
            response = oci.pagination.list_call_get_all_results(
                identity_client.list_users,
                compartment_id=tenancy_ocid
            )
            for user in response.data:
                # Filter out deleted/federated users if needed
                if user.lifecycle_state == "ACTIVE":
                    users.append(user)
            return users

        def list_user_api_keys(identity_client, user_id):
            response = identity_client.list_api_keys(user_id)
            return response.data

        def choose_key_to_keep(keys, policy="newest"):
            # Keys have 'time_created' and 'fingerprint'
            if not keys:
                return None

            if policy == "newest":
                return max(keys, key=lambda k: k.time_created)
            elif policy == "oldest":
                return min(keys, key=lambda k: k.time_created)
            else:
                raise ValueError("Unsupported policy: {}".format(policy))

        def remediate_user_api_keys(identity_client, user):
            keys = list_user_api_keys(identity_client, user.id)
            if len(keys) <= 1:
                return  # compliant

            print(f"User {user.name} ({user.id}) has {len(keys)} API keys")

            keep_key = choose_key_to_keep(keys, KEEP_POLICY)
            print(f"  Keeping key with fingerprint: {keep_key.fingerprint}, created at {keep_key.time_created}")

            for key in keys:
                if key.fingerprint == keep_key.fingerprint:
                    continue  # skip the one we're keeping

                print(f"  Will delete extra key: {key.fingerprint}, created at {key.time_created}")
                if not DRY_RUN:
                    identity_client.delete_api_key(user_id=user.id, fingerprint=key.fingerprint)
                    print(f"  Deleted key: {key.fingerprint}")

        def main():
            identity_client, tenancy_ocid = get_iam_client()
            users = list_all_users(identity_client, tenancy_ocid)

            print(f"Found {len(users)} active users in tenancy {tenancy_ocid}")

            for user in users:
                # Optional: Filter users (e.g., by name prefix, tag, etc.)
                # if not user.name.startswith("prod-"):
                #     continue
                remediate_user_api_keys(identity_client, user)

            if DRY_RUN:
                print("\nDRY RUN mode: no keys were actually deleted. Set DRY_RUN = False to apply changes.")

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

        ***

        ## 4. How to Use for “Monitoring”

        1. **Monitoring-only:**
           * Keep `DRY_RUN = True`.
           * Run the script on a schedule (e.g., cron, OCI Functions + Events).
           * Collect output/logs to alert if any noncompliant users are found.

        2. **Monitoring with Auto-Remediation:**
           * Set `DRY_RUN = False`.
           * Run on a schedule to automatically delete extra keys and enforce 1 key per user.

        ***

        ## 5. Optional: Scope / Safeguards

        * Add filters so you only enforce this on:
          * Specific groups (e.g., only human users, not service users).
          * Users with certain tags.
        * Log every change (user ID, key fingerprint, timestamp) to a file or logging system.
        * Test first in a non-production tenancy with `DRY_RUN = True`, then live.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # IAM user used for OCI monitoring (example)
        resource "oci_identity_user" "oci_iam_monitoring_user" {
          compartment_id = var.TENANCY_OCID                          # Replace with your tenancy OCID
          name           = "OCI_IAM_MONITORING_USER"                 # Replace with your monitoring user name
          description    = "Service user for IAM monitoring"         # Optional
          email          = "IAM_MONITORING_USER_EMAIL@example.com"   # Replace with a valid email
        }

        # Single active API key for the monitoring user
        # Generate a keypair outside Terraform and paste the PUBLIC key here
        resource "oci_identity_api_key" "oci_iam_monitoring_api_key" {
          user_id   = oci_identity_user.oci_iam_monitoring_user.id
          key_value = file("PATH_TO_PUBLIC_KEY_PEM") # Replace with path to the public key PEM
        }
        ```

        This enforces exactly one active API signing key for the monitoring user in Terraform by having only a single `oci_identity_api_key` resource tied to that IAM user; remove any additional `oci_identity_api_key` resources for this user from your Terraform configuration (or import existing keys and then remove them) to have Terraform destroy them.

        Note: Destroying extra `oci_identity_api_key` resources will immediately revoke those keys and can break any clients still using them.

        To verify, `terraform plan` should show:

        * No changes to `oci_identity_user.oci_iam_monitoring_user`.
        * At most one `oci_identity_api_key` resource for this user.
        * Any surplus `oci_identity_api_key` resources for this user marked with `- destroy`.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
