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

# Google.iam.admin.v1.undeleteserviceaccount

### Event Information

* **Event meaning & impact**
  * `google.iam.admin.v1.UndeleteServiceAccount` is emitted when a previously deleted Service Account is restored within its 30-day soft-delete window.
  * The Service Account’s identity (email/unique ID) becomes active again, and it can regain access to resources via existing or reattached IAM bindings, keys, and workload identities.

* **Security & compliance considerations**
  * Undeleting can silently re-enable access paths that were intentionally removed, impacting least-privilege, SoD, and deprovisioning controls (e.g., ISO 27001 A.9, SOC 2 CC6.x, CIS GCP 1.x, PCI-DSS 7.x).
  * Treat this as a privileged action requiring justification and change records; ensure the actor (user/service) performing undelete has appropriate roles (e.g., `roles/iam.serviceAccountAdmin`) and is operating under an approved change.

* **Operational response & guardrails**
  * Correlate this event with prior `DeleteServiceAccount` events, IAM policy changes, and key usage to verify the restore is intentional and risk-acceptable.
  * Automate alerts on this event, trigger a review workflow (re-validate IAM roles, keys, and bindings), and, if the account was decommissioned for security reasons, immediately re-delete or restrict it via organization policy and IAM.

### Examples

* **Re-activating a previously compromised service account**
  * An attacker (or malicious insider) could undelete a service account that was disabled/deleted following a security incident and regain access tokens/keys or rebind new keys.
  * Violates least-privilege and incident-response expectations in ISO 27001, SOC 2, and NIST 800-53 (IR, AC families) if no approval / change management is enforced.

* **Restoring legacy high-privilege roles and bypassing access reviews**
  * A service account with broad roles (e.g., `roles/owner`, `roles/editor`, or org-level custom roles) might be undeleted after being removed during an access review, effectively rolling back a risk remediation.
  * Undermines access governance and periodic recertification controls required in SOX, PCI DSS, and NIST (AC-2, AC-6).

* **Circumventing identity lifecycle and separation-of-duties controls**
  * If identity lifecycle dictates that unused/breached service accounts be permanently removed, `UndeleteServiceAccount` can be used to bypass that process without ticketing or approval, especially if logging/alerts are weak.
  * Non-compliant with change-management and SoD controls (e.g., ISO 27001 A.9 & A.12, PCI DSS 7 & 10) if the same role both deletes and undeletes accounts without independent oversight.

### Remediation

#### Using Console

* **Prevent reactivation of compromised / retired service accounts**
  * In GCP Console, permanently delete high‑risk service accounts so they cannot be undeleted:
    1. Go to **IAM & Admin → Service Accounts** → select the project.
    2. Locate the service account (if disabled, it still appears).
    3. Click the service account → **Delete** → confirm.
    4. After 30 days soft‑delete window, it is permanently removed and cannot be undeleted (aligns with ISO 27001, SOC 2, NIST IR/AC).
  * For accounts that must remain:
    1. Remove all keys: **Service account** → **Keys** → delete all keys.
    2. Remove all roles: **IAM & Admin → IAM** → find the service account principal → click **Edit principal** → remove high‑privilege roles (`Owner`, `Editor`, custom org roles) → **Save**.
    3. Disable the account: **Service account** → three‑dot menu → **Disable**; use this only with strong monitoring and approval workflow.

* **Control `UndeleteServiceAccount` via IAM and approvals**
  * Restrict who can undelete accounts:
    1. Go to **IAM & Admin → Roles** → open custom admin roles and ensure they **do not include** `iam.serviceAccounts.undelete`.
    2. In **IAM & Admin → IAM**, remove `Owner`/`Editor` from users/groups; instead, assign least‑privilege custom roles without undelete permission.
    3. Reserve undelete capability for a tightly controlled group (e.g., Security Admins) with a documented change‑management / ticketing process (supports NIST AC‑2/AC‑6, PCI DSS 7).
  * Enforce separation of duties:
    1. One group manages service account lifecycle (create/delete); another group (security / compliance) alone has `undelete` (or vice versa).
    2. Use **Cloud Identity Groups** and grant roles to groups only, not individuals, so SoD is enforced centrally.

