Back to Blogs
#ai#discuss#agents#googlecloud

Your AI Agent Just Got a Passport: Agent Identity on Google Cloud and the Redeploy Trap Nobody Talks About

Why your agents need their own identity Most teams run every AI agent under one shared...

8 min read
Cover for Your AI Agent Just Got a Passport: Agent Identity on Google Cloud and the Redeploy Trap Nobody Talks About

Why your agents need their own identity

Most teams run every AI agent under one shared service account. That works until an agent does something it should not, and the audit log only says "the service account did it." You cannot tell which agent acted, and you cannot cut off one agent without breaking the rest.

Google Cloud now has a fix for this called Agent Identity, part of the Gemini Enterprise Agent Platform (the new name for Vertex AI). Each agent gets its own verifiable identity, so you can grant, deny and audit access per agent, just like you do for people.

In this post I walk through how it works, how to deploy an ADK agent with it, and one production trap I think every SRE should know about before they wire this into a CI/CD pipeline: every redeploy gives your agent a brand new identity, and its old permissions do not follow it.

What Agent Identity actually is

Think of it as a passport for each agent. When you deploy an agent to Agent Runtime with this feature turned on, Google Cloud gives it a unique identity based on the open SPIFFE standard, plus an X.509 certificate that Google creates and manages for you.

In IAM, that identity shows up as a principal you can bind roles to:

principal://agents.global.org-ORGANIZATION_ID.system.id.goog/resources/aiplatform/projects/PROJECT_NUMBER/locations/LOCATION/reasoningEngines/AGENT_ENGINE_ID
Enter fullscreen mode Exit fullscreen mode

A few details worth knowing:

  • The trust domain is created for you when you enable the Agent Platform API. With an organization it lives at the org level (agents.global.org-...). Without one it lives at the project level (agents.global.proj-...).
  • The identity is tied to the agent's resource ID, not to the framework you built it with. ADK, LangGraph and others all work the same way.
  • Out of the box the agent gets two default roles, roles/aiplatform.agentContextEditor and roles/aiplatform.agentDefaultAccess, so it can reach its own logs, metrics, model access, sessions and memory.
  • The part I like most as an SRE: tokens are locked to the runtime by a Google-managed Context-Aware Access policy using mTLS certificate binding. If someone steals the token and tries to use it from outside the agent's runtime, it fails with a 401. Stolen credentials cannot be replayed.

One gotcha: in IAM bindings you must use the principal:// format, not the raw spiffe:// string. Using the SPIFFE string returns an INVALID_ARGUMENT error.

Hands-on: deploy an ADK agent with its own identity

You have three ways to turn it on. Pick the one that fits how you work.

Option 1: Python SDK. Define a normal ADK agent, wrap it, and pass identity_type when you deploy:

from google.adk.agents import Agent
import agentplatform
from agentplatform import types
from agentplatform.frameworks import AdkApp

agent = Agent(
    model="gemini-2.5-flash",
    name="minimal_agent",
    instruction="You are a helpful assistant.",
)

client = agentplatform.Client(
    project=PROJECT_ID,
    location=LOCATION,
    http_options=dict(api_version="v1beta1"),
)

remote_app = client.runtimes.create(
    agent=AdkApp(agent=agent),
    config={
        "display_name": "running-agent-with-identity",
        "identity_type": types.IdentityType.AGENT_IDENTITY,
        "requirements": ["google-cloud-agentplatform[adk,runtimes]"],
        "staging_bucket": f"gs://{BUCKET_NAME}",
    },
)

print(remote_app.api_resource.spec.effective_identity)
Enter fullscreen mode Exit fullscreen mode

Note the v1beta1 API version. Agent Identity needs it.

Option 2: adk deploy. Drop a small config file in your agent folder before deploying:

echo '{ "identity_type": "AGENT_IDENTITY" }' > .agent_engine_config.json
Enter fullscreen mode Exit fullscreen mode

Option 3: Agents CLI. Good for quick tests and prototypes:

