
The obvious solution to store secrets when running in an Azure environment is through the use of Azure Keyvault. But how does one authenticate with Keyvault without keeping credentials in code or configuration? This can be achieved using a managed identity. Managed identities are an Azure feature which provides Azure services with an automatically managed identity in Azure AD. Managed identities come in two different types, user assigned managed identities and system assigned managed identities, in this post we’ll be referring to system assigned managed identities whereby Azure automatically provisions the credentials to the application for us.
Create the Azure resources
We’ll first need a resource group and Keyvault instance to store our secrets. Let’s do this via the azure cli.
Set the Azure Subscription that you’ll be working in.
$ az login
$ az account list
$ az account set — subscription {subscription id}Now that we are in context of the desired subscription, create a new resource group to hold the Keyvault instance and the Keyvault instance itself.
$ az group create -n “demo-rg” -l australiaeast
$ az keyvault create -n “demo-kv” -g “demo-rg” -l australiaeastLet’s also add a secret to the new Keyvault instance for our application to use
$ az keyvault secret set — vault-name “demo-kv” — name “Secrets — Password” — value “super_secret”Note the syntax used as the secret name i.e. the double hyphen, this allows us to bind the secret to a Secrets object.
public class Secrets
{
public string Password { get; set; }
}Configuring the application to load secrets from Keyvault into configuration
First install the nuget package for the Keyvault IConfigurationProvider:
nuget install Microsoft.Extensions.Configuration.AzureKeyVaultAnd also the nuget package allowing us to authenticate with Keyvault using a managed identity.
nuget install Microsoft.Azure.Services.AppAuthenticationAdd the the Keyvault configuration provider to the configuration builder using the AzureServiceTokenProvider as shown below.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((ctx, builder) =>
{
var baseUrl = “https://{your-vault-name}.vault.azure.net/";
var tokenProvider = new AzureServiceTokenProvider(); var kvClient = new KeyVaultClient((authority, resource, scope) => tokenProvider.KeyVaultTokenCallback(authority, resource, scope)); builder.AddAzureKeyVault(baseUrl, kvClient, new DefaultKeyVaultSecretManager());
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Now that we have secrets merged into our IConfiguration, we can bind the Secrets object as shown in the following Startup class, and inject it where required.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<Secrets>(s => Configuration.Bind(“Secrets”, s));
services.AddControllers();
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Testing locally
dotnet includes a local secret manager, allowing us to store secrets outside of source control. However in a team environment when using the secret manager, you’ll still be required to share secrets with your teammates in some way across machines. Instead we can authenticate with Keyvault using our personal credentials by running az login on our development machine whereby your local azure profile will be used. Note that your AD user account must be granted “get” and “list” permission on the Keyvault instance.
Providing AppService access to Keyvault
When running in Azure we obviously won’t be using our local azure profile. We can use Azure Resource Manager (ARM) to provide an AppService instance access to our Keyvault instance. The following snippet demonstrates how to provide an AppService WebApp (and its staging slot) with “get” and “list” secrets permissions, note the identity type of the WebApp is set to “SystemAssigned”. This approach also allows us to follow the principle of least privilege whereby only those required to access and manage secrets can be granted access through an Azure AD group for example.
{
"name": "[variables('{your-web-apps-name}')]",
"type": "Microsoft.Web/sites",
"kind": "api",
"location": "[resourceGroup().location]",
"apiVersion": "2018–11–01",
"dependsOn": ["[concat('Microsoft.Web/serverfarms/', variables('{your-web-apps-plan-name}'))]"],
"identity": { "type": "SystemAssigned" },
... omitted for brevity ...
},
{
"type": "Microsoft.KeyVault/vaults",
"name": "[variables('keyVaultName')]",
"dependsOn": [
"[resourceId('Microsoft.Web/Sites', variables('{your-web-apps-name}'))]",
"[resourceId('Microsoft.Web/Sites/Slots', variables('{your-web-apps-name}'), 'Staging')]"
],
"apiVersion": "2015–06–01",
"location": "[resourceGroup().location]",
"properties": {
"enabledForDeployment": true,
"enabledForTemplateDeployment": true,
"enabledForDiskEncryption": true,
"tenantId": "[subscription().tenantId]",
"sku": { "name": "Standard", "family": "A" },
"enabledForDeployment": "true",
"enabledForTemplateDeployment": "true",
"enabledForVolumeEncryption": "true",
"accessPolicies":
[
{
"objectId": "[reference(concat('Microsoft.Web/Sites/', variables('{your-web-apps-name}')), '2018–02–01', 'Full').identity.principalId]",
"tenantId": "[subscription().tenantId]",
"permissions": {
"secrets": ["get", "list"]
}
},
{
"objectId": "[reference(concat('Microsoft.Web/Sites/', variables('{your-web-apps-name}'), '/Slots/Staging'), '2018–02–01', 'Full').identity.principalId]",
"tenantId": "[subscription().tenantId]",
"permissions": { "secrets": ["get", "list"] }
}
]
}
}Finishing notes
Ensuring secure credentials is a critical task, using Managed Identities in combination with Keyvault allows us to keep credentials secure, out of source control, off developer machines and also the ability to apply the principle of least privilege to secrets through Keyvault access policies.

