Exercise 5: Workload Identity

Task 1: Enable Workload Identity

  1. Use the Azure CLI to enable Workload Identity on your AKS cluster.

    az aks update -g $RESOURCE_GROUP -n $AKS_NAME  --enable-oidc-issuer --enable-workload-identity
    az aks update -g $RESOURCE_GROUP -n $AKS_NAME  --enable-oidc-issuer --enable-workload-identity

    This operation may take 5-10 minutes to complete.

Task 1: Create Identity Resources

  1. Create a managed identity for our application to use:

    $IDENTITY_NAME = "workload-identity-demo"
    az identity create -g $RESOURCE_GROUP -n $IDENTITY_NAME
    IDENTITY_NAME="workload-identity-demo"
    az identity create -g $RESOURCE_GROUP -n $IDENTITY_NAME
  2. Get the details of the managed identity:

    $IDENTITY_CLIENT_ID = (az identity show -g $RESOURCE_GROUP -n $IDENTITY_NAME --query clientId -o tsv)
    $IDENTITY_RESOURCE_ID = (az identity show -g $RESOURCE_GROUP -n $IDENTITY_NAME --query id -o tsv)
    IDENTITY_CLIENT_ID=$(az identity show -g $RESOURCE_GROUP -n $IDENTITY_NAME --query clientId -o tsv)
    IDENTITY_RESOURCE_ID=$(az identity show -g $RESOURCE_GROUP -n $IDENTITY_NAME --query id -o tsv)
  3. Create a namespace for our workload identity demo:

    kubectl create namespace workload-identity-demo
    kubectl create namespace workload-identity-demo
  4. Create a service account and annotate it with the managed identity’s client ID:

    $serviceAccountYaml = @"
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        azure.workload.identity/client-id: $IDENTITY_CLIENT_ID
      name: workload-identity-sa
      namespace: workload-identity-demo
    "@
    
    $serviceAccountYaml | kubectl apply -f -
    cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        azure.workload.identity/client-id: $IDENTITY_CLIENT_ID
      name: workload-identity-sa
      namespace: workload-identity-demo
    EOF
  5. Create a federated identity credential to establish trust between the managed identity and the service account:

    $AKS_OIDC_ISSUER = (az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)
    az identity federated-credential create `
      --name "kubernetes-federated-credential" `
      --identity-name $IDENTITY_NAME `
      --resource-group $RESOURCE_GROUP `
      --issuer $AKS_OIDC_ISSUER `
      --audience api://AzureADTokenExchange `
      --subject system:serviceaccount:workload-identity-demo:workload-identity-sa
    AKS_OIDC_ISSUER=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)
    az identity federated-credential create \
      --name "kubernetes-federated-credential" \
      --identity-name $IDENTITY_NAME \
      --resource-group $RESOURCE_GROUP \
      --issuer $AKS_OIDC_ISSUER \
      --audience api://AzureADTokenExchange \
      --subject system:serviceaccount:workload-identity-demo:workload-identity-sa

Task 2: Create a Storage Account

  1. Create an Azure Storage account to test the identity:

    $STORAGE_ACCOUNT_NAME = "widemostorage$((New-Guid).ToString().Substring(0,8))"
    az storage account create `
      --name $STORAGE_ACCOUNT_NAME `
      --resource-group $RESOURCE_GROUP `
      --location $LOCATION `
      --sku Standard_LRS
    STORAGE_ACCOUNT_NAME="widemostorage$(openssl rand -hex 4)"
    az storage account create \
      --name $STORAGE_ACCOUNT_NAME \
      --resource-group $RESOURCE_GROUP \
      --location $LOCATION \
      --sku Standard_LRS
  2. Assign the reader role to the managed identity for the storage account:

    $STORAGE_ACCOUNT_ID = (az storage account show --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --query id -o tsv)
    
    az role assignment create `
      --role "Reader" `
      --assignee $IDENTITY_CLIENT_ID `
      --scope $STORAGE_ACCOUNT_ID
    STORAGE_ACCOUNT_ID=$(az storage account show --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --query id -o tsv)
    
    az role assignment create \
      --role "Reader" \
      --assignee $IDENTITY_CLIENT_ID \
      --scope $STORAGE_ACCOUNT_ID

Task 3: Validate Workload Identity

  1. Deploy a pod that uses the workload identity to access the storage account, note the use of the azure.workload.identity/use label:

    $workloadIdentityPodYaml = @"
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        azure.workload.identity/use: "true" 
      name: workload-identity-test
      namespace: workload-identity-demo
    spec:
      serviceAccountName: workload-identity-sa
      containers:
      - image: k8sonazureworkshoppublic.azurecr.io/mcr.microsoft.com/azure-cli
        name: azure-cli
        command:
        - "/bin/bash"
        - "-c"
        - "az login --service-principal -u $IDENTITY_CLIENT_ID --tenant `$(printenv AZURE_TENANT_ID) --federated-token `$(cat  `$AZURE_FEDERATED_TOKEN_FILE) && echo 'Login successful' && sleep infinity"
      nodeSelector:
        kubernetes.io/os: linux
    "@
    
    $workloadIdentityPodYaml | kubectl apply -f -
    cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        azure.workload.identity/use: "true" 
      name: workload-identity-test
      namespace: workload-identity-demo
    spec:
      serviceAccountName: workload-identity-sa
      containers:
      - image: k8sonazureworkshoppublic.azurecr.io/mcr.microsoft.com/azure-cli
        name: azure-cli
        command:
        - "/bin/bash"
        - "-c"
        - "az login --service-principal -u $IDENTITY_CLIENT_ID --tenant \$(printenv AZURE_TENANT_ID) --federated-token \$(cat \$AZURE_FEDERATED_TOKEN_FILE) && echo 'Login successful' && sleep infinity"
      nodeSelector:
        kubernetes.io/os: linux
    EOF
  2. Wait for the pod to be running:

    kubectl wait --for=condition=Ready pod/workload-identity-test -n workload-identity-demo
    kubectl wait --for=condition=Ready pod/workload-identity-test -n workload-identity-demo
  3. Test the workload identity by accessing the storage account:

    # First check that the pod is still running
    kubectl get pod workload-identity-test -n workload-identity-demo
    
    # Create a container in the storage account
    kubectl exec -it workload-identity-test -n workload-identity-demo -- bash -c "az storage account list"
    # First check that the pod is still running
    kubectl get pod workload-identity-test -n workload-identity-demo
    
    # Create a container in the storage account
    kubectl exec -it workload-identity-test -n workload-identity-demo -- bash -c "az storage account list"

    This should get the details of the storage account without requiring you to provide credentials, demonstrating that the workload identity is working correctly.

Task 4: Clean Up

  1. Clean up the workload identity resources:

    kubectl delete namespace workload-identity-demo
    kubectl delete namespace workload-identity-demo
  2. Clean up the managed identity and storage account:

    az identity delete --name $IDENTITY_NAME --resource-group $RESOURCE_GROUP
    az storage account delete --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --yes
    az identity delete --name $IDENTITY_NAME --resource-group $RESOURCE_GROUP
    az storage account delete --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --yes