Skip to main content

Choosing the Right Agent Identity: Managed Identity, Entra Agent ID, OBO, or Service Principal

Four identity badges on a decision fork — managed identity, Entra Agent ID, on-behalf-of, and service principal — with an agent choosing a path

The first design decision
#

A regional insurer stands up a shared claims agent so adjusters can ask plain-English questions about loss history against the claims data in Snowflake. Think show me the open first-notice-of-loss claims over $50,000 filed in the Northeast this quarter.

A newly hired adjuster, deliberately scoped to a single line of business, asks it for a fraud-pattern summary. The agent hands back claim details and policyholder data the adjuster could never have opened directly. No misconfiguration, no policy violation.

The root cause was one decision made months earlier. The agent runs under its own application identity, so the adjuster’s restrictions never applied, and the audit trail blames the agent, not the person who asked.

That is the failure mode the identity decision exists to prevent, and it is not hypothetical. A documented 2026 incident at another firm unfolded exactly this way: a shared agent returning data a limited-permission employee could never have reached on their own.

It is the first decision you make about an agent, before a line of orchestration code. Get it right and least privilege, attribution, and revocation come almost for free; get it wrong and you build a confused deputy into the foundation.

Azure gives you four identities to choose from: a managed identity, a Microsoft Entra Agent ID, an on-behalf-of flow, or a classic service principal. This piece is about how to choose, not how to code all four. It builds on the Zero-Secrets Azure series, which established keyless as the credential foundation. Here we answer which identity sits on top.

What you’ll learn
#

  • The two questions that put almost any agent onto one of four identities.
  • When a managed identity is enough, and when you need Entra Agent ID’s governance layer on top of it.
  • Why on-behalf-of is the only correct choice the moment a specific user’s data is in play.
  • Where a classic service principal still fits, and how to keep it keyless.
  • How the attended-versus-unattended token flow decides whether an agent can exceed the requesting user’s access.

Two questions decide it
#

Almost every agent resolves to one of the four on two axes, and Microsoft’s own guidance is organized around exactly these:

  1. Is the agent acting as itself, or on behalf of a user? A background summarizer acts as itself. An agent reading your mailbox acts as you.
  2. What is it reaching: Azure resources (governed by RBAC) or Microsoft Graph / M365 data (governed by delegated or application permissions)?
flowchart TB classDef q fill:#546e7a,stroke:#37474f,color:#fff classDef mi fill:#1565c0,stroke:#0d47a1,color:#fff classDef obo fill:#2e7d32,stroke:#1b5e20,color:#fff classDef sp fill:#c62828,stroke:#8e0000,color:#fff classDef agent fill:#00695c,stroke:#004d40,color:#fff Q1{Acting as itself
or for a specific user?}:::q Q1 -->|for a user| OBO[On-behalf-of
delegated permissions]:::obo Q1 -->|as itself| Q2{Runs on Azure compute?}:::q Q2 -->|no / external IdP| SP[Service principal
federated credential first]:::sp Q2 -->|yes| Q3{Needs agent governance?
sponsorship · CA · kill-switch}:::q Q3 -->|resource access only| MI[Managed identity
user-assigned]:::mi Q3 -->|yes| AGENT[Entra Agent ID
blueprint + agent identity]:::agent

Two questions, four answers. “For a user” branches to on-behalf-of; “as itself” splits on whether the workload runs on Azure and whether it needs agent-specific governance.

The decision matrix
#

Read this first; the sections below are the detail behind each row.

The agent…UseWhy
reaches Azure resources as itself (Blob, Cosmos, AI Search, Key Vault)Managed identity (user-assigned)Keyless, RBAC-scoped, Azure-native: the everyday default
needs governance: sponsorship, Conditional Access, a kill switch, agent-aware auditEntra Agent ID (blueprint + agent identity)A first-class agent identity; use a federated managed identity as its credential
acts on a specific user’s mail, files, or calendarOn-behalf-of (delegated permissions)The agent inherits the user’s permission boundary and cannot exceed it
runs outside Azure, uses an external IdP, or a legacy tool needs a client ID + secretService principal (federated credential → certificate → secret, in that order)The fallback when managed-identity or Agent-ID federation isn’t possible

There is no single Microsoft page that compares all four. This matrix is assembled from the agent-identity architecture and authorization guidance, not copied from one table.

Managed identity: the everyday default
#

