Applications often need credentials to access other services. An ASP.NET Core application might need to read files from Azure Storage, retrieve secrets from Azure Key Vault, or connect to other Azure resources.
A common approach is to store a username, password, or client secret in an application configuration file. Although this may work during development, it creates extra work and security risks in production. Credentials can accidentally appear in source control, logs, deployment settings, or diagnostic files. They also need to be rotated before they expire.
Azure Managed Identity provides a different approach. Instead of storing a service password in your application, you let Azure provide an identity that the application can use to request access tokens.
In this article, we'll create a managed identity for an Azure-hosted application, grant it access to Azure resources, and use it from a .NET application.
1. What Is Azure Managed Identity?
Managed Identity is an identity feature in Microsoft Entra ID that allows supported Azure resources to authenticate to other services without requiring you to manage credentials in your application.
For example, an application running in Azure App Service can use its managed identity to access Azure Key Vault. The application requests a token through Azure's identity infrastructure, and Key Vault checks whether that identity has permission to perform the requested operation.
The application does not need to store a client secret for this authentication flow.
There are two main types of managed identity.
System-assigned managed identity
A system-assigned identity is created for a specific Azure resource, such as an App Service or virtual machine.
Its lifecycle is tied to that resource. When the resource is deleted, its system-assigned identity is also removed.
This is a good choice when an identity is intended for one application resource.
User-assigned managed identity
A user-assigned identity is created as a separate Azure resource. You can assign it to multiple supported Azure resources and manage its lifecycle independently.
This can be useful when an identity must remain stable across resource replacements or needs to be shared by several workloads. Sharing an identity also means those workloads share its permissions, so use it only when that access model is appropriate.
2. Enable Managed Identity in Azure App Service
Let's assume you have an ASP.NET Core application deployed to Azure App Service and want it to access Azure Key Vault.
First, sign in to Azure:
az login
Select the subscription containing your application:
az account set --subscription "YOUR-SUBSCRIPTION-ID"
Enable a system-assigned managed identity for the App Service:
az webapp identity assign \
--name my-dotnet-app \
--resource-group my-resource-group
Replace the example application name, resource group, and subscription with your own values.
The command returns identity details, including a principal ID. You can inspect the identity configuration later with:
az webapp identity show \
--name my-dotnet-app \
--resource-group my-resource-group
The principal ID identifies the managed identity in Microsoft Entra ID. You'll use this identity when assigning permissions to the target resource.
Enabling the identity does not automatically grant access to Key Vault, Storage, or any other service. Authentication and authorization are separate steps.
3. Grant the Identity Access to Azure Key Vault
After enabling the identity, grant it only the permissions required by the application.
Azure Key Vault supports Azure role-based access control (Azure RBAC) when the vault is configured to use the RBAC permission model.
For example, if the application only needs to read secrets, the Key Vault Secrets User role is commonly appropriate.
First, find the vault's resource ID:
az keyvault show \
--name my-app-keyvault \
--resource-group my-resource-group \
--query id \
--output tsv
Store the returned resource ID in a shell variable if your shell supports the syntax below:
VAULT_ID=$(az keyvault show \
--name my-app-keyvault \
--resource-group my-resource-group \
--query id \
--output tsv)
Get the managed identity's principal ID:
PRINCIPAL_ID=$(az webapp identity show \
--name my-dotnet-app \
--resource-group my-resource-group \
--query principalId \
--output tsv)
Assign the role:
az role assignment create \
--assignee-object-id "$PRINCIPAL_ID" \
--assignee-principal-type ServicePrincipal \
--role "Key Vault Secrets User" \
--scope "$VAULT_ID"
These commands assume a shell that supports variable assignment using the syntax shown.
If the vault uses the older vault access policy model rather than Azure RBAC, permissions must be configured through that model instead. Check the vault's permission configuration before assigning roles.
Role assignments can take some time to propagate. If access fails immediately after assigning a role, allow for propagation and check the role assignment and vault configuration before changing application code.
4. Add the Required .NET Packages
For a .NET application, the Azure Identity library provides credential implementations that can acquire tokens from supported identity sources.
The Azure Security Key Vault Secrets library provides the client used to retrieve secrets.
Install both packages:
dotnet add package Azure.Identity
dotnet add package Azure.Security.KeyVault.Secrets
These packages work together:
The application does not need a stored Key Vault password or client secret when using the App Service's managed identity.
5. Read a Secret Using DefaultAzureCredential
Create a service class that reads a secret from Key Vault.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
public sealed class KeyVaultService
{
private readonly SecretClient _secretClient;
public KeyVaultService(IConfiguration configuration)
{
var vaultUri = configuration["KeyVault:VaultUri"];
if (string.IsNullOrWhiteSpace(vaultUri))
{
throw new InvalidOperationException(
"Key Vault URI is not configured.");
}
_secretClient = new SecretClient(
new Uri(vaultUri),
new DefaultAzureCredential());
}
public async Task<string> GetSecretAsync(
string secretName,
CancellationToken cancellationToken = default)
{
KeyVaultSecret secret = await _secretClient.GetSecretAsync(
secretName,
cancellationToken: cancellationToken);
return secret.Value;
}
}
The application needs the vault URI, but it does not need a stored password to authenticate.
Add the URI to your configuration:
{
"KeyVault": {
"VaultUri": "https://my-app-keyvault.vault.azure.net/"
}
}
The URI is not a credential. Nevertheless, keep environment-specific configuration appropriate for each deployment.
Register the service in Program.cs:
builder.Services.AddSingleton<KeyVaultService>();
You can then inject KeyVaultService into the component that needs to retrieve a secret.
For example, a controller can request the secret through dependency injection:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/configuration")]
public class ConfigurationController : ControllerBase
{
private readonly KeyVaultService _keyVaultService;
public ConfigurationController(
KeyVaultService keyVaultService)
{
_keyVaultService = keyVaultService;
}
[HttpGet("check")]
public async Task<IActionResult> Check(
CancellationToken cancellationToken)
{
var value = await _keyVaultService.GetSecretAsync(
"ApplicationSetting",
cancellationToken);
return Ok(new
{
Retrieved = !string.IsNullOrEmpty(value)
});
}
}
This example demonstrates retrieving a secret, but production applications should avoid returning secret values through an API. The response above reports only whether a value was retrieved.
Also consider whether a secret needs to be fetched on every request. For frequently used settings, an appropriate caching strategy can reduce repeated Key Vault calls. Make sure the caching and refresh behavior matches your secret-rotation requirements.
6. How Does DefaultAzureCredential Work?
DefaultAzureCredential chooses from a sequence of supported credential sources.
In a developer environment, it may use a credential from Azure CLI or another configured development tool. In Azure, it can use a managed identity when the environment supports it.
This allows the same application code to run locally and in Azure without embedding service passwords in the source code.
For local development, sign in with Azure CLI:
az login
The signed-in developer account must have the required permissions to access the vault.
In production, the deployed App Service must have its managed identity enabled, and that identity must have the necessary role assignment.
If your application uses a user-assigned managed identity, configure the credential to select the intended identity. For example, you can provide the managed identity's client ID through the appropriate DefaultAzureCredential options or supported environment configuration.
Do not assume that the first available credential is always the one you intended. When troubleshooting, check which credential source is being used and ensure the identity has the required permissions.
7. Use Managed Identity to Access Azure Storage
The same approach works with other Azure services that support Microsoft Entra ID authentication.
Suppose your application needs to read blobs from Azure Storage. Install the Blob Storage library:
dotnet add package Azure.Storage.Blobs
Then create a BlobServiceClient using DefaultAzureCredential:
using Azure.Identity;
using Azure.Storage.Blobs;
var storageUri =
new Uri("https://mystorageaccount.blob.core.windows.net");
var blobServiceClient = new BlobServiceClient(
storageUri,
new DefaultAzureCredential());
The application still needs authorization to read the required blobs. For example, the Storage Blob Data Reader role can provide read access at an appropriate scope, such as a storage account or container.
Use the narrowest scope that meets the application's requirements. A role assigned at the storage-account level can grant access more broadly than one assigned to a specific container.
Managed identity removes the need to store an authentication secret, but it does not bypass Azure resource permissions.
8. Common Managed Identity Errors
Authentication fails
Confirm that managed identity is enabled on the correct Azure resource.
Check the identity configuration:
az webapp identity show \
--name my-dotnet-app \
--resource-group my-resource-group
Also verify that the application is running in an environment supported by the selected credential and that the correct identity is being used.
Access is denied
A successful token request does not mean that the application has permission to read a secret or access a storage container.
Check the role assignment, its scope, the target resource's permission model, and whether the assignment has propagated.
For Key Vault, confirm that the vault uses the permission model for which you configured access.
The application works locally but fails in Azure
Your local developer account may have permissions that the production identity does not have.
Test the production identity's role assignments separately. Do not solve this problem by adding a client secret to the application or granting broad permissions without a clear reason.
Network access is blocked
Managed identity handles authentication, not network connectivity. If the target resource uses private endpoints, firewall restrictions, or other network controls, verify that the application can reach the resource endpoint.
An application can obtain an access token successfully and still fail to connect to the target service.
Role assignments appear correct, but access still fails
Check whether the identity used by the application matches the principal receiving the role assignment. This is particularly important when using user-assigned identities or when resources have been recreated.
Review application logs and Azure diagnostic information without logging access tokens or secret values.
9. Security Practices to Follow
Managed identity reduces credential-management work, but the application still needs sound security controls.
Grant only the roles and resource scopes the application requires.
Avoid using broad administrative roles to fix access errors.
Do not log secret values or access tokens.
Keep development and production permissions separate.
Review role assignments when an application's responsibilities change.
Monitor access to sensitive resources where supported.
Treat network restrictions and authorization as separate controls.
Plan how secrets will be rotated if the application still depends on secrets for systems that do not support managed identity.
Not every service supports managed identity or Microsoft Entra ID authentication. In those cases, use a suitable secret-management approach rather than embedding credentials in source code.
Conclusion
Azure Managed Identity lets applications authenticate to supported Azure services without storing service passwords or client secrets in application code.
To use it successfully, enable an identity on the hosting resource, grant the identity the correct permissions, and configure the application to request tokens through Azure Identity.
If access fails, check authentication, authorization, resource configuration, and network connectivity separately. This makes troubleshooting easier and avoids unnecessary permission changes.
For ASP.NET Core applications hosted in Azure, managed identity is a practical way to reduce credential exposure while keeping access to services such as Key Vault and Azure Storage under explicit control.
Join the conversation! Your thoughts help the community grow.