Home Blog GCP Architecture Series #34 — Inheritance in Practice: Tracing Why an Account Has Access…
GCP Architecture GCP Architecture Series

GCP Architecture Series #34 — Inheritance in Practice: Tracing Why an Account Has Access

Someone has access they should not have, and the resource's own policy does not mention them. Effective access is a union over the whole ancestry, so the answer is somewhere above — and both of the tools you would naturally reach for answer a narrower question than the one you asked, without telling you so.

Verified against current vendor documentation on 16 September 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

#33 established that effective access is a union over the ancestry. This post is the operational consequence: given a principal and a resource, find the binding responsible. Every failure below comes from an inspection that silently narrowed the question.

1
"I checked the resource policy and they are not in it"

You checked one term of the union. An allow policy does not show any roles gained through policy inheritance — the output is what was set at that resource, and nothing about it indicates that more applies.

Correct approach

Read a get-iam-policy result as "bindings set here", never as "access here". The absence of a principal is not evidence.

2
"I searched all IAM policies across the organization"

Closer, but the search finds bindings as written. Google's own worked example is explicit: this request only returns principals who have been granted the Owner role on the project, and does not include those who inherit it.

Correct approach

Use the search to enumerate bindings at every level of the ancestry, then compute the union yourself. It is an inventory tool, not an evaluation engine.

3
"The search returned nothing, so they have no access"

Two ways that conclusion fails. The principal may never be named — a group is, and they are in it. And your own permissions can truncate the answer: the caller must have the iam.roles.get permission for permissions to be listed in a searchAllIamPolicies response.

Correct approach

Search for the groups the principal belongs to as well as the principal, and confirm you hold iam.roles.get before trusting an empty or permission-less result.

4
"Found the binding, deleted it, done"

The binding you found may not be the one granting the access. If an inherited role already gives a principal all of the permissions that they need, a grant on the resource itself is redundant — granting another role that contains the same or fewer permissions is redundant, and has no effect.

Correct approach

Before deleting, establish whether the binding is the source or a duplicate of something inherited. Removing a redundant binding changes nothing and looks like a completed remediation.

Architecture

Allow policies are hierarchical and propagate down the structure. A binding can sit at any of four levels, and tracing means enumerating all four.

Diagram: the four levels a Google Cloud IAM binding can sit at, what each inspection method shows and hides, a terminating procedure for tracing why a principal has access to a resource, and the redundant-binding trap that makes a deletion appear to work
Four places a binding can hide, three tools that each answer a different question, and one procedure that terminates.

The four levels

LevelWhat a binding here reaches
Organization IAM roles granted at this level are inherited by all resources under the organization. The widest blast radius and the least frequently reviewed.
Folder Roles granted at the highest folder level will be inherited by projects or other folders that are contained in that parent folder — so folder nesting means more than one folder may contribute.
Project Everything in the project. Projects represent a trust boundary within your company, which is why this is where most grants land.
Resource One object. Additional resources such as Pub/Sub topics and Compute Engine instances support lower-level roles, alongside the Cloud Storage and BigQuery systems.

What each tool actually tells you

MethodAnswersHides
get-iam-policy on the resource Bindings set at this exact level. Everything inherited. No indication any exists.
asset search-all-iam-policies Bindings as written, anywhere in scope. Inheritance; group membership; permissions you cannot read.
The Google Cloud console Inherited roles — to view them, use the console. Less than the others, which is the surprise.
The one place the console is the more informative interface

#32 and #33 both ran the other way: the console made the weaker choice easy, or hid a version flag that the API exposed. Here it inverts. The documented route to seeing inherited roles is the console; the CLI returns the local policy only. So the habit those posts encourage — prefer the API, it tells you more — is wrong for this one question, and a team that has standardised on the CLI will systematically miss inherited grants.

A procedure that terminates

  1. Establish the ancestry. gcloud projects get-ancestors will get the ancestors for a project and print the folder and organization hierarchy for the given project. This is the list of levels you must check; without it you are guessing at depth.
  2. Expand the principal into a set. The principal itself, plus every group it belongs to, plus any group those groups belong to. Bindings name groups far more often than people, by #31's design argument.
  3. Read the policy at every level — resource, project, each folder, organization — asking for version 3, or conditions will be missing from what you read, per #33.
  4. Collect every binding matching any member of the set, and note which level each came from.
  5. Reduce. Of the bindings found, determine which actually confer the permission in question, and which are redundant against a wider inherited role.