* **Monitor, alert, and validate against compliance (GCP Console + Cloud Logging)**
  * Enable audit logs for IAM in all projects and org:
    1. Go to **IAM & Admin → Audit Logs** → select **IAM Service**.
    2. Turn on **Admin Read** and **Admin Write** for **All principals** for each project and at the **Organization** level.
  * Create alerts for `UndeleteServiceAccount` and high‑risk changes:
    1. Go to **Logging → Logs Explorer**.
    2. Use a filter, e.g.:
       ```
       protoPayload.methodName="google.iam.admin.v1.IAM.UndeleteServiceAccount"  
       OR protoPayload.methodName="google.iam.admin.v1.IAM.DeleteServiceAccount"  
       ```
    3. Click **Create alert** → configure **Notification channel** (email / Pub/Sub → SIEM) and require incident tickets for each alert (supports ISO 27001 A.9/A.12, PCI DSS 10).
  * Periodically review:
    1. In **IAM & Admin → IAM**, run quarterly reviews to identify and remove legacy high‑privilege roles and org‑level custom roles from service accounts.
    2. Export IAM policies and audit logs to BigQuery / SIEM and reconcile with access reviews to ensure that deleted accounts or roles have not been reintroduced without approval.

#### Using CLI

* **Prevent undelete by design (preferred)**
  * Replace deletion with a “tombstone” pattern: remove all bindings, keys, and workload identity bindings, but do not rely on `delete` as the security measure:
    * Strip roles from the service account:
      ```bash theme={null}
      gcloud projects get-iam-policy PROJECT_ID \
        --format=json > /tmp/policy.json

      # Edit /tmp/policy.json to remove bindings referencing serviceAccount:SA_NAME@PROJECT_ID.iam.gserviceaccount.com

      gcloud projects set-iam-policy PROJECT_ID /tmp/policy.json
      ```
    * Disable the service account (blocks token use and avoids the need for undelete):
      ```bash theme={null}
      gcloud iam service-accounts disable SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
        --project=PROJECT_ID
      ```
    * Delete all keys (user‑managed) to meet key‑revocation / incident-response requirements:
      ```bash theme={null}
      gcloud iam service-accounts keys list \
        --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
        --project=PROJECT_ID \
        --format="value(name)" | xargs -I{} \
        gcloud iam service-accounts keys delete {} \
          --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
          --project=PROJECT_ID --quiet
      ```

* **Constrain who can undelete / restore high-privilege service accounts**
  * Remove `roles/iam.serviceAccountAdmin`, `roles/owner`, and custom roles containing `iam.serviceAccounts.undelete` from operational / dev teams; assign only to a tightly controlled break-glass or security-admin group:
    ```bash theme={null}
    gcloud projects remove-iam-policy-binding PROJECT_ID \
      --member="group:ops-team@ORG_DOMAIN" \
      --role="roles/iam.serviceAccountAdmin"

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="group:security-admins@ORG_DOMAIN" \
      --role="roles/iam.securityAdmin"
    ```
  * Create a custom role that explicitly excludes undelete for normal admins:
    ```bash theme={null}
    gcloud iam roles create saLimitedAdmin \
      --project=PROJECT_ID \
      --title="SA Limited Admin" \
      --permissions="iam.serviceAccounts.get,iam.serviceAccounts.list,iam.serviceAccounts.update,iam.serviceAccounts.create,iam.serviceAccounts.disable,iam.serviceAccounts.enable" \
      --stage="GA"
    ```
    Then bind this role instead of `roles/iam.serviceAccountAdmin`:
    ```bash theme={null}
    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="group:iam-admins@ORG_DOMAIN" \
      --role="projects/PROJECT_ID/roles/saLimitedAdmin"
    ```

