Skip to main content

Workload Identity

Workloads deployed on an Azure Kubernetes Services (AKS) cluster require Microsoft Entra application credentials or managed identities to access Microsoft Entra protected resources, such as Azure Key Vault and Microsoft Graph. Microsoft Entra Workload ID integrates with the capabilities native to Kubernetes to federate with external identity providers.

In this lab, you will learn how to:

  • Enable Workload Identity on an AKS cluster
  • Create a managed identity
  • Create a Kubernetes service account
  • Create a federated identity credential
  • Deploy a sample application utilizing Workload Identity
  • Access secrets in Azure Key Vault with Workload Identity

Prerequisites

Before you begin, you will need an Azure subscription with Owner permissions and a GitHub account.

In addition, you will need the following tools installed on your local machine:

Setup Azure CLI

Start by logging into Azure by run the following command and follow the prompts:

az login --use-device-code
tip

You can log into a different tenant by passing in the --tenant flag to specify your tenant domain or tenant ID.

Run the following command to register preview features.

az extension add --name aks-preview

Setup Resource Group

In this workshop, we will set environment variables for the resource group name and location.

Important

The following commands will set the environment variables for your current terminal session. If you close the current terminal session, you will need to set the environment variables again.

To keep the resource names unique, we will use a random number as a suffix for the resource names. This will also help you to avoid naming conflicts with other resources in your Azure subscription.

Run the following command to generate a random number.

RAND=$RANDOM
export RAND
echo "Random resource identifier will be: ${RAND}"

Set the location to a region of your choice. For example, eastus or westeurope but you should make sure this region supports availability zones.

export LOCATION=eastus

Create a resource group name using the random number.

export RG_NAME=myresourcegroup$RAND
tip

You can list the regions that support availability zones with the following command:

az account list-locations \
--query "[?metadata.regionType=='Physical' && metadata.supportsAvailabilityZones==true].{Region:name}" \
--output table

Run the following command to create a resource group using the environment variables you just created.

az group create \
--name ${RG_NAME} \
--location ${LOCATION}

Setup AKS Cluster

Set the AKS cluster name.

export AKS_NAME=myakscluster$RAND

Run the following command to create an AKS cluster with some best practices in place.

az aks create \
--resource-group ${RG_NAME} \
--name ${AKS_NAME} \
--location ${LOCATION} \
--network-plugin azure \
--network-plugin-mode overlay \
--network-dataplane cilium \
--network-policy cilium \
--enable-managed-identity \
--enable-workload-identity \
--enable-oidc-issuer \
--generate-ssh-keys
tip

The AKS cluster created for this lab only includes a few best practices such as enabling Workload Identity and setting the cluster networking to Azure CNI powered by Cilium. For complete guidance on implementing AKS best practices be sure to check out the best practices and baseline architecture for an AKS cluster guides on Microsoft Learn.

Once the AKS cluster has been created, run the following command to connect to it.

az aks get-credentials \
--resource-group ${RG_NAME} \
--name ${AKS_NAME}

Setup Azure Key Vault

Set the Azure Key Vault name.

export AKV_NAME="mykeyvault$RAND"

Run the following command to create an key vault and export its resource identifier for later use.

export AKV_ID=$(az keyvault create \
--resource-group ${RG_NAME} \
--name ${AKV_NAME} \
--enable-rbac-authorization \
--query id \
--output tsv)

Once the AKS cluster and key vault has been deployed, you can proceed with the workshop.

Enable Workload Identity and OpenID Connect (OIDC) on an AKS cluster

Use the following command to check your AKS cluster to see if Workload Identity is already enabled.

az aks show \
--resource-group ${RG_NAME} \
--name ${AKS_NAME} \
--query "securityProfile.workloadIdentity.enabled" \
--output tsv

Use the following command to check if the OIDC issuer is enabled on your AKS cluster.

az aks show \
--resource-group ${RG_NAME} \
--name ${AKS_NAME} \
--query "oidcIssuerProfile.enabled" \
--output tsv

If you need to enable Workload Identity and/or the OIDC issuer, run the following command to enable them on your AKS cluster.

--name ${AKS_NAME} \
--enable-oidc-issuer \
--enable-workload-identity
note

This will can take several moments to complete.

Get the OIDC Issuer URL

After the cluster has been updated, run the following command to get the OIDC Issuer URL and save it in an environment variable.

export AKS_OIDC_ISSUER="$(az aks show \
--resource-group ${RG_NAME} \
--name ${AKS_NAME} \
--query "oidcIssuerProfile.issuerUrl" \
--output tsv)"

Create a Managed Identity

A Managed Identity is a account (identity) created in Microsoft Entra ID. These identities allows your application to leverage them to use when connecting to resources that support Microsoft Entra authentication. Applications can use managed identities to obtain Microsoft Entra tokens without having to manage any credentials.

Run the following command to save the name of the user-assigned managed identity to an environment variable.

export USER_ASSIGNED_IDENTITY_NAME="myIdentity"

Run the following command to create a Managed Identity.

az identity create \
--resource-group ${RG_NAME} \
--name ${USER_ASSIGNED_IDENTITY_NAME} \
--location ${LOCATION} \

You will need several properties of the managed identity for the next steps. Run the following commands to capture the details of the managed identity and save the values as environment variables.


export USER_ASSIGNED_CLIENT_ID="$(az identity show \
--resource-group ${RG_NAME} \
--name ${USER_ASSIGNED_IDENTITY_NAME} \
--query "clientId" \
--output tsv)"

export USER_ASSIGNED_PRINCIPAL_ID="$(az identity show \
--name "${USER_ASSIGNED_IDENTITY_NAME}" \
--resource-group ${RG_NAME} \
--query "principalId" \
--output tsv)"

Create a Kubernetes Service Account

Create a Kubernetes service account and annotate it with the client ID of the managed identity created in the previous step. This annotation is used to associate the managed identity with the service account.

Run the following command to save the name of the service account to an environment variable.

export SERVICE_ACCOUNT_NAME="workload-identity-sa"

The service account namespace should be the same as the namespace where your application pods will be deployed. Run the following command to save the name of the service account namespace to an environment variable. In this example, we are using the default namespace, but you can change it to any namespace you want.

export SERVICE_ACCOUNT_NAMESPACE="default"

Run the following command to create the service account and annotate it with the client ID of the managed identity.

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
azure.workload.identity/client-id: ${USER_ASSIGNED_CLIENT_ID}
name: ${SERVICE_ACCOUNT_NAME}
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
EOF

Create the Federated Identity Credential

Run the following command to save the name of the federated identity credential to an environment variable.

export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity"

Run the following command to create the federated identity credential which creates a link between the managed identity, the service account issuer, and the subject. For more information about federated identity credentials in Microsoft Entra, see Overview of federated identity credentials in Microsoft Entra ID.

az identity federated-credential create \
--name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \
--identity-name ${USER_ASSIGNED_IDENTITY_NAME} \
--resource-group ${RG_NAME} \
--issuer ${AKS_OIDC_ISSUER} \
--subject "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}" \
--audience api://AzureADTokenExchange
note

It takes a few seconds for the federated identity credential to propagate after it is added. If a token request is made immediately after adding the federated identity credential, the request might fail until the cache is refreshed. To avoid this issue, you can add a slight delay after adding the federated identity credential.

Assign the Key Vault Secrets User role to the user-assigned managed identity that you created previously. This step gives the managed identity permission to read secrets from the key vault.

az role assignment create \
--assignee-object-id "${USER_ASSIGNED_PRINCIPAL_ID}" \
--role "Key Vault Secrets User" \
--scope "${AKV_ID}" \
--assignee-principal-type ServicePrincipal

Deploy a Sample Application Utilizing Workload Identity

When you deploy your application pods, the manifest should reference the service account created in the Create Kubernetes service account step. The manifest depicted here shows the reference to the service account in the pod template spec section.

Important

Ensure that the application pods using workload identity include the label azure.workload.identity/use: "true" in the pod template spec section. Otherwise the pods will fail after they are restarted.

Run the following command to deploy a sample application that uses workload identity.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: sample-workload-identity
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
labels:
azure.workload.identity/use: "true" # Required. Only pods with this label can use workload identity.
spec:
serviceAccountName: ${SERVICE_ACCOUNT_NAME}
containers:
- image: busybox
name: busybox
command: ["sh", "-c", "sleep 3600"]
EOF

Access Secrets in Azure Key Vault with Workload Identity

Run the following command to make sure the Azure account you are signed in on has the appropriate privileges to create secrets in an Azure Key Vault.

az role assignment create \
--assignee-object-id $(az ad signed-in-user show --query id -o tsv) \
--role "Key Vault Administrator" \
--scope "${AKV_ID}" \
--assignee-principal-type User

Next, run the following command to create a secret in the key vault.

az keyvault secret set \
--vault-name "${AKV_NAME}" \
--name "my-secret" \
--value "Hello\!"

Run the following command to deploy a pod that references the service account and key vault URL.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: sample-workload-identity-key-vault
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
labels:
azure.workload.identity/use: "true"
spec:
serviceAccountName: ${SERVICE_ACCOUNT_NAME}
containers:
- image: ghcr.io/azure/azure-workload-identity/msal-go
name: oidc
env:
- name: KEYVAULT_URL
value: $(az keyvault show -n ${AKV_NAME} -g ${RG_NAME} --query "properties.vaultUri" -o tsv)
- name: SECRET_NAME
value: my-secret
nodeSelector:
kubernetes.io/os: linux
EOF

To check whether all properties are injected properly, use the kubectl describe command:

kubectl describe pod sample-workload-identity-key-vault -n ${SERVICE_ACCOUNT_NAMESPACE} | grep "SECRET_NAME:"

To verify that pod is able to get a token and access the resource, use the kubectl logs command:

kubectl logs -n ${SERVICE_ACCOUNT_NAMESPACE} sample-workload-identity-key-vault

You have successfully deployed a sample application that utilizes Workload Identity to access a secret in Azure Key Vault.

Cleanup

To clean up the resources created in this lab, run the following command to delete the resource group and all its contents. If you want to use the resources again, you can skip this step.

az group delete \
--name ${RG_NAME} \
--yes \
--no-wait

This will delete the resource group and all its contents.