Sitemap
The new control plane

“Identity is the new control plane”. Articles around Microsoft Entra ID, Entra External ID and Azure AD B2C. Click the “Archive” link at the bottom for more posts.

Using the Entra ID Agentic ID sample

--

Image of agentic ID
AI generated

The repo is here.

“This repository contains samples demonstrating how to use Agent Identities in Microsoft Entra ID with .NET. Agent Identities enable AI agents to securely access downstream services using either autonomous (app-only) or user-delegated tokens”.

“Agent Identities are a new capability announced at Ignite 2025 in Microsoft Entra ID that enable AI agents to:

  • Autonomous Agent Identity — Acquire app-only tokens for operations that don’t require user context.
  • Agent User Identity — Acquire tokens with user context for operations requiring user identity (e.g., sending emails, participating in Teams channels)”

The Learn article on agent ID is here.

Essentially, you define blueprints that contain agent IDs, each with a sponsor and an owner for auditability.

In the repo, there is a /scripts directory.

.\Setup-EntraIdApps.ps1 -TenantId ae...3b -OutputFormat UpdateConfig

This produces:

========================================
Entra ID Agent Identity Blueprint Setup
Customer Service Agent Sample
========================================

Instance Prefix: CustomerServiceSample-

[INFO] Checking Microsoft Graph connection...
[INFO] Not connected to Microsoft Graph. Connecting...
[SUCCESS] Connected to tenant: ae...3b
[INFO] Current user: rory@entra-tenant.onmicrosoft.com (will be set as
SponsorUserId)

--- Step 1: Creating Agent Identity Blueprint ---

[INFO] Using specialized Agent Identity Blueprint endpoint...
[INFO] Creating Agent Identity Blueprint application:
CustomerServiceSample-Orchestrator
[INFO] Creating Agent Identity Blueprint using beta endpoint...
[SUCCESS] Successfully created Agent Identity Blueprint
[INFO] Blueprint Application ID: 2a..4d
[INFO] Blueprint App ID (Client ID): 2a..4d
[WARNING] Could not create Agent Identity Blueprint using API. Falling back
to standard application.
[WARNING] Error: The property 'scopes' cannot be found on this object.
Verify that the property exists.
[INFO] Checking for existing app: CustomerServiceSample-Orchestrator
[SUCCESS] Found existing app: CustomerServiceSample-Orchestrator
(ClientId: 2a...4d)

--- Step 5: Creating Service Principal for Blueprint ---

[INFO] Creating service principal for blueprint...
Making request to create service principal for Agent Blueprint:
System.Collections.Hashtable.Orchestrator.ClientId
[SUCCESS] Service principal created successfully

--- Step 2: Adding Client Secret to Blueprint ---


--- Step 3: Creating Downstream Service Applications ---

[INFO] Processing service: CustomerServiceSample-OrderAPI
[INFO] Checking for existing app: CustomerServiceSample-OrderAPI
[INFO] Creating new app: CustomerServiceSample-OrderAPI
[SUCCESS] Created app: CustomerServiceSample-OrderAPI
(ClientId: 18...73)
[INFO] Configuring API scopes...
[INFO] Setting Application ID URI: api://18...73
[SUCCESS] Adding scope: Orders.Read
[INFO] Updating OAuth2 permission scopes...
[SUCCESS] API scopes configured successfully
[INFO] Configuring app roles...
[SUCCESS] Adding app role: Orders.Read.All
[INFO] Updating app roles...
[SUCCESS] App roles configured successfully
[SUCCESS] Service CustomerServiceSample-OrderAPI configured successfully

[INFO] Processing service: CustomerServiceSample-ShippingAPI
[INFO] Checking for existing app: CustomerServiceSample-ShippingAPI
[INFO] Creating new app: CustomerServiceSample-ShippingAPI
[SUCCESS] Created app: CustomerServiceSample-ShippingAPI
(ClientId: b2...7f)
[INFO] Configuring API scopes...
[INFO] Setting Application ID URI: api://b2...7f
[SUCCESS] Adding scope: Shipping.Read
[SUCCESS] Adding scope: Shipping.Write
[INFO] Updating OAuth2 permission scopes...
[SUCCESS] API scopes configured successfully
[SUCCESS] Service CustomerServiceSample-ShippingAPI configured successfully

[INFO] Processing service: CustomerServiceSample-EmailAPI
[INFO] Checking for existing app: CustomerServiceSample-EmailAPI
[INFO] Creating new app: CustomerServiceSample-EmailAPI
[SUCCESS] Created app: CustomerServiceSample-EmailAPI
(ClientId: 35...65)
[INFO] Configuring API scopes...
[INFO] Setting Application ID URI: api://35...65
[SUCCESS] Adding scope: Email.Send
[INFO] Updating OAuth2 permission scopes...
[SUCCESS] API scopes configured successfully
[SUCCESS] Service CustomerServiceSample-EmailAPI configured successfully


