Use Azure Key Vault in .NET Core Web Application

Use Azure Key Vault in .NET Core Web Application

Let’s say we have a web application, which uploads the files to Azure Blob. The web application holds storage account name, blob container name and the access key for the storage accounts.

Ideally, the web application should not hold the secrets. The secrets should not be checked in into the source control. So, we can keep the secrets in the key vault. But there is new issue if this is done. How can the web application authenticates itself to access the secret ?

Managed identities is the solution. Managed identities help to authenticate Azure service. So, this will work if the application is deployed in Azure. But before deploying this app to Azure, the app will be in development.

During development, development team would still need to access the key vault for debugging the issues or writing new code which is supposed to use secrets from vault.

So, in this article, we will see how to keep the secrets in key vault and then how to access them during development from the visual studio.

Before we begin

For following all steps in this article, we will need Azure Subscription. If you don’t have an Azure subscription, create a free account before you begin.

Visual Studio 2019 version 16.3 or later, or Visual Studio 2017 version 15.7 with the Web Development workload installed. Download it now.

Create Storage Account

In Azure Portal, go to Storage Accounts option from left navigation and then click on Add button. Below image shows the create storage account panel from Azure portal.

Create storage account in Azure portal
Create storage account in Azure portal

Create Web App

Let’s create a .NET Core web application (MVC) using Visual Studio. This application will have a file upload control on home page and a FileUploadController which validates the files and upload them to Azure blob.

We need to add Azure.Storage.Blobs package to this web application.

Code from Index.cshtml :

@{
ViewData["Title"] = "Home Page";
}
<h2 class="display-4">Welcome</h2>
<br />
<br />
<form method="post" enctype="multipart/form-data" asp-controller="FileUpload" asp-action="Index">
<div class="card">
<div class="card-header">
<strong> Upload one or more files</strong>
</div>
<div class="card-body">
<div class="form-group row">
<div class="col-md-10">
<input type="file" name="files" multiple />
</div>
</div>
</div>
<div class="card-footer">
<div class="form-group row">
<div class="col-md-10">
<input type="submit" class="btn-primary" value="Upload to server" />
</div>
</div>
</div>
</div>
</form>
view raw Index.cshtml hosted with ❤ by GitHub

Code from FileUploadController.cs

public class FileUploadController : Controller
{
private readonly StorageConfig storageConfig;
public FileUploadController(StorageConfig storageConfig)
{
this.storageConfig = storageConfig;
}
[HttpPost("FileUpload")]
public async Task<IActionResult> Index(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
string fileNamePattern = @"^[\w\-. ]+$";
var filePaths = new List<string>();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
bool shouldProcess = Regex.IsMatch(formFile.FileName, fileNamePattern);
if (shouldProcess)
{
filePaths.Add(formFile.FileName);
await UploadFileToStorage(formFile.OpenReadStream(), formFile.FileName);
}
}
}
return Ok(new { message = "Files uploaded to Blob", count = files.Count, size, filePaths });
}
private async Task<bool> UploadFileToStorage(Stream fileStream, string fileName)
{
//// STEP 1: Create Blob URI
string blobHost = $"https://{storageConfig.AccountName}.blob.core.windows.net/";
string containerFilePart = $"{storageConfig.ContainerName}/{fileName}";
Uri blobUri = new Uri($"{blobHost}{containerFilePart}");
// STEP 2: Storage Credentials
StorageSharedKeyCredential storageCredentials
= new StorageSharedKeyCredential(storageConfig.AccountName,
storageConfig.Key);
// STEP 3: Upload using BlobClient object
BlobClient blobClient = new BlobClient(blobUri, storageCredentials);
await blobClient.UploadAsync(fileStream);
return await Task.FromResult(true);
}
}

Our application has three secrets, which are currently stored in appsettings.json:

  • AccountName, which holds the name of storage account
  • ContainerName, which holds name of blob container
  • Key, which holds the access key

