Managed Identities Internals: How Azure Workloads Authenticate Without Secrets

Managed identities have appeared in nearly every post of this series as the answer to “how does the workload authenticate”. Today they get their own post, because understanding the mechanism is what separates using them from debugging them.

The Token Flow

On a VM or scale set, the Instance Metadata Service listens at 169.254.169.254, reachable only from inside the machine, no authentication needed because possession of the network namespace is the authentication. Your code (or the SDK) sends a GET to the identity endpoint naming the resource it wants a token for, IMDS forwards to Entra using certificates the platform manages invisibly, and back comes a bearer token, cached and auto refreshed. App Service, Functions, and Container Apps expose the same contract through an internal endpoint with environment variables carrying the address and a header secret. Nothing is stored, nothing rotates on your calendar, and nothing can leak from source control because nothing is in source control. The corollary that surprises people: any process on the machine can request tokens for the machine’s identity, which is why identity per workload beats one identity shared by a fleet, and why the pod level story on AKS is workload identity federation from the hardening post rather than the node’s identity.

curl -s -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net" \
  | jq .access_token

System vs User Assigned: A Decision Rule

A system assigned identity is born with the resource and dies with it, one to one. A user assigned identity is a standalone resource attached to any number of compute resources. The rule that resolves every debate: use user assigned when the identity’s role assignments should outlive or precede the compute, which in Terraform managed estates is almost always. Create the identity and its role assignments in one apply, and instances, slots, and replacement resources attach to it without RBAC churn; blue green swaps and scale set reimages keep working because permissions belong to the identity, not the instance lifecycle. System assigned remains fine for singleton resources whose permissions are truly theirs alone. The propagation caveat applies to both: role assignments take time to propagate, occasionally minutes, so a fresh deployment failing authorization for its first ninety seconds is expected physics, and retry logic, not panic, is the response.

DefaultAzureCredential and the Failure Modes

The SDKs’ DefaultAzureCredential tries a chain, environment variables, workload identity, managed identity, developer tools like the CLI login, until one yields a token, which is why the same code authenticates as you locally and as the managed identity in Azure with zero branches. The production failure modes to know: multiple user assigned identities on one resource with no client ID specified (the endpoint cannot guess, pass the client ID explicitly in the credential options); tokens cached for the default resource when you needed a different audience; local development working while the deployed app fails because the developer’s account had broader RBAC than the identity (parity test your role assignments); and the AKS special case where the old node identity path conflicts with workload identity expectations. Diagnostics are straightforward once you know where to look: the Entra sign in logs include managed identity sign ins under service principal sign ins, showing every token issuance with resource and result, and a denied call is visible there before it is visible anywhere else. Federation completes the picture: the same identity object federates to GitHub Actions (the OIDC post) and to Kubernetes service accounts, meaning “managed identity” is less a feature than the tenant’s universal answer to workload authentication, inside Azure and out.

Cheers
Osama

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.