Cloud IAM Permission Denied – The Silent Blocker
You're getting Permission Denied errors in Google Cloud even as an owner. The real fix is adjusting IAM roles and service account bindings, not status checks.
When This Error Hits
You're logged into Google Cloud Console with an Owner role across your project. You try to deploy a Compute Engine instance via the CLI or deploy a Cloud Function, and bam: PERMISSION_DENIED. The error message is vague — something like “The caller does not have permission” or “Permission denied on resource.” This hits hardest when you're automating deployments with CI/CD pipelines, like GitHub Actions or Cloud Build, using a service account. I've seen it trigger when someone tries to attach a custom service account to a new VM or run a Cloud Function that calls another API.
What's Actually Going Wrong
The root cause here is almost never your user account's permissions. You might be an owner, but the identity making the request — often a service account or a compute resource's default account — is missing specific IAM role bindings. Google Cloud's IAM is granular: having Owner on the project gives you broad rights, but it doesn't automatically grant the iam.serviceAccounts.actAs permission on specific service accounts. That's the silent killer. When you tell a VM to run as a service account, or a Cloud Function to authenticate with one, the API checks not just the project-level Owner role but the specific actAs permission on that service account resource. Without it, you get Permission Denied.
Another common scenario: you've granted a service account the roles/storage.objectViewer role at the project level, then created a new bucket with uniform bucket-level access. That role won't apply — you need to bind it directly on the bucket in the IAM policy. Google Cloud's permission model isn't always intuitive; it's hierarchical but with exceptions. The fix is to check the exact resource and identity pair.
How to Fix It – Step by Step
- Identify the identity that's failing. Look at the error log. It'll say something like “Service account ‘my-sa@project.iam.gserviceaccount.com’ does not have permission.” Note that email. If the error is from a Compute Engine instance, it's the service account attached to the VM. For Cloud Functions, it's the runtime service account.
- Add the missing role binding. For the
actAsissue, go to IAM & Admin > Service Accounts in the console. Find the target service account (the one your VM or function wants to act as). Click the pencil icon and add a member: the identity you found in step 1. Assign it theService Account Userrole (roles/iam.serviceAccountUser). This grants theiam.serviceAccounts.actAspermission. - Check resource-level roles. If you're accessing a specific bucket, BigQuery dataset, or Cloud SQL instance, the role might need to be applied at the resource level. For example, to grant a service account read access to a bucket:
Don't rely on project-level roles for everything — they don't always cascade to sub-resources with uniform access policies.gcloud storage buckets add-iam-policy-binding gs://your-bucket-name --member='serviceAccount:your-sa@project.iam.gserviceaccount.com' --role='roles/storage.objectViewer' - If using Cloud Build or CI/CD, grant the Cloud Build service account the necessary roles. By default, Cloud Build uses
@cloudbuild.gserviceaccount.com. It needsService Account Useron any service account it deploys or runs as. Also grant itroles/iam.roleViewerso it can list bindings.
Still Stuck? Check These
If you've done all that and it's still failing, two things: first, verify you're using the right identity. Double-check the service account email in the error — copy-paste it. I've wasted hours because of a typo in the email. Second, check for organization policies. Your org might have a constraint like iam.disableServiceAccountCreation or iam.allowedPolicyMemberDomains that blocks binding external users. Run gcloud resource-manager org-policies list --organization=ORG_ID to see active constraints. If a policy is blocking, you'll need to request an exception from your admin.
One last thing: sometimes the fix takes a minute to propagate. IAM changes are eventually consistent. Wait 60 seconds and retry. It's not instant. If you're automating, add a small delay before the next step.
Was this solution helpful?