🔑 Azure KeyVault setup for AKS

Azure Key Vault is a cloud service provided by Microsoft Azure to securely store and manage sensitive information such as:

  • Secrets (passwords, connection strings, API keys)

  • Keys (encryption keys)

  • Certificates

Instead of storing sensitive values directly in application configuration files or Kubernetes manifests, Azure Key Vault allows applications to retrieve secrets securely at runtime using Azure-managed identities and role-based access control (RBAC).

🟦 Creating Azure Key Vault for AKS Integration

Azure Key Vault supports two authorization models.

Choose one based on your requirement:

  • Option A: Azure RBAC (Recommended)

  • Option B: Vault Access Policy (Legacy / Alternative)

Step 1: Create Azure Key Vault (Common for both)

  1. Sign in to https://portal.azure.com
  2. Search for Key Vault → Click Create

Azure KeyVault

  1. Fill in the Basics tab:

    • Subscription

    • Resource Group

    • Key Vault name (example: aks-ClusterKeyVault)

    • Region (same as AKS)

    • Pricing tier: Standard

  2. Click Next

Step 2: Choose Access Configuration (DIFFERENT STEP)

🔹 Option A: Azure RBAC (Recommended)

On the Access configuration tab:

Select Azure role-based access control (Azure RBAC)

🔹 Option B: Vault Access Policy (Legacy)

On the Access configuration tab:

Select Vault access policy

Azure KeyVault

Step 3: Networking (Common for both)

• Select Public endpoint (or configure Private Endpoint if required)

• Allow access from trusted Azure services

• Click Next

Step 4: Review and Create (Common)

• Review configuration

• Click Create

Step 5: Add Secrets to Key Vault (Common)

• Open the created Key Vault

• Go to Secrets

• Click + Generate/Import

• Add required secrets

Azure KeyVault Azure KeyVault

Step 6: Install Key Vault CSI Driver Add-on in AKS

Enable the Azure Key Vault Secrets Provider add-on on the AKS cluster:

az aks enable-addons ^
  --addons azure-keyvault-secrets-provider ^
  --name <AKS-NAME> ^
  --resource-group <Resourcegroup>

This command may take a few minutes to complete. Verify installation:

kubectl get pods -n kube-system

You should see pods similar to:

secrets-store-csi-driver-xxxx

secrets-store-provider-azure-xxxx

Azure KeyVault

Step 7:Identify Managed Identities used by AKS

List managed identities created for the AKS cluster:

az identity list --query "[].{name:name,rg:resourceGroup}" -o table

You should find an identity with the name:

azurekeyvaultsecretsprovider-aks-cluster-name

Example: azurekeyvaultsecretsprovider-cluster-leasetool

Azure KeyVault

Step 8: Get Managed Identity Object ID (Principal ID)

This Object ID is required for RBAC role assignment.

az identity show ^
  --name <secret-provider-IdentityName> ^
  --resource-group <secret-provider-ResourceGroup> ^
  --query principalId -o tsv

Azure KeyVault

Step 9: Assigning Role

🔹 Option A (RBAC Policy): Assign RBAC role to the Managed Identity If the Key Vault is configured in Azure RBAC mode, Access Policies are ignored.

Assign the minimum required role:

Role: Key Vault Secrets User (read-only, recommended for applications)

az role assignment create ^
  --assignee <MANAGED_IDENTITY_OBJECT_ID> ^
  --role "Key Vault Secrets User" ^
  --scope /subscriptions/<SUB_ID>/resourceGroups/<KV_RG>/providers/Microsoft.KeyVault/vaults/<KEYVAULT_NAME>

For the which is subscription id, please run the below command:

az account show --query id -o tsv

Azure KeyVault

Azure KeyVault

Important: Azure RBAC propagation takes 10–15 minutes. Pods must be restarted after the role becomes effective.

🔹 Option B (Vault Policy): Grant Key Vault Access Using Vault Access Policy

Run the following command to grant read-only access to Key Vault secrets:

az keyvault set-policy ^
  --name <KEYVAULT_NAME> ^
  --object-id <MANAGED_IDENTITY_OBJECT_ID> ^
  --secret-permissions get list

To verify that the policy was added:

az keyvault show ^
  --name <KEYVAULT_NAME> ^
  --query properties.accessPolicies

Step 10: Get Client ID and Tenant ID

Client ID (used by CSI driver)

az identity show ^
  --name <secretprovider-IdentityName> ^
  --resource-group <secretprovider-ResourceGroup> ^
  --query clientId -o tsv

Azure KeyVault

Tenant ID

az account show --query tenantId -o tsv

Azure KeyVault

Step 11: Create SecretProviderClass

Create secretproviderclass.yaml:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: leasetool-kv
  namespace: pvtest
spec:
  provider: azure
  secretObjects:                     
  - secretName: leasetool-kv-secrets
    type: Opaque
    data:
    - objectName: DBCONNECTSTR
      key: DB_CONNECTSTR
    - objectName: SYSADMINPWD
      key: SYSADMIN_PWD
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "true"
    userAssignedIdentityID: <Client ID>
    keyvaultName: aks-ClusterKeyVault
    tenantId: <Tenant ID>
    objects: |
      array:
        - |
          objectName: DBCONNECTSTR
          objectType: secret
        - |
          objectName: SYSADMINPWD
          objectType: secret

