Accueil / Articles / APIM devant les appels de Foundry agentic avec RBAC aux droits minimaux

APIM devant les appels de Foundry agentic avec RBAC aux droits minimaux

Appels de modèles frontaux avec API Management, identité gérée, rôles personnalisés du plan de données et backends d’application fonctionnelle.

2858 mots

Ce que nous construisons

AI Foundry peut héberger de nombreuses familles de modèles. La création d’agents sur cette plateforme devient intéressante — mais aussi complexe — lorsque chaque modèle dispose de mécanismes d’authentification différents et que des règles relatives aux données personnelles ou PCI s’appliquent. Azure API Management (APIM) associé à des identités basées sur le principe du moindre privilège offre une porte d’entrée unique.

Partie 1 — Les fondations

Mettre en place les ressources Foundry/OpenAI, une Application Function qui appellera les modèles, ainsi qu’APIM qui servira d’interface pour cette fonction. Déterminer à l’avance les régions et la configuration du réseau privé.

Partie 2 — Déploiement des modèles

Déployer uniquement les modèles de chat dont vous avez réellement besoin. Enregistrer les noms de déploiement séparément des noms des modèles de base.

PARTIE 2 : RBAC personnalisé basé sur le principe du moindre privilège

OpenAI — uniquement les complétions de chat

Des rôles personnalisés qui n’autorisent que les actions sur les données nécessaires à votre fonction sont préférables aux attributions générales de type Cognitive Services Contributor.

Anthropic — les rôles intégrés pour les actions sur les données peuvent être insuffisants

Certaines routes de modèles tiers nécessitent des actions sur les données supplémentaires ; vérifiez-le à l’aide du corps d’erreur d’une appel échoué, puis mettez à jour le rôle — et non l’inverse.

RG="rg-foundry-func-demo"
LOCATION="eastus2"
UMI_NAME="umi-foundry-func"
FOUNDRY_NAME="foundry-func-demo"
STORAGE_NAME="stfoundryfuncdemo"
FUNC_NAME="func-foundry-py-$RANDOM"
SUBSCRIPTION_ID=$(az account show --query id -o tsv)

az group create --name $RG --location $LOCATION
az identity create --name $UMI_NAME --resource-group $RG

UMI_CLIENT_ID=$(az identity show --name $UMI_NAME --resource-group $RG --query clientId -o tsv)
UMI_PRINCIPAL_ID=$(az identity show --name $UMI_NAME --resource-group $RG --query principalId -o tsv)
UMI_RESOURCE_ID=$(az identity show --name $UMI_NAME --resource-group $RG --query id -o tsv)

az cognitiveservices account create \
  --name $FOUNDRY_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --kind AIServices \
  --sku S0 \
  --custom-domain $FOUNDRY_NAME \
  --yes

FOUNDRY_ENDPOINT=$(az cognitiveservices account show \
  --name $FOUNDRY_NAME --resource-group $RG \
  --query properties.endpoint -o tsv)

FOUNDRY_ID=$(az cognitiveservices account show \
  --name $FOUNDRY_NAME --resource-group $RG --query id -o tsv)
az cognitiveservices account deployment create \
  --name $FOUNDRY_NAME \
  --resource-group $RG \
  --deployment-name gpt-5-mini \
  --model-name gpt-5-mini \
  --model-version "2025-08-07" \
  --model-format OpenAI \
  --sku-name GlobalStandard \
  --sku-capacity 10

