docs(s41): verified Foundry auth recipe + necessary-but-not-sufficient note
This commit is contained in:
parent
44d6902186
commit
123f71f547
2 changed files with 76 additions and 0 deletions
65
docs/2026-07-15-foundry-auth-recipe.md
Normal file
65
docs/2026-07-15-foundry-auth-recipe.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Azure AI Foundry auth recipe (S4.1)
|
||||
|
||||
> Verified against Microsoft Learn and the pinned `agent-framework-foundry==1.8.2` in 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
|
||||
> offline `python -m portfolio_optimiser.preflight --profile azure` checks the config; this doc
|
||||
> covers what preflight cannot: the actual authentication.
|
||||
|
||||
## The recipe (local dev, Intel Mac)
|
||||
|
||||
1. **`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).
|
||||
2. The code passes a **lazy `AzureCliCredential()`** (`azure.identity.aio`) to `FoundryChatClient`
|
||||
(`AzureFoundryBackend.create_chat_client`). Constructing the credential acquires **no token** —
|
||||
the token is fetched from the `az login` session only on the first live call. So passing a
|
||||
credential object is *not* auto-login.
|
||||
- `AzureCliCredential` is preferred over `DefaultAzureCredential` on a non-Azure host: the latter
|
||||
probes the IMDS managed-identity endpoint (times out / stalls) and can pick a stray identity.
|
||||
3. 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 the `REPLACE-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".
|
||||
Loading…
Add table
Add a link
Reference in a new issue