Safeguard
Cloud Security

Best practices for using Azure Managed Identity instead o...

A step-by-step guide to Azure Managed Identity best practices: system vs user assigned identities, least-privilege roles, and secretless authentication.

Karan Patel
Cloud Security Engineer
8 min read

Static secrets are the easiest thing to leak and the hardest thing to fully rotate. Connection strings end up in .env files that get committed by accident, service principal credentials sit in CI variables for years past their expiry date, and every rotation touches a dozen downstream systems that quietly break. If your Azure workloads still authenticate with client secrets or certificates you manage by hand, you're carrying risk that doesn't need to exist. Following Azure Managed Identity best practices lets you eliminate stored credentials entirely for resource-to-resource authentication, because Azure Active Directory (Microsoft Entra ID) issues and rotates the tokens for you behind the scenes. This guide walks through a practical, step-by-step rollout: choosing between a system assigned vs user assigned identity, wiring up role assignments correctly, updating application code, and verifying that everything actually works before you rip out the old secrets. By the end, you'll have a repeatable pattern for Azure resource authentication without secrets that scales across your entire environment.

Step 1: Azure Managed Identity Best Practices Start With System Assigned vs User Assigned Identity

Before touching any resource, decide which identity type fits your architecture. This is the single most consequential decision in any Azure Managed Identity best practices checklist, because it determines lifecycle, reusability, and blast radius.

A system assigned identity is created directly on a resource (a VM, an App Service, an Azure Function) and shares that resource's lifecycle — delete the resource, and the identity disappears with it. It's a clean 1:1 mapping, which makes it easy to reason about, but it means every resource needs its own role assignments, and you can't reuse the identity elsewhere.

A user assigned identity is a standalone Azure resource with its own lifecycle. You create it independently, then attach it to one or many compute resources. This is the better fit when several services need the same permission set (for example, a fleet of Function Apps that all read from the same Key Vault) because you assign roles once and attach the identity everywhere it's needed.

Rule of thumb: use system assigned identities for single-purpose, short-lived resources where the 1:1 lifecycle binding is actually desirable. Use user assigned identities when you need to share credentials logically across a group of resources or when the identity needs to outlive any single compute instance (useful during blue-green deployments or resource replacement).

Step 2: Enable the Identity on Your Resource

For a system assigned identity via Azure CLI:

az webapp identity assign \
  --name my-app-service \
  --resource-group my-rg

For a user assigned identity, create it first, then attach it:

az identity create \
  --name my-shared-identity \
  --resource-group my-rg

az webapp identity assign \
  --name my-app-service \
  --resource-group my-rg \
  --identities /subscriptions/<sub-id>/resourcegroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-shared-identity

Both commands return a principalId (also called the object ID) and, for user assigned identities, a clientId. Save these — you'll need the principalId for role assignments and the clientId when your application needs to explicitly request a token from a specific identity (common when a resource has multiple identities attached).

Step 3: Grant Least-Privilege Role Assignments

This is where managed identity security is won or lost. It's tempting to grant Contributor on a whole resource group and move on, but that defeats the purpose of moving away from static secrets — you've just replaced a leaked password with an over-privileged identity.

Scope every role assignment to the narrowest resource and the narrowest role that accomplishes the task:

az role assignment create \
  --assignee <principalId> \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub-id>/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/my-vault

Prefer built-in data-plane roles like Storage Blob Data Reader, Key Vault Secrets User, or Cosmos DB Built-in Data Reader over management-plane roles such as Contributor. Data-plane roles grant access to the actual data operations your app performs, without also granting the ability to delete or reconfigure the resource itself. If no built-in role fits, define a custom role with only the actions you need rather than reaching for a broad built-in one.

Step 4: Update Application Code to Request Tokens

Once the identity exists and has permissions, your application needs to actually use it instead of a connection string or API key. The Azure.Identity SDK abstracts this across languages with DefaultAzureCredential, which automatically detects and uses the managed identity when running in Azure:

var credential = new DefaultAzureCredential();
var client = new SecretClient(
    new Uri("https://my-vault.vault.azure.net/"),
    credential);
