Managed Identity in Azure: Auth ohne Secrets
Wer kennt es nicht: Ein Connection String mit eingebettetem Passwort in der appsettings.json, ein Storage Access Key in den Pipeline-Variablen und ein Dutzend Key Vault Secrets, die niemand mehr wirklich überblickt. Ihr nutzt immer noch Passwörter und Access Keys in euren Azure Apps? In diesem Artikel zeigen wir euch, wie ihr mit Azure Managed Identity endlich damit Schluss machen könnt und warum das nicht nur sicherer, sondern auch operativ angenehmer ist.
Das Problem ist nicht das Secret selbst, sondern alles, was drumherum passiert: Rotation, Leak-Prävention, Zugriffssteuerung. Dieser Artikel führt euch durch die wichtigsten Szenarien, von Azure SQL über Storage Accounts bis zu Key Vault, und zeigt konkrete Schritte, wie ihr tokenbasierte Auth per Managed Identity einführt.
Warum Passwörter und Keys problematisch sind
Der klassische Weg läuft so: Du erzeugst einen Datenbankbenutzer mit Passwort, packst den Connection String in die App-Konfiguration und gibst denselben String an drei weitere Services weiter, weil es schneller geht. Mit der Zeit liegt das Secret in der CI-Pipeline, in der lokalen .env-Datei des Kollegen, in einem Ticket-Kommentar und im Confluence-Space von 2021. Dieses Phänomen nennt sich Secret Sprawl, und es betrifft fast jedes Team, das nicht aktiv dagegensteuert. Dabei werden Secrets überall verteilt und nicht zentral verwaltet, was die Kontrolle über sie extrem erschwert.
Langlebige Credentials sind dabei das eigentliche Problem. Secrets, die selten oder nie rotiert werden, sind in den meisten Security-Incidents die offene Tür, durch die Angreifer eintreten. Und selbst wenn ihr rotieren wollt: Wer kennt alle Stellen, wo das Secret verwendet wird? Falsch aktualisierte Connection Strings führen zu Outages, Change-Fenster müssen koordiniert werden, und am Ende dreht jemand einen Schlüssel zurück, weil die App in Prod nicht mehr startet.
Dazu kommen die Risiken bei einem Leak. Secrets tauchen in Git-Historien auf, in Crash-Dumps, in Log-Ausgaben oder einfach in Screenshots, die geteilt werden. Die Reichweite eines geleakten Credentials hängt davon ab, wie weit die Rechte gehen, also wie viele Berechtigungen die zugehörige Identität hat. Ein Credential, das auf alles zugreifen kann, öffnet bei einem Leak alle Türen gleichzeitig.
Die Lösung liegt darin, AuthN (Wer bist du?) und AuthZ (Was darfst du?) sauber zu trennen und Identitäten von Azure verwalten zu lassen, statt selbst Secrets zu pflegen. Genau das ist der Kern von Managed Identity. Das Azure Well-Architected Framework empfiehlt tokenbasierte Identity als Security-Baseline für alle Azure-Workloads.
Managed Identity: System-assigned vs. User-assigned
Eine Managed Identity ist eine Entra ID Identity, also ein Service Principal, den Azure für dich betreibt. Du musst kein Secret erstellen, keine Rotation durchführen und kein Passwort kennen. Stattdessen fragt deine App zur Laufzeit über den Azure Instance Metadata Service (IMDS) ein kurzlebiges Token an und nutzt das für den Zugriff auf Ressourcen.
Es gibt zwei Typen: System-assigned und User-assigned.
Bei einer System-assigned Managed Identity wird die Identität direkt an eine Azure-Ressource gebunden, zum Beispiel einen App Service oder eine Function App. Sie entsteht, wenn du die Ressource erstellst (bzw. wenn du sie aktivierst), und verschwindet, wenn du die Ressource löschst. Das ist der einfachste Einstieg: Minimaler Aufwand, klare Ownership. Der Nachteil zeigt sich bei Migration oder Reuse: Die Identität ist nicht portierbar, und beim Recreate einer Ressource bekommt die neue Instanz eine andere principalId.
Eine User-assigned Managed Identity ist eine eigenständige Ressource in Azure. Du kannst sie mehreren Ressourcen gleichzeitig zuweisen, zum Beispiel mehreren App Service Slots bei einem Blue-Green-Deployment oder mehreren Function Apps, die denselben Datenbankbenutzer nutzen sollen. Ihre principalId bleibt stabil, auch wenn du Compute-Ressourcen austauschst. Das macht sie besonders geeignet für Multi-Region-Setups und Staging-Szenarien.
Die Faustregel für die Entscheidung: Wenn du eine App, eine Identity brauchst und keine Wiederverwendung planst, reicht system-assigned. Sobald Identitäten über mehrere Ressourcen geteilt werden oder stabile Identitäten bei Compute-Rotation benötigt werden, ist user-assigned die bessere Wahl. Achte dabei aber auf Governance: Eine geteilte Identität bedeutet auch geteilte Rechte, was dem Least-Privilege-Prinzip schnell widerspricht.
| System-assigned | User-assigned | |
|---|---|---|
| Lifecycle | Gebunden an Azure-Ressource | Eigenständige Ressource |
| Wiederverwendung | Nein | Ja, mehrere Ressourcen möglich |
Stabilität der principalId | Ändert sich beim Recreate | Stabil |
| Governance-Aufwand | Gering | Höher (Ownership, Naming) |
Managed Identity erstellen und zuweisen
Der erste Schritt ist immer, die Identität an deine Azure-Ressource zu hängen. Im Portal aktivierst du das unter Identity → System assigned → On. Danach siehst du die principalId und die tenantId, die du für die nachfolgende RBAC-Konfiguration (Role-Based Access Control) brauchst.
Für Infrastructure as Code empfiehlt sich Bicep. Hier ein Beispiel, das einen App Service mit system-assigned Identity anlegt und ihr eine Rolle zuweist:
// Role ID für 'Storage Blob Data Reader'
var storageBlobDataReaderRoleId = '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'
resource appService 'Microsoft.Web/sites@2023-01-01' = {
name: appServiceName
location: location
identity: {
// Aktiviert system-assigned Managed Identity
type: 'SystemAssigned'
}
properties: {
serverFarmId: appServicePlan.id
}
}
// Rolle zuweisen, z.B. "Storage Blob Data Reader" auf einen Storage Account
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(appService.id, storageAccount.id, storageBlobDataReaderRoleId)
scope: storageAccount
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataReaderRoleId)
principalId: appService.identity.principalId
principalType: 'ServicePrincipal'
}
}
Wenn du mehrere Managed Identities an einer Ressource verwendest (user-assigned), musst du der App mitteilen, welche Identität sie nutzen soll. Das geht über die Umgebungsvariable AZURE_CLIENT_ID mit der clientId der User-assigned Identity. Ohne diese Angabe greift DefaultAzureCredential bei mehreren verfügbaren Identitäten auf eine undefinierte zurück.
Beim Aufräumen gilt: Eine System-assigned Identity wird automatisch entfernt, wenn du die Ressource löschst. User-assigned Identities bleiben hingegen bestehen und müssen manuell entfernt werden. Vergiss dabei nicht die RBAC Role Assignments, die auf diese Identity verweisen, sonst bleiben verwaiste Zuweisungen in deinen Ressourcen.
Azure SQL mit Managed Identity
Azure SQL unterstützt Entra ID Auth und ist damit eines der wichtigsten Ziele für Managed Identity. Um es einzurichten, brauchst du zunächst einen Entra Admin am SQL Server, der Benutzer in den Datenbanken anlegen darf.
Dann legst du in der Zieldatenbank einen Datenbankbenutzer für deine Managed Identity an. Das funktioniert mit T-SQL:
-- In der Zieldatenbank ausführen (nicht in master)
CREATE USER [AppServiceName] FROM EXTERNAL PROVIDER;
-- Minimale Rechte zuweisen
ALTER ROLE db_datareader ADD MEMBER [AppServiceName];
ALTER ROLE db_datawriter ADD MEMBER [AppServiceName];
Wenn du eine Entra-Gruppe verwenden willst, zum Beispiel für mehrere Apps, die denselben Datenbankzugriff brauchen, funktioniert das genauso: Du legst die Gruppe als Datenbankbenutzer an und steuerst die Mitgliedschaft zentral in Entra ID.
Im Code holst du das Token über DefaultAzureCredential und setzt es direkt auf die SqlConnection:
using Azure.Identity;
using Microsoft.Data.SqlClient;
var credential = new DefaultAzureCredential();
// Token für Azure SQL anfordern
var tokenRequestContext = new TokenRequestContext(
new[] { "https://database.windows.net/.default" });
var accessToken = await credential.GetTokenAsync(tokenRequestContext);
await using var connection = new SqlConnection(connectionString);
// Access Token ohne Passwort setzen
connection.AccessToken = accessToken.Token;
await connection.OpenAsync();
Der Connection String enthält dabei kein Passwort mehr: Server=tcp:myserver.database.windows.net;Database=mydb;. Alternativ unterstützt Microsoft.Data.SqlClient ab Version 4.x die Authentication-Property Active Directory Managed Identity direkt, welches sich selbst um das Abholen und Setzen des Tokens kümmert.
Die häufigsten Stolpersteine: Wenn kein Entra Admin am SQL Server gesetzt ist, schlägt das CREATE USER FROM EXTERNAL PROVIDER mit einem kryptischen Fehler fehl. Stelle außerdem sicher, dass du den CREATE USER-Befehl in der richtigen Datenbank ausführst und nicht in master. Und vermeide db_owner als Quickfix: Least privilege gilt hier genauso wie überall sonst.
Storage Accounts mit Managed Identity (inkl. Azure Functions)
Storage Accounts (unterteilt in Management Plane = Verwaltungs-/Konfigurationsebene und Data Plane = Zugriff auf die gespeicherten Daten) bieten Data-Plane-Zugriff über RBAC, und genau das solltest du nutzen, statt Shared Keys oder SAS Tokens weiterzureichen. Der erste Schritt ist optional, aber empfehlenswert: Deaktiviere den Shared Key Access am Storage Account. Das erzwingst du entweder direkt in den Account-Einstellungen oder skalierst es über eine Azure Policy.
Dann weist du der Managed Identity die passenden Rollen zu. Wichtig: Management Plane-Rollen wie Contributor geben keinen Zugriff auf die eigentlichen Daten. Du brauchst explizit Data Plane-Rollen:
- Blob:
Storage Blob Data ContributoroderStorage Blob Data Reader - Queue:
Storage Queue Data Contributor - Table:
Storage Table Data Contributor
Den Scope kannst du auf Account-Ebene setzen oder auf einen spezifischen Container bzw. eine Queue, wenn du noch granularer kontrollieren willst.
Im Code ist der Wechsel minimal. Der BlobServiceClient aus dem Azure SDK nimmt direkt ein TokenCredential entgegen:
using Azure.Identity;
using Azure.Storage.Blobs;
var credential = new DefaultAzureCredential();
// Kein Account Key, nur Endpoint und Credential
var blobServiceClient = new BlobServiceClient(
new Uri("https://mystorageaccount.blob.core.windows.net"),
credential);
var containerClient = blobServiceClient.GetBlobContainerClient("mycontainer");
Für Azure Functions und Durable Functions gilt dasselbe Prinzip. Statt eines Connection Strings mit AccountKey= konfigurierst du nur den Account-Namen oder den Endpoint-URI. Die Azure Functions Runtime holt das Token über die Managed Identity der Function App automatisch.
Ein häufiger Fallstrick ist das Netzwerk: Auch mit korrekter Identity kann der Zugriff durch privaten Endpoint-Konfigurationen oder Storage Firewall-Regeln blockiert werden. Prüfe in dem Fall die Netzwerkeinstellungen des Storage Accounts, bevor du die Identity-Konfiguration in Frage stellst. Typische Fehlermeldungen sind SDK-spezifische Exceptions wie RequestFailedException (HTTP 403) mit Meldungen wie “Server failed to authenticate the request” oder allgemeine AuthenticationFailed-Fehler, die auf Netzwerk- oder Firewall-Probleme hinweisen.
Key Vault mit Managed Identity
Key Vault ist die natürliche Ergänzung zu Managed Identity. Du willst keine Secrets im Code, aber irgendwo müssen Drittanbieter-Passwörter oder Zertifikate liegen. Key Vault ist der richtige Ort dafür, und mit Managed Identity kann deine App darauf zugreifen, ohne selbst ein Secret für den Zugriff auf den Secret Store zu kennen.
Für die Zugriffssteuerung empfehle ich Azure RBAC for Key Vault, da Microsoft die klassischen Access Policies als veralteten Ansatz betrachtet. Mit RBAC kannst du Rollen auf den üblichen Azure-Scopes vergeben und alles über einen zentralen Ort auditieren. Die wichtigste Rolle für Apps ist Key Vault Secrets User, nicht Key Vault Administrator. Least privilege gilt auch hier.
Im Code verwendest du den SecretClient aus dem Azure SDK:
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
var credential = new DefaultAzureCredential();
var client = new SecretClient(
new Uri("https://mykeyvault.vault.azure.net/"),
credential);
// Secret zur Laufzeit laden
KeyVaultSecret secret = await client.GetSecretAsync("my-secret-name");
string secretValue = secret.Value;
Wenn deine App auf Azure App Service läuft, gibt es noch eine bequemere Alternative: Key Vault References. Dabei konfigurierst du die App-Einstellung mit einer Referenz auf das Secret:
@Microsoft.KeyVault(SecretUri=https://mykeyvault.vault.azure.net/secrets/my-secret-name)
Der App Service löst die Referenz zur Laufzeit auf und stellt den Wert als Umgebungsvariable bereit, ohne dass du SDK-Code schreiben musst. Für einfache Secrets ist das deutlich ergonomischer, der direkte SDK-Zugriff gibt dir aber mehr Kontrolle über Caching und Fehlerbehandlung.
Dazu braucht die Managed Identity des App Service die Rolle Key Vault Secrets User auf dem Key Vault, damit die Referenzierung funktioniert.
Typische Stolperstellen: Wenn du Access Policies und RBAC gemischt konfiguriert hast, kann der Zugriff inkonsistent sein. Wähle ein Modell und halte dich daran. Außerdem solltest du beachten, dass das SDK das Secret intern cacht. Wenn du frisch rotierte Secrets brauchst, musst du explizit eine neue Instanz des SecretClient erzeugen oder den Cache invalidieren.
Developer Experience und CI/CD
Managed Identity funktioniert nur direkt in Azure. Für lokale Entwicklung und CI-Pipelines brauchst du eine kompatible Lösung, und genau das bietet DefaultAzureCredential aus dem Azure.Identity-Paket.
DefaultAzureCredential durchläuft eine definierte Credential-Chain: Auf Azure Compute wird die Managed Identity bevorzugt, in der lokalen Entwicklung kommen VisualStudio-Credentials, die Azure CLI oder die Azure Developer CLI zum Einsatz. Im Entwickleralltag bedeutet das: Du loggst dich einmal mit az login ein, und dieselbe App, die auf Azure die Managed Identity nutzt, greift lokal auf dein Entra-Konto zurück, ohne eine einzige Code-Änderung.
Ein einziges Credential-Objekt kannst du für alle Azure-Dienste wiederverwenden:
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Azure.Storage.Blobs;
using Microsoft.Data.SqlClient;
// Einmalig erstellen, überall wiederverwenden
var credential = new DefaultAzureCredential();
// SQL
var tokenRequestContext = new TokenRequestContext(
new[] { "https://database.windows.net/.default" });
var sqlToken = await credential.GetTokenAsync(tokenRequestContext);
// Storage
var blobServiceClient = new BlobServiceClient(
new Uri("https://mystorageaccount.blob.core.windows.net"), credential);
// Key Vault
var secretClient = new SecretClient(
new Uri("https://mykeyvault.vault.azure.net/"), credential);
Für deterministisches Verhalten in CI-Umgebungen, wo du nicht willst, dass DefaultAzureCredential durch alle Kandidaten probiert, kannst du DefaultAzureCredentialOptions nutzen und bestimmte Credential-Typen ausschließen.
In Pipelines ist das Pendant zu Managed Identity die Workload Identity Federation. Das klassische Modell mit einem Service Principal und einem Client Secret hat dieselben Probleme wie alle anderen Long-lived Credentials: Das Secret muss rotiert werden, kann leaken und erzeugt operativen Aufwand. Mit Workload Identity Federation konfigurierst du ein Federated Credential am Service Principal, das einem OIDC-Token (OpenID Connect) aus deiner Pipeline als Vertrauensbasis dient. Die Pipeline fordert ein OIDC-Token von ihrem Provider an (GitHub Actions, Azure DevOps, etc.) und tauscht es gegen ein Entra ID Access Token, ohne dass ein Secret ausgetauscht wird. Das Trust kann granular sein: auf ein bestimmtes Repository, einen Branch oder eine Deployment-Umgebung beschränkt.
Das Prinzip für die Einrichtung ist tool-agnostisch:
- In Entra ID: App Registration bzw. Service Principal anlegen und ein Federated Credential konfigurieren (Issuer und Subject des OIDC-Tokens der Pipeline)
- In der Pipeline: OIDC-Token vom Runner anfordern und Azure Login via Federation durchführen
- RBAC Role Assignments minimal halten (z.B. nur Deployment-Rechte auf die Ziel-Resource Group)
Die Abgrenzung ist klar: Für Workloads, die auf Azure Compute laufen, nutzt du Managed Identity. Für externe Compute-Umgebungen wie GitHub Actions, Azure DevOps-Agents oder andere CI-Systeme außerhalb von Azure, nutzt du Workload Identity Federation.
Wenn DefaultAzureCredential einen CredentialUnavailableException wirft, liegt das häufig an einem falschen Token-Scope (SQL erwartet https://database.windows.net/.default, Storage https://storage.azure.com/.default) oder daran, dass die Credential-Chain keinen passenden Kandidaten findet. Mit der Umgebungsvariable AZURE_LOG_LEVEL=information oder AZURE_TENANT_ID als zusätzlichem Hinweis lässt sich das schnell eingrenzen.
Wann Managed Identity nicht ausreicht
Managed Identity ist kein Allheilmittel. Es funktioniert nur für Azure-Ressourcen, die Entra ID Auth unterstützen, und nur für Workloads, die auf Azure Compute laufen.
Wenn dein Workload on-premises oder in einer anderen Cloud läuft, ist Managed Identity nicht verfügbar. Hier ist Workload Identity Federation die bessere Alternative, oder, für ältere Setups, zertifikatsbasierte Auth mit kurzlebigen Zertifikaten aus einem HSM (Hardware Security Module). Wenn ein Drittanbieter-Service keine Entra ID Integration bietet und nur Passwörter oder API Keys akzeptiert, kommst du um Secrets nicht herum. In diesem Fall ist Key Vault als Ablageort trotzdem sinnvoll, auch wenn deine App dann weiterhin ein Secret aus dem Vault laden muss.
Unabhängig davon, ob du Managed Identity nutzen kannst oder nicht, gelten ein paar Security Guardrails:
- Least Privilege: Weise nur die Rollen zu, die tatsächlich benötigt werden, und verwende den kleinstmöglichen Scope (Container statt Account, Datenbank statt Server)
- Separate Identities: Gib jeder App und jeder Umgebung eine eigene Identität, um Blast Radius zu begrenzen
- Monitoring: Aktiviere Entra ID Sign-in Logs, überwache Role Assignments auf unerwartete Änderungen und nutze Key Vault Access Logs für Auditing
Der Setup-Aufwand ist in den meisten Fällen überschaubar. Managed Identity aktivieren, Datenbankbenutzer anlegen, RBAC-Rolle zuweisen, Connection String anpassen: Das ist eine Sache von Minuten, nicht Tagen. Die Vorteile sind dafür dauerhaft: weniger Secrets, keine Rotation, kleinere Leak-Fläche und ein sauberes Audit-Trail für jeden Zugriff.
Wie sieht euer aktueller Umgang mit Credentials in Azure aus, und wo habt ihr den Umstieg auf Managed Identity schon gemacht? Schau gerne bei uns auf LinkedIn vorbei und diskutiere mit.