Skip to main content

resourcemanager.folders.setIamPolicy

Event Information

  • What it is: resourcemanager.folders.setIamPolicy is a Google Cloud Resource Manager API call that sets (overwrites) the IAM policy on a Folder resource. It is typically triggered by commands like gcloud resource-manager folders set-iam-policy or via the REST API/Client libraries.

  • Security & impact:

    • It can add, remove, or replace roles and member bindings (including custom roles, service accounts, external identities) on the folder.
    • Because IAM is inherited, this can cascade to all projects and resources under that folder, potentially granting or revoking broad access.
  • Compliance considerations (e.g., ISO 27001, SOC 2, PCI, HIPAA):

    • Treat this event as high‑sensitivity and log/monitor it via Cloud Audit Logs + SIEM.
    • Require strong change control (approval workflow, ticket linkage) and least privilege (only Org/Folder Admins with business justification).
    • Periodically review folder IAM policies and compare changes against policy-as-code baselines (e.g., Terraform, CSPM tools) to detect unauthorized or non-compliant modifications.

Examples

  • Overly broad folder-level access (e.g., roles/owner / roles/editor to a group)

    • Impact: Users gain inherited control over all child projects/resources (compute, storage, BigQuery) enabling data exfiltration, service deletion, or privilege escalation.
    • Compliance: Violates least privilege in ISO 27001 / SOC 2; risks non-compliant access to regulated data (HIPAA, PCI DSS).
  • Granting external identities access at folder scope (e.g., user:personal@gmail.com, domain:partner.com)

    • Impact: Third parties can create/modify resources or IAM at project level, potentially exposing customer data or production systems.
    • Compliance: Breaches data residency / third-party access controls (GDPR, HIPAA BAA terms, PCI DSS 7) due to unmanaged external accounts.
  • Misconfigured service account / CI pipeline role at folder level (e.g., roles/resourcemanager.folderAdmin)

    • Impact: Compromised pipeline or service account can rewrite IAM across all projects in the folder, disable logging, or backdoor access.
    • Compliance: Undermines separation of duties and change control (ISO 27001 A.6/A.8, SOC 2 CC6, PCI DSS 7.2) and can invalidate audit trails.

Remediation

Using Console

  • Remove overly broad folder-level access (roles/owner, roles/editor, etc.)

    • In Console: Go to IAM & Admin → IAM, click Resource type filter → Folders, select the target folder → click Show inherited permissions. Identify principals (e.g., groups) with Owner, Editor, or other broad roles at folder level.
    • For each risky binding: click Edit principal (pencil icon) → remove Owner / Editor / other broad role → Save. Then re-add only required granular roles at project or resource level (e.g., roles/storage.objectViewer, roles/bigquery.dataViewer) based on job function.
    • Validate: Use IAM → Policy Analyzer (if available) or export IAM policies (Security → IAM Recommender / Policy Troubleshooter) to confirm that inherited broad roles are removed. Document the change for ISO 27001 / SOC 2 access review, and ensure any regulated projects (HIPAA/PCI) now only use least‑privilege roles.
  • Restrict external identities at folder scope

    • In Console: IAM & Admin → IAM, filter to the folder as above. In the principals list, search for @gmail.com, @yahoo.com, or partner domains (e.g., @partner.com, or type domain:partner.com into the filter).
    • For each external identity found at folder level: click Edit principal → remove folder‑level roles → Save. Re‑grant access, if strictly needed, at the specific project or lower resource scope with minimal roles, ideally via Cloud Identity / managed accounts under your org domain and, for vendors, using Cloud Identity federation with conditional access.
    • Validate and document: Confirm no personal accounts or unmanaged domains retain folder‑ or org‑level access. Capture screenshots / export IAM policy for GDPR, HIPAA, PCI DSS 7 evidence (third‑party access, data residency).
  • Tighten service account / CI pipeline roles at folder level

    • Identify high‑risk roles: In IAM & Admin → IAM at folder scope, filter Principal type → Service accounts and look for roles like roles/resourcemanager.folderAdmin, roles/owner, roles/editor, or other powerful IAM / org roles.
    • For each CI/service account: click Edit principal → remove folder‑level admin roles → Save. Then:
      • Move permissions to the specific projects the pipeline needs.
      • Replace powerful roles with custom or predefined least‑privilege roles (e.g., roles/deploymentmanager.editor, roles/storage.admin on specific buckets).
      • If a folder‑level role is truly required, use a narrowly scoped custom role without IAM‑write or logging‑disable permissions.
    • Governance: Enable and verify Cloud Audit Logs for Admin Activity and Data Access on the folder and child projects; ensure that pipeline changes go through Change Management (e.g., approvals in Cloud Build / GitHub Actions). Record this in your ISO 27001 / SOC 2 change and SoD documentation.

