Securing credentials in Azure-hosted applications using managed identities and Azure Key Vault - Implementing managed identities
In this post, we'll discover how to enhance the security of our web applications hosted on Azure by leveraging managed identities.
We are considering a scenario where a web application (such as a Blazor app) is hosted in Azure App Services and needs to access other Azure-native resources (e.g., Azure Key Vault, Azure SQL Database or Azure Storage). In this post, we’ll focus specifically on this intra-Azure communication scenario.
The case where the application must interact with resources hosted outside Azure (such as third-party APIs or on-premises services) will be addressed in the next post.
Throughout this post, to illustrate our concepts, we will continue working with the Blazor application we developed in the previous post.
What are managed identities ?
As stated in the introduction, we are dealing with a scenario where all the resources are hosted within the Azure ecosystem. This setup simplifies our task, as we can fully leverage the features provided by the platform, particularly Microsoft Entra ID.
What is Microsoft Entra ID ?
Microsoft Entra ID is Microsoft’s cloud-based identity and access management (IAM) service. It is part of the broader Microsoft Entra product family, which focuses on securing access across hybrid and multicloud environments.
Of course, Azure is built upon this technology (each time a new tenant is created in Azure, a Microsoft Entra ID directory is automatically provisioned and associated with it). Microsoft Entra ID verifies user identities when signing into applications or services and controls what authenticated users can access and do within a resource (Microsft Entra ID ultimately acts as a directory service—an authoritative registry that defines who has access to what, and under which role or permission set).
Who | What | Role |
---|---|---|
Nicolas DESCARTES | mystorage (Azure Storage) | Contributor |
John Doe | mydatabase (Azure SQL Database) | Contributor |
Nicolas DESCARTES | mydatabase (Azure SQL Database) | Reader |
... | ... | ... |
But Microsoft Entra ID goes beyond simply registering users and their roles. It can be used in a more abstract and powerful way by defining which Azure resource is allowed to access another Azure resource and under what role. In other words, it's not limited to users—it also applies to non-human identities, such as applications, functions, or virtual machines. This enables fine-grained access control across Azure services, making it a central pillar of secure, identity-driven resource management.
Who | What | Role |
---|---|---|
Nicolas DESCARTES | mystorage (Azure Storage) | Contributor |
John Doe | mydatabase (Azure SQL Database) | Contributor |
Nicolas DESCARTES | mydatabase (Azure SQL Database) | Reader |
myazurefunction (Azure Function) | mydatabase (Azure SQL Database) | Contributor |
In this context, Microsoft Entra ID can, for example, grant an Azure Function permission to access an Azure SQL Database.
Why it matters in Azure ?
When all resources (like web apps, databases, storage accounts) are hosted in Azure, Microsoft Entra ID then becomes the central service to securely manage access between them—without exposing sensitive credentials.
This Azure feature, which automatically grants an application or service access to another Azure resource, is known as a managed identity.
How can a managed identity be implemented in Azure in practice ?
We will revisit the example from the previous post, with the goal of connecting our Blazor application to the storage account without relying on any explicit access keys.
Enabling managed identity for our app service
The first step is to enable the managed identity for the service that requires access (in our case, the Blazor application). To do this:
We navigate to our service in the Azure Portal.
In the left-hand menu, we select Identity under the *Settings *section.
Under the System assigned tab, we toggle the Status to On.
Granting our Blazor app's managed identity access to the storage account
The second step involves granting our managed identity access to any service it depends on. To do this:
In the Azure Portal, we navigate to the storage account we want our app to access (or any other resource).
From the left-hand menu, we click Access control (IAM).
We click Add > Add role assignment.
In the Role dropdown, we choose a role that fits the required level of access. In our case, we will choose Storage Blob Data Contributor (for read/write access).
Under Assign access to, we choose Managed identity.
Under Managed identity type, we select App Service and then choose our app from the list.
Deploying our app to Azure
The final step is to update our Blazor application accordingly and redeploy it to Azure. To do this:
- Modify the appsettings.json file.
1{
2 "Logging": {
3 "LogLevel": {
4 "Default": "Information",
5 "Microsoft.AspNetCore": "Warning"
6 }
7 },
8 "AllowedHosts": "*",
9 "StorageConnectionString": "https://eocsmanagedidentity.blob.core.windows.net"
10}
We can observe that credentials no longer appear in the configuration file: we are only mentioning the storage's url.
- Modify the BlocService.cs class.
1public class BlobService
2{
3 private readonly string _connectionString;
4 private readonly string _containerName;
5
6 public BlobService(IConfiguration config)
7 {
8 _connectionString = config["StorageConnectionString"];
9 _containerName = "eocs";
10 }
11
12 public async Task UploadTextFileAsync(string fileName, string content)
13 {
14 var credential = new DefaultAzureCredential();
15 var client = new BlobServiceClient(new Uri(_connectionString), credential);
16
17 var container = client.GetBlobContainerClient(_containerName);
18 await container.CreateIfNotExistsAsync();
19
20 var blob = container.GetBlobClient(fileName);
21 var bytes = Encoding.UTF8.GetBytes(content);
22 using var stream = new MemoryStream(bytes);
23 await blob.UploadAsync(stream, overwrite: true);
24 }
25}
We use DefaultAzureCredential (from Azure.Identity) which automatically picks up the managed identity when running in Azure.
- Deploy the code in Azure.
This action can be done from Visual Studio.
Testing the app
But now my code no longer works in the local environment. How can I configure Visual Studio to ensure it still functions properly ?
This section is specific to Visual Studio. For other IDEs, the code may need to be adjusted accordingly.
That's an excellent point ! Indeed, if we run our program in debug mode using Visual Studio and attempt to upload a file, an error will occur.
But does that mean we can no longer work with our favorite IDE? Absolutely not!
Remember that in the BlobService.cs class, we utilized the DefaultAzureCredential class, which by default leverages managed identities to authenticate and connect to Azure resources. It turns out that this class can be configured to authenticate using other methods as well.
A suitable solution would thus be to use managed identities when the application is hosted on Azure, and to fall back on Visual Studio credentials when running in debug mode locally. We will proceed by following these steps.
- Edit the code
We will configure the runtime to use Visual Studio credentials when running in debug mode, and to switch to managed identities in all other environments.
1public class BlobService
2{
3 private IConfiguration _configuration;
4 private readonly string _connectionString;
5 private readonly string _containerName;
6
7 public BlobService(IConfiguration config)
8 {
9 _configuration = config;
10 _connectionString = config["StorageConnectionString"];
11 _containerName = "eocs";
12 }
13
14 public async Task UploadTextFileAsync(string fileName, string content)
15 {
16 var environment = _configuration["ASPNETCORE_ENVIRONMENT"];
17 var options = new DefaultAzureCredentialOptions();
18 if (environment == "Development")
19 {
20 // If we are in debug mode, we use Visual Studio credentials.
21 options.ExcludeAzureCliCredential = true;
22 options.ExcludeAzureDeveloperCliCredential = true;
23 options.ExcludeAzurePowerShellCredential = true;
24 options.ExcludeEnvironmentCredential = true;
25 options.ExcludeInteractiveBrowserCredential = true;
26 options.ExcludeManagedIdentityCredential = true;
27 options.ExcludeSharedTokenCacheCredential = true;
28 options.ExcludeWorkloadIdentityCredential = true;
29 }
30 else
31 {
32 // Otherwise, we use managed identities.
33 options.ExcludeAzureCliCredential = true;
34 options.ExcludeAzureDeveloperCliCredential = true;
35 options.ExcludeAzurePowerShellCredential = true;
36 options.ExcludeEnvironmentCredential = true;
37 options.ExcludeInteractiveBrowserCredential = true;
38 options.ExcludeSharedTokenCacheCredential = true;
39 options.ExcludeWorkloadIdentityCredential = true;
40 options.ExcludeVisualStudioCredential = true;
41 }
42
43 var credential = new DefaultAzureCredential(options);
44 var client = new BlobServiceClient(new Uri(_connectionString), credential);
45
46 var container = client.GetBlobContainerClient(_containerName);
47 await container.CreateIfNotExistsAsync();
48
49 var blob = container.GetBlobClient(fileName);
50 var bytes = Encoding.UTF8.GetBytes(content);
51 using var stream = new MemoryStream(bytes);
52 await blob.UploadAsync(stream, overwrite: true);
53 }
54}
- Register the credentials
Simply adding this code is not sufficient: Visual Studio itself must also be granted access to the storage account. To achieve this, we need to explicitly authorize our development environment in Azure, so that it can securely and confidently connect to the resource.
- In Visual Studio, go to Tools > Options.
- Search Azure Service Authentication > Account selection in the dialog box.
- We authenticate with the Azure account that holds ownership of the target resources.
- In the Azure portal, provide this account with access rights to the storage account (with the Storage Blob Data Contributor).
This solution is by no means universal and could likely be adapted and improved for greater effectiveness. Furthermore, we have received feedback indicating that it is not entirely reliable and may occasionally fail under certain configurations, such as when multi-factor authentication is enabled.
In such cases, a viable solution is to use access keys during debug mode and switch to managed identities when deploying the code to production.
In the final post, we’ll explore how to eliminate the need to store credentials directly in configuration files when accessing resources outside of Azure. Enter Azure Key Vault !