This guide provides a complete step-by-step process to configure the Azure Kubernetes Service (AKS) backup extension with Azure Workload Identity authentication from scratch.

Prerequisites

  • AKS cluster with OIDC issuer enabled
  • Azure Workload Identity enabled on the cluster
  • Storage account for backup storage
  • Azure Backup Vault with appropriate policies
  • Sufficient Azure permissions to create role assignments

Step 1: Verify AKS Cluster Prerequisites

1.1 Verify OIDC Issuer is Enabled

# Check if OIDC issuer is enabled and get the issuer URL
az aks show --name <aks-cluster-name> --resource-group <aks-resource-group> --query "oidcIssuerProfile.issuerUrl" -o tsv

If not enabled, enable it:

az aks update --name <aks-cluster-name> --resource-group <aks-resource-group> --enable-oidc-issuer

1.2 Verify Azure Workload Identity is Enabled

# Check if workload identity is enabled
az aks show --name <aks-cluster-name> --resource-group <aks-resource-group> --query "securityProfile.workloadIdentity.enabled" -o tsv

If not enabled, enable it:

az aks update --name <aks-cluster-name> --resource-group <aks-resource-group> --enable-workload-identity

Step 2: Prepare Storage Infrastructure

2.1 Create Storage Account (if not exists)

# Create storage account for backup storage
az storage account create \
  --name <storage-account-name> \
  --resource-group <storage-resource-group> \
  --location <location> \
  --sku Standard_LRS \
  --kind StorageV2

2.2 Create Backup Container

# Create container for AKS backups
az storage container create \
  --name aks-backups \
  --account-name <storage-account-name>

Step 3: Create Azure Backup Vault and Policy

3.1 Create Backup Vault

# Create backup vault
az dataprotection backup-vault create \
  --resource-group <backup-resource-group> \
  --vault-name <backup-vault-name> \
  --location <location> \
  --type SystemAssigned \
  --storage-settings '[{"type":"LocallyRedundant","datastore-type":"VaultStore"}]'

3.2 Create Backup Policy

# Create backup policy for AKS
az dataprotection backup-policy create \
  --resource-group <backup-resource-group> \
  --vault-name <backup-vault-name> \
  --name policy-aks \
  --policy policy-aks.json

Example policy-aks.json:

{
  "datasourceTypes": ["Microsoft.ContainerService/managedClusters"],
  "objectType": "BackupPolicy",
  "policyRules": [
    {
      "backupParameters": {
        "backupType": "Incremental",
        "objectType": "AzureBackupParams"
      },
      "dataStore": {
        "dataStoreType": "OperationalStore",
        "objectType": "DataStoreInfoBase"
      },
      "name": "BackupDaily",
      "objectType": "AzureBackupRule",
      "trigger": {
        "objectType": "ScheduleBasedTriggerContext",
        "schedule": {
          "repeatingTimeIntervals": ["R/2024-01-01T06:00:00+00:00/P1D"],
          "timeZone": "UTC"
        },
        "taggingCriteria": [
          {
            "criteria": null,
            "isDefault": true,
            "tagInfo": {
              "eTag": null,
              "id": "Default_",
              "tagName": "Default"
            },
            "taggingPriority": 99
          }
        ]
      }
    },
    {
      "isDefault": true,
      "lifecycles": [
        {
          "deleteAfter": {
            "duration": "P7D",
            "objectType": "AbsoluteDeleteOption"
          },
          "sourceDataStore": {
            "dataStoreType": "OperationalStore",
            "objectType": "DataStoreInfoBase"
          },
          "targetDataStoreCopySettings": []
        }
      ],
      "name": "Default",
      "objectType": "AzureRetentionRule"
    }
  ]
}

Step 4: Install AKS Backup Extension (Stable Release)

4.1 Install the Extension

# Install AKS backup extension with minimal configuration
az k8s-extension create \
  --name azure-aks-backup \
  --extension-type microsoft.dataprotection.kubernetes \
  --cluster-name <aks-cluster-name> \
  --cluster-type managedClusters \
  --resource-group <aks-resource-group> \
  --configuration-settings \
    "blobContainer=aks-backups" \
    "storageAccount=<storage-account-name>" \
    "storageAccountResourceGroup=<storage-resource-group>" \
    "storageAccountSubscriptionId=<subscription-id>"