{
  "etag": "\"c39e7d0e-7c32-48a2-ba62-8a897265efbb\"",
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/deployments/gpt-5-mini",
  "name": "gpt-5-mini",
  "properties": {
    "callRateLimit": null,
    "capabilities": {
      "agentsV2": "true",
      "area": "US",
      "assistants": "true",
      "chatCompletion": "true",
      "responses": "true"
    },
    "capacitySettings": null,
    "currentCapacity": 10,
    "deploymentState": "Running",
    "dynamicThrottlingEnabled": null,
    "model": {
      "callRateLimit": null,
      "format": "OpenAI",
      "name": "gpt-5-mini",
      "publisher": null,
      "source": null,
      "sourceAccount": null,
      "version": "2025-08-07"
    },
    "parentDeploymentName": null,
    "provisioningState": "Succeeded",
    "raiPolicyName": "Microsoft.DefaultV2",
    "rateLimits": [
      {
        "count": 10.0,
        "dynamicThrottlingEnabled": null,
        "key": "request",
        "matchPatterns": null,
        "minCount": null,
        "renewalPeriod": 60.0
      },
      {
        "count": 10000.0,
        "dynamicThrottlingEnabled": null,
        "key": "token",
        "matchPatterns": null,
        "minCount": null,
        "renewalPeriod": 60.0
      }
    ],
    "routing": null,
    "scaleSettings": null,
    "serviceTier": null,
    "spilloverDeploymentName": null,
    "versionUpgradeOption": "OnceNewDefaultVersionAvailable"
  },
  "resourceGroup": "rg-foundry-func-demo",
  "sku": {
    "capacity": 10,
    "family": null,
    "name": "GlobalStandard",
    "size": null,
    "tier": null
  },
  "systemData": {
    "createdAt": "2026-07-05T18:03:12.346795+00:00",
    "createdBy": "krishnan.sriram@hotmail.com",
    "createdByType": "User",
    "lastModifiedAt": "2026-07-05T18:03:12.346795+00:00",
    "lastModifiedBy": "krishnan.sriram@hotmail.com",
    "lastModifiedByType": "User"
  },
  "tags": null,
  "type": "Microsoft.CognitiveServices/accounts/deployments"
}
FOUNDRY_ID=$(az cognitiveservices account show \
  --name $FOUNDRY_NAME --resource-group $RG --query id -o tsv)

az resource create \
  --id "$FOUNDRY_ID/deployments/claude-opus-4-6" \
  --api-version 2025-10-01-preview \
  --is-full-object \
  --properties '{
    "location": "eastus2",
    "properties": {
      "model": {
        "format": "Anthropic",
        "name": "claude-opus-4-6",
        "version": "1"
      },
      "modelProviderData": {
        "organizationName": "SRK Tech",
        "countryCode": "US",
        "industry": "technology"
      }
    },
    "sku": {
      "name": "GlobalStandard",
      "capacity": 10
    }
  }'
# execution Results
{
  "etag": "\"a5b65a53-a65a-494b-9059-b16dc700b743\"",
  "extendedLocation": null,
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/deployments/claude-opus-4-6",
  "identity": null,
  "kind": null,
  "location": null,
  "managedBy": null,
  "name": "claude-opus-4-6",
  "plan": null,
  "properties": {
    "capabilities": {
      "agentsV2": "true",
      "chatCompletion": "true"
    },
    "currentCapacity": 10,
    "deploymentState": "Running",
    "model": {
      "format": "Anthropic",
      "name": "claude-opus-4-6",
      "version": "1"
    },
    "modelProviderData": {
      "countryCode": "US",
      "industry": "technology",
      "organizationName": "SRK Tech"
    },
    "provisioningState": "Succeeded",
    "raiPolicyName": "Microsoft.DefaultV2",
    "rateLimits": [
      {
        "count": 10,
        "key": "request",
        "renewalPeriod": 60
      },
      {
        "count": 10000,
        "key": "token",
        "renewalPeriod": 60
      }
    ],
    "versionUpgradeOption": "OnceNewDefaultVersionAvailable"
  },
  "resourceGroup": "rg-foundry-func-demo",
  "sku": {
    "capacity": 10,
    "family": null,
    "model": null,
    "name": "GlobalStandard",
    "size": null,
    "tier": null
  },
  "systemData": {
    "createdAt": "2026-07-05T18:06:43.6897129Z",
    "createdBy": "krishnan.sriram@hotmail.com",
    "createdByType": "User",
    "lastModifiedAt": "2026-07-05T18:06:43.6897129Z",
    "lastModifiedBy": "krishnan.sriram@hotmail.com",
    "lastModifiedByType": "User"
  },
  "tags": null,
  "type": "Microsoft.CognitiveServices/accounts/deployments"
}
az cognitiveservices account deployment list \
  --name $FOUNDRY_NAME --resource-group $RG -o table
