Kubernetes workloads can now assume cloud provider identities without needing service account tokens or secrets, which is a massive security win.
Let’s say you have an Azure Kubernetes Service (AKS) cluster and you want a pod running in that cluster to access Azure Key Vault. Traditionally, you’d have a few options, none of them ideal:
- Kubernetes Secrets: Create a Kubernetes Secret containing the Azure AD application credentials (client ID and client secret) for a service principal. Mount this secret as a file into the pod. This means the secret is stored in etcd and potentially exposed if the pod is compromised.
- Managed Identity (older way): Configure your AKS cluster to use a user-assigned managed identity, and then assign that identity to pods that need to access Azure resources. This is better, but still involves some cluster-level configuration and doesn’t give granular identity per pod.
Workload Identity Federation changes this. It allows your Kubernetes pods to directly impersonate an Azure AD service principal (or even a user identity) without ever needing to manage Azure AD credentials within Kubernetes. The core idea is that Kubernetes (specifically, the service account used by the pod) issues a signed token that Azure AD trusts, and Azure AD then issues a short-lived Azure AD token in exchange.
Here’s how it works in practice with AKS:
First, you need to set up the Azure AD side.
-
Create an Azure AD Application Registration: This represents the identity your Kubernetes workload will impersonate.
# Log in to Azure CLI az login # Create a new Azure AD application registration az ad app create --display-name aks-workload-identity-app --null-id APP_ID=$(az ad app list --display-name aks-workload-identity-app --query "[].appId" -o tsv) echo "Application (client) ID: $APP_ID" # Create a client secret for the application (this is what the pod will exchange its K8s token for) az ad app credential reset --id $APP_ID --append --display-name aks-workload-identity-app-secret CLIENT_SECRET=$(az ad app credential list --id $APP_ID --query "[].secretText" -o tsv) echo "Client Secret: $CLIENT_SECRET" # Create a service principal for the application az ad sp create --id $APP_ID SP_ID=$(az ad sp list --display-name aks-workload-identity-app --query "[].id" -o tsv) echo "Service Principal ID: $SP_ID"You’ll get an
APP_IDand aCLIENT_SECRET. TheAPP_IDis what your pod will request a token for. TheCLIENT_SECRETis what the pod will use to exchange its Kubernetes token for an Azure AD token. This is a crucial distinction. -
Grant Permissions to the Service Principal: Now, grant this service principal the necessary permissions to the Azure resource it needs to access. For example, to access Key Vault:
# Get your subscription ID SUBSCRIPTION_ID=$(az account show --query id -o tsv) # Get your resource group name (replace with your actual resource group) RESOURCE_GROUP_NAME="my-aks-rg" # Get Key Vault name (replace with your actual Key Vault name) KEY_VAULT_NAME="my-aks-kv" # Assign "Key Vault Secrets Officer" role to the service principal az role assignment create --role "Key Vault Secrets Officer" --assignee $SP_ID --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME/providers/Microsoft.KeyVault/vaults/$KEY_VAULT_NAME"
Next, you need to configure AKS to enable Workload Identity and link your Kubernetes Service Account to the Azure AD application.
-
Enable Workload Identity on AKS:
# Get your AKS cluster name and resource group AKS_CLUSTER_NAME="my-aks-cluster" AKS_RESOURCE_GROUP="my-aks-rg" # Same RG as Key Vault, or wherever your AKS is # Enable Workload Identity on the AKS cluster az aks update --name $AKS_CLUSTER_NAME --resource-group $AKS_RESOURCE_GROUP --enable-workload-identityThis command installs the necessary components into your cluster.
-
Create a Kubernetes Service Account: This is the identity your pod will use.
# Create a Kubernetes namespace if it doesn't exist kubectl create namespace my-app-ns # Create a Kubernetes Service Account kubectl create serviceaccount my-app-sa -n my-app-ns -
Create a Workload Identity Binding: This is the key piece that links the Kubernetes Service Account to the Azure AD Application.
# Get the Azure AD Application (client) ID APP_ID="<your-app-id>" # From step 1 # Get the Azure AD Tenant ID TENANT_ID=$(az account show --query tenantId -o tsv) # Create the Workload Identity binding cat <<EOF | kubectl apply -f - apiVersion: aadpodidentity.k8s.io/v1 kind: WorkloadIdentity metadata: name: aks-workload-identity-binding namespace: my-app-ns spec: serviceAccount: name: my-app-sa namespace: my-app-ns azureIdentity: type: application resourceID: "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${AKS_RESOURCE_GROUP}/providers/Microsoft.AAD/applications/${APP_ID}" # This is the Azure AD Application ID type: servicePrincipal # This is important for App Registration identities EOFCorrection: The
WorkloadIdentityCRD is deprecated. You should useAzureIdentityandAzureIdentityBindinginstead. Let’s correct that.Corrected Step 5: First, create an
AzureIdentityresource that references your Azure AD application.# Get the Azure AD Application (client) ID APP_ID="<your-app-id>" # From step 1 # Get the Azure AD Tenant ID TENANT_ID=$(az account show --query tenantId -o tsv) # Create the AzureIdentity resource cat <<EOF | kubectl apply -f - apiVersion: aadpodidentity.k8s.io/v1 kind: AzureIdentity metadata: name: my-azure-identity namespace: my-app-ns spec: type: application resourceID: "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${AKS_RESOURCE_GROUP}/providers/Microsoft.AAD/applications/${APP_ID}" clientID: "${APP_ID}" tenantID: "${TENANT_ID}" EOFThen, create an
AzureIdentityBindingto link theAzureIdentityto the KubernetesServiceAccount.# Create the AzureIdentityBinding resource cat <<EOF | kubectl apply -f - apiVersion: aadpodidentity.k8s.io/v1 kind: AzureIdentityBinding metadata: name: my-azure-identity-binding namespace: my-app-ns spec: azureIdentity: name: my-azure-identity namespace: my-app-ns serviceAccount: name: my-app-sa namespace: my-app-ns EOF
Now, configure your pod to use this Service Account.
- Deploy your Pod: In your pod’s YAML, specify the
serviceAccountNameto be the one you created.
Apply this YAML:apiVersion: v1 kind: Pod metadata: name: my-app-pod namespace: my-app-ns spec: serviceAccountName: my-app-sa # This is the K8s Service Account containers: - name: app-container image: mcr.microsoft.com/azuredocs/azure-cli:latest # Example image that can run Azure CLI commands command: ["/bin/sh", "-c", "sleep infinity"]kubectl apply -f your-pod.yaml
The magic happens when your application code, running inside the pod, uses an Azure SDK or the Azure CLI. The Azure SDKs (when configured correctly, typically by setting AZURE_FEDERATED_TOKEN_FILE and AZURE_CLIENT_ID environment variables) will automatically:
- Discover the Kubernetes Service Account it’s running as.
- Locate the
aadpodidentity.k8s.io/v1secrets containing the Kubernetes token (usually mounted at/var/run/secrets/tokens/) and the Azure ADclientIDandtenantID(from theAzureIdentityCR). - Exchange the Kubernetes Service Account token for an Azure AD token by making a POST request to the Azure AD token endpoint, proving its identity.
- Use the obtained Azure AD token to authenticate with Azure services.
The core mechanism is that the aad-pod-identity add-on (installed by az aks update --enable-workload-identity) injects a mutated AzureIdentity and AzureIdentityBinding into the pod’s service account, providing the necessary environment variables (AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE) and a volume mount for the Kubernetes token. The Azure SDK then uses these to perform the token exchange.
The most surprising part of this is how seamlessly the Azure SDKs abstract away the token exchange. You don’t write any code to call Azure AD directly; the SDK handles the https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token endpoint using the provided Kubernetes token as a client_assertion.
The next step after successfully federating identities is to manage secrets securely.