--- Step 4: Configuring Inheritable Permissions for Blueprint ---

[INFO] Setting up inheritable permissions to downstream APIs...
[INFO] Configuring inheritable permissions for Agent Identity Blueprint...
[INFO] Creating service principal for: CustomerServiceSample-OrderAPI
[INFO] Creating service principal for: CustomerServiceSample-ShippingAPI
[INFO] Creating service principal for: CustomerServiceSample-EmailAPI
Successfully added inheritable permissions to Agent Identity Blueprints
Permissions are now available for inheritance by agent blueprints

--- Step 6: Granting Admin Consent for Inheritable Permissions ---

[INFO] Granting admin consent for orchestrator app...
[SUCCESS] Admin consent granted successfully


========================================
Setup Completed Successfully!
========================================


--- Updating Configuration Files ---

[INFO] Setting SponsorUserId to current user:
rory@entra-tenant.onmicrosoft.com (5c...64)
[INFO] Detected tenant domain: entra-tenant.onmicrosoft.com
[INFO] Updating: C:\...\ms-identity-agent-identities\
dotnet\CustomerServiceAgent\src\AgentOrchestrator\appsettings.json
[SUCCESS] Updated successfully
[INFO]
Configuring client secret using User Secrets...
Successfully saved AzureAd:ClientCredentials:0:ClientSecret to the secret
store.
[SUCCESS] Successfully configured client secret in User Secrets
[INFO] The secret is stored securely outside of source control
[INFO] For production, use Azure Key Vault or environment variables
[INFO] See SECRETS-MANAGEMENT.md for detailed instructions

[INFO] Updating: C:\...\ms-identity-agent-identities\dotnet\
CustomerServiceAgent\src\AgentOrchestrator\AgentOrchestrator.http
[SUCCESS] Updated TenantName to: entra-tenant.onmicrosoft.com
[INFO] Updating: C:\...\ms-identity-agent-identities\dotnet\
CustomerServiceAgent\src\DownstreamServices\EmailService\appsettings.json
[SUCCESS] Updated successfully
[INFO] Updating: C:\...\ms-identity-agent-identities\dotnet\
CustomerServiceAgent\src\DownstreamServices\OrderService\appsettings.json
[SUCCESS] Updated successfully
[INFO] Updating: C:\...\ms-identity-agent-identities\dotnet\
CustomerServiceAgent\src\DownstreamServices\ShippingService\appsettings.json
[SUCCESS] Updated successfully
[SUCCESS]
All configuration files updated!

--- Next Steps ---

[INFO] 1. Review the configuration values above
[INFO] 5. Verify the updated appsettings.json files
[INFO] 6. Build and run the solution:
dotnet run --project src/CustomerServiceAgent.AppHost
[INFO] For detailed setup instructions, see: docs/setup/02-entra-id-setup.md
DeletedDateTime Id AppRoleId CreatedDateTime
--------------- -- --------- ---------------
6t...Bc 4a...14 25/08/2026 3:25:53 am

This creates a blueprint (under the Agents tab).

Note: This blueprint is contained under the “Agents” tab in the portal rather than the classic app registrations list view, but it is backed by a standard application object underneath.

The blueprint establishes the “kind” of agent and records metadata shared across all agent identities of a common kind.

Image of blueprint with three agent identities

The blueprint tab looks like:

Press enter or click to view image in full size
Image of blueprint orchestrator page in portal

with me as the owner and sponsor:

Press enter or click to view image in full size
Image of blueprint owner and sponsor

and the permission:

Press enter or click to view image in full size
Image of AgentIdUser.ReadWrite permission

If you click on “Credentials”, you see a client secret.

Press enter or click to view image in full size
Image of blueprint client secret

This is the secret created at the end of the script.

The secret is stored using .NET User Secrets.

To see the secrets:

PS C:\...\ms-identity-agent-identities\dotnet\CustomerServiceAgent\src\
AgentOrchestrator> dotnet user-secrets list
AzureAd:ClientCredentials:0:ClientSecret = 27...ak

In the blueprint, if you click on agent identities:

you see:

Press enter or click to view image in full size
Image of agent ID inside blueprint

and if you click on “agent identity1”:

Press enter or click to view image in full size
Image of agent identity page

you see an agent identity that points back to the blueprint.

Each agent ID has a corresponding agent user.

