Skip to main content

Service Accounts

Exam guide§4.2

A service account (SA) is an identity for a workload, not a person. Code running on a VM, a Cloud Function, or GKE authenticates AS an SA. It is both an identity (you grant it roles) AND a resource (you grant people roles ON it).

Two ways an SA gets used:

  • Attached SA (preferred) - assign the SA to the resource (VM, function, Cloud Run). The metadata server hands out short-lived tokens automatically. No keys to manage.
  • SA key (avoid) - a downloaded JSON private key. Long-lived, leakable, the top cause of GCP credential incidents.

There are three types of service account, distinguished by who creates them:

Service accountUser-created (custom)• You create it• Assign any IAM roles / scopes• Attach to any VM instanceBuilt-in default• Compute Engine + App Engine• Auto-created per project• Editor role by defaultGoogle APIs accountPROJECT_NUMBER@cloudservices.gserviceaccount.com• Runs internal Google processes• Editor role
Every project has two built-in service accounts and the Google APIs account before you create any of your own. User-created accounts are the ones you build and lock down per workload.
NumbersDefault service accounts
  • Compute Engine default SA: PROJECT_NUMBER-compute@developer.gserviceaccount.com - historically had the broad Editor role. Newer orgs disable that automatic grant via the iam.automaticIamGrantsForDefaultServiceAccounts org policy. Lock it down.
  • App Engine / Cloud Functions default SA: PROJECT_ID@appspot.gserviceaccount.com.
  • Google-managed service agents (e.g. service-...@...gserviceaccount.com) are created by Google to let a service act on your resources - don't delete them.
  • Google APIs service account: PROJECT_NUMBER@cloudservices.gserviceaccount.com - runs internal Google processes on your behalf; also automatically granted the Editor role.
  • An SA email is its identifier; it has a unique numeric ID too.
  • 10 user-managed keys max per SA. You can disable an SA (gcloud iam service-accounts disable) instead of deleting it.
CompareTwo different roles, don't confuse them
Roles granted TO the SAWhat the workload can do (e.g. the SA has `roles/storage.objectAdmin`).
roles/iam.serviceAccountUserGranted to a person/SA - permission to attach/act as that SA, needed to deploy a VM or function that runs as it.

Deploying a resource that runs as an SA requires serviceAccountUser on that SA, plus normal deploy permissions.

GotchaGive the SA minimum permissions

Grant the SA only the roles its workload needs, scoped to the specific resources. Do not reuse the default Editor SA for everything. One purpose-built SA per workload.

IAM lets you slice a project into microservices, each with its own SA and its own scoped access - even reaching into another project or a single bucket:

project_acomponent_1Service Account 1Editorcomponent_2Service Account 2Storage.objectViewerbucket_1project_b
One SA per component. component_1 runs as Service Account 1 (Editor on project_b); component_2 runs as an isolated Service Account 2 (objectViewer on bucket_1). Re-scoping a component means re-granting its SA - no VM is re-created.
GotchaService Account User grants access to everything the SA can reach

A person or group with roles/iam.serviceAccountUser on an SA can act as it, and therefore reach every resource that SA has access to. Be cautious granting it - the person inherits the SA's full blast radius.

CommandsCreate and attach an SA
# Create
gcloud iam service-accounts create my-app-sa \
--display-name="My App SA"
 
# Grant it a scoped role
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:my-app-sa@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
 
# Attach at VM creation (no keys needed)
gcloud compute instances create my-vm \
--service-account=my-app-sa@PROJECT_ID.iam.gserviceaccount.com \
--scopes=cloud-platform

Service account keys

Every SA has a key pair used to authenticate it. There are two kinds, and the difference is who holds the private half:

Google-managed (default)All SAs have a Google-managedkey pairGoogle stores both halves inescrow - private never exposedEach public key signs for amaximum of 2 weeksRotated automaticallyUser-managed ("external")Google stores only the publichalf; you hold the private keyYou own private-key securityand rotationUp to 10 keys per SAUsable outside Google CloudManage via IAM API, gcloud,or the console
Every SA has a Google-managed key pair (Google holds both halves in escrow and rotates them). User-managed "external" keys hand you the private half - and the responsibility for keeping it safe and rotating it.

Inside Google Cloud (Compute Engine, App Engine, GKE, Cloud Run), keys are Google-managed automatically. Create user-managed keys only as a last resort - prefer short-lived credentials (tokens) or service account impersonation instead.

GotchaGoogle cannot recover a lost user-managed private key

Google never stores the private half of a user-managed key. Lose it and it is gone - Google cannot help you recover it. You own its security and its rotation.

