How your Azure workloads authenticate: from access keys to federated managed identities

9 July 2026 · Josh Ellis · 11 min read

Access keys, app registrations, managed identities and federation compared. Where your Azure workloads sit on the identity ladder and how to move up.

Every Azure environment we assess has one. An app that was stood up in an afternoon, authenticated with a storage account key or a connection string, and shipped. It worked, so nobody touched it again. Three years later it is load-bearing, the key lives in a pipeline variable, a wiki page and at least one former staff member's head, and nobody can tell you what breaks if you rotate it.

We built our own MDR platform on Azure and went through this progression ourselves. Workload authentication has a maturity ladder with four rungs, and most environments stop on the first one because v1 worked.

Level 1: Access keys and connection strings

The bottom rung. Storage account keys, Service Bus SAS keys, Cosmos DB keys, Event Hub connection strings. Paste the value into an app setting and the code runs. That speed is why every platform ships with them and why they end up everywhere.

The trade-offs stack up fast:

No identity. The key authenticates a request without identifying the caller. Your storage logs record that a request used Shared Key authentication, and that is the end of the trail. Three apps, a scheduled script and a contractor's laptop can all use the same key, and the logs cannot tell them apart. When the security team asks "what accessed this container on Saturday night", the honest answer is "something with the key".

Full-scope access. A storage account key grants access to every container, queue and table in the account. There is no way to scope it down. Whatever holds the key holds the account.

Rotation is an outage risk. Rotating the key breaks everything that uses it, and after a few years nobody has the full list of what that is. So nobody rotates it. Anyone who has seen the key in the last five years still has it.

It spreads. Keys get copied into pipeline variables, local appsettings.json files, Teams messages and runbooks. Each copy is a place an attacker can find it, and source code leaks with embedded keys remain one of the most common cloud breach entry points.

Level 1 is acceptable for a throwaway proof of concept. It has no place in anything a business depends on.

Level 2: App registrations with client secrets

The first real step up. You register an application in Entra ID, create a client secret or certificate, and grant the service principal specific roles. The workload now has an identity.

That identity changes what your logs can tell you. Service principal sign-ins appear in the Entra sign-in logs with the app name, IP address and result. Access is scoped through RBAC rather than granting the whole resource. Secrets carry an expiry date, so credentials age out instead of living forever. If you must use a credential at this level, use a certificate rather than a client secret. Certificates resist interception in transit and cannot be pasted into a chat message as a working credential.

An app registration secret is still a password with extra steps. It gets stored somewhere, copied somewhere, and known by someone. The expiry that makes it better than a key also creates the classic Level 2 incident: the secret expires at 2am on a Saturday, an integration goes dark, and nobody remembers which of the 40 app registrations in the tenant it belongs to. We see expired-secret outages more often than we see key compromises.

The other quiet failure at this level is permission sprawl. App registrations granted Directory.ReadWrite.All or a Contributor role "temporarily" during setup, never reviewed again. The identity is auditable, but nobody audits it.

Level 2 is the right answer when the workload runs outside Azure and Level 4 is not available to you. Treat it as a deliberate choice with certificate credentials, expiry monitoring and least-privilege roles.

Level 3: Managed identities

The rung where the credential disappears. A managed identity is an Entra identity attached to an Azure resource: a Function App, VM, Logic App, container app. The platform issues tokens to the workload at runtime. There is no secret to create, store, rotate or leak. Nothing to paste into a pipeline variable, nothing to expire on a Saturday.

For anything running on Azure compute and talking to Azure services or Microsoft Graph, this should be the default. The developer effort is close to zero. Swap the connection string for DefaultAzureCredential, assign the identity a role on the target resource, delete the key from your app settings, and you are done. Most teams stuck at Level 1 are one afternoon of work away from Level 3 and do not know it.

Two caveats keep this from being the final rung:

It only works on Azure. A managed identity is bound to Azure compute. An on-premises server, a GitHub Actions runner or a SaaS integration cannot have one, which is why teams fall back to Level 2 for those workloads.

Secretless does not mean safe. A managed identity with Contributor on the subscription is still a serious problem. If an attacker compromises the Function App, they hold every permission its identity holds. Least privilege matters as much here as it did two rungs down. Grant the narrowest role on the narrowest scope, and prefer user-assigned identities where you want the permission set defined once and reviewed in one place.

Level 4: Managed identities federating through an app registration

The advanced rung, and the one with the least written about it. Federation combines the strengths of the two levels below it: the secretless runtime of a managed identity with the capabilities only an app registration has, such as multi-tenant access.

The mechanics: you add a federated identity credential to the app registration that names your managed identity as a trusted subject. At runtime, the workload requests a token as its managed identity, then exchanges that token to authenticate as the app registration. No client secret or certificate exists anywhere in the chain. The app registration has an identity vouching for it rather than a credential.

