Startseite / Artikel / APIM vor agnostischen Foundry-Aufrufen mit RBAC nach dem Prinzip des geringsten Privilegs

APIM vor agnostischen Foundry-Aufrufen mit RBAC nach dem Prinzip des geringsten Privilegs

Frontend-Modellaufrufe mit API Management, verwalteter Identität, benutzerdefinierten Datenflächenrollen und Function App-Backends.

2858 Wörter

Was wir entwickeln

AI Foundry kann viele Modellfamilien beherbergen. Das Erstellen von Agenten darauf wird interessant – und kompliziert –, wenn jedes Modell unterschiedliche Authentifizierungsmechanismen hat und PII/PCI-Regeln gelten. Azure API Management (APIM) in Kombination mit Identitäten nach dem Prinzip des geringsten Privilegs bietet ein zentrales Eingangstor.

Teil 1 – Die Grundlage

Versorgen Sie die Foundry/OpenAI-Ressourcen, eine Function App, die die Modelle aufruft, sowie APIM, das diese Funktion abwickelt. Entscheiden Sie im Voraus über Regionen und private Netzwerke.

Teil 2 – Bereitstellung der Modelle

Bereiten Sie nur die Chat-Modelle vor, die Sie tatsächlich benötigen. Notieren Sie die Bereitstellungsnamen getrennt von den Namen der Basismodelle.

TEIL 2: Anpassbare RBAC-Regeln mit geringstem Privileg

OpenAI – nur Chat-Completions

Anpassbare Rollen, die nur die Datenoperationen zulassen, die Ihre Funktion benötigt, sind besser als allgemeine Zuweisungen im Rahmen von Cognitive Services Contributor.

Anthropic – eingebaute Rollen könnten Datenaktionen übersehen

Einige Drittanbieter-Modellrouten benötigen zusätzliche Datenaktionen; überprüfen Sie dies am Fehlerinhalt einer fehlgeschlagenen Anfrage und passen Sie anschließend die Rolle an – nicht umgekehrt.

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"

TEIL 3 – Azure Function

1–2. Speicherung + Python Function App

3. Hinzufügen einer vom Benutzer zugewiesenen verwalteten Identität

4. Anwendungs-Einstellungen

Endpoint-URLs, Bereitstellungsnamen, APIM-Schlüssel oder Zielgruppen-Einstellungen – vertrauliche Informationen sollten niemals gespeichert werden.

5–7. Scaffold, requirements.txt, function_app.py, Bereitstellung

8. Testen über APIM

Rufen Sie die APIM-Route auf, nicht die Funktion-URL, damit Richtlinien (Durchsatzbegrenzung, JWT, IP-Filter) tatsächlich angewendet werden.

Hilfsmittel zur Fehlerbehebung

401/403 deuten in der Regel auf RBAC oder einen falschen Bereitstellungsnamen hin. 404 bedeutet oft eine Inkonsistenz zwischen Region und Modell. Timeout-Stürme entstehen häufig dadurch, dass die Funktion neu gestartet wird, während der Backend-Timer von APIM zu niedrig eingestellt ist.

Gelernte Erkenntnisse

Zentralisieren Sie den Zugriff auf Modelle hinter APIM; verwenden Sie verwaltete Identitäten für die Modelle; halten Sie das benutzerdefinierte RBAC streng; testen Sie die Datenvorgänge jeder neuen Modellfamilie; protokollieren Sie die Korrelations IDs von APIM über die Funktion bis zur Modellaufruf.

Entwicklung ohne API-Frontdoor führt erneut zu einer unkontrollierten Verbreitung von Schlüsseln. APIM ist der Ort, an dem der Verkehr der Agenten handhabbar wird.

Empfehlung zur Richtlinie: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Empfehlung zur Richtlinie: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Empfehlung zur Richtlinie: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Empfehlung zur Richtlinie: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Empfehlung zur Richtlinie: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Empfehlung zur Richtlinie: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie den direkten Funktionszugriff in Produktivnetzwerken.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie in Produktivnetzwerken direkte Funktionsaufrufe.