CommandsList an SA's keys
gcloud iam service-accounts keys list \
--iam-account=my-app-sa@PROJECT_ID.iam.gserviceaccount.com

Impersonation & short-lived credentials

Impersonation lets one identity temporarily act as a service account WITHOUT ever holding that SA's key. You use your own credentials to mint a short-lived token for the SA, then call APIs as the SA. This is the modern replacement for downloading key files.

Mental model: "let me borrow that SA's permissions for a few minutes, then throw the token away."

NumbersThe key permission
  • To impersonate an SA you need roles/iam.serviceAccountTokenCreator ON that SA.
  • Generated access tokens are short-lived - default 1 hour, max 12 hours (org policy constraints/iam.allowServiceAccountCredentialLifetimeExtension gates the extension).
  • Contrast with roles/iam.serviceAccountUser = attach/run as (deploy-time); serviceAccountTokenCreator = mint tokens for (runtime impersonation).
CompareToken Creator vs Service Account User

Easy to swap on the exam:

serviceAccountUserAttach an SA to a VM or function so it runs as that SA.
serviceAccountTokenCreatorGenerate access/ID tokens (impersonate) for that SA.

If a question is about impersonating or --impersonate-service-account, the answer is Token Creator.

GotchaImpersonation is auditable and keyless

Every impersonated call is logged (who borrowed which SA), and no long-lived key exists to leak. Prefer it over SA keys for admins and CI that must act as an SA occasionally.

CommandsImpersonate on the fly
# Run any gcloud command AS the SA for this invocation
gcloud storage ls gs://my-bucket \
--impersonate-service-account=my-sa@PROJECT_ID.iam.gserviceaccount.com
 
# Grant the impersonation right
gcloud iam service-accounts add-iam-policy-binding \
my-sa@PROJECT_ID.iam.gserviceaccount.com \
--member="user:admin@example.com" \
--role="roles/iam.serviceAccountTokenCreator"
 
# Mint a raw short-lived access token
gcloud auth print-access-token \
--impersonate-service-account=my-sa@PROJECT_ID.iam.gserviceaccount.com

Access scopes (legacy VM authorization)

Access scopes are an older, VM-level authorization layer applied to the attached SA. When you create a VM you choose scopes ("Allow full access to all Cloud APIs", "Allow default access", or Set access for each API - e.g. Storage: Read Write). They gate which APIs the VM's SA token may call, regardless of the SA's IAM roles.

Application AApplication BAuthorizationserverAuthorizationserverCloud Storage bucket1 request2 token: read_only3 read only1 request2 token: read_write3 read + write
Each app authenticates to the authorization server (1); the server returns an access token carrying that app's scope (2). The app then calls Cloud Storage with the token (3): Application A's read-only token can only read the bucket, Application B's read-write token can also modify it.

Scopes were the only mechanism for granting permissions to service accounts before IAM roles existed. You can change a VM's scopes after creation, but only while the instance is stopped. For user-created SAs, use IAM roles instead.

GotchaEffective permission = IAM roles ∩ access scopes

A VM's SA can do something only if both allow it. If the SA has roles/storage.admin but the VM's scope is Storage Read Only, writes are still blocked. The modern best practice: attach a purpose-built SA, set scope to cloud-platform (--scopes=cloud-platform), and control everything with IAM roles - don't rely on scopes for least privilege.

GotchaOn a VM you act as the attached SA, not yourself

SSH into an instance and any gcloud/API call runs as the VM's attached SA - your own personal roles don't apply. So if you personally hold Compute Instance Admin but the SA only has storage.objectViewer, then from the VM gcloud compute instances list fails ("insufficient authentication scopes") while downloading a bucket object works. Grant the SA (not just yourself) whatever the workload needs, and remember writes also need a role that permits them (e.g. storage.objectCreator) - viewer alone gives a 403 on upload.

Recap

DECISIONHow should this identity act as an SA?
VM/app always runs as SAAttach the SAneeds serviceAccountUser
Admin/CI borrows SA brieflyImpersonateneeds serviceAccountTokenCreator
Avoid all key filesEither of the abovenever download a key
Pick this when: impersonate for occasional/admin/CI use; attach for a workload that always runs as the SA
DECISIONAttached SA or a key file?
GCE / GKE / Cloud Run / FunctionsAttached SA (metadata tokens)
Another cloud or CI with an IdPWorkload Identity Federation
Truly external, no federationSA key (last resort, rotate it)
Pick this when: use an attached SA whenever code runs on GCP; keys only for off-GCP with no federation option