Skip to main content

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 get access 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 the SecretProviderClass
  • <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​