Strategietipp: Legen Sie Token-Budgets sowie Rate-Limits pro Benutzer in APIM fest, damit ein fehlerhaftes Agenten-Looping die auf Anwendungsebene festgelegten Grenzen nicht umgehen kann, indem es die Funktion direkt aufruft – blockieren Sie in Produktivnetzwerken direkte Funktionsaufrufe.

Getrennte Umgebungen benötigen separate APIM-Produkte und Identitäten. Das Teilen einer einzigen Foundry-Deployment zwischen Entwicklung und Produktivumgebung, um „Geld zu sparen“, führt in der Regel dazu, dass Anfragen durchsickern und die Isolation zwischen den Bereichen beeinträchtigt wird.

Dokumentieren Sie, welche Benutzer Modelle deployen dürfen und welche sie nur aufrufen können. Rechte zum Aufruf ohne Rechte zur Deployment sind in der Regel die gesunde Aufteilung.

Beim Hinzufügen einer neuen Modellfamilie sollten die benutzerdefinierte Rolle, der APIM-Backend, die Funktionseinstellungen sowie ein Smoke-Test im selben PR aktualisiert werden. Durch teilweises Rollout treten 403-Fehler nur an Freitagen auf.

Es ist vorzuziehen, Korrelations-Header von APIM über die Funktion bis hin zum Protokollierungssystem des Modell-SDKs zu verwenden, damit ein einzelnes Support-Ticket den vollständigen Ablauf nachvollziehen kann.

Getrennte Umgebungen benötigen separate APIM-Produkte und -Identitäten. Das Teilen derselben Foundry-Deployment zwischen Entwicklungs- und Produktumgebung, um Geld zu sparen, führt in der Regel dazu, dass Anfragen durchsickern und die Isolation zwischen den Umgebungen beeinträchtigt wird.

Dokumentieren Sie, welche Benutzer Modelle deployen dürfen und welche sie nur aufrufen können. Rechte zum Aufruf ohne Rechte zum Deployen bilden in der Regel eine gesunde Aufteilung.

Beim Hinzufügen einer neuen Modellfamilie sollten die benutzerdefinierte Rolle, der APIM-Backend, die Funktionseinstellungen sowie ein Smoke-Test im selben PR aktualisiert werden. Durch teilweises Rollout treten 403-Fehler nur an Freitagen auf.

Es sollten Korrelations-Header von APIM über die Funktion in das Logging des Modell-SDK gesendet werden, damit ein einzelnes Support-Ticket den vollständigen Pfad rekonstruieren kann.

Getrennte Umgebungen erfordern separate APIM-Produkte und Identitäten. Das Teilen einer einzigen Foundry-Deployment zwischen Entwicklungs- und Produktumgebung, um Geld zu sparen, führt in der Regel dazu, dass Anfragen durchsickern und die Isolation zwischen den Umgebungen beeinträchtigt wird.

Dokumentieren Sie, welche Benutzer Principals Modelle deployen dürfen und welche sie nur aufrufen können. Rechte zum Aufruf ohne Rechte zur Deployment sind die übliche gesunde Aufteilung.

Beim Hinzufügen einer neuen Modellfamilie sollten die benutzerdefinierte Rolle, der APIM-Backend, die Funktionseinstellungen sowie ein Smoke-Test im selben Pull Request aktualisiert werden. Teilweise Rollouts sind der Grund, warum 403-Fehler nur an Freitagen auftreten.

Es sollten Korrelations-Header von APIM über die Funktion in das Logging des Modell-SDK gesendet werden, damit ein einzelnes Support-Ticket den vollständigen Pfad rekonstruieren kann.

Getrennte Umgebungen benötigen separate APIM-Produkte und -Identitäten. Das Teilen einer einzigen Foundry-Deployment zwischen Entwicklungs- und Produktumgebung, um „Geld zu sparen“, führt in der Regel dazu, dass Anfragen durchsickern und die Isolation zwischen den Bereichen beeinträchtigt wird.