Using CLI

  • Tighten folder-level IAM to least privilege

    • List current bindings and identify broad roles / external principals:
      • gcloud alpha resource-manager folders get-iam-policy FOLDER_ID --format=json > folder-iam.json
    • Remove roles/owner, roles/editor, and overly broad roles from groups and users; replace with least-privilege custom / predefined roles at folder or project scope:
      • gcloud alpha resource-manager folders remove-iam-policy-binding FOLDER_ID --member="group:example-group@org.com" --role="roles/editor"
      • gcloud projects add-iam-policy-binding PROJECT_ID --member="group:example-group@org.com" --role="roles/compute.viewer"
    • For ISO 27001 / SOC 2 / PCI DSS alignment, document role design and access reviews (e.g., quarterly) and enforce org policies to block legacy roles:
      • gcloud org-policies set-policy org-policy-least-privilege.yaml --organization=ORG_ID
  • Restrict external identities and third-party access at folder scope

    • Enumerate and remove external members (personal Gmail, partner domains) at folder level; re-grant via managed groups or workload identity federation at project level only:
      • gcloud alpha resource-manager folders get-iam-policy FOLDER_ID --flatten="bindings[].members" --filter="bindings.members:(user: OR domain:)" --format="table(bindings.role, bindings.members)"
      • gcloud alpha resource-manager folders remove-iam-policy-binding FOLDER_ID --member="user:personal@gmail.com" --role="roles/viewer"
    • Enforce domain restriction + external account controls (GDPR / HIPAA / PCI DSS 7) using org policies:
      • gcloud org-policies set-policy domain_restriction.yaml --organization=ORG_ID
    • For partners, use dedicated projects + VPC-SC / per-project IAM, not folder-scope; ensure DPAs/BAAs cover access and log all access via Cloud Audit Logs + CMEK.
  • Constrain service accounts / CI roles and monitor IAM changes

    • Remove high-impact roles (e.g., roles/owner, roles/editor, roles/resourcemanager.folderAdmin) from service accounts/CI at folder scope; assign minimal, task-specific roles at project scope:
      • gcloud alpha resource-manager folders remove-iam-policy-binding FOLDER_ID --member="serviceAccount:ci-sa@PROJECT_ID.iam.gserviceaccount.com" --role="roles/resourcemanager.folderAdmin"
      • gcloud projects add-iam-policy-binding PROJECT_ID --member="serviceAccount:ci-sa@PROJECT_ID.iam.gserviceaccount.com" --role="roles/deployments.deployAgent"
    • Disable direct key usage where possible and use Workload Identity Federation; restrict IAM write permissions and logging configuration to a small break-glass admin group to maintain audit integrity (ISO 27001 A.6/A.8, SOC 2 CC6, PCI DSS 7.2).
    • Implement continuous detection for risky folder-level grants using SCC / Cloud Asset Inventory and alert on SetIamPolicy / SetOrgPolicy events: e.g.,
      • gcloud asset search-all-iam-policies --scope="folders/FOLDER_ID" --query="policy:(roles/owner OR roles/editor OR roles/resourcemanager.folderAdmin)" --format="table(resource, policy.bindings.role, policy.bindings.members)"

