This article shows how Azure Key Vault could be used together with Azure Functions. The Azure Functions can use the system assigned identity to access the Key Vault. This needs to be configured in the Key Vault access policies using the service principal. By using the Microsoft.Azure.KeyVault and the Microsoft.Extensions.Configuration.AzureKeyVault nuget packages, defining direct references in the Azure Functions configuration is not required. The secrets can be read directly from the Key Vault. This also has the advantage of referencing only the secret and not the direct version of the secret. The latest version of the secret is used (depending on the cache)
Code: https://github.com/damienbod/AzureDurableFunctions
Posts in this series
- Using External Inputs in Azure Durable functions
- Azure Functions Configuration and Secrets Management
- Using Key Vault and Managed Identities with Azure Functions
- Waiting for Azure Durable Functions to complete
- Azure Durable Functions Monitoring and Diagnostics
- Retry Error Handling for Activities and Orchestrations in Azure Durable Functions
History
2021-03-07 Update packages and using DefaultAzureCredential for Azure Key vault access
2020-09-18 Updated Configuration, updated Nuget packages
The configuration is setup in the Startup class which inherits from the FunctionsStartup class. We use a string property AzureKeyVaultEndpoint which is used to decide if the Key Vault configuration should be used or not. For local development, Key Vault is not used, user secrets are used. For the Azure deployment, the AzureKeyVaultEndpoint is set with the value of your Key Vault. The configuration is read into the application and added as options to the DI.
using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Azure.KeyVault; using Microsoft.Azure.Services.AppAuthentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using MyAzureFunctions; using MyAzureFunctions.Activities; using System; using System.Reflection; [assembly: FunctionsStartup(typeof(Startup))] namespace MyAzureFunctions { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { builder.Services.AddScoped<MyActivities>(); builder.Services.AddOptions<MyConfiguration>() .Configure<IConfiguration>((settings, configuration) => { configuration.GetSection("MyConfiguration").Bind(settings); }); builder.Services.AddOptions<MyConfigurationSecrets>() .Configure<IConfiguration>((settings, configuration) => { configuration.GetSection("MyConfigurationSecrets").Bind(settings); }); } public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder) { var builtConfig = builder.ConfigurationBuilder.Build(); var keyVaultEndpoint = builtConfig["AzureKeyVaultEndpoint"]; if (!string.IsNullOrEmpty(keyVaultEndpoint)) { // might need this depending on local dev env //var credential = new DefaultAzureCredential( // new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true }); // using Key Vault, either local dev or deployed builder.ConfigurationBuilder .SetBasePath(Environment.CurrentDirectory) .AddAzureKeyVault(new Uri(keyVaultEndpoint), new DefaultAzureCredential()) .AddJsonFile("local.settings.json", true) .AddEnvironmentVariables() .Build(); } else { // local dev no Key Vault builder.ConfigurationBuilder .SetBasePath(Environment.CurrentDirectory) .AddJsonFile("local.settings.json", true) .AddUserSecrets(Assembly.GetExecutingAssembly(), true) .AddEnvironmentVariables() .Build(); } } } }
The local.settings.json contains the configurations for the Azure Functions. (No secrets). The AzureKeyVaultEndpoint has no value. If this was set with the URL of a Key Vault, this would activate the Key Vault for local development.
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "AzureWebJobsSecretStorageType": "Files", "FUNCTIONS_WORKER_RUNTIME": "dotnet", "ASPNETCORE_ENVIRONMENT": "Development", //"AZURE_TENANT_ID": "your tenant", // To use the key vault in local debug, the Firewall needs to allow this //"AzureKeyVaultEndpoint": "your azure ad tenant" "AzureKeyVaultEndpoint": "" }, "MyConfiguration": { "Name": "Lilly", "AmountOfRetries": 7 } }
The MyConfigurationSecrets class is used to hold the secret configurations.
namespace MyAzureFunctions { public class MyConfigurationSecrets { public string MySecretOne { get; set; } public string MySecretTwo { get; set; } } }
The configuration can be used then like any ASP.NET Core application. The services are added in the constructor and can be used as required.
using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.DurableTask; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace MyAzureFunctions.Activities { public class MyActivities { private readonly MyConfiguration _myConfiguration; private readonly MyConfigurationSecrets _myConfigurationSecrets; public MyActivities(IOptions<MyConfiguration> myConfiguration, IOptions<MyConfigurationSecrets> myConfigurationSecrets) { _myConfiguration = myConfiguration.Value; _myConfigurationSecrets = myConfigurationSecrets.Value; }
When deploying, the Azure Functions needs access to the Key Vault. The Azure Functions requires a system assigned Identity. You can activate this, or check that it is created in the Azure portal.
In the Azure Key Vault add a new Access policy.
Search for the required system Identity, ie your Azure Functions, and add the required permissions as your app needs.
The secret configurations are no longer required in the App.Settings of the Azure Functions.
When the functions are called, the actual version is used depending on the cache.
Links:
https://damienbod.com/2018/12/23/using-azure-key-vault-with-asp-net-core-and-azure-app-services/
https://docs.microsoft.com/en-us/azure/azure-functions/durable/
https://github.com/Azure/azure-functions-durable-extension
https://damienbod.com/2019/03/14/running-local-azure-functions-in-visual-studio-with-https/
Microsoft Azure Storage Explorer
Microsoft Azure Storage Emulator
[…] Using Key Vault and Managed Identities with Azure Functions (Damien Bowden) […]
So you have a dedicated class for the secrets MyConfigurationSecrets
Presumably the values are in the Key Vault, but in which format?
Are they entered as individual secrets (one secret for each property on MyConfigurationSecrets)
Or as a single secret in the form of a json string?