The search API helps with steps 3 and 4 at scale — it takes a memberTypes filter that contains one of the following IAM principal types, so you can restrict a sweep to service accounts or groups — but the union in step 5 is yours to compute.

Why This Architecture Holds Up

Two structural facts make the naive search fail, and they pull in opposite directions.

FactEffect on a search for a principal
Good practice binds groups, not users (#31). The principal is not in any binding. Searching for them returns nothing, correctly and uselessly.
Policies inherit downward, and grants concentrate at project and above. The binding is not near the resource. Searching the resource returns nothing, correctly and uselessly.

So the better an organisation follows the guidance in #31 and #32 — few durable groups, bound at a sensible level, no per-user grants on individual objects — the further the answer sits from the thing you are investigating. Tidy IAM is harder to trace, not easier. That is not an argument against tidiness; it is an argument for treating tracing as a procedure rather than a lookup.

The redundancy trap, which makes a failed fix look like a successful one

Google's example uses the concentric basic roles: grant Kalani Editor at the project, and a Viewer grant on a topic beneath it is redundant, because Editor already contains everything Viewer has. Now invert it into an incident. You are asked to remove someone's read access to that topic. You find the Viewer binding on the topic, delete it, and verify the topic policy no longer mentions them. Nothing has changed — they still read the topic through Editor at the project. The deletion succeeded, the policy diff looks right, and the access is intact. The only defence is step 5: ask which binding is the source before removing any of them.

What to do with this

  1. Never conclude from one level. A principal absent from a resource policy tells you nothing about their access to it.
  2. Get the ancestry first. It defines when the search is finished, which is the difference between a procedure and a hunt.
  3. Search for groups, not just the account. In a well-run estate the account will not appear anywhere.
  4. Confirm your own read permissions. Missing iam.roles.get makes results quietly incomplete rather than erroneous.
  5. Prove the revocation, do not assume it. Re-check effective access after the change, at the resource, not at the level you edited.
  6. Reach for the purpose-built tools when the manual walk gets long — Policy Troubleshooter and Policy Analyzer exist precisely to compute this, and they are #38.

Key Architecture Decisions

DecisionChoose thisBecause
Interpreting a resource policy Read it as "set here", not "access here" An allow policy does not show roles gained through policy inheritance.
Seeing inherited roles The Google Cloud console It is the documented route to view inherited roles.
Using the asset search for access questions As an inventory of bindings It returns only principals granted the role directly on the resource.
Scoping the investigation get-ancestors first It prints the folder and organization hierarchy for the project.
Identifying the principal Expand to groups, recursively Well-designed policies bind groups, so the account may appear nowhere.
Reading policies during a trace Request version 3 Otherwise conditions are absent from what you read (#33).
Trusting an empty search result Only after checking your permissions iam.roles.get is required for permissions to be listed.
Removing a binding you found Establish it is the source first A role redundant against a wider inherited role has no effect to remove.
Verifying a revocation Re-check at the resource The union may still grant it from a level you did not edit.

Closing Thought

There is a particular kind of tool that is dangerous not because it is wrong but because it is narrower than its name suggests. get-iam-policy on a bucket returns a document called the bucket's allow policy, and it is complete and accurate about bindings set on the bucket. Search across an organization for a role and you get every binding granting it, accurately. Neither is lying. Both invite a conclusion they do not support, and neither says so in its output.

The defence is not a better tool — #38 covers those, and they are worth using. It is knowing what question you asked. "Is this principal in this policy" and "can this principal act on this resource" are different questions with different answers, and almost every access investigation that goes wrong went wrong by treating the first as an answer to the second. Get the ancestry, expand the groups, read every level, then decide.

Next in this series

#35 returns to roles and goes a level deeper on the one kind you own: custom roles — launch stages, and the permissions you cannot put in one no matter how much you want to.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent