AKS Workload Identity
This feature is an Enterprise feature. See our pricing plans or contact our sales team for more information.
Workload Identity allows Kubernetes workloads to securely access Azure resources without storing credentials in the cluster. It federates a Kubernetes Service Account with a Microsoft Entra identity, which improves security and simplifies identity management.
To configure Workload Identity in a tenant cluster, you must link the Kubernetes Service Account (KSA) used by your workload with a Microsoft Entra Workload ID. This KSA must exist in the control plane cluster where the tenant cluster runs.
Use the sync.toHost feature to expose the KSA to the control plane cluster.
Prerequisites​
Ensure you have the following:
kubectlinstalledazCLI installed and configured- A running AKS cluster with Workload Identity Federation enabled. For more information, see the Deploy and configure Workload Identity on an Azure Kubernetes Service (AKS) cluster documentation.
Integrate AKS Workload Identity with vCluster​
To integrate your workloads with Azure Kubernetes Service (AKS) Workload Identity, you must have a running instance of the vCluster Platform.
If you have not set one up yet, follow the vCluster Platform installation guide.
After deploying the platform, create a new access key to authenticate with the vCluster Platform API. This key is required to retrieve the synced Kubernetes Service Account (KSA) name and complete the Workload Identity setup.
Set up the necessary environment variables. These variables include information about your Azure resource group, vCluster details, and authentication keys.
Modify the following with your specific values to generate a copyable command:export RESOURCE_GROUP_NAME=vcluster-demo-rgexport SERVICE_ACCOUNT_NAME=vcluster-demoexport SERVICE_ACCOUNT_NAMESPACE=defaultexport VCLUSTER_NAME=vcluster-demoexport VCLUSTER_NAMESPACE=team-xexport AKS_CLUSTER_NAME=my-aks-clusterexport HOST=YOUR_PLATFORM_HOSTexport ACCESS_KEY=YOUR_ACCESS_KEYexport REGION=westeuropeexport FEDERATED_IDENTITY_CREDENTIAL_NAME=vcluster-demoexport USER_ASSIGNED_IDENTITY_NAME=vcluster-demo-workload-identityEnsure you specify the correct Azure Resource Group name, vCluster Platform host, and auth key.
Create a
vcluster.yamlfile that includes the following configuration:sync:toHost:serviceAccounts:enabled: trueRun the following command to deploy the tenant cluster:
vcluster create $VCLUSTER_NAME --namespace $VCLUSTER_NAMESPACE --values vcluster.yamlCreate a Kubernetes
ServiceAccountthat can be used to interact with the tenant cluster:cat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata:name: "${SERVICE_ACCOUNT_NAME}"namespace: "${SERVICE_ACCOUNT_NAMESPACE}"EOF- bash
- Terraform
Define a function to fetch the KSA name using curl and use it to export
KSA_NAMEenvironment variable.# Define the function to get the KSA name using curlget_ksa_name() {local vcluster_ksa_name=$1local vcluster_ksa_namespace=$2local vcluster_name=$3local host=$4local access_key=$5local resource_path="/kubernetes/management/apis/management.loft.sh/v1/translatevclusterresourcenames"local host_with_scheme=$([[ $host =~ ^(http|https):// ]] && echo "$host" || echo "https://$host")local sanitized_host="${host_with_scheme%/}"local full_url="${sanitized_host}${resource_path}"local response=$(curl -s -k -X POST "$full_url" \-H "Content-Type: application/json" \-H "Authorization: Bearer ${access_key}" \-d @- <<EOF{"spec": {"name": "${vcluster_ksa_name}","namespace": "${vcluster_ksa_namespace}","vclusterName": "${vcluster_name}"}}EOF)local status_name=$(echo "$response" | jq -r '.status.name')if [[ -z "$status_name" || "$status_name" == "null" ]]; thenecho "Error: Unable to fetch KSA name from response: $response"exit 1fiecho "$status_name"}# Get the KSA nameexport KSA_NAME=$(get_ksa_name "$SERVICE_ACCOUNT_NAME" "$SERVICE_ACCOUNT_NAMESPACE" "$VCLUSTER_NAME" "$HOST" "$ACCESS_KEY")We prepared a Terraform module that you can use in order to easily fetch updated resource name from Platform API.
module "synced_service_account_name" {source = "github.com/loft-sh/vcluster-terraform-modules//single-namespace-rename"providers = {http.default = http.default}host = var.vcluster_platform_hostaccess_key = var.access_keyresource_name = var.service_account_nameresource_namespace = var.service_account_namespacevcluster_name = var.vcluster_name}Create the federated identity credential between the managed identity, the service account issuer, and the subject.
First, create the managed identity and retrieve the required values:
export SUBSCRIPTION="$(az account show --query id --output tsv)"az identity create \--name "${USER_ASSIGNED_IDENTITY_NAME}" \--resource-group "${RESOURCE_GROUP_NAME}" \--location "${REGION}" \--subscription "${SUBSCRIPTION}"export AKS_OIDC_ISSUER="$(az aks show --name "${AKS_CLUSTER_NAME}" \--resource-group "${RESOURCE_GROUP_NAME}" \--query "oidcIssuerProfile.issuerUrl" \--output tsv)"export USER_ASSIGNED_CLIENT_ID="$(az identity show \--resource-group "${RESOURCE_GROUP_NAME}" \--name "${USER_ASSIGNED_IDENTITY_NAME}" \--query 'clientId' \--output tsv)"export TENANT_ID="$(az identity show \--resource-group "${RESOURCE_GROUP_NAME}" \--name "${USER_ASSIGNED_IDENTITY_NAME}" \--query 'tenantId' \--output tsv)"Then create the federated identity credential. The namespace parameter depends on your vCluster deployment type:
Namespace configuration- vCluster (not using the platform): Use the namespace where vCluster is deployed
- Platform-managed vCluster: The namespace follows the pattern
loft-<project-name>-v-<vcluster-name>
- vCluster without the Platform
- Platform-managed vCluster
For vCluster deployments not connected with the platform (i.e. deployed with
vcluster createor Helm without the platform):az identity federated-credential create \--name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \--identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \--resource-group "${RESOURCE_GROUP_NAME}" \--issuer "${AKS_OIDC_ISSUER}" \--subject system:serviceaccount:"${VCLUSTER_NAMESPACE}":"${KSA_NAME}" \--audience api://AzureADTokenExchangeFor vCluster deployments managed by the platform with a project:
# Set project name and construct the namespaceexport PROJECT_NAME="my-project" # Replace with your actual project nameexport PLATFORM_NAMESPACE="loft-${PROJECT_NAME}-v-${VCLUSTER_NAME}"az identity federated-credential create \--name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \--identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \--resource-group "${RESOURCE_GROUP_NAME}" \--issuer "${AKS_OIDC_ISSUER}" \--subject system:serviceaccount:"${PLATFORM_NAMESPACE}":"${KSA_NAME}" \--audience api://AzureADTokenExchangeAssign the necessary RBAC roles to the managed identity so it can access Azure resources. For this example, you'll assign the
Readerrole at the subscription level:# Get the principal ID of the managed identityexport PRINCIPAL_ID="$(az identity show \--resource-group "${RESOURCE_GROUP_NAME}" \--name "${USER_ASSIGNED_IDENTITY_NAME}" \--query 'principalId' \--output tsv)"# Assign Reader role to the managed identity at subscription levelaz role assignment create \--assignee "${PRINCIPAL_ID}" \--role "Reader" \--scope "/subscriptions/${SUBSCRIPTION}"# Wait for role assignment to propagateecho "Waiting for role assignment to propagate..."sleep 30# Verify the role assignmentaz role assignment list --assignee "${PRINCIPAL_ID}" --output tablenoteYou can assign more specific roles and scopes based on your requirements. For example, if you need the identity to manage specific resource groups or resources, assign appropriate roles such as "Contributor" or custom roles with specific permissions.
Annotate the service account with the user-assigned identity client ID and tenant ID:
kubectl annotate serviceaccount \"${SERVICE_ACCOUNT_NAME}" \-n "${SERVICE_ACCOUNT_NAMESPACE}" \azure.workload.identity/use="true" \azure.workload.identity/client-id="${USER_ASSIGNED_CLIENT_ID}" \azure.workload.identity/tenant-id="${TENANT_ID}"Deploy an application. Replace placeholders with your actual values:
cat <<EOF | kubectl apply -f -apiVersion: v1kind: Podmetadata:name: azure-cli-testlabels:app: azure-cli-testazure.workload.identity/use: "true"spec:serviceAccountName: "${SERVICE_ACCOUNT_NAME}"containers:- name: azure-cliimage: mcr.microsoft.com/azure-cli:latestcommand: ["/bin/bash", "-c", "--"]args:- |echo "Testing Azure CLI login";az login --service-principal -u \$AZURE_CLIENT_ID -t \$AZURE_TENANT_ID --federated-token \$(cat \$AZURE_FEDERATED_TOKEN_FILE);az account show;sleep 3600EOFVerify that the setup is complete and the pod is able to access Azure resources using the Workload Identity.
kubectl logs -l app=azure-cli-test -n default