Skip to main content

google.iam.admin.v1.UpdateServiceAccount

Event Information

  • Event purpose & scope

    • google.iam.admin.v1.UpdateServiceAccount is fired when a service account’s metadata is modified via the IAM Admin API (e.g., display name, description, disabled flag), not when its IAM policy (roles/bindings) is changed.
    • Typical sources: gcloud iam service-accounts update, Console edits, or API clients calling projects.serviceAccounts.patch.
  • Security & compliance relevance

    • Changes can affect how a service account is identified, whether it is enabled/disabled, and sometimes can indicate repurposing of an identity—important for SOC 2, ISO 27001, and internal IAM governance.
    • Track for anomalous edits (e.g., unexpected re-enabling of previously disabled accounts) and ensure only authorized roles (e.g., roles/iam.serviceAccountAdmin) are performing these actions.
  • Practical monitoring actions

    • In Cloud Logging, filter on protoPayload.@type="type.googleapis.com/google.iam.admin.v1.UpdateServiceAccount" and alert when:
      • authenticationInfo.principalEmail is outside approved admin groups, or
      • requestMetadata.callerSuppliedUserAgent or source IP is unusual, or
      • serviceAccount.disabled flips from true to false.
    • Feed these logs into SIEM / SCC, and use them as part of periodic IAM reviews and change-control evidence.

Examples

  • Privilege escalation via role change

    • An attacker updates a service account used by an internal app to add high-privilege roles (e.g., roles/owner, roles/iam.serviceAccountTokenCreator) on the project.
    • Impact: Full project takeover (read/write/delete of data, KMS use, billing changes). Violates least-privilege principles in ISO 27001, SOC 2, and CIS GCP.
  • Persistence by key/impersonation enablement

    • A malicious admin modifies a low-profile service account to allow key creation or to grant roles/iam.serviceAccountUser to another principal they control.
    • Impact: Long-lived credentials or impersonation path for persistent access, bypassing identity lifecycle controls (NIST 800-53 AC-2, AC-6).
  • Lateral movement via resource access expansion

    • The service account for a CI/CD pipeline is updated to include access to additional projects or sensitive APIs (e.g., BigQuery datasets with PII, Cloud Storage backups).
    • Impact: Cross-project lateral movement and data exfiltration, undermining data access controls required by GDPR, HIPAA, and PCI-DSS (principle of data minimization and access segregation).

Remediation

Using Console

  • Immediate containment & investigation (Console + IAM Recommender)

    • In IAM & Admin → IAM, sort by Type = Service account and locate the impacted accounts (by name/email). For each:
      • Click the pencil icon, remove high-privilege roles (roles/owner, roles/iam.admin, roles/iam.serviceAccountTokenCreator, broad *Admin roles) and any roles not required by the app; click Save.
      • In IAM & Admin → Role recommendations, review suggestions and apply least-privilege roles where possible; document all changes for ISO 27001/SOC 2 change records.
    • In IAM & Admin → Audit Logs, filter by Resource type = Service Account and Method name contains setIamPolicy or roles.setIamPolicy to identify who changed what and when; preserve logs for incident response and compliance (NIST 800‑53 AU-6, IR controls).
  • Lock down service account capabilities (keys, impersonation, lateral movement)

    • In IAM & Admin → Service Accounts:
      • Click each sensitive/low-profile service account → Keys tab → delete all unnecessary keys; disable “Enable key creation” for admins by removing roles/iam.serviceAccountKeyAdmin or similar from their identities in IAM.
      • On the Permissions tab, remove roles/iam.serviceAccountUser and roles/iam.serviceAccountTokenCreator grants to any untrusted or unnecessary principals to break impersonation paths; keep only specific workloads/principals that must impersonate (aligns with NIST AC‑2/AC‑6).
    • For CI/CD and cross-project access:
      • In each project’s IAM page, restrict the CI/CD service account to tightly scoped roles (e.g., dataset‑specific BigQuery roles, bucket‑level Storage roles) instead of project‑wide or multi-project high-privilege roles.
      • For BigQuery and Cloud Storage, use resource-level permissions: go to BigQuery → Dataset → Share or Cloud Storage → Bucket → Permissions, remove broad grants and add minimal needed roles (satisfies GDPR/HIPAA/PCI-DSS data minimization and segregation).
  • Prevent recurrence (guardrails & monitoring)

    • Implement predefined custom roles and groups: in IAM & Admin → Roles, create least-privilege custom roles for admins and CI/CD; then in IAM, assign these instead of primitive roles (Owner, Editor). Restrict who can assign high-privilege roles by limiting roles/iam.admin and roles/resourcemanager.projectIamAdmin to a small, controlled group.
    • In Security → Security Command Center (if enabled) and IAM → Audit Logs, set up alerts (via Cloud Logging → Logs Router → Sinks and Alerting) for:
      • Any assignment of roles/owner, roles/iam.serviceAccountUser, roles/iam.serviceAccountTokenCreator, roles/iam.serviceAccountKeyAdmin to service accounts or users.
      • New key creation events for service accounts and IAM policy changes to CI/CD or low-profile accounts.
    • Regularly review IAM → Policy Analyzer and IAM → Service Accounts for over-privileged accounts; formalize quarterly access reviews and approvals to support ISO 27001, SOC 2, CIS GCP, and NIST 800‑53 access review requirements.