Name             ResourceGroup
---------------  --------------------
gpt-5-mini       rg-foundry-func-demo
claude-opus-4-6  rg-foundry-func-demo
cat > /tmp/foundry-openai-role.json << EOF
{
  "Name": "Foundry OpenAI Chat Inference User",
  "IsCustom": true,
  "Description": "Minimal data-plane access to call chat completions on a Microsoft Foundry OpenAI deployment.",
  "Actions": [],
  "DataActions": [
    "Microsoft.CognitiveServices/accounts/OpenAI/deployments/chat/completions/action"
  ],
  "AssignableScopes": ["/subscriptions/$SUBSCRIPTION_ID"]
}
EOF
az role definition create --role-definition /tmp/foundry-openai-role.json

# Execution results
{
  "assignableScopes": [
    "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe"
  ],
  "createdBy": null,
  "createdOn": "2026-07-05T18:22:39.111105+00:00",
  "description": "Minimal data-plane access to call chat completions on a Microsoft Foundry OpenAI deployment.",
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/c7035712-c66d-482d-9fd5-0fc116a2d9f4",
  "name": "c7035712-c66d-482d-9fd5-0fc116a2d9f4",
  "permissions": [
    {
      "actions": [],
      "condition": null,
      "conditionVersion": null,
      "dataActions": [
        "Microsoft.CognitiveServices/accounts/OpenAI/deployments/chat/completions/action"
      ],
      "notActions": [],
      "notDataActions": []
    }
  ],
  "roleName": "Foundry OpenAI Chat Inference User",
  "roleType": "CustomRole",
  "type": "Microsoft.Authorization/roleDefinitions",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:22:39.111105+00:00"
}

az role assignment create \
  --assignee-object-id $UMI_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Foundry OpenAI Chat Inference User" \
  --scope $FOUNDRY_ID

# Execution results
{
  "condition": null,
  "conditionVersion": null,
  "createdBy": null,
  "createdOn": "2026-07-05T18:23:42.642379+00:00",
  "delegatedManagedIdentityResourceId": null,
  "description": null,
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/providers/Microsoft.Authorization/roleAssignments/580a3b61-aaad-456e-9b3e-6ced977dd378",
  "name": "580a3b61-aaad-456e-9b3e-6ced977dd378",
  "principalId": "a114ced5-b53e-4c42-8949-bddd053610cc",
  "principalType": "ServicePrincipal",
  "resourceGroup": "rg-foundry-func-demo",
  "roleDefinitionId": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/c7035712-c66d-482d-9fd5-0fc116a2d9f4",
  "scope": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01",
  "type": "Microsoft.Authorization/roleAssignments",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:23:42.913375+00:00"
}
cat > /tmp/foundry-anthropic-role.json << EOF
{
  "Name": "Foundry Anthropic Inference User",
  "IsCustom": true,
  "Description": "Minimal data-plane access to invoke Claude models on a Microsoft Foundry AIServices resource.",
  "Actions": [],
  "DataActions": [
    "Microsoft.CognitiveServices/accounts/AIServices/providers/action",
    "Microsoft.CognitiveServices/accounts/OpenAI/*/read"
  ],
  "AssignableScopes": ["/subscriptions/$SUBSCRIPTION_ID"]
}
EOF

