Sunday, July 12, 2026

Azure Managed Identity: From Beginner to Advanced

Azure Managed Identity: From Beginner to Advanced

Managing secrets is one of the most painful parts of building cloud applications. Connection strings in config files, client secrets that expire at 2 AM, certificates someone forgot to renew — we have all been there. Azure Managed Identity solves this problem by removing credentials from your code entirely.

This post takes you from the basics all the way to production best practices, with everything verified against the latest Microsoft Learn documentation.


Part 1: The Basics (Beginner)

What is a Managed Identity?

A managed identity is an automatically managed identity in Microsoft Entra ID that Azure resources can use to authenticate to any service that supports Microsoft Entra authentication — without you storing any credentials in code, config, or Key Vault.

Key points:

  • Azure creates and manages the credentials behind the scenes. You never see them, rotate them, or store them.
  • Managed identities are free — there is no extra cost with Microsoft Entra ID.
  • The feature was formerly called Managed Service Identity (MSI) — you will still see "MSI" in older docs, blogs, and even some parameter names.

The Two Types

Property System-assigned User-assigned
Creation Created as part of an Azure resource (VM, App Service, Function App, etc.) Created as a standalone Azure resource
Lifecycle Tied to the parent resource — deleted when the resource is deleted Independent — must be explicitly deleted
Sharing Cannot be shared; belongs to exactly one resource Can be assigned to multiple resources
Common use cases Workloads contained in a single resource that need an independent identity Workloads spanning multiple resources; pre-authorization scenarios; resources that get recycled frequently but need consistent permissions

Microsoft's current recommendation: User-assigned managed identities are the recommended type for most scenarios, because they can be provisioned independently of compute, pre-authorized before deployment, and shared across resources.

Three Core Concepts

Every managed identity is backed by a special service principal in Microsoft Entra ID. Three IDs matter:

  1. Client ID — a unique identifier tied to the identity's application registration. Used when your code needs to pick a specific user-assigned identity.
  2. Principal ID (Object ID) — the object ID of the service principal. Used when assigning RBAC roles.
  3. Service Principal — the Entra ID object that represents the identity in your tenant.

How Authentication Works (The Mental Model)

  1. You enable a managed identity on a resource (say, a VM).
  2. Azure creates a service principal in your Entra tenant.
  3. You grant that identity an RBAC role on a target resource (e.g., Storage Blob Data Reader on a storage account).
  4. Your code asks a local, non-routable endpoint for a token — no secrets involved.
  5. Azure returns a Microsoft Entra access token (JWT).
  6. Your code presents that token to the target service.

The magic is in step 4: the token endpoint is only reachable from inside the resource itself.


Part 2: Hands-On (Getting Started)

Enable a System-Assigned Identity

Azure portal: Resource → Identity blade → System assigned tab → set Status to On → Save.

Azure CLI:

# On a VM
az vm identity assign --name MyVM --resource-group MyRG

# On a Web App
az webapp identity assign --name MyWebApp --resource-group MyRG

PowerShell:

# On a VM
$vm = Get-AzVM -ResourceGroupName "MyRG" -Name "MyVM"
Update-AzVM -ResourceGroupName "MyRG" -VM $vm -IdentityType SystemAssigned

Create and Assign a User-Assigned Identity

# 1. Create the identity
az identity create --name MyUAMI --resource-group MyRG

# 2. Assign it to a VM
az vm identity assign --name MyVM --resource-group MyRG \
  --identities /subscriptions/<sub-id>/resourcegroups/MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/MyUAMI

Grant Permissions with RBAC

The identity is useless until it has permissions. Assign a role scoped as narrowly as possible:

az role assignment create \
  --assignee <principal-id-of-managed-identity> \
  --role "Storage Blob Data Reader" \
  --scope /subscriptions/<sub-id>/resourceGroups/MyRG/providers/Microsoft.Storage/storageAccounts/mystorageacct

See It Work: Request a Token Manually

From inside an Azure VM, call the Azure Instance Metadata Service (IMDS) endpoint:

curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' \
  -H "Metadata: true" -s

What each piece means:

  • 169.254.169.254 — the IMDS endpoint, a well-known non-routable IP reachable only from inside the VM.
  • api-version=2018-02-01 — use this version or later.
  • resource — the audience of the token (here, Azure Resource Manager). Note the trailing slash — the value must exactly match what Entra ID expects.
  • Metadata: true header — mandatory; it mitigates server-side request forgery (SSRF) attacks.

If the VM has multiple user-assigned identities, add client_id=<client-id> (or object_id / msi_res_id) to specify which identity to use.


Part 3: Using Managed Identity in Code (Intermediate)