agents-cli deploy --agent-identity
Enter fullscreen mode Exit fullscreen mode

A tip I recommend: you can create the Agent Runtime instance with only identity_type and no code. That gives you the identity first, so you can set up IAM before the agent ever runs. Then add the code later with client.runtimes.update(...).

If you skip the identity flag, the agent keeps using service accounts as before, so existing pipelines do not break.

Least privilege in two layers

The pattern that works best is to split permissions into a shared floor and a narrow ceiling.

Layer 1: a baseline for every agent in the project. Use a principalSet so one binding covers all agents, now and in the future. Put only boring, low-risk roles here:

AGENTS="principalSet://agents.global.org-ORGANIZATION_ID.system.id.goog/attribute.platformContainer/aiplatform/projects/PROJECT_NUMBER"

for ROLE in roles/serviceusage.serviceUsageConsumer roles/browser \
            roles/aiplatform.expressUser roles/logging.logWriter \
            roles/monitoring.metricWriter; do
  gcloud projects add-iam-policy-binding PROJECT_ID \
    --member="$AGENTS" --role="$ROLE"
done
Enter fullscreen mode Exit fullscreen mode

Layer 2: narrow grants per agent. Anything sensitive, like a BigQuery dataset or a Cloud Storage bucket with customer data, goes to one specific agent's principal:// only:

gcloud storage buckets add-iam-policy-binding gs://orders-data \
  --member="principal://agents.global.org-ORGANIZATION_ID.system.id.goog/resources/aiplatform/projects/PROJECT_NUMBER/locations/us-central1/reasoningEngines/AGENT_ENGINE_ID" \
  --role="roles/storage.objectViewer"
Enter fullscreen mode Exit fullscreen mode

Multi-agent bonus (A2A). If a parent agent calls a subagent registered in Agent Registry, the parent agent's identity needs two roles: roles/agentregistry.viewer to look the subagent up, and roles/aiplatform.user on the subagent's reasoning engine to call it. Grant these to the parent agent, not to your own user account. This is an easy mistake that shows up as confusing permission errors.

The redeploy trap nobody talks about

This is the part that will bite SRE and platform teams. Every time a pipeline deletes and recreates an agent, the agent gets a new reasoningEngines ID, which means a brand new principal. Permissions granted to the old principal do not carry over.

Picture a normal day:

  1. Your agent runs fine with read access to gs://orders-data.
  2. A blue-green deploy, an ephemeral test environment or a terraform apply with a replace recreates the agent.
  3. The new agent starts, passes its health check, and then quietly gets 403 errors the first time it touches the bucket.

No deploy failure, no alert, just an agent that suddenly "can't find" data. That is a silent outage.

It gets worse in the other direction. Deleting an agent does not remove the IAM bindings that point to it. Those dead bindings stay in your policies until someone cleans them up by hand. Over time that is policy clutter and an audit headache.

How to fix it

Fix 1: grant access as a post-deploy step. Read the new identity right after deploy and bind roles to it:

IDENTITY=$(gcloud ai reasoning-engines describe RESOURCE_ID \
  --project=PROJECT_ID --location=LOCATION \
  --format="value(spec.effectiveIdentity)")

gcloud storage buckets add-iam-policy-binding gs://orders-data \
  --member="principal://${IDENTITY}" \
  --role="roles/storage.objectViewer"
Enter fullscreen mode Exit fullscreen mode

Fix 2: let Terraform build the principal for you. Derive it from the deployed resource so bindings are always recreated with the agent:

resource "google_vertex_ai_reasoning_engine" "agent" {
  provider     = google-beta
  project      = var.project_id
  region       = var.region
  display_name = "orders-agent"

  spec {
    agent_framework = "google-adk"
    identity_type   = "AGENT_IDENTITY"
    # source_code_spec and deployment_spec here
  }
}

