GCP Error Guide: 'iam.serviceAccounts.actAs' Denied Impersonation Errors
Fix the GCP iam.serviceAccounts.actAs permission denied error: diagnose missing Service Account User role, deploy-time impersonation, and Workload Identity binds.
- #gcp
- #troubleshooting
- #errors
- #iam
Overview
The iam.serviceAccounts.actAs error means the caller is allowed to perform an operation but is not allowed to run it as the service account it specified. GCP separates “can you do X” from “can you act as service account Y.” When you deploy a Cloud Run service, create a VM, or run a function “as” a service account, the platform checks iam.serviceAccounts.actAs on that service account; if the caller lacks it, the request is denied with PERMISSION_DENIED.
You will see this from gcloud or the deployment API:
ERROR: (gcloud.run.deploy) PERMISSION_DENIED: Permission 'iam.serviceAccounts.actAs' denied on service account
run-runtime@my-prod-project.iam.gserviceaccount.com (or it may not exist).
Or in the compute/function form:
ERROR: Required 'iam.serviceAccounts.actAs' permission for
'projects/my-prod-project/serviceAccounts/vm-sa@my-prod-project.iam.gserviceaccount.com'
It occurs whenever one identity attaches another identity to a resource: deploying Cloud Run/Functions with --service-account, creating Compute instances with a service account, submitting Dataflow/Dataproc jobs, or configuring Workload Identity. The actAs check is on the target service account, separate from the deploy permission itself.
Symptoms
- Deploys fail with
Permission 'iam.serviceAccounts.actAs' denied on service account <sa>. - The deployer can otherwise create the resource — the only missing piece is acting as the runtime SA.
- CI pipelines that worked before a SA change suddenly fail at the attach step.
- Workload Identity / GKE pods fail to impersonate a Google service account.
gcloud run deploy api --image gcr.io/my-prod-project/api:latest \
--service-account run-runtime@my-prod-project.iam.gserviceaccount.com --region us-central1
ERROR: (gcloud.run.deploy) PERMISSION_DENIED: Permission 'iam.serviceAccounts.actAs' denied on service account
run-runtime@my-prod-project.iam.gserviceaccount.com (or it may not exist).
Common Root Causes
1. Deployer lacks Service Account User on the runtime SA
The deploying identity needs roles/iam.serviceAccountUser (which contains iam.serviceAccounts.actAs) on the service account being attached.
gcloud iam service-accounts get-iam-policy \
run-runtime@my-prod-project.iam.gserviceaccount.com \
--format="table(bindings.role, bindings.members)"
ROLE MEMBERS
roles/iam.serviceAccountAdmin ['user:james@example.com']
The deployer has admin over the SA’s metadata but no serviceAccountUser binding, so it cannot act as it.
2. The role is bound at the wrong scope
actAs must be granted on the specific service account (or a parent that propagates), not on an unrelated project or a different SA.
gcloud projects get-iam-policy my-prod-project \
--flatten="bindings[].members" \
--filter="bindings.role=roles/iam.serviceAccountUser" \
--format="table(bindings.members)"
Listed 0 items.
No project-level serviceAccountUser and none on the SA itself — nothing grants actAs to the deployer.
3. Wrong runtime service account name (or it doesn’t exist)
The --service-account value is misspelled or points at a deleted SA; the error literally says “or it may not exist.”
gcloud iam service-accounts list \
--filter="email~run-runtime" --format="table(email, disabled)"
EMAIL DISABLED
run-runtime@my-prod-project.iam.gserviceaccount.com False
If the list is empty, the SA name is wrong/deleted; the actAs failure is really a not-found.
4. The runtime service account is disabled
A disabled SA cannot be acted as even if the binding exists.
gcloud iam service-accounts describe \
run-runtime@my-prod-project.iam.gserviceaccount.com \
--format="value(disabled)"
True
disabled: True blocks impersonation; re-enable it before deploying.
5. Cross-project SA without the binding propagated
Attaching an SA from another project requires the actAs grant in that SA’s project, plus often disabling iam.disableCrossProjectServiceAccountUsage.
gcloud iam service-accounts get-iam-policy \
shared-sa@central-project.iam.gserviceaccount.com \
--project central-project \
--format="table(bindings.role, bindings.members)"
ROLE MEMBERS
An empty policy in the SA’s home project means the deployer in another project has no actAs there.
6. Workload Identity binding missing (GKE / federation)
For Workload Identity, the Kubernetes service account must be bound to the Google SA with roles/iam.workloadIdentityUser, which underpins the impersonation.
gcloud iam service-accounts get-iam-policy \
gke-app@my-prod-project.iam.gserviceaccount.com \
--format="table(bindings.role, bindings.members)"
ROLE MEMBERS
No workloadIdentityUser binding means the KSA can’t impersonate the GSA; pods fail with actAs/impersonation errors.
Diagnostic Workflow
Step 1: Read the error for the exact target SA
The message names the service account being acted upon, e.g. run-runtime@my-prod-project.iam.gserviceaccount.com. Everything keys off this SA, not the resource being deployed.
Step 2: Confirm the runtime SA exists and is enabled
gcloud iam service-accounts describe <SA_EMAIL> \
--format="value(email, disabled)"
A not-found or disabled: True is the real problem — fix that before touching IAM bindings.
Step 3: Identify the deploying identity
gcloud auth list
gcloud config get-value account
This is the member that needs actAs on the SA — in CI it’s usually a different service account than your user.
Step 4: Check the actAs grant on the SA
gcloud iam service-accounts get-iam-policy <SA_EMAIL> \
--format="table(bindings.role, bindings.members)"
Look for roles/iam.serviceAccountUser (or workloadIdentityUser for GKE) listing the deploying identity. If absent, that’s the fix.
Step 5: Grant the binding on the service account, then retry
gcloud iam service-accounts add-iam-policy-binding <SA_EMAIL> \
--member="serviceAccount:ci-deployer@my-prod-project.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountUser"
For GKE Workload Identity use roles/iam.workloadIdentityUser with the serviceAccount:PROJECT.svc.id.goog[NAMESPACE/KSA] member. Allow a minute for propagation, then redeploy.
Example Root Cause Analysis
A CI pipeline deploying a Cloud Run service starts failing after a security review reshuffled service accounts:
ERROR: (gcloud.run.deploy) PERMISSION_DENIED: Permission 'iam.serviceAccounts.actAs' denied on service account
run-runtime@my-prod-project.iam.gserviceaccount.com
The runtime SA exists and is enabled, and the deploy itself is permitted (the deployer has roles/run.admin). So the only gap is acting as the runtime SA. The pipeline runs as ci-deployer@my-prod-project.iam.gserviceaccount.com. Checking the runtime SA’s policy:
gcloud iam service-accounts get-iam-policy \
run-runtime@my-prod-project.iam.gserviceaccount.com \
--format="table(bindings.role, bindings.members)"
ROLE MEMBERS
roles/iam.serviceAccountUser ['user:james@example.com']
The review granted serviceAccountUser to the human admin but never re-added the CI deployer, which previously held it via a now-removed group. The deployer can deploy but can’t attach the runtime SA.
Fix: grant serviceAccountUser on the runtime SA to the CI deployer:
gcloud iam service-accounts add-iam-policy-binding \
run-runtime@my-prod-project.iam.gserviceaccount.com \
--member="serviceAccount:ci-deployer@my-prod-project.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountUser"
The next pipeline run deploys successfully.
Prevention Best Practices
- Grant
roles/iam.serviceAccountUseron the specific runtime service account, not broadly at the project, so deployers can act only as the SAs they should. - Keep the deploy permission (e.g.
run.admin) and theactAsgrant together in your IAM-as-code so a SA reshuffle never drops one without the other. - Use distinct runtime SAs per service with least privilege, and document which CI identity is allowed to act as each.
- For GKE, manage the
workloadIdentityUserbinding alongside the KSA so pods reliably impersonate the right GSA. - When using cross-project SAs, grant
actAsin the SA’s home project and confirm cross-project usage isn’t blocked by org policy. - For triage, the free incident assistant can identify the target SA and missing role from the error. More walkthroughs are in the GCP guides.
Quick Command Reference
# Target SA from the error message is what needs the binding
# Does the runtime SA exist and is it enabled?
gcloud iam service-accounts describe <SA_EMAIL> --format="value(email, disabled)"
# Who is deploying?
gcloud auth list
gcloud config get-value account
# Current actAs grants on the SA
gcloud iam service-accounts get-iam-policy <SA_EMAIL> \
--format="table(bindings.role, bindings.members)"
# Grant Service Account User on the SA
gcloud iam service-accounts add-iam-policy-binding <SA_EMAIL> \
--member="serviceAccount:<DEPLOYER>" --role="roles/iam.serviceAccountUser"
# GKE Workload Identity binding
gcloud iam service-accounts add-iam-policy-binding <GSA_EMAIL> \
--member="serviceAccount:<PROJECT>.svc.id.goog[<NS>/<KSA>]" \
--role="roles/iam.workloadIdentityUser"
Conclusion
An iam.serviceAccounts.actAs denial means the caller can perform the operation but is not allowed to run it as the specified service account. The usual root causes:
- The deployer lacks
roles/iam.serviceAccountUseron the runtime SA. - The role is bound at the wrong scope (different project or SA).
- The runtime SA name is wrong or the SA was deleted.
- The runtime SA is disabled.
- A cross-project SA lacks the
actAsgrant in its home project. - A missing
workloadIdentityUserbinding for GKE Workload Identity.
Read the target SA from the error, confirm it exists and is enabled, then grant serviceAccountUser (or workloadIdentityUser) on that SA to the deploying identity — the actAs check is always about the SA you’re attaching, not the resource you’re creating.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.