If you click on “View agent’s user account”, you see the agent's user account:

Press enter or click to view image in full size
Image of agent user page

If you click on “View” next to “Agent identity's access”, you see the permissions:

Press enter or click to view image in full size
Image of agent identities access permissions e.g. Email.Send, Orders.ReadAll

These correspond to the app registrations:

Press enter or click to view image in full size
Image of three app registrations — email, order and shipping

Each of these is an API that the orchestrator calls.

If you look at OrderAPI e.g., you see under “Expose an API”:

Press enter or click to view image in full size
Image of app registration — Expose and API — api://…

The script also configures the appsettings.json:

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Identity": "Debug"
}
},
"AllowedHosts": "*",
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "ae...3b",
"ClientId": "2a...4d",
"ClientCredentials": [
{
"SourceType": "ClientSecret"
}
],
"CallbackPath": "/signin-oidc"
},
"AgentIdentities": {
"AgentIdentity": "YOUR_AGENT_IDENTITY_ID",
"AgentUserId": "YOUR_AGENT_USER_ID",
"SponsorUserId": "5c...64"
},
"DownstreamApis": {
"OrderService": {
"BaseUrl": "https://localhost:7001",
"Scopes": [
"api://18...73/.default"
]
},
"ShippingService": {
"BaseUrl": "https://localhost:7003",
"Scopes": [
"api://b2...7f/.default"
]
},
"EmailService": {
"BaseUrl": "https://localhost:7004",
"Scopes": [
"api://35...65/.default"
]
},
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "User.Read Mail.Send ChannelMessage.Send"
}
}
}

So what we have is:

  • An Agent Orchestrator service that acts as the AI agent, creating an “agent blueprint,” an autonomous agent identity, and an agent user identity
  • Calls to multiple downstream APIs (Order, Shipping, Email) using either app-only or delegated tokens depending on the operation
  • .NET Aspire used for orchestration plus distributed tracing/logging, so you can visually observe how each downstream call’s identity/token flow works via the Aspire dashboard
  • In-memory stores, so it runs without external dependencies (aside from Entra ID app registrations)
  • The AgentOrchestrator.http file is used to manually exercise the flow: first call api/agentidentity to create/get an agent identity, then use that identity’s GUID to call api/customerservice/process, which triggers the orchestrator to call the downstream APIs on behalf of the agent

The .http file looks like:

Press enter or click to view image in full size
Image of API calls e.g. Send request — POST {{AgentOrchestrator_HostAddress}}/api/agentidentity?agentIdentityName=agent%20identity1&agentUserIdentityUpn={{AgentUserName}}@{{TenantName}} Accept: application/json Content-Type: application/json

“Send request” is a hyperlink that executes the request.

Get Rory Braybrook’s stories in your inbox

Join Medium for free to get updates from this writer.

So it's similar to Postman.

Run the sample. You should see the Aspire portal:

Press enter or click to view image in full size
Image of Aspire portal page

The URLs won’t work because the services are just API. There is no website.

You can use this to inspect the traces if there are issues.

In the .http file, click “Send request” for the top call:

/api/agentidentity

You should see:

