15.2 Assigning Service Accounts to Resources & Managing SA IAM Policies
Key Takeaways
- Attaching a service account to a resource requires iam.serviceAccounts.actAs, granted through roles/iam.serviceAccountUser on that specific account.
- Google enforces a two-key model -- resource-creation permission plus serviceAccountUser on the SA -- specifically to block privilege escalation.
- Never grant serviceAccountUser at the project or folder level; bind it on the individual service account resource instead.
- Compute Engine is the only major resource type that lets you swap its attached service account after creation, while stopped.
- Anyone with shell access to a VM can query the metadata server for its attached service account's token, so restrict access with OS Login or IAP.
Why This Matters
The official exam guide lists two related bullets under domain 5: "Assigning service accounts to resources" and "Managing IAM of a service account." Where section 15.1 covered a service account as a principal (what it can do to other resources), this section covers the other half of the dual identity: a service account as a resource -- who is allowed to use it, attach it to a workload, or administer it. This distinction is the crux of Google Cloud's privilege-escalation defenses, and ACE loves to test it with "why did this action get denied?" scenarios.
Attaching a Service Account to a Resource
When you launch a Compute Engine VM, deploy a Cloud Function, or deploy a Cloud Run revision, you choose which service account the running workload will use to call other Google Cloud APIs. That is "attaching" a service account to a resource.
gcloud functions deploy export-daily-report \
--runtime=python312 \
--trigger-http \
--service-account=billing-exporter@acme-prod.iam.gserviceaccount.com
To attach any service account to any resource, the person performing the deployment needs the iam.serviceAccounts.actAs permission on that specific service account. That permission is bundled inside the Service Account User role (roles/iam.serviceAccountUser).
Why This Rule Exists: Privilege Escalation Prevention
Imagine a developer holds Compute Instance Admin (roles/compute.instanceAdmin.v1), which lets them create VMs -- but nothing else. Without serviceAccountUser on a specific, highly-privileged service account, that developer cannot launch a VM using that account. If they could, they would effectively inherit all of that service account's permissions (for example, Project Owner) simply by attaching it to a VM they control -- a privilege-escalation path. Google Cloud closes this gap by requiring both:
- Permission to create or manage the resource (for example,
roles/compute.instanceAdmin.v1), and iam.serviceAccounts.actAson the specific service account being attached.
This is a two-key system, and ACE scenario questions often describe exactly this failure mode: "a user can create VMs but gets a permission-denied error when attaching Service Account X -- what role is missing?" The answer is almost always roles/iam.serviceAccountUser, scoped to that service account -- not a project-wide grant.
Granting the Role at the Right Scope
Google's best practice is explicit: do not grant serviceAccountUser at the project or folder level, because that would let a principal act as every service account in scope -- re-creating the same escalation risk you are trying to prevent. Instead, bind the role directly on the individual service account resource:
gcloud iam service-accounts add-iam-policy-binding \
billing-exporter@acme-prod.iam.gserviceaccount.com \
--member="user:dana@acme.com" \
--role="roles/iam.serviceAccountUser"
This creates an IAM policy on the service account itself (its resource-level policy), separate from the project's IAM policy. Both policies exist simultaneously and serve different purposes:
| Policy Location | Controls |
|---|---|
| Project IAM policy | What the service account (as a principal) can do to other resources in the project |
| Service account's own IAM policy | Who (as a principal) can act as, admin, or view this service account (the SA as a resource) |
Changing the Attached Service Account Later
For almost every resource type, the service account is fixed at creation time -- to change it, you redeploy (a new Cloud Run revision, a new Cloud Function version). Compute Engine is the documented exception: you can change which service account is attached to an existing VM, but only while the instance is stopped:
gcloud compute instances stop web-01 --zone=us-central1-a
gcloud compute instances set-service-account web-01 \
--zone=us-central1-a \
--service-account=new-sa@acme-prod.iam.gserviceaccount.com \
--scopes=cloud-platform
gcloud compute instances start web-01 --zone=us-central1-a
Expect the exam to test this exception directly: "Which of these resources allows the attached service account to be changed after creation?" The answer is Compute Engine instances.
Other Core Roles for Managing the SA as a Resource
| Role | What It Allows |
|---|---|
roles/iam.serviceAccountAdmin | View, edit, disable, delete the service account itself |
roles/iam.serviceAccountUser | Attach or impersonate the service account (actAs) |
roles/iam.serviceAccountKeyAdmin | Create and delete keys for the service account |
roles/iam.serviceAccountViewer | Read-only visibility into the service account's metadata |
Metadata Server Risk
Once a service account is attached to a Compute Engine VM, any code that runs on that VM -- including a user with SSH access -- can reach the instance's metadata server (http://metadata.google.internal) and retrieve a live access token for that service account. This is why Google recommends restricting shell access with OS Login or Identity-Aware Proxy (IAP), and, where possible, blocking arbitrary processes from querying the metadata server.
Exam Scenario
A security review finds that a contractor with Compute Instance Admin was able to create a VM attached to the finance team's highly-privileged finance-etl service account, and used it to read data far outside their normal access. What single control, if correctly scoped, would have prevented this?
roles/iam.serviceAccountUser should never have been granted to the contractor on the finance-etl service account -- either it should not have been granted at all, or it should have been granted only on a narrowly-scoped, low-privilege service account instead.
Takeaways
- Attaching a service account to a resource requires
iam.serviceAccounts.actAs, granted throughroles/iam.serviceAccountUseron that specific account. - Google enforces a two-key model -- resource-creation permission plus
serviceAccountUseron the SA -- specifically to block privilege escalation. - Never grant
serviceAccountUserat the project or folder level; bind it on the individual service account resource instead. - Compute Engine is the only major resource type that lets you swap its attached service account after creation, while the instance is stopped.
- Anyone with shell access to a VM can query the metadata server for its attached service account's token, so restrict access with OS Login or IAP.
A developer already holds the Compute Instance Admin role and can create VMs, but receives a permission-denied error when trying to launch a VM attached to the finance team's service account. What role is missing?
Which Google Cloud resource type is the documented exception that allows an administrator to change its attached service account after the resource has already been created?