Azure Foundry Connector: Environment & API Reference
What exactly this connector queries, what permissions it needs, and what test data must exist for it to produce output.
Read architecture/11-platform-mental-model.md first for the conceptual model.
Credentials Required
# Azure Entra (app registration with client_credentials grant)
AZURE_TENANT_ID=<directory-tenant-id>
AZURE_CLIENT_ID=<app-registration-client-id>
AZURE_CLIENT_SECRET=<app-registration-secret>
# Optional: limit to specific subscriptions (auto-discovers all if empty)
AZURE_SUBSCRIPTION_IDS=<comma-separated-sub-ids>
Required Roles (three separate scopes)
1. ARM scope — subscription level
| Role | Where to assign | Purpose |
|---|---|---|
Reader | Azure Portal → Subscription → Access control (IAM) | List AIServices accounts, projects, Function Apps, role assignments |
2. Foundry data plane — AIServices account level
| Role | Where to assign | Purpose |
|---|---|---|
Azure AI User | Azure Portal → AIServices account → Access control (IAM) | List agents, connections, threads |
This is the most commonly missed permission. Without it, the connector discovers accounts and projects via ARM but gets 401 on every data-plane call (agents, connections, threads).
The role is assigned on the AIServices account resource, not per-project. All sub-projects inherit from the parent account.
Role GUID: 53ca6127-db4c-4b6e-9c83-d83438ad60d0
3. Graph API — app registration permissions (optional but recommended)
| Permission | Type | Purpose |
|---|---|---|
Application.Read.All | Application | Read SP details (credential types, status) |
Directory.Read.All | Application | Read SP owners |
User.Read.All | Application | Detect disabled owner accounts |
AuditLog.Read.All | Application | Sign-in logs (needs P1/P2 licence) |
Without Graph permissions: Connector still works. Evidence completeness shows unavailable_no_access for entra_sp_details and sign_in_logs. You won't see owner names or sign-in timestamps.
API Calls (exact endpoints)
ARM — Resource discovery
GET https://management.azure.com/subscriptions?api-version=2022-12-01
Auto-discover all subscriptions. Cached per session.
GET https://management.azure.com/subscriptions/{sub}/providers/Microsoft.MachineLearningServices/workspaces?api-version=2024-10-01
Legacy Hub/Project model. Filters kind ∈ ["Hub", "Project"].
GET https://management.azure.com/subscriptions/{sub}/providers/Microsoft.CognitiveServices/accounts?api-version=2023-05-01
Current AIServices model (2025+). Filters kind == "AIServices".
GET https://management.azure.com{account_resource_id}/projects?api-version=2025-04-01-preview
Enumerate projects under an AIServices account.
GET https://management.azure.com/subscriptions/{sub}/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&$filter=principalId eq '{id}'
RBAC role assignments for a managed identity.
Foundry data plane — Agent and connection discovery
Token scope depends on endpoint domain:
| Endpoint | Scope |
|---|---|
*.services.ai.azure.com | https://ai.azure.com/.default |
*.api.azureml.ms | https://ml.azure.com/.default |
*.cognitiveservices.azure.com | https://cognitiveservices.azure.com/.default |
# List agents (new API)
GET {project_endpoint}/assistants?api-version=v1
# List agents (legacy API)
GET {project_endpoint}/agents/v1.0/assistants?api-version=2024-12-01-preview
Paginates via OpenAI-compatible has_more + after={last_id}.
# List connections
GET {project_endpoint}/connections?api-version=v1
# List threads (execution evidence)
GET {project_endpoint}/threads?api-version=v1&limit=100
Client-side filters to last 30 days. Thread count = proxy for agent execution frequency.
Graph API — Identity enrichment
GET /v1.0/servicePrincipals/{object_id}
GET /v1.0/servicePrincipals/{object_id}/owners
GET /v1.0/auditLogs/signIns?$filter=servicePrincipalId eq '{id}'&$top=1&$orderby=createdDateTime desc
Scan Phases (execution order)
Phase 1 — Workspace discovery (ARM):
- Enumerate subscriptions
- List ML workspaces (legacy Hub/Project)
- List CognitiveServices accounts (AIServices)
- For each AIServices account: list projects
Phase 2 — Agent and connection discovery (data plane): 5. For each project: list agents (assistants) 6. For each project: list connections 7. For each project: count recent threads (execution evidence)
Phase 3 — ARM RBAC (per managed identity): 8. For each managed identity principal ID: query role assignments 9. Resolve role definition names (cached lookup)
Phase 4 — Entra enrichment (Graph, optional): 10. For each managed identity: fetch SP details 11. For each SP: fetch owners 12. For each SP: fetch last sign-in timestamp
Phase 5 — Transform and submit: 13. Build NormalizedGraph (nodes, edges, evidence completeness) 14. POST to platform ingestion endpoint
Test Data Requirements
Minimum viable (to see non-zero output)
- AIServices account in at least one subscription
- App registration with
Readeron that subscription - App registration with
Azure AI Useron that AIServices account - At least one Foundry project under the account
- At least one AI agent created in that project
For richer output
- Agent with connections (Azure OpenAI, Azure AI Search, etc.)
- Agent with recent usage (threads created in last 30 days)
- Managed identity with ARM role assignments (Cognitive Services Contributor, etc.)
- Graph API permissions on the app registration (for owner/sign-in data)
What the current dev environment has
Our dev tenant (bcf375ed-..., subscription 2a25bc41-...) contains:
- 3 AIServices accounts:
gpt-nano-for-summary→ 1 project, 1 agents-appt-scheduler→ 1 project, 2 agentssv0-foundry-agent-project→ 1 project, 2 agents
- 5 agents total across 3 projects
- 6 managed identities with ARM role assignments
- 2 connections discovered
- 4 execution evidence records (thread counts)
What was missing (and fixed 2026-03-10)
s-appt-scheduler and sv0-foundry-agent-project were returning 401 on agents and connections endpoints. Fix: Added Azure AI User role on both AIServices accounts for the connector app registration.
Before fix: 1 agent, 0 connections, 0 execution evidence After fix: 5 agents, 2 connections, 4 execution evidence
Evidence Completeness
| Evidence Source | Status when working | Common failure |
|---|---|---|
workspaces | available | Wrong credentials or no AIServices accounts exist |
agents | available | Missing Azure AI User role on AIServices account → 401 |
connections | available or partial | Missing Azure AI User role → 401, or some projects denied |
execution_evidence | available or partial | No threads in last 30 days, or endpoint doesn't support threads API |
arm_rbac | available or unavailable_no_access | Missing Reader role on subscription |
entra_sp_details | available or unavailable_no_access | Missing Application.Read.All Graph permission |
sign_in_logs | available or unavailable_not_enabled | No P1/P2 licence, or missing AuditLog.Read.All |
data_plane_authority | available | Synthetic (always available if agents found) |
Troubleshooting
"No permission to list agents in project X (401)" — Missing Azure AI User role on the AIServices account. Go to Azure Portal → AIServices account → Access control (IAM) → Add role assignment → Azure AI User → assign to the connector app registration.
"Discovered 0 Foundry workspaces" — Either no AIServices accounts exist in the subscription, or the app registration lacks Reader role on the subscription.
"0 agents" but workspaces found — Agents exist in the Foundry portal but the connector can't list them. Check the Azure AI User role assignment.
"arm_rbac: unavailable_no_access" — The Reader role is present (can list resources) but role assignment queries return 403. This can happen if the role is inherited from a management group with restricted permissions.
"sign_in_logs: unavailable_not_enabled" — The Azure AD tenant doesn't have P1/P2 licence. Sign-in logs require a premium licence. Non-blocking.
Thread counts seem low — Threads API returns project-level threads, not agent-filtered. If multiple agents share a project, counts are approximate. Also, only threads from the last 30 days are counted.