kubernetesaksazureworkload-identitykey-vaultcsi-driversecuritydevops

AKS Workload Identity Flow with Azure Key Vault

April 20, 2026·11 min read

A complete walkthrough of passwordless secret access in AKS using Workload Identity, Azure Active Directory Federated Credentials, and the CSI Secret Store Driver — no static credentials, no secrets in YAML.

Introduction

Managing secrets in Kubernetes is one of those problems that looks simple on the surface but has real security implications beneath. The naive approach — baking credentials into environment variables or ConfigMaps — works, but leaves sensitive data exposed in etcd, container specs, and logs.

In this blog, I walk through a production-grade approach: using AKS Workload Identity together with the CSI Secret Store Driver to pull secrets from Azure Key Vault without ever handling a password, client secret, or static credential.

This is not just a how-to. The goal is to understand every component in the chain — why it exists, what problem it solves, and how it connects to the next piece.


What Problem Are We Solving?

Traditional approaches to secrets in Kubernetes fall into one of these categories:

  • Hardcoded credentials in environment variables — simple but dangerous
  • Kubernetes Secrets — base64-encoded (not encrypted) and stored in etcd
  • Init containers that fetch secrets at startup — better, but requires managing a service principal with a password
  • Vault agent sidecars — powerful but adds operational complexity

Each of these involves some form of static credential — something that can be leaked, rotated incorrectly, or forgotten in a Git history.

Workload Identity eliminates the static credential entirely. Instead of asking a pod to prove who it is with a password, the cluster vouches for it using a cryptographically signed token that Azure AD can verify directly.


Tech Stack

ComponentTechnology
Container OrchestrationAzure Kubernetes Service (AKS)
Identity ProviderAzure Active Directory (Entra ID)
Secret StoreAzure Key Vault
Identity MechanismWorkload Identity (OIDC + Managed Identity)
Secret DeliveryCSI Secret Store Driver
Auth ProtocolOpenID Connect (OIDC)
Token TypeSigned JWT (JSON Web Token)

Architecture Overview

AKS Workload Identity Flow Full flow — Pod → OIDC Token → Azure AD → Access Token → Key Vault → CSI Driver → Mounted Secret

The architecture spans three Azure services — AKS, Azure Active Directory, and Azure Key Vault — and involves three components inside the cluster itself.

  • Inside AKS Cluster: Pod → Service Account → OIDC Token → CSI Driver → Mounted Secret
  • Azure Active Directory: Federated Credential validates OIDC token → issues Access Token via Managed Identity
  • Azure Key Vault: RBAC check → returns encrypted secret → CSI driver mounts it

Complete Flow — Step by Step

StepActionDescription
1Pod → Service AccountApplication pod uses its Kubernetes Service Account as identity
2Service Account → OIDC TokenAKS issues a signed JWT (OIDC Token) for the Service Account
3OIDC Token → Azure ADToken sent to Azure AD via Federated Credential (OIDC trust link)
4Azure AD validatesFederated Credential verifies the OIDC token's issuer and subject
5Azure AD issues Access TokenManaged Identity (password-free) receives an Access Token from Azure AD
6Access Token → Key VaultAccess Token presented to Azure Key Vault for authorization
7RBAC CheckKey Vault validates Key Vault Secrets User role assignment
8Secret ReturnedEncrypted secret value sent to the CSI driver
9CSI Driver receives secretCSI Secret Store Driver fetches and mounts the secret
10Secret Mounted in PodSecret available at /mnt/secrets-store/ inside the container

Deep Dive: Each Component

1. Pod and Service Account

Every pod in Kubernetes runs under a Service Account. By default this is the default service account in the namespace — but for Workload Identity to work, you create a dedicated Service Account and annotate it with the Managed Identity's client ID.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: workload-identity-sa
  namespace: robot-shop
  annotations:
    azure.workload.identity/client-id: "<MANAGED_IDENTITY_CLIENT_ID>"

2. OIDC Token

When the pod starts, the kubelet mounts a projected volume containing a signed JWT — the OIDC Token. This token is issued by the cluster's OIDC issuer URL and contains the pod's namespace and service account name as claims.

