Managed Identities for AKS

Connecting pods to Azure services like Key Vault, Storage, or Service Bus traditionally meant storing connection strings or client secrets in your Kubernetes Secrets. Managed Identities eliminate that entirely. Your pods get a real Azure identity and request short-lived tokens from Azure directly — no credentials to rotate, no secrets to leak.

Why credentials in Kubernetes Secrets are not enough

Storing a service principal secret in a Kubernetes Secret feels secure, but it has real problems:

  • Kubernetes Secrets are base64-encoded, not encrypted, by default. Anyone with cluster access can decode them.
  • Secrets must be rotated when they expire. In a large cluster with many services, this becomes an operational burden.
  • Leaked secrets remain valid until manually revoked. A leaked credential in a container image or log file compromises your Azure resources until someone notices and rotates it.

Managed Identities solve all three problems. There is no secret — just an identity. Tokens are short-lived and rotated automatically. Even if a pod is compromised, the attacker cannot extract a reusable credential.

The cluster managed identity

Every AKS cluster uses a managed identity for its own operations — creating load balancers, managing VMs, updating DNS records. Enable it at cluster creation:

# Create cluster with system-assigned managed identity (recommended)
az aks create \
  --resource-group my-aks-rg \
  --name my-cluster \
  --enable-managed-identity \
  --generate-ssh-keys

# Or use a user-assigned identity you control
az identity create \
  --resource-group my-aks-rg \
  --name aks-cluster-identity

IDENTITY_ID=$(az identity show \
  --resource-group my-aks-rg \
  --name aks-cluster-identity \
  --query id -o tsv)

az aks create \
  --resource-group my-aks-rg \
  --name my-cluster \
  --enable-managed-identity \
  --assign-identity $IDENTITY_ID \
  --generate-ssh-keys

The cluster identity needs specific Azure roles to function. AKS assigns these automatically for the managed resource group (where nodes live). If you bring your own VNet or other pre-existing resources, you may need to assign roles manually.

Workload Identity — giving pods Azure identities

Workload Identity uses OpenID Connect federation to link a Kubernetes service account to an Azure User-Assigned Managed Identity. When a pod uses that service account, the Azure Identity SDK automatically requests tokens from Azure AD on its behalf.

The flow:

  1. AKS acts as an OIDC issuer — it can sign JWT tokens for service accounts
  2. You create a User-Assigned Managed Identity in Azure
  3. You create a federated credential that trusts tokens from your AKS cluster for a specific service account
  4. Pods using that service account can request Azure AD tokens by calling a local endpoint

Setting up Workload Identity

# Step 1: Enable OIDC issuer and Workload Identity on the cluster
az aks update \
  --resource-group my-aks-rg \
  --name my-cluster \
  --enable-oidc-issuer \
  --enable-workload-identity

# Get the OIDC issuer URL (needed for federation)
OIDC_ISSUER=$(az aks show \
  --resource-group my-aks-rg \
  --name my-cluster \
  --query "oidcIssuerProfile.issuerUrl" -o tsv)
echo $OIDC_ISSUER

# Step 2: Create a user-assigned managed identity
az identity create \
  --resource-group my-aks-rg \
  --name pod-identity

IDENTITY_CLIENT_ID=$(az identity show \
  --resource-group my-aks-rg \
  --name pod-identity \
  --query clientId -o tsv)

IDENTITY_OBJECT_ID=$(az identity show \
  --resource-group my-aks-rg \
  --name pod-identity \
  --query principalId -o tsv)

# Step 3: Assign Azure roles to the identity
# Example: give it access to read Key Vault secrets
az role assignment create \
  --assignee-object-id $IDENTITY_OBJECT_ID \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/{sub}/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/my-vault

# Step 4: Create a Kubernetes service account annotated with the identity
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: default
  annotations:
    azure.workload.identity/client-id: "$IDENTITY_CLIENT_ID"
EOF

# Step 5: Create the federated credential linking SA to the managed identity
az identity federated-credential create \
  --name my-app-federation \
  --identity-name pod-identity \
  --resource-group my-aks-rg \
  --issuer $OIDC_ISSUER \
  --subject "system:serviceaccount:default:my-app-sa" \
  --audience api://AzureADTokenExchange

Using Workload Identity in a pod

