IAM API role assignment fails for custom service account with insufficient permissions

I’m trying to automate service account onboarding using the IAM API but getting permission denied errors when assigning roles to custom service accounts. This is blocking our entire onboarding automation workflow.

The API call fails with:


HTTP 403: Permission denied
required permission: iam.serviceAccounts.setIamPolicy
on resource: projects/my-project/serviceAccounts/custom-sa@my-project.iam.gserviceaccount.com

I’m using a service account with roles/iam.serviceAccountAdmin which should have sufficient permissions according to the documentation. The role assignment works fine through the console UI but fails via API. I’ve verified the service account exists and I can read its details via the API, just can’t assign roles to it. Is there an organization policy that might be interfering with programmatic role assignments?

The iam.serviceAccountAdmin role should work, but there are nuances. First, check if you have any organization policies restricting service account key creation or role assignments. Also verify that your calling service account has the role at the correct scope - project level might not be enough if you’re working across projects.

I hit this same issue last year. The problem is often that serviceAccountAdmin lets you manage service accounts but not necessarily grant all roles to them. You need additional permissions depending on which roles you’re trying to assign. For example, to grant BigQuery roles, your account needs BigQuery admin permissions too. Check the specific roles you’re trying to assign.

You need to have the roles yourself at a higher scope, OR use roles/iam.securityAdmin which allows granting any role. However, securityAdmin is very powerful and should be used carefully. Another option is to create a custom role with just the setIamPolicy permission for service accounts plus the specific roles you need to grant. This follows least privilege better than using securityAdmin.

Interesting point about needing permissions for the roles being assigned. I’m trying to grant roles/compute.instanceAdmin and roles/storage.objectViewer to the custom service account. Do I need to have those exact roles myself, or is there a way to delegate this without granting myself all possible roles I might need to assign?

Let me provide a comprehensive solution covering all three critical aspects:

Role Permissions Analysis: The issue is that roles/iam.serviceAccountAdmin gives you permissions to manage service accounts (create, delete, list) but NOT to grant arbitrary roles to them. When you assign a role to a service account, you need two things:

  1. iam.serviceAccounts.setIamPolicy permission (which you have)
  2. Permission to grant the specific role you’re assigning

For your case assigning compute.instanceAdmin and storage.objectViewer:


// Your automation account needs:
roles/iam.serviceAccountAdmin  // To modify SA
roles/compute.admin  // To grant compute roles
roles/storage.admin  // To grant storage roles

Service Account Admin Best Practice: Instead of granting yourself all possible roles, use roles/iam.securityAdmin which has iam.roles.* permissions allowing you to grant any role. However, this is highly privileged. Better approach for automation:

Create a custom role:


gcloud iam roles create ServiceAccountRoleManager \
  --project=PROJECT_ID \
  --permissions=iam.serviceAccounts.setIamPolicy,iam.roles.get,resourcemanager.projects.getIamPolicy,resourcemanager.projects.setIamPolicy

Then grant your automation account this custom role PLUS the specific roles it needs to delegate (compute.admin, storage.admin in your case).

Organization Policy Check: This is often the hidden blocker. Check these constraints:

  1. List active policies:

gcloud resource-manager org-policies list --project=PROJECT_ID
  1. Check service account restrictions:

gcloud resource-manager org-policies describe \
  constraints/iam.allowedPolicyMemberDomains \
  --project=PROJECT_ID

If this constraint is set, it may block service accounts from certain domains. You might need to add your service account’s domain to the allowed list.

  1. Check if automated changes are restricted:

gcloud resource-manager org-policies describe \
  constraints/iam.disableServiceAccountKeyCreation \
  --project=PROJECT_ID

Complete Solution: For your automation service account, grant:

  • roles/iam.serviceAccountAdmin (service account management)
  • roles/iam.securityAdmin (ability to grant any role) OR specific admin roles for each service
  • Verify no org policy constraints block programmatic IAM changes

The API call should then work. The console works because you’re authenticated as a user with broader permissions, while your automation service account has restricted scope. The key is ensuring your automation account has permission to grant the specific roles you’re assigning, not just permission to modify service accounts.

Check your organization policies carefully - specifically ‘constraints/iam.allowedPolicyMemberDomains’ and ‘constraints/iam.disableServiceAccountKeyCreation’. Even if your IAM roles are correct, org policies can block programmatic changes. Use ‘gcloud resource-manager org-policies describe’ to check active constraints at org and project levels.