This is not a secret. It is a cryptographically signed statement from the cluster: this pod is running as this service account in this namespace. Azure AD can verify its authenticity without ever contacting the cluster directly — it just needs the cluster's public OIDC discovery endpoint.

3. Federated Credential — The Trust Bridge

On the Azure AD side, you create a Federated Credential on the Managed Identity. This tells Azure AD:

  • Trust tokens issued by this OIDC issuer (your AKS cluster)
  • Where the subject claim matches this service account

This is the key security boundary. Without this configuration, Azure AD would reject any token from the cluster — even a valid one.

az identity federated-credential create \
  --name <FEDERATED_CREDENTIAL_NAME> \
  --identity-name <MANAGED_IDENTITY_NAME> \
  --resource-group <RG_NAME> \
  --issuer <AKS_OIDC_ISSUER_URL> \
  --subject system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT_NAME>

4. Managed Identity — Password-Free Identity

The Managed Identity is an Azure-native identity that has no password, no client secret, and no expiry. It is managed entirely by Azure. You assign Azure RBAC roles to it — in this case, Key Vault Secrets User on the Key Vault.

The pod never holds this identity's credentials. It only holds the OIDC Token, which proves it has the right to assume this identity.

5. Access Token from Azure AD

Azure AD validates the OIDC Token against the Federated Credential configuration. If the issuer URL matches and the subject (namespace/service account) matches, Azure AD issues an Access Token scoped to Azure Key Vault.

This token is short-lived (typically 1 hour) and automatically rotated. No manual rotation. No secret sprawl.

6 & 7. Key Vault RBAC Check

The Access Token is presented to Azure Key Vault. Key Vault validates the token and checks whether the Managed Identity has the Key Vault Secrets User role on this specific Key Vault instance.

If the role assignment exists, access is granted. If not — even with a valid token — access is denied. This is Azure RBAC at the data plane level.

8 & 9. Secret Returned to CSI Driver

Key Vault returns the secret value (still encrypted in transit). The CSI Secret Store Driver receives this value and prepares to mount it inside the pod.

10. Secret Mounted at /mnt/secrets-store/

The CSI driver mounts the secret as a file inside the container at the path defined in your SecretProviderClass. The application reads it like a regular file — no SDK, no API call, no environment variable injection required.

volumeMounts:
  - name: secrets-store
    mountPath: "/mnt/secrets-store"
    readOnly: true
volumes:
  - name: secrets-store
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: "azure-keyvault-provider"

Prerequisites

Before you begin, ensure the following are in place:

  1. AKS Cluster with OIDC Issuer enabled — Required for the cluster to issue OIDC tokens. Enable with --enable-oidc-issuer at creation or update an existing cluster.
  2. Workload Identity add-on enabled on AKS — Install via --enable-workload-identity flag. This deploys the mutating webhook that injects the OIDC token into pods.
  3. CSI Secret Store Driver installed — Install the secrets-store-csi-driver and azure provider via Helm or AKS add-on.
  4. Azure Key Vault created — The Key Vault that will store your application secrets.
  5. User-Assigned Managed Identity — Created in Azure, with Key Vault Secrets User role assigned on the Key Vault.

Step-by-Step Setup

Step 1 — Enable OIDC and Workload Identity on AKS

az aks update \
  --resource-group <RG_NAME> \
  --name <CLUSTER_NAME> \
  --enable-oidc-issuer \
  --enable-workload-identity

Get the OIDC issuer URL — you will need this when creating the Federated Credential:

az aks show \
  --resource-group <RG_NAME> \
  --name <CLUSTER_NAME> \
  --query "oidcIssuerProfile.issuerUrl" \
  -o tsv

Step 2 — Create Managed Identity and Assign Key Vault Role

az identity create \
  --name <MANAGED_IDENTITY_NAME> \
  --resource-group <RG_NAME>

# Get the principal ID
PRINCIPAL_ID=$(az identity show \
  --name <MANAGED_IDENTITY_NAME> \
  --resource-group <RG_NAME> \
  --query principalId -o tsv)

