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
-
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)
-
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
-
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
-
Role Assignment Fails
- Verify you have sufficient permissions to assign roles
- Use
--assignee-object-idand--assignee-principal-typeflags 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:
- Extension Managed Identity: Automatically created by the backup extension
- Federated Identity Credentials: Link Kubernetes service accounts to Azure managed identity
- Service Account Annotations: Connect pods to the managed identity
- Deployment Labels: Enable workload identity injection
- 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.