az role definition create --role-definition /tmp/foundry-anthropic-role.json
# Execution results
{
  "assignableScopes": [
    "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe"
  ],
  "createdBy": null,
  "createdOn": "2026-07-05T18:26:14.247654+00:00",
  "description": "Minimal data-plane access to invoke Claude models on a Microsoft Foundry AIServices resource.",
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/323e0475-0098-4161-b03d-69bce7452c9c",
  "name": "323e0475-0098-4161-b03d-69bce7452c9c",
  "permissions": [
    {
      "actions": [],
      "condition": null,
      "conditionVersion": null,
      "dataActions": [
        "Microsoft.CognitiveServices/accounts/AIServices/providers/action",
        "Microsoft.CognitiveServices/accounts/OpenAI/*/read"
      ],
      "notActions": [],
      "notDataActions": []
    }
  ],
  "roleName": "Foundry Anthropic Inference User",
  "roleType": "CustomRole",
  "type": "Microsoft.Authorization/roleDefinitions",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:26:14.247654+00:00"
}

az role assignment create \
  --assignee-object-id $UMI_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Foundry Anthropic Inference User" \
  --scope $FOUNDRY_ID

# Execution Results
{
  "condition": null,
  "conditionVersion": null,
  "createdBy": null,
  "createdOn": "2026-07-05T18:28:12.789555+00:00",
  "delegatedManagedIdentityResourceId": null,
  "description": null,
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/providers/Microsoft.Authorization/roleAssignments/18e509dc-b539-49d8-a2e6-65a12415e206",
  "name": "18e509dc-b539-49d8-a2e6-65a12415e206",
  "principalId": "a114ced5-b53e-4c42-8949-bddd053610cc",
  "principalType": "ServicePrincipal",
  "resourceGroup": "rg-foundry-func-demo",
  "roleDefinitionId": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/323e0475-0098-4161-b03d-69bce7452c9c",
  "scope": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01",
  "type": "Microsoft.Authorization/roleAssignments",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:28:13.050559+00:00"
}
az storage account create \
  --name $STORAGE_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --sku Standard_LRS
az functionapp create \
  --name $FUNC_NAME \
  --resource-group $RG \
  --storage-account $STORAGE_NAME \
  --consumption-plan-location $LOCATION \
  --runtime python \
  --runtime-version 3.12 \
  --functions-version 4 \
  --os-type Linux
az functionapp identity assign \
  --name $FUNC_NAME \
  --resource-group $RG \
  --identities $UMI_RESOURCE_ID
# Execution results
{
  "principalId": null,
  "tenantId": null,
  "type": "UserAssigned",
  "userAssignedIdentities": {
    "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourcegroups/rg-foundry-func-demo/providers/Microsoft.ManagedIdentity/userAssignedIdentities/umi-foundry-func": {
      "clientId": "e7c8a6b7-bd4e-43c7-b49b-d9d6a9e01821",
      "principalId": "a114ced5-b53e-4c42-8949-bddd053610cc"
    }
  }
}
az functionapp config appsettings set \
  --name $FUNC_NAME \
  --resource-group $RG \
  --settings \
    FOUNDRY_ENDPOINT="$FOUNDRY_ENDPOINT" \
    FOUNDRY_DEPLOYMENT="gpt-5-mini" \
    FOUNDRY_API_VERSION="2024-12-01-preview" \
    CLAUDE_BASE_URL="https://${FOUNDRY_NAME}.services.ai.azure.com/anthropic" \
    CLAUDE_OPUS_DEPLOYMENT="claude-opus-4-6" \
    LANGUAGE_ENDPOINT="$FOUNDRY_ENDPOINT" \
    LANGUAGE_API_VERSION="2024-11-01" \
    UMI_CLIENT_ID="$UMI_CLIENT_ID"
az functionapp config appsettings list \
  --name $FUNC_NAME --resource-group $RG \
  --query "[].name" -o tsv