After setup, pods that reference the annotated service account automatically get the Azure identity injected as environment variables:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
        azure.workload.identity/use: "true"    # Required label
    spec:
      serviceAccountName: my-app-sa            # The annotated SA
      containers:
      - name: app
        image: myapp:1.0

The Workload Identity webhook injects three environment variables into the pod:

  • AZURE_CLIENT_ID — the managed identity’s client ID
  • AZURE_TENANT_ID — your Azure tenant ID
  • AZURE_FEDERATED_TOKEN_FILE — path to a projected volume with the OIDC token

The Azure Identity SDK reads these automatically. In Python, Go, Java, or .NET, using DefaultAzureCredential will pick them up and authenticate without any code changes:

# Python example using azure-identity
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()  # Automatically uses workload identity
client = SecretClient(
    vault_url="https://my-vault.vault.azure.net",
    credential=credential
)
secret = client.get_secret("my-db-password")

Managed Identity for ACR access

AKS can use a managed identity to pull images from Azure Container Registry without any image pull secrets:

# Attach an ACR to AKS using managed identity
az aks update \
  --resource-group my-aks-rg \
  --name my-cluster \
  --attach-acr my-container-registry

# Verify the role assignment was created
az role assignment list \
  --scope $(az acr show --name my-container-registry --query id -o tsv) \
  --query "[?roleDefinitionName=='AcrPull']"

After attachment, pods can reference images from the ACR without any imagePullSecrets in the pod spec. The node kubelet uses the cluster managed identity to authenticate with ACR.

Accessing Key Vault secrets via CSI driver

The Secrets Store CSI driver mounts Azure Key Vault secrets directly into pods as files or environment variables:

# Enable the CSI driver add-on
az aks enable-addons \
  --addons azure-keyvault-secrets-provider \
  --resource-group my-aks-rg \
  --name my-cluster
# SecretProviderClass linking to Key Vault
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-kv-provider
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    clientID: "<managed-identity-client-id>"
    keyvaultName: "my-vault"
    tenantID: "<your-tenant-id>"
    objects: |
      array:
        - |
          objectName: db-password
          objectType: secret
          objectVersion: ""
---
# Pod mounting the secret
spec:
  serviceAccountName: my-app-sa
  containers:
  - name: app
    image: myapp:1.0
    volumeMounts:
    - name: secrets-store
      mountPath: "/mnt/secrets"
      readOnly: true
  volumes:
  - name: secrets-store
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: "azure-kv-provider"

The secret value appears at /mnt/secrets/db-password inside the container. When the secret rotates in Key Vault, the mounted value updates automatically.

Common mistakes

  1. Forgetting the azure.workload.identity/use: “true” pod label. The Workload Identity mutating webhook only injects environment variables into pods with this label. Without it, the pod does not get the token file and authentication fails.
  2. Using the cluster identity for application workloads. The cluster identity has broad permissions to manage Azure infrastructure. Application pods should use a separate, scoped managed identity with only the permissions the app needs.
  3. Not verifying the federated credential subject matches exactly. The subject string must exactly match system:serviceaccount:NAMESPACE:SA-NAME. A typo in the namespace or service account name means the federation never works.
  4. Granting Owner or Contributor roles to the pod identity. Pod identities should follow least privilege. A pod that reads Key Vault secrets only needs Key Vault Secrets User. Never grant broad subscription-level roles.
  5. Using AAD Pod Identity (deprecated) for new setups. AAD Pod Identity is deprecated and has known reliability issues. All new workloads should use the Workload Identity approach described on this page.

Frequently asked questions

What is the difference between a cluster managed identity and workload identity?

The cluster managed identity is used by AKS itself to manage Azure resources like load balancers and node VMs. Workload Identity is what your application pods use to call Azure APIs like Key Vault or Storage. They are separate identities with separate roles.

Can I give different pods different Azure permissions?

Yes, that is exactly the point of Workload Identity. Each service account can be federated with a different managed identity, so your frontend pod might have read-only access to Blob Storage while your backend pod has read/write access to Key Vault.

What is the older alternative to Workload Identity?

AAD Pod Identity was the previous approach. It worked by intercepting IMDS (instance metadata service) calls on each node. Microsoft has deprecated AAD Pod Identity in favor of the newer Workload Identity approach, which uses federated credentials and is more reliable.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.