How to Secure Azure Service Accounts with Managed Identities

Published:30 April 2026 - 6 min. read

Audit your Active Directory for weak passwords and risky accounts. Run your free Specops scan now!

If your app, function, VM, or automation job still signs into Azure with a username and secret, you are adding avoidable operational and security overhead every time someone stores it, copies it, rotates it, or forgets it exists. Managed identities are the clean option: Azure holds the credential, rotates it automatically, and you never see it.

This tutorial walks you through creating a managed identity for your Azure workload and granting it exactly the role it needs. You will enable the identity, assign a narrowly scoped role, validate that your workload can get a token, and remove the old secret. You will also put Privileged Identity Management (PIM) around the humans who manage the workload so nothing has standing privilege when you are done.

Prerequisites

If you want to follow along hands-on, you’ll need:

Deploying the Managed Identity

This is the part most teams want to rush. Do not. The setup works when you create the identity first, assign narrowly, validate, and only then remove the old secret. Strange how that keeps working.


Key Insight: The question is not “Which identity looks modern?” The question is “Which identity matches the workload’s lifecycle without leaving a secret behind?”


That lifecycle question drives the rest of the setup.

System-Assigned vs. User-Assigned

Managed identities come in two forms:

  • System-assigned identity: tied to one Azure resource and deleted with it.

  • User-assigned identity: a standalone resource you can attach to more than one workload.

Use system-assigned when the identity should die with the workload. Use user-assigned when you need the same identity across multiple resources or a stable identity before deployment. That choice looks boring until you need to change it later.

Step 1: Find What Already Exists

Before creating anything, check what managed identities already exist in the tenant. You want to avoid naming collisions and understand what scope is already in use.

az rest --method get --url 'https://graph.microsoft.com/v1.0/servicePrincipals?$filter=(servicePrincipalType eq '\''ManagedIdentity'\'')'

That query returns every managed identity in the tenant. It is useful when you need to find existing identities instead of guessing which team named them prod-app and left it there.

Then check the role assignments at the scope you plan to use:

az role assignment list \
  --scope /subscriptions/<sub-id>/resourceGroups/<rg-name> \
  --fill-principal-name false \
  -o table

That shows what is already assigned at the resource group scope. --fill-principal-name false keeps the command from doing extra Graph lookups you do not need during a review pass.

Step 2: Create Or Enable The Managed Identity

If the workload needs a reusable identity, create a user-assigned managed identity first:

az identity create \
  --name uai-ordering-api \
  --resource-group rg-shared-identities \
  --location eastus

This gives you a principalId for RBAC and a clientId for runtime selection. If the workload should own its identity lifecycle, enable a system-assigned identity on the Azure resource instead of creating a standalone one.

Step 3: Grant The Smallest Useful Role

Now bind the identity to the exact data it needs. For a Key Vault-backed app, that usually means a vault-scoped data role, not subscription-wide access.

principalId=$(az identity show \
  --name uai-ordering-api \
  --resource-group rg-shared-identities \
  --query principalId -o tsv)

This pulls the managed identity’s principalId so you can assign the role in the next step.

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

Key Vault Secrets User is a data-plane role. That matters. Control-plane access and data-plane access are not the same thing, and mixing them up is how people accidentally hand an app far more power than it needs.

Scope When to use it Risk if too broad
Resource A single narrowly scoped resource Lowest blast radius
Resource group Several related resources owned together Easy to overreach
Subscription Rare exception only Hard to justify later

Keep the scope as narrow as the workload allows. Subscription scope is a last resort, not a shortcut.

Step 4: Validate Token-Based Access

Once the identity is assigned, test it from the Azure resource that actually runs the workload — the workload host. That host is whatever Azure resource has the managed identity attached: a VM, an App Service instance, a Function App, or similar. On a VM, Azure App Service, or other Azure resource with a managed identity, use az login --identity. If you are using a user-assigned identity, specify the client ID or resource ID explicitly.

az login --identity --client-id <user-assigned-client-id>

This tells Azure CLI to sign in as the user-assigned identity attached to the workload host instead of your personal account.

az account get-access-token --resource https://vault.azure.net --query expiresOn -o tsv

This asks Azure CLI for a Key Vault token and prints the expiration time, which confirms the managed identity can actually mint a token for the target service.

For application code, the DefaultAzureCredential path is the normal way Azure Identity libraries switch to managed identity in production without changing your application logic.

Step 5: Remove The Secret

Do not leave the old client secret in place because it is still there.

Your cleanup should be simple:

  • Remove the client secret or password from the app registration.

  • Delete the secret from your vault, CI variables, and deployment files.

  • Rotate any dependent credentials that may have copied it.

  • Re-run the workload validation after removal.

If the app still works after the secret is gone, you are done with the deployment piece.

Put PIM Around The Humans

Managed identities secure the workload. Privileged Identity Management (PIM) secures the people. You need both if you want zero standing privilege instead of just prettier standing privilege.

Nobody likes this section because it puts friction where people are used to convenience. That friction is the control.

Human role Default state Why it matters
Owner Eligible No permanent admin
User Access Administrator Eligible Limits who can widen access
Approver Time-bound Forces a second check

Use PIM to:

  • Make Owner and User Access Administrator eligible, not permanently active.

  • Keep activation windows short.

  • Review who can activate and who can approve.

  • Stop treating “we need it for troubleshooting” as a permanent operating model.

If someone can assign managed identities, grant roles, and keep those rights forever, you still have a standing-privilege problem. It just has nicer documentation now.

Audit Who Can Abuse The Identity

You should also audit who can change the permission model, not just who can use it. That is the part people skip because it feels administrative instead of exciting, which is exactly why it gets ignored.

Use Azure RBAC role assignments to find the humans who can grant or extend access at the scope you care about:

az role assignment list \
  --scope /subscriptions/<sub-id> \
  --fill-principal-name false \
  --query "[?roleDefinitionName=='Owner' || roleDefinitionName=='User Access Administrator'].[principalName, roleDefinitionName, scope]" \
  -o table

That command shows the principals with the power to hand out more access at subscription scope. Run the same pattern at the resource-group or resource scope if your identity only needs a narrow boundary.

Question What to check Where
Who can assign roles? Owners and User Access Administrators Subscription and resource group
Who can activate PIM? Eligible approvers and activators PIM settings
What still has secrets? App registrations and vaults Microsoft Entra and Key Vault

Then ask three simple questions:

  • Who can assign the identity a new role?

  • Who can activate PIM for the people who manage this workload?

  • Which app registrations and service principals still carry secrets that should not exist anymore?

If the answers are fuzzy, you do not have an identity problem. You have an ownership problem with a login attached.

Common Mistakes

The deployment usually fails for the same reasons:

The biggest mistake is treating managed identities as authorization. It is authentication. Azure RBAC still does the permission work.

Mistake Better move
Keep the old secret “just in case” Remove it after validation
Give the managed identity a subscription-wide role Scope it to the vault or resource
Use a user-based service account for automation Replace it with a managed identity
Skip ownership review Track who can grant access

The last line is the one teams miss most often. The identity is clean, but the people around it still can widen access whenever they want.

Remove The Secret And Keep Privilege Time-Bound

If your Azure workload still depends on a stored secret, the path forward is straightforward: create a managed identity, grant only the role it needs, validate the token flow, and remove the secret from the app registration. Then use PIM so the humans stay time-bound too.

The end state is boring in the best way: no password vault entry to manage, and no standing privilege left behind because nobody had time to clean it up. Just one identity model for the workload and one privilege model for the people who manage it. That is what the security team actually wants, even if they make you fill out a form to say it.

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!