Managed Identities: Why You Should Never See a Connection String Again
How managed identities let Azure-hosted workloads authenticate to other Azure services without secrets β system-assigned vs user-assigned, when to use which, and the pattern that quietly defines half the right answers on SC-500.
The pattern that defines half the right answers on SC-500
A managed identity is an Azure-managed service principal β an identity that exists for an Azure resource, with secrets that Azure creates, stores, and rotates for you. You never see a client secret, never put a connection string in code, never have a secret to leak.
When your code on Azure App Service (or Container Apps, or a Function, or a VM, or an AKS pod, or a Logic Appβ¦) wants to read from Key Vault, write to Cosmos DB, pull an image from ACR, or send a message to Service Bus, it just asks for a token from a local managed identity endpoint and presents that token to the target service. The target service trusts Microsoft Entra and authorises the request based on the RBAC role you assigned.
Two flavours exist:
- System-assigned β tied to the lifecycle of one Azure resource. Created when you enable it on the resource; deleted when the resource is deleted. Cannot be shared.
- User-assigned β a standalone Azure resource you create. Multiple Azure resources can use it. Survives the deletion of any one consumer.
The SC-500 pattern: any scenario where the proposed answer involves a connection string, a stored client secret, or βrotate the key every 90 daysβ β there is almost always a better answer involving a managed identity. Read every Azure-to-Azure auth question for this cue.
System-assigned vs user-assigned
| Feature | System-assigned | User-assigned |
|---|---|---|
| Lifecycle | Born and dies with the parent Azure resource | Independent β survives deletion of any consumer |
| Sharing across resources | No β exactly one consumer | Yes β many resources can attach the same identity |
| RBAC and permissions on recreation | A new system-assigned identity has a new object ID β all role assignments and Graph consents must be re-applied | Object ID is stable β recreate consumers freely without re-granting |
| How to create | Toggle 'Identity' on the resource (portal, CLI, Bicep) | Create a standalone `userAssignedIdentities` resource, then attach |
| Best for | A single Azure resource with a small, well-scoped set of permissions and no need to share | Multiple instances of the same workload (e.g. an AKS workload across pods), or any case where identity must outlive a consumer |
| Per-consumer limit | One per resource (system-assigned) | Per-service limit (e.g. multiple user-assigned on a single VM is supported) |
| Federated credentials | Not supported | Supported (workload identity federation for AKS, GitHub Actions, etc.) |
When to pick which
- One Azure resource, dedicated permissions, fine to lose-and-recreate β system-assigned. Simplest model. Most App Service / Function / Logic App scenarios start here.
- Multiple resources sharing the same access pattern (a fleet of VMs accessing the same Key Vault, an AKS workload across many pods) β user-assigned. One identity, many consumers.
- Need to grant role assignments BEFORE the consumer is created (so the workload comes up with access already in place) β user-assigned. You can create the identity, grant the role, then create the consumer.
- Federated workload identity (AKS pods using workload identity, GitHub Actions OIDC, external workloads) β user-assigned. Only user-assigned supports federated credentials.
How Azure services consume managed identities
The general pattern for SC-500 questions:
- Enable managed identity on the Azure resource (system-assigned toggle, or attach a user-assigned).
- The Azure resource gets a service principal in Entra ID with an object ID.
- Grant that service principal an RBAC role on the target Azure resource (or grant Microsoft Graph application permissions for Graph calls).
- The workload, running in the Azure resource, requests a token from the local IMDS endpoint using the Azure Identity SDK (
DefaultAzureCredentialin .NET / Python / Java / JavaScript). - The token is presented to the target service; the target service authorises based on the granted role.
This pattern replaces a long list of antipatterns:
| Feature | Antipattern (wrong answer) | Managed identity (right answer) |
|---|---|---|
| Auth to Key Vault | Bake `--client-secret` into App Service app settings | Enable system-assigned MI on App Service, grant `Key Vault Secrets User` role |
| Auth to Storage | Store storage account key in code or in a config file | MI on the workload, grant `Storage Blob Data Contributor` role |
| Auth to Service Bus | Use a connection string with SAS key | MI on the workload, grant `Azure Service Bus Data Sender` role |
| Auth to Cosmos DB | Embed master key in code or config | MI on the workload, grant `Cosmos DB Built-in Data Contributor` |
| Auth to ACR for image pull | Use admin user (`acrAdmin` enabled) with username/password | MI on the compute (App Service / Container Apps / AKS), grant `AcrPull` |
| Auth to SQL | SQL auth user with password in a connection string | MI on the workload, create contained user mapped to MI in SQL, grant minimum DB rights |
| Auth to Microsoft Graph | App registration with client secret in Key Vault, rotate every 90 days | MI on the workload, grant required Graph application permission with admin consent |
Exam pattern: 'remove the connection string'
When an SC-500 scenario sounds like:
βRavi at Maple Genomics has an Azure Function that calls Key Vault using a client secret stored in App Settings. The security team wants to eliminate stored secrets. What should Ravi do?β
The right answer almost always includes:
- Enable a system-assigned (or attach a user-assigned) managed identity on the Function App.
- Grant the managed identity the appropriate RBAC role on the target resource β for Key Vault Secrets, thatβs
Key Vault Secrets User(read) orKey Vault Secrets Officer(read/write). - Update the application code to use
DefaultAzureCredentialfrom the Azure Identity SDK β which will automatically pick up the managed identity in Azure-hosted environments.
Wrong answers will typically suggest: rotating the secret, using Key Vault references (still uses a secret under the hood at the policy level β managed identity is preferred), or storing the secret in a separate Key Vault (still a secret). The pattern is: when the question contains βstored secretβ + βAzure-to-Azure authβ, the answer is managed identity.
Where managed identities are supported
The list of Azure services that support managed identities is long and grows. Key services on SC-500 scope:
- Compute β App Service, Azure Functions, Logic Apps (standard plan), Azure Container Apps, AKS (via workload identity federation with user-assigned), Virtual Machines, Virtual Machine Scale Sets, Azure Container Instances, Service Fabric, Azure Batch.
- Data / messaging targets (as the target of MI-based auth) β Key Vault, Storage (Blob, Queue, Table, Files via REST), Azure SQL, Cosmos DB, Service Bus, Event Grid, Event Hubs, Azure Managed Redis, Container Registry.
- APIs β Microsoft Graph (application permissions), Azure Resource Manager, Defender for Cloud APIs.
Notably, App Service and Container Apps support both system-assigned and user-assigned. AKS uses workload identity federation with a user-assigned MI to bind a Kubernetes service account to an Entra identity β no secrets in the cluster.
Scenario: Esme rebuilds a legacy app pattern
Esme at Northwind Bank inherits a legacy App Service Web App that reads from Azure SQL and writes audit events to Service Bus. It uses two SQL connection strings (read replica + primary) with passwords, and a Service Bus connection string β all stored in App Settings. The auditor wants secrets eliminated.
Her remediation:
- Enable system-assigned MI on the App Service. One toggle, one minute.
- Configure SQL. In Azure SQL, set a Microsoft Entra admin. Connect with the admin and run
CREATE USER [northwind-payments-app] FROM EXTERNAL PROVIDER(wherenorthwind-payments-appmatches the App Service name β the system-assigned MI is named after the resource). Grantdb_datareaderanddb_datawriter. Replace the SQL connection string with an Entra-authenticated one (Authentication=Active Directory Default;...). - Configure Service Bus. Assign the App Serviceβs MI the
Azure Service Bus Data Senderrole on the target Service Bus namespace (or queue/topic). Update code fromServiceBusClient(connectionString)toServiceBusClient(fullyQualifiedNamespace, new DefaultAzureCredential()). - Delete the app settings holding the old connection strings. Rotate the SQL admin password and Service Bus SAS key out of memory of any operator who knew them.
The App Service now holds no secrets. Auditor closes the finding. If the App Service is deleted and recreated, Esme rebinds the new MIβs object ID in SQL and re-assigns the Service Bus role β a 10-line script. (Alternatively, she could have used a user-assigned MI to make recreation trivial; for a single long-lived web app, system-assigned was the right call.)
Key terms
Knowledge check
Ravi at Maple Genomics has an Azure Function that reads secrets from Key Vault using a stored client ID and client secret of an app registration. He wants to eliminate the stored secret. What is the most direct change?
Asha at Aurora Health Service is deploying a 12-VM scale set where every VM needs the same access to a shared Storage account. She wants the role assignment to outlive any individual VM. Which managed identity should she use?
Esme at Northwind Bank deletes an App Service and recreates it with the same name. The new App Service has system-assigned MI re-enabled. The application now gets `Forbidden` errors when reading Key Vault secrets. What is the most likely cause?
Whatβs next
Next module: Azure Key Vault itself β how to deploy it securely, configure firewall and access (the RBAC vs access-policy decision), enable Defender for Key Vault, and use Defender CSPM secret scanning to find rogue secrets across your subscriptions.