Note: The extension will automatically add useAAD=true and storageAccountURI settings.

4.2 Wait for Extension Installation

# Monitor extension installation
az k8s-extension show \
  --name azure-aks-backup \
  --cluster-name <aks-cluster-name> \
  --cluster-type managedClusters \
  --resource-group <aks-resource-group> \
  --query "provisioningState" -o tsv

Wait until the status shows “Succeeded”.

Step 5: Identify Extension Managed Identity

5.1 Get Extension Details

# Get the extension managed identity details
az k8s-extension show \
  --name azure-aks-backup \
  --cluster-name <aks-cluster-name> \
  --cluster-type managedClusters \
  --resource-group <aks-resource-group> \
  --query "aksAssignedIdentity.principalId" -o tsv

5.2 Find the Managed Identity Client ID

# Get the managed identity client ID using the principal ID
PRINCIPAL_ID=$(az k8s-extension show \
  --name azure-aks-backup \
  --cluster-name <aks-cluster-name> \
  --cluster-type managedClusters \
  --resource-group <aks-resource-group> \
  --query "aksAssignedIdentity.principalId" -o tsv)

# Find the managed identity and get client ID
az identity list \
  --query "[?principalId=='$PRINCIPAL_ID'].{Name:name, ResourceGroup:resourceGroup, ClientId:clientId}" \
  --output table

Save the ClientId and ResourceGroup for the next steps.

Step 6: Configure Workload Identity for Backup Extension

6.1 Get Kubernetes Context

# Connect to your AKS cluster
az aks get-credentials --name <aks-cluster-name> --resource-group <aks-resource-group>

# Verify backup extension pods are running
kubectl get pods -n dataprotection-microsoft

6.2 Configure Service Account Annotations

# Set variables
MANAGED_IDENTITY_CLIENT_ID="<client-id-from-step-5>"

# Add workload identity annotations to service accounts
kubectl annotate serviceaccount -n dataprotection-microsoft \
  dataprotection-microsoft-controller-sa \
  azure.workload.identity/client-id=$MANAGED_IDENTITY_CLIENT_ID --overwrite

kubectl annotate serviceaccount -n dataprotection-microsoft \
  dataprotection-microsoft-kubernetes-agent-server \
  azure.workload.identity/client-id=$MANAGED_IDENTITY_CLIENT_ID --overwrite

kubectl annotate serviceaccount -n dataprotection-microsoft \
  dataprotection-microsoft-geneva-sa \
  azure.workload.identity/client-id=$MANAGED_IDENTITY_CLIENT_ID --overwrite

6.3 Configure Deployment Labels

# Add workload identity labels to deployments
kubectl patch deployment -n dataprotection-microsoft \
  dataprotection-microsoft-controller \
  -p '{"spec":{"template":{"metadata":{"labels":{"azure.workload.identity/use":"true"}}}}}'

kubectl patch deployment -n dataprotection-microsoft \
  dataprotection-microsoft-kubernetes-agent \
  -p '{"spec":{"template":{"metadata":{"labels":{"azure.workload.identity/use":"true"}}}}}'

kubectl patch deployment -n dataprotection-microsoft \
  dataprotection-microsoft-geneva-service \
  -p '{"spec":{"template":{"metadata":{"labels":{"azure.workload.identity/use":"true"}}}}}'

Step 7: Create Federated Identity Credentials

7.1 Get Required Information

# Get OIDC issuer URL
OIDC_ISSUER=$(az aks show \
  --name <aks-cluster-name> \
  --resource-group <aks-resource-group> \
  --query "oidcIssuerProfile.issuerUrl" -o tsv)

# Set variables
MANAGED_IDENTITY_NAME="<managed-identity-name-from-step-5>"
MANAGED_IDENTITY_RG="<managed-identity-resource-group-from-step-5>"

7.2 Create Federated Identity Credentials

# Create federated credential for controller service account
az identity federated-credential create \
  --name dataprotection-controller \
  --identity-name $MANAGED_IDENTITY_NAME \
  --resource-group $MANAGED_IDENTITY_RG \
  --issuer $OIDC_ISSUER \
  --subject system:serviceaccount:dataprotection-microsoft:dataprotection-microsoft-controller-sa \
  --audience api://AzureADTokenExchange

