Using Key Vault references with Azure App Configuration
When working in Azure, storing secrets in Key Vault is a good idea. And to make it better, there’s the Key Vault Reference notation. This feature makes sure no one can read the secret(s) unless someone grants permission.
For storing configuration, values a different service is available, called Azure App Configuration.
Both services are excellent for storing & sharing the values of your cloud services.
Wouldn’t it be great to be able to combine the two? What I mean by that is to use Key Vault references inside your App Configuration. Well, you can!
There is some work involved as you need to set up access to Key Vault from within the application.
Key Vault References in App Configuration
If you’re using the Azure Portal, it’s easy to add a new Key Vault reference in the App Configuration.
Head to the Configuration Explorer and press the Create button.
It will bring a small blade to the side of the screen from which you can add the secret with an appropriate name.
Having finished this, press the Apply button, and you’ll see the reference added and visible in the Configuration Manager.
Configuring Key Vault access
The initial setup
If you have been using App Configuration without Key Vault references, you probably have some code to set up the App Configuration connection, as mentioned in the docs (this is an ASP.NET Core example)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.ConfigureAppConfiguration(config =>
{
var settings = config.Build();
var connection = settings.GetConnectionString("AppConfig");
config.AddAzureAppConfiguration(connection);
}).UseStartup<Startup>());
If so, after having added the Key Vault reference, there’s a pretty big chance it now throws the following exception.
No key vault credential or secret resolver callback configured, and no matching secret client could be found.
Digging deeper shows the following message:
Microsoft.Extensions.Configuration.AzureAppConfiguration.KeyVaultReferenceException
: ‘No key vault credential or secret resolver callback configured, and no matching secret client could be found.. ErrorCode:, Key:MySecret, Label:, Etag:Ag5GDZo0EnQXmi6UTDt1M8PZhTY, SecretIdentifier:https://my-vault.vault.azure.net/secrets/MySecret’
Solving the exception
As it happens, the code for accessing App Configuration doesn’t give your application permission to retrieve secrets from Key Vault. For me, this was somewhat unexpected as I figured it would use my Azure Managed Identity. It turns out you need to set this up by yourself and use the ConfigureKeyVault
method.
While I understand the reasoning behind this, it was somewhat unexpected.
The documentation uses the DefaultAzureCredential
object without further configuration. Using the defaults is fine unless you’re a consultant who has access to multiple tenants (like me).
When working on a local machine, making sure to use the correct tenant is critical. If you forget to do this, you’ll probably get an Unauthorized response.
Service request failed. Status: 401 (Unauthorized)
Content:
{"error":{"code":"Unauthorized","message":"AKV10032: Invalid issuer. Expected one of https://sts.windows.net/a9881c2b-9e85-469c-b89e-53dd43c868a3/, https://sts.windows.net/80dded40-1028-4be9-a9aa-543b26ed0572/, found https://sts.windows.net/ca14ab91-1205-404b-b234-4580f68f479f/."}}
Using the DefaultAzureCredential
is still viable, but you need to specify a couple of options. Please take a look at the code below.
config.AddAzureAppConfiguration(options =>
{
ConfigureOptions(options, appConfigUri);
}
private static void ConfigureOptions(AzureAppConfigurationOptions options, Uri appConfigEndpoint)
{
var credentials = GetAzureCredentials();
options.Connect(appConfigEndpoint, credentials);
options.ConfigureKeyVault(kv => kv.SetCredential(credentials));
}
private static TokenCredential GetAzureCredentials()
{
var isDeployed = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"));
return new DefaultAzureCredential(
new DefaultAzureCredentialOptions
{
// Prevent deployed instances from trying things that don't work and generally take too long
ExcludeInteractiveBrowserCredential = isDeployed,
ExcludeVisualStudioCodeCredential = isDeployed,
ExcludeVisualStudioCredential = isDeployed,
ExcludeSharedTokenCacheCredential = isDeployed,
ExcludeAzureCliCredential = isDeployed,
ExcludeManagedIdentityCredential = false,
Retry =
{
// Reduce retries and timeouts to get faster failures
MaxRetries = 2,
NetworkTimeout = TimeSpan.FromSeconds(5),
MaxDelay = TimeSpan.FromSeconds(5)
},
// this helps devs use the right tenant
InteractiveBrowserTenantId = DefaultTenantId,
SharedTokenCacheTenantId = DefaultTenantId,
VisualStudioCodeTenantId = DefaultTenantId,
VisualStudioTenantId = DefaultTenantId
}
);
}
When running locally, we can use the logins specified in the development environments.
The DefaultTenantId
makes sure we’re logging in on the correct tenant, and we’re accessing Key Vault using the same credentials for both App Configuration and Key Vault.