Using Key Vault and Managed Identities with Azure Functions

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

History

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))
            {
                // using Key Vault, either local dev or deployed
                var azureServiceTokenProvider = new AzureServiceTokenProvider();
                var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

                builder.ConfigurationBuilder
                        .AddAzureKeyVault(keyVaultEndpoint)
                        .SetBasePath(Environment.CurrentDirectory)
                        .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",
    "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/functions-how-to-use-azure-function-app-settings

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

Install the Azure Functions Core Tools

NodeJS

Azure CLI

Azure SDK

Visual Studio zure development extensions

One comment

  1. […] Using Key Vault and Managed Identities with Azure Functions (Damien Bowden) […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: