Skip to main content
Version: v0.26 Stable

AKS Workload Identity

Enterprise-Only Feature

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 virtual 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 host cluster where the virtual cluster runs.

Use the sync.toHost feature to expose the KSA to the host cluster.

Prerequisites​

Ensure you have the following:

Integrate AKS Workload Identity with vCluster​

  1. 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.

  2. 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-rg
    export SERVICE_ACCOUNT_NAME=vcluster-demo
    export SERVICE_ACCOUNT_NAMESPACE=default
    export VCLUSTER_NAME=vcluster-demo
    export VCLUSTER_NAMESPACE=team-x
    export AKS_CLUSTER_NAME=my-aks-cluster
    export HOST=YOUR_PLATFORM_HOST
    export ACCESS_KEY=YOUR_ACCESS_KEY
    export REGION=westeurope
    export FEDERATED_IDENTITY_CREDENTIAL_NAME=vcluster-demo
    export USER_ASSIGNED_IDENTITY_NAME=vcluster-demo-workload-identity

    Ensure you specify the correct Azure Resource Group name, vCluster Platform host, and auth key.

  3. Create a vcluster.yaml file that includes the following configuration:

    sync:
    toHost:
    serviceAccounts:
    enabled: true
  4. Run the following command to deploy the virtual cluster:

     vcluster create $VCLUSTER_NAME --namespace $VCLUSTER_NAMESPACE --values vcluster.yaml
  5. Create a Kubernetes ServiceAccount that can be used to interact with the virtual cluster:

     cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: "${SERVICE_ACCOUNT_NAME}"
    namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
    EOF

    Define a function to fetch the KSA name using curl and use it to export KSA_NAME environment variable.

    # Define the function to get the KSA name using curl
    get_ksa_name() {
    local vcluster_ksa_name=$1
    local vcluster_ksa_namespace=$2
    local vcluster_name=$3
    local host=$4
    local access_key=$5

    local 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" ]]; then
    echo "Error: Unable to fetch KSA name from response: $response"
    exit 1
    fi
    echo "$status_name"
    }

    # Get the KSA name
    export KSA_NAME=$(get_ksa_name "$SERVICE_ACCOUNT_NAME" "$SERVICE_ACCOUNT_NAMESPACE" "$VCLUSTER_NAME" "$HOST" "$ACCESS_KEY")
  6. Create the federated identity credential between the managed identity, the service account issuer, and the subject:

    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)"

    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://AzureADTokenExchange
  7. Assign the necessary RBAC roles to the managed identity so it can access Azure resources. For this example, you'll assign the Reader role at the subscription level:

    # Get the principal ID of the managed identity
    export 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 level
    az role assignment create \
    --assignee "${PRINCIPAL_ID}" \
    --role "Reader" \
    --scope "/subscriptions/${SUBSCRIPTION}"

    # Wait for role assignment to propagate
    echo "Waiting for role assignment to propagate..."
    sleep 30

    # Verify the role assignment
    az role assignment list --assignee "${PRINCIPAL_ID}" --output table
    note

    You 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.

  8. 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}"
  9. Deploy an application. Replace placeholders with your actual values:

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
    name: azure-cli-test
    labels:
    app: azure-cli-test
    azure.workload.identity/use: "true"
    spec:
    serviceAccountName: "${SERVICE_ACCOUNT_NAME}"
    containers:
    - name: azure-cli
    image: mcr.microsoft.com/azure-cli:latest
    command: ["/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 3600
    EOF
  10. Verify 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