from azure.identity import ManagedIdentityCredential
from azure.keyvault.secrets import SecretClient

# Omit client_id for system assigned; pass it for user assigned
credential = ManagedIdentityCredential(client_id="<clientId>")
client = SecretClient(vault_url="https://my-vault.vault.azure.net/", credential=credential)

If a resource has more than one user assigned identity attached, always pass the explicit client_id — omitting it when multiple identities are present causes Azure to return an ambiguous-identity error rather than guessing which one you meant.

Step 5: Remove the Old Secrets and Rotate Access

Don't leave a fallback secret "just in case." A managed identity implementation that still accepts a connection string as a backup authentication path isn't Azure resource authentication without secrets — it's authentication with an extra step and an unmonitored second door. Once you've verified the identity path works end to end:

  1. Revoke or delete the old service principal credential, connection string, or SAS token.
  2. Remove the secret from Key Vault, CI variables, and any .env or configuration files.
  3. Search your codebase and infrastructure-as-code templates for lingering references to the old credential so nothing silently falls back to it.
  4. Audit sign-in and resource logs for a few days to confirm no client is still authenticating with the deprecated secret.

Step 6: Apply Conditional Access and Monitoring

Managed identities remove the risk of a leaked static secret, but they don't remove the need for oversight. Layer in additional controls:

  • Use Azure Policy to require that specific resource types (Function Apps, VMs, AKS workloads) have a managed identity enabled and deny deployments that fall back to key-based auth.
  • Enable diagnostic logging on Microsoft Entra ID sign-ins so token issuance to managed identities is visible in your SIEM.
  • Set up alerts for role assignment changes on sensitive scopes like Key Vault or storage accounts, since an over-permissioned identity is now your primary attack surface instead of a stolen credential.
  • Periodically review which identities are actually in use — orphaned user assigned identities with stale role assignments are a common source of privilege creep.

Troubleshooting and Verification

Before you consider the migration complete, verify the identity is actually functioning and check for the failure modes that come up most often:

  • "Identity not found" or 403 errors immediately after assignment: Role assignments and identity provisioning can take a few minutes to propagate through Azure AD. Wait 5–10 minutes and retry before assuming the configuration is wrong.
  • Confirm the identity exists and is enabled:
    az webapp identity show --name my-app-service --resource-group my-rg
    
    Check that principalId is populated and type matches what you expect (SystemAssigned, UserAssigned, or SystemAssigned,UserAssigned).
  • Verify role assignments actually landed:
    az role assignment list --assignee <principalId> --all --output table
    
  • Ambiguous identity errors with multiple user assigned identities: Always pass an explicit client_id (or object_id) in your credential configuration rather than relying on default resolution.
  • Local development doesn't have a managed identity: DefaultAzureCredential falls back to Azure CLI, Visual Studio, or environment credentials locally, so developers can test the same code path without a managed identity being present — just make sure whatever local credential you use has equivalent scoped permissions, not broader ones.
  • Token acquisition works but data calls still fail: This is almost always a role assignment scoped too narrowly or pointed at the wrong resource — re-check the --scope value against the exact resource ID, not just the resource group.

How Safeguard Helps

Migrating to managed identities is a strong step, but it's only as good as your ability to prove it's actually holding across a growing environment. Safeguard continuously scans your repositories, pipelines, and infrastructure-as-code for hardcoded secrets, expiring service principal credentials, and configuration drift that reintroduces static credentials after a migration is supposedly done. It maps role assignments tied to managed identities back to the workloads that use them, flags over-privileged grants like unscoped Contributor roles, and surfaces orphaned identities that still hold access nobody remembers granting.

For teams building out Azure Managed Identity best practices at scale, that visibility matters more than the initial cutover: secrets have a way of creeping back in through a rushed hotfix or a new service that skipped the standard onboarding path. Safeguard's software supply chain monitoring closes that gap by treating credential hygiene as a continuously verified property of your pipeline, not a one-time migration checklist — so the secretless architecture you build this quarter is still secretless a year from now.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.