5 min read
Azure AD Workload Identity for AKS: The Future of Pod Identity
Azure AD Workload Identity for AKS: The Future of Pod Identity
Azure AD Workload Identity is the next evolution of pod identity in AKS. It uses Kubernetes native features (service account tokens) and Azure AD federated credentials to provide a more secure and flexible authentication mechanism.
Why Workload Identity?
Compared to AAD Pod Identity, Workload Identity offers:
- No node-level dependencies (no NMI DaemonSet)
- Works with Windows containers
- Compatible with Azure Arc-enabled Kubernetes
- Smaller attack surface
- Faster pod startup times
- Support for custom clouds
How It Works
- Kubernetes issues a service account token to the pod
- The application exchanges the token for an Azure AD token using OIDC federation
- Azure AD validates the token against the AKS OIDC issuer
- Application receives an Azure AD access token
Prerequisites
Enable OIDC issuer on your AKS cluster:
# Update existing cluster
az aks update \
--resource-group myResourceGroup \
--name myAKSCluster \
--enable-oidc-issuer
# Get the OIDC issuer URL
OIDC_ISSUER=$(az aks show \
--resource-group myResourceGroup \
--name myAKSCluster \
--query oidcIssuerProfile.issuerUrl -o tsv)
echo $OIDC_ISSUER
Creating a Managed Identity with Federation
# Create a user-assigned managed identity
az identity create \
--resource-group myResourceGroup \
--name workload-identity-demo
# Get the identity details
IDENTITY_CLIENT_ID=$(az identity show \
--resource-group myResourceGroup \
--name workload-identity-demo \
--query clientId -o tsv)
IDENTITY_TENANT_ID=$(az identity show \
--resource-group myResourceGroup \
--name workload-identity-demo \
--query tenantId -o tsv)
Establishing Federated Credential
Link the Kubernetes service account to the Azure identity:
# Create federated credential
az identity federated-credential create \
--name my-app-federated \
--identity-name workload-identity-demo \
--resource-group myResourceGroup \
--issuer $OIDC_ISSUER \
--subject system:serviceaccount:default:my-app-sa \
--audience api://AzureADTokenExchange
Creating the Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default
annotations:
azure.workload.identity/client-id: "{client-id-guid}"
labels:
azure.workload.identity/use: "true"
Installing the Workload Identity Webhook
# Add Helm repo
helm repo add azure-workload-identity https://azure.github.io/azure-workload-identity/charts
helm repo update
# Install the webhook
helm install workload-identity-webhook azure-workload-identity/workload-identity-webhook \
--namespace azure-workload-identity-system \
--create-namespace \
--set azureTenantID="${IDENTITY_TENANT_ID}"
Deploying a Pod with Workload Identity
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
azure.workload.identity/use: "true"
spec:
serviceAccountName: my-app-sa
containers:
- name: app
image: myregistry.azurecr.io/my-app:v1
env:
- name: AZURE_TENANT_ID
value: "{tenant-id}"
- name: AZURE_CLIENT_ID
value: "{client-id}"
The webhook automatically injects:
AZURE_CLIENT_IDenvironment variableAZURE_TENANT_IDenvironment variableAZURE_FEDERATED_TOKEN_FILEpointing to the projected token- A volume with the service account token
Using Workload Identity in Code
Python Example
from azure.identity import WorkloadIdentityCredential
from azure.keyvault.secrets import SecretClient
# Automatically uses AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_FEDERATED_TOKEN_FILE
credential = WorkloadIdentityCredential()
client = SecretClient(
vault_url="https://mykeyvault.vault.azure.net",
credential=credential
)
secret = client.get_secret("my-secret")
print(f"Retrieved secret: {secret.name}")
.NET Example
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
// Automatically uses environment variables set by the webhook
var credential = new WorkloadIdentityCredential();
var client = new SecretClient(
new Uri("https://mykeyvault.vault.azure.net"),
credential
);
KeyVaultSecret secret = await client.GetSecretAsync("my-secret");
Console.WriteLine($"Retrieved secret: {secret.Name}");
Go Example
package main
import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
)
func main() {
cred, err := azidentity.NewWorkloadIdentityCredential(nil)
if err != nil {
panic(err)
}
client, err := azsecrets.NewClient(
"https://mykeyvault.vault.azure.net",
cred,
nil,
)
if err != nil {
panic(err)
}
resp, err := client.GetSecret(context.Background(), "my-secret", "", nil)
if err != nil {
panic(err)
}
fmt.Printf("Secret value: %s\n", *resp.Value)
}
Assigning Azure Permissions
# Grant Key Vault access
az keyvault set-policy \
--name myKeyVault \
--spn $IDENTITY_CLIENT_ID \
--secret-permissions get list
# Grant Storage access
az role assignment create \
--role "Storage Blob Data Reader" \
--assignee $IDENTITY_CLIENT_ID \
--scope /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account}
# Grant SQL access
az sql server ad-admin create \
--resource-group myResourceGroup \
--server myserver \
--display-name "Workload Identity" \
--object-id $(az identity show -g myResourceGroup -n workload-identity-demo --query principalId -o tsv)
Terraform Configuration
resource "azurerm_user_assigned_identity" "workload" {
name = "workload-identity-demo"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
}
resource "azurerm_federated_identity_credential" "workload" {
name = "my-app-federated"
resource_group_name = azurerm_resource_group.main.name
parent_id = azurerm_user_assigned_identity.workload.id
audience = ["api://AzureADTokenExchange"]
issuer = azurerm_kubernetes_cluster.main.oidc_issuer_url
subject = "system:serviceaccount:default:my-app-sa"
}
resource "kubernetes_service_account" "my_app" {
metadata {
name = "my-app-sa"
namespace = "default"
annotations = {
"azure.workload.identity/client-id" = azurerm_user_assigned_identity.workload.client_id
}
labels = {
"azure.workload.identity/use" = "true"
}
}
}
Debugging
Check Token Projection
kubectl exec -it my-app-pod -- cat /var/run/secrets/azure/tokens/azure-identity-token
Verify Environment Variables
kubectl exec -it my-app-pod -- env | grep AZURE
Test Authentication
# Inside the pod
curl -H "Authorization: Bearer $(cat /var/run/secrets/azure/tokens/azure-identity-token)" \
"https://management.azure.com/subscriptions?api-version=2020-01-01"
Migration from Pod Identity
- Create federated credentials for existing identities
- Update service accounts with workload identity annotations
- Update applications to use WorkloadIdentityCredential
- Test thoroughly
- Remove AAD Pod Identity components
Conclusion
Azure AD Workload Identity represents the future of identity management in AKS. Its use of standard Kubernetes features and Azure AD federation provides a more secure, portable, and efficient solution compared to traditional pod identity.
Tomorrow, we’ll explore Container Insights for comprehensive AKS monitoring.