Skip to main content
compliance · gcp

IAM roles bound at project scope that could be resource-scoped

rule IDs covered
1
severity
medium
resource types
all

What does ZopNight detect here?

Project-level IAM bindings apply to every current and future resource in the project. ZopNight's RC-1251 flags each such role that could instead be bound on individual resources, and skips 13 non-scopable roles (Owner, billing, org-policy administration) where "move it to resource level" would be impossible advice.

Signal and threshold

How ZopNight evaluates IAM roles bound at project scope that could be resource-scoped.
Field Value
Rule IDsRC-1251
Categorycompliance
Severitymedium
Metricnone — pure configuration read
Sourceiam_project_level.go

Project scope is a forward commitment

A role bound at project level does not just cover the resources that existed when someone clicked Save. It covers every resource anyone creates in that project forever after. Grant roles/storage.admin on the project because a pipeline needed one bucket, and next quarter’s new buckets, including the one holding customer exports, are silently included. That forward-inheriting property is what separates a project binding from the same role on a single bucket or Cloud SQL instance, and it is why blast-radius reviews treat the two very differently even when the role name is identical.

The 13 roles the rule refuses to flag

Advice is only useful if it is possible to follow, so the rule maintains an explicit skip list of roles with no resource-level equivalent. Project Owner, Editor, and Viewer are excluded here because RC-1250 already flags them with the correct remediation: replace, not re-scope. A project-wide Owner grant cannot be “moved to a resource”. The four roles/billing.* roles and the project/organization administration roles (roles/resourcemanager.projectIamAdmin, roles/resourcemanager.organizationAdmin, roles/orgpolicy.policyAdmin, roles/iam.organizationRoleAdmin) plus the two roles/serviceusage.* roles are inherently project- or org-scoped. What remains, the findings you actually see, are roles like roles/storage.admin or roles/cloudsql.admin that genuinely could live on the specific resources a member uses.

Narrowing without breaking anything

Add the resource-level binding first, remove the project-level one second, and keep the order strict, since the overlap window is what prevents an outage:

Terminal window
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--format="table(bindings.role, bindings.members)"

lists what is bound project-wide today; then, for example, gcloud storage buckets add-iam-policy-binding gs://BUCKET --member=... --role=... creates the narrow grant before you delete the broad one. Verify the workload with the narrow grant under real traffic before removing anything.

Provenance instead of a hardcoded label

Detection rides on how the rows are produced: the discoverer’s project-policy walk is the only producer of GCP iam-role resources, so the resource type itself proves the binding is project-level, replacing an older label that was stamped true on every row and therefore discriminated nothing. The rule then abstains on any role in the skip list. All evidence is read from the project IAM policy; ZopNight never edits bindings, and the finding retires on the discovery cycle after the project-level binding disappears.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·