
Why your agents need their own identity Most teams run every AI agent under one shared...
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.
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
A few details worth knowing:
agents.global.org-...). Without one it lives at the project level (agents.global.proj-...).roles/aiplatform.agentContextEditor and roles/aiplatform.agentDefaultAccess, so it can reach its own logs, metrics, model access, sessions and memory.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.
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)
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
Option 3: Agents CLI. Good for quick tests and prototypes:
agents-cli deploy --agent-identity
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.
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
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"
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.
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:
gs://orders-data.terraform apply with a replace recreates the agent.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.
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"
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
}
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.
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.
With Cloud Logging turned on, every access shows the agent's own identity instead of a shared service account. Two cases matter:
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.
Be honest with your team about these before you roll it out:
v1beta1 API version, so treat it as a moving target and check the docs before each upgrade.legacyBucketReader, legacyBucketWriter, legacyBucketOwner). If your buckets still depend on those, plan a cleanup.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:
identity_type = AGENT_IDENTITYprincipalSetIf 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?
discussTL;DR Welcome back to Dev Opportunity Radar! This is a weekly series where I share opportunities,...
devchallengeWe are so excited to announce the winners of the Hacktoberfest Open-Source AI Challenge: Touch...
googlecloudHow I designed, connected, scaled, governed, evaluated, and QLoRA fine-tuned an enterprise SecOps & FinOps agent on Google Cloud.
gemmaYes. Speculative decoding with Google's 59 MB QAT multi-token prediction (MTP) drafters speeds up Gemma 4 E4B 1.72x and E2B 1.40x on an 8 GB RTX 3050, and 1.49x and 1.18x on a 4 GB laptop GTX 1650 Ti, served by llama.cpp one request at a time, with unchanged GSM8K and BFCL scores.
nvidiaThe same Tesla T4 on the same Google Cloud VM decoded Gemma 4 E2B at 115 tokens/s at 45 °C and 107 at 77 °C. The card sits at its 70 W limit, a hotter chip draws more power at the same clock, so the clock drops. No thermal throttle fires, and no spec sheet mentions it.
oracleHow I pair-programmed with Google Antigravity powered by Gemini 4 Argon to build a zero-dependency Go TUI and OpenTelemetry/Prometheus observability stack for Oracle Databases and Linux SAN storage.
Workflows from the Neura Market marketplace related to this DeepSeek resource