portfolio-optimiser/docs/2026-07-15-foundry-auth-recipe.md
Kjell Tore Guttormsen 142bfa9666 docs(1b): Claude på Foundry er en TREDJE klientflate — FoundryChatClient kan ikke binde den
STATE bar påstanden «uverifisert: om FoundryChatClient kan binde en Claude-
deployment» som en bisetning. Operatøren ba om at slikt ikke får ligge. Målt mot
Microsoft Learn i stedet, og antakelsen er FALSIFISERT.

Claude-modeller solgt via Azure Marketplace kalles med Anthropics egen Messages
API på en egen endepunkt-form:

  https://<resource>.services.ai.azure.com/anthropic/v1/messages   <- Claude
  https://<resource>.services.ai.azure.com/api/projects/<project>  <- FoundryChatClient

Klienter: `anthropic`-pakka, `@anthropic-ai/foundry-sdk`, eller REST med
`anthropic-version: 2023-06-01`. Kun /v1/messages og /v1/messages/count_tokens er
eksponert på «Hosted on Azure»-versjonen. Deployment-navnet er `model`-parameteren.

Konsekvens for M1 (fase 1b), og den er en BESLUTNING, ikke en detalj:
- Microsoft-solgt modell (gpt-5-mini o.l.) -> 1b er REN KONFIG, azure-profilen
  som shippet er riktig søm, ingenting å bygge.
- Claude -> 1b krever en NY BACKEND-PROFIL i produksjonskode.

Samme klasse som notatet under «Endpoint format»: *.openai.azure.com og
*.cognitiveservices.azure.com er andre flater igjen. Tre flater, én ressurs-host.

Samme pass fanget forutsetningene som gater portalarbeidet: Marketplace-subscribe-
tillatelse, Contributor/Owner på ressursgruppa, prosjekt i støttet region, og data
retention PÅ abonnementet for Covered Models (ZDR gir 400, og Microsoft kan ikke
endre det for deg).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X737cbkG2uAXJ2Bvhf6X5M
2026-08-13 20:31:36 +02:00

5.7 KiB

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. 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).

Claude models on Foundry are a THIRD client surface (measured 2026-08-13)

Verified against Microsoft Learn (microsoft_docs_search, "Deploy and use Claude models in Microsoft Foundry" / "Claude models in Microsoft Foundry — API overview"), because the question came up while planning M1 and an assumption was cheaper to falsify than to inherit.

FoundryChatClient CANNOT serve a Claude deployment. Claude models sold through the Azure Marketplace are called with Anthropic's own Messages API, on their own endpoint shape:

https://<resource>.services.ai.azure.com/anthropic/v1/messages     # Claude
https://<resource>.services.ai.azure.com/api/projects/<project>    # FoundryChatClient

Clients: the anthropic Python package (or @anthropic-ai/foundry-sdk, or REST with the anthropic-version: 2023-06-01 header). Only POST /v1/messages and POST /v1/messages/count_tokens are exposed on the Hosted on Azure version. The deployment name is what goes in the model parameter — same rule as the Foundry surface, different endpoint.

What this means for M1 (fase 1b):

  • A Microsoft-sold model (gpt-5-mini, gpt-4.1-mini, …) → M1 is configuration only. The azure profile as shipped is the right seam; nothing to build.
  • A Claude model → M1 needs a NEW backend profile (production code, anthropic SDK, Entra ID or key auth). That is a decision to take before the portal work, not a discovery during it.

This is the same class as the note under Endpoint format: *.openai.azure.com and *.cognitiveservices.azure.com are other surfaces again. Three surfaces, one resource host.

Extra prerequisites measured in the same pass (they gate the portal work, not the code): Marketplace subscription permission, Contributor or Owner on the resource group, a project in a region the model supports, and — for Anthropic-designated Covered Models — data retention enabled on the subscription (zero-data-retention subscriptions get a 400 invalid_request_error, and Microsoft cannot change that setting for you).

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".