# Execution results
FUNCTIONS_WORKER_RUNTIME
FUNCTIONS_EXTENSION_VERSION
AzureWebJobsStorage
WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
WEBSITE_CONTENTSHARE
AzureWebJobsDashboard
FOUNDRY_ENDPOINT
FOUNDRY_DEPLOYMENT
FOUNDRY_API_VERSION
CLAUDE_BASE_URL
CLAUDE_OPUS_DEPLOYMENT
LANGUAGE_ENDPOINT
LANGUAGE_API_VERSION
UMI_CLIENT_ID
mkdir foundry-llm-function-py && cd foundry-llm-function-py

func new --name AskLLM --template "HTTP trigger" --authlevel function
azure-functions
azure-identity
openai
anthropic
requests
import azure.functions as func
import json
import logging
import os

from azure.identity import ManagedIdentityCredential, get_bearer_token_provider
from openai import AzureOpenAI
from anthropic import AnthropicFoundry

app = func.FunctionApp()

# --- Shared config ---
FOUNDRY_ENDPOINT = os.environ.get("FOUNDRY_ENDPOINT")
FOUNDRY_API_VERSION = os.environ.get("FOUNDRY_API_VERSION", "2024-12-01-preview")
CLAUDE_BASE_URL = os.environ.get("CLAUDE_BASE_URL")
UMI_CLIENT_ID = os.environ.get("UMI_CLIENT_ID")

# --- Per-model deployment names ---
GPT_DEPLOYMENT = os.environ.get("FOUNDRY_DEPLOYMENT")            # e.g. "gpt-5-mini"
CLAUDE_OPUS_DEPLOYMENT = os.environ.get("CLAUDE_OPUS_DEPLOYMENT")    # e.g. "claude-opus-4-6"

DEFAULT_MODEL = "gpt"

# --- Single UMI credential, shared by both clients ---
_credential = ManagedIdentityCredential(client_id=UMI_CLIENT_ID)

_openai_token_provider = get_bearer_token_provider(
    _credential, "https://cognitiveservices.azure.com/.default"
)
_claude_token_provider = get_bearer_token_provider(
    _credential, "https://cognitiveservices.azure.com/.default"
)

_openai_client = AzureOpenAI(
    azure_endpoint=FOUNDRY_ENDPOINT,
    api_version=FOUNDRY_API_VERSION,
    azure_ad_token_provider=_openai_token_provider,
)

_claude_client = AnthropicFoundry(
    azure_ad_token_provider=_claude_token_provider,
    base_url=CLAUDE_BASE_URL,
)

# --- Model registry: alias -> (provider, deployment name) ---
MODEL_REGISTRY = {
    "gpt": ("openai", GPT_DEPLOYMENT),
    "opus": ("anthropic", CLAUDE_OPUS_DEPLOYMENT),
}


def _call_openai(deployment: str, query: str, user_context: str | None) -> str:
    messages = []
    if user_context:
        messages.append({"role": "system", "content": str(user_context)})
    messages.append({"role": "user", "content": query})

    result = _openai_client.chat.completions.create(
        model=deployment,
        messages=messages,
    )
    return result.choices[0].message.content or ""


def _call_claude(deployment: str, query: str, user_context: str | None) -> str:
    kwargs = {
        "model": deployment,
        "max_tokens": 1024,
        "messages": [{"role": "user", "content": query}],
    }
    if user_context:
        kwargs["system"] = [{"type": "text", "text": str(user_context)}]

    result = _claude_client.messages.create(**kwargs)
    return "".join(block.text for block in result.content if block.type == "text")