Below is how the appsettings.json should look like:

{
"AccountName": "<<your-storage-account>>",
"ContainerName": "<<your-sample-container>>",
"Key": "<<xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>>"
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
view raw appsettings.json5 hosted with ❤ by GitHub

Code from StorageConfig.cs:

public class StorageConfig
{
private readonly IConfiguration configuration;
public StorageConfig(IConfiguration configuration)
{
this.configuration = configuration;
}
public string AccountName
{
get { return this.configuration["AccountName"]; }
}
public string ContainerName
{
get { return this.configuration["ContainerName"]; }
}
public string Key
{
get { return this.configuration["Key"]; }
}
}
view raw StorageConfig.cs hosted with ❤ by GitHub

Below is the code from startup to initialize that class from appsettings.json values:

services.AddSingleton<StorageConfig>(new StorageConfig(Configuration));
view raw Startup.cs hosted with ❤ by GitHub

If this application is run, it should be able to upload the documents to configured blob container. Next, let’s try to move these 3 values to key vault.

Create key vault

Follow steps in my blog post to create the key vault.

Below is the script which shows how to create the key vault and how to add secrets to it.

## login to Azure subscription
az login
## Create resource group
az group create –name "SampleRG" –location westindia
## Create key vault with name SampleKV
az keyvault create –name "SampleKV" –resource-group "SampleRG" –location eastus
## Store secret using CLI
az keyvault secret set –vault-name "SampleKV" –name "ExamplePassword" –value "somepassword"
## show the secret
az keyvault secret show –name "ExamplePassword" –vault-name "SampleKV"
## Delete resource group
az group delete –name SampleRG
view raw commands.sh hosted with ❤ by GitHub

Add Secrets using Portal

Let’s also see how to add secrets using Azure portal.

Login to Azure Portal and open the key vault instance. Then select Secrets under Setting from left navigation of the key vault.

Key Vault Secrets
Key Vault Secrets

Next, click on Generate / Import button to add new keys / secrets. The new panel let’s you add secrets in the form of the name / value pairs.

In addition to name and value of secret, we can also set the activation date and expiration date on the secret. This dated values behavior makes the key rotation / replacement easy and hassle free.

There is one more input, enabled, which enables to enable or disable particular secret if something has to be disabled abruptly.

Panel to add secrets in key vault
Panel to add secrets in key vault

For the sake of this demo, add previously discussed secrets i.e. AccountName, ContainerName and Key values in the key vault.

Next, let’s see how to access these secrets from the Visual Studio.

Back in Visual Studio

So, let’s modify the solution so that the code is able to retrieve the secrets when it is executed in visual studio.

Add Connected Service

Right click on the project form solution explorer and then select Add -> Connected service option. It should present a screen as shown below.

Then select Secure Secrets with Azure Key Vault option. It will ask you to login. Login using the account which has Azure subscription.

Add connected service to the project
Add connected service to the project

On next panel, there is option to either create new key vault or use an existing one. Let’s select the existing vault which is created in previous step and then click on Add button to add the connected service.

Select existing key vault in Visual Studio
Select existing key vault in Visual Studio

Add NuGet Packages

In Solution Explorer, right-click on your project, and select Manage NuGet Packages. In the Browse tab, locate and install these two NuGet packages: 

Add App Configurations

New configurations should be added in Program.cs while creating the IHostBuilder instance. Below is the complete code from Program.cs :

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var keyVaultEndpoint = GetKeyVaultEndpoint();
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
private static string GetKeyVaultEndpoint() => "https://<<your-key-vault>&gt;.vault.azure.net";
}
view raw Program.cs hosted with ❤ by GitHub

Then remove secrets from appsettings.json.

Run and Verify

Now, when the application is executed in the visual studio, the application is still able to reach the blob container and upload the files. That means, our application is able to read the secrets from Key Vault.

I hope you enjoyed this article. Let me know your thoughts.

Leave a Reply