Use managed identity instead of connection strings
Connection strings with keys end up in config files, pipelines and screenshots. Managed identity lets your Azure app authenticate with no secret at all.
Most Azure services hand you a connection string with an access key in it. It works on the first try, which is exactly why so many apps still use them. The problem is what happens next: the key gets copied into appsettings.json, a pipeline variable, a teammate's local config and, eventually, a commit. Rotating it means finding every copy.
Managed identity removes the secret entirely. Your App Service, Function App or container gets an identity in Microsoft Entra ID, and Azure handles the tokens for you.
How it works
- Turn on a managed identity for the app (system-assigned is the simplest: it's created and deleted with the app).
- Grant that identity a role on the resource it needs, such as Azure Service Bus Data Sender on a namespace or Storage Blob Data Contributor on a storage account.
- In code, connect using the resource's address and a credential instead of a connection string.
The code change is small
using Azure.Identity;
using Azure.Messaging.ServiceBus;
using Azure.Storage.Blobs;
var credential = new DefaultAzureCredential();
var serviceBus = new ServiceBusClient("my-namespace.servicebus.windows.net", credential);
var blobs = new BlobServiceClient(new Uri("https://mystorage.blob.core.windows.net"), credential);
Register these as singletons and inject them as usual. Nothing else in your code needs to know how they authenticate.
Local development still works
DefaultAzureCredential tries a chain of sources. In Azure it uses the managed identity. On your machine it falls back to the account you're signed in with in Visual Studio, VS Code or the Azure CLI (az login). Give your own account the same roles on a development resource and the same code runs everywhere.
In production, some teams prefer to use ManagedIdentityCredential directly. It skips the chain, starts a little faster and makes it obvious what the app is meant to use.
Azure Functions triggers too
Function triggers and bindings support identity-based connections. Instead of a connection string setting, you give the namespace:
{
"ServiceBusConnection__fullyQualifiedNamespace": "my-namespace.servicebus.windows.net"
}
The trigger uses the Function App's managed identity, as long as it has the matching data role.
Things that trip people up
- Roles take a few minutes to apply. A
401right after a new role assignment usually just means it hasn't propagated yet. - Management roles aren't data roles.
Contributorlets you manage a storage account, not read its blobs. You need theDataroles. - Grant the smallest scope that works. A single queue or container is better than the whole subscription.
Takeaway
If a resource supports Entra ID authentication, and most Azure data services do, use a managed identity and a data role instead of a key. There's nothing to leak, nothing to rotate, and the code change is a couple of lines.