Use Azure Key Vault secrets in Helm values
Mount secrets from Azure Key Vault into DIAL pods using the Secrets Store CSI Driver, so credentials and encryption keys never appear in your Helm values. This guide is for DevOps engineers running DIAL on AKS who already manage an Azure Key Vault. It builds on the Azure deployment guide.
Goal
Retrieve secrets from Azure Key Vault and expose them to DIAL Core and DIAL Chat — as mounted files, or as Kubernetes secrets referenced through environment variables.
Prerequisites
- DIAL deployed on AKS (see Azure deployment)
- The Secrets Store CSI Driver and the Azure provider installed in the cluster
- A user-assigned managed identity with
getaccess to the target Key Vault - An Azure Key Vault containing the secrets you want to mount
Step 1: Define a SecretProviderClass
Add a SecretProviderClass through the chart's extraDeploy values. It tells the CSI driver which Key Vault secrets to retrieve:
extraDeploy:
- apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: "<NAME>" # must be unique within the namespace
namespace: "<NAMESPACE>"
spec:
provider: azure
parameters:
clientID: "<USER_ASSIGNED_CLIENT_ID>"
cloudName: AzurePublicCloud
keyvaultName: <KEYVAULT_NAME>
tenantID: "<IDENTITY_TENANT>"
usePodIdentity: "false"
objects: |
array:
- |
objectName: secret1
objectType: secret
objectVersion: ""
- |
objectName: secret2
objectType: secret
objectVersion: ""
Replace the placeholders:
<NAME>— the name of theSecretProviderClass<NAMESPACE>— the namespace where DIAL is deployed<USER_ASSIGNED_CLIENT_ID>— the client ID of the user-assigned managed identity<KEYVAULT_NAME>— the Key Vault name<IDENTITY_TENANT>— the Microsoft Entra tenant ID
Step 2: Mount the secrets into the pod
Add a CSI volume and mount it into DIAL Core through the chart values:
core:
extraVolumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "<NAME>"
extraVolumeMounts:
- name: secrets
readOnly: true
mountPath: /mnt/secrets-store
Step 3: Reference the mounted secrets
Point DIAL Core's configuration at the mounted file paths:
core:
env:
aidial.config.files: >-
[
"/mnt/secrets-store/secret1",
"/mnt/secrets-store/secret2"
]
This is sufficient when DIAL Core can read configuration directly from the mounted files. The following examples cover the case where a component needs the secrets as environment variables instead.
Sync secrets to a Kubernetes secret
To expose Key Vault values as environment variables, add a secretObjects block to the SecretProviderClass. The CSI driver then mirrors the mounted files into a Kubernetes secret, which a component consumes through extraEnvVarsSecret.
DIAL Chat authentication secrets
Define the SecretProviderClass with a secretObjects mapping:
extraDeploy:
- apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
namespace: "<NAMESPACE>"
name: "<NAME>" # must be unique within the namespace
spec:
provider: azure
parameters:
usePodIdentity: "false"
clientID: "<USER_ASSIGNED_CLIENT_ID>"
cloudName: AzurePublicCloud
keyvaultName: <KEYVAULT_NAME>
tenantID: "<IDENTITY_TENANT>"
objects: |
array:
- |
objectName: nextauth-secret
objectType: secret
- |
objectName: auth-azuread-client-id
objectType: secret
- |
objectName: auth-azuread-client-secret
objectType: secret
secretObjects:
- secretName: "<SECRET_NAME>"
type: Opaque
data:
- objectName: nextauth-secret
key: NEXTAUTH_SECRET
- objectName: auth-azuread-client-id
key: AUTH_AZURE_AD_CLIENT_ID
- objectName: auth-azuread-client-secret
key: AUTH_AZURE_AD_SECRET
Reference the Kubernetes secret in the DIAL Chat values:
chat:
extraEnvVarsSecret: "<SECRET_NAME>"
extraVolumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "<NAME>"
extraVolumeMounts:
- name: secrets
readOnly: true
mountPath: /mnt/secrets-store
<SECRET_NAME> is the name of the Kubernetes secret the driver creates.
DIAL Core encryption secrets
Map the encryption secret and key from Key Vault to a Kubernetes secret:
extraDeploy:
- apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
namespace: "<NAMESPACE>"
name: "<NAME>" # must be unique within the namespace
spec:
provider: azure
parameters:
usePodIdentity: "false"
clientID: "<USER_ASSIGNED_CLIENT_ID>"
cloudName: AzurePublicCloud
keyvaultName: <KEYVAULT_NAME>
tenantID: "<IDENTITY_TENANT>"
objects: |
array:
- |
objectName: encryption-secret
objectType: secret
- |
objectName: encryption-key
objectType: secret
secretObjects:
- secretName: "<SECRET_NAME>"
type: Opaque
data:
- objectName: encryption-secret
key: aidial.encryption.secret
- objectName: encryption-key
key: aidial.encryption.key
Reference it in the DIAL Core values:
core:
configuration:
encryption:
existingSecret: "<SECRET_NAME>"
extraVolumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "<NAME>"
extraVolumeMounts:
- name: secrets
readOnly: true
mountPath: /mnt/secrets-store/dial-core-encryption
Result
DIAL Core and DIAL Chat read their secrets from Azure Key Vault at runtime. Rotating a secret in Key Vault updates the mounted files without changing the Helm release.
Further reading
- Azure deployment — the AKS deployment whose Helm values use these secrets
- Manage secrets — supply DIAL's secrets through Kubernetes Secrets or a vault
- Encryption configuration — how DIAL Core uses the encryption secret and key