When the agent acts as itself against Azure resources, a managed identity is the answer. There is no secret to store, the platform rotates the credential, and access is scoped by RBAC. Microsoft’s guidance is blunt: for Azure-to-Azure authentication, “managed identities and the DefaultAzureCredential class are the recommended option.”

Prefer user-assigned. A system-assigned identity is bound one-to-one to a resource. A user-assigned identity is created once and shared across the compute that needs it, and its roles can exist before the compute does, which is what an autoscaling agent fleet needs. It also sidesteps the Entra object-creation rate limit that rapid system-assigned creation can hit. Microsoft calls user-assigned the recommended type for most scenarios.

// The current, documented pattern for pinning a user-assigned identity.
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
    ManagedIdentityClientId = "<user-assigned-client-id>"
});

Watch out: the older new ManagedIdentityCredential(clientId) string constructor (the one most existing tutorials use) is now [Obsolete]. Use DefaultAzureCredentialOptions.ManagedIdentityClientId or ManagedIdentityId.FromUserAssignedClientId(...).

The per-resource RBAC mechanics (which role for Blob, Cosmos, AI Search, and the rest) are the whole subject of the keyless cookbook.

Entra Agent ID: governance, not just a token
#

A managed identity gets an agent a token. It does not, on its own, give you an accountable owner, a Conditional Access boundary, agent-aware audit entries, or a single switch to shut a misbehaving agent off. That is what Microsoft Entra Agent ID (GA in 2026) adds: a purpose-built identity model for agents.

Its object model has four parts:

  • Agent identity blueprint: the template that holds credentials and applies policy.
  • Blueprint principal: the runtime service principal that acquires tokens on the blueprint’s behalf.
  • Agent identity: a service-principal subtype with no credentials of its own.
  • Agent user account (optional): a 1:1 paired user object, used only when a resource strictly requires one, like a mailbox or a Teams channel.

Microsoft is explicit that classic service principals are “not recommended for agent workloads” precisely because they lack this governance layer.

What you get over a plain managed identity:

  • Enforced sponsorship: every blueprint has an accountable human or group owner, assigned at creation.
  • Conditional Access for agents: agents can’t satisfy MFA, so they’re targeted by agent-specific policies, not human ones.
  • Identity Protection: risky-agent detection for unfamiliar resource access, sign-in spikes, and suspicious credential use.
  • A kill switch: disabling a blueprint instantly blocks every agent identity minted from it.

The credential lives on the blueprint, never on the agent identity. Trying to add a secret to an agent identity fails outright. In production the blueprint’s credential should be a federated managed identity, so there is no stored secret anywhere in the chain.

A note for your code: Entra Agent ID is not a new TokenCredential class you new up. It’s a configuration and governance layer. The runtime uses the Microsoft Entra Auth SDK (a sidecar HTTP surface) or Microsoft.Identity.Web config with a managed-identity-backed assertion. Don’t go looking for an AgentIdentityCredential; there isn’t one.

Use Agent ID when agents are becoming an estate you must govern, not a single script. Practitioners are candid that it takes real setup (inventory, a blueprint strategy, baseline policy) before it pays off. Several of its adjacent controls are still preview or licensing-gated, so check what’s current before you commit hard to a preview feature.

On-behalf-of: when the agent acts as a user
#

The moment an agent touches a specific person’s data (their mail, files, calendar) it must act with that person’s delegated permission, not its own. That is the on-behalf-of flow: the user signs in, the app passes their token, and it’s exchanged for a downstream token carrying the user’s own scope. The agent can now only reach what the user already could.

The distinction is the whole point of the claims-agent scenario above. A delegated Mail.Read grant lets an agent read only the signed-in user’s mailbox. An application Mail.Read grant lets it (or anyone holding its credential) read every mailbox in the tenant. Microsoft’s rule is one line worth pinning up: “avoid granting app permissions when delegated permissions would suffice.”

OBO carries a governance bonus too: risky activity is attributed to the user, so remediation targets that session rather than disabling the agent for everyone.

The guarantee is structural, not just policy. An on-behalf-of token grants the agent the intersection of what the app was permitted and what the user can already do. In Microsoft’s words, the app “can’t access anything the signed-in user couldn’t access.” It holds at the protocol level: an OBO token carries only delegated scopes, never the agent’s own application roles, so even an agent that separately holds broad app permissions cannot use them on a user’s request. The new adjuster in the opening could only ever have seen their own book of business.

// Attended agent: exchange the signed-in user's token for a downstream one
// that carries the user's own delegated permissions.
var credential = new OnBehalfOfCredential(
    tenantId, clientId, clientSecretOrCert, userAssertion);

Use OBO for interactive, user-facing agents; use an app identity (a managed identity where possible) for autonomous, background work. Some agents do both and pick the token per operation: a nightly sync under their own identity, user chats on behalf of the user.

Service principal: the last resort
#

A classic app registration is the option to reach for last. Microsoft’s app-registration best practices rank credentials plainly: use a managed identity if you can; if the workload runs off-Azure on a platform that supports it, use a federated credential; if not, a certificate; and only as a last resort, a client secret.

That means a service principal still has real uses: a workload running outside Azure with no federation option, an external-IdP integration, or a legacy tool or SaaS connector that only understands a client ID and secret. When you do need one, you can still make it keyless. Configure a federated credential on the app registration itself, trusting a managed identity, so there’s no stored secret.

Watch out: Conditional Access does not apply to service-principal sign-ins. An app registration authenticating with a secret gets no MFA and no CA enforcement at all, a blind spot many teams only discover in an audit. It’s a strong reason to move agent workloads onto managed identity or Agent ID, both of which can be governed.

The four, side by side
#

One caveat before the table, because it prevents a category error. The four don’t sit at the same level:

  • Managed identity and service principal are credential-bearing identities: objects that mint tokens in their own name.
  • On-behalf-of is a runtime flow, not an identity: it borrows whatever credential its host already holds to produce a token that represents the user. There is no “OBO object” to point at, disable, or audit on its own.
  • Entra Agent ID is a governance model: layered on a service-principal-shaped object that natively uses both of the other patterns.

Read the table as how they differ in practice, not four interchangeable options.

Managed identityEntra Agent IDOn-behalf-ofService principal
What it isCredential-bearing identityGoverned identity model (blueprint + agent identity)A runtime token-exchange flowCredential-bearing identity
Acts asItselfItself or the user (dual-mode)The user (agent is the actor)Itself
CredentialPlatform-managed, auto-rotated, never exposedOn the blueprint only; prefer a federated managed identityBorrows the host’s credentialDev-managed: federated → certificate → secret
GovernanceRBAC scope only; not covered by Conditional Access for workload identitiesSponsorship, agent-specific Conditional Access, risky-agent detection, blueprint kill switchBound to the user’s CA and consent, evaluated at the downstream callCA for workload identities only if single-tenant + Workload ID Premium; no agent-aware audit
RunsAzure onlyAzure or external (federated)Wherever the host runsAzure or non-Azure
WhenAs itself against Azure resourcesAn estate of agents you must governStrictly within one user’s own accessOff-Azure, external IdP, or legacy tooling

How an agent actually gets a token
#

Which identity you pick decides who the agent is. A second question decides how it authenticates and whose access it carries, and this is where “the agent can only reach what the user can” is won or lost. There are two operating modes, and one of them has two steps:

  • Unattended, no user present. The agent authenticates as itself with the client-credentials flow. The token represents the agent, carries application permissions or RBAC, and reaches whatever the agent was granted. Background jobs, scheduled scoring, batch work.
  • Attended, a user is present. The user first signs in through an interactive front door, a separate client app, because an agent identity can’t perform the sign-in leg itself. That produces the user’s token, which the agent then exchanges via on-behalf-of for a downstream token carrying the user’s delegated permissions. “Interactive” is the front door; “on-behalf-of” is the exchange behind it: two hops of one attended flow, not two competing choices. Foundry just calls the pair attended.
flowchart TB classDef core fill:#1565c0,stroke:#0d47a1,color:#fff classDef obo fill:#2e7d32,stroke:#1b5e20,color:#fff classDef front fill:#00695c,stroke:#004d40,color:#fff classDef infra fill:#546e7a,stroke:#37474f,color:#fff subgraph UN[Unattended — no user present] direction LR AG1[Agent
its own identity]:::core -->|client credentials| R1[(Resource
app permissions / RBAC)]:::infra end subgraph AT[Attended — a user is present] direction LR U[User]:::front -->|1 · signs in
front-door client app| CA[Client app]:::front CA -->|2 · passes user token| AG2[Agent]:::core AG2 -->|3 · on-behalf-of exchange| R2[(Resource
only what the user can access)]:::obo end

Unattended is one hop: the agent as itself. Attended is two: an interactive sign-in that establishes who the user is, then an on-behalf-of exchange that binds every downstream call to that user’s own access.

