Azure Key Vault external secrets
Use Azure Key Vault to deliver secrets to Aidbox on Kubernetes — from creating the infrastructure to configuring a Client with a vault-backed secret.
This functionality is available starting from version 2602.
Overview
This tutorial walks through setting up Azure Key Vault as an external secret store for Aidbox running on AKS. By the end you will have:
- An Azure Key Vault holding a client secret
- The Secrets Store CSI Driver mounting that secret as a file inside the Aidbox pod
- A vault config mapping the file to a named secret with resource scope
- A
Clientthat references the secret via a FHIR extension — the actual value is never stored in the database
flowchart LR
AKV(Azure Key Vault):::blue2 -->|fetches secret| CSI(CSI Provider):::neutral2
CSI -->|mounts as file| Pod(/run/azure-secrets/client-secret):::blue2
Config(vault-config.json):::blue2 -->|maps name → path + scope| Aidbox(Aidbox):::violet2
Pod -->|reads at runtime| Aidbox
Aidbox -->|stores reference| DB(_secret extension):::neutral2
Prerequisites
- Azure subscription with permissions to create resource groups, AKS clusters, and Key Vaults
- Azure CLI
(
az) installed kubectlandhelminstalled- Setup the local Aidbox instance using Getting Started guide
Step 1. Create Azure infrastructure
Create a resource group, a Key Vault, and store a client secret:
Key Vault names are globally unique. Replace <your-keyvault-name> with a unique name throughout this tutorial.
az group create --name aidbox-rg --location eastus
az keyvault create \
--name <your-keyvault-name> \
--resource-group aidbox-rg \
--location eastus
az keyvault secret set \
--vault-name <your-keyvault-name> \
--name client-secret \
--value 'super-secret-value'
Step 2. Create AKS cluster
az aks create \
--resource-group aidbox-rg \
--name aidbox-aks \
--node-count 1 \
--generate-ssh-keys
az aks get-credentials \
--resource-group aidbox-rg \
--name aidbox-aks
Step 3. Install Secrets Store CSI Driver
helm repo add secrets-store-csi-driver \
https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set enableSecretRotation=true \
--set rotationPollInterval=30s
enableSecretRotation and rotationPollInterval control how often the driver checks for updated secrets. Set the interval based on your rotation requirements.
Step 4. Install Azure Key Vault Provider
helm repo add csi-secrets-store-provider-azure \
https://azure.github.io/secrets-store-csi-driver-provider-azure/charts
helm install azure-kv csi-secrets-store-provider-azure/csi-secrets-store-provider-azure \
--namespace kube-system \
--set secrets-store-csi-driver.install=false
Wait for pods to be ready:
kubectl wait --for=condition=ready pod \
-l app=csi-secrets-store-provider-azure \
--namespace kube-system \
--timeout=120s
Step 5. Grant AKS access to Key Vault
Get the kubelet managed identity and assign the Key Vault Secrets User role:
IDENTITY_ID=$(az aks show \
--resource-group aidbox-rg \
--name aidbox-aks \
--query "identityProfile.kubeletidentity.objectId" \
--output tsv)
IDENTITY_CLIENT_ID=$(az aks show \
--resource-group aidbox-rg \
--name aidbox-aks \
--query "identityProfile.kubeletidentity.clientId" \
--output tsv)
KV_ID=$(az keyvault show \
--name <your-keyvault-name> \
--resource-group aidbox-rg \
--query id --output tsv)
az role assignment create \
--assignee "$IDENTITY_ID" \
--role "Key Vault Secrets User" \
--scope "$KV_ID"
Save IDENTITY_CLIENT_ID — you will need it in the next step.
Step 6. Create SecretProviderClass
This tells the CSI Driver which secrets to fetch from Azure Key Vault and how to authenticate using the kubelet managed identity:
Replace the placeholders:
<IDENTITY_CLIENT_ID>— kubelet identity client ID from the previous step<your-keyvault-name>— your Key Vault name<your-tenant-id>— your Azure AD tenant ID (get it withaz account show --query tenantId --output tsv)
Step 7. Create vault config
The vault config JSON maps named secrets to file paths and declares which resources may access them:
Each entry under "secret" maps a secret name to:
| Field | Description |
|---|---|
path | Absolute path to the file containing the secret value |
scope | Array of resource references allowed to access this secret. Entries can be "ResourceType/id" (specific instance, e.g. "Client/basic") or "ResourceType" (any instance of that type, e.g. "Client") |
Step 8. Deploy Aidbox
See Recommended environment variables for the full list of required Aidbox settings.
Step 9. Verify secret mount
Confirm that the CSI Driver has mounted the secret file:
kubectl exec deploy/aidbox -- ls /run/azure-secrets/
Expected output:
client-secret
Verify the content:
kubectl exec deploy/aidbox -- cat /run/azure-secrets/client-secret
Step 10. Create Client with vault-backed secret
Create a Client that references the secret by name using the FHIR primitive extension pattern. The _secret element carries two extensions: data-absent-reason with value masked (indicating the field is intentionally absent) and secret-reference with the secret name from the vault config.
Reading the Client back returns the extension with the secret name, never the actual value:
At runtime, Aidbox resolves client-secret through the vault config, verifies that Client/basic is in scope, and reads the file content. You can verify this by authenticating as the client — the vault-backed secret value is used for authentication:
# Succeeds (returns 200 OK)
curl -u basic:super-secret-value http://<aidbox-url>/fhir/Patient
# Fails (returns 401 Unauthorized)
curl -u basic:wrong-password http://<aidbox-url>/fhir/Patient
Secret rotation
Update the secret in Azure Key Vault:
az keyvault secret set \
--vault-name <your-keyvault-name> \
--name client-secret \
--value 'new-secret-value'
The CSI Driver picks up the change based on rotationPollInterval (30s in this tutorial). Aidbox detects the file modification and invalidates its cache — the new value is used on the next access. No pod restart required.
Next steps
For the full reference on the vault config format, extension pattern, scope enforcement, and the list of supported resources, see External Secrets.
Last updated: