Perché la managed identity elimina i secret

Quando un’applicazione ospitata su Azure (App Service, Functions, Container Apps, VM, AKS) deve autenticarsi verso un altro servizio — Azure Key Vault, Azure Storage, Azure SQL, un endpoint di Azure AI Foundry — la strada peggiore è mettere una connection string o una chiave nel codice o in configurazione. La managed identity risolve il problema: è un’identità gestita da Microsoft Entra ID e associata alla risorsa, che ottiene automaticamente token OAuth2 senza che tu debba custodire alcun secret. Il rotation delle credenziali è trasparente e gestito dalla piattaforma.

System-assigned vs user-assigned

Le due varianti rispondono a scenari architetturali diversi:

  • System-assigned: creata e legata al ciclo di vita di una singola risorsa. Nasce quando la abiliti sulla risorsa e viene eliminata automaticamente quando la risorsa viene distrutta. Relazione 1:1. Ideale quando l’identità serve a un solo workload e non deve sopravvivergli.
  • User-assigned: una risorsa Azure standalone che crei separatamente e poi condividi tra più risorse (più App Service, uno scale set, più container). Ha un ciclo di vita indipendente. È la scelta giusta quando vuoi assegnare i ruoli una volta sola e riutilizzare la stessa identità su più deployment, oppure quando l’identità deve esistere prima della risorsa (pre-provisioning delle role assignment in pipeline).

Assegnare i ruoli RBAC

Avere l’identità non basta: senza autorizzazioni il token viene emesso ma l’accesso è negato. Devi creare una role assignment che leghi il principal dell’identità a un ruolo su uno scope preciso (resource, resource group, subscription). Principi chiave:

  • Usa ruoli data-plane, non solo control-plane. Per leggere segreti da Key Vault serve Key Vault Secrets User, non “Reader” o “Contributor”. Per un blob serve Storage Blob Data Reader/Contributor, non “Storage Account Contributor”.
  • Applica il least privilege: assegna al livello di scope più stretto possibile (il singolo Key Vault, non l’intera subscription).
  • Il principal a cui assegni il ruolo deve essere quello giusto: l’object ID della system-assigned identity di quella risorsa, oppure l’object ID della user-assigned identity condivisa.

Come funziona DefaultAzureCredential

DefaultAzureCredential della Azure SDK (Azure.Identity) è pensata per far girare lo stesso codice in locale e in cloud senza modifiche. Al primo GetToken prova in sequenza una catena di credential e usa la prima che riesce:

  1. Environment (variabili AZURE_CLIENT_ID/TENANT_ID/SECRET per service principal)
  2. Workload Identity / Managed Identity (in cloud)
  3. Visual Studio / VS Code
  4. Azure CLI (az login) / Azure PowerShell / Azure Developer CLI

In produzione scatta la managed identity; sullo sviluppatore scattano le sue credenziali interattive (az login). Il pattern d’uso è semplicemente passare la credential al client:

var client = new SecretClient(
    new Uri("https://kv-app.vault.azure.net/"),
    new DefaultAzureCredential());

Il caso della user-assigned identity

Qui sta la sottigliezza più importante: se la risorsa ha più identità (es. la system-assigned più una o più user-assigned), l’Instance Metadata Service non sa quale usare e la richiesta di token è ambigua. Devi disambiguare passando il client ID:

var cred = new DefaultAzureCredential(
    new DefaultAzureCredentialOptions {
        ManagedIdentityClientId = "<user-assigned-client-id>"
    });

Senza questo, la credential può fallire o autenticarsi con l’identità sbagliata — token emesso ma accesso negato dal RBAC.

Trappole tipiche d’esame

  • App con user-assigned identity ma senza client ID nella credential → l’autenticazione fallisce o è ambigua: imposta ManagedIdentityClientId (o la env var AZURE_CLIENT_ID) su DefaultAzureCredentialOptions.
  • Role assignment fatta al principal sbagliato (es. all’utente sviluppatore o alla system-assigned invece che alla user-assigned condivisa) → in produzione arriva 403. Assegna il ruolo all’object ID dell’identità effettivamente usata dall’app.
  • Assegnato “Contributor” ma l’app non legge i segreti → serve il ruolo data-plane (Key Vault Secrets User, Storage Blob Data Reader), non un ruolo control-plane.
  • “Funziona in locale, fallisce in cloud” → in locale usa le credenziali dello sviluppatore; in cloud manca la managed identity abilitata o la role assignment: verifica che l’identità sia attiva e correttamente autorizzata.
  • Serve un’identità che sopravvive alla risorsa o condivisa tra più app → scegli user-assigned; se deve morire con la risorsa e serve a un solo workload, system-assigned.