Blog

Passwordless Azure Functions: Skip Secrets with Managed Identity

Azure Functions keep showing up in real workloads, and one common pattern is calling a secured Web API from a function. The catch: every call pattern usually drags in another Azure AD app registration, another client secret, and another certificate quietly approaching expiration. A feature called Managed Service Identity (MSI) is built to take that pain off the developer's plate.

MSI lets an Azure-hosted resource authenticate against Azure AD without storing a secret in code or configuration. This walkthrough shows how to wire that up between a Function and a JWT-protected API.

Setting up the API side

The Web API in this pattern is a standard Azure-hosted ASP.NET service that accepts JWT bearer tokens. Authentication is configured in Startup so that only callers presenting a valid Azure AD token get past the authorization filter. The audience is set to the AD application's ClientId, and the authority is built from the Azure AD instance URL plus the tenant ID. Anything hitting the API without a matching token gets a 401.

Registering the API as an Azure AD application is the only manual setup required. Once the ClientId exists, the API does not care how the caller obtained its token—only that the token is valid and targeted at the right audience.

Turning on Managed Identity

Identity gets switched on inside the Azure Portal under the Function's "Platform features" blade. The relevant toggle is "System Assigned," which creates a brand-new service principal in the Azure AD tenant the moment it is enabled. That principal's lifecycle is tied directly to the Function: deleting the Function deletes the principal, no orphan accounts lingering in the directory.

Once enabled, the identity appears under Enterprise Applications for the tenant. Developers do not need to grab its ClientId or generate any secret. Azure handles credential rotation internally and exposes the identity to the Function as ambient context.

Calling the API without a secret

With the identity live, the Function no longer hardcodes a client secret or thumbprint. Instead it instantiates AzureServiceTokenProvider from Microsoft.Azure.Services.AppAuthentication, passes in the AD App Id of the Web API as the target resource, and asks for an access token. That token drops into the Authorization header as a Bearer token before calling the secured endpoint.

If no connection string is set in code, the provider falls back through a chain: Managed Service Identity first, then Visual Studio, Azure CLI, and Integrated Windows Authentication during local development. The same variable, AzureServicesAuthConnectionString, can be injected as an environment variable when developers need to override behavior without recompiling.

Who should care

Scenario Best fit
Function calls Azure-hosted Web API System-assigned MSI
Multiple Functions share one identity User-assigned MSI
Calling external APIs (Stripe, SendGrid, etc.) Key Vault + app secrets
Existing code using AzureServiceTokenProvider Migrate to DefaultAzureCredential
New development DefaultAzureCredential from the start

Our analysis

The win here is operational. Secrets stored in app settings are a common leak vector in cloud setups, and rotating them is a chore that often gets deferred until an incident forces the issue. Moving to a system-assigned identity means there is nothing to rotate, nothing to leak, and nothing for a secret scanner to flag in the repository. Azure handles credential lifecycle automatically.

The trade-offs are worth naming. MSI only works for Azure-to-Azure calls. Anything that leaves Azure—third-party SaaS APIs, legacy on-prem services—still needs traditional credentials. MSI also locks the identity to one resource. For shared identities across multiple Functions, a user-assigned managed identity is the right next step, though it requires a separate resource to provision and manage.

Compared with the usual alternative—client ID plus secret stored in Azure Key Vault—MSI is strictly simpler for the Azure-to-Azure case. Key Vault still earns its keep for the long tail of external credentials and for scenarios where audit logging on access is required.

What to watch next

Microsoft's direction is clearly toward DefaultAzureCredential, which unifies the token-fetching chain under one credential type across the modern Azure SDKs. AzureServiceTokenProvider is now in maintenance mode. Watch for deprecation notices and plan migration when SDKs in use bump to versions that drop support. For anything multi-resource or shared, adopt a user-assigned identity pattern from day one rather than retrofitting it later.

Key takeaways

  • Enable system-assigned managed identity on the Function and drop the client secret entirely.
  • Use DefaultAzureCredential in new code; treat AzureServiceTokenProvider as legacy.
  • Reserve Key Vault for credentials that leave Azure, not for Azure-to-Azure auth.

Frequently asked questions

Do I still need an Azure AD app registration for the Web API?

Yes. The Web API still needs its own registration so Azure AD knows the audience and authority for issuing tokens. MSI only removes the secret from the calling side.

What happens to the managed identity if I delete the Function?

Azure automatically deletes the service principal and its credentials. Nothing to clean up manually.

Can I use MSI to call third-party APIs like Twitter or Stripe?

No. MSI only works with Azure AD-protected resources. External APIs still need their own keys or tokens, ideally stored in Key Vault.

Is AzureServiceTokenProvider still supported?

It is in maintenance mode. Microsoft recommends DefaultAzureCredential for new development across the modern Azure SDKs.

What is the difference between system-assigned and user-assigned identities?

A system-assigned identity is tied to one resource and dies with it. A user-assigned identity is a standalone resource that can be attached to multiple services, which is the right choice for shared workloads.

Related