This is the pattern we use in our own platform. Our tooling runs on Azure compute and needs to reach into customer tenants, which a managed identity cannot do on its own since it is bound to our tenant. A multi-tenant app registration can, but we refused to run a fleet of client secrets for it. Federation resolved the conflict: the managed identity proves the workload is our workload, and the app registration carries the cross-tenant permissions. If the compute is destroyed, the identity dies with it. There is nothing to steal from a config file because the config file holds nothing.

The same federation mechanism extends beyond managed identities. GitHub Actions and Azure DevOps pipelines can federate into an app registration using OIDC, which removes deployment credentials from your CI/CD system. If your pipelines still authenticate with a service principal secret, that is the same upgrade wearing different clothes.

The ladder at a glance

Level 1: Access keys Level 2: App registration Level 3: Managed identity Level 4: Federation
Credential to manage Yes, never expires Yes, expires No No
Caller visible in logs No Yes Yes Yes
Access scoping Whole resource RBAC RBAC RBAC
Rotation burden Manual, breaks things Expiry-driven None None
Works outside Azure Yes Yes No Partial (OIDC federation)
Multi-tenant capable No Yes No Yes

Finding your Level 1s

The workloads stuck at the bottom of the ladder do not announce themselves. Three checks will surface most of them.

Storage accounts still accepting keys. List accounts where shared key access has not been disabled:

az storage account list --query '[?allowSharedKeyAccess == `null` || allowSharedKeyAccess == `true`].name' -o tsv

Then check whether anything is using the key before you turn it off:

StorageBlobLogs
| where TimeGenerated > ago(14d)
| where AuthenticationType in ("AccountKey", "SAS")
| summarize Requests = count() by AccountName, AuthenticationType, CallerIpAddress, UserAgentHeader

This query assumes the storage account has a diagnostic setting sending resource logs to a Log Analytics workspace, which is off by default and absent in most environments. If the table comes back empty, confirm the logs exist before concluding the keys are unused. No diagnostics? You have two options: enable them and wait two weeks for a picture to build, or use the storage account's Transactions metric in Azure Monitor split by the Authentication dimension, which needs no setup and shows key usage counts straight away, though without the caller detail.

An empty result with logging confirmed means you can set allowSharedKeyAccess to false today. A populated one is your migration worklist, with the IP and user agent telling you which app to go fix.

Service principals authenticating with secrets. The Entra sign-in logs show every service principal sign-in. The KQL below needs the Entra diagnostic setting for ServicePrincipalSignInLogs pointed at a workspace; if that is not in place, the same data sits in the Entra portal under sign-in logs on the service principals tab. Start with the busiest:

AADServicePrincipalSignInLogs
| where TimeGenerated > ago(30d)
| where ResultType == 0
| summarize SignIns = count() by ServicePrincipalName, AppId, ClientCredentialType
| order by SignIns desc

For each app on that list, check its credentials in Entra. Anything with a client secret and an Azure-hosted workload behind it is a Level 3 or Level 4 candidate. While you are in there, review the expiry dates. Entra's recommendations feature will flag secrets approaching expiry, which beats finding out at 2am.

Connection strings in app configuration. Review the app settings on your Function Apps and App Services for storage connection strings and account keys. Each one you find is a workload that could be using its managed identity instead.

Keeping Level 1 from coming back

Cleaning up existing keys is half the job. Azure Policy handles the other half by stopping new ones from appearing. Most Azure services expose a control-plane switch for local authentication: allowSharedKeyAccess on storage accounts, disableLocalAuth on Cosmos DB, Service Bus and Event Hubs. Microsoft ships built-in policy definitions targeting each of them, and "Storage accounts should prevent shared key access" and "Azure Cosmos DB database accounts should have local authentication methods disabled" are the two we deploy most often.

The rollout pattern: assign the policies in Audit at management group scope, work the compliance report down using the checks above, then flip the assignment to Deny. From that point, a new storage account with shared key access enabled fails at deployment, and your engineers have the workload identity conversation at build time instead of during an incident. For services that support it, the Modify effect can remediate existing resources as part of the same assignment, though take care using Modify to turn keys off, since anything still using them breaks the moment the policy lands.

Deny turns a clean-up project into a standard. Without it, the next contractor or the next proof of concept starts the whole cycle again.

Where to from here

Run the checks above and you will know where your estate sits on the ladder. The pattern we see across our customer base: a handful of Level 2 app registrations that people know about, and a long tail of Level 1 keys that nobody does. The keys are the risk, because they are unaudited, unrotated and unscoped by design. Find them, migrate them, then put policy in place so the count stays at zero.

The move from Level 1 to Level 3 costs an afternoon per workload for anything already running on Azure. The move to Level 4 takes design work and pays for itself the first time you skip a secret rotation cycle, or the first time an auditor asks how your platform authenticates and the answer is "there are no credentials to steal".

If you want a second pair of eyes across how your Azure workloads authenticate, or the overall security of your Azure environment, that is the kind of assessment we do. Get in touch.

Share

Back to insights