Retrieve Secrets from Azure Key Vault Using Managed Identities in Dotnet 8
To retrieve Key Vault secrets in Dotnet, most tutorials will tell you to set up your appsettings.json file like this:
"KeyVault": {
"KeyVaultURL": "https://{Replace your KeyVaultName }.vault.azure.net/",
"ClientId": "{Your ClientId}",
"ClientSecret": "{Your Secret value}",
"DirectoryId": "{Your Directory / Tenant Id}"
},
But what’s the point of storing connection strings in Azure Key Vault if your client secret is publicly accessible in appsettings.json? In this tutorial, I’ll walk you through how to retrieve secrets from Key Vault using Azure Managed Identities and only the KeyVaultURL in appsettings.json.
Web App
There are two fundamental pillars for making this approach work. The first lies in turning on the system-assigned managed identity in your web app.
Once you create your web app in Azure, go to the Settings blade, then click Identity. In the System assigned tab, turn the status to On. This allows the web app to communicate with other Azure resources. At the top of this screen, it explains, “The managed identity is authenticated with Microsoft Entra ID, so you don’t have to store any credentials in code.” This method eliminates the need include ClientId, ClientSecret, or DirectoryId in in appsettings.json. Entra ID is already doing that for you.
Key Vault
Head over to Key Vault and create an instance. Choose “Vault access policy” as the Permission model. In the code below, I need to store two secrets: the connection string for my Azure storage account where I’m storing my images as blobs and another to store the metadata on each image. (Note: I could easily store the metadata as a CosmosDB document, but for the purposes of this demo, I want to show that you can programmatically access both an Azure resource and an external resource using Key Vault secrets).
In the Objects blade, click Secrets, then Generate/Import at the top. Use a descriptive name and then paste the corresponding connection string in the value field.
The second pillar for connecting Entra ID and Key Vault resides in Access policies. In your Key Vault instance, click the Access policies blade and create a new policy. I’m using the Key & Secret Management template and allowing all Key and Secret permissions.
Click next and in the Principal tab, search for the web app that you created earlier, giving the app permission to access your Key Vault instance.
Finish up the process and let’s get to coding.
Packages
Using the NuGet Package Manager or the CLI, install Azure.Identity and Azure.Security.KeyVault.Secrets. If you’re going to be connecting to Blob Storage or any other Azure resource, you’ll also want to install the necessary packages. For this code, I’m also using Azure.Storage.Blobs.
Code
In my appsettings.json file, all I need now is the URL to the Key Vault which you can find in the Overview blade of your Key Vault instance.
"KVUri": "https://in8-vault-kv.vault.azure.net/",
One line!
Now in Program.cs, retrieve the KeyVault Endpoints from appsettings.json.
var configuration = builder.Configuration;
var keyVaultEndpoint = configuration["KVUri"];
Create a default credential which will prompt an Azure login via one of the following authentication methods: Managed Identity, Environment variable, Visual Studio credentials, or the Azure CLI. (This means that if you have the Azure CLI installed, you could also register yourself as a user in the Key Vault access policies and retrieve the secrets without having to register a web app. Obviously, this wouldn’t work in production, but could be useful in development if you want to save on costs).
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var credential = new DefaultAzureCredential();
var secretClient = new SecretClient(new Uri(keyVaultEndpoint), credential);
Next, use the secretClient variable to get the secrets you stored in Key Vault. You need to first get the KeyVaultSecret object and then access the string value. Method chaining will throw an error.
KeyVaultSecret blobStorageSecret = secretClient.GetSecret("AzureMediaServiceStorage");
KeyVaultSecret mediaServiceSecret = secretClient.GetSecret("MediaServiceContext");
// Access the secret values
var blobStorageConnection = blobStorageSecret.Value;
var mediaServiceContext = mediaServiceSecret.Value;
Update the configuration with the secret strings.
configuration.AddInMemoryCollection(
new Dictionary<string, string?>
{
{ "BlobStorageConnection", blobStorageConnection },
{ "MediaServiceContext", mediaServiceContext }
}
);
//end of if statement
}
Add services to the container.
builder.Services.AddSingleton(new BlobServiceClient(configuration["BlobStorageConnection"]));
builder.Services.AddDbContext<FilesInBlobContext>(options =>
{
var mediaServiceContext = configuration["MediaServiceContext"];
options.UseSqlServer(mediaServiceContext);
});
Run the code and voilà! You used Managed Identities and Azure Key Vault to secretly store connection strings without having to precariously store ClientSecret, ClientId, or DirectoryId.
Be safe out there, y’all!