Step 12: Update Deployment to consume secrets Environment variables from Kubernetes Secret

Under env need to add like this so that the values can be fetched from the secrets:

  - name: DB_CONNECTSTR
          valueFrom:
            secretKeyRef:
              name: leasetool-kv-secrets
              key: DB_CONNECTSTR

        - name: SYSADMIN_PWD
          valueFrom:
            secretKeyRef:
              name: leasetool-kv-secrets
              key: SYSADMIN_PWD

        volumeMounts:
        - name: secrets-store
          mountPath: "/mnt/secrets"
          readOnly: true

      volumes:
      - name: secrets-store
        csi:
          driver: secrets-store.csi.k8s.io
          readOnly: true
          volumeAttributes:
            secretProviderClass: leasetool-kv

So the complete deployment.yaml file will look like:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: leasetool #This is the name of the Deployment, can be replaced with the required ApplicationName (eg: SCMWebApp..)
  namespace: pvtest
spec:
  replicas: 1 #This speocifies that one pod should be running at all times
  selector:
    matchLabels:
      app: leasetool #This is the name of the ApplicationName, can be replaced with the required ApplicationName (eg: SCMWebApp..)
  template:
    metadata:
      labels:
        app: leasetool #This is the name of the ApplicationName, can be replaced with the required ApplicationName (eg: SCMWebApp..)
    spec:
      imagePullSecrets: 
      - name: acr-secret  # ImagePullSecret: Secret Name: acr-secret, which is used to pull the images from ACR

      # 🔐 POD-LEVEL SECURITY
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000

      containers:
      - name: leasetool   #The container is named leasetool, can be replaced with the required ApplicationName (eg: SCMWebApp..)
        image: planvisage.azurecr.io/leasetool:latest
        imagePullPolicy: Always

        ports:
        - containerPort: 8080 # Exposes port 8080 inside container.

        # 🔐 CONTAINER-LEVEL SECURITY
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1000
          capabilities:
            drop:
              - ALL
        # Setting Environment Variables
        env:
        - name: TIME_ZONE
          value: "Asia/Kolkata"

        - name: DB_TYPE
          value: ""

        - name: BLOB_CONTAINER_NAME
          value: ""

        - name: LOG_TARGET
          value: "FILE"

        - name: SECURE_COOKIE 
          value: "False" # False since we have not set SSL for the testing, this can be set to True when SSL is setup.

        # 🔐 FROM AZURE KEY VAULT (Synced Kubernetes Secret)
        - name: DB_CONNECTSTR
          valueFrom:
            secretKeyRef:
              name: leasetool-kv-secrets
              key: DB_CONNECTSTR

        - name: SYSADMIN_PWD
          valueFrom:
            secretKeyRef:
              name: leasetool-kv-secrets
              key: SYSADMIN_PWD

        volumeMounts:
        # Writable temp directory (required for read-only root FS)
        - name: tmp
          mountPath: /tmp

        # Writable directories
        - name: applogs
          mountPath: /app/wwwroot/log

        - name: appconfig
          mountPath: /app/wwwroot/xml

        - name: appdata
          mountPath: /app/wwwroot/data

        - name: appdesign
          mountPath: /app/wwwroot/design

        # 🔐 Mount Secrets Store CSI (Required to trigger secret sync)
        - name: secrets-store
          mountPath: "/mnt/secrets"
          readOnly: true

      volumes:
      # 🔐 Azure Key Vault CSI Volume
      - name: secrets-store
        csi:
          driver: secrets-store.csi.k8s.io
          readOnly: true
          volumeAttributes:
            secretProviderClass: leasetool-kv

      # Writable volumes
      - name: applogs
        emptyDir: {}

      - name: appconfig
        emptyDir: {}

      - name: appdata
        emptyDir: {}

      - name: appdesign
        emptyDir: {}

      - name: tmp
        emptyDir: {}


The environment variables used in the above deployment.yaml (such as database connection strings, blob storage configuration, logging, and security settings) are explained in detail in the document below.

Refer to: Application Environmental Variables

Step 13: Apply configurations and restart deployment

kubectl apply -f secretproviderclass.yaml
kubectl apply -f deployment.yaml

Follow the below steps only if application was already deployed, else go back to Deployment page and proceed with deployment.

kubectl rollout restart deployment <appname> -n <namespace>
kubectl get pods -n <namespace>

Verification

After confirming if the pod is running, we can confirm if the vault secret was created by running below command:

kubectl get secret <secretname-given-inSecretProviderClass> -n <namespace>

Azure KeyVault

kubectl exec -n pvtest -it <podname> -- ls /mnt/secrets 

Azure KeyVault

Confirming the Secret Value coming up correctly:

kubectl exec -n pvtest -it <podname> -- cat /mnt/secrets/SYSADMINPWD

Azure KeyVault