You should almost never call IMDS directly in real applications. Use the Azure Identity client library instead — it handles endpoint discovery, caching, and retries across all Azure hosting environments (VM, App Service, Functions, AKS, Container Apps, Arc).

C# (.NET)

using Azure.Identity;
using Azure.Storage.Blobs;

// System-assigned identity
var credential = new ManagedIdentityCredential();

// User-assigned identity (specify the client ID)
var uamiCredential = new ManagedIdentityCredential(
    ManagedIdentityId.FromUserAssignedClientId("<client-id>"));

var blobClient = new BlobServiceClient(
    new Uri("https://<storage-account>.blob.core.windows.net"),
    credential);

Python

from azure.identity import ManagedIdentityCredential
from azure.storage.blob import BlobServiceClient

# System-assigned
credential = ManagedIdentityCredential()

# User-assigned
credential = ManagedIdentityCredential(client_id="<client-id>")

blob_client = BlobServiceClient(
    account_url="https://<storage-account>.blob.core.windows.net",
    credential=credential
)

PowerShell (e.g., in Azure Automation or an Azure VM)

# Connect to Azure using the managed identity
Connect-AzAccount -Identity

# Connect using a specific user-assigned identity
Connect-AzAccount -Identity -AccountId "<client-id>"

# Get a token for a specific resource
$token = Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com"

DefaultAzureCredential: Convenient, But Know the Trade-offs

DefaultAzureCredential tries a chain of authentication methods and picks whichever works at runtime. Locally it uses your Visual Studio / Azure CLI / Azure PowerShell login; in Azure it discovers the managed identity — no code changes between environments.

var credential = new DefaultAzureCredential(); // great for getting started

This is fantastic for the dev inner loop, but it has real production drawbacks (covered in Part 5).


Part 4: Advanced Scenarios

The Token Endpoint Varies by Host

Different Azure services expose the managed identity endpoint differently — another reason to use the SDK rather than raw HTTP:

Host Endpoint
Azure VMs, VM Scale Sets, AKS/ACI containers http://169.254.169.254/metadata/identity/oauth2/token (IMDS)
App Service / Functions / Container Apps IDENTITY_ENDPOINT + IDENTITY_HEADER environment variables
Azure Arc-enabled servers http://localhost:40342/metadata/identity/oauth2/token (Hybrid IMDS), with an extra challenge-token step
Service Fabric IDENTITY_ENDPOINT + IDENTITY_HEADER + IDENTITY_SERVER_THUMBPRINT

Azure Arc: Managed Identity Beyond Azure

Arc-enabled servers (on-premises or other clouds) get a managed identity too, via the Hybrid Instance Metadata Service (HIMDS). The token flow is multi-step: request a challenge token file (readable only by privileged users), then exchange it for the access token. This design ensures only higher-privileged processes on the machine can obtain tokens.

AKS: Workload Identity, Not Pod IMDS Access

On AKS, cluster-level managed identities cover the cluster-to-Azure scenario (the control plane and kubelet acting on Azure). For pod-to-Azure authentication, the modern approach is Microsoft Entra Workload ID — pods get short-lived federated tokens instead of hitting IMDS.

Related hardening: AKS now supports blocking pod access to IMDS for non-host-network pods (preview). Once enabled, pods can no longer grab node-level managed identity tokens — a real security win, since any pod reaching IMDS could otherwise obtain tokens for the node's identity.

Managed Identity as a Federated Identity Credential (Cross-Tenant)

Managed identities only work within the tenant that hosts the subscription. For cross-tenant access, configure the managed identity as a federated identity credential (FIC) on an Entra app registration. Your workload uses its managed identity token as proof to acquire tokens as the multi-tenant app — completely certificateless. Microsoft.Identity.Web calls this pattern "certificateless credentials" and recommends it as the best option for production workloads on Azure.

Token Caching

The managed identity subsystem caches tokens at the platform level, and the Azure Identity SDKs cache in-memory via MSAL. Still:

  • Reuse credential instances across clients. High-volume apps that create new credentials per request can hit HTTP 429 throttling from Entra ID.
  • Handle the case where a target service rejects a token as expired.

Important Boundaries to Remember

  • Managed identities are tenant-specific. Moving a subscription to another directory means recreating and reconfiguring identities.
  • A resource can have one system-assigned identity but multiple user-assigned identities.
  • When you switch an existing resource's identity type (e.g., system-assigned → user-assigned on AKS), components keep using the old identity until its token expires — the switchover can take hours.

Part 5: Production Best Practices

This is where the "it works on my machine" story becomes "it works reliably at 3 AM."

1. Prefer User-Assigned Identities