# Create federated credential for kubernetes agent service account
az identity federated-credential create \
  --name dataprotection-agent \
  --identity-name $MANAGED_IDENTITY_NAME \
  --resource-group $MANAGED_IDENTITY_RG \
  --issuer $OIDC_ISSUER \
  --subject system:serviceaccount:dataprotection-microsoft:dataprotection-microsoft-kubernetes-agent-server \
  --audience api://AzureADTokenExchange

# Create federated credential for geneva service account
az identity federated-credential create \
  --name dataprotection-geneva \
  --identity-name $MANAGED_IDENTITY_NAME \
  --resource-group $MANAGED_IDENTITY_RG \
  --issuer $OIDC_ISSUER \
  --subject system:serviceaccount:dataprotection-microsoft:dataprotection-microsoft-geneva-sa \
  --audience api://AzureADTokenExchange

Step 8: Grant Storage Permissions

8.1 Assign Storage Blob Data Contributor Role

# Grant Storage Blob Data Contributor role to the managed identity
az role assignment create \
  --assignee $MANAGED_IDENTITY_CLIENT_ID \
  --role "Storage Blob Data Contributor" \
  --scope "/subscriptions/<subscription-id>/resourceGroups/<storage-resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>"

Note: This command requires elevated permissions. If you don’t have the necessary permissions, request someone with appropriate access to run this command.

Step 9: Restart Backup Extension Pods

9.1 Restart Deployments

# Restart deployments to pick up workload identity configuration
kubectl rollout restart deployment -n dataprotection-microsoft dataprotection-microsoft-controller
kubectl rollout restart deployment -n dataprotection-microsoft dataprotection-microsoft-kubernetes-agent
kubectl rollout restart deployment -n dataprotection-microsoft dataprotection-microsoft-geneva-service

# Wait for rollouts to complete
kubectl rollout status deployment -n dataprotection-microsoft dataprotection-microsoft-controller --timeout=120s
kubectl rollout status deployment -n dataprotection-microsoft dataprotection-microsoft-kubernetes-agent --timeout=120s
kubectl rollout status deployment -n dataprotection-microsoft dataprotection-microsoft-geneva-service --timeout=120s

Step 10: Verify Configuration

10.1 Check BackupStorageLocation Status

# Verify that BackupStorageLocation is available
kubectl get backupstoragelocation -n dataprotection-microsoft

# Expected output should show PHASE as "Available"

10.2 Check Pod Logs (Optional)

# Check velero container logs for authentication issues
kubectl logs -n dataprotection-microsoft \
  deployment/dataprotection-microsoft-kubernetes-agent \
  -c velero --tail=20

Step 11: Create Backup Instance

11.1 Prepare Backup Instance Configuration

Create a file backup-instance-config.json:

{
  "backupInstanceName": "<aks-cluster-name>-backup-instance",
  "properties": {
    "dataSourceInfo": {
      "resourceID": "/subscriptions/<subscription-id>/resourceGroups/<aks-resource-group>/providers/Microsoft.ContainerService/managedClusters/<aks-cluster-name>",
      "resourceLocation": "<location>",
      "resourceName": "<aks-cluster-name>",
      "resourceType": "Microsoft.ContainerService/managedclusters",
      "resourceUri": "/subscriptions/<subscription-id>/resourceGroups/<aks-resource-group>/providers/Microsoft.ContainerService/managedClusters/<aks-cluster-name>",
      "datasourceType": "Microsoft.ContainerService/managedClusters",
      "objectType": "Datasource"
    },
    "dataSourceSetInfo": {
      "resourceID": "/subscriptions/<subscription-id>/resourceGroups/<aks-resource-group>/providers/Microsoft.ContainerService/managedClusters/<aks-cluster-name>",
      "resourceLocation": "<location>",
      "resourceName": "<aks-cluster-name>",
      "resourceType": "Microsoft.ContainerService/managedclusters",
      "resourceUri": "/subscriptions/<subscription-id>/resourceGroups/<aks-resource-group>/providers/Microsoft.ContainerService/managedClusters/<aks-cluster-name>",
      "datasourceType": "Microsoft.ContainerService/managedClusters",
      "objectType": "DatasourceSet"
    },
    "policyInfo": {
      "policyId": "/subscriptions/<subscription-id>/resourceGroups/<backup-resource-group>/providers/Microsoft.DataProtection/backupVaults/<backup-vault-name>/backupPolicies/policy-aks",
      "policyParameters": {
        "dataStoreParametersList": [
          {
            "objectType": "AzureOperationalStoreParameters",
            "dataStoreType": "OperationalStore",
            "resourceGroupId": "/subscriptions/<subscription-id>/resourceGroups/<aks-resource-group>"
          }
        ],
        "backupDatasourceParametersList": [
          {
            "objectType": "KubernetesClusterBackupDatasourceParameters",
            "snapshotVolumes": true,
            "includeClusterScopeResources": true
          }
        ]
      }
    },
    "objectType": "BackupInstance",
    "friendlyName": "<aks-cluster-name>/aks-backup-instance"
  }
}

