Resourcemanager.Organizations.Setiampolicy Remediation
Event Information
-
What it is:
resourcemanager.organizations.setIamPolicyis an Admin API call that replaces the IAM policy on an entire GCP organization resource (not incremental). It’s typically invoked viagcloud organizations set-iam-policy, REST API, or through tools that manage org-level IAM. -
Security / GCPIAM impact: This event means someone or something has rewritten the org-level IAM policy, potentially adding/removing org-wide roles and members. It can:
- Grant or revoke powerful roles (e.g.,
roles/owner,roles/resourcemanager.organizationAdmin) across all projects/folders. - Introduce risky principals (e.g.,
allUsers, external domains), affecting least-privilege and segregation-of-duties.
- Grant or revoke powerful roles (e.g.,
-
Compliance / practical actions:
- Treat as high-sensitivity; log, alert, and review every occurrence for SOX/PCI-DSS/ISO 27001 evidence.
- Compare before/after IAM policies (use Cloud Logging and version control) to detect privilege escalation or policy drift.
- Restrict who can call this API (via
resourcemanager.organizations.setIamPolicypermission) to a small break-glass/admin group and enforce approvals via change management.
Examples
-
Organization-level privilege escalation
- An attacker sets an overly permissive policy (e.g.,
roles/ownerorroles/resourcemanager.organizationAdmin) for their account or a compromised service account at the organization level, gaining full control over all projects and folders. - Violates least-privilege and separation-of-duties expectations in ISO 27001, SOC 2, and CIS GCP benchmarks.
- An attacker sets an overly permissive policy (e.g.,
-
Bypassing security & compliance controls
setIamPolicyis used to remove or weaken bindings forroles/orgPolicyPolicyAdmin,roles/securityAdmin, orroles/loggingAdmin, preventing security teams from enforcing org policies, SCC findings, and centralized logging.- Can break controls required by PCI DSS and HIPAA around centralized monitoring, auditability, and configuration hardening.
-
Persistence and data exfiltration path
- A malicious actor adds a broad group (e.g.,
allAuthenticatedUsersor an external domain group) to roles likeroles/resourcemanager.projectCreatoror high-privilege custom roles at org level, enabling creation of shadow projects or access paths for data exfiltration. - Conflicts with identity governance and access control requirements in NIST 800-53 (AC-2, AC-6) and CIS GCP controls.
- A malicious actor adds a broad group (e.g.,
Remediation
Using Console
-
Containment & investigation (Org-level escalation)
- In GCP Console, go to IAM & Admin → IAM, set the Scope selector (top bar) to your Organization.
- Sort by Role and look for any principals with Owner,
roles/resourcemanager.organizationAdmin, or other highly privileged custom roles. - For each suspicious principal:
- Click the pencil icon → Remove high-privilege roles → Save.
- If the account is compromised (user/service account): in IAM & Admin → Service Accounts or Admin Console (for users), disable it and rotate all related keys/secrets.
- In IAM & Admin → Audit Logs and Cloud Logging, filter on
protoPayload.methodName="SetIamPolicy"andresource.type="organization"to identify when/where the escalation occurred; preserve logs for compliance (ISO 27001 A.12, SOC 2 CC7, CIS GCP).
-
Restore and harden security/compliance controls (securityAdmin/orgPolicy/logging)
- In IAM & Admin → IAM (scope: Organization), verify correct bindings for:
roles/orgPolicyPolicyAdmin(or tightly scoped policy admin)roles/securityAdmin(or SCC/security team roles)roles/loggingAdmin/roles/loggingConfigWriter(for centralized logs)
- Re-add legitimate security/compliance groups (e.g.,
secops@,compliance@) to these roles at Org level: Grant access → Add principal → Select role → Save; ensure no unapproved principals retain these roles. - In Organization policies (IAM & Admin → Organization policies), review and re-enable critical policies (e.g., domain restriction, disable service account key creation); validate logging sinks in Logging → Log Router still send all ADMIN_READ / DATA_ACCESS logs to central SIEM for PCI DSS/HIPAA auditability.
- In IAM & Admin → IAM (scope: Organization), verify correct bindings for:
-
Remove persistence paths & enforce least privilege (projectCreator/shadow projects)
- In IAM & Admin → IAM (scope: Organization), search for principals such as
allUsers,allAuthenticatedUsers, external domains, or broad groups on:roles/resourcemanager.projectCreator- Any high-privilege custom roles or roles that can create projects/folders/service accounts.
- For any broad or external principals: Edit principal → Remove these roles at org level → Save; replace with tightly scoped internal groups as needed (per NIST 800-53 AC-2/AC-6, CIS GCP).
- In IAM & Admin → Folders and Manage Resources, list projects/folders recently created; for suspicious “shadow” projects, remove IAM bindings, disable APIs, and, if confirmed malicious, shut them down (select project → Shut down) after exporting required logs/evidence.
- In IAM & Admin → IAM (scope: Organization), search for principals such as
Using CLI
-
Immediate containment & rollback of org-level IAM escalation
- Identify and revoke unauthorized org-level bindings (including
roles/owner,roles/resourcemanager.organizationAdmin, broad groups) and re‑apply baseline access:ORG_ID="1234567890"# 1. Export current org policygcloud organizations get-iam-policy $ORG_ID > org-iam-current.yaml# 2. (Offline) Edit org-iam-current.yaml: remove/adjust malicious bindings# Ensure only approved groups/service accounts keep org-level roles# e.g. roles/resourcemanager.organizationAdmin, roles/owner# 3. Apply corrected policygcloud organizations set-iam-policy $ORG_ID org-iam-current.yaml - Enumerate and clean up shadow access paths (project creators, custom roles, external groups):
# List all org IAM bindings for reviewgcloud organizations get-iam-policy $ORG_ID \--format="table(bindings.role, bindings.members)"# Remove overly broad project creator or custom-role grantsgcloud organizations remove-iam-policy-binding $ORG_ID \--member="group:allAuthenticatedUsers" \--role="roles/resourcemanager.projectCreator"
- Identify and revoke unauthorized org-level bindings (including
-
Restore and lock down security/compliance control roles (Security Admin, Org Policy, Logging)
- Re‑grant required roles only to tightly controlled security groups, and verify no weakened bindings:
SEC_GROUP="group:security-admins@example.com"gcloud organizations add-iam-policy-binding $ORG_ID \--member="$SEC_GROUP" \--role="roles/orgpolicy.policyAdmin"gcloud organizations add-iam-policy-binding $ORG_ID \--member="$SEC_GROUP" \--role="roles/security.admin"gcloud organizations add-iam-policy-binding $ORG_ID \--member="$SEC_GROUP" \--role="roles/logging.admin"
- Enforce constraints to prevent future org‑level privilege escalation and bypass of security controls (aligns with ISO 27001, SOC 2, PCI DSS, HIPAA, NIST 800‑53, CIS GCP):
# Example: restrict who can create projects (blocks shadow projects)gcloud org-policies set-policy project_creation_policy.yaml# Example project_creation_policy.yaml# name: organizations/1234567890/policies/constraints/resourcemanager.projectCreator# spec:# rules:# - allow_all: false# - values:# allowedValues:# - user:admin@example.com# - group:project-creators@example.com
- Re‑grant required roles only to tightly controlled security groups, and verify no weakened bindings:
-
Detection, monitoring, and long‑term hardening
- Review Cloud Audit Logs for
SetIamPolicyon the organization and security‑sensitive roles, and enable high‑severity alerts:# Filter recent org-level SetIamPolicy callsgcloud logging read \'resource.type="organization" AND protoPayload.methodName="SetIamPolicy"' \--organization=$ORG_ID --limit=100 \--format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.serviceData.policyDelta.bindingDeltas)" - Implement continuous controls to meet CIS GCP, NIST AC‑2/AC‑6:
- Require approval workflows and break‑glass processes for org‑level roles (via IAM Conditions, Cloud Functions / Cloud Workflows + security ticketing).
- Regularly export and baseline‑compare org IAM and org policies with CI/CD or compliance tooling to detect unauthorized changes.
- Review Cloud Audit Logs for
Using Python
-
Identify and contain org-level IAM abuse
-
Enumerate and review all org-level bindings, focusing on
roles/owner,roles/resourcemanager.organizationAdmin,roles/resourcemanager.projectCreator, custom high-privilege roles, and security roles (roles/orgPolicyPolicyAdmin,roles/securityAdmin,roles/loggingAdmin), then immediately remove or narrow any non-approved principals and rotate credentials for compromised accounts. -
Example Python (Cloud Resource Manager v1) to list and then clean risky bindings (ensure
ORGANIZATION_IDandALLOWED_PRINCIPALSreflect your policies and approval list):from googleapiclient import discoveryfrom google.oauth2 import service_accountORG_ID = "123456789012" # your organization IDALLOWED_PRINCIPALS = {"roles/owner": {"group:cloud-admins@example.com"},"roles/resourcemanager.organizationAdmin": {"group:org-admins@example.com"},"roles/resourcemanager.projectCreator": {"group:project-creators@example.com"},"roles/orgPolicyPolicyAdmin": {"group:secops@example.com"},"roles/securityAdmin": {"group:secops@example.com"},"roles/loggingAdmin": {"group:secops@example.com"},}SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]creds = service_account.Credentials.from_service_account_file("service-account.json", scopes=SCOPES)crm = discovery.build("cloudresourcemanager", "v1", credentials=creds)def get_org_policy():req = crm.organizations().getIamPolicy(resource=f"organizations/{ORG_ID}",body={"options": {"requestedPolicyVersion": 3}},)return req.execute()def set_org_policy(policy):# Required for conditional bindingsif policy.get("version", 0) < 3:policy["version"] = 3req = crm.organizations().setIamPolicy(resource=f"organizations/{ORG_ID}", body={"policy": policy})return req.execute()def is_high_risk_member(member: str) -> bool:if member in ("allUsers", "allAuthenticatedUsers"):return True# Add domain-level and external checks as per your governanceif member.endswith("@gmail.com") or member.endswith("@yahoo.com"):return True# Example: block external domainif member.endswith("@external-partner.com"):return Truereturn Falsedef clean_org_bindings():policy = get_org_policy()bindings = policy.get("bindings", [])new_bindings = []for b in bindings:role = b["role"]members = set(b.get("members", []))# Enforce allow-list for specific sensitive rolesif role in ALLOWED_PRINCIPALS:allowed = ALLOWED_PRINCIPALS[role]# Only keep members that are in the approved listfiltered = {m for m in members if m in allowed}if filtered:b["members"] = sorted(filtered)new_bindings.append(b)# else drop the binding entirelycontinue# For all other roles, at least strip obviously risky principalsfiltered = {m for m in members if not is_high_risk_member(m)}if filtered:b["members"] = sorted(filtered)new_bindings.append(b)# if nothing left, we drop this bindingpolicy["bindings"] = new_bindingsresult = set_org_policy(policy)print("Updated org IAM policy etag:", result.get("etag"))if __name__ == "__main__":clean_org_bindings()
-
-
Reinstate security/compliance guardrails and monitoring
-
Re-add or validate bindings so that only dedicated security/compliance groups hold
roles/orgPolicyPolicyAdmin,roles/securityAdmin,roles/loggingAdmin, androles/accessContextManager.policyAdmin, and ensure audit logs (Admin, Data Access, Policy Denied) are enabled org-wide; configure log sinks locked to a security project to meet PCI DSS / HIPAA centralized logging. -
Example: Python to ensure security teams have required roles (idempotent “ensure-binding” pattern):
SEC_BINDINGS = {"roles/orgPolicyPolicyAdmin": {"group:secops@example.com"},"roles/securityAdmin": {"group:secops@example.com"},"roles/loggingAdmin": {"group:secops@example.com"},}def ensure_security_bindings():policy = get_org_policy()bindings = policy.get("bindings", [])role_to_binding = {b["role"]: b for b in bindings}changed = Falsefor role, must_have in SEC_BINDINGS.items():if role not in role_to_binding:bindings.append({"role": role, "members": sorted(must_have)})changed = Trueelse:b = role_to_binding[role]members = set(b.get("members", []))if not must_have.issubset(members):members.update(must_have)b["members"] = sorted(members)changed = Trueif changed:policy["bindings"] = bindingsset_org_policy(policy)print("Security bindings enforced")else:print("Security bindings already compliant")if __name__ == "__main__":ensure_security_bindings()
-
-
Prevent persistence / re-escalation and align with least-privilege controls
-
Remove broad identities like
allAuthenticatedUsers, external domains, or unapproved groups from org-level high-privilege roles and especially fromroles/resourcemanager.projectCreator; replace them with tightly controlled groups, enable Org Policy constraints (constraints/iam.allowedPolicyMemberDomains,constraints/iam.disableServiceAccountKeyCreation,constraints/compute.disableSerialPortAccess), and require approvals for org-level IAM changes (change management to align with ISO 27001, SOC 2, NIST 800-53 AC-2/AC-6). -
Example: Python snippet to specifically harden
roles/resourcemanager.projectCreatorand strip public/external members to reduce “shadow project” risk:SENSITIVE_ROLE = "roles/resourcemanager.projectCreator"def harden_project_creator():policy = get_org_policy()bindings = policy.get("bindings", [])for b in bindings:if b["role"] != SENSITIVE_ROLE:continuemembers = set(b.get("members", []))safe_members = {m for m in membersif not is_high_risk_member(m)and m.startswith("group:project-creators@yourcorp.com")}if safe_members:b["members"] = sorted(safe_members)else:# If nothing safe remains, remove binding entirelybindings.remove(b)policy["bindings"] = bindingsset_org_policy(policy)print("Hardened project creator role")if __name__ == "__main__":harden_project_creator()
-