Skip to content
DevOps AI ToolKit
Newsletter
All guides
GCP with AI By James Joyner IV · · 9 min read

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.serviceAccountUser on 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 the actAs grant 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 workloadIdentityUser binding alongside the KSA so pods reliably impersonate the right GSA.
  • When using cross-project SAs, grant actAs in 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:

  1. The deployer lacks roles/iam.serviceAccountUser on the runtime SA.
  2. The role is bound at the wrong scope (different project or SA).
  3. The runtime SA name is wrong or the SA was deleted.
  4. The runtime SA is disabled.
  5. A cross-project SA lacks the actAs grant in its home project.
  6. A missing workloadIdentityUser binding 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.

Free download · 368-page PDF

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.