Azure App Configuration and KeyVault
Recently I started on a new project and therefor a whole new application. It’s build using .NET Core and like many apps, it needs some configuration that varies between deployments. Azure now has a service called Azure App Configuration that allows you to store and manage your configuration. Combined with Azure KeyVault to store your secrets, we get configuration management nearly for free. Let’s dive in!
Setup using ARM
We fist need a few resources in Azure to make it all work. Here’s the ARM template to create the Azure App Configuration instance.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appConfigurationName": {
"type": "string"
},
"skuName": {
"type": "string",
"defaultValue": "free"
},
"location": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.AppConfiguration/configurationStores",
"apiVersion": "2019-11-01-preview",
"name": "[parameters('appConfigurationName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('skuName')]"
},
"properties": {
"encryption": {}
}
}
]
}
Next, we need an ARM template to create the KeyVault:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"keyVaultName": {
"type": "string"
},
"accessPolicies": {
"defaultValue": {
"list": []
},
"type": "object"
},
"location": {
"type": "string"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2016-10-01",
"name": "[parameters('keyVaultName')]",
"location": "[parameters('location')]",
"properties": {
"sku": {
"family": "A",
"name": "standard"
},
"accessPolicies": "[parameters('accessPolicies').list]",
"tenantId": "<tenantID>",
"enabledForDeployment": false,
"enabledForDiskEncryption": false,
"enabledForTemplateDeployment": true
}
}
]
}
Permissions
To be able to read from both App Configuration and KeyVault using Managed Identities we need to set some permissions. For App Configuration this is done under ‘Access Control (IAM)’. Add yourself with the ‘App Configuration Data Reader’-role. On KeyVault this is done using policies on the ‘Access Policies’-blade. Add yourself with at least the permission to Get and List secrets.
Adding a simple config item
Using the Azure portal, navigate to the Azure App Configuration you just created using either the portal or the ARM template and find the ‘Configuration Explorer’ blade. Here you can add a new item by clicking the Create button and choose Key-Value. Use ‘TestApp:Settings:BackgroundColor’ as the Key and ‘Bleu’ as its value. Leave the Label and Content type blank for now.
Read the configuration in code
To make our app read the configuration we need just a few steps. If you are following along without any app you could simply create a new .NET Core Api running ‘dotnet new webapi’ on the command-line. First, we need to install a NuGet package.
dotnet add package Microsoft.Azure.AppConfiguration.AspNetCore
This one gives you the ‘AddAzureAppConfiguration’ from the code below. In this example, I’m using the Managed Identities to get access to both Azure App Configuration and KeyVault. There are other ways to do that like using keys or connectionstrings but I prefer this easy and secure way of working. To use the Managed Identities we need to install another NuGet. I’ve you are using Visual Studio Code like I am, you will need a preview version of this package. I tried the latest preview version but that one did not work…
dotnet add package Azure.Identity --version 1.2.0-preview.1
With both packages installed we can now add the following code to our Program.cs
public static IWebHostBuilder CreateHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
var credentials = new DefaultAzureCredential();
config.AddAzureAppConfiguration(options =>
{
options.Connect(new Uri(settings["AppConfig:Endpoint"]), credentials)
.ConfigureKeyVault(kv =>
{
kv.SetCredential(credentials);
});
});
})
.UseStartup<Startup>();
Before running the application we first need to login to Azure using the Azure CLI. By doing that, the DefaultAzureCredential provider will use that account to get an authenticated token to access both App Configuration and KeyVault. Use the following commands:
az login
az account set --subcription <subcriptionName>
When you created a new ASP.Net Core API you will probably have a WeatherForecastController. Open that and inject IConfiguration in the constructor like so:
private readonly ILogger<WeatherForecastController> _logger;
private readonly IConfiguration _configuration;
public WeatherForecastController(ILogger<WeatherForecastController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
Now add the following line to one of the functions and run the app. You will see that this will return ‘Blue’.
var color = _configuration["TestApp:Settings:BackgroundColor"];
Config per environment
Most of the applications we build will be deployed to different environments and therefore need different config. The connectionstring to a database for example will most likely be different. Luckily we can also handle those cases in Azure App Configuration by adding labels to a config item. Let’s go back to the App Configuration in the portal, click the three dots on the right on the item we just created and click ‘Add value’. Add a value of ‘Green’ and use ‘Test’ as the label. You will now have these two values:
We have to make a small change to our code to make the application fetch the correct values as shown below. It now first fetches the items without a label and then the ones with the label that has the name of the environment we currently run on. When it finds items with the same key, those will be overwritten. Change your hosting environment in the launch.json file (Visual Studio Code) and run the app. The value of the config item should now be ‘Green’.
public static IWebHostBuilder CreateHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
var credentials = new DefaultAzureCredential();
config.AddAzureAppConfiguration(options =>
{
options.Connect(new Uri(settings["AppConfig:Endpoint"]), credentials)
// Load configuration values with no label
.Select(KeyFilter.Any, LabelFilter.Null)
// Override with any configuration values specific to current hosting env
.Select(KeyFilter.Any, hostingContext.HostingEnvironment.EnvironmentName)
.ConfigureKeyVault(kv =>
{
kv.SetCredential(credentials);
});
});
})
.UseStartup<Startup>();
Getting a SQL ConnectionString
Some of your configuration, especially secrets, should not be stored in App Configuration but in a secure vault like KeyVault. We can however use App Configuration to get to the values in KeyVault. The benefit of that is that we can have a separate instance of KeyVault for test and a separate one for production but still have one entry-point from the app, being Azure App Configuration. We can then still benefit from having labels in App Configuration to split environments. Let’s go over to KeyVault and add a secret. Open the Secrets blade and add a secret with the name ‘SqlConnectionString’. Now that we’ve done that it’s time to reference that from Azure App Configuration. Add another item there using the Configuration Explorer and choose ‘Key Vault Reference’. Use ‘ConnectionStrings:SqlConnectionString’ as the key, select your vault and the key we just created. By using ‘:’ we are actually using a hierarchy in our keys. ASP.Net will treat that the same as if you would have written the following in your appsettings.json file
"ConnectionStrings": {
"SqlConnectionString": ""
},
And that is pretty cool because that means we can now use ‘var connection = Configuration.GetConnectionString(“SqlConnectionString”);’ to get that value just like we were used to!