@app.route(route="ask", methods=["POST"], auth_level=func.AuthLevel.FUNCTION)
def AskLLM(req: func.HttpRequest) -> func.HttpResponse:
    try:
        body = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": "Request body must be valid JSON"}),
            status_code=400,
            mimetype="application/json",
        )

    query = body.get("query")
    user_context = body.get("context")
    model_alias = (body.get("model") or DEFAULT_MODEL).strip().lower()

    if not query or not isinstance(query, str):
        return func.HttpResponse(
            json.dumps({"error": '"query" (string) is required in the request body'}),
            status_code=400,
            mimetype="application/json",
        )

    if model_alias not in MODEL_REGISTRY:
        return func.HttpResponse(
            json.dumps({
                "error": f'"model" must be one of: {", ".join(MODEL_REGISTRY.keys())}',
            }),
            status_code=400,
            mimetype="application/json",
        )

    provider, deployment = MODEL_REGISTRY[model_alias]

    if not deployment:
        return func.HttpResponse(
            json.dumps({"error": f'No deployment configured for model "{model_alias}"'}),
            status_code=500,
            mimetype="application/json",
        )

    try:
        if provider == "openai":
            answer = _call_openai(deployment, query, user_context)
        else:
            answer = _call_claude(deployment, query, user_context)

        return func.HttpResponse(
            json.dumps({"answer": answer, "model": model_alias}),
            status_code=200,
            mimetype="application/json",
        )
    except Exception as e:
        logging.exception(f"Foundry call failed for model={model_alias}")
        return func.HttpResponse(
            json.dumps({"error": "Failed to get a response from the LLM", "detail": str(e)}),
            status_code=500,
            mimetype="application/json",
        )
func azure functionapp publish $FUNC_NAME

Getting site publishing info...
[2026-07-05T23:43:36.764Z] Starting the function app deployment...
Removing WEBSITE_CONTENTAZUREFILECONNECTIONSTRING app setting.
Removing WEBSITE_CONTENTSHARE app setting.
Creating archive for current directory...
Performing remote build for functions project.
Creating placeholder blob for linux consumption function app...
.....................
.....................
.....................
SCM_RUN_FROM_PACKAGE placeholder blob scm-latest-func-foundry-py-19514.zip located
Uploading built content /home/site/artifacts/functionappartifact.squashfs for linux consumption function app...
Resetting all workers for func-foundry-py-19514.azurewebsites.net
Deployment successful. deployer = Push-Deployer deploymentPath = Functions App ZipDeploy. Extract zip. Remote build.
Remote build succeeded!
[2026-07-05T23:44:23.731Z] Syncing triggers...
Functions in func-foundry-py-19514:
    AskLLM - [httpTrigger]
        Invoke url: https://func-foundry-py-19514.azurewebsites.net/api/ask
FUNC_KEY=$(az functionapp keys list \
  --name $FUNC_NAME --resource-group $RG \
  --query "functionKeys.default" -o tsv)
curl -X POST "https://$FUNC_NAME.azurewebsites.net/api/ask?code=$FUNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the capital of France?"}'

# Execution results
{"answer": "The capital of France is Paris.", "model": "gpt"}
curl -X POST "https://$FUNC_NAME.azurewebsites.net/api/ask?code=$FUNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the capital of France?", "model": "opus"}'

# Execution results
{"answer": "The capital of France is **Paris**.", "model": "opus"}
az group delete --name $RG --yes --no-wait
az role definition delete --name "Foundry OpenAI Chat Inference User"
az role definition delete --name "Foundry Anthropic Inference User"

PARTIE 3 — Azure Function

1–2. Stockage + Application fonctionnelle Python

3. Attacher une identité gérée attribuée par l’utilisateur

4. Paramètres de l’application

URL d’endpoint, noms de déploiement, clés APIM ou paramètres de public cible — ne commitez jamais de secrets.

5–7. Structure de base, requirements.txt, function_app.py, déploiement

8. Tester via APIM

Appelez la route APIM, et non l’URL de la fonction, afin que les politiques (limitation de débit, JWT, filtre IP) s’appliquent réellement.

Référence de dépannage

Les codes 401/403 indiquent généralement un problème de RBAC ou un nom de déploiement incorrect. Le code 404 signifie souvent une incompatibilité entre région et modèle. Les tempêtes de délais d’attente sont souvent dues au démarrage en froid de la fonction, alors que le délai d’attente du backend APIM est trop bas.

