How to use Azure Key Vault with Kubernetes cluster

Jan Sunavec
4 min readNov 29, 2023
How to use Azure Key Vault with Kubernetes cluster

There are numerous ways to incorporate Key Vault within the application or alongside workload IDs, but our emphasis will be on simplicity. We’ll establish a Key Vault storage, introduce additional secrets, create a managed identity, and integrate it into the Pod.

Kubernetes part

Firstly, we must enable a secret provider for the cluster. The provider serves as the bridge connecting Kubernetes and Key Vault.

az aks enable-addons --addons azure-keyvault-secrets-provider --name <cluster-name> --resource-group <cluster-resource-group-name>

An Azure service will be generated on each node. You can verify it using the following command:

kubectl get pods -n kube-system -l ‘app in (secrets-store-csi-driver,secrets-store-provider-azure)’

Let’s generate a managed identity, akin to a dedicated service account designed for connections to services such as Key Vault. Further information can be found here:
https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-manage-user-assigned-managed-identities?pivots=identity-mi-methods-azp#create-a-user-assigned-managed-identity

az identity create -g <resource-group> -n <identity-name>

So we have identity but we need to assign it with our cluster.

az vmss identity assign -g <resource-group> -n <agent-pool-vmss> -identities <identity-resource-id>

If you have multiple virtual machine (VM) scale sets, you’ll need to assign the managed identity to each of them, especially to the scale sets where you require access to secrets from Key Vault.

Key Vault Part

Firstly we need to create the Key Vault.

az keyvault create -n my-super-vault -g <resource-group-name> -l westeurope --enable-rbac-authorization

This is just storage and we need to put secrets there.

az keyvault secret set --vault-name my-super-vault -n root-password --value “adfasdfasdrcqewrc”
az keyvault secret set --vault-name my-super-vault -n auth-token --value “`!e%0g{VZxn5o8”

With our Key Vault properly configured, the next step is to associate it with our managed identity.

export IDENTITY_CLIENT_ID=”$(az identity show -g <resource-group> --name <identity-name> --query ‘clientId’ -o tsv)”
export KEYVAULT_SCOPE=$(az keyvault show --name <key-vault-name> --query id -o tsv)

az role assignment create --role "Key Vault Administrator" --assignee $IDENTITY_CLIENT_ID --scope $KEYVAULT_SCOPE
az keyvault set-policy -n my-super-vault --secret-permissions get --spn IDENTITY_CLIENT_ID

And we need also make this identity proper role. But run this command after you will create key vault record.

export KEYVAULT_SCOPE=$(az keyvault show --name <key-vault-name> --query id -o tsv)
az role assignment create --role "Key Vault Administrator" --assignee <identity-client-id> --scope $KEYVAULT_SCOPE

Final destination

The final step involves deployment. We must furnish a secretProviderClass to effectively map Key Vault records onto the files.

# This is a SecretProviderClass example using user-assigned identity to access your key vault
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: my-super-secret-provider-class
spec:
provider: azure
secretObjects:
- data:
- objectName: root-password
key: root-password
- objectName: auth-token
key: auth-token
secretName: final-secrets # this is Kind:secret name
type: Opaque
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true" # Set to true for using managed identity
userAssignedIdentityID: <client-id> # Set the clientID of the user-assigned managed identity to use
keyvaultName: my-super-vault # Set to the name of your key vault
cloudName: "" # [OPTIONAL for Azure] if not provided, the Azure environment defaults to AzurePublicCloud
objects: |
array:
- |
objectName: root-password
objectType: secret # object types: secret, key, or cert
objectVersion: "" # [OPTIONAL] object versions, default to latest if empty
- |
objectName: auth-token
objectType: key
objectVersion: ""
tenantId: <tenant-id>

kubectl apply -f secretproviderclass.yaml

And this is the final step.

# This is a sample pod definition for using SecretProviderClass and the user-assigned identity to access your key vault
kind: Pod
apiVersion: v1
metadata:
name: busybox-secrets-store-inline-user-msi
spec:
containers:
- name: busybox
image: registry.k8s.io/e2e-test-images/busybox:1.29-4
command:
- "/bin/sleep"
- "10000"
env:
- name: AUTH_TOKEN
valueFrom:
secretKeyRef:
key: auth-token
name: final-secrets
volumeMounts:
- name: secrets-store01-inline
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: secrets-store01-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "my-super-secret-provider-class"
kubectl apply -f ./pod.yaml

Upon executing this command, you should observe the creation of a new entry with Kind: secret, named “final-secrets.” You might express a preference to solely use the secret without mounting it as a file. Regrettably, Azure operates with a singular trigger that generates a secret solely when it’s mounted. However, once the secret is established, you can utilize it in its current form.

--

--

Jan Sunavec

CTO, R&D director, Ad-Tech, Video Streaming, OTT, CTV, OpenRTB