DISCLAIMER: This post is purely a personal opinion, not representing or affiliating my employer's.
In my previous post, we discussed how Azure Logic App can access to Azure Key Vault. Now in this post, I'm going to talk about how Azure Functions can access to Key Vault directly using Managed Identity.
All sample codes used in this post can be found at here.
Enabling Managed Identity on Azure Functions
Both Logic Apps and Functions supports Managed Identity out-of-the-box. In other words, instance itself works as a service principal so that we can directly assign roles onto the instance to access to Key Vault. This is very simple. Just follow this official document and you will be able to enable Managed Identity feature. Here in this post, I'm not going to discuss too much on this.
Accessing to Key Vault from Azure Functions
According to the document previously mentioned, the code snippet for Key Vault might look like:
var provider = new AzureServiceTokenProvider(); | |
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(provider.KeyVaultTokenCallback)); | |
var secret = await kv.GetSecretAsync("https://my-keyvault.vault.azure.net", "[secret key]") | |
.ConfigureAwait(false); |
Once you get the secret
, you can do whatever you need. That's easy. Actually this is it. But we can do some more. Let's have a look.
Adding Dependency Injection
As you can see, basically we use KeyVaultClient
class that internally uses HttpClient
class. Therefore, we can register this as a singleton instance through IoC container. If you want to use IoC container in Azure Functions, you better to use this package library. Here's how we can register singleton instance.
public class AppModule : Module | |
{ | |
public override void Load(IServiceCollection services) | |
{ | |
var azureServiceTokenProvider = new AzureServiceTokenProvider(); | |
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); | |
services.AddSingleton<IKeyVaultClient>(kv); | |
... | |
} | |
} |
Then, use the IFunctionFactory
instance to manage all dependencies. Here's the code at the function level.
public class GetSecretFunction : FunctionBase<ILogger>, IGetSecretFunction | |
{ | |
private readonly IKeyVaultClient _kv; | |
... | |
public GetSecretFunction(AppSettings settings, IMapper mapper, IKeyVaultClient kv) | |
{ | |
this._kv = kv ?? throw new ArgumentNullException(nameof(kv)); | |
... | |
} | |
public override async Task<TOutput> InvokeAsync<TInput, TOutput>(TInput input, FunctionOptionsBase options = null) | |
{ | |
... | |
var secret = await this._kv | |
.GetSecretAsync("https://my-keyvault.vault.azure.net/", "[secret key]") | |
.ConfigureAwait(false); | |
... | |
} | |
} |
IKeyVaultClient
has been registered as a singleton instance and this is simply used in each function level.
So far, we have walked through how we can directly access to Key Vault from Azure Functions using Managed Identity, as well as how we can make use of dependency injection for this feature. In fact, we don't have to use dependency injection as mentioned earlier. However, usually business doesn't only require Key Vault access itself, but also has other requirements like this post, AutoMapper Dependency Injection into Azure Functions. Therefore, using dependency injection for KeyVaultClient
would be very handy. It also gives much flexibility for testing and modularising. In the next post, let's discuss how we can create more value with this Key Vault access from Azure Functions.