Home / Azure

AzureHow-toIntermediate

Transformation: replace client secrets with managed identities and workload identity federation

Remove stored secrets from apps and pipelines. Inventory secret-based service principals, move Azure workloads to managed identities, move GitHub, Azure DevOps and AKS workloads to federated credentials, then block new secrets.

Deepak Bhardwaj3 min read

Applies toMicrosoft Entra ID, Azure managed identities, AKS workload identity, GitHub Actions, Azure DevOps, Azure CLI 2.50+

Why

Client secrets expire (causing outages), leak (causing incidents) and need rotation (causing toil). Managed identities and federated credentials have no secret for you to store.

1. Inventory

az ad app list --all --query "[?length(passwordCredentials) > \`0\`].{name:displayName, appId:appId, secrets:length(passwordCredentials)}" -o table

For each app, note where it runs. That decides the replacement:

Workload runs in Replace secret with
App Service, Functions, Container Apps, VMs, ADF System- or user-assigned managed identity
AKS pods Workload identity (federated credential on a user-assigned identity)
GitHub Actions Federated credential (OIDC)
Azure DevOps Workload identity federation service connection
Another cloud (AWS, GCP) or on-prem Kubernetes Federated credential trusting that issuer
On-prem server, no OIDC issuer Certificate credential, or Azure Arc managed identity

2. Azure-hosted apps: managed identity

az webapp identity assign -g rg-app -n app-orders
PRINCIPAL=$(az webapp identity show -g rg-app -n app-orders --query principalId -o tsv)
az role assignment create --assignee-object-id $PRINCIPAL --assignee-principal-type ServicePrincipal \
  --role "Storage Blob Data Contributor" --scope <storage-account-id>

In code, switch to DefaultAzureCredential (Azure SDKs), which uses the managed identity in Azure and your developer login locally.

3. GitHub Actions: OIDC

az identity create -g rg-cicd -n id-gha-deploy
az identity federated-credential create -g rg-cicd --identity-name id-gha-deploy -n gh-main \
  --issuer https://token.actions.githubusercontent.com \
  --subject repo:contoso/orders:environment:production \
  --audiences api://AzureADTokenExchange
permissions:
  id-token: write
  contents: read
steps:
  - uses: azure/login@v2
    with:
      client-id: ${{ vars.AZURE_CLIENT_ID }}
      tenant-id: ${{ vars.AZURE_TENANT_ID }}
      subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

4. AKS: workload identity

az aks update -g rg-aks -n aks-prod --enable-oidc-issuer --enable-workload-identity
ISSUER=$(az aks show -g rg-aks -n aks-prod --query oidcIssuerProfile.issuerUrl -o tsv)
az identity federated-credential create -g rg-aks --identity-name id-orders -n orders-sa \
  --issuer $ISSUER --subject system:serviceaccount:prod:orders --audiences api://AzureADTokenExchange

Annotate the service account with azure.workload.identity/client-id and label pods with azure.workload.identity/use: "true".

5. Azure DevOps

Convert existing ARM service connections to workload identity federation from the service connection page (there is a convert option), or create new ones with that scheme. Test a pipeline run, then delete the old secret.

6. Remove secrets and block new ones

az ad app credential list --id <appId> -o table
az ad app credential delete --id <appId> --key-id <keyId>

Use Entra ID application management policies to restrict or ban new password credentials on app registrations once the migration is done.

Tip

Do one app end to end — identity, roles, code, pipeline, secret deleted — before batch-converting. The first one surfaces every SDK and permission gotcha.

Checklist

  • Inventory of secret-based apps with owners
  • Each workload mapped to managed identity or federation
  • Least-privilege role assignments on the new identities
  • Old secrets deleted, app management policy enforced