Recipe: Key Vault Reference in Node.js Apps on Azure Container Apps
Use Key Vault references and managed identity so Express workloads can consume secrets without embedding values in code or deployment files.
flowchart TD
APP[Express App] -.-> MI[Managed Identity]
MI -.-> ENTRA[Microsoft Entra ID]
MI -.-> KV[Azure Key Vault]
KV -.-> SEC[Container Apps Secret]
SEC -.-> APP
style APP fill:#107c10,color:#fff
Prerequisites
- Container App (
$APP_NAME) and resource group ($RG) - Key Vault (
$KEYVAULT_NAME) with secret name api-key - Azure CLI with Container Apps extension
az extension add --name containerapp --upgrade
| Command | Why it is used |
az extension add ... | Installs or updates the Container Apps Azure CLI extension. |
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 "Key Vault Secrets User" \
--scope "$(az keyvault show --name "$KEYVAULT_NAME" --query id --output tsv)"
az containerapp secret set \
--name "$APP_NAME" \
--resource-group "$RG" \
--secrets "api-key=keyvaultref:https://$KEYVAULT_NAME.vault.azure.net/secrets/api-key,identityref:system"
az containerapp update \
--name "$APP_NAME" \
--resource-group "$RG" \
--set-env-vars "API_KEY=secretref:api-key"
| Command | Why it is used |
az containerapp identity assign ... | Assigns or inspects managed identity configuration for the Container App. |
Express route using Key Vault SDK
const express = require("express");
const { DefaultAzureCredential } = require("@azure/identity");
const { SecretClient } = require("@azure/keyvault-secrets");
const app = express();
const credential = new DefaultAzureCredential();
const keyVaultUrl = process.env.KEYVAULT_URL;
const client = new SecretClient(keyVaultUrl, credential);
app.get("/secret-check", async (_req, res) => {
const secret = await client.getSecret("api-key");
res.status(200).json({ configured: Boolean(secret.value) });
});
Advanced Topics
- Prefer one vault per environment to reduce blast radius.
- Trigger a new revision after rotation when deterministic pickup timing is required.
- Use user-assigned identity for shared secret access across multiple apps.
See Also
Sources