Implementing Workload Identity in AKS

Published:15 June 2026 - 5 min. read

Audit your Active Directory for weak passwords and risky accounts. Run your free Specops scan now!

Static credentials are easy to create and hard to defend. One secret lands in a Kubernetes manifest, another gets copied into a pipeline variable, and before long nobody can tell which workload still needs which password.

Azure Kubernetes Service (AKS) has a better pattern: Microsoft Entra Workload ID. Instead of storing long-lived client secrets in pods, a Kubernetes service account can exchange a projected token for a Microsoft Entra access token. The workload gets the Azure access it needs, and you stop treating Kubernetes Secrets like a password vault.

In this tutorial, you’ll configure workload identity for an AKS workload, connect it to a user-assigned managed identity, and verify the pod can access Azure without a static secret.

Prerequisites

To follow along, you’ll need:

  • An Azure subscription where you can create or modify an AKS cluster.
  • Azure CLI installed and authenticated with az login.
  • kubectl configured for your AKS cluster.
  • Permission to create managed identities and federated identity credentials.
  • An AKS cluster that supports workload identity.

Related: Microsoft Entra Workload ID for AKS

Why Workload Identity Matters

A Kubernetes pod often needs to talk to Azure services. It might read a secret from Key Vault, write to a Storage account, or query a database. The old shortcut was to put a client ID and client secret somewhere the pod could read.

That shortcut creates three problems:

  • Secrets are copied into too many places.
  • Rotation becomes an outage risk.
  • Incident response gets harder because nobody knows which pod used which credential.

Workload identity changes the trust model. The pod proves who it is through its Kubernetes service account. Microsoft Entra trusts that service account through a federated identity credential. Azure returns a token without the workload ever storing a password.

Old pattern Workload identity pattern
Store client secret in Kubernetes Project a short-lived service account token
Rotate secret manually Let token exchange handle credential freshness
Hard to map secret to workload Bind identity to namespace and service account
Secret leak can persist Trust is scoped and revocable

The goal is not just fewer secrets. The goal is a clearer identity boundary.

Enable Workload Identity on AKS

If you are creating a new cluster, enable workload identity and the OIDC issuer during cluster creation.

az aks create \
  --resource-group rg-aks-demo \
  --name aks-workload-demo \
  --enable-oidc-issuer \
  --enable-workload-identity \
  --generate-ssh-keys

For an existing cluster, enable both features.

az aks update \
  --resource-group rg-aks-demo \
  --name aks-workload-demo \
  --enable-oidc-issuer \
  --enable-workload-identity

Then retrieve the OIDC issuer URL. You’ll need this URL when creating the federated identity credential.

AKS_OIDC_ISSUER=$(az aks show \
  --resource-group rg-aks-demo \
  --name aks-workload-demo \
  --query oidcIssuerProfile.issuerUrl \
  --output tsv)

echo $AKS_OIDC_ISSUER

If this command returns an empty value, stop. The issuer is not enabled correctly, and the token exchange will not work.

Create a User-Assigned Managed Identity

Next, create the Azure identity your pod will use.

az identity create \
  --resource-group rg-aks-demo \
  --name id-aks-reader

Capture the identity values.

IDENTITY_CLIENT_ID=$(az identity show \
  --resource-group rg-aks-demo \
  --name id-aks-reader \
  --query clientId \
  --output tsv)

IDENTITY_PRINCIPAL_ID=$(az identity show \
  --resource-group rg-aks-demo \
  --name id-aks-reader \
  --query principalId \
  --output tsv)

echo $IDENTITY_CLIENT_ID

Assign the identity the smallest role it needs. For a quick read-only test, scope the role to a resource group.

SUBSCRIPTION_ID=$(az account show --query id --output tsv)

az role assignment create \
  --assignee $IDENTITY_PRINCIPAL_ID \
  --role Reader \
  --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-aks-demo

Do not assign Contributor just because it makes the demo easier. Workload identity removes static secrets, but it does not fix over-permissioned roles.

Create the Kubernetes Service Account

A workload identity binding starts in Kubernetes with a service account. Create a namespace and service account annotated with the managed identity client ID.

apiVersion: v1
kind: Namespace
metadata:
  name: workload-demo
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: workload-reader
  namespace: workload-demo
  annotations:
    azure.workload.identity/client-id: "REPLACE_WITH_CLIENT_ID"

Save the file as service-account.yaml, replace REPLACE_WITH_CLIENT_ID, and apply it.

kubectl apply -f service-account.yaml

The service account name and namespace matter. Microsoft Entra will trust this exact subject string later:

