Skip to content

IAM Roles for Vertex AI: What Each One Actually Grants

9 min read · updated August 11, 2026

Role names are marketing for permissions. The only reliable way to know what a Vertex AI role grants is to list the permission strings inside it, and doing that turns up at least one thing about these roles that the names actively mislead you about.

How to read a role, rather than its description

Predefined roles are bundles of permissions, and the bundle changes. Google publishes permission-change notices for exactly this reason, and a role that was safe when you granted it can acquire permissions later without any action from you. So the operation worth learning is not “which role should I use” but “what does this role contain today”:

gcloud iam roles describe roles/aiplatform.user \
  --format='value(includedPermissions)' | tr ';' '\n' | sort

# how many, which is itself informative
gcloud iam roles describe roles/aiplatform.user \
  --format='value(includedPermissions)' | tr ';' '\n' | wc -l

Run that against the role you are about to grant, every time, and read the output. Vertex AI permissions follow a legible pattern — aiplatform.RESOURCE.VERB, as in aiplatform.endpoints.predict, aiplatform.models.upload, aiplatform.customJobs.create — so scanning for verbs like create, delete and deploy across resources you did not expect takes about a minute and is the whole review.

Everything below is about the roles as documented at the time of writing. Predefined role contents are revised, and the describe command above is the authority for your project on the day you run it.

roles/aiplatform.user, and why it is broad

roles/aiplatform.user is the role every quickstart tells you to grant, and for good reason: it contains aiplatform.endpoints.predict, which is the permission required to send a prompt, and it works for essentially everything a developer wants to do.

What the name does not convey is the range. “User” sounds like a consumer role, but the bundle covers creating and running training jobs, uploading models, deploying models to endpoints, managing datasets and creating index endpoints. In cost terms that is the difference between a principal that can spend tokens and a principal that can start a multi-GPU training job.

For a human developer in a development project, that breadth is appropriate and fighting it wastes everyone’s time. For a production service account whose entire purpose is to call one model, it is far more than the job needs, and it is the account most likely to have its credential end up somewhere it should not.

roles/aiplatform.viewer is not read-only

This is the finding that justifies reading permission lists rather than names. roles/aiplatform.viewer reads as the safe look-but-do-not-touch role, and it has been documented as including aiplatform.endpoints.predict and aiplatform.endpoints.explain alongside the get and list permissions.

If that holds in your project — check with the describe command; this is exactly the sort of thing that changes — then “viewer” is a spending role. Granting it broadly to an analyst group so they can see what exists also lets every member of that group invoke every deployed model in the project, including a dedicated endpoint whose per-call cost is measured in node-hours.

The reasoning behind it is defensible: prediction is a read of the model in the sense that it does not modify any resource. But “does not modify state” and “does not cost money” are different properties, and IAM’s viewer convention was built around the first.

The narrowest role for an inference-only caller

For a service that only calls a model, a custom role containing exactly one permission is the correct answer, and it is unusually easy to build because there is genuinely only one permission involved.

# inference-caller.yaml
title: "Vertex AI inference caller"
description: "Invoke deployed endpoints and nothing else"
stage: "GA"
includedPermissions:
- aiplatform.endpoints.predict
gcloud iam roles create vertexInferenceCaller \
  --project=PROJECT_ID \
  --file=inference-caller.yaml

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:svc-inference@PROJECT_ID.iam.gserviceaccount.com" \
  --role="projects/PROJECT_ID/roles/vertexInferenceCaller"

Bind it at the narrowest resource that works. A binding on a single endpoint rather than on the project is strictly better where your workload allows it:

gcloud ai endpoints add-iam-policy-binding ENDPOINT_ID \
  --region=us-central1 \
  --member="serviceAccount:svc-inference@PROJECT_ID.iam.gserviceaccount.com" \
  --role="projects/PROJECT_ID/roles/vertexInferenceCaller"

Two limits on how far this goes. Some Vertex AI surfaces need additional permissions — batch prediction reads and writes Cloud Storage or BigQuery, so it needs those roles as well as this one, and streaming or tuning surfaces have their own permission strings. And a custom role is yours to maintain: when Google adds a permission that a new API version requires, a predefined role gains it automatically and your custom role does not. That is the cost of the precision, and it is usually worth paying for a production service account and not worth paying for a developer.

The roles that are not aiplatform roles

Most real Vertex AI permission failures are not Vertex AI permissions. They are one of a small set of adjacent roles that the task needs and that nobody thinks to check because the error arrives from an aiplatform API call.

  • roles/iam.serviceAccountUser — needed by the principal that deploys something which runs as a service account. Deploying a Cloud Run service, a function or a training job with --service-account is an act-as operation, and without this role the deploy fails with a message about not having permission to act as the service account. It is granted on the service account, not on the project, and doing it that way is meaningfully narrower.
  • roles/storage.objectUser — batch prediction reads inputs and writes outputs to Cloud Storage, and training reads data from it. The identity that needs this is often the Vertex AI service agent rather than the submitter, which is the next section.
  • roles/bigquery.dataViewer with roles/bigquery.jobUser — the pair required when the source or destination is BigQuery. dataViewer alone lets you see a table and not run the query that reads it, which produces a permission error that names the job rather than the data.
  • roles/serviceusage.serviceUsageConsumer — needed when a principal in one project calls an API billed to another. Relevant whenever a shared inference project serves several application projects, and invisible until you build exactly that.
  • roles/aiplatform.admin — the management role, which adds deletion and IAM-policy control on Vertex AI resources on top of what user can do. Grant it to whoever owns the endpoints and not to the service that calls them.

When something fails, the fastest diagnosis is not to read role descriptions but to ask IAM directly which principal is missing which permission on which resource:

gcloud projects get-iam-policy PROJECT_ID \
  --flatten="bindings[].members" \
  --filter="bindings.members:svc-inference@PROJECT_ID.iam.gserviceaccount.com" \
  --format='value(bindings.role)'

Read that against the permission named in the error rather than against your expectations. Deny policies and organisation policy constraints can also block an action that the bindings appear to allow, and those do not show up in a project policy listing at all — if the bindings look correct and the call is still refused, that is the next place to look rather than a reason to grant a broader role.

Service agents you did not create

Vertex AI operates partly through Google-managed service agents, which are accounts in your project that Google creates and grants roles to. The Vertex AI service agent is the account that reads your training data from Cloud Storage, pulls your custom container from Artifact Registry, and writes model artifacts back.

Two consequences worth carrying. When a training job or a custom container deployment fails on permissions, the identity that lacked access is often the service agent rather than the account that submitted the job, so checking your own permissions proves nothing. And an audit that enumerates “who can read this bucket” must include service agents, or it will report a bucket as tightly held while a managed identity has been reading it all along. The job-submission side of this, including which agent needs what, is in Vertex AI service account permissions.