Azure Users vs Managed Identities: Stop Storing Secrets in Code
In Azure there are two fundamentally different types of identities: human identities (users and administrators who sign in through Microsoft Entra ID) and managed identities (identities assigned to Azure services so they can authenticate to other Azure services automatically). Knowing when to use each type — and why putting credentials in code is never the right answer — is one of the most important security concepts for anyone working with Azure.
Human Identities: Microsoft Entra ID Users
When a developer, administrator, or operator signs into the Azure portal, the CLI, or Azure DevOps, they authenticate as a human user through Microsoft Entra ID (formerly called Azure Active Directory). Their identity consists of a username (usually their work email), a password, and potentially multi-factor authentication.
Human identities are appropriate for:
- Interactive work — logging into the portal, running CLI commands, approving deployments.
- Actions that need an audit trail tied to a specific person (who changed this configuration?).
- One-off or exploratory operations where automation is not the goal.
Human identities are not appropriate for applications. If your application uses your personal Azure account credentials to access a storage account, several problems emerge: the credentials expire and need rotation, the application stops working if you leave the company, every deployment environment has to know your password, and a credential leak compromises your entire account.
See Azure Identity Basics and Azure Service Principals for how non-human identities work for automated workloads.
The Problem with Credentials in Code
Before managed identities existed, the standard pattern for giving an application access to Azure resources was to create a service principal, generate a client secret (a password), and store that secret in the application’s configuration or environment variables.
Here is what that code looked like — and why it creates problems:
# BAD: Hardcoded credentials approach
# The application needs to read a secret from Key Vault.
# To authenticate, it uses a client ID and client secret stored in environment variables.
import os
from azure.identity import ClientSecretCredential
from azure.keyvault.secrets import SecretClient
TENANT_ID = "your-tenant-id"
CLIENT_ID = "your-app-client-id"
CLIENT_SECRET = os.environ["AZURE_CLIENT_SECRET"] # Still has to come from somewhere!
credential = ClientSecretCredential(
tenant_id=TENANT_ID,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET
)
client = SecretClient(
vault_url="https://my-keyvault.vault.azure.net",
credential=credential
)
secret = client.get_secret("db-password")The AZURE_CLIENT_SECRET is a credential that must exist somewhere — in a .env file, in a CI/CD pipeline secret store, in an app configuration file, or in an environment variable on a server. This creates several risks:
- The secret can be accidentally committed to a git repository.
- Every environment (dev, staging, production) needs its own copy of the secret.
- The secret must be rotated periodically, and every place it is stored must be updated simultaneously.
- If one environment is compromised, the attacker has credentials they can use anywhere the secret grants access.
- Audit logs show “application X accessed Key Vault” but not which deployment or which person triggered it.
This problem class — credential management — is one of the most common sources of cloud security incidents. Managed identities eliminate it entirely for Azure-to-Azure access.
What Managed Identities Are
A managed identity is an identity in Microsoft Entra ID that is automatically created and managed by Azure for a specific Azure resource. When you enable a managed identity on an Azure VM, App Service, Azure Function, or other supported service, Azure creates an Entra ID identity associated with that resource. Azure handles the underlying credentials — generating them, storing them securely, and rotating them automatically.
Your application code never sees a password or secret. Instead, your code asks the Azure Instance Metadata Service (IMDS) — a special endpoint available inside Azure at http://169.254.169.254 — for a short-lived access token. The IMDS returns a token that is valid for one hour. Your code uses this token to authenticate to other Azure services. The entire credential lifecycle is invisible to you.
The two types of managed identities are:
- System-assigned: Created when you enable the identity on a specific resource. Its lifecycle is tied to that resource — it is deleted when the resource is deleted.
- User-assigned: Created as a standalone Azure resource that you create, name, and attach to one or more resources. Useful when multiple resources need the same identity and permissions, or when you want to manage the identity independently from the resources it is attached to.
Before and After: Managed Identity in Practice
Here is the same Key Vault access task from above, rewritten to use a managed identity. The application runs on an Azure App Service with a system-assigned managed identity enabled.
# GOOD: Managed identity approach
# No client ID, no client secret, no .env file, no secret rotation needed.
# The DefaultAzureCredential automatically detects the managed identity when
# running in Azure, and falls back to developer credentials (az login) locally.
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
credential = DefaultAzureCredential() # That's it. No credentials to manage.
client = SecretClient(
vault_url="https://my-keyvault.vault.azure.net",
credential=credential
)
secret = client.get_secret("db-password")The differences are significant:
- There is no client secret anywhere in the code, the environment, or the deployment pipeline.
- The same code works in every environment (dev, staging, production) — the credential switches automatically based on where the code is running.
- When the developer runs the code locally,
DefaultAzureCredentialuses theiraz loginsession. When it runs in Azure, it uses the managed identity. No environment-specific configuration needed. - There is nothing to rotate. Azure rotates the underlying managed identity credentials on its own schedule, transparently.
- If the App Service is deleted, the system-assigned identity is deleted automatically. No orphaned credentials.
The Key Vault still controls access — the managed identity must be granted the Key Vault Secrets User role or a Key Vault access policy before it can read secrets. Managed identities do not bypass authorization; they just replace password-based authentication. See Managed Identities Overview for a deeper technical guide.
Enabling a Managed Identity on an Azure Resource
Via CLI
Enable a system-assigned managed identity on an existing App Service:
az webapp identity assign \
--name my-app-service \
--resource-group myapp-dev-rgThe output includes the principalId — the object ID of the new managed identity in Entra ID. You use this ID when granting RBAC roles or Key Vault access policies.
Enable a system-assigned managed identity on a VM:
az vm identity assign \
--name my-vm \
--resource-group myapp-dev-rgCreate a user-assigned managed identity (a standalone resource):
az identity create \
--name my-app-identity \
--resource-group myapp-dev-rgThen attach it to a resource:
az webapp identity assign \
--name my-app-service \
--resource-group myapp-dev-rg \
--identities /subscriptions/{sub}/resourceGroups/myapp-dev-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-app-identityVia Portal
For an App Service:
- Open the App Service in the portal.
- In the left sidebar, under Settings, click Identity.
- On the System assigned tab, switch the Status toggle to On.
- Click Save and confirm the prompt.
- The portal shows the Object (principal) ID of the new identity.
Granting the Managed Identity Access to Azure Resources
Enabling a managed identity creates an identity in Entra ID. It does not grant the identity any permissions. You need to explicitly grant access to each resource the application needs.
To grant a managed identity access to a storage account (for example, to read blobs):
# Get the principal ID of the managed identity
IDENTITY_PRINCIPAL_ID=$(az webapp identity show \
--name my-app-service \
--resource-group myapp-dev-rg \
--query principalId \
--output tsv)
# Get the storage account resource ID
STORAGE_ID=$(az storage account show \
--name myappstorage001 \
--resource-group myapp-dev-rg \
--query id \
--output tsv)
# Assign the Storage Blob Data Reader role
az role assignment create \
--assignee $IDENTITY_PRINCIPAL_ID \
--role "Storage Blob Data Reader" \
--scope $STORAGE_IDFor Key Vault secrets access, the recommended approach is to use Key Vault RBAC (enabled on new vaults by default) and assign the Key Vault Secrets User built-in role:
KEY_VAULT_ID=$(az keyvault show \
--name my-keyvault \
--resource-group myapp-dev-rg \
--query id \
--output tsv)
az role assignment create \
--assignee $IDENTITY_PRINCIPAL_ID \
--role "Key Vault Secrets User" \
--scope $KEY_VAULT_IDSee Azure RBAC for how the role assignment model works.
When to Use Each Identity Type
Use a human Entra ID user identity for:
- Interactive work in the portal or CLI
- Actions that need to be attributable to a specific person in audit logs
- Approvals, reviews, and manual changes
Use a system-assigned managed identity for:
- A single Azure resource (VM, App Service, Function App) that needs to access other Azure services
- When you want the identity to be automatically cleaned up when the resource is deleted
- Simplest cases where one identity per resource is fine
Use a user-assigned managed identity for:
- Multiple Azure resources that need identical permissions (a fleet of VMs, multiple App Service slots)
- When the identity should survive the deletion of individual resources
- When you want to pre-create the identity and its role assignments before deploying the resource that uses it
- Blue-green deployments where the same identity carries over to a replacement resource
Use a service principal (not managed identity) for:
- Applications running outside Azure (on-premises, GitHub Actions, other clouds) that need to access Azure resources
- CI/CD pipelines running in external systems
- Any scenario where the code runs somewhere that cannot use the Azure IMDS endpoint
Common Identity and Credential Mistakes
- Storing client secrets in .env files committed to git. This is the most common source of Azure credential leaks. Git history preserves secrets even if you delete the file later. Use a pre-commit hook or a secrets scanner to prevent this. If it has already happened, invalidate the secret immediately and audit the access logs.
- Using a human user account as an application identity. When the person leaves the company or changes roles, their access is revoked and the application breaks. Applications should always use service principals or managed identities, never personal accounts.
- Creating managed identities but forgetting to grant them permissions. A managed identity with no role assignments cannot access anything. The identity creation step and the role assignment step are separate — both are required.
- Using a system-assigned identity for shared resources. If multiple resources need the same permissions, giving each a separate system-assigned identity means maintaining the same role assignments multiple times. Use a user-assigned identity instead and attach it to all the resources that need it.
- Not using DefaultAzureCredential for local development. Some teams configure their code to read environment variables with explicit credentials and then manually swap in managed identity code before deployment. Using
DefaultAzureCredential(available in the Azure SDK for all major languages) eliminates this — the same code works locally (using az login credentials) and in Azure (using the managed identity) without any environment-specific configuration.
Summary
- Human identities (Entra ID users) are for interactive work. Applications and services should not use human credentials.
- Managed identities give Azure services an Entra ID identity without any secrets to store, rotate, or leak.
- System-assigned managed identities are tied to a specific resource’s lifecycle. User-assigned managed identities are standalone resources that can be attached to multiple services.
- Use DefaultAzureCredential in your SDK code — it handles both local development (az login) and Azure runtime (managed identity) automatically.
- Enabling a managed identity does not grant it any permissions. You must explicitly assign RBAC roles or Key Vault access policies to the identity.
- For workloads running outside Azure, use a service principal rather than a managed identity.
Frequently asked questions
What is the difference between a system-assigned and user-assigned managed identity?
A system-assigned managed identity is created automatically when you enable it on a specific Azure resource (a VM, an App Service, etc.) and its lifecycle is tied to that resource — when the resource is deleted, the identity is deleted. A user-assigned managed identity is a standalone Azure resource you create independently and can attach to multiple services. User-assigned identities are better when multiple resources need the same permissions, or when you want the identity to outlive a specific resource.
Can managed identities access resources in other subscriptions or tenants?
Managed identities can access resources in other subscriptions within the same Microsoft Entra ID tenant, as long as they are granted the appropriate RBAC role on the target resource. Cross-tenant access is not supported for managed identities — for that, you need a service principal with explicit cross-tenant configuration.
Does using a managed identity mean my application never needs any credentials?
For accessing Azure resources (Key Vault, Storage, Service Bus, etc.), yes — a managed identity eliminates credentials entirely. For accessing non-Azure services (third-party APIs, on-premises databases), you still need credentials. In those cases, the recommended pattern is to store those secrets in Azure Key Vault and have your application use a managed identity to access Key Vault, keeping the actual secrets out of code.