Setting Up Microsoft Entra Workload ID for an Existing AKS Cluster (Through Federated Identity Credentials)

This guide provides exact, concise steps to configure Microsoft Entra Workload ID (Workload Identity Federation) for an existing Azure Kubernetes Service (AKS) cluster and pods running on it. It walks through enabling OIDC/workload identity on the cluster, creating a user-assigned managed identity (UAMI), creating a Kubernetes ServiceAccount annotated for workload identity, and creating the federated identity credential that binds them. Brief explanations are included for each step.

Prerequisites

  • Azure CLI v2.47.0 or later is installed and logged in; select the subscription as needed.
  • An existing AKS cluster with resource group and cluster name known.
  • kubectl configured (will be set via az aks get-credentials).
  • Sufficient permissions to manage AKS, managed identities, and federated credentials.

Workload identity relies on the cluster’s OIDC issuer and a Microsoft Entra trust (federated identity credential) to exchange tokens securely without node-managed secrets.

1) Set Working Variables

Using environment variables ensures consistent reuse across commands and prevents typo-related misconfigurations.

```bash
# Core context
export RESOURCE_GROUP="<your-aks-resource-group>"
export CLUSTER_NAME="<your-aks-name>"
export LOCATION="<azure-region>"   # e.g., eastus
export RANDOM_ID=$RANDOM           # for uniqueness in names
export SUBSCRIPTION_ID="<your-azure-subscription-id>"

# Managed identity
export USER_ASSIGNED_IDENTITY_NAME="uami-workload-$RANDOM_ID"

# Kubernetes service account
export SERVICE_ACCOUNT_NAMESPACE="default"
export SERVICE_ACCOUNT_NAME="workload-identity-sa-$RANDOM_ID"

# Federated identity credential
export FEDERATED_IDENTITY_CREDENTIAL_NAME="fic-$RANDOM_ID"
```

2) Enable OIDC Issuer and Workload Identity on the Existing AKS Cluster

The OIDC issuer URL is required so that Microsoft Entra can validate tokens from the cluster; the workload identity feature handles token projection and env injection for pods using annotated ServiceAccounts.

```bash
# Ensure OIDC issuer and workload identity are enabled on the AKS cluster
az aks update \
   --resource-group "${RESOURCE_GROUP}" \
   --name "${CLUSTER_NAME}" \
   --enable-oidc-issuer \
   --enable-workload-identity
```

Confirm and capture the cluster OIDC issuer:

```bash
export AKS_OIDC_ISSUER=$(az aks show \
   --resource-group "${RESOURCE_GROUP}" \
   --name "${CLUSTER_NAME}" \
   --query "oidcIssuerProfile.issuerUrl" -o tsv)
echo "AKS OIDC issuer: ${AKS_OIDC_ISSUER}"
```

3) Create a User-Assigned Managed Identity (UAMI) and Capture Its IDs

The UAMI is the identity that pods will assume via token exchange; it will be trusted using a federated credential and referenced by the ServiceAccount annotation.

```bash
# Create the user-assigned managed identity
az identity create \
   --name "${USER_ASSIGNED_IDENTITY_NAME}" \
   --resource-group "${RESOURCE_GROUP}" \
   --location "${LOCATION}"

# Capture its client ID (used in the ServiceAccount annotation)
export USER_ASSIGNED_CLIENT_ID=$(az identity show \
   --name "${USER_ASSIGNED_IDENTITY_NAME}" \
   --resource-group "${RESOURCE_GROUP}" \
   --query "clientId" -o tsv)

# Optional: capture resource ID and principalId if needed for RBAC to Azure resources
export USER_ASSIGNED_IDENTITY_ID=$(az identity show \
   --name "${USER_ASSIGNED_IDENTITY_NAME}" \
   --resource-group "${RESOURCE_GROUP}" \
   --query "id" -o tsv)
export USER_ASSIGNED_PRINCIPAL_ID=$(az identity show \
   --name "${USER_ASSIGNED_IDENTITY_NAME}" \
   --resource-group "${RESOURCE_GROUP}" \
   --query "principalId" -o tsv)
```

You’ll later grant this UAMI Azure RBAC against target services (e.g., Key Vault, Storage) separately from the identity setup.

4) Get Cluster Credentials and Create the Kubernetes ServiceAccount Annotated for Workload Identity

The ServiceAccount annotation azure.workload.identity/client-id tells the webhook which UAMI the pod should use. The subject used for the federated credential must match the system:serviceaccount:<namespace>:<name> value.

```bash
# Merge cluster kubeconfig locally
az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}"

# Create the annotated ServiceAccount
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
   name: "${SERVICE_ACCOUNT_NAME}"
   namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
   annotations:
      azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
EOF
```

5) Create the Federated Identity Credential on the UAMI

The federated identity credential binds the UAMI to trust tokens issued by the AKS OIDC issuer for a specific ServiceAccount subject. The standard audience for AKS is api://AzureADTokenExchange.

```bash
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"
```

Propagation of the federated credential can take a few seconds; immediate token requests might fail until caches refresh -— allow a brief delay before testing.

6) (Optional) Grant the UAMI access to target Azure resources

Workload identity only handles authentication. Authorization is granted by assigning roles on target resources to the UAMI’s principalId. For example, to allow read access to Azure Container Registry, grant the appropriate AcrPull to the UAMI principal.

Example pattern:

```bash
# Example: assign Reader on a resource group (replace scope and role as needed)
export ACR_NAME="<azure-container-registry-name>"
export SCOPE="/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.ContainerRegistry/registries/${ACR_NAME}"
az role assignment create \
   --assignee-object-id "${USER_ASSIGNED_PRINCIPAL_ID}" \
   --assignee-principal-type ServicePrincipal \
   --role "AcrPull" \
   --scope "${SCOPE}"
```

7) (Optional) Use the ServiceAccount in a pod/deployment

Any pod using this ServiceAccount will get a projected token and environment variables for Microsoft Entra token exchange. Ensure app SDKs use Azure Identity libraries that honor the environment/token file.

Example pod spec pattern (snippet):

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
   name: demo-wi-app
   labels:
      azure.workload.identity/use: "true"
spec:
   replicas: 1
   selector:
      matchLabels:
         app: demo-wi-app
   template:
      metadata:
         labels:
            app: demo-wi-app
            azure.workload.identity/use: "true"
      spec: 
         serviceAccountName: "${SERVICE_ACCOUNT_NAME}"
            containers:
            - name: app
              image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
```

Notes and Limits

  • Up to 20 federated identity credentials can be added per managed identity -- allow a short delay after adding a credential before first token request.
  • The subject must exactly match the ServiceAccount’s namespace and name, and the issuer must be the AKS cluster’s OIDC issuer URL.

Quick Verification Checklist

  • OIDC issuer is enabled and captured in AKS_OIDC_ISSUER.
  • UAMI exists; USER_ASSIGNED_CLIENT_ID is set.
  • ServiceAccount exists and is annotated with azure.workload.identity/client-id = USER_ASSIGNED_CLIENT_ID.
  • Federated identity credential is created with issuer = AKS_OIDC_ISSUER and subject = system:serviceaccount:<ns>:<sa>.
  • Optional: UAMI granted proper RBAC on target Azure resources.

Troubleshooting Tips

  • If tokens fail immediately after creating the federated credential, wait briefly and retry to allow propagation.
  • Ensure the ServiceAccount annotation uses the UAMI clientId, not principalId or objectId.
  • Validate the subject string matches the ServiceAccount exactly, including namespace.