Using Python

  • Tighten folder-level IAM & remove broad roles (owner / editor / broad custom roles) from groups and externals

    • Discover and review bindings on the target folder (and optionally recursively) and generate a report of high‑risk principals and roles:
      from google.cloud import resourcemanager_v3

      FOLDER_NAME = "folders/1234567890" # <-- change

      HIGH_RISK_ROLES = {
      "roles/owner",
      "roles/editor",
      "roles/resourcemanager.folderAdmin",
      "roles/resourcemanager.projectCreator",
      "roles/iam.securityAdmin",
      "roles/iam.serviceAccountAdmin",
      }

      EXTERNAL_DOMAIN_SUFFIXES = (
      "@gmail.com",
      "@yahoo.com",
      "@outlook.com",
      # add known external domains or use allow-list of corp domains
      )

      def is_external_member(member: str) -> bool:
      # basic heuristic – prefer an allow‑list of internal domains in production
      if member.startswith("user:") or member.startswith("group:"):
      email = member.split(":", 1)[1]
      return email.endswith(EXTERNAL_DOMAIN_SUFFIXES)
      if member.startswith("domain:"):
      domain = member.split(":", 1)[1]
      # treat any non‑corp domain as external
      return domain not in {"corp.example.com"} # <-- change
      return False

      def main():
      client = resourcemanager_v3.FoldersClient()
      policy = client.get_iam_policy(request={"resource": FOLDER_NAME})
      print(f"IAM Policy for {FOLDER_NAME}")
      for b in policy.bindings:
      risky_role = b.role in HIGH_RISK_ROLES
      for m in b.members:
      external = is_external_member(m)
      if risky_role or external:
      print(f"RISK: role={b.role}, member={m}, risky_role={risky_role}, external={external}")

      if __name__ == "__main__":
      main()
    • Remediate by: (1) designing least‑privilege custom roles at project level, (2) migrating groups to scoped roles (roles/viewer, per‑service roles like roles/bigquery.dataViewer), (3) then editing the folder IAM policy to remove the high‑risk bindings:
      from google.cloud import resourcemanager_v3

      FOLDER_NAME = "folders/1234567890" # <-- change
      PRINCIPALS_TO_STRIP = {
      "group:all-devs@example.com", # <-- change
      "group:ci-pipeline@example.com", # <-- change
      }
      ROLES_TO_STRIP = {
      "roles/owner",
      "roles/editor",
      "roles/resourcemanager.folderAdmin",
      }

      def main():
      client = resourcemanager_v3.FoldersClient()
      policy = client.get_iam_policy(request={"resource": FOLDER_NAME})

      new_bindings = []
      for b in policy.bindings:
      if b.role in ROLES_TO_STRIP:
      remaining_members = [
      m for m in b.members
      if m not in PRINCIPALS_TO_STRIP
      ]
      if remaining_members:
      b.members[:] = remaining_members
      new_bindings.append(b)
      else:
      # drop entire binding
      continue
      else:
      new_bindings.append(b)

      policy.bindings[:] = new_bindings
      # use etag for optimistic locking to satisfy change‑control processes
      updated = client.set_iam_policy(
      request={"resource": FOLDER_NAME, "policy": policy}
      )
      print(f"Updated bindings for {FOLDER_NAME}:")
      for b in updated.bindings:
      print(b.role, b.members)

      if __name__ == "__main__":
      main()
    • Map to compliance: document the role redesign and approval (change tickets), and keep the script output as evidence for ISO 27001 / SOC 2 / PCI DSS least‑privilege reviews.
  • Remove / restrict external identities at folder scope & enforce org‑level controls

    • Enumerate and flag all external members and external domain: bindings at folder level (to satisfy GDPR / HIPAA / PCI DSS 7 third‑party access controls):
      from google.cloud import resourcemanager_v3

      FOLDER_NAME = "folders/1234567890" # <-- change
      INTERNAL_DOMAINS = {"corp.example.com"} # <-- change

      def is_external(member: str) -> bool:
      if member.startswith(("user:", "group:")):
      email = member.split(":", 1)[1]
      domain = email.split("@")[-1]
      return domain not in INTERNAL_DOMAINS
      if member.startswith("domain:"):
      domain = member.split(":", 1)[1]
      return domain not in INTERNAL_DOMAINS
      return False

      def main():
      client = resourcemanager_v3.FoldersClient()
      policy = client.get_iam_policy(request={"resource": FOLDER_NAME})
      for b in policy.bindings:
      for m in b.members:
      if is_external(m):
      print(f"EXTERNAL at folder: role={b.role}, member={m}")

      if __name__ == "__main__":
      main()
    • After confirming business need and BAA/DPA/PCI agreements, either: (1) move access down to specific projects/resources, (2) replace personal accounts with managed identities in a dedicated group, or (3) remove the binding entirely via set_iam_policy (similar pattern as above, filtering is_external(member)).
    • At the org level, configure:
      • constraints/iam.allowedPolicyMemberDomains to restrict which domains can appear in IAM policies.
      • constraints/iam.disableServiceAccountKeyCreation and workload identity for partners.
        Use policy export as audit evidence that external access is controlled.
  • Constrain service accounts / CI roles at folder scope & implement SoD and logging guardrails

    • Inventory service accounts and automated principals bound at folder level with elevated roles (e.g., roles/resourcemanager.folderAdmin, roles/iam.securityAdmin), then downgrade to least‑privilege project‑level roles:
      from google.cloud import resourcemanager_v3

      FOLDER_NAME = "folders/1234567890"
      ELEVATED_ROLES = {
      "roles/resourcemanager.folderAdmin",
      "roles/iam.securityAdmin",
      "roles/iam.serviceAccountAdmin",
      "roles/owner",
      }

      def main():
      client = resourcemanager_v3.FoldersClient()
      policy = client.get_iam_policy(request={"resource": FOLDER_NAME})
      for b in policy.bindings:
      if b.role in ELEVATED_ROLES:
      for m in b.members:
      if m.startswith("serviceAccount:"):
      print(f"ELEVATED SA at folder: role={b.role}, member={m}")

      if __name__ == "__main__":
      main()
    • For each CI/service account:
      • Move permissions to per‑project custom roles aligned with pipeline stages (build/test/deploy), and use per‑env SAs (dev/stage/prod) to enforce separation of duties.
      • Remove folder‑level admin roles and replace with narrowly scoped permissions (e.g., roles/deploymentmanager.editor, roles/clouddeploy.releaseManager) only where needed.
    • Ensure compliance and auditability:
      • Require approvals (e.g., via Cloud Build/Cloud Deploy gates) for IAM‑affecting changes.
      • Enable Cloud Audit Logs for Admin Activity and Data Access on the org/folder and monitor for SetIamPolicy / SetOrganizationPolicy by CI/service accounts, feeding into SIEM with alerts for ISO 27001 A.6/A.8, SOC 2 CC6, PCI DSS 7.2 evidence.