By the end of this lab, you'll be able to:
- Add the WIF service principal to the AKS admin AD group — so the pipeline can authenticate to AKS for deployments
- Retrieve the Application Insights connection string — required by the
azure-monitor-opentelemetrySDK - Store the connection string as a Key Vault secret — so the pipeline can inject it securely into Kubernetes
- Create an Azure DevOps variable group — linking the Key Vault secret for use in the pipeline
⏱️ Estimated Time: ~15 minutes
Before starting, ensure you have:
- Azure CLI authenticated (
az login) - Completed Lab 3 — Deploy App to ACR
- AKS, Key Vault, and Application Insights provisioned by Terraform (Lab 2)
- Entra ID group
devopsjourney-aks-group-apr2026created (Lab 1.3)
The Workload Identity Federation service principal needs to be a member of the devopsjourney-aks-group-apr2026 group. This grants the pipeline identity AKS Cluster Admin RBAC to run kubectl apply during the deploy stage.
-
🔍 Find the WIF service principal
In Azure DevOps → Project Settings → Service connections → click your ARM service connection (e.g.,
azure-devops-journey-apr2026) → Manage Workload Identity.Note the Object ID of the Enterprise Application / Service Principal.
Or find it via CLI:
# List service principals — find the one matching your WIF identity name az ad sp list \ --display-name "azure-devops-journey-identity" \ --query "[].{Name:displayName, ObjectId:id}" -o table
-
➕ Add the service principal to the AKS admin group
# Get the WIF service principal object ID SP_OBJ_ID=$(az ad sp list \ --display-name "azure-devops-journey-identity" \ --query "[0].id" -o tsv) # Add to the AKS admin group az ad group member add \ --group "devopsjourney-aks-group-apr2026" \ --member-id "$SP_OBJ_ID" echo "✅ Added $SP_OBJ_ID to devopsjourney-aks-group-apr2026"
✅ Expected Output:
✅ Added a1b2c3d4-... to devopsjourney-aks-group-apr2026 -
🔍 Verify group membership
az ad group member list \ --group "devopsjourney-aks-group-apr2026" \ --query "[].{Name:displayName, Type:userType}" -o table
⚠️ Important: The app usesazure-monitor-opentelemetry==1.8.7which requires the full Connection String — NOT just the Instrumentation Key. The connection string includes endpoint URLs needed by the OpenTelemetry exporter. The environment variable name isAPPLICATIONINSIGHTS_CONNECTION_STRING.
-
📋 Retrieve the connection string via Azure CLI
# Install the extension if not already present az extension add --name application-insights --only-show-errors # Get the connection string (update RG and AI name to your values) az monitor app-insights component show \ --app devopsjourneyapr2026ai \ -g devopsjourneyapr2026-rg \ --query connectionString \ -o tsv
✅ Expected Output:
InstrumentationKey=8896c09a-a3e3-4a72-9914-f826e85c6a5f;IngestionEndpoint=https://uksouth-1.in.applicationinsights.azure.com/;LiveEndpoint=https://uksouth.livediagnostics.monitor.azure.com/;ApplicationId=abc12345-...💡 You can also find this in the Azure Portal: Application Insights → your resource → Overview → Connection String (copy button).
-
📋 Save the full connection string
Copy the entire value starting with
InstrumentationKey=...— you will use this in the next step.
The pipeline reads the connection string from Key Vault and injects it as a Kubernetes secret. The Python app reads APPLICATIONINSIGHTS_CONNECTION_STRING at startup.
-
🔐 Add the secret to Key Vault
# Store the full connection string as secret "AIKEY" CONN_STRING=$(az monitor app-insights component show \ --app devopsjourneyapr2026ai \ -g devopsjourneyapr2026-rg \ --query connectionString \ -o tsv) az keyvault secret set \ --vault-name "devopsjourneyapr2026-kv" \ --name "AIKEY" \ --value "$CONN_STRING"
✅ Expected Output:
{ "id": "https://devopsjourneyapr2026-kv.vault.azure.net/secrets/AIKEY/...", "name": "AIKEY", "value": "InstrumentationKey=...;IngestionEndpoint=...;LiveEndpoint=..." } -
✅ Verify the secret
az keyvault secret show \ --vault-name "devopsjourneyapr2026-kv" \ --name "AIKEY" \ --query "{Name:name, Value:value}" \ -o table
The variable group links to Key Vault and makes the AIKEY secret available to pipelines as $(AIKEY).
-
📚 Navigate to Pipelines → Library
In Azure DevOps → Pipelines → Library → + Variable group.
-
🔧 Configure the variable group
- Variable group name:
devopsjourney - Link secrets from an Azure key vault as variables: Toggle ON
- Azure subscription: Select your WIF service connection
- Key vault name:
devopsjourneyapr2026-kv - Click + Add → select
AIKEY - Click Save
- Variable group name:
-
✅ Verify the variable group
The variable group
devopsjourneyshould showAIKEYas a linked Key Vault secret (displayed as****).
Infrastructure checklist:
- WIF service principal is a member of
devopsjourney-aks-group-apr2026 - Key Vault secret
AIKEYcontains the full App Insights connection string - Azure DevOps variable group
devopsjourneyis created and linked to Key Vault
Technical validation:
# Verify WIF SP is in the group
az ad group member check \
--group "devopsjourney-aks-group-apr2026" \
--member-id "$(az ad sp list --display-name 'azure-devops-journey-identity' --query '[0].id' -o tsv)"
# Verify Key Vault secret exists (shows name only, not value)
az keyvault secret list \
--vault-name "devopsjourneyapr2026-kv" \
--query "[?name=='AIKEY'].{Name:name, Enabled:attributes.enabled}" -o table
# Verify connection string format
AIKEY=$(az keyvault secret show \
--vault-name "devopsjourneyapr2026-kv" \
--name "AIKEY" \
--query value -o tsv)
echo "$AIKEY" | grep -c "InstrumentationKey=" && echo "✅ Connection string format valid"✅ Expected Output:
{
"value": true
}
Name Enabled
------ -------
AIKEY True
✅ Connection string format valid
🔧 Troubleshooting (click to expand)
# Problem: "Insufficient privileges" adding SP to group
# Solution: Use a user with Group Administrator or User Administrator role
# Or do it in the Azure Portal: Entra ID → Groups → devopsjourney-aks-group-apr2026 → Members → Add
# Problem: Key Vault access denied when setting secret
# Solution: Ensure your user has Key Vault Administrator RBAC on the vault
az role assignment create \
--assignee "$(az account show --query user.name -o tsv)" \
--role "Key Vault Administrator" \
--scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/devopsjourneyapr2026-rg/providers/Microsoft.KeyVault/vaults/devopsjourneyapr2026-kv"
# Problem: Variable group cannot read Key Vault (pipeline fails)
# Solution: Ensure the WIF service connection has Key Vault Secrets User role on the Key Vault
az role assignment create \
--assignee "<wif-sp-object-id>" \
--role "Key Vault Secrets User" \
--scope "/subscriptions/<sub-id>/resourceGroups/devopsjourneyapr2026-rg/providers/Microsoft.KeyVault/vaults/devopsjourneyapr2026-kv"
# Problem: App Insights resource not found by CLI
# Solution: List all App Insights in the resource group
az monitor app-insights component list \
-g devopsjourneyapr2026-rg \
--query "[].{Name:name, Kind:kind}" -o table- The AKS cluster uses Azure RBAC — the admin group has Azure Kubernetes Service Cluster Admin role. The pipeline's WIF service principal must be a group member to run
az aks get-credentialsand executekubectl applyduring the deployment stage. - The connection string is required, not just the Instrumentation Key — the
azure-monitor-opentelemetrySDK uses the OpenTelemetry Azure Monitor exporter, which needs the fullInstrumentationKey=...;IngestionEndpoint=...;LiveEndpoint=...string to reach the correct regional endpoint. - Key Vault provides secret governance — rotation, access auditing, RBAC, and soft-delete protection. Pipeline variables (even marked secret) are stored in Azure DevOps and lack the enterprise controls of Key Vault.
- The Kubernetes deployment manifest references a Kubernetes secret
aikey. The pipeline creates this secret from$(AIKEY)(the variable group value), and the pod receivesAPPLICATIONINSIGHTS_CONNECTION_STRINGas an environment variable at startup.
The pipeline identity has AKS access and the App Insights connection string is securely stored. In the next lab you'll add the Deploy stage to push the application to AKS.
← Back to Lab 3 | Continue to Lab 4.2 →