* **Enforce approval, monitoring, and lifecycle compliance**
  * Require that “restore” operations happen only via a controlled CI/CD or ITSM pipeline (change ticket ID required) that calls `gcloud` with restricted service accounts:
    ```bash theme={null}
    gcloud iam service-accounts undelete SA_UNIQUE_ID \
      --project=PROJECT_ID
    ```
    Ensure the pipeline’s service account has `iam.serviceAccounts.undelete` but no direct console access.
  * Enable and monitor Cloud Audit Logs for `UndeleteServiceAccount`, `SetIamPolicy`, and `CreateServiceAccountKey`, and pipe to SCC / SIEM with real-time alerts to satisfy ISO 27001 / PCI DSS logging requirements:
    ```bash theme={null}
    gcloud logging sinks create sa-iam-audit-sink \
      storage.googleapis.com://BUCKET_NAME \
      --log-filter='protoPayload.methodName=("google.iam.admin.v1.IAM.UndeleteServiceAccount" OR "google.iam.admin.v1.IAM.SetIamPolicy" OR "google.iam.admin.v1.IAM.CreateServiceAccountKey")' \
      --project=PROJECT_ID
    ```
  * Codify “no legacy high-privilege roles” as policy using Org Policy + Policy Controller (if using GKE/Anthos) to block restoring `roles/owner`, `roles/editor`, or disallowed custom roles, and periodically reconcile via script:
    ```bash theme={null}
    gcloud projects get-iam-policy PROJECT_ID \
      --format="json(bindings)" | jq '.bindings[] | select(.role=="roles/owner" or .role=="roles/editor")'
    ```

#### Using Python

* **Enforce strong IAM guardrails on `iam.serviceAccounts.undelete`**
  * Create an org‑level custom role *without* `iam.serviceAccounts.undelete` and migrate all human/admin identities to it; only a tightly controlled break-glass group should retain this permission.
  * Attach an org policy to restrict who can manage service accounts and keys, e.g.:
    ```bash theme={null}
    gcloud org-policies set-policy org-policy.yaml
    ```
  * Example Python snippet to enumerate who has `iam.serviceAccounts.undelete` (via Cloud Asset Inventory) for review/cleanup:
    ```python theme={null}
    from google.cloud import asset_v1

    client = asset_v1.AssetServiceClient()
    scope = "organizations/1234567890"
    asset_types = ["cloudresourcemanager.googleapis.com/Project"]
    resp = client.analyze_iam_policy(
        request={
            "analysis_query": {
                "scope": scope,
                "options": {"expand_groups": True, "expand_roles": True},
                "access_selector": {
                    "permissions": ["iam.serviceAccounts.undelete"]
                },
            }
        }
    )
    for binding in resp.main_analysis.analysis_results:
        print(binding.iam_binding)
    ```

