Skip to main content

google.iam.admin.v1.SetIamPolicy

Event Information

  • Event purpose & scope

    • google.iam.admin.v1.SetIamPolicy is generated when an IAM policy is set or replaced on a GCP resource (e.g., project, folder, organization, service account, some Google Cloud services).
    • It typically originates from calls to setIamPolicy via gcloud, console, or API, and can overwrite existing bindings if not using proper updateMask / conditional updates.
  • Security & compliance implications

    • This event indicates privilege changes (add/remove roles, modify members, or adjust conditional bindings) and is critical for SOC 2, ISO 27001, and PCI-DSS controls around access management and least privilege.
    • It should be monitored as a high-sensitivity change: alert on additions of high-privilege roles (e.g., roles/owner, roles/editor, roles/iam.serviceAccountTokenCreator) or grants to broad principals (e.g., allUsers, allAuthenticatedUsers).
  • Practical monitoring & response

    • Enable and route Audit Logs (Admin Activity) for IAM to a central sink (e.g., Cloud Logging → Pub/Sub → SIEM) and create rules keyed on protoPayload.methodName="google.iam.admin.v1.SetIamPolicy".
    • Enrich detections by checking authenticationInfo.principalEmail and resourceName to confirm who changed which resource, and feed into approval workflows or periodic access reviews to ensure policies remain compliant.

