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

# Gcp managed service account keys remediation

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are concise, console-based steps to remediate the finding “Service Account Keys Should Be Managed By Google” for GCP IAM.

        ***

        ## 1. Identify service accounts with user‑managed keys

        1. In the Google Cloud Console, go to:\
           **IAM & Admin → Service Accounts**\
           (URL: `https://console.cloud.google.com/iam-admin/serviceaccounts`)
        2. In each project, look at the **KEYS** column:
           * If it shows **User-managed keys**, that account is in violation.

        Repeat for all relevant projects (or use the project selector at the top).

        ***

        ## 2. Plan migration to Google‑managed credentials (no JSON keys)

        Before deleting keys, make sure each workload can use keyless / Google‑managed authentication instead:

        ### a) Google Cloud resources (GCE, GKE, Cloud Run, Cloud Functions, etc.)

        1. For each workload, decide which **service account** it should run as.
        2. Assign that service account to the resource:
           * **Compute Engine VM**:
             * Go to **Compute Engine → VM instances**.
             * Click the VM → **Edit**.
             * Under **Service account**, select the target service account.
             * Save.
           * **GKE (Workloads)**:
             * Use **Workload Identity** (recommended):
               * Enable Workload Identity on the cluster (if not already).
               * Create a Kubernetes service account and bind it to the GCP service account.
               * (Console part is mostly under **Kubernetes Engine → Clusters → Security**; detailed configuration usually uses `gcloud`/kubectl.)
           * **Cloud Run / Cloud Functions / Cloud Scheduler, etc.**:
             * In each service’s **Edit** page, set the **Runtime service account** (or equivalent) to the correct service account.

        When a Google Cloud resource runs as a service account, Google automatically provides and rotates a **Google‑managed key** (not visible or downloadable).

        ***

        ### b) External workloads (on‑prem, other clouds, laptops/CI)

        For anything that used a JSON key file:

        1. Go to **IAM & Admin → Workload Identity Federation**.
           * URL: `https://console.cloud.google.com/iam-admin/workload-identity-pools`
        2. Create or use an existing **Workload Identity Pool** and **Provider** for your external environment (OIDC, AWS, Azure AD, etc.).
        3. On the **Service account** page:
           * Go to **IAM & Admin → Service Accounts**.
           * Click the target service account → **Permissions** (or **Show Info Panel**).
           * **Grant access to the workload identity pool** (role `roles/iam.workloadIdentityUser` to the pool or provider).
        4. Update the external workload to use Workload Identity Federation instead of a JSON key (you’ll point it to the OIDC / AWS / other identity and the pool/provider; the app then exchanges external identity tokens for short‑lived Google credentials).

        ***

        ## 3. Remove user‑managed keys

        Once workloads are confirmed working with Google‑managed or federated credentials:

        1. Go to **IAM & Admin → Service Accounts**.
        2. Click the affected service account.
        3. Go to the **KEYS** tab.
        4. Under **User-managed keys**:
           * For each key ID:
             * Click **Delete** (trash icon).
             * Confirm deletion.

        Do this for all service accounts reported with user‑managed keys.

        ***

        ## 4. Prevent new user‑managed keys (optional but recommended)

        You can use an Org Policy to block new user‑managed keys:

        1. Go to: **IAM & Admin → Organization Policies**.\
           (URL: `https://console.cloud.google.com/iam-admin/orgpolicies`)
        2. Find policy:\
           **Constraints on service account key creation**: `constraints/iam.disableServiceAccountKeyCreation`
        3. Click it → **Edit**:
           * Set to **Enforced** to disable creation of new user‑managed keys.
        4. Save.

        ***

        ## 5. Verify remediation

        1. Re‑run your security scanner / SCC / policy check.
        2. In **IAM & Admin → Service Accounts**, confirm:
           * **KEYS** column shows only **Google-managed keys** (or none).
           * No service accounts have remaining user‑managed keys.

        This satisfies the requirement that “Service Account Keys Should Be Managed By Google.”
      </Accordion>

      <Accordion title="Using CLI">
        Below are concise, CLI‑based steps to (1) prevent new user‑managed keys and (2) remove existing ones.

        ***

        ## 1. Prevent creation of new user‑managed keys

        You do this via Organization/Folder/Project policies:

        ### 1.1. Disable *creation* of new keys

        ```bash theme={null}
        # Set at org level (replace ORG_ID)
        gcloud org-policies set-policy <<EOF
        name: organizations/ORG_ID/policies/constraints/iam.disableServiceAccountKeyCreation
        spec:
          rules:
            - enforce: true
        EOF
        ```

        To do it at the project level instead (replace PROJECT\_ID):

        ```bash theme={null}
        gcloud org-policies set-policy <<EOF
        name: projects/PROJECT_ID/policies/constraints/iam.disableServiceAccountKeyCreation
        spec:
          rules:
            - enforce: true
        EOF
        ```

        ### 1.2. Disable *upload* of external keys (optional but recommended)

        ```bash theme={null}
        # Org level
        gcloud org-policies set-policy <<EOF
        name: organizations/ORG_ID/policies/constraints/iam.disableServiceAccountExternalKeyUpload
        spec:
          rules:
            - enforce: true
        EOF
        ```

        Or at project level:

        ```bash theme={null}
        gcloud org-policies set-policy <<EOF
        name: projects/PROJECT_ID/policies/constraints/iam.disableServiceAccountExternalKeyUpload
        spec:
          rules:
            - enforce: true
        EOF
        ```

        These enforce using Google‑managed keys (no user‑managed key creation or upload).

        ***

        ## 2. Identify existing user‑managed service account keys

        List all service accounts for a project:

        ```bash theme={null}
        PROJECT_ID=your-project-id

        gcloud iam service-accounts list \
          --project="${PROJECT_ID}" \
          --format="value(email)"
        ```

        For each service account, list its keys:

        ```bash theme={null}
        SA_EMAIL=service-account@${PROJECT_ID}.iam.gserviceaccount.com

        gcloud iam service-accounts keys list \
          --iam-account="${SA_EMAIL}" \
          --format="table(name, keyType, validAfterTime, validBeforeTime)"
        ```

        Look for keys with `keyType` = `USER_MANAGED`.

        ***

        ## 3. Remove existing user‑managed keys

        Delete each user‑managed key by its full key ID (the `name` field):

        ```bash theme={null}
        KEY_NAME=projects/${PROJECT_ID}/serviceAccounts/${SA_EMAIL}/keys/KEY_ID

        gcloud iam service-accounts keys delete "${KEY_NAME##*/}" \
          --iam-account="${SA_EMAIL}" \
          --project="${PROJECT_ID}" \
          --quiet
        ```

        You can script this for all user‑managed keys in a project:

        ```bash theme={null}
        PROJECT_ID=your-project-id

        for SA in $(gcloud iam service-accounts list --project="${PROJECT_ID}" --format="value(email)"); do
          for KEY in $(gcloud iam service-accounts keys list \
              --iam-account="${SA}" \
              --project="${PROJECT_ID}" \
              --format="value(name)" \
              --filter="keyType=USER_MANAGED"); do
            echo "Deleting key ${KEY} for ${SA}"
            gcloud iam service-accounts keys delete "${KEY##*/}" \
              --iam-account="${SA}" \
              --project="${PROJECT_ID}" \
              --quiet
          done
        done
        ```

        ***

        ## 4. Ensure workloads use Google‑managed credentials

        For each workload:

        * GCE: Use service account attached to VM (no key file).
        * GKE: Use Workload Identity (no key file).
        * Cloud Run / Cloud Functions / App Engine: Use default or custom service account directly (no key file).

        Once workloads no longer rely on user‑managed keys, and the org policies above are enforced, service account keys will be fully Google‑managed.
      </Accordion>

      <Accordion title="Using Python">
        To have **only Google‑managed keys** for your service accounts, you must:

        1. **Find all user‑managed keys**.
        2. **Delete them** (after migrating workloads off JSON keys).
        3. Optionally **enforce org policies** to prevent new ones.

        Below is how to do 1–2 with Python for GCP IAM.

        ***

        ### 1. Prerequisites

        ```bash theme={null}
        pip install google-api-python-client google-auth
        ```

        Make sure you authenticate with an identity that has:

        * `roles/iam.serviceAccountAdmin` **or**
        * `roles/iam.serviceAccountKeyAdmin`

        For local dev:

        ```bash theme={null}
        gcloud auth application-default login
        ```

        ***

        ### 2. Python: List and delete user‑managed keys for a service account

        `keyType` meanings in IAM:

        * `USER_MANAGED` → JSON keys you create (should be removed).
        * `SYSTEM_MANAGED` → Google‑managed keys (do not delete these; they’re for things like Workload Identity, GCE, GKE, Cloud Run, etc.).

        ```python theme={null}
        from googleapiclient import discovery
        from google.auth import default

        def delete_user_managed_keys(project_id: str, service_account_email: str, dry_run: bool = True):
            """
            Deletes all USER_MANAGED keys for a specific service account.
            Set dry_run=False to actually delete.
            """
            credentials, _ = default()
            service = discovery.build("iam", "v1", credentials=credentials)

            sa_name = f"projects/{project_id}/serviceAccounts/{service_account_email}"

            # List keys
            request = service.projects().serviceAccounts().keys().list(
                name=sa_name,
                keyTypes=["USER_MANAGED"]  # Only user-managed keys
            )
            response = request.execute()

            keys = response.get("keys", [])
            if not keys:
                print(f"No USER_MANAGED keys found for {service_account_email}")
                return

            print(f"Found {len(keys)} USER_MANAGED key(s) for {service_account_email}:")
            for key in keys:
                key_name = key["name"]
                print(f" - {key_name}")

            if dry_run:
                print("Dry run only: no keys deleted. Set dry_run=False to delete.")
                return

            # Delete keys
            for key in keys:
                key_name = key["name"]
                print(f"Deleting key: {key_name}")
                del_request = service.projects().serviceAccounts().keys().delete(name=key_name)
                del_request.execute()

            print("Deletion complete.")

        if __name__ == "__main__":
            # Example usage
            PROJECT_ID = "my-project-id"
            SERVICE_ACCOUNT_EMAIL = "my-sa@my-project-id.iam.gserviceaccount.com"

            # First run as dry run to verify:
            delete_user_managed_keys(PROJECT_ID, SERVICE_ACCOUNT_EMAIL, dry_run=True)

            # When ready to actually delete:
            # delete_user_managed_keys(PROJECT_ID, SERVICE_ACCOUNT_EMAIL, dry_run=False)
        ```

        ***

        ### 3. Python: Iterate over all service accounts in a project

        If you want to clean up all service accounts in a project:

        ```python theme={null}
        from googleapiclient import discovery
        from google.auth import default

        def delete_all_user_managed_keys_in_project(project_id: str, dry_run: bool = True):
            credentials, _ = default()
            service = discovery.build("iam", "v1", credentials=credentials)

            parent = f"projects/{project_id}"

            # List all service accounts
            request = service.projects().serviceAccounts().list(name=parent)
            response = request.execute()

            service_accounts = response.get("accounts", [])
            if not service_accounts:
                print(f"No service accounts found in project {project_id}")
                return

            print(f"Found {len(service_accounts)} service account(s) in {project_id}")

            for sa in service_accounts:
                email = sa["email"]
                print(f"\nChecking service account: {email}")

                sa_name = sa["name"]
                key_request = service.projects().serviceAccounts().keys().list(
                    name=sa_name,
                    keyTypes=["USER_MANAGED"]
                )
                key_response = key_request.execute()
                keys = key_response.get("keys", [])

                if not keys:
                    print("  No USER_MANAGED keys.")
                    continue

                print(f"  Found {len(keys)} USER_MANAGED key(s):")
                for key in keys:
                    key_name = key["name"]
                    print(f"   - {key_name}")

                if dry_run:
                    print("  Dry run only: no keys deleted. Set dry_run=False to delete.")
                    continue

                for key in keys:
                    key_name = key["name"]
                    print(f"  Deleting key: {key_name}")
                    del_request = service.projects().serviceAccounts().keys().delete(name=key_name)
                    del_request.execute()

            print("\nCleanup complete.")

        if __name__ == "__main__":
            PROJECT_ID = "my-project-id"

            # First dry run:
            delete_all_user_managed_keys_in_project(PROJECT_ID, dry_run=True)

            # Then actually delete:
            # delete_all_user_managed_keys_in_project(PROJECT_ID, dry_run=False)
        ```

        ***

        ### 4. Prevent new user‑managed keys (org policy – not Python, but recommended)

        At org/folder/project level, set these org policies to **True** to block new user‑managed keys:

        * `constraints/iam.disableServiceAccountKeyCreation`
        * `constraints/iam.disableServiceAccountKeyUpload`

        Example (project scope):

        ```bash theme={null}
        gcloud org-policies set-policy policy.yaml
        ```

        `policy.yaml` example:

        ```yaml theme={null}
        name: projects/PROJECT_NUMBER/policies/constraints/iam.disableServiceAccountKeyCreation
        spec:
          rules:
          - enforce: true
        ---
        name: projects/PROJECT_NUMBER/policies/constraints/iam.disableServiceAccountKeyUpload
        spec:
          rules:
          - enforce: true
        ```

        ***

        Use the Python scripts to **remove existing USER\_MANAGED keys**, and use the org policies to ensure **only Google‑managed keys** are used going forward.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "google_service_account" "app_sa" {
          account_id   = "APP_SERVICE_ACCOUNT_ID"   # e.g. "my-app-sa"
          display_name = "APP_SERVICE_ACCOUNT_NAME" # e.g. "My App Service Account"
          project      = "PROJECT_ID"
        }
        ```

        To have keys managed by Google (and avoid user‑managed keys), do **not** define any `google_service_account_key` resources for this account (or remove existing `google_service_account_key` resources from Terraform so they are destroyed; this will immediately invalidate those keys and break anything still using them).

        `terraform plan` should show only `google_service_account` resources (no `google_service_account_key` creates), and for any removed keys it should show `-/+` or `-` (destroy) actions for the `google_service_account_key` resources.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