{
"agentIdentity": {
"@odata.type": "#Microsoft.Graph.AgentIdentity",
"displayName": "agent identity1",
"agentIdentityBlueprintId": "2a...4d",
"id": "54...58",
"sponsors@odata.bind": null,
"owners@odata.bind": null
},
"agentUserIdentity": {
"@odata.type": "#microsoft.graph.agentUser",
"displayName": "agentuser1",
"userPrincipalName": "agentuser1@entra-tenant.onmicrosoft.com",
"id": "3f...70",
"identityParentId": "54...58",
"mailNickname": "agentuser1",
"accountEnabled": true
},
"adminConsentUrlScopes": "https://login.microsoftonline.com/
ae...3b/v2.0/adminconsent?client_id=54...58&
scope=api%3A%2F%2F...65%2FEmail.Send%20api%3A%2F%2F...73
%2FOrders.Read%20api%3A%2F%2F...7f
%2FShipping.Read%20api%3A%2F%2F...7f%2FShipping.Write
&redirect_uri=https://entra.microsoft.com/TokenAuthorize&state=xyz123",
"adminConsentUrlRoles": "https://login.microsoftonline.com/
ae...3b/v2.0/adminconsent?client_id=54...58
&role=api%3A%2F%2F...73%2FOrders.Read.All
&redirect_uri=https://entra.microsoft.com/TokenAuthorize&state=xyz123"

Now copy the agentIdentity ID (“54…58”) into the @AgentIdentity line in the .http file (as per the image above) and save.

Then in the browser, execute the two HTTPS URLs above (“adminConsentUrlScopes” and “adminConsentUrlRoles”) in separate tabs. I was logged on as a GA for these.

Then grant permission on all the screens by clicking “Allow" e.g.

Image of grant — Allow this application to access data

and:

Image of grant — Allow this agent to access data

There are a few of these.

Now click “Send request” for the second call:

/api/customerservice/process

You should see:

{
"orderId": "12345",
"status": "Completed",
"orderDetails": {
"orderId": "12345",
"customerId": "C001",
"productName": "Enterprise Software License",
"quantity": 10,
"price": 9999.99,
"status": "Processing",
"orderDate": "2026-08-23T03:34:10.3914844Z"
},
"customerHistory": null,
"deliveryInfo": {
"orderId": "12345",
"trackingNumber": "TRK-UPDATED-2E109F08",
"status": "Updated by Agent",
"carrier": "Express Delivery",
"estimatedDelivery": "2026-08-26T03:34:10.4442694Z",
"shippingAddress": "Updated via orchestration"
},
"emailSent": true,
"teamsMessagePosted": false,
"messages": [
"Fetching order details using agent identity...",
"Order 12345 retrieved successfully",
"Updating delivery info using agent user identity...",
"Delivery info updated successfully",
"Sending email notification using agent user identity...",
"Email notification sent successfully",
"Customer service request processed successfully"
]
}

The email notification is just a dummy. No actual email is sent.

The flow uses two OIDC grant types:

“client_credentials” for the Autonomous Agent Identity (app-only)

and:

“urn:ietf:params:oauth:grant-type:user-fic” (user Federated Identity Credential) — for the Agent User Identity

In the agent Identities model, an agent Identity doesn’t get its own manually managed client secret or certificate.

Instead, it trusts its parent blueprint application to vouch for it. This trust relationship is the Federated Identity Credential — the agent identity is federated to its parent.

In a diagram:

Blueprint App (Application object, the Orchestrator)
│ (agentIdentityBlueprintId reference)
▼
Agent Identity (Service Principal, type = Microsoft.Graph.AgentIdentity)
│ (identityParentId reference)
▼
Agent User Identity (also created via Graph, linked to the Agent Identity)

For more FIC details, refer to this and this and this.

An example of the client credentials JWT is:

{
"aud": "api://18...73",
"iss": "https://sts.windows.net/ae...3b/",
"iat": 1787629639,
"nbf": 1787629639,
"exp": 1787633539,
"aio": "k2...YA",
"appid": "54...58",
"appidacr": "2",
"idp": "https://sts.windows.net/ae...3b/",
"oid": "54...58",
"rh": "1.A...AA.",
"roles": [
"Orders.Read.All"
],
"sub": "54...58",
"tid": "ae...3b",
"uti": "4E...AA",
"ver": "1.0",
"xms_ftd": "WR...1z"
}

An example of the FIC JWT is:

{
"aud": "api://35...65",
"iss": "https://sts.windows.net/ae...3b/",
"iat": 1787629642,
"nbf": 1787629642,
"exp": 1787633951,
"acr": "0",
"aio": "AZ...4V",
"appid": "54...58",
"appidacr": "2",
"ipaddr": "www.xxx.yy.zzz",
"name": "agentuser1",
"oid": "3f...70",
"rh": "1.A...AA.",
"scp": "Email.Send",
"sid": "007...3b7",
"sub": "Mx...00",
"tid": "ae...3b",
"unique_name": "agentuser1@entra-tenant.onmicrosoft.com",
"upn": "agentuser1@entra-tenant.onmicrosoft.com",
"uti": "P_8...cAA",
"ver": "1.0",
"xms_ftd": "2LS...21z"
}

In table form:

This maps directly to the pattern demonstrated in the sample:

  • reads use the autonomous (app-only) agent identity via “client_credentials”
  • writes that must be attributed to a specific user use the agent user identity via the “user-fic” grant

Note: I tried to get the sample working with Entra External ID (EEID), but although the script runs without issues and EEID does have an Agents tab in the portal, I get errors like:

I cannot find any references confirming whether agents and agentic IDs are supported in EEID.

All good!

--

--

Rory Braybrook
Rory Braybrook

Written by Rory Braybrook

NZ Microsoft Identity dude and MVP. Entra ID/Entra External ID/Azure AD B2C/VC. StackOverflow: https://bit.ly/2XU4yvJ Presentations: http://bit.ly/334ZPt5

The new control plane
The new control plane

Published in The new control plane

“Identity is the new control plane”. Articles around Microsoft Entra ID, Entra External ID and Azure AD B2C. Click the “Archive” link at the bottom for more posts.