Examples

  • Over-privileged role assignment on sensitive resources

    • SetIamPolicy on a production project or high-value resource (e.g., KMS key, GCS bucket with PHI/PII) that adds broad roles like roles/owner, roles/editor, or roles/storage.admin to a wide group or allUsers/allAuthenticatedUsers.
    • Impact: Violates least-privilege (e.g., ISO 27001 A.9, NIST AC-6), can lead to data exfiltration or key misuse; must be flagged and rolled back via policy versioning / Terraform state.
  • Privilege escalation or bypassing separation of duties

    • A user with limited rights uses SetIamPolicy on projects/* or folders/* to grant themselves or a peer roles/iam.securityAdmin or roles/iam.serviceAccountTokenCreator.
    • Impact: Enables lateral movement and impersonation, potentially breaching SOC 2 / PCI-DSS access control requirements; configure IAM Recommender, org policies (constraints/iam.allowedPolicyMemberDomains) and Access Approval / Approval workflows to control.
  • Granting external or unmanaged identities access to regulated data

    • SetIamPolicy adds external domains or consumer Google accounts to resources holding regulated data (e.g., BigQuery datasets with PCI/PHI) via roles/bigquery.dataViewer or higher.
    • Impact: Possible non-compliance with GDPR, HIPAA, PCI-DSS (data residency, data sharing); enforce organization policies restricting member domains, use VPC-SC perimeters, and monitor SetIamPolicy logs in Cloud Logging / SCC with alerting.

Remediation

Using Console

  • Immediately roll back risky IAM bindings and lock down sensitive resources

    • In GCP Console, go to IAM & Admin → Audit Logs (or Logging → Logs Explorer) and filter on protoPayload.methodName="SetIamPolicy" and the specific resource (project, KMS key, GCS bucket, BigQuery dataset) to identify the exact change (who/when/what role).
    • Navigate to the impacted resource in the Console (e.g., Cloud Storage → Buckets → [bucket] → Permissions, BigQuery → [dataset] → Share, KMS → Key rings/keys → Permissions, or IAM & Admin → IAM for projects/folders), and:
      • Remove broad roles (Owner, Editor, Storage Admin, BigQuery Data Viewer/Admin, etc.) from wide groups, allUsers, allAuthenticatedUsers, external domains, or consumer accounts.
      • Re-add only required, narrow roles (prefer predefined or custom least-privilege roles) to managed identities in your corporate domain.
    • If managed by Terraform or another IaC tool, treat Console as read-only going forward: restore the last known-good configuration from version control, run terraform plan/apply to overwrite the drifted IAM, and ensure state is in sync to avoid re-introducing non-compliant bindings.
  • Prevent privilege escalation and enforce least-privilege & domain restrictions

    • In IAM & Admin → IAM, remove any self-granted or peer-granted high-privilege roles (Security Admin, Organization Admin, Service Account Token Creator, Project Owner) from users who should not have them; ensure separation of duties by assigning these to tightly controlled admin groups only.
    • Configure IAM Recommender (IAM & Admin → Recommender) to get and apply role reduction suggestions, and set Organization Policies in IAM & Admin → Organization Policies:
      • constraints/iam.allowedPolicyMemberDomains to restrict which identity domains can be added.
      • Optionally, additional constraints like constraints/iam.disableServiceAccountKeyCreation and others aligned with NIST AC-6 / ISO 27001 A.9.
    • Implement approval workflows: integrate Access Approval (for supported services) and/or use ticketing-based change management so any IAM change on production or regulated resources requires secondary approval, supporting SOC 2 / PCI-DSS change-control and access-control requirements.
  • Harden perimeter and monitoring for regulated data access

    • For datasets/buckets with PCI/PHI/PII, label and group them (e.g., using Labels and separate projects/folders) and, where applicable, place them inside VPC Service Controls perimeters (Security → VPC Service Controls) to reduce exfiltration risk.
    • In Organization Policies, enforce domain and external sharing restrictions (e.g., constraints/iam.allowedPolicyMemberDomains, relevant BigQuery and Storage sharing constraints) to prevent adding external or unmanaged identities; document this as part of GDPR/HIPAA/PCI-DSS data sharing controls.
    • Set up continuous monitoring: in Logging → Logs Explorer, create log-based metrics on SetIamPolicy with conditions like members:"allUsers" OR roles/owner OR roles/editor OR roles/iam.securityAdmin OR gmail.com and configure Cloud Monitoring alerts and Security Command Center findings to notify security/compliance teams for rapid review and remediation.

Using CLI

  • Immediately roll back over-privileged / mis-scoped IAM and lock down policies

    • Identify and revert bad bindings using policy versioning or explicit set-iam-policy with a corrected JSON:
      # View current policy
      gcloud projects get-iam-policy PROJECT_ID --format=json > policy.json

      # Edit policy.json: remove roles/owner, roles/editor, roles/storage.admin from wide groups / allUsers / allAuthenticatedUsers, etc.

      # Apply corrected policy
      gcloud projects set-iam-policy PROJECT_ID policy.json
      Repeat similarly for KMS keys, buckets, BigQuery datasets:
      gcloud kms keys get-iam-policy KEY_NAME --keyring=KR --location=LOC > key-policy.json
      gcloud kms keys set-iam-policy KEY_NAME key-policy.json --keyring=KR --location=LOC

      gsutil iam get gs://BUCKET > bucket-policy.json
      gsutil iam set bucket-policy.json gs://BUCKET

      bq show --format=prettyjson PROJECT:DATASET > bq-dataset.json
      # edit access[] then:
      bq update --source=bq-dataset.json PROJECT:DATASET
    • Enforce org-level guardrails and least privilege to meet ISO 27001 A.9 / NIST AC‑6:
      # Restrict who can administer IAM
      gcloud organizations get-iam-policy ORG_ID > org-policy.json
      # ensure only limited group has roles/iam.securityAdmin, etc., then:
      gcloud organizations set-iam-policy ORG_ID org-policy.json

      # Restrict which domains can be members
      gcloud org-policies set-policy allowed-domains.yaml
      # allowed-domains.yaml uses constraints/iam.allowedPolicyMemberDomains
      Use custom roles instead of broad primitives and clean up Terraform to align desired vs. actual IAM (update .tf and run terraform plan && terraform apply).
  • Prevent privilege escalation and enforce separation of duties

    • Detect and remove self-granted or peer-granted high-privilege roles:
      # Example: remove a bad binding
      gcloud projects remove-iam-policy-binding PROJECT_ID \
      --member="user:USER@domain.com" \
      --role="roles/iam.securityAdmin"
      Audit SetIamPolicy events:
      gcloud logging read \
      'protoPayload.methodName="SetIamPolicy" AND resource.type="project"' \
      --organization=ORG_ID --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.resourceName, protoPayload.serviceData.policyDelta)"
    • Harden IAM administration to satisfy SOC 2 / PCI-DSS:
      # Limit who can grant tokens / impersonate
      gcloud projects remove-iam-policy-binding PROJECT_ID \
      --member="user:USER@domain.com" \
      --role="roles/iam.serviceAccountTokenCreator"
      Use:
      • Approval workflows / Access Approval for sensitive projects.
      • IAM Recommender to downscope broad roles:
        gcloud recommender recommendations list \
        --recommender=google.iam.policy.Recommender \
        --project=PROJECT_ID --location=global
      • Separate admin accounts / groups for IAM vs. security monitoring, and enforce 2FA.
  • Block external / unmanaged identities from regulated data and monitor continuously

    • Remove external / consumer accounts from PHI/PCI datasets and storage:
      # BigQuery: list and then strip non-corporate members
      bq show --format=prettyjson PROJECT:DATASET > ds.json
      # edit access[] to remove external users/domains
      bq update --source=ds.json PROJECT:DATASET

      # Cloud Storage: remove allUsers / allAuthenticatedUsers or external domains
      gsutil iam ch -d allUsers:objectViewer gs://BUCKET
      gsutil iam ch -d allAuthenticatedUsers:objectViewer gs://BUCKET
      gsutil iam get gs://BUCKET > bucket-iam.json # verify cleanup
    • Enforce domain and perimeter restrictions (GDPR/HIPAA/PCI alignment):
      # Restrict identity domains org-wide
      gcloud org-policies set-policy allowed-domains.yaml # uses constraints/iam.allowedPolicyMemberDomains

      # (If using VPC-SC) Add projects with regulated data to a service perimeter
      gcloud access-context-manager perimeters update PERIMETER_NAME \
      --add-resources=projects/PROJECT_NUMBER \
      --policy=POLICY_ID
    • Configure continuous monitoring and alerts on SetIamPolicy:
      # Example log-based metric for risky SetIamPolicy
      gcloud logging metrics create risky_set_iam_policy \
      --description="Sensitive IAM policy changes" \
      --log-filter='protoPayload.methodName="SetIamPolicy" AND
      (protoPayload.serviceData.policyDelta.bindingDeltas.permission="roles/owner" OR
      protoPayload.serviceData.policyDelta.bindingDeltas.permission="roles/editor" OR
      protoPayload.serviceData.policyDelta.bindingDeltas.member:"allUsers" OR
      protoPayload.serviceData.policyDelta.bindingDeltas.member:"allAuthenticatedUsers")'
      Attach alerting in Cloud Monitoring and surface findings in Security Command Center; document incidents and remediations for audit evidence.

Using Python

  • Detect and roll back over-privileged role assignments on sensitive resources

    • Use Cloud Logging to find risky SetIamPolicy calls (broad roles on sensitive resources), then remove them and re-apply least-privilege via Terraform or a controlled policy file:

      from google.cloud import logging_v2
      import json

      client = logging_v2.Client()
      logger = client.logger("iam-setiampolicy-audit")

      # Example: query recent SetIamPolicy calls that granted broad roles on KMS keys or buckets
      FILTER = """
      protoPayload.methodName="SetIamPolicy"
      protoPayload.serviceName=("cloudkms.googleapis.com" OR "storage.googleapis.com")
      protoPayload.request.policy.bindings.role=(
      "roles/owner" OR "roles/editor" OR "roles/storage.admin"
      )
      """

      for entry in client.list_entries(filter_=FILTER):
      payload = entry.payload
      resource_name = payload["resourceName"]
      policy = payload["request"]["policy"]
      bindings = policy.get("bindings", [])
      risky = [b for b in bindings if b["role"] in [
      "roles/owner", "roles/editor", "roles/storage.admin"
      ]]
      if risky:
      logger.log_text(f"Risky SetIamPolicy detected on {resource_name}: {json.dumps(risky)}")
      # In practice: trigger a rollback pipeline (e.g., Cloud Build) that reapplies a known-good IAM policy
    • For production projects, enforce guardrails via org policies and deny policies to block these roles from being assigned to allUsers/allAuthenticatedUsers:

      from google.cloud import orgpolicy_v2

      client = orgpolicy_v2.OrgPolicyClient()
      project = "projects/1234567890"
      policy_name = f"{project}/policies/iam.allowedPolicyMemberDomains"

      policy = orgpolicy_v2.Policy(
      name=policy_name,
      spec=orgpolicy_v2.PolicySpec(
      rules=[orgpolicy_v2.PolicySpec.PolicyRule(
      deny_all=True # deny non-corporate domains & anonymous
      )]
      )
      )
      client.update_policy(policy=policy)
  • Prevent privilege escalation / separation-of-duties bypass via IAM controls

    • Continuously scan IAM policies on projects/* and folders/* to detect users who can grant themselves roles/iam.securityAdmin or roles/iam.serviceAccountTokenCreator and remove those bindings:

      from google.cloud import resourcemanager_v3, iam_v1

      crm_client = resourcemanager_v3.ProjectsClient()
      iam_client = iam_v1.IAMPolicyClient()

      def remove_risky_admin_roles(project_id: str):
      resource = f"projects/{project_id}"
      policy = iam_client.get_iam_policy(resource=resource)
      risky_roles = ["roles/iam.securityAdmin", "roles/iam.serviceAccountTokenCreator"]
      new_bindings = []
      changed = False

      for b in policy.bindings:
      if b.role in risky_roles:
      # keep bindings only for approved admin groups/service accounts
      allowed_members = [
      m for m in b.members
      if m.startswith("group:sec-admins@corp.com") # example SOD group
      ]
      if allowed_members:
      b.members[:] = allowed_members
      new_bindings.append(b)
      changed = True
      else:
      new_bindings.append(b)

      if changed:
      policy.bindings[:] = new_bindings
      iam_client.set_iam_policy(resource=resource, policy=policy)

      for p in crm_client.list_projects():
      if p.state == resourcemanager_v3.Project.State.ACTIVE:
      remove_risky_admin_roles(p.project_id)
    • Combine with:

      • IAM Recommender (UI/API) to downscope excessive admin roles.
      • Org Policy constraints like constraints/iam.disableServiceAccountKeyCreation and constraints/iam.allowedPolicyMemberDomains to reduce impersonation/abuse paths.
      • Approval workflows (e.g., via Access Approval, ticketing integration) for role elevation to meet SOC 2 / PCI-DSS change control.
  • Restrict external/unmanaged identities on regulated data and enforce compliant access

    • Scan for external domains or consumer accounts on BigQuery datasets / GCS buckets and remove them unless explicitly approved; apply org policies to restrict member domains:

      from google.cloud import bigquery
      from google.cloud import storage

      ALLOWED_DOMAIN = "corp.com"

      def is_external_member(member: str) -> bool:
      if member.startswith(("allUsers", "allAuthenticatedUsers")):
      return True
      if ":" not in member:
      return False
      _, identity = member.split(":", 1)
      if "@" in identity and not identity.endswith("@" + ALLOWED_DOMAIN):
      return True
      return False

      def clean_bq_dataset_iam(project_id: str):
      bq = bigquery.Client(project=project_id)
      for ds in bq.list_datasets():
      dataset = bq.get_dataset(ds.reference)
      policy = bq.get_iam_policy(dataset.reference)
      changed = False
      for b in list(policy.bindings):
      if any(is_external_member(m) for m in b["members"]):
      b["members"] = [m for m in b["members"] if not is_external_member(m)]
      changed = True
      if changed:
      bq.set_iam_policy(dataset.reference, policy)

      def clean_gcs_bucket_iam(project_id: str):
      cs = storage.Client(project=project_id)
      for bucket in cs.list_buckets():
      policy = bucket.get_iam_policy()
      changed = False
      for role, members in list(policy.items()):
      keep = [m for m in members if not is_external_member(m)]
      if len(keep) != len(members):
      policy[role] = keep
      changed = True
      if changed:
      bucket.set_iam_policy(policy)

      project_id = "my-regulated-project"
      clean_bq_dataset_iam(project_id)
      clean_gcs_bucket_iam(project_id)
    • For GDPR/HIPAA/PCI-DSS workloads:

      • Enforce iam.allowedPolicyMemberDomains and VPC-SC service perimeters for regulated projects.
      • Enable SCC and create alerting policies on SetIamPolicy logs that add external members to BigQuery, GCS, or KMS, so any violations are quickly detected and remediated.