WorkflowToken representsPermissionsWho consentsBlast radius
Client credentials (unattended)The agentApplication / RBACAdminWhatever the agent holds, potentially broad
Interactive front door (attended, step 1)The userEstablishes who the user isUser or adminn/a
On-behalf-of (attended, step 2)The user, agent as actorDelegated, per the userUser or adminOnly what that user can already reach

The practical payoff is the one the opening turned on: put the claims agent on the attended path, and the on-behalf-of exchange binds every query to the requesting adjuster’s own access. The newcomer sees their book of business and nothing more, and the audit trail names the adjuster, not the agent.

How Foundry chooses for you
#

If you build on Azure AI Foundry Agent Service, much of this is provisioned automatically. The automatic behavior has one sharp edge worth knowing.

Every Foundry project gets a system-assigned managed identity plus a default agent identity blueprint. All unpublished, in-development agents in a project share one common agent identity, convenient for iterating without repeated permission setup. But publishing an agent mints a new, dedicated blueprint and agent identity, and the shared project identity’s role assignments do not carry over. You have to re-grant them to the published agent’s identity.

flowchart LR classDef core fill:#1565c0,stroke:#0d47a1,color:#fff classDef infra fill:#546e7a,stroke:#37474f,color:#fff classDef ok fill:#2e7d32,stroke:#1b5e20,color:#fff PROJ[Foundry project
system-assigned MI]:::infra --> SHARED[Default blueprint +
ONE shared agent identity]:::core SHARED --> A1[Unpublished agent A]:::core SHARED --> A2[Unpublished agent B]:::core A1 -->|publish| DED[Dedicated blueprint +
agent identity
re-grant roles here]:::ok

Foundry shares one identity across a project’s unpublished agents, then splits off a dedicated identity on publish. The blast-radius win comes with a chore: role assignments don’t follow, so re-check them after every publish.

The whole chain stays keyless: the blueprint federates against the project’s managed identity, so no client secret is ever stored. The same principle the Zero-Secrets series applies everywhere else.

Rank your credentials
#

Whichever identity you pick, the credential under it should be as high on this list as your hosting allows:

Credential preference (best to worst): workload-identity federation (no stored secret, tokens auto-rotate) → certificate in Key Vault → certificate in a Kubernetes secret → client secret (long-lived, high-maintenance, the last resort).

This is not a style preference. Over-privileged, statically-credentialed agents are a measured risk: one 2026 survey found organizations that grant agents excessive access see roughly 4.5× higher incident rates. The identity you choose sets the ceiling on how much damage a compromised or confused agent can do.

Key takeaways
#

  • The identity decision comes before any orchestration code, and it sets the ceiling on how much damage a confused or compromised agent can do.
  • Managed identity (user-assigned) is the everyday default for an agent acting as itself against Azure resources. Reach for it first.
  • The moment a specific user’s data is in play, on-behalf-of is the only correct answer. The OBO token carries delegated scopes only, so the agent can never exceed that user’s access.
  • Entra Agent ID buys governance (sponsorship, agent-aware Conditional Access, a blueprint kill switch), not a new credential type. Adopt it when you run an estate, not a single script.
  • A service principal is the last resort, and it’s a Conditional Access blind spot. If you must use one, keep it keyless with a federated credential.

Where to start
#

Come back to the two questions. Is the agent acting as itself or for a user? Is it reaching Azure resources or Graph/M365? Those answers put almost any agent onto one of the four identities: a managed identity for the everyday as-itself case, Entra Agent ID when you need to govern an estate of them, on-behalf-of whenever a specific user’s data is in play, and a service principal only when nothing else fits, kept keyless with a federated credential.

Two threads continue from here. The credential foundation under all of this is the Zero-Secrets Azure series (the cookbook has the per-resource RBAC), and when these agents call models and tools through a gateway, the AI gateway in Azure API Management is where the same managed-identity principle governs the ingress. Federating an agent’s identity on AKS, and scoping Conditional Access through the on-behalf-of chain, are each their own piece for a later day.

Related

Microsoft Foundry's Production-Agent Release: A Solution Architect's Read

·
Microsoft’s Build 2026 announcement, Frontier models and production agents advancing Microsoft Foundry for the agentic era, reframes Foundry from a model catalog into a place you run production agents. The headline that makes it real: hosted agents in Foundry Agent Service are now generally available, and the catalog spans both OpenAI and Anthropic frontier models (1,900+ on Microsoft Learn, not the “11,000” some coverage repeats).