Every Azure project eventually has the same conversation. Where do we store the connection string? Someone suggests an environment variable, and someone else points out that environment variables end up in deployment pipelines, in Docker compose files, in Terraform state, and occasionally in accidental commits. A secret manager gets proposed, and the secret manager needs its own credentials to access the secrets. The problem recurses.

Managed Identity doesn't solve the secret manager problem by adding another layer. It removes the credential from the equation entirely for workloads running inside Azure, so the application doesn't authenticate with a stored credential but as itself, using an identity that Azure manages automatically.

What Managed Identity actually does

When you enable a Managed Identity on an Azure resource, Azure creates an identity in Microsoft Entra ID tied to that resource's lifecycle. The resource can then request short-lived tokens from the Azure Instance Metadata Service endpoint at 169.254.169.254, which is only reachable from within Azure infrastructure, and those tokens are what the resource uses to authenticate against other Azure services. There's nothing to store, nothing to rotate manually, and nothing that can be leaked in a repository because the credential never exists as a static string anywhere in your codebase or configuration.

The key property from Microsoft's documentation is precise: managed identities give code running on an Azure resource access to other resources without developers needing to handle or put credentials directly into code. The emphasis on "code running on an Azure resource" matters because Managed Identity only works from within Azure. A local development machine can't reach the Instance Metadata Service endpoint, which means the local development flow still needs an alternative authentication mechanism, typically az login or a service principal configured for development only.

System-assigned vs user-assigned

There are two types of Managed Identity, and Microsoft's current recommendation, updated in its official best practice documentation, is that user-assigned identities are more efficient in a broader range of scenarios.

A system-assigned identity is created directly on a resource and its lifecycle is tied to that resource, so when the resource is deleted, the identity is deleted too. This sounds convenient but creates a management problem at scale: every resource gets its own identity, every identity needs its own role assignments, and if you have twenty App Services that all need read access to the same storage account, you end up managing twenty separate identities with twenty separate role assignments that have to stay synchronized.

A user-assigned identity is created as a standalone resource in Azure and can be assigned to multiple resources simultaneously, with its lifecycle independent of any particular resource. If you delete the App Service, the identity persists. You can pre-define what a user-assigned identity can access, get it approved by whoever owns your access control policy, and then assign it to new resources as they're created without going through a new approval cycle each time.

Microsoft's naming recommendation makes the operational difference clear: name identities after their permission set rather than after the consumer. An identity named id-blogreader-prod-eastus outlives any specific workload and its purpose stays readable in audit logs, while an identity named id-appsvc-01 doesn't communicate what it can do and its permissions tend to drift over time as the team changes.

The security mistake most developers make

Turning on Managed Identity and removing the hardcoded credential is the right first step, but stopping there is where most teams leave a significant gap.

Managed identities do not make a workload secure. They make it credential-free, and that distinction matters because the identity still holds whatever permissions you've granted it, and those permissions are available to anything running on the resource the identity is assigned to. Microsoft's own documentation states this explicitly: if a user has access to install or execute code on a resource with a managed identity, that user has access to everything the identity can reach, even if they have no direct access to those target resources.

This means that granting a Managed Identity broad permissions like Contributor on a storage account and then assigning it to a shared compute resource where multiple teams run code effectively elevates every team's access to that storage account. The credential problem is gone but the permission problem remains and is now implicit rather than visible in a secrets manager.

The practical fix is applying least-privilege role assignments: instead of Contributor, assign Storage Blob Data Reader if the workload only reads blobs, and instead of Key Vault Administrator, assign Key Vault Secrets User if the workload only reads secrets. Azure RBAC has purpose-specific built-in roles for most common scenarios, and using them reduces the blast radius if a workload is ever compromised.

Using DefaultAzureCredential

The practical implementation for most teams is DefaultAzureCredential, part of the Azure Identity SDK and available for Python, JavaScript, Java, and .NET. It tries a sequence of authentication methods in order and uses the first one that succeeds, which means the same code works in both local development and in Azure without any environment-specific branching.

from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient
 
credential = DefaultAzureCredential()
client = BlobServiceClient(
    account_url="https://mystorageaccount.blob.core.windows.net",
    credential=credential
)

In a local environment, DefaultAzureCredential typically picks up the credentials from az login, and on an Azure resource with Managed Identity enabled it picks up the managed identity token from the Instance Metadata Service. The same line of code handles both cases.

The order of credential resolution matters when debugging authentication issues. DefaultAzureCredential tries EnvironmentCredential, WorkloadIdentityCredential, ManagedIdentityCredential, SharedTokenCacheCredential, VisualStudioCodeCredential, AzureCliCredential, AzurePowerShellCredential, and AzureDeveloperCliCredential in that sequence. If you're seeing unexpected authentication behavior in local development, it's usually because an earlier credential in the chain is being picked up unexpectedly, typically an environment variable in the shell that's overriding the az login credential.

import { DefaultAzureCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
 
const credential = new DefaultAzureCredential();
const client = new SecretClient(
  "https://mykeyvault.vault.azure.net",
  credential
);
 
const secret = await client.getSecret("my-database-connection-string");

Where Managed Identity doesn't work

Two scenarios where a service principal with a client secret or certificate is still the right choice: workloads running outside Azure that can't reach the Instance Metadata Service, and federated scenarios where the workload needs to authenticate to Azure from a non-Azure environment like GitHub Actions or another cloud provider. For GitHub Actions specifically, Microsoft supports OIDC-based federation that eliminates the stored secret problem without requiring the workload to run in Azure.

The 2026 shift toward identity-first

Azure's direction in 2026 is increasingly identity-first by default, with system-assigned managed identities now the default for Kubernetes workloads on AKS, replacing API keys in that context. The pattern is becoming a baseline expectation rather than an advanced configuration, and for new projects starting on Azure, configuring Managed Identity from the beginning is significantly easier than retrofitting it into an existing codebase that passes credentials around as strings.

For the full Microsoft documentation on Managed Identity and how to configure it for specific Azure services:

👉 Managed identities for Azure resources - Overview

References


Information based on official Microsoft documentation and verified sources as of September 2026. Azure services and recommendations may change. Verify current guidance at Microsoft Learn before implementing in production environments.