Microsoft's guidance is clear: user-assigned identities are more efficient in a broader range of scenarios.

  • Identities and role assignments can be created before the resources that use them, so infrastructure deployments do not fail because the deployer lacks role-assignment rights.
  • One identity can serve many resources — fewer identities and role assignments to govern.
  • Lifecycle is decoupled: recycle compute freely without losing permissions.

Use system-assigned only when a resource genuinely needs its own unique identity that should die with it.

2. Use Deterministic Credentials in Production

DefaultAzureCredential is great for development but risky in production:

  • Unpredictable behavior: the chain checks environment variables and installed tools; someone running az login on a production VM can silently change which credential your app uses — potentially elevating or reducing privileges.
  • Debugging pain: when auth fails, you must dig through logs to find which credential in the chain failed.
  • Performance overhead: sequentially trying credentials adds latency.
  • Permission mismatches: the first credential that gets a token wins, even if it has the wrong permissions.

Production pattern — pick the credential explicitly:

TokenCredential credential = builder.Environment.IsProduction()
    ? new ManagedIdentityCredential(ManagedIdentityId.FromUserAssignedClientId(clientId))
    : new DefaultAzureCredential();

3. Understand the Retry Strategy

The Azure Identity library behaves differently depending on how you use managed identity:

  • Fail-fast mode (default DefaultAzureCredential behavior): no retries on token failure. Good for the dev loop, bad for production.
  • Resilient mode: up to five retries with exponential backoff starting at 0.8 seconds. Activated by using ManagedIdentityCredential directly, using ChainedTokenCredential containing it, or setting AZURE_TOKEN_CREDENTIALS=ManagedIdentityCredential (requires Azure.Identity 1.16.0+ for the environment-variable approach).

One more reason to use ManagedIdentityCredential directly in production.

4. Apply Least Privilege — Ruthlessly

  • Grant the narrowest role at the narrowest scope (resource > resource group > subscription).
  • Prefer data-plane roles (e.g., Storage Blob Data Reader) over broad control-plane roles like Contributor.
  • Audit role assignments regularly and revoke what is unused.

5. Separate Identities per Environment

Use different managed identities for dev, staging, and production. If an identity is ever compromised or misconfigured, the blast radius stays contained to one environment.

6. Monitor and Audit

  • Managed identity CRUD operations show up in Azure Activity logs.
  • Sign-in activity appears in Microsoft Entra sign-in logs — filter by service principal sign-ins to watch your identities in action.
  • Enable SDK diagnostic logging to catch credential-chain issues early.
  • Document which permissions each identity has and why.

7. Reuse Credential Instances

Create one credential object per application and share it across all SDK clients. This maximizes the MSAL token cache and avoids Entra ID throttling.

8. Eliminate the Last Secrets

A production maturity checklist:

  • [ ] No connection strings with keys — use Entra auth for Storage, SQL, Service Bus, Cosmos DB, etc.
  • [ ] No client secrets in app registrations for Azure-hosted workloads — use managed identity + federated identity credentials instead.
  • [ ] Key Vault accessed via managed identity (RBAC), not access policies with secrets.
  • [ ] CI/CD pipelines using OIDC / workload identity federation instead of service principal secrets.
  • [ ] AKS pods using Workload ID; IMDS restriction enabled where possible.

Quick Troubleshooting Guide

Symptom Likely cause Fix
400 Bad Request from IMDS with multiple identities Multiple user-assigned identities but no client_id specified Pass client_id, object_id, or msi_res_id
Token acquired but target service returns 403 RBAC role missing, wrong scope, or role propagation delay Verify role assignment; allow a few minutes for propagation
Auth works locally, fails in Azure Local credential (CLI/VS) has broader permissions than the managed identity Grant the managed identity the required role; use deterministic credentials
Old permissions still in effect after role change Platform/SDK token cache Tokens reflect changes only after refresh — wait for expiry or restart
DefaultAzureCredential slow or flaky in production Chain probing + fail-fast mode Switch to ManagedIdentityCredential directly

Wrap-Up

Managed identity is one of those rare features that improves security and reduces operational work at the same time. The learning curve is short:

  1. Beginner: enable an identity, assign a role, watch a token flow.
  2. Intermediate: wire up the Azure Identity SDK, understand system vs. user-assigned.
  3. Advanced: federated credentials, Arc/HIMDS, AKS workload identity, cross-tenant patterns.
  4. Production: user-assigned by default, deterministic credentials, least privilege, per-environment identities, monitoring.

If your Azure workloads still hold a client secret or storage key anywhere, that is your next refactoring target.


References (Microsoft Learn)


Featured Post

Azure Managed Identity: From Beginner to Advanced

Azure Managed Identity: From Beginner to Advanced Managing secrets is one of the most painful parts of building cloud applications. Connect...

Popular posts