# Assign Key Vault Secrets User role
az role assignment create \
  --assignee $PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show --name <KV_NAME> --query id -o tsv)

Step 3 — Create Federated Credential

az identity federated-credential create \
  --name <FEDERATED_CREDENTIAL_NAME> \
  --identity-name <MANAGED_IDENTITY_NAME> \
  --resource-group <RG_NAME> \
  --issuer <OIDC_ISSUER_URL> \
  --subject system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT_NAME>

Step 4 — Create Kubernetes Service Account

kubectl create namespace robot-shop

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: workload-identity-sa
  namespace: robot-shop
  annotations:
    azure.workload.identity/client-id: "<MANAGED_IDENTITY_CLIENT_ID>"
EOF

Step 5 — Create SecretProviderClass

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-keyvault-provider
  namespace: robot-shop
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    clientID: "<MANAGED_IDENTITY_CLIENT_ID>"
    keyvaultName: "<KEY_VAULT_NAME>"
    objects: |
      array:
        - |
          objectName: db-password
          objectType: secret
    tenantId: "<AZURE_TENANT_ID>"

Step 6 — Mount Secret in Pod

spec:
  serviceAccountName: workload-identity-sa
  containers:
    - name: app
      image: my-app:latest
      volumeMounts:
        - name: secrets-store
          mountPath: "/mnt/secrets-store"
          readOnly: true
  volumes:
    - name: secrets-store
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: "azure-keyvault-provider"

What You Actually Learn From This Setup

Zero-credential architecture is achievable

No passwords. No client secrets. No service principal keys rotated on a schedule. The cluster vouches for the pod using cryptographic tokens, and Azure AD validates them without any pre-shared secret.

OIDC federation is the foundation

The Federated Credential is the piece most tutorials gloss over. Understanding it — what it trusts, what claims it validates, and why it is scoped to a specific namespace and service account — is what turns this from a copy-paste exercise into a real security primitive.

RBAC at the data plane matters

Having a valid Access Token is not enough. Key Vault still enforces its own RBAC. You can have a perfectly valid identity and still be denied if the role assignment is missing or scoped to the wrong Key Vault.

CSI driver decouples secrets from application code

The application reads a file. It does not know about Azure, Key Vault, OIDC, or Managed Identity. That decoupling is valuable — you can swap the secret backend without touching application code.

Automatic rotation works transparently

When a secret is rotated in Key Vault, the CSI driver can refresh the mounted file automatically. No pod restart required, no manual intervention.


Complete Flow Summary

AKS cluster with OIDC issuer enabled
        ↓
Workload Identity webhook injects OIDC token into pod
        ↓
Pod presents OIDC token to Azure AD
        ↓
Federated Credential validates issuer + subject
        ↓
Azure AD issues Access Token for Managed Identity
        ↓
Access Token presented to Azure Key Vault
        ↓
Key Vault checks RBAC → Key Vault Secrets User role
        ↓
Secret returned to CSI Secret Store Driver
        ↓
Secret mounted at /mnt/secrets-store/ inside container
        ↓
Application reads secret as a regular file

Takeaway

AKS Workload Identity with CSI Secret Store Driver is the cleanest approach to secrets management in Azure Kubernetes environments. It eliminates static credentials entirely, integrates naturally with Azure RBAC, and keeps secrets out of your Kubernetes manifests, environment variables, and etcd.

Once you understand the trust chain — cluster issues OIDC token → Azure AD validates via Federated Credential → Managed Identity receives Access Token → Key Vault checks RBAC → CSI driver mounts secret — the whole system becomes transparent.

If you are setting this up yourself, break things intentionally. Remove the Federated Credential and watch the authentication fail. Delete the RBAC role assignment and see Key Vault reject the token. The fastest way to understand a security system is to understand exactly what each gate is checking.


Resources


Connect With Me

For a detailed project walkthrough, step-by-step implementation, and live demo, feel free to connect with me.

Connect with me on LinkedIn and follow for more DevOps and Kubernetes projects.