Recipe: Managed Identity in .NET Apps on Azure Container Apps¶
Use DefaultAzureCredential and ASP.NET Core dependency injection to access Azure resources without connection secrets.
flowchart LR
C[Client] --> APP[ASP.NET Core App]
APP -.-> MI[Managed Identity]
MI -.-> ENTRA[Microsoft Entra ID]
MI -.-> BLOB[Azure Blob Storage]
style APP fill:#107c10,color:#fff Prerequisites¶
- Existing Container App (
$APP_NAME) in resource group ($RG) - Storage account (
$STORAGE_ACCOUNT) and container ($STORAGE_CONTAINER) - Azure CLI with Container Apps extension
Enable managed identity and RBAC¶
az containerapp identity assign \
--name "$APP_NAME" \
--resource-group "$RG" \
--system-assigned
export PRINCIPAL_ID=$(az containerapp show \
--name "$APP_NAME" \
--resource-group "$RG" \
--query "identity.principalId" \
--output tsv)
az role assignment create \
--assignee-object-id "$PRINCIPAL_ID" \
--assignee-principal-type ServicePrincipal \
--role "Storage Blob Data Reader" \
--scope "$(az storage account show --name "$STORAGE_ACCOUNT" --resource-group "$RG" --query id --output tsv)"
ASP.NET Core 8 DI pattern¶
using Azure.Identity;
using Azure.Storage.Blobs;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(_ =>
{
var accountUrl = builder.Configuration["Storage:AccountUrl"]!;
return new BlobServiceClient(new Uri(accountUrl), new DefaultAzureCredential());
});
var app = builder.Build();
app.MapGet("/blobs", async (BlobServiceClient blobService, IConfiguration config) =>
{
var containerName = config["Storage:Container"] ?? "app-data";
var container = blobService.GetBlobContainerClient(containerName);
var names = new List<string>();
await foreach (var blob in container.GetBlobsAsync())
{
names.Add(blob.Name);
}
return Results.Ok(new { blobs = names });
});
app.Run();
Advanced Topics¶
- Use separate user-assigned identities for read-only and write paths.
- Add readiness checks that validate token acquisition and service authorization.
- Use policy-based authorization for resource-specific routes.