Home Blog GCP Architecture Series #31 — IAM Principals: Users, Groups, Domains and Service Accounts…
GCP Architecture GCP Architecture Series

GCP Architecture Series #31 — IAM Principals: Users, Groups, Domains and Service Accounts

A role binding needs something to bind to, and Google Cloud offers more kinds of something than most teams use. Two of them are addressed to everyone on the internet, one is both an identity and a resource, and the difference between the two that look interchangeable — a user and a group — is the difference between an access model that survives a reorganisation and one that does not.

Verified against current vendor documentation on 13 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

The principal is the half of a policy nobody reviews. All four of these come from choosing the wrong kind, or from not knowing what counts.

1
"We grant to users, it is more precise"

It is more precise and it does not survive people. Google's guidance is the other way round: you can use a group's membership to manage users' roles instead of granting IAM roles to individual users. Precision at the binding is what makes an access model that has to be edited every time somebody changes team.

Correct approach

Bind groups, manage membership. The binding then describes a role in the organisation rather than a person, and joining or leaving becomes a membership change rather than a policy change.

2
"We are nowhere near 1,500 principals"

Count again, because the count is not what you think. IAM counts every appearance: it does not deduplicate principals that appear in more than one role binding. A user granted three roles on a project is three principals against the limit, not one.

Correct approach

Measure occurrences rather than distinct identities. A policy with 400 people and four roles each is already past the limit.

3
"So we will use more groups instead"

Up to a point, and then a different ceiling. Domains and Google groups in all role bindings within a single allow policy are capped at 250. Groups relieve the pressure on membership and add a constraint of their own on variety.

Correct approach

Few groups, bound few times, with membership doing the work. The two limits pull in opposite directions and the design that satisfies both is a small stable set of groups rather than one per team per role.

4
"Audit logging has nothing to do with our IAM limits"

It shares the budget. The 1,500 counts all appearances in role bindings and the principals that the allow policy exempts from Data Access audit logging. A logging decision consumes access-control capacity.

Correct approach

Know that the two features share one number before you hit it, because nothing about an audit-logging exemption suggests it will exhaust a policy.

Architecture

A principal is whatever a role can be granted to, and the identifier prefix tells you which kind you are looking at. That prefix is worth reading carefully, because several of them are not people.

Diagram: the Google Cloud IAM principal types and their identifier prefixes, the two limits on an allow policy that pull in opposite directions, and why the principal count includes audit logging exemptions
Six kinds of principal, two limits that fight each other, and one budget shared with a feature that is not access control.

The kinds, and what each is for

PrefixWhat it addressesUse it for
user: One human identity. Break-glass and genuinely singular ownership. Not routine access.
group: A Google group, by membership. Everything routine. The only type whose population changes without a policy change.
serviceAccount: A workload identity — which is also a resource with its own policy. Anything not a person.
domain: Every identity in a Cloud Identity or Workspace domain. Rarely. It is a very wide grant that reads like a narrow one.
principalSet: A set defined by a pool or a resource container — for example all service accounts in a project, folder, or organization. Federated workforce and workload identities, and set-wise grants.
allUsers, allAuthenticatedUsers Anyone on the internet; anyone with a Google account. Deliberate public access, and nothing else.

Two entries in that table deserve more than a row. allUsers and allAuthenticatedUsers are the public-access principals, and post #22's storage.publicAccessPrevention exists specifically to stop them being used on buckets — the organization policy and the principal type are two halves of one conversation. And domain: is the quiet one: it grants to every identity in a domain, which is an entire company, expressed in the same syntax as a single user.

A service account is two things at once

This is the structural oddity of IAM and it explains a great deal of later confusion. A service account is a principal — you grant it roles, and it acts. It is also a resource, with an allow policy of its own governing who may use it. That second policy is what post #26 turned on: a user with Service Account User on a deploy account inherits everything that account can do, and the grant lives on the service account rather than on the thing being deployed.

The console drops the prefix, and only for new service accounts

Google documents a small asymmetry worth knowing before it wastes an afternoon: when granting access to a new service account in the Google Cloud console, you omit the serviceAccount: prefix. The prefix is part of the identifier everywhere else — in the API, in Terraform, in a policy you read back — so a value copied out of the console into a configuration file is either right or wrong depending on which field it came from.