* **Automate detection & response for `UndeleteServiceAccount` to enforce approvals**
  * Enable Admin Activity Logs in Cloud Logging and create a log‑based alert on `google.iam.admin.v1.UndeleteServiceAccount` to meet ISO/SOC2/NIST monitoring expectations.
  * Use a Cloud Function / Cloud Run (Python) triggered from Logging to auto-revoke any undeleted SA (remove keys, high-priv roles) unless a change ticket/approval exists.
  * Example Python handler for a log-based trigger (pseudo-response workflow):
    ```python theme={null}
    import base64, json
    from googleapiclient.discovery import build
    from google.oauth2 import service_account

    SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
    CREDS = service_account.Credentials.from_service_account_file(
        "sa.json", scopes=SCOPES
    )

    def _iamadmin():
        return build("iam", "v1", credentials=CREDS, cache_discovery=False)

    def _cloudresourcemanager():
        return build("cloudresourcemanager", "v1", credentials=CREDS, cache_discovery=False)

    def remediate_undelete(event, context):
        data = json.loads(base64.b64decode(event["data"]).decode("utf-8"))
        proto = data["protoPayload"]
        method = proto.get("methodName", "")
        if method != "google.iam.admin.v1.IAM.UndeleteServiceAccount":
            return

        # Extract service account name and who did it
        target_sa = proto["response"]["name"]          # e.g. projects/1234567890/serviceAccounts/sa@p.iam.gserviceaccount.com
        actor       = proto["authenticationInfo"].get("principalEmail")

        iam = _iamadmin()

        # 1. Immediately disable the SA again (if policy requires ticket/approval)
        iam.projects().serviceAccounts().disable(
            name=target_sa,
            body={}
        ).execute()

        # 2. Delete any user-managed keys to prevent token/credential reuse
        keys = iam.projects().serviceAccounts().keys().list(
            name=target_sa,
            keyTypes="USER_MANAGED"
        ).execute().get("keys", [])
        for k in keys:
            iam.projects().serviceAccounts().keys().delete(name=k["name"]).execute()

        # 3. Optionally remove high-privilege roles bound to this SA
        #    (simple example across a single project – extend to folders/org as needed)
        project_id = target_sa.split("/")[1]
        crm = _cloudresourcemanager()
        policy = crm.projects().getIamPolicy(
            resource=project_id, body={}
        ).execute()

        member = f"serviceAccount:{target_sa.split('/')[-1]}"
        high_risks = {
            "roles/owner",
            "roles/editor",
            # add legacy custom roles here: "organizations/ORG_ID/roles/LegacyAdmin"
        }

        changed = False
        for b in policy.get("bindings", []):
            if b["role"] in high_risks and member in b.get("members", []):
                b["members"].remove(member)
                changed = True

        if changed:
            crm.projects().setIamPolicy(
                resource=project_id, body={"policy": policy}
            ).execute()
    ```

* **Codify lifecycle, SoD, and “no‑undelete” rules for compromised / legacy SAs**
  * Maintain a CMDB / security registry (e.g., in Firestore or a Git repo) of service accounts marked as `COMPROMISED`/`DECOMMISSIONED`; your remediation function should auto-disable and alert if any such SA is undeleted, enforcing IR and change‑management controls.
  * Ensure SoD by having: one role allowed to *delete* SAs, another (security/change manager) allowed to *approve/temporarily undelete*; enforce via separate groups and documented workflows (ISO 27001 A.9/A.12, PCI DSS 7 & 10).
  * Example Python snippet to enforce “never re‑enable compromised SAs” using a local deny‑list (replace with Firestore / DB in production):
    ```python theme={null}
    COMPROMISED_SAS = {
        "sa-compromised@project-id.iam.gserviceaccount.com",
        # ...
    }

    def is_compromised(sa_email: str) -> bool:
        return sa_email in COMPROMISED_SAS

    def remediate_undelete(event, context):
        import base64, json
        from googleapiclient.discovery import build
        from google.oauth2 import service_account

        data = json.loads(base64.b64decode(event["data"]).decode("utf-8"))
        proto = data["protoPayload"]
        if proto.get("methodName") != "google.iam.admin.v1.IAM.UndeleteServiceAccount":
            return

        sa_name = proto["response"]["name"]
        sa_email = sa_name.split("/")[-1]

        if not is_compromised(sa_email):
            # Optionally allow but still strip high-priv roles as in previous example
            return

        creds = service_account.Credentials.from_service_account_file(
            "sa.json",
            scopes=["https://www.googleapis.com/auth/cloud-platform"],
        )
        iam = build("iam", "v1", credentials=creds, cache_discovery=False)

        # Re-disable immediately and notify SOC
        iam.projects().serviceAccounts().disable(name=sa_name, body={}).execute()
        # TODO: send notification to SIEM / email / ticketing system
    ```