locals {
  agent_id        = element(split("/", google_vertex_ai_reasoning_engine.agent.id), -1)
  agent_principal = "principal://agents.global.org-${data.google_organization.org.org_id}.system.id.goog/resources/aiplatform/projects/${data.google_project.project.number}/locations/${var.region}/reasoningEngines/${local.agent_id}"
}

resource "google_storage_bucket_iam_member" "orders_reader" {
  bucket = "orders-data"
  role   = "roles/storage.objectViewer"
  member = local.agent_principal
}
Enter fullscreen mode Exit fullscreen mode

Because the binding depends on the agent resource, Terraform replaces both together, and destroying the agent removes its binding too. That also solves the orphaned binding problem.

Fix 3: lean on the baseline layer. Project-level principalSet bindings survive delete and recreate, so the agent still has quota, logging and model access right after a redeploy. Keep per-agent bindings only for sensitive data.

Fix 4: add a smoke test. After deploy, have the pipeline send one real request that touches each protected resource. A 403 here fails the pipeline instead of failing in front of users.

Guardrails: what agents must never do

Allow policies say what an agent can do. For agents that act on their own, you also want hard limits that no allow policy can override. Agent Identity works with two IAM tools for this.

IAM deny policies. Block specific permissions for all agent identities across the org, no matter what roles someone grants later. Good candidates are destructive actions like deleting buckets or creating IAM roles. The official docs include a ready deny policy example you can adapt (link in Sources).

Principal Access Boundary (PAB). This limits where an agent can operate at all. For example, you can say agents in your org may only touch resources inside one folder. Even if someone accidentally grants an agent a role on a production project outside that folder, PAB blocks it.

My rule of thumb: allow policies are for daily work, deny policies are for "never, ever", and PAB is the fence around the whole yard. Agent Identity also plugs into VPC Service Controls if you need a network-level perimeter on top.

Audit: finally knowing which agent did what

With Cloud Logging turned on, every access shows the agent's own identity instead of a shared service account. Two cases matter:

  • When the agent acts on its own, the log shows only the agent's identity.
  • When the agent acts on behalf of a user, the log shows both the agent and the user.

That second case is a big deal for compliance reviews. You can answer "which agent read this data, and for whom?" without guessing.

To see all your agents and their identities in one place, open the Agent Platform Deployments page in the console and look at the Identity column, or read spec.effectiveIdentity from the API.

Limitations and caveats

Be honest with your team about these before you roll it out:

  • It needs the v1beta1 API version, so treat it as a moving target and check the docs before each upgrade.
  • Agent identities cannot be granted the legacy Cloud Storage bucket roles (legacyBucketReader, legacyBucketWriter, legacyBucketOwner). If your buckets still depend on those, plan a cleanup.
  • The redeploy trap above is by design, not a bug. Your pipeline has to handle it.
  • Deleted agents leave orphaned IAM bindings unless your tooling removes them.
  • You can turn off the Context-Aware Access token binding for special token-sharing cases, but Google strongly discourages it because it reopens the door to credential theft. I would not.
  • Identity controls what an agent can reach, not what it decides to do. A prompt injection can still trick an agent into misusing access it legitimately has. Pair this with content safety controls like Model Armor and keep a human in the loop for risky actions.

Wrap-up: a production checklist

Agent Identity turns agents into first-class citizens in IAM. The feature is easy to turn on. The real work is in the pipeline around it. Here is the checklist I would use:

  • [ ] Deploy every agent with identity_type = AGENT_IDENTITY
  • [ ] Put low-risk shared roles on a project principalSet
  • [ ] Grant sensitive data access per agent only
  • [ ] Rebind per-agent roles automatically after every redeploy (Terraform or a post-deploy step)
  • [ ] Clean up IAM bindings when an agent is deleted
  • [ ] Add a post-deploy smoke test that touches each protected resource
  • [ ] Add deny policies for destructive permissions
  • [ ] Fence agents in with Principal Access Boundary
  • [ ] Turn on Cloud Logging so every action is traceable to one agent

If you are running agents in production on Google Cloud, I would love to hear how you are handling identity today. Shared service accounts, or something better?

Sources