The two limits, and why they disagree

LimitValueWhat pushes against it
Principals in one allow policy 1,500 Every appearance, not every identity. Granting individually blows this fast.
Domains and Google groups in one allow policy 250 Every distinct group you invent. Granting through many narrow groups blows this instead.

The first limit punishes granting to people. The second punishes solving that by inventing a group per team per role. Between them they describe the shape of an access model Google expects: a modest number of groups, each meaning something durable, bound a small number of times, with the churn absorbed by membership.

Why This Architecture Holds Up

All four principal types can hold a role. Only one of them has a population that changes without touching a policy, and that single property is what makes it the default.

When someone joins or leavesWhat has to change
Bound as user:Every allow policy they appear in, found by searching the estate.
Bound via group:One membership list, outside IAM entirely.
Bound via domain:Nothing — which is the problem, not the feature.

The middle row is the entire argument. It is not that groups are tidier; it is that a group moves the churn to a system built for churn, and leaves the policy describing a durable fact. A binding that says "the platform on-call group may do this" stays true across three reorganisations. A binding naming a person stops being true the first time they change team, and nothing in IAM will tell you.

The counting rule that catches people out

Because IAM does not deduplicate principals that appear in more than one role binding, the 1,500 is a budget on bindings-times-principals, not on people. Four roles for 400 people is 1,600 and fails. Four roles for four groups is 16, whatever the groups contain. That is the strongest quantitative argument for groups in the whole of IAM, and it is buried in a quotas page rather than stated as guidance.

Where the other types genuinely belong

  • user: — break-glass, and ownership of something that really is one person's. Post #22's domain-restricted sharing decides which users can be named at all.
  • domain: — almost never in a grant. Its natural home is the allowed values of the domain-restriction policy, where breadth is the point.
  • principalSet: — the federated cases from post #30, and container-wide grants such as all service accounts in a project, folder, or organization. Powerful and worth reading twice, because the set is defined by where a thing lives rather than by what it is.
  • allUsers — public by intent, blocked by policy everywhere else. If it appears in a policy nobody chose deliberately, that is the finding.

What to do with this

  1. Audit for user: bindings and treat each as a question rather than a defect — some are legitimate. Post #23's asset export makes IAM policy queryable, which is how you find them across an estate.
  2. Count occurrences, not identities, when judging headroom against 1,500.
  3. Keep the group vocabulary small and durable enough to survive a reorganisation, which also keeps you clear of 250.
  4. Look for allUsers and allAuthenticatedUsers first in any review. They are the only principals whose presence is almost always either deliberate or an incident.

Key Architecture Decisions

DecisionChoose thisBecause
Routine human access group: Google's guidance is to manage roles through membership rather than granting to individual users.
Judging policy headroom Count appearances IAM does not deduplicate principals appearing in more than one role binding.
Group vocabulary Small and durable Domains and groups in one allow policy are capped at 250.
Before adding audit-logging exemptions Check the principal count Exempted principals count against the same 1,500.
domain: in a grant Almost never It grants to every identity in the domain, in the syntax of a single user.
Workload identity serviceAccount:, and govern its own policy It is a principal and a resource; who may act as it is a separate grant.
Copying an identifier from the console Re-add the prefix The console omits serviceAccount: when granting to a new one.
Reviewing a policy Read the public principals first allUsers and allAuthenticatedUsers are deliberate or an incident, never incidental.
Finding user bindings estate-wide The asset export from #23 IAM policy is one of its four metadata types.

Closing Thought

Most IAM attention goes to the role — which permissions, how narrow, predefined or custom. The principal gets a fraction of it, which is odd, because the role decides what can be done and the principal decides for how long that stays correct. A perfectly scoped role granted to a named person is wrong the day they change team, and it goes on being wrong quietly.

The quotas page makes the argument better than any guidance does. Four roles for four hundred people is 1,600 principals and fails; four roles for four groups is sixteen, whatever those groups contain. That is not a limit you work around — it is the platform declining to support the access model you were about to build. The numbers are doing what good limits do: telling you the shape of the answer before you have finished asking.

Next in this series

#32 takes the other half of a binding: roles — basic, predefined and custom — what each costs to maintain, and why the three basic roles are still the most-granted and least-appropriate thing in IAM.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent
<();