That Kubernetes Secret named azure-client-secret has been sitting in your namespace for months, and nobody wants to be the person who admits they do not know where else that credential was copied.
You can rotate it, seal it, sync it, and wrap it in process. But if your pod still authenticates to Azure with a long-lived client secret, you are protecting a credential that should not exist in the first place. Microsoft Entra Workload Identity gives you a cleaner path: let AKS issue a short-lived Kubernetes service account token, let Microsoft Entra ID validate that token through OIDC federation, and let your workload receive an Azure access token without storing a static secret in the cluster.

What Workload Identity Changes
Microsoft Entra Workload Identity replaces the old pattern of putting app registration secrets, certificates, or pod identity plumbing inside your AKS runtime. Instead of asking a pod to hold Azure credentials, you bind a Kubernetes service account to a Microsoft Entra identity.
When the pod starts, the AKS workload identity webhook mutates only pods labeled with azure.workload.identity/use: "true". That mutation injects Azure-specific environment variables and a projected service account token volume. Your application, using Azure Identity client libraries or MSAL, exchanges that projected token for a Microsoft Entra access token.
Here is the practical mental model.
| Piece | What It Does | What You Control |
|---|---|---|
| AKS OIDC issuer | Publishes the cluster issuer URL and signing keys | Enabled on the AKS cluster |
| Kubernetes service account | Represents the workload inside Kubernetes | Namespace, name, and annotation |
| User-assigned managed identity | Represents the workload in Azure | Azure RBAC permissions |
| Federated identity credential | Trust rule between Entra ID and the service account | Issuer, subject, and audience |
| Pod label | Opts the workload into token injection | azure.workload.identity/use: "true" |
This matters for AKS security because the blast radius moves from “anyone who reads this secret can authenticate until it expires or gets rotated” to “only this service account in this namespace can exchange this projected token through a specific federation rule.” That is a much better default for non-human identity management.
Prerequisites
Before you start, make sure you have an AKS cluster you can change, permission to create or modify a user-assigned managed identity, and permission to grant that identity access to the Azure resource your workload needs.
You also need Azure CLI 2.47.0 or later. Microsoft’s AKS Workload Identity deployment documentation calls out that version requirement, and it is worth checking before you paste anything into a terminal.
az --version
For the examples below, set a few shell variables. Replace the values with your real resource group, cluster name, region, and subscription ID.
export RESOURCE_GROUP="rg-aks-workload-id-demo" export CLUSTER_NAME="aks-workload-id-demo" export LOCATION="eastus" export SUBSCRIPTION="00000000-0000-0000-0000-000000000000" export SERVICE_ACCOUNT_NAMESPACE="default" export SERVICE_ACCOUNT_NAME="workload-identity-sa" export USER_ASSIGNED_IDENTITY_NAME="aks-app-mi"
Do not skip the namespace decision. The federated credential subject includes the namespace and service account name, so changing either value later means the trust rule no longer matches.
Enable the AKS OIDC Issuer and Workload Identity
Your cluster must expose an OIDC issuer before Microsoft Entra ID can validate Kubernetes service account tokens. For an existing cluster, enable both the OIDC issuer and Workload Identity with az aks update.
az aks update \
--resource-group "${RESOURCE_GROUP}" \
--name "${CLUSTER_NAME}" \
--enable-oidc-issuer \
--enable-workload-identity
For a new cluster, Microsoft’s documented az aks create flow uses the same two flags: --enable-oidc-issuer and --enable-workload-identity. In production, you would fold those into your normal AKS build process rather than treating Workload Identity as a day-two add-on.
After enabling the issuer, capture the issuer URL. The federated identity credential will use this exact value.
export AKS_OIDC_ISSUER="$(az aks show --name "${CLUSTER_NAME}" \
--resource-group "${RESOURCE_GROUP}" \
--query "oidcIssuerProfile.issuerUrl" \
--output tsv)"
echo "${AKS_OIDC_ISSUER}"
The URL follows Microsoft’s documented AKS OIDC format, similar to https://{region}.oic.prod-aks.azure.com/{tenant_id}/{uuid}. Treat it as an identifier, not as a value you hand-type from memory. One wrong character and your token exchange fails while everything “looks” configured.

