3.7 KiB
Azure AI Foundry auth recipe (S4.1)
Verified against Microsoft Learn and the pinned
agent-framework-foundry==1.8.2in this repo's.venv(see.claude/projects/2026-07-15-s41-azure-preflight/research/01-foundry-auth-recipe.md). This note is the operator's manual auth recipe for the single planned live Foundry run (M1). The offlinepython -m portfolio_optimiser.preflight --profile azurechecks the config; this doc covers what preflight cannot: the actual authentication.
The recipe (local dev, Intel Mac)
az login— sign in to the correct Entra tenant with the Azure CLI. This is a manual operator step; the code never runs it (no auto-login).- The code passes a lazy
AzureCliCredential()(azure.identity.aio) toFoundryChatClient(AzureFoundryBackend.create_chat_client). Constructing the credential acquires no token — the token is fetched from theaz loginsession only on the first live call. So passing a credential object is not auto-login.AzureCliCredentialis preferred overDefaultAzureCredentialon a non-Azure host: the latter probes the IMDS managed-identity endpoint (times out / stalls) and can pick a stray identity.
- Set the config the preflight validates:
PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT— the Foundry project endpoint (below).PORTFOLIO_MODEL_MAP— path to an out-of-tree model_map with real azure deployment names, so tenant-specific names are never committed. (Or replace theREPLACE-WITH-*placeholders.)
FoundryChatClient signature (pinned 1.8.2)
FoundryChatClient(*, project_endpoint, model, credential, ...) — all keyword-only. credential
is REQUIRED on the project_endpoint path: the 1.8.2 client raises
ValueError("Azure credential is required when using project_endpoint without a project_client.")
at construction if omitted. There is no lazy DefaultAzureCredential default. The model
parameter is model (the portal deployment name), not deployment_name.
Endpoint format
Canonical Foundry project endpoint:
https://<resource>.services.ai.azure.com/api/projects/<project>
The bare-host form https://<resource>.services.ai.azure.com also appears in official samples and
is accepted. The preflight requires https:// + a host ending .services.ai.azure.com; it does
not require the /api/projects/ path. A *.openai.azure.com or *.cognitiveservices.azure.com
endpoint is a different client surface (use OpenAIChatClient, not FoundryChatClient).
RBAC role
Assign Foundry User (role GUID 53ca6127-db72-4b80-b1b0-d745d6d5456d) to the identity, at the
Foundry resource/project scope. Do NOT use Azure AI Developer (scoped to hubs/ML
workspaces, not Foundry projects) or Cognitive Services User (the classic Azure OpenAI surface).
Note the recent rename: Foundry User was formerly Azure AI User — reference the GUID, not the
display name, since older community answers use both.
Preflight is necessary-but-not-sufficient
A green preflight rules out the offline-detectable misconfiguration class: unset endpoint,
malformed/wrong-surface endpoint URL, unresolved REPLACE-WITH-* placeholder deployments, and an
inconsistent model-map. It cannot prove the paid live call will succeed. The following surface
only at the live call and are out of scope for an offline check:
- RBAC — missing/wrong role → 403 (propagation lag can cause 401-then-200).
- Token / tenant / consent — expired or wrong-tenant credential → 401.
DeploymentNotFound— a well-formed deployment name that does not exist in the project → 404.- api-version skew and regional outages.
Treat a green preflight as "config is shaped correctly", never as "M1 will work".