Google Cloud IAM is one of those topics where the marketing diagram looks simple and the practical reality is meaningfully nuanced. The exam tests both — the conceptual model and the practical edge cases. This guide explains what IAM is, the three role types, service accounts, Workload Identity, and the mistakes that catch otherwise-prepared engineers.
The IAM mental model
Every IAM decision is composed of three things:
- Principal — the “who.” A user, group, service account, or Workload Identity pool member.
- Role — the “what.” A collection of permissions (like
storage.objects.get) bundled into a named role (likeroles/storage.objectViewer). - Resource — the “on which.” Where the binding applies: an organisation, a folder, a project, or a specific resource.
You combine these into an IAM binding: “principal X has role Y on resource Z.” A collection of bindings on a resource is its IAM policy. When a principal tries to perform an action, GCP evaluates every applicable binding and decides whether the action is allowed.
IAM is allow-only. There are no deny rules at the IAM level (other than the separate Org Policy / Deny Policies system, which is beyond ACE scope). If no binding grants the permission, the action is denied by default. If any applicable binding grants the permission, it is allowed.
Resource hierarchy and inheritance
GCP has a 4-level resource hierarchy:
Organization └── Folder (can nest up to 10 deep) └── Project └── Resource (VM, bucket, cluster, etc.)IAM bindings inherit downward. A role granted at the organisation level applies to every folder, project, and resource beneath it. A role granted at project level applies to every resource in that project. A role granted at resource level applies to just that resource.
Bindings union. If you have roles/viewer at org level and roles/owner at project level, you effectively have roles/owner on that project. A lower-level binding cannot subtract a permission granted at a higher level.
Practical implication: apply IAM bindings at the smallest scope that works. Org-level admin grants are powerful and dangerous — one over-broad binding at org level affects every project beneath it.
Principals — the “who”
Principal types GCP recognises:
- Google Account — an individual human user with a Google identity (Gmail, Workspace, or Cloud Identity).
- Service Account — a non-human identity used by applications and workloads. Has an email address ending in
@PROJECT_ID.iam.gserviceaccount.com(for user-managed) or various Google-managed patterns. - Google Group — a collection of Google Accounts. Binding a role to a group binds it to every current and future member. Preferred for human users at scale.
- Google Workspace Domain — every user in a given Workspace domain.
- Cloud Identity Domain — every user in a Cloud Identity domain.
- allAuthenticatedUsers — any signed-in Google user. Use with extreme care.
- allUsers — anyone on the internet, signed in or not. Use only for genuinely public resources (a Cloud Run service exposing a public API, a Cloud Storage bucket serving a public website).
- Workload Identity Pool / Provider members — federated identities from external sources (AWS, Azure, OIDC providers).
Exam-relevant detail: Cloud Run public access requires the binding allUsers → roles/run.invoker. Without it, the service returns 403. Cloud Storage public objects require allUsers → roles/storage.objectViewer at the bucket level.
Roles — the “what”
A role is a named bundle of permissions. A permission has the form SERVICE.RESOURCE.VERB, e.g., compute.instances.create, storage.objects.get, iam.serviceAccounts.actAs.
You do not assign permissions to principals directly. You assign roles, which contain permissions. This is the same model AWS IAM and Azure RBAC use.
The three role types
1. Primitive roles (legacy, broad — use sparingly)
Three primitive roles, granted at project level:
- Owner — full administrative access to all resources in the project, plus the ability to manage IAM.
- Editor — read/write access to most resources but cannot manage IAM.
- Viewer — read-only access to most resources.
These predate per-service roles and are intentionally broad. Google's guidance and the ACE exam both treat primitive roles as almost always the wrong answer in production. Use only for very small projects (e.g., a personal sandbox) or for short-lived emergency access.
2. Predefined roles (recommended)
Predefined roles are Google-curated bundles of permissions scoped to a specific service. There are hundreds. The naming convention is roles/SERVICE.PURPOSE:
roles/compute.instanceAdmin.v1— full management of VMs.roles/compute.viewer— read-only VM access.roles/storage.objectViewer— read objects in Cloud Storage buckets.roles/storage.objectCreator— write objects (no read).roles/storage.admin— full bucket and object management.roles/container.admin— full GKE administration.roles/container.developer— deploy workloads but not manage clusters.roles/iam.serviceAccountUser— use a service account (e.g., attach it to a VM).roles/iam.serviceAccountTokenCreator— generate short-lived tokens for a service account (impersonation).roles/cloudsql.client— connect to Cloud SQL instances.roles/run.invoker— invoke a Cloud Run service.roles/run.admin— manage Cloud Run services.roles/logging.viewer— read logs in Cloud Logging.roles/monitoring.viewer— read metrics in Cloud Monitoring.
Always prefer predefined roles when one fits. Google maintains them as new permissions are added to services, so you do not have to update your bindings as services evolve.
3. Custom roles (when nothing predefined fits)
Custom roles let you bundle an arbitrary set of permissions. You define the role at the organisation or project level, give it a name (roles/myCustomRole), and pick the exact permissions it contains.
Use custom roles when:
- No predefined role matches your need without granting too much.
- You want a precise least-privilege grant for a specific workflow.
- You are building a delegated-admin model where specific teams manage specific functions.
Trade-off: you own the maintenance. When Google adds new permissions to a service, your custom role does not automatically pick them up. Predefined roles do.
Policies and bindings
An IAM policy is a list of bindings attached to a resource. In JSON:
{
"bindings": [
{
"role": "roles/storage.objectViewer",
"members": [
"user:alice@example.com",
"group:data-readers@example.com",
"serviceAccount:reporter@my-project.iam.gserviceaccount.com"
]
},
{
"role": "roles/storage.admin",
"members": ["user:bob@example.com"]
}
],
"etag": "BwWWja0YfJA=",
"version": 3
}etag prevents concurrent edits from clobbering each other. If you GET a policy, modify it, and PUT it back, GCP rejects the PUT if the etag has changed in the meantime.
To modify a policy via gcloud, use:
gcloud projects add-iam-policy-binding PROJECT_ID \ --member="user:alice@example.com" \ --role="roles/storage.objectViewer"gcloud handles the get-modify-put flow automatically. For organisation- or folder-level bindings, use gcloud organizations add-iam-policy-binding or gcloud resource-manager folders add-iam-policy-binding.
Service accounts in depth
A service account (SA) is a non-human identity used by workloads. SAs have email addresses, are themselves resources within a project, and can be granted IAM roles on other resources. The key distinction from human users: SAs are used by code, not by people.
Service account types
- User-managed SAs — you create them, name them, and manage their lifecycle. Use for production workloads.
- Default SAs — created automatically when you enable certain services. The Compute Engine default SA is the most-known example. Avoid using default SAs for production workloads — they have broad default permissions.
- Google-managed SAs — used by Google services to act on your behalf. You do not manage these directly.
How code authenticates as a service account
Three patterns, in order of preference:
- Attach the SA to a compute resource. A VM with an attached SA gets credentials automatically through the metadata server. Cloud Run services and Cloud Functions get credentials the same way. No keys involved.
- Workload Identity (for GKE specifically). Bind a Kubernetes SA to a Google SA; pods get short-lived tokens through the metadata service. No keys.
- Service account impersonation. A user or another SA gets short-lived tokens for the target SA via the IAM API. Useful for developers running gcloud locally without long-lived keys.
JSON service-account keys are the legacy option and should be avoided. If a question shows a developer downloading a JSON key, the right answer almost always involves replacing that with one of the three patterns above.
Workload Identity (GKE)
Workload Identity is the recommended way for GKE pods to authenticate to GCP APIs. It establishes a trust relationship between a Kubernetes service account (KSA) and a Google service account (GSA): pods running as the KSA receive short-lived tokens that authenticate as the GSA.
The setup, conceptually:
- Enable Workload Identity on the GKE cluster (
--workload-pool=PROJECT_ID.svc.id.goog). - Create a GSA and grant it the GCP roles the workload needs.
- Create a KSA in the pod's namespace.
- Bind the KSA to the GSA: grant the KSA the
roles/iam.workloadIdentityUserrole on the GSA. - Annotate the KSA with
iam.gke.io/gcp-service-account=GSA_EMAIL. - Run pods with
serviceAccountName: KSA_NAME.
Pods now make GCP API calls authenticated as the GSA, with credentials rotated automatically. No JSON keys, no manual credential management.
Service account impersonation
Two principals are involved in impersonation:
- The caller — a user or SA that wants to act as the target SA temporarily.
- The target SA — the SA whose identity will be used.
The caller must have roles/iam.serviceAccountTokenCreator on the target SA. With that binding, the caller can request short-lived access tokens (typically 1-hour) for the target SA, and use those tokens to make API calls.
Common gcloud usage:
gcloud auth login # as your user gcloud storage cp file.txt gs://prod-bucket/ \ --impersonate-service-account=deploy-sa@my-project.iam.gserviceaccount.comThis is the modern alternative to downloading deploy SA JSON keys. Developers authenticate as themselves; specific operations run as the deploy SA via impersonation.
Conditional bindings
IAM bindings can include conditions that restrict when the binding applies. Conditions use Common Expression Language (CEL) and can reference attributes like request time, resource name, or resource type.
Example: grant access only during business hours:
{
"role": "roles/compute.instanceAdmin",
"members": ["user:alice@example.com"],
"condition": {
"title": "Business hours only",
"expression": "request.time.getHours('America/Los_Angeles') >= 9 && request.time.getHours('America/Los_Angeles') < 18"
}
}Common uses on the exam:
- Time-bounded access (temporary admin grants that auto-expire).
- Resource-name conditions (only buckets matching a name pattern).
- Resource-type conditions (only Compute Engine VMs, not GKE clusters).
Best practices (and the exam expects these)
- Prefer predefined over primitive roles. Owner/Editor/Viewer should be a red flag.
- Bind to groups, not individuals. Manage group membership outside of IAM bindings.
- Use the smallest scope that works. Resource > project > folder > organisation.
- Avoid JSON service-account keys. Use direct attachment, Workload Identity, or impersonation.
- Use Workload Identity for GKE always, not the node SA.
- Do not use default service accounts for production workloads. Create user-managed SAs.
- Audit IAM regularly. Cloud Asset Inventory and IAM Recommender flag over-privileged bindings.
- Use conditional bindings for time-bounded or scope-narrowed access.
- Separate the “who can use the SA” binding from the “what the SA can do” binding.
roles/iam.serviceAccountUseron the SA controls who can act as it. Roles on other resources control what it can do.
Real-world examples
Example 1: A Cloud Run service that reads from a Cloud Storage bucket
- Create a user-managed SA:
app-sa@my-project.iam.gserviceaccount.com. - Grant the SA
roles/storage.objectVieweron the bucket only (not project-wide). - Deploy Cloud Run with the SA:
gcloud run deploy --service-account=app-sa@.... - Cloud Run gets credentials automatically via the metadata server. No keys involved.
Example 2: A developer running terraform that needs to provision GCP resources
- Create a deploy SA:
terraform-deploy@my-project.iam.gserviceaccount.com. - Grant the deploy SA the predefined roles it needs (e.g.,
roles/compute.networkAdmin,roles/iam.serviceAccountAdmin). - Grant the developer
roles/iam.serviceAccountTokenCreatoron the deploy SA. - Developer runs
gcloud auth loginas themselves, then uses impersonation when running terraform:gcloud auth application-default login --impersonate-service-account=terraform-deploy@.... - No JSON keys. Developer audit trail preserved.
Example 3: A GKE pod that needs to write to BigQuery
- Enable Workload Identity on the cluster.
- Create a GSA:
bq-writer@my-project.iam.gserviceaccount.com. - Grant the GSA
roles/bigquery.dataEditoron the target dataset. - Create a KSA in the pod's namespace.
- Grant the KSA
roles/iam.workloadIdentityUseron the GSA. - Annotate the KSA with the GSA email.
- Run the pod with that KSA. The pod authenticates to BigQuery as the GSA, with no keys.
Common ACE exam traps
- Primitive role as the “easy” answer. If you see
roles/ownerorroles/editorin options, suspect a trap. Predefined or custom roles are almost always the expected answer. - JSON key as a “solution.” Any answer involving “download a JSON key” is almost always wrong. Look for Workload Identity, impersonation, or direct attachment.
- Org-level binding when project-level works. If a scenario asks for the least-privilege grant, project- or resource-level beats org-level.
- Default SA used in production. Default SAs have broad permissions. Production answers should use user-managed SAs.
- OS Login confusion. When OS Login is enabled, SSH keys in metadata are ignored. SSH access is controlled by
roles/compute.osLogin(androles/compute.osAdminLoginfor sudo). - Confusing “use this SA” with “manage this SA.”
roles/iam.serviceAccountUserlets you act as the SA.roles/iam.serviceAccountAdminlets you manage the SA (rotate keys, delete it). These are different roles for different purposes.
Frequently asked questions
What is the difference between a primitive, predefined, and custom IAM role in GCP?
Primitive roles are the legacy Owner / Editor / Viewer roles. They are broad and rarely the right choice. Predefined roles are Google-managed roles scoped to a specific service (e.g., roles/storage.objectViewer). Custom roles are roles you define yourself with a specific permission set. Google’s recommendation: use predefined roles when one fits; create a custom role when none fits; avoid primitive roles for production.
What is a service account in GCP?
A service account is an identity used by an application or workload (rather than a human user). Service accounts have email addresses, can be granted IAM roles, and can be attached to compute resources like VMs, GKE pods, or Cloud Run services. They are how non-human code authenticates to GCP APIs.
What is Workload Identity?
Workload Identity is the recommended way for GKE workloads to authenticate to GCP APIs. It binds a Kubernetes service account to a Google service account so pods receive short-lived tokens automatically, with no JSON keys involved. It is one of the most-tested Domain 5 topics on the Google ACE exam.
How does IAM policy inheritance work in GCP?
IAM policies are inherited downward through the resource hierarchy: Organization → Folder → Project → Resource. A role granted at the organization level applies to every folder, project, and resource beneath. Bindings union together — you cannot subtract a permission at a lower level. Less-permissive policies do not override more-permissive parent policies.
When should I use service account impersonation?
Use service account impersonation when a user (or another service account) needs temporary short-lived access as a different identity, without downloading long-lived JSON keys. Common pattern: developers run gcloud commands as themselves but impersonate a deploy service account for production operations.
What is the principle of least privilege in GCP IAM?
Grant each principal only the permissions they need, scoped to the smallest resource they need it on, for the shortest time they need it. Practically: use predefined or custom roles instead of primitive roles, bind roles at the resource or project level rather than organization level, and use service account impersonation or short-lived tokens for elevated operations rather than persistent admin grants.
Practise IAM scenarios
Domain 5 (Access and security) is 20% of the ACE exam and the most-failed domain after Domain 3. Drill it with realistic practice questions. 30 free questions, no signup.