Create the Managed Identity
Next, create a user-assigned managed identity. This identity is what your pod will become when it accesses Azure resources.
az identity create \
--name "${USER_ASSIGNED_IDENTITY_NAME}" \
--resource-group "${RESOURCE_GROUP}" \
--location "${LOCATION}" \
--subscription "${SUBSCRIPTION}"
Capture the identity client ID. The Kubernetes service account annotation uses the client ID, not the object ID or resource ID.
export USER_ASSIGNED_CLIENT_ID="$(az identity show \
--name "${USER_ASSIGNED_IDENTITY_NAME}" \
--resource-group "${RESOURCE_GROUP}" \
--query "clientId" \
--output tsv)"
Now grant this identity the smallest Azure permission your workload needs. If the workload reads a Key Vault secret, grant a Key Vault role or access policy appropriate to your vault configuration. If it writes to a storage account, scope the assignment to that storage account instead of the subscription.
That scoping step is where Workload Identity either becomes a security improvement or just a new way to over-permission pods. The federation proves who the pod is; Azure RBAC still decides what that identity can do.
Bind the Kubernetes Service Account
Create a Kubernetes service account and annotate it with the managed identity client ID. This service account is the Kubernetes side of the binding.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
name: "${SERVICE_ACCOUNT_NAME}"
namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
EOF
At this point, the service account is annotated, but Microsoft Entra ID still does not trust it. The missing piece is the federated identity credential.
Create the Federated Identity Credential
The federated identity credential is the trust rule. It says: trust tokens from this AKS issuer, for this Kubernetes subject, with this audience, and exchange them for tokens as this managed identity.
export FEDERATED_IDENTITY_CREDENTIAL_NAME="aks-app-federated-credential"
az identity federated-credential create \
--name "${FEDERATED_IDENTITY_CREDENTIAL_NAME}" \
--identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \
--resource-group "${RESOURCE_GROUP}" \
--issuer "${AKS_OIDC_ISSUER}" \
--subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}" \
--audience api://AzureADTokenExchange
The subject format is not decorative. It must be system:serviceaccount:<namespace>:<service-account-name>. If your deployment uses prod-api in the payments namespace, that subject is different from prod-api in default.
Microsoft’s documentation also notes a propagation delay after adding a federated identity credential. If your first token request fails immediately after creation, wait briefly before tearing apart your manifest. Directory cache timing is a boring problem, but it can masquerade as a broken configuration.
One more guardrail: Microsoft documents api://AzureADTokenExchange as the default and recommended audience for Microsoft Entra Workload Identity. Do not change it unless you have a specific, documented reason.

Deploy a Pod That Uses the Identity
A pod must do two things to use the identity: run as the annotated service account and include the workload identity label.
apiVersion: v1
kind: Pod
metadata:
name: sample-workload-identity
namespace: default
labels:
azure.workload.identity/use: "true"
spec:
serviceAccountName: workload-identity-sa
containers:
- name: app
image: ghcr.io/azure/azure-workload-identity/msal-go
env:
- name: KEYVAULT_URL
value: "https://YOUR-KEYVAULT-NAME.vault.azure.net/"
- name: SECRET_NAME
value: "sample-secret"
nodeSelector:
kubernetes.io/os: linux
Apply your manifest after replacing the Key Vault values with your own.
kubectl apply -f pod.yaml
Then verify the pod starts and inspect logs for the application-level token or secret retrieval result.
kubectl get pod sample-workload-identity -n default kubectl logs sample-workload-identity -n default
If the pod starts but authentication fails, check the boring-but-deadly details first: the pod label, service account name, namespace, service account annotation, managed identity client ID, federated credential subject, and Azure RBAC scope. Most Workload Identity failures are not mysterious. They are mismatches.
Replace Static Secrets Without Breaking the App
Workload Identity does not automatically delete your old Kubernetes Secrets. You still need a Kubernetes secrets management cutover.
Deploy the identity path in parallel with the old secret path. Update one workload to use Azure Identity or MSAL, verify Azure access, then remove the old environment variables, secret mounts, and secret objects.
A safe migration checklist looks like this:
-
Confirm the app uses a supported Azure Identity library or MSAL flow.
-
Create the managed identity and grant only the resource permissions the app needs.
-
Bind one Kubernetes service account to one federated credential subject.
-
Add
azure.workload.identity/use: "true"to the pod template. -
Remove client secrets from Kubernetes manifests, Helm values, CI variables, and sealed-secret sources.
-
Rotate or revoke the old credential after the workload proves healthy without it.
If you are migrating from Azure AD pod identity, use Workload Identity as the destination pattern. Microsoft’s migration guidance describes running Workload Identity in parallel, verifying authentication transactions, and then removing the old pod-managed identity setup. That is the right instinct: prove the new identity path before you rip out the old one.
Troubleshooting the Usual Failure Modes
When Workload Identity fails, the error often appears inside your application logs as a token acquisition problem. Resist the urge to start by changing code. First verify the identity chain.
| Symptom | Check |
|---|---|
| Pod has no injected token volume or Azure env vars | Confirm azure.workload.identity/use: "true" is on the pod template metadata |
| Token exchange fails | Compare federated credential issuer and subject to the AKS issuer URL and service account |
| Azure access is denied | Check RBAC or Key Vault permissions on the managed identity |
| It works in one namespace but not another | Recreate or add a federated credential for the actual namespace/service account subject |
| First request fails right after setup | Wait for federated credential propagation and retry |
Also remember Microsoft’s documented limit: a managed identity can have a maximum of 20 federated identity credentials. That limit matters if you plan to map every namespace and service account combination to the same identity. In many environments, separate managed identities per application or trust boundary are cleaner anyway.
Conclusion
Microsoft Entra Workload Identity is not just a nicer AKS authentication feature. It is a practical way to remove static Azure credentials from your cluster and make workload access auditable, scoped, and easier to reason about.
The workflow is straightforward: enable the AKS OIDC issuer, create a user-assigned managed identity, annotate a Kubernetes service account, create the federated identity credential, and label the pod. The discipline is in the details. Keep the namespace and service account subject exact, scope Azure permissions tightly, and remove old secrets after the workload identity federation path proves it no longer needs them.
Do that, and your AKS workloads stop carrying passwords around like loose change. Good riddance.
Sources
-
https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet
-
https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster
-
https://learn.microsoft.com/en-us/azure/aks/workload-identity-migrate-from-pod-identity
-
https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation
-
https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access