Einen API-Key in die Config zu kopieren ist der einfache Weg, Azure OpenAI aufzurufen — und der schnellste Weg, durch ein Enterprise-Security-Review zu fallen. Ein Key ist ein langlebiges Geheimnis: Er landet in Config-Dateien, in Pipelines, manchmal in der Git-Historie, und jeder Request mit dem Key sieht gleich aus — bei Audits lässt sich nicht sagen, welche App oder welche Person den Aufruf gemacht hat. In regulierten Branchen ist diese Kombination ein No-Go.
Dieser Walkthrough zeigt stattdessen das Muster, das Reviews besteht: schlüssellose Authentifizierung mit Managed Identity und DefaultAzureCredential — derselbe Code lokal und in Azure, und der schlüsselbasierte Zugriff auf die Ressource ist komplett deaktiviert. Er richtet sich an .NET-Entwickler, die nach Azure deployen und deren AI-Integration den Kontakt mit einem Security-Team überleben soll.
Was sich 2026 geändert hat
Wer Azure OpenAI zuletzt vor ein, zwei Jahren angefasst hat, findet vier Dinge anders vor:
- Microsoft Foundry ersetzt die eigenständige Azure-OpenAI-Ressource als empfohlenen Ressourcentyp (Provider
Microsoft.CognitiveServices, KindAIServices). Für Entra-ID-Authentifizierung ist eine Custom Subdomain erforderlich. - Die RBAC-Rolle für Inferenz heißt „Foundry User" — kürzlich umbenannt von „Azure AI User", gleiche Rollen-ID. Microsoft rät für Foundry-Szenarien von den älteren
Cognitive Services *-Rollen ab. - Das offizielle OpenAI .NET SDK gegen den OpenAI-v1-Endpunkt (
https://<resource>.openai.azure.com/openai/v1) ersetzt die veralteten Azure-spezifischen Inferenz-SDKs. Keinapi-version-Jonglieren mehr. - Der Token-Scope für Foundry ist
https://ai.azure.com/.default— die Brücke zwischenAzure.Identityund dem OpenAI SDK istBearerTokenPolicy.
Wie es funktioniert
Die API läuft auf App Service mit einer system-assigned Managed Identity — quasi ein Service-Konto, das Azure verwaltet: kein Passwort, kein Key. Wenn die App das Modell aufrufen will, holt sie sich ein Token von Entra ID, und Entra ID prüft, ob diese Identität die Rolle Foundry User auf der Ressource hat. Diese Rolle erlaubt das Aufrufen der Modelle und sonst nichts.
DefaultAzureCredential sorgt dafür, dass derselbe Code überall funktioniert. Es ist nicht ein einzelnes Credential, sondern eine Kette: Der Code probiert verschiedene Credential-Quellen durch und nimmt die erste, die funktioniert. Auf dem Laptop gibt es keine Managed Identity, also wird das Azure-CLI-Login verwendet; auf App Service findet er die Managed Identity. Kein If-Statement, keine Sonder-Config — was auch bedeutet, dass der eigene Benutzer ebenfalls die Foundry-User-Rolle braucht, damit die lokale Entwicklung funktioniert.
Der schlüssellose Code
Die ganze API ist eine .NET-10-Minimal-API mit einem Endpunkt. Der schlüssellose Teil sind etwa zehn Zeilen — der Umstieg ist kein großer Rewrite:
// DefaultAzureCredential probiert eine Kette von Credential-Quellen:// lokal greift es auf das Azure-CLI-Login zu (az login),// auf Azure App Service nutzt es die system-assigned Managed Identity.var credential = new DefaultAzureCredential();// "https://ai.azure.com/.default" ist der Token-Scope// für Microsoft-Foundry-Ressourcen.var tokenPolicy = new BearerTokenPolicy(credential, "https://ai.azure.com/.default");return new ChatClient(model: deployment,authenticationPolicy: tokenPolicy,options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) });
Zuerst den Token-Scope prüfen: Für Foundry-Ressourcen muss er https://ai.azure.com/.default lauten. Die BearerTokenPolicy-Konstruktor-Überladung ist noch als experimentell markiert (OPENAI001), daher braucht es ein Pragma, um die Warnung zu unterdrücken.
Die Config enthält nur den Endpunkt und den Deployment-Namen — keine Geheimnisse. Der echte Endpunkt liegt in einer gitignorierten appsettings.local.json, weshalb das Repo öffentlich sein kann.
Die Infrastruktur in Terraform
In echten Projekten klickt man das nicht im Portal zusammen. Drei Details im Terraform sind am wichtigsten:
resource "azurerm_cognitive_account" "foundry" {kind = "AIServices"sku_name = "S0"# Für Entra-ID-Authentifizierung ist eine Custom Subdomain erforderlich.custom_subdomain_name = "foundry-${var.base_name}-${random_string.suffix.result}"# Keys sind nicht nur ungenutzt - sie sind deaktiviert.# Jeder Request muss ein Entra-ID-Token tragen.local_auth_enabled = false}
- Die Custom Subdomain ist Pflicht. Ohne sie funktioniert die Entra-ID-Authentifizierung nicht — und die Fehlermeldung verrät nicht, warum.
local_auth_enabled = falsedeaktiviert den schlüsselbasierten Zugriff komplett. Wenn das Security-Review fragt, wie die Keys rotiert werden, lautet die Antwort: Es gibt keine Keys.- Die Rollenzuweisungen sind minimal. Die Managed Identity der Web-App bekommt die Foundry-User-Rolle, beschränkt auf diese eine Ressource — nicht die Resource Group, nicht die Subscription. Der eigene Benutzer bekommt dieselbe Rolle für die lokale Entwicklung. Das Terraform referenziert die Rolle per GUID (
53ca6127-db72-4b80-b1b0-d745d6d5456d), sodass die Umbenennung „Azure AI User" → „Foundry User" nichts kaputt machen kann.
Modelle werden nach Zeitplan abgekündigt — vor dem Festlegen einer Version prüfen, was die Region anbietet:
az cognitiveservices model list --location swedencentral
Lokal ausführen, dann deployen
az logincd infracp terraform.tfvars.example terraform.tfvars # Subscription + eigene Object-ID eintragenterraform initterraform apply
Die App über eine gitignorierte appsettings.local.json auf den openai_v1_endpoint-Output zeigen lassen, dann:
cd src/ChatApidotnet runcurl -X POST http://localhost:5002/chat \-H "Content-Type: application/json" \-d '{ "message": "Hello, who are you?" }'
Kein Key auf dem Rechner — DefaultAzureCredential hat das az login aufgegriffen, und die Foundry-User-Rolle hat den Rest erledigt. Das Deployment ist ein dotnet publish und ein az webapp deploy; die genauen Befehle stehen im README des Repos.
Was ein Security-Review am Ende tatsächlich anschaut, ist das Azure-Portal: Der App Service zeigt eine system-assigned Identity, für die es kein Passwort gibt, das leaken könnte; diese Identität hat genau eine Rolle auf genau einer Ressource — der gesamte Blast-Radius, falls die App je kompromittiert wird; und die Keys-Seite der Ressource ist deaktiviert. Nichts zu stehlen.
Rollenzuweisungen brauchen ein paar Minuten, bis sie greifen. Warten und erneut versuchen — vor Ablauf von ca. 5 Minuten nicht mit dem Debuggen anfangen.
Was Sie brauchen
- Ein Azure-Abonnement
- Azure CLI, eingeloggt mit
az login - .NET 10 SDK
- Terraform >= 1.9
In einem echten Enterprise-Setup kommt noch mehr dazu — user-assigned Identities, Sicherheitsgruppen und ein API-Management-Gateway vor den Modellen — aber das hier gezeigte schlüssellose Muster ist das Fundament, auf dem all das aufbaut.


