entra-agent-id
Microsoft Entra Agent ID (preview) for creating OAuth2-capable AI agent identities via Microsoft Graph beta API. Covers Agent Identity Blueprints, BlueprintPrincipals, Agent Identities, required permissions, sponsors, and Workload Identity Federation. Includes Microsoft Entra SDK
Install
npx skills add https://github.com/microsoft/skills/tree/main/.github/skills/entra-agent-id
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Microsoft Entra Agent ID
Create and manage OAuth2-capable identities for AI agents using Microsoft Graph beta API.
Preview API — All Agent Identity endpoints are under
/betaonly. Not available in/v1.0.
Before You Start
Search microsoft-docs MCP for the latest Agent ID documentation:
- Query: "Microsoft Entra agent identity setup"
- Verify: API parameters match current preview behavior
Conceptual Model
Agent Identity Blueprint (application) ← one per agent type/project
└── BlueprintPrincipal (service principal) ← MUST be created explicitly
├── Agent Identity (SP): agent-1 ← one per agent instance
├── Agent Identity (SP): agent-2
└── Agent Identity (SP): agent-3
Prerequisites
PowerShell (recommended for interactive setup)
# Requires PowerShell 7+
Install-Module Microsoft.Graph.Beta.Applications -Scope CurrentUser -Force
Python (for programmatic provisioning)
pip install azure-identity requests
Required Entra Roles
One of: Agent Identity Developer, Agent Identity Administrator, or Application Administrator.
Environment Variables
AZURE_TENANT_ID=<your-tenant-id>
AZURE_CLIENT_ID=<app-registration-client-id>
AZURE_CLIENT_SECRET=<app-registration-secret>
Authentication
⚠️
DefaultAzureCredentialis NOT supported. Azure CLI tokens containDirectory.AccessAsUser.All, which Agent Identity APIs explicitly reject (403). You MUST use a dedicated app registration withclient_credentialsflow or connect viaConnect-MgGraphwith explicit delegated scopes.
PowerShell (delegated permissions)
Connect-MgGraph -Scopes @(
"AgentIdentityBlueprint.Create",
"AgentIdentityBlueprint.ReadWrite.All",
"AgentIdentityBlueprintPrincipal.Create",
"User.Read"
)
Set-MgRequestContext -ApiVersion beta
$currentUser = (Get-MgContext).Account
$userId = (Get-MgUser -UserId $currentUser).Id
Python (application permissions)
import os
import requests
from azure.identity import ClientSecretCredential
credential = ClientSecretCredential(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"],
)
token = credential.get_token("https://graph.microsoft.com/.default")
GRAPH = "https://graph.microsoft.com/beta"
headers = {
"Authorization": f"Bearer {token.token}",
"Content-Type": "application/json",
"OData-Version": "4.0", # Required for all Agent Identity API calls
}
Core Workflow
Step 1: Create Agent Identity Blueprint
Sponsors are required and must be User objects — ServicePrincipals and Groups are rejected.
import subprocess
# Get sponsor user ID (client_credentials has no user context, so use az CLI)
result = subprocess.run(
["az", "ad", "signed-in-user", "show", "--query", "id", "-o", "tsv"],
capture_output=True, text=True, check=True,
)
user_id = result.stdout.strip()
blueprint_body = {
"@odata.type": "Microsoft.Graph.AgentIdentityBlueprint",
"displayName": "My Agent Blueprint",
"sponsors@odata.bind": [
f"https://graph.microsoft.com/beta/users/{user_id}"
],
}
resp = requests.post(f"{GRAPH}/applications", headers=headers, json=blueprint_body)
resp.raise_for_status()
blueprint = resp.json()
app_id = blueprint["appId"]
blueprint_obj_id = blueprint["id"]
Step 2: Create BlueprintPrincipal
This step is mandatory. Creating a Blueprint does NOT auto-create its service principal. Without this, Agent Identity creation fails with:
400: The Agent Blueprint Principal for the Agent Blueprint does not exist.
sp_body = {
"@odata.type": "Microsoft.Graph.AgentIdentityBlueprintPrincipal",
"appId": app_id,
}
resp = requests.post(f"{GRAPH}/servicePrincipals", headers=headers, json=sp_body)
resp.raise_for_status()
If implementing idempotent scripts, check for and create the BlueprintPrincipal even when the Blueprint already exists (a previous run may have created the Blueprint but crashed before creating the SP).
Step 3: Create Agent Identities
agent_body = {
"@odata.type": "Microsoft.Graph.AgentIdentity",
"displayName": "my-agent-instance-1",
"agentIdentityBlueprintId": app_id,
"sponsors@odata.bind": [
f"https://graph.microsoft.com/beta/users/{user_id}"
],
}
resp = requests.post(f"{GRAPH}/servicePrincipals", headers=headers, json=agent_body)
resp.raise_for_status()
agent = resp.json()
API Reference
| Operation | Method | Endpoint | OData Type |
|---|---|---|---|
| Create Blueprint | POST |
/applications |
Microsoft.Graph.AgentIdentityBlueprint |
| Create BlueprintPrincipal | POST |
/servicePrincipals |
Microsoft.Graph.AgentIdentityBlueprintPrincipal |
| Create Agent Identity | POST |
/servicePrincipals |
Microsoft.Graph.AgentIdentity |
| List Agent Identities | GET |
/servicePrincipals?$filter=... |
— |
| Delete Agent Identity | DELETE |
/servicePrincipals/{id} |
— |
| Delete Blueprint | DELETE |
/applications/{id} |
— |
All endpoints use base URL: https://graph.microsoft.com/beta
Required Permissions
| Permission | Purpose |
|---|---|
Application.ReadWrite.All |
Blueprint CRUD (application objects) |
AgentIdentityBlueprint.Create |
Create new Blueprints |
AgentIdentityBlueprint.ReadWrite.All |
Read/update Blueprints |
AgentIdentityBlueprintPrincipal.Create |
Create BlueprintPrincipals |
AgentIdentity.Create.All |
Create Agent Identities |
AgentIdentity.ReadWrite.All |
Read/update Agent Identities |
There are 18 Agent Identity-specific Graph application permissions. Discover all:
az ad sp show --id 00000003-0000-0000-c000-000000000000 \
--query "appRoles[?contains(value, 'AgentIdentity')].{id:id, value:value}" -o json
Grant admin consent (required for application permissions):
az ad app permission admin-consent --id <client-id>
Admin consent may fail with 404 if the service principal hasn't replicated. Retry with 10–40s backoff.
Cleanup
# Delete Agent Identity
requests.delete(f"{GRAPH}/servicePrincipals/{agent['id']}", headers=headers)
# Delete BlueprintPrincipal (get SP ID first)
sps = requests.get(
f"{GRAPH}/servicePrincipals?$filter=appId eq '{app_id}'",
headers=headers,
).json()
for sp in sps.get("value", []):
requests.delete(f"{GRAPH}/servicePrincipals/{sp['id']}", headers=headers)
# Delete Blueprint
requests.delete(f"{GRAPH}/applications/{blueprint_obj_id}", headers=headers)
Best Practices
- Always create BlueprintPrincipal after Blueprint — not auto-created; implement idempotent checks on both
- Use User objects as sponsors — ServicePrincipals and Groups are rejected
- Handle permission propagation delays — after admin consent, wait 30–120s; retry with backoff on 403
- Include
OData-Version: 4.0header on every Graph request - Use Workload Identity Federation for production auth — for local dev, use a client secret on the Blueprint (see references/oauth2-token-flow.md)
- Set
identifierUrison Blueprint before using OAuth2 scoping (api://{app-id}) - Never use Azure CLI tokens for API calls — they contain
Directory.AccessAsUser.Allwhich is hard-rejected - Check for existing resources before creating — implement idempotent provisioning
References
| File | Contents |
|---|---|
| references/oauth2-token-flow.md | Production (Managed Identity + WIF) and local dev (client secret) token flows |
| references/known-limitations.md | 29 known issues organized by category (from official preview known-issues page) |
| references/sdk-sidecar.md | Microsoft Entra SDK for AgentID — endpoints, 3P agent patterns, Docker/K8s deployment, security |
External Links
| Resource | URL |
|---|---|
| Official Setup Guide | https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/agent-id-setup-instructions |
| AI-Guided Setup | https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/agent-id-ai-guided-setup |
| Microsoft Entra SDK for AgentID — Overview | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/overview |
| Microsoft Entra SDK for AgentID — Endpoints | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints |
Files (skills)
-
references
-
known-limitations.md 4.8 KB
# Known Limitations (Preview) Source: [Microsoft Entra Agent ID preview: Known issues and gaps](https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/preview-known-issues) ## API & Object Model 1. **Preview API only** — all endpoints are under `/beta`, not `/v1.0` 2. **Sponsors must be Users** — ServicePrincipals and Groups are not accepted as sponsors 3. **BlueprintPrincipal not auto-created** — requires explicit `POST /servicePrincipals` after Blueprint creation 4. **Agent Identities cannot have password credentials** — credentials belong on the Blueprint only (`PropertyNotCompatibleWithAgentIdentity` error) 5. **Agent Identities have no backing application object** — they are service-principal-only entities 6. **Blueprint needs explicit `identifierUris`** — not set by default, required for OAuth2 scope resolution (`api://{app-id}/.default`) 7. **No Graph relationship filtering for Agent IDs** — `/ownedObjects`, `/deletedItems`, `/owners` etc. return all types; must client-side filter by `odata.type` 8. **Orphaned agent users after deletion** — deleting a blueprint or identity does NOT auto-delete its agent users; clean up manually via admin center or Graph API ## Roles & Permissions 9. **`Directory.AccessAsUser.All` hard rejection** — if present on the client, all other Agent ID delegated permissions are ignored → 403 Forbidden 10. **No viable delegated permission for creating agent identities** — must use application permissions 11. **No quick-start permission bundle** — must discover and grant 18+ individual Agent Identity permissions 12. **Permission propagation delay** — 30–120+ seconds after admin consent before tokens include new claims; use delegated permissions where possible and add retry with exponential backoff 13. **Global Reader cannot list agent identities** — `GET /servicePrincipals/graph.agentIdentity` returns 403; use `GET /servicePrincipals` instead 14. **Custom roles cannot include Agent ID actions** — use built-in roles (Agent ID Administrator, Agent ID Developer) 15. **Administrative units not supported** — cannot add agent identities, blueprints, or blueprint principals to admin units; use `owners` property instead 16. **Agent ID Admin cannot update agent user photos** — use User Administrator role ## Admin Center & Management 17. **No blueprint management in Entra admin center** — must use Microsoft Graph APIs / PowerShell to create and edit blueprints 18. **`/me` endpoint unavailable** in `client_credentials` flow — use `az ad signed-in-user show` or Graph delegated permissions for user context ## Authentication & Consent 19. **No SSO to web apps** — Agent IDs cannot sign in via Microsoft Entra ID sign-in pages (no OpenID Connect or SAML); use web APIs instead 20. **Admin consent workflow (ACW) broken** — does not work properly for permissions requested by Agent IDs; contact tenant admin directly 21. **Cannot grant app permissions to blueprint principals** — grant application permissions to individual agent identities instead 22. **Cannot assign app roles where target resource is an agent identity** — use blueprint principal as the target resource 23. **Risk-based step-up blocks consent silently** — no "risky" indication in the UX ## Groups, Logs & Monitoring 24. **No dynamic group membership** — agent identities and agent users cannot be added to dynamic groups; use security groups with fixed membership 25. **Audit logs do not distinguish Agent IDs** — operations on blueprints/identities logged as `ApplicationManagement`, agent users as `User Management`; cross-reference object IDs via Graph to determine entity type 26. **Graph activity logs do not distinguish Agent IDs** — agent identity requests logged as applications, agent user requests as users; join with sign-in logs ## Performance & Scale 27. **Sequential creation requests may fail** — creating multiple entities in quick succession (e.g., blueprint → principal → identity) can return `400 Bad Request: Object with id {id} not found`; especially with application permissions. Use delegated permissions where possible and add exponential backoff retry. ## Product Integrations 28. **Copilot Studio** — only custom engine agents are supported; Agent IDs are used for channel auth only (not connectors or tools) 29. **MSAL complexity** — Agent ID scenarios require managing Federated Identity Credentials manually. For .NET use [Microsoft.Identity.Web.AgentIdentities](https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web.AgentIdentities/README.AgentIdentities.md). For other languages use the [Microsoft Entra SDK for Agent ID](https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/overview). ## Reporting Issues Report unlisted issues via [aka.ms/agentidfeedback](https://aka.ms/agentidfeedback). -
oauth2-token-flow.md 4.9 KB
# OAuth2 Token Flow Source: [Agent ID Setup Instructions](https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/agent-id-setup-instructions) Agent Identities authenticate at runtime using credentials configured on the **Blueprint** (not the Agent Identity itself). Two options are available: | Option | Use case | Credential type | |--------|----------|-----------------| | **Managed Identity + WIF** | Production (Azure-hosted) | Federated Identity Credential | | **Client secret** | Local development / testing | Password credential on Blueprint | --- ## Option A: Managed Identity + Workload Identity Federation (Production) ### Architecture ``` Container App (user-assigned MI) -> ManagedIdentityCredential.get_token("api://{blueprint-app-id}/.default") -> Azure AD token exchange (MI token -> Agent ID token) -> JWT with oid = MI principal, aud = api://{blueprint-app-id} -> Backend validates JWT signature + claims ``` ### 1. Set Application ID URI on Blueprint Required for OAuth2 scope resolution: ```python requests.patch( f"{GRAPH}/applications/{blueprint_obj_id}", headers=headers, json={"identifierUris": [f"api://{app_id}"]}, ) ``` ### 2. Create Federated Identity Credential Create on the Blueprint (not the Agent Identity): ```python fic_body = { "name": "my-fic-name", "issuer": f"https://login.microsoftonline.com/{tenant_id}/v2.0", "subject": "{mi-principal-id}", # The MI's object ID (principalId), NOT client ID "audiences": ["api://AzureADTokenExchange"], } requests.post( f"{GRAPH}/applications/{blueprint_obj_id}/microsoft.graph.agentIdentityBlueprint/federatedIdentityCredentials", headers=headers, json=fic_body, ) ``` ### 3. Acquire Token (Caller Side) ```python from azure.identity import ManagedIdentityCredential cred = ManagedIdentityCredential(client_id=mi_client_id) token = cred.get_token(f"api://{blueprint_app_id}/.default") # Include in requests: Authorization: Bearer {token.token} ``` ### 4. Validate Token (Backend) ```python import jwt from jwt import PyJWKClient jwks_uri = f"https://login.microsoftonline.com/{tenant_id}/discovery/v2.0/keys" jwks_client = PyJWKClient(jwks_uri) signing_key = jwks_client.get_signing_key_from_jwt(token_str) claims = jwt.decode( token_str, signing_key.key, algorithms=["RS256"], audience=f"api://{blueprint_app_id}", issuer=f"https://sts.windows.net/{tenant_id}/", ) ``` ### Key Rules (WIF) - **Federated credentials go on the Blueprint**, not the Agent Identity SP. Use the `.../microsoft.graph.agentIdentityBlueprint/federatedIdentityCredentials` path. - **`subject` is the MI's principalId (object ID)**, not its client ID. - **`audiences` must be `["api://AzureADTokenExchange"]`**, not your API audience. - **Issuer format**: `https://login.microsoftonline.com/{tenant}/v2.0` - **Token issuer** (for validation): `https://sts.windows.net/{tenant}/` (note the trailing slash and different domain) --- ## Option B: Client Secret (Local Development / Testing Only) For local development where no Managed Identity is available. ### 1. Add a Password Credential to the Blueprint Via PowerShell: ```powershell $secretBody = @{ "passwordCredential" = @{ "displayName" = "Dev Secret" "endDateTime" = "2027-01-01T00:00:00Z" } } $credential = Invoke-MgGraphRequest -Method POST ` -Uri "https://graph.microsoft.com/beta/applications/<BLUEPRINT_OBJECT_ID>/addPassword" ` -Headers @{"OData-Version"="4.0"; "Content-Type"="application/json"} ` -Body ($secretBody | ConvertTo-Json -Depth 5) -OutputType PSObject $credential.secretText # Save NOW — cannot be retrieved later ``` Or via Python (with an existing token): ```python secret_body = { "passwordCredential": { "displayName": "Dev Secret", "endDateTime": "2027-01-01T00:00:00Z", } } resp = requests.post( f"{GRAPH}/applications/{blueprint_obj_id}/addPassword", headers=headers, json=secret_body, ) secret_text = resp.json()["secretText"] # Save NOW ``` ### 2. Acquire Token Locally ```python from azure.identity import ClientSecretCredential credential = ClientSecretCredential( tenant_id=TENANT_ID, client_id=BLUEPRINT_APP_ID, # Blueprint's appId client_secret=SECRET_TEXT, # From step 1 ) token = credential.get_token(f"api://{BLUEPRINT_APP_ID}/.default") ``` ### Key Rules (Client Secret) - **Save `secretText` immediately** — it cannot be retrieved after creation. - **Secrets belong on the Blueprint only** — agent identities cannot have password credentials (`PropertyNotCompatibleWithAgentIdentity`). - **NOT for production** — use Managed Identity + WIF in production. - **Respect org policy** — if `endDateTime` exceeds your tenant's credential lifetime policy, reduce it. - **Use `ClientSecretCredential`**, not `DefaultAzureCredential`. Azure CLI tokens contain `Directory.AccessAsUser.All` which is rejected by Agent ID APIs. -
sdk-sidecar.md 13 KB
# Microsoft Entra SDK for AgentID: Polyglot Agent Authentication Containerized companion service that handles token management for AI agents via HTTP — any language, any framework. > **Preview** — Image: `mcr.microsoft.com/entra-sdk/auth-sidecar:<tag>`. Check [GitHub releases](https://github.com/AzureAD/microsoft-identity-web/releases) for tags. ## Architecture ``` Client App → Your Agent (Python/Node/Go/Java) → Microsoft Entra SDK for AgentID (localhost:5000) → Microsoft Entra ID ↓ Downstream APIs ``` The Microsoft Entra SDK for AgentID runs as a companion container in the same pod or Docker network. Your agent calls it over HTTP — no SDK embedding required. ### Agent Integration (3P and Custom Agents) Third-party and custom agents authenticate using the Blueprint → BlueprintPrincipal → AgentIdentity hierarchy. The Microsoft Entra SDK for AgentID acquires tokens for these agent identities — whether you are building your own custom agent or integrating a third-party agent: ``` GET /AuthorizationHeaderUnauthenticated/graph ?AgentIdentity={agent-app-id} ┌──────────────────┐ ─────────────────────────────────▶ ┌─────────────────────────────┐ │ Agent │ │ Microsoft Entra SDK │ │ (any language) │ ◀───────────────────────────────── │ for AgentID (:5000) │ │ │ { authorizationHeader: │ │ └────────┬─────────┘ "Bearer eyJ..." } │ · AzureAd Config (Tenant, │ │ │ ClientId, FIC) │ │ │ · Agent Identity Params │ │ │ · Downstream API Scopes │ │ │ · Token Cache │ │ └──────────────┬──────────────┘ │ │ │ Authorization: Bearer <JWT> │ OAuth 2.0 ▼ ▼ ┌──────────────────────┐ ┌──────────────────────────┐ │ Downstream APIs │ │ Microsoft Entra ID │ │ · Microsoft Graph │ │ · Blueprint │ │ · Custom APIs │ │ · AgentIdentity │ │ · Azure Services │ │ │ └──────────────────────┘ └──────────────────────────┘ ``` ## Microsoft Entra SDK for AgentID Configuration ### Core Settings ```yaml env: - name: AzureAd__Instance value: "https://login.microsoftonline.com/" - name: AzureAd__TenantId value: "<your-tenant-id>" - name: AzureAd__ClientId value: "<blueprint-app-id>" ``` ### Client Credentials > **⚠️ Client secrets are for development only.** Production deployments must use Federated Identity Credentials (FIC) via Workload Identity (`SignedAssertionFilePath`) or Managed Identity. ```yaml # Dev ONLY: Client Secret (do NOT use in production) - name: AzureAd__ClientCredentials__0__SourceType value: "ClientSecret" - name: AzureAd__ClientCredentials__0__ClientSecret value: "<secret>" # Prod (AKS): Federated Identity Credentials via Workload Identity — RECOMMENDED - name: AzureAd__ClientCredentials__0__SourceType value: "SignedAssertionFilePath" # Prod (VM/App Service): Managed Identity - name: AzureAd__ClientCredentials__0__SourceType value: "SignedAssertionFromManagedIdentity" - name: AzureAd__ClientCredentials__0__ManagedIdentityClientId value: "<managed-identity-client-id>" ``` ### Downstream API ```yaml - name: DownstreamApis__Graph__BaseUrl value: "https://graph.microsoft.com/v1.0/" - name: DownstreamApis__Graph__Scopes__0 value: "https://graph.microsoft.com/.default" - name: DownstreamApis__Graph__RequestAppToken value: "true" ``` ## Endpoint Reference | Endpoint | Method | Auth Required | Purpose | |----------|--------|---------------|---------| | `/Validate` | GET | Yes | Validate inbound bearer token, return claims | | `/AuthorizationHeader/{name}` | GET | Yes | Validate inbound token + acquire downstream token (OBO) | | `/AuthorizationHeaderUnauthenticated/{name}` | GET | No | Acquire app/agent token without inbound user token | | `/DownstreamApi/{name}` | ANY | Yes | Validate + call downstream API with auto token | | `/DownstreamApiUnauthenticated/{name}` | ANY | No | Call downstream API with app/agent token | | `/healthz` | GET | No | Health probe | ### Agent Identity Query Parameters | Parameter | Purpose | Example | |-----------|---------|---------| | `AgentIdentity` | Agent app (client) ID — autonomous mode | `?AgentIdentity=<agent-client-id>` | | `AgentIdentity` + `AgentUsername` | Interactive mode by UPN | `?AgentIdentity=<id>&AgentUsername=user@contoso.com` | | `AgentIdentity` + `AgentUserId` | Interactive mode by Object ID | `?AgentIdentity=<id>&AgentUserId=<oid>` | Rules: - `AgentUsername`/`AgentUserId` require `AgentIdentity` - `AgentUsername` and `AgentUserId` are mutually exclusive - `AgentIdentity` alone = autonomous agent - `AgentIdentity` + inbound bearer = interactive (OBO) agent ## Code Patterns ### Autonomous 3P Agent (Python) ```python import os import requests SIDECAR_URL = os.environ.get("SIDECAR_URL", "http://localhost:5000") AGENT_APP_ID = os.environ["AGENT_CLIENT_ID"] def get_agent_token(downstream_api: str = "Graph") -> str: """Acquire autonomous agent token via Microsoft Entra SDK for AgentID.""" url = f"{SIDECAR_URL}/AuthorizationHeaderUnauthenticated/{downstream_api}" resp = requests.get(url, params={"AgentIdentity": AGENT_APP_ID}, timeout=30) resp.raise_for_status() return resp.json()["authorizationHeader"] def call_downstream_api(endpoint: str) -> dict: """Call a downstream API using agent identity token.""" token = get_agent_token() resp = requests.get(endpoint, headers={"Authorization": token}, timeout=10) resp.raise_for_status() return resp.json() ``` ### Interactive Agent with User Delegation (Python) ```python def get_delegated_token(user_token: str, downstream_api: str = "Graph") -> str: """Acquire delegated token via OBO flow.""" url = f"{SIDECAR_URL}/AuthorizationHeader/{downstream_api}" resp = requests.get( url, headers={"Authorization": f"Bearer {user_token}"}, timeout=30, ) resp.raise_for_status() return resp.json()["authorizationHeader"] ``` ### Autonomous Agent (TypeScript) ```typescript const SIDECAR_URL = process.env.SIDECAR_URL ?? "http://localhost:5000"; const AGENT_APP_ID = process.env.AGENT_CLIENT_ID!; async function getAgentToken(downstreamApi = "Graph"): Promise<string> { const url = `${SIDECAR_URL}/AuthorizationHeaderUnauthenticated/${downstreamApi}`; const res = await fetch(url + `?AgentIdentity=${AGENT_APP_ID}`); if (!res.ok) throw new Error(`Microsoft Entra SDK for AgentID error: ${res.status}`); const data = await res.json(); return data.authorizationHeader; } ``` ### Token Validation Middleware ```python def validate_incoming_token(bearer_token: str) -> dict: """Validate an incoming bearer token and extract claims.""" resp = requests.get( f"{SIDECAR_URL}/Validate", headers={"Authorization": f"Bearer {bearer_token}"}, timeout=10, ) resp.raise_for_status() return resp.json()["claims"] ``` ### Direct Downstream Call via Microsoft Entra SDK for AgentID ```python def call_graph_me(user_token: str) -> dict: """Call Microsoft Graph /me via Microsoft Entra SDK for AgentID proxy.""" resp = requests.get( f"{SIDECAR_URL}/DownstreamApi/Graph", params={"optionsOverride.RelativePath": "me"}, headers={"Authorization": f"Bearer {user_token}"}, timeout=10, ) resp.raise_for_status() return resp.json()["content"] ``` ## Deployment ### Docker Compose (Development) ```yaml version: '3.8' services: sidecar: image: mcr.microsoft.com/entra-sdk/auth-sidecar:1.0.0-azurelinux3.0-distroless ports: - "5001:5000" environment: - AzureAd__Instance=https://login.microsoftonline.com/ - AzureAd__TenantId=${TENANT_ID} - AzureAd__ClientId=${BLUEPRINT_APP_ID} - AzureAd__ClientCredentials__0__SourceType=ClientSecret - AzureAd__ClientCredentials__0__ClientSecret=${BLUEPRINT_CLIENT_SECRET} - DownstreamApis__Graph__BaseUrl=https://graph.microsoft.com/v1.0/ - DownstreamApis__Graph__Scopes__0=https://graph.microsoft.com/.default - DownstreamApis__Graph__RequestAppToken=true - ASPNETCORE_URLS=http://+:5000 agent: build: ./agent ports: - "3000:3000" environment: - SIDECAR_URL=http://sidecar:5000 - AGENT_CLIENT_ID=${AGENT_CLIENT_ID} depends_on: - sidecar ``` ### Kubernetes (Production) ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: agent-deployment spec: replicas: 3 template: metadata: labels: app: agent azure.workload.identity/use: "true" spec: serviceAccountName: agent-sa containers: - name: agent image: myregistry/agent:latest env: - name: SIDECAR_URL value: "http://localhost:5000" - name: sidecar image: mcr.microsoft.com/entra-sdk/auth-sidecar:1.0.0 ports: - containerPort: 5000 env: - name: AzureAd__TenantId valueFrom: configMapKeyRef: name: agent-config key: tenant-id - name: AzureAd__ClientId valueFrom: configMapKeyRef: name: agent-config key: client-id - name: AzureAd__ClientCredentials__0__SourceType value: "SignedAssertionFilePath" - name: Kestrel__Endpoints__Http__Url value: "http://127.0.0.1:5000" resources: requests: { memory: "128Mi", cpu: "100m" } limits: { memory: "256Mi", cpu: "250m" } livenessProbe: httpGet: { path: /healthz, port: 5000 } initialDelaySeconds: 10 readinessProbe: httpGet: { path: /healthz, port: 5000 } initialDelaySeconds: 5 ``` ## Security > **⚠️ The Microsoft Entra SDK for AgentID API must NOT be publicly accessible.** Pod-local or same Docker network only. 1. **Bind to localhost** — `Kestrel__Endpoints__Http__Url=http://127.0.0.1:5000` 2. **Never expose via LoadBalancer/Ingress** 3. **Use Workload Identity in AKS** — `SignedAssertionFilePath` over client secrets 4. **Use Key Vault for certificates** — `SourceType=KeyVault` in production 5. **Separate ConfigMap from Secrets** in Kubernetes ## Troubleshooting | Symptom | Cause | Fix | |---------|-------|-----| | 404 on `/AuthorizationHeader/{name}` | `{name}` not in config | Add `DownstreamApis__{name}__BaseUrl` env var | | 400 `AgentUsername requires AgentIdentity` | Missing `AgentIdentity` param | Always pair user params with `AgentIdentity` | | 400 `mutually exclusive` | Both `AgentUsername` and `AgentUserId` | Use one or the other | | 401 on `/Validate` | Invalid/expired inbound token | Check token audience matches `AzureAd__ClientId` | | 500 token acquisition failure | Wrong creds or missing admin consent | `kubectl logs <pod> -c sidecar` | | Connection refused | SDK not ready or wrong URL | Verify `SIDECAR_URL` and `/healthz` | ## External Links | Resource | URL | |----------|-----| | SDK Overview | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/overview | | Installation | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/installation | | Configuration | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/configuration | | Endpoints | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints | | Agent Identities | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/agent-identities | | Security | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/security | | OpenAPI Spec | https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web.Sidecar/OpenAPI/Microsoft.Identity.Web.AgentID.json |
-
-
SKILL.md 9.2 KB
--- name: entra-agent-id description: | Microsoft Entra Agent ID (preview) for creating OAuth2-capable AI agent identities via Microsoft Graph beta API. Covers Agent Identity Blueprints, BlueprintPrincipals, Agent Identities, required permissions, sponsors, and Workload Identity Federation. Includes Microsoft Entra SDK for AgentID (containerized sidecar) for polyglot agent authentication (Docker/Kubernetes), 3P agent integration, autonomous and interactive agent patterns. Triggers: "agent identity", "agent id", "Agent Identity Blueprint", "BlueprintPrincipal", "entra agent", "agent identity provisioning", "Graph agent identity", "entra sidecar", "agent id sidecar", "auth sidecar", "3P agent", "third-party agent identity", "polyglot agent auth". --- # Microsoft Entra Agent ID Create and manage OAuth2-capable identities for AI agents using Microsoft Graph beta API. > **Preview API** — All Agent Identity endpoints are under `/beta` only. Not available in `/v1.0`. ## Before You Start Search `microsoft-docs` MCP for the latest Agent ID documentation: - Query: "Microsoft Entra agent identity setup" - Verify: API parameters match current preview behavior ## Conceptual Model ``` Agent Identity Blueprint (application) ← one per agent type/project └── BlueprintPrincipal (service principal) ← MUST be created explicitly ├── Agent Identity (SP): agent-1 ← one per agent instance ├── Agent Identity (SP): agent-2 └── Agent Identity (SP): agent-3 ``` ## Prerequisites ### PowerShell (recommended for interactive setup) ```powershell # Requires PowerShell 7+ Install-Module Microsoft.Graph.Beta.Applications -Scope CurrentUser -Force ``` ### Python (for programmatic provisioning) ```bash pip install azure-identity requests ``` ### Required Entra Roles One of: **Agent Identity Developer**, **Agent Identity Administrator**, or **Application Administrator**. ## Environment Variables ```bash AZURE_TENANT_ID=<your-tenant-id> AZURE_CLIENT_ID=<app-registration-client-id> AZURE_CLIENT_SECRET=<app-registration-secret> ``` ## Authentication > **⚠️ `DefaultAzureCredential` is NOT supported.** Azure CLI tokens contain > `Directory.AccessAsUser.All`, which Agent Identity APIs explicitly reject (403). > You MUST use a dedicated app registration with `client_credentials` flow or > connect via `Connect-MgGraph` with explicit delegated scopes. ### PowerShell (delegated permissions) ```powershell Connect-MgGraph -Scopes @( "AgentIdentityBlueprint.Create", "AgentIdentityBlueprint.ReadWrite.All", "AgentIdentityBlueprintPrincipal.Create", "User.Read" ) Set-MgRequestContext -ApiVersion beta $currentUser = (Get-MgContext).Account $userId = (Get-MgUser -UserId $currentUser).Id ``` ### Python (application permissions) ```python import os import requests from azure.identity import ClientSecretCredential credential = ClientSecretCredential( tenant_id=os.environ["AZURE_TENANT_ID"], client_id=os.environ["AZURE_CLIENT_ID"], client_secret=os.environ["AZURE_CLIENT_SECRET"], ) token = credential.get_token("https://graph.microsoft.com/.default") GRAPH = "https://graph.microsoft.com/beta" headers = { "Authorization": f"Bearer {token.token}", "Content-Type": "application/json", "OData-Version": "4.0", # Required for all Agent Identity API calls } ``` ## Core Workflow ### Step 1: Create Agent Identity Blueprint Sponsors are required and **must be User objects** — ServicePrincipals and Groups are rejected. ```python import subprocess # Get sponsor user ID (client_credentials has no user context, so use az CLI) result = subprocess.run( ["az", "ad", "signed-in-user", "show", "--query", "id", "-o", "tsv"], capture_output=True, text=True, check=True, ) user_id = result.stdout.strip() blueprint_body = { "@odata.type": "Microsoft.Graph.AgentIdentityBlueprint", "displayName": "My Agent Blueprint", "sponsors@odata.bind": [ f"https://graph.microsoft.com/beta/users/{user_id}" ], } resp = requests.post(f"{GRAPH}/applications", headers=headers, json=blueprint_body) resp.raise_for_status() blueprint = resp.json() app_id = blueprint["appId"] blueprint_obj_id = blueprint["id"] ``` ### Step 2: Create BlueprintPrincipal > **This step is mandatory.** Creating a Blueprint does NOT auto-create its > service principal. Without this, Agent Identity creation fails with: > `400: The Agent Blueprint Principal for the Agent Blueprint does not exist.` ```python sp_body = { "@odata.type": "Microsoft.Graph.AgentIdentityBlueprintPrincipal", "appId": app_id, } resp = requests.post(f"{GRAPH}/servicePrincipals", headers=headers, json=sp_body) resp.raise_for_status() ``` If implementing idempotent scripts, check for and create the BlueprintPrincipal even when the Blueprint already exists (a previous run may have created the Blueprint but crashed before creating the SP). ### Step 3: Create Agent Identities ```python agent_body = { "@odata.type": "Microsoft.Graph.AgentIdentity", "displayName": "my-agent-instance-1", "agentIdentityBlueprintId": app_id, "sponsors@odata.bind": [ f"https://graph.microsoft.com/beta/users/{user_id}" ], } resp = requests.post(f"{GRAPH}/servicePrincipals", headers=headers, json=agent_body) resp.raise_for_status() agent = resp.json() ``` ## API Reference | Operation | Method | Endpoint | OData Type | |-----------|--------|----------|------------| | Create Blueprint | `POST` | `/applications` | `Microsoft.Graph.AgentIdentityBlueprint` | | Create BlueprintPrincipal | `POST` | `/servicePrincipals` | `Microsoft.Graph.AgentIdentityBlueprintPrincipal` | | Create Agent Identity | `POST` | `/servicePrincipals` | `Microsoft.Graph.AgentIdentity` | | List Agent Identities | `GET` | `/servicePrincipals?$filter=...` | — | | Delete Agent Identity | `DELETE` | `/servicePrincipals/{id}` | — | | Delete Blueprint | `DELETE` | `/applications/{id}` | — | All endpoints use base URL: `https://graph.microsoft.com/beta` ## Required Permissions | Permission | Purpose | |-----------|---------| | `Application.ReadWrite.All` | Blueprint CRUD (application objects) | | `AgentIdentityBlueprint.Create` | Create new Blueprints | | `AgentIdentityBlueprint.ReadWrite.All` | Read/update Blueprints | | `AgentIdentityBlueprintPrincipal.Create` | Create BlueprintPrincipals | | `AgentIdentity.Create.All` | Create Agent Identities | | `AgentIdentity.ReadWrite.All` | Read/update Agent Identities | There are **18 Agent Identity-specific** Graph application permissions. Discover all: ```bash az ad sp show --id 00000003-0000-0000-c000-000000000000 \ --query "appRoles[?contains(value, 'AgentIdentity')].{id:id, value:value}" -o json ``` Grant admin consent (required for application permissions): ```bash az ad app permission admin-consent --id <client-id> ``` > Admin consent may fail with 404 if the service principal hasn't replicated. Retry with 10–40s backoff. ## Cleanup ```python # Delete Agent Identity requests.delete(f"{GRAPH}/servicePrincipals/{agent['id']}", headers=headers) # Delete BlueprintPrincipal (get SP ID first) sps = requests.get( f"{GRAPH}/servicePrincipals?$filter=appId eq '{app_id}'", headers=headers, ).json() for sp in sps.get("value", []): requests.delete(f"{GRAPH}/servicePrincipals/{sp['id']}", headers=headers) # Delete Blueprint requests.delete(f"{GRAPH}/applications/{blueprint_obj_id}", headers=headers) ``` ## Best Practices 1. **Always create BlueprintPrincipal after Blueprint** — not auto-created; implement idempotent checks on both 2. **Use User objects as sponsors** — ServicePrincipals and Groups are rejected 3. **Handle permission propagation delays** — after admin consent, wait 30–120s; retry with backoff on 403 4. **Include `OData-Version: 4.0` header** on every Graph request 5. **Use Workload Identity Federation for production auth** — for local dev, use a client secret on the Blueprint (see [references/oauth2-token-flow.md](references/oauth2-token-flow.md)) 6. **Set `identifierUris` on Blueprint** before using OAuth2 scoping (`api://{app-id}`) 7. **Never use Azure CLI tokens** for API calls — they contain `Directory.AccessAsUser.All` which is hard-rejected 8. **Check for existing resources** before creating — implement idempotent provisioning ## References | File | Contents | |------|----------| | [references/oauth2-token-flow.md](references/oauth2-token-flow.md) | Production (Managed Identity + WIF) and local dev (client secret) token flows | | [references/known-limitations.md](references/known-limitations.md) | 29 known issues organized by category (from official preview known-issues page) | | [references/sdk-sidecar.md](references/sdk-sidecar.md) | Microsoft Entra SDK for AgentID — endpoints, 3P agent patterns, Docker/K8s deployment, security | ### External Links | Resource | URL | |----------|-----| | Official Setup Guide | https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/agent-id-setup-instructions | | AI-Guided Setup | https://learn.microsoft.com/en-us/entra/agent-id/identity-platform/agent-id-ai-guided-setup | | Microsoft Entra SDK for AgentID — Overview | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/overview | | Microsoft Entra SDK for AgentID — Endpoints | https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.