Introduction
In this post, I will describe how to set up and use an Azure key vault to store your secret values.
Sometimes, we see secrets like storage keys and connection strings written as literals in the code of a project, such as:
public static class Secrets
{
public const string ApiKey = "MyAppKey";
}
This doesn’t seem too bad because:
- It is the fastest way to obtain a key
- Probably the key won’t change too often in time
But there are some serious drawbacks to this way of working as well:
- If the key does change, code needs to be adapted and redeployed.
- The key is plain visible in the code.
- The key is “for ever” in the source code system, maybe even on a public repository.
- When you change the environment (from DEV to ACC to PROD), the key will probably change as well. This becomes a problem with a hard-coded key.
It would be nice to store the key elsewhere, but what are the options?
- The key can be stored in a configuration file. This is better already, but this file will still be readable by developers (and on the public repo).
- The key can be stored in Azure. This is what we’re going to talk about in this article.
Prerequisites for this Article
If you want to follow along with the examples, you’ll need an Azure subscription. On the Azure home page, you can find the steps to create a free subscription, that will be valid for 3 months.
Introducing Azure Key Vault
We can store the following items in a Key Vault, for later use:
- Secrets - A lot of types of data can be stored here, such as tokens, passwords, keys, …
- Keys - Encryption keys can go here, and can be references later to encrypt / decrypt your data.
- Certificates
These items are stored securely in the vault, only users (or processes) with the right access rights will be able to retrieve them. This access is monitored, so you can know who accessed what, and how the performance of the Key Vault is.
Creating an Azure Key Vault
In the Microsoft Azure Portal
- Click on the “Create a resource” button at the top left.
- In the blade that appears, enter “Key Vault” in the search box and select “Key Vault” from the list below.
Click “Create” and fill in the necessary parameters:
- Name: A unique name for the key vault
- Subscription: The subscription that will contain your key vault
- Resource group: Here, you can either select an existing resource group or create a new one. For this example, you may want to create a new resource group so you can clean up everything easily when you are done “playing”.
- Location
- Pricing tier: Standard, unless you want HSM backed keys.
- Access policies: By default, the current user will be the owner of the key vault. You can add or remove permissions here.
- Click on “Create” and the key vault will be created for you. This can take some time.
Inserting Values in the Key Vault
- Find your new key vault in Azure, and click on it. If your subscription contains a lot of objects, you may first select the resource group that the key vault is in.
- You now see the overview page, with some useful information.
- The main important piece of information here is the DNS Name (top right). You will need this to connect to the key vault from your code.
- You can also see the number of requests, the average latency, and the success ratio.
- Pro tip: Make a note of the average latency as a baseline value for future requests.
- On the left side, click on “Secrets”. You will see all the currently stored secrets. If you just created the key vault, this will be empty.
- Click on “Generate/Import” to create a new secret:
- Upload options: Manual
- Name: Password (for our example)
- Value: My Secret
- Content type: Leave this empty
- If you wish, you can also set an activation date and an expiration date for this secret. We will leave this empty for our example.
- Make sure that “enabled” is set to yes and click “Create”.
When you click on the “Secrets” button on the left again, you will now see an entry for this key.
If you prefer to do this by scripting, the next section is for you.
Setting Up the Key Vault Using Azure Cloud Shell
Using a script to create an Azure object makes it repeatable. If you have multiple tenants, you can compose a script that will create the necessary objects for each tenant. This will save you time because:
- Obviously, executing a script is faster than creating each object by hand
- Consistency. If everything is scripted, you can be sure that all the objects are created the same for each tenant. This can save you hours of finding configuration bugs.
- You can keep the scripts in source control, which allows you to version them as well.
Open Cloud Shell
At the top, click the “Cloud Shell” icon. If this is the first time that you open the cloud shell, a wizard will be shown to set up the shell. You can choose the scripting language to use (PowerShell or Linux Bash), and then Azure will create some storage for you. There is also a fair warning that the storage will cost you some money.
For this example, I will use Linux Bash.
RESOURCE_GROUP='CodeProject'
LOCATION='WestEurope'
KEY_VAULT='CPKeyVault666'
az group create --name $RESOURCE_GROUP --location $LOCATION
az keyvault create --resource-group $RESOURCE_GROUP --name $KEY_VAULT
az keyvault list
az keyvault secret set --vault-name $KEY_VAULT --name Password --value 'My Secret'
az keyvault secret list --vault-name $KEY_VAULT
az keyvault secret show --vault-name $KEY_VAULT --name Password --query value --output tsv
Using Azure Key Vault in Your .NET Project
Project Setup
Using Visual Studio 2019, create a new .NET Core Console App, name it ‘KeyVault
’.
NuGet Packages
To use Azure Key Vault, you’ll first need to add 2 NuGet packages to your project:
Microsoft.Azure.KeyVault
Microsoft.Azure.Services.AppAuthentication
Open the “Package Manager Console” (Tools > NuGet Package Manager > Package Manager Console…) and type the following statements:
install-package Microsoft.Azure.KeyVault
install-package Microsoft.Azure.Services.AppAuthentication
In your source file, you will need the following using
statements:
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
Reading a String From the Key Vault
To separate the concerns in the application, it is best to create a separate class for this, such as:
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Azure.Services.AppAuthentication;
namespace KeyVault
{
public class KeyvaultUtilities : IKeyvaultUtilities
{
private readonly IKeyVaultClient _keyVaultClient;
private readonly string _vaultBaseUrl;
public KeyvaultUtilities(string keyvaultName)
{
_vaultBaseUrl = $"https://{keyvaultName}.vault.azure.net";
AzureServiceTokenProvider azureServiceTokenProvider =
new AzureServiceTokenProvider();
_keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback
(azureServiceTokenProvider.KeyVaultTokenCallback));
}
public async Task<string> GetSecretAsync(string keyname)
{
try
{
var secret = await _keyVaultClient.GetSecretAsync(_vaultBaseUrl, keyname)
.ConfigureAwait(false);
return secret.Value;
}
catch (KeyVaultErrorException kvex)
{
throw new KeyNotFoundException
($"Keyname '{keyname}' does not seem to exist in this key vault", kvex);
}
}
}
}
The purpose is to read a secret from the key vault, so that is the only method that I have implemented. You can add other key vault related methods in the class when needed.
Using this class is easy. Instead of passing the key vault name as a string
, you may get it from a settings file. That will also allow you to travel easily through your development environments.
Notice that we never created a secret with a name “xyz
”. Trying to retrieve this value will throw a KeyNotFoundException
.
using System;
using System.Threading.Tasks;
namespace KeyVault
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
IKeyvaultUtilities util = new KeyvaultUtilities("cpkeyvault666");
string pwd = await util.GetSecretAsync("Password");
Console.WriteLine("Password: " + pwd);
string xyz = await util.GetSecretAsync("xyz");
Console.WriteLine("xyz: " + pwd);
}
}
}
Cleanup in Azure
On the Azure Portal, go back to the Cloud Shell. Delete the ‘CodeProject
’ resource group:
RESOURCE_GROUP='CodeProject'
az group delete --name $RESOURCE_GROUP --yes
This will delete the ‘Codeproject
’ resource group, with all of its contents. Don’t worry if you don’t perform this step, the key vault only costs you a wobbling 3 cents per 10000 operations. You can calculate your costs here: https://azure.microsoft.com/en-us/pricing/calculator/.
You can also delete the resource group through the Azure portal.
First Retrieval of the Secret Can Be (very) Slow
Retrieving the first key can take several seconds. If you are not sure that you will always need a secret from the key vault, you may consider using the class Lazy<T>.
The next retrievals are fast.
For this reason, you may consider to register the KeyVaultUtilities
as a singleton and inject it instead of recreating it each time. How you do this will depend on the type of application that you are creating.
References