Skip to content

Key Vault Secrets Management (Managed Identity)

Use this recipe to access Key Vault secrets from a Python Container App without embedding secrets in code or images.

Architecture

flowchart LR
    P[Container Apps platform] --> S[Secret reference resolution]
    S --> KV[Azure Key Vault]
    KV --> SECRET[Resolved secret value]
    SECRET --> APP[Container App]
    P -.-> MI[Managed Identity]
    MI -.-> ENTRA[Microsoft Entra ID]
    MI -.-> KV

Solid arrows show runtime data flow. Dashed arrows show identity and authentication.

Prerequisites

  • Existing Container App: $APP_NAME in $RG
  • Existing Key Vault with RBAC authorization enabled
  • Azure CLI with Container Apps extension

Step 1: Enable managed identity on the Container App

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)

Step 2: Grant Key Vault secret permissions via RBAC

export KEY_VAULT_ID=$(az keyvault show \
  --name "$KEY_VAULT_NAME" \
  --resource-group "$RG" \
  --query "id" \
  --output tsv)

az role assignment create \
  --assignee-object-id "$PRINCIPAL_ID" \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" \
  --scope "$KEY_VAULT_ID"

Step 3: Add secret and app configuration

Set a sample secret in Key Vault:

az keyvault secret set \
  --vault-name "$KEY_VAULT_NAME" \
  --name "api-base-url" \
  --value "https://example.internal"

Store Key Vault URL in Container Apps settings:

az containerapp update \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --set-env-vars KEY_VAULT_URL="https://$KEY_VAULT_NAME.vault.azure.net/"

Key Vault references still require correct RBAC and network path

Secret resolution fails when identity permissions, private DNS linkage, or endpoint connectivity are incomplete.

Step 4: Python code (SDK access)

Install dependencies:

pip install azure-identity azure-keyvault-secrets

Read secrets using DefaultAzureCredential:

import os
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

vault_url = os.environ["KEY_VAULT_URL"]
credential = DefaultAzureCredential()
client = SecretClient(vault_url=vault_url, credential=credential)

secret = client.get_secret("api-base-url")
print(secret.value)

Container Apps specifics

  • Use Container App secrets for app-level values that are not in Key Vault.
  • Use Key Vault for centralized secret lifecycle and rotation.
  • For private access, combine with VNet integration and Key Vault private endpoint.

Secret Access Pattern Comparison

Pattern Security Posture Operational Overhead Recommended Usage
Inline secret in app config Lowest Low initially, high long-term risk Avoid in production
Container App secret store Medium Medium App-local secrets with periodic rotation
Key Vault + managed identity Highest Medium upfront, lower ongoing Production baseline for sensitive secrets

Separate secret naming from business naming

Use stable secret names (for example database-password) and rotate values behind the same key to reduce application change frequency.

Verification steps

  1. Validate role assignment:
az role assignment list \
  --assignee "$PRINCIPAL_ID" \
  --scope "$KEY_VAULT_ID" \
  --output table
  1. Verify secret read in app logs:
az containerapp logs show \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --follow false
  1. Validate secret metadata (without exposing secret values):
az keyvault secret show \
  --vault-name "$KEY_VAULT_NAME" \
  --name "api-base-url" \
  --query "{name:name,enabled:attributes.enabled,updated:attributes.updated}"

See Also

Sources