Access Tiers and Granted Privileges
Every manifest that grants human access does it through the same three-tier
permissions block. This page states exactly what each tier can and cannot do in
Google Cloud.
The tiers are cumulative, and they differentiate on the data plane only — the infrastructure control plane is read-only in all three. Nobody gets to change infrastructure directly, because infrastructure is declared in manifests; a direct console change would only produce drift that the engine reconciles away on the next run.
Naming
Three vocabularies refer to the same three tiers, so you will meet all of them:
| Manifest field | Common name | Custom role |
|---|---|---|
permissions.administrators | admin (applicative admin) | managed_admin |
permissions.contributors | dev | managed_editor |
permissions.viewers | read | managed_viewer |
Declare them with OrganizationUser and OrganizationUserGroup names, never raw
email addresses:
spec:
permissions:
administrators:
groups: [platform-admins]
contributors:
groups: [payments-team]
members: [alice]
viewers:
groups: [finance-readers]
The permission sets are identical in both hosting modes — Infrastream SaaS and self-hosted in your own GCP organization. There is one definition to audit and one never-grant list to enforce; the only difference is which organization defines and holds the role.
What each tier grants
| Capability | read | dev | admin |
|---|---|---|---|
| Infrastructure control-plane read | ✓ | ✓ | ✓ |
| Spanner / Cloud SQL / BigQuery control-plane read | ✓ | ✓ | ✓ |
Cloud Storage object read (get, list) | ✓ | ✓ | ✓ |
Spanner data read (select, read) | ✓ | ✓ | ✓ |
| BigQuery data read (query) | ✓ | ✓ | ✓ |
| Identity Platform user read | ✓ | ✓ | ✓ |
| Cloud Storage object create (add only) | — | ✓ | ✓ |
| Logging, mesh and edge configuration read | — | ✓ | ✓ |
| Cloud Storage object overwrite and delete | — | — | ✓ |
| Spanner DML (insert / update / delete) | — | — | ✓ |
| Cloud SQL connect and IAM login | — | — | ✓ |
| BigQuery load / insert | — | — | ✓ |
| Identity Platform user create / update / delete | — | — | ✓ |
| Schema or DDL changes, anywhere | — | — | — |
| Secret payloads, IAM policy, impersonation | — | — | — |
Three shapes in that table are worth explaining, because each is a deliberate decision rather than a gap.
dev's write access is append-only, and only on objects
Cloud Storage is the one data plane where "add but do not destroy" is actually
expressible: storage.objects.create without storage.objects.delete is
genuinely append-only, because a GCS overwrite-in-place is a delete underneath. A
dev can drop a new file and cannot replace or remove an existing one.
Spanner and BigQuery have no equivalent. spanner.databases.write is
insert-update-delete indivisibly, and bigquery.tables.updateData covers
WRITE_TRUNCATE as well as append. A dev database write would therefore be full
CRUD, which is not "slight", so dev gets no database write — that remains a
deliberate expansion decision rather than a default.
admin has authority over contents, not structure
admin can write every row and every object, and stops at the schema line. admin cannot alter a table's shape, drop a database, or change a bucket's policy, because those are declared in manifests. That is the applicative-admin boundary: full authority over data, none over structure.
read reads application data, never credential material
The line is not read-versus-write. It is application data versus
Infrastream-managed credentials. A reader can select a Spanner row and get a
Cloud Storage object — that is the tier's purpose. A reader can never reach a
Secret Manager payload, a Kubernetes Secret, a KMS key, an identity-provider
client secret, a VM serial port, or a service-account key.
Bulk-extraction APIs are also held above the read tier, because streaming a whole
table is a different act from reading a record to verify it: Cloud SQL backup
export, the BigQuery Storage Read API, and Spanner partitionQuery /
partitionRead are admin-only.
What no tier can do
These are withheld from every tier, including admin.
| Withheld | What it would otherwise expose |
|---|---|
secretmanager.versions.access | Secret payloads |
container.secrets.* | Kubernetes Secrets via GKE RBAC |
firebaseauth.configs.getHashConfig | Identity Platform password-hash parameters |
firebaseauth.configs.getSecret | Upstream identity-provider OAuth client secrets |
iam.serviceAccountKeys.create / .get | Long-lived service-account credentials |
cloudkms.cryptoKeyVersions.useToDecrypt | Anything wrapped with a tenant CMEK |
cloudsql.sslCerts.create | A client certificate, which is a database credential |
compute.instances.getSerialPortOutput | VM startup secrets |
Any *.setIamPolicy and any form of service-account impersonation are withheld
too. Those two are what make the ladder hold: no tier can promote itself into
the tier above, because no tier can edit an IAM policy or borrow another
identity.
All control-plane mutation is withheld on the same grounds — it would be drift.
That includes compute.*, run.* and container.* create/update/delete, Spanner
updateDdl / drop, BigQuery table and dataset mutations,
cloudsql.instances.update / .delete, storage.buckets.update, logging.sinks.*,
serviceusage.services.enable, orgpolicy.*, and
resourcemanager.projects.delete / .update / .move.
Two of those deserve a note on why they are withheld rather than merely unnecessary:
storage.buckets.updatewould let admin disable the object versioning that makes admin's own object deletes recoverable.logging.sinks.*andlogging.logs.deletewould let a tier that can write data destroy the audit trail recording that it did.
How grants are resolved
Highest tier wins. If a principal appears in more than one tier — directly in
viewers and via a group in administrators, say — they receive the widest
tier's role once, not one binding per tier.
Grants are per project. A tier binding is applied at the project level, not on
the tenant folder, so it does not inherit across your whole tenant tree. The
folder carries only roles/resourcemanager.folderViewer, enough to navigate the
hierarchy.
A tier with no configured role grants nothing. If the role backing a tier is not configured, those principals are skipped rather than falling through to a wider tier. They keep folder navigation and get no project-level access.
Two boundaries this model does not cover
The tier ladder governs Google Cloud IAM. Two authorization systems sit beyond its edge, and access there is decided by their own rules:
- Kubernetes RBAC.
container.clusters.connectis held at dev and above. It opens a session against the cluster's API server, and what that session may do is decided by RBAC bindings that this IAM model neither describes nor controls. No tier holdscontainer.secrets.*orcontainer.pods.execon the GCP side. - PostgreSQL roles. admin's Cloud SQL connect permission gets you a
connection. What you can do once connected is governed by database roles and
grants, which are declared separately — see
Database.
Related
- The Security Framework — the layers this sits inside
- Managing Access — the practical guide to granting these tiers
Project,Environment,OrganizationalUnit— manifests carrying apermissionsblock