Leçons apprises

Centralisez l’accès aux modèles derrière APIM ; utilisez des identités gérées pour ces modèles ; maintenez un contrôle strict sur le RBAC personnalisé ; testez les actions sur les données de chaque nouvelle famille de modèles ; enregistrez les IDs de corrélation depuis APIM, jusqu’à la fonction, puis jusqu’à l’appel au modèle.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défaillant ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Conseil de politique : placez les budgets de tokens et les limites de débit par principal dans APIM afin qu’un cycle d’agent défectueux ne puisse pas contourner les limites au niveau de l’application en appelant directement la fonction — bloquez l’accès direct aux fonctions dans les réseaux de production.

Chaque environnement nécessite des produits et des identités APIM distincts. Partager un déploiement Foundry entre l’environnement de développement et la production pour « économiser de l’argent » entraîne généralement des fuites d’informations et brise l’isolation nécessaire.

Décrivez clairement quels principaux peuvent créer des déploiements de modèles et lesquels ne peuvent que les invoquer. Des droits d’invoque sans droits de déploiement constituent généralement une répartition saine.

Lors de l’ajout d’une nouvelle famille de modèles, mettez à jour le rôle personnalisé, le backend APIM, les paramètres de la fonction ainsi qu’un test de fonctionnement dans la même demande de modification. Les déploiements partiels expliquent pourquoi les erreurs 403 n’apparaissent qu’en vendredi.

Préférez les en-têtes de corrélation provenant d’APIM, passant par la fonction jusqu’à l’enregistrement dans le SDK du modèle, afin qu’un seul ticket de support puisse reconstituer l’ensemble du parcours.

Chaque environnement nécessite des produits et des identités APIM distincts. Partager un déploiement Foundry entre l’environnement de développement et celui de production pour « économiser de l’argent » entraîne généralement des fuites d’informations et brise l’isolation nécessaire.

Décrivez clairement quels utilisateurs peuvent créer des déploiements de modèles et quels autres ne peuvent que les invoquer. Des droits d’invoque sans droits de déploiement constituent généralement une répartition équilibrée.

Lors de l’ajout d’une nouvelle famille de modèles, mettez à jour le rôle personnalisé, le backend APIM, les paramètres de la fonction ainsi qu’un test de fonctionnement dans la même demande de modification. Les déploiements partiels expliquent pourquoi les erreurs 403 n’apparaissent qu’en vendredi.

Préférez les en-têtes de corrélation provenant d’APIM, passant par la fonction jusqu’à l’enregistrement dans le SDK du modèle, afin qu’un seul ticket de support puisse reconstituer l’ensemble du chemin.

Chaque environnement nécessite des produits et des identités APIM distincts. Partager une même déploiement Foundry entre l’environnement de développement et celui de production pour « économiser de l’argent » entraîne généralement des fuites d’informations et brise l’isolation nécessaire.

Décrivez clairement quels utilisateurs peuvent créer des déploiements de modèles et quels autres ne peuvent que les invoquer. Des droits d’invoque sans droits de déploiement constituent généralement une répartition équilibrée.

Lors de l’ajout d’une nouvelle famille de modèles, mettez à jour le rôle personnalisé, le backend APIM, les paramètres de la fonction ainsi qu’un test de validation dans la même demande de modification. Les mises à jour partielles expliquent pourquoi les erreurs 403 apparaissent uniquement le vendredi.

Préférez les en-têtes de corrélation provenant d’APIM, passant par la fonction jusqu’à l’enregistrement dans le SDK du modèle, afin qu’un seul ticket de support puisse reconstituer l’ensemble du chemin.

Des environnements distincts nécessitent des produits APIM et des identités séparés. Partager une même déploiement Foundry entre l’environnement de développement et le produit en production pour « économiser de l’argent » entraîne généralement des fuites d’informations et brise l’isolation requise.