11.2 Create the Backup Instance

# Create backup instance
az dataprotection backup-instance create \
  --resource-group <backup-resource-group> \
  --vault-name <backup-vault-name> \
  --backup-instance ./backup-instance-config.json

11.3 Verify Backup Instance

# List backup instances
az dataprotection backup-instance list \
  --resource-group <backup-resource-group> \
  --vault-name <backup-vault-name> \
  --query "[].{Name:name, Status:properties.currentProtectionState}" \
  --output table

Expected output should show the backup instance with status “ConfiguringProtection” or “ProtectionConfigured”.

Step 12: Test Backup Operation (Optional)

12.1 Trigger Manual Backup

# Trigger an on-demand backup
az dataprotection backup-instance adhoc-backup \
  --resource-group <backup-resource-group> \
  --vault-name <backup-vault-name> \
  --backup-instance-name <backup-instance-name> \
  --backup-rule-name BackupDaily \
  --retention-tag-override Default

12.2 Monitor Backup Jobs

# List backup jobs
az dataprotection job list \
  --resource-group <backup-resource-group> \
  --vault-name <backup-vault-name> \
  --query "[].{JobId:name, Status:properties.status, StartTime:properties.startTime}" \
  --output table

Troubleshooting

Common Issues and Solutions

  1. BackupStorageLocation shows “Unavailable”

    • Verify storage account permissions
    • Check if managed identity has Storage Blob Data Contributor role
    • Wait for role propagation (can take 5-10 minutes)
  2. Authentication Errors in Logs

    • Verify service account annotations have correct client ID
    • Ensure deployment labels include azure.workload.identity/use: "true"
    • Check federated identity credentials are created correctly
  3. Extension Installation Fails

    • Verify AKS cluster has OIDC issuer and workload identity enabled
    • Check if storage account and container exist
    • Ensure correct subscription and resource group names
  4. Role Assignment Fails

    • Verify you have sufficient permissions to assign roles
    • Use --assignee-object-id and --assignee-principal-type flags if Graph API access is limited

Verification Commands

# Check extension status
az k8s-extension list --cluster-name <aks-cluster-name> --cluster-type managedClusters --resource-group <aks-resource-group>

# Check workload identity configuration
kubectl get sa -n dataprotection-microsoft -o yaml | grep -A3 -B3 "azure.workload.identity"

# Check federated credentials
az identity federated-credential list --identity-name <managed-identity-name> --resource-group <managed-identity-resource-group>

# Check role assignments
az role assignment list --assignee <managed-identity-client-id> --scope "/subscriptions/<subscription-id>/resourceGroups/<storage-resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>"

Summary

This configuration enables the AKS backup extension to use Azure Workload Identity for secure, keyless authentication to Azure storage. The key components are:

  1. Extension Managed Identity: Automatically created by the backup extension
  2. Federated Identity Credentials: Link Kubernetes service accounts to Azure managed identity
  3. Service Account Annotations: Connect pods to the managed identity
  4. Deployment Labels: Enable workload identity injection
  5. Storage Permissions: Grant access to backup storage account

Once configured, the backup extension will authenticate using workload identity tokens instead of secrets or connection strings, providing enhanced security and simplified credential management.