system:serviceaccount:workload-demo:workload-reader

If the namespace or service account name changes, the federated credential must change too.

Create the Federated Identity Credential

The federated identity credential connects Microsoft Entra to the Kubernetes service account.

az identity federated-credential create \
  --resource-group rg-aks-demo \
  --identity-name id-aks-reader \
  --name fic-workload-reader \
  --issuer $AKS_OIDC_ISSUER \
  --subject system:serviceaccount:workload-demo:workload-reader \
  --audience api://AzureADTokenExchange

This command says: tokens issued by this AKS OIDC issuer for this Kubernetes service account can be exchanged for tokens as this managed identity.

That is the core trust relationship. Everything else is just getting the pod to use it.

Related: Configure workload identity federation

Deploy a Pod That Uses the Identity

Now create a test pod. The important parts are the service account and the workload identity label.

apiVersion: v1
kind: Pod
metadata:
  name: azure-cli-test
  namespace: workload-demo
  labels:
    azure.workload.identity/use: "true"
spec:
  serviceAccountName: workload-reader
  containers:
    - name: azure-cli
      image: mcr.microsoft.com/azure-cli:latest
      command: ["/bin/sh", "-c"]
      args:
        - sleep 3600

Apply the pod.

kubectl apply -f pod.yaml
kubectl wait --for=condition=Ready pod/azure-cli-test -n workload-demo --timeout=120s

The label tells the workload identity webhook to inject the environment variables and token file the Azure SDK and CLI expect.

Verify the Pod Can Use Azure Without a Secret

Exec into the pod and sign in with the managed identity.

kubectl exec -n workload-demo -it azure-cli-test -- /bin/sh

Inside the container, run:

az login --federated-token "$(cat $AZURE_FEDERATED_TOKEN_FILE)" \
  --service-principal \
  --username $AZURE_CLIENT_ID \
  --tenant $AZURE_TENANT_ID

az group show --name rg-aks-demo --query name --output tsv

If the role assignment is correct, the command returns the resource group name. No client secret was mounted. No password was copied into a Kubernetes Secret. The workload authenticated through the federated service account token.

Troubleshoot Common Failures

Workload identity has a few predictable failure modes. Use the table below before changing random pieces.

Symptom Likely cause Fix
AZURE_FEDERATED_TOKEN_FILE is missing Pod label or service account binding is missing Add azure.workload.identity/use: "true" and confirm serviceAccountName
Token exchange fails Federated credential subject does not match Check namespace and service account name exactly
Azure command returns authorization error Identity authenticated but lacks permissions Fix the Azure RBAC role assignment
OIDC issuer is blank Cluster was not enabled correctly Enable OIDC issuer and workload identity on the AKS cluster
Works in one namespace only Federated credential is scoped to one service account Create another credential or use the correct service account

A good troubleshooting order is:

kubectl get pod azure-cli-test -n workload-demo -o yaml
kubectl get serviceaccount workload-reader -n workload-demo -o yaml
az identity federated-credential list \
  --resource-group rg-aks-demo \
  --identity-name id-aks-reader
az role assignment list --assignee $IDENTITY_PRINCIPAL_ID --all

If the Kubernetes objects look right, check the federated credential. If the credential looks right, check Azure RBAC.

Replace Static Secrets Gradually

Do not migrate every workload in one sprint. Start with one pod that reads one Azure service. Prove the identity path, then repeat the pattern.

For each workload, document:

  • Namespace and service account name.
  • Managed identity name.
  • Federated identity credential name.
  • Azure role assignment and scope.
  • Azure service the workload accesses.

That list becomes your ownership map. It also makes incident response easier because you can revoke one workload’s trust without hunting for leaked secrets.

Clean Up the Demo

When you are done testing, remove the demo resources.

kubectl delete namespace workload-demo

az identity delete \
  --resource-group rg-aks-demo \
  --name id-aks-reader

If you created the AKS cluster just for this tutorial, remove the resource group.

az group delete --name rg-aks-demo --yes --no-wait

Stop Shipping Passwords in Pods

Workload identity is not just another AKS feature to toggle on. It is a better boundary between Kubernetes workloads and Azure resources.

The pattern is simple: enable the OIDC issuer, create a managed identity, bind it to a Kubernetes service account with a federated identity credential, and assign the identity only the Azure permissions it needs. After that, the pod can get Azure tokens without carrying a static client secret.

Start with one workload. Remove one stored secret. Prove the token exchange. Then repeat until static credentials become the exception instead of the default.

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!