Using CLI

  • Immediate containment & rollback (privilege escalation / persistence / lateral movement)

    • Identify the compromised service account and list its current bindings and keys:
      • gcloud iam service-accounts get-iam-policy SA_NAME@PROJECT_ID.iam.gserviceaccount.com --project=PROJECT_ID
      • gcloud iam service-accounts keys list --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com --project=PROJECT_ID
    • Remove high-privilege or persistence-enabling roles from the service account and project-level IAM (ISO 27001 A.9, CIS GCP IAM):
      • From project IAM:
        • gcloud projects get-iam-policy PROJECT_ID > policy.yaml
        • Edit policy.yaml to remove bindings like roles/owner, roles/editor, roles/iam.serviceAccountTokenCreator, roles/iam.serviceAccountUser, or cross-project roles for the SA, then:
        • gcloud projects set-iam-policy PROJECT_ID policy.yaml
      • From SA IAM:
        • gcloud iam service-accounts get-iam-policy SA_NAME@PROJECT_ID.iam.gserviceaccount.com --project=PROJECT_ID > sa-policy.yaml
        • Edit to remove roles/iam.serviceAccountUser, roles/iam.serviceAccountKeyAdmin, or any unwanted member bindings:
        • gcloud iam service-accounts set-iam-policy SA_NAME@PROJECT_ID.iam.gserviceaccount.com sa-policy.yaml --project=PROJECT_ID
    • Disable or delete compromised keys and regenerate only if absolutely required (NIST 800-53 AC-2, AC-6; PCI-DSS key management):
      • gcloud iam service-accounts keys delete KEY_ID --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com --project=PROJECT_ID
      • For CI/CD / lateral movement risk, also revoke unnecessary cross-project roles: use gcloud projects get-iam-policy on all linked projects and remove the SA from sensitive roles (e.g., BigQuery, Storage, KMS).
  • Hardening IAM to prevent recurrence (least privilege, data minimization)

    • Enforce least privilege per use-case and create dedicated service accounts with scoped roles instead of roles/owner/roles/editor or broad *Admin on the project (SOC 2 CC6, ISO 27001 A.9, GDPR data minimization):
      • Example tightening for CI/CD SA:
        • gcloud projects add-iam-policy-binding PROJECT_ID --member="serviceAccount:CI_SA@PROJECT_ID.iam.gserviceaccount.com" --role="roles/storage.objectAdmin"
        • Avoid adding global roles like roles/bigquery.admin, prefer dataset-level ACLs and per-project roles.
    • Restrict who can modify service accounts and grant impersonation/keys:
      • Limit roles/iam.serviceAccountAdmin, roles/iam.serviceAccountUser, roles/iam.serviceAccountTokenCreator, and roles/iam.serviceAccountKeyAdmin to a small break-glass admin group.
      • Example: remove broad group access:
        • gcloud projects get-iam-policy PROJECT_ID > policy.yaml
        • Remove group bindings for these roles, then: gcloud projects set-iam-policy PROJECT_ID policy.yaml
    • Use Org Policies and constraints to block risky patterns (supports CIS GCP, HIPAA/PCI segregation):
      • Disable SA key creation where possible:
        • gcloud resource-manager org-policies allow constraints/iam.disableServiceAccountKeyCreation TRUE --project=PROJECT_ID
      • Restrict who can impersonate all service accounts:
        • gcloud resource-manager org-policies allow constraints/iam.allowedPolicyMemberDomains ... (restrict to corporate domains)
        • Prefer using narrower custom roles instead of wide roles/iam.serviceAccountUser on projects/PROJECT_ID/serviceAccounts/*.
  • Detection, investigation & compliance alignment

    • Enable and monitor Cloud Audit Logs for IAM changes, SA key operations, and project-level policy changes; create log-based alerts for high-risk roles or impersonation grants (NIST 800-53 AU-2/AU-6, SOC 2 monitoring):
      • Example high-risk filter:
        • resource.type="project" AND protoPayload.methodName=("SetIamPolicy" OR "SetServiceAccountPolicy" OR "CreateServiceAccountKey") AND ("roles/owner" OR "roles/iam.serviceAccountUser" OR "roles/iam.serviceAccountTokenCreator" OR "roles/iam.serviceAccountKeyAdmin")
    • For suspected compromise, revoke active tokens and sessions and rotate upstream app credentials (e.g., CI/CD systems, internal apps) that use the impacted service accounts; re-run access reviews for BigQuery datasets with PII, backup buckets, and KMS keys (GDPR, HIPAA, PCI-DSS segregation).
    • Document the change and remediation steps, link to internal IAM standards (least privilege, no keys unless justified, impersonation controls), and feed lessons learned into access review and change-management processes to satisfy ISO 27001 A.12/A.18 and SOC 2 CC5/CC7.

Using Python

  • Immediately detect and revoke over-privileged / persistence-enabling role changes

    • Use Cloud Audit Logs + Python to detect suspicious role bindings (e.g., roles/owner, roles/iam.serviceAccountTokenCreator, roles/iam.serviceAccountUser, key creation roles) on service accounts, then automatically strip them from project or service account IAM policies.
    • This supports least-privilege and SoD requirements (ISO 27001 A.9, SOC 2 CC6, CIS GCP, NIST 800-53 AC-2/AC-6).
    from google.cloud import resourcemanager_v3
    from google.iam.v1 import policy_pb2

    PROJECT_ID = "my-project-id"
    SENSITIVE_ROLES = {
    "roles/owner",
    "roles/iam.serviceAccountTokenCreator",
    "roles/iam.serviceAccountUser",
    "roles/iam.serviceAccountKeyAdmin",
    "roles/iam.serviceAccountAdmin",
    }

    def clean_project_policy(project_id: str):
    client = resourcemanager_v3.ProjectsClient()
    project_name = f"projects/{project_id}"
    policy = client.get_iam_policy(request={"resource": project_name})
    new_bindings = []

    for b in policy.bindings:
    if b.role in SENSITIVE_ROLES and any("serviceAccount:" in m for m in b.members):
    # Drop binding or surgically remove service accounts
    filtered_members = [m for m in b.members if not m.startswith("serviceAccount:")]
    if filtered_members:
    b.members[:] = filtered_members
    new_bindings.append(b)
    else:
    new_bindings.append(b)

    policy.bindings[:] = new_bindings
    client.set_iam_policy(request={"resource": project_name, "policy": policy})

    if __name__ == "__main__":
    clean_project_policy(PROJECT_ID)
  • Lock down service account keys and impersonation paths; rotate if compromised

    • Enumerate service accounts, remove roles/iam.serviceAccountUser and key-admin roles from non-admin principals, disable key creation where not required, and rotate keys/credentials if abuse is suspected, aligning with NIST 800-53 AC-2/IA-5 and SOC 2 change management controls.
    • Use Python with the IAM Admin API to (a) remove risky members from service account IAM, (b) disable/delete user-managed keys.
    from google.cloud import iam_admin_v1

    PROJECT_ID = "my-project-id"
    SENSITIVE_MEMBER_PREFIXES = {"allUsers", "allAuthenticatedUsers"}
    RISKY_ROLES = {
    "roles/iam.serviceAccountUser",
    "roles/iam.serviceAccountTokenCreator",
    "roles/iam.serviceAccountKeyAdmin",
    }

    def harden_service_account(sa_email: str):
    client = iam_admin_v1.IAMClient()
    name = f"projects/{PROJECT_ID}/serviceAccounts/{sa_email}"

    # Clean IAM policy on the service account
    policy = client.get_iam_policy(request={"resource": name})
    new_bindings = []
    for b in policy.bindings:
    if b.role in RISKY_ROLES:
    safe_members = [
    m for m in b.members
    if not any(m.startswith(p) for p in SENSITIVE_MEMBER_PREFIXES)
    ]
    # Optionally restrict to a central break-glass group only
    # safe_members = [m for m in safe_members if m == "group:breakglass-admins@org.com"]
    if safe_members:
    b.members[:] = safe_members
    new_bindings.append(b)
    else:
    new_bindings.append(b)

    policy.bindings[:] = new_bindings
    client.set_iam_policy(request={"resource": name, "policy": policy})

    # Disable/delete existing user-managed keys for this SA
    key_client = iam_admin_v1.IAMClient()
    for key in key_client.list_service_account_keys(
    request={
    "name": name,
    "key_types": [iam_admin_v1.ListServiceAccountKeysRequest.KeyType.USER_MANAGED],
    }
    ).keys:
    key_client.delete_service_account_key(request={"name": key.name})

    if __name__ == "__main__":
    harden_service_account("sa-internal-app@my-project-id.iam.gserviceaccount.com")
  • Constrain lateral movement by scoping CI/CD and cross-project access; continuously verify

    • Review and constrain CI/CD service account permissions to least privilege (project- or resource-scoped roles instead of roles/editor/owner, no broad BigQuery/Storage access), and ensure separate accounts per environment/project to satisfy GDPR/HIPAA/PCI-DSS segregation and data-minimization.
    • Use Python to scan for CI/CD SAs with cross-project or high-risk roles and remove/replace them with narrower roles.
    from google.cloud import resourcemanager_v3

    ORG_PROJECTS = ["prod-project", "stg-project", "dev-project"]
    CICD_SA_SUFFIX = "-cicd@"
    OVERBROAD_ROLES = {
    "roles/owner",
    "roles/editor",
    "roles/bigquery.admin",
    "roles/storage.admin",
    }

    def find_and_downgrade_cicd(project_id: str):
    client = resourcemanager_v3.ProjectsClient()
    res = f"projects/{project_id}"
    policy = client.get_iam_policy(request={"resource": res})

    for b in list(policy.bindings):
    if b.role in OVERBROAD_ROLES:
    risky_members = [
    m for m in b.members
    if m.startswith("serviceAccount:") and CICD_SA_SUFFIX in m
    ]
    if risky_members:
    # Remove CI/CD SAs from broad roles; keep others
    b.members[:] = [m for m in b.members if m not in risky_members]
    if not b.members:
    policy.bindings.remove(b)

    client.set_iam_policy(request={"resource": res, "policy": policy})

    if __name__ == "__main__":
    for p in ORG_PROJECTS:
    find_and_downgrade_cicd(p)