Using the Entra ID Agentic ID sample
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 UpdateConfigThis 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.
The blueprint tab looks like:
with me as the owner and sponsor:
and the permission:
If you click on “Credentials”, you see a 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...akIn the blueprint, if you click on agent identities:
you see:
and if you click on “agent identity1”:
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:
If you click on “View” next to “Agent identity's access”, you see the permissions:
These correspond to the app registrations:
Each of these is an API that the orchestrator calls.
If you look at OrderAPI e.g., you see under “Expose an 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:
“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:
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/agentidentityYou 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.
and:
There are a few of these.
Now click “Send request” for the second call:
/api/customerservice/processYou 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:
- IDX10214: Audience validation failed. See https://aka.ms/identitymodel/app-context-switches
- Error: AADSTS500208: The domain is not a valid login domain for the account type
I cannot find any references confirming whether agents and agentic IDs are supported in EEID.
All good!











