Skip to content

Recipe: Managed Identity in Python Apps on Azure Container Apps

Use managed identity and DefaultAzureCredential to access Azure services from Python without storing credentials in code or configuration.

flowchart TD
    C[Client] --> I[Container Apps Ingress]
    I --> APP[Container App]
    APP -.-> MI[Managed Identity]
    MI -.-> ENTRA[Microsoft Entra ID]
    MI -.-> BLOB[Azure Blob Storage]
    MI -.-> SQL[Azure SQL]
    MI -.-> KV[Azure Key Vault]

Prerequisites

  • Existing Container App ($APP_NAME) in resource group ($RG)
  • Azure services to access (Storage, SQL, Cosmos DB, Key Vault)
  • Azure CLI 2.57+ and Container Apps extension
az extension add --name containerapp --upgrade
Command Purpose
az extension add --name containerapp --upgrade Ensures the Container Apps CLI extension is installed and up to date before you enable managed identities or attach them to the app.

System-assigned vs user-assigned identity

Identity type Scope Lifecycle Typical use
System-assigned One app Deleted with app Simple single-app workloads
User-assigned Reusable Independent resource Shared identity across apps/jobs

Enable identity via CLI

System-assigned:

az containerapp identity assign \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --system-assigned
Command Purpose
az containerapp identity assign --name "$APP_NAME" --resource-group "$RG" --system-assigned Enables a system-assigned managed identity on the Python app so it can authenticate to Azure services without embedded secrets.

User-assigned:

az identity create \
  --name "id-$APP_NAME" \
  --resource-group "$RG" \
  --location "$LOCATION"

export UAMI_ID=$(az identity show \
  --name "id-$APP_NAME" \
  --resource-group "$RG" \
  --query "id" \
  --output tsv)

az containerapp identity assign \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --user-assigned "$UAMI_ID"
Command Why it is used
az identity create ... Creates a user-assigned managed identity for image pulls or runtime access.

Enable identity via Bicep

resource app 'Microsoft.App/containerApps@2023-05-01' = {
  name: appName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    managedEnvironmentId: environmentId
    template: {
      containers: [
        {
          name: 'app'
          image: imageName
        }
      ]
    }
  }
}

RBAC role assignments

Grant least privilege on each target resource.

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)"
Command Why it is used
az containerapp show ... Reads the Container App configuration so the documented setting can be verified.

Use DefaultAzureCredential in Python

DefaultAzureCredential uses managed identity in Container Apps, and falls back to Azure CLI credentials in local development.

from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()

Example: Flask app reading Blob Storage with managed identity

import os
from flask import Flask, jsonify
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

app = Flask(__name__)
credential = DefaultAzureCredential()

account_url = os.environ["STORAGE_ACCOUNT_URL"]
container_name = os.getenv("STORAGE_CONTAINER", "app-data")

@app.get("/blobs")
def list_blobs():
    service = BlobServiceClient(account_url=account_url, credential=credential)
    container = service.get_container_client(container_name)
    names = [blob.name for blob in container.list_blobs()]
    return jsonify(blobs=names), 200

Set environment variables:

az containerapp update \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --set-env-vars \
    STORAGE_ACCOUNT_URL="https://$STORAGE_ACCOUNT.blob.core.windows.net" \
    STORAGE_CONTAINER="app-data"
Command Why it is used
az containerapp update ... Updates the existing Container App configuration without recreating the app.

Common service patterns

  • Storage: Storage Blob Data Reader/Contributor
  • Azure SQL: database user mapped to Entra principal
  • Cosmos DB: Cosmos DB Built-in Data Reader/Contributor
  • Key Vault: Key Vault Secrets User

Local development with identity fallback

az login
az account set --subscription "<subscription-id>"
python -m flask --app src.app run --port 8000
Command Why it is used
az login ... Authenticates the CLI session before Azure resource operations.

DefaultAzureCredential will use Azure CLI token locally and managed identity in Container Apps.

Advanced Topics

  • Use user-assigned identities for blast-radius control and explicit reuse.
  • Split read/write privileges into separate identities for zero-trust boundaries.
  • Add startup checks that verify token acquisition and downstream authorization.

See Also

Sources