Net core Key vault configuration using Azure.Security.KeyVault.Secrets
Asked Answered
P

6

18

I have found out it is easy to connect to Azure KeyVault using Managed Identity. The documentation shows how to do it :

             var azureServiceTokenProvider = new AzureServiceTokenProvider();
            var keyVaultClient = new KeyVaultClient(
                new KeyVaultClient.AuthenticationCallback(
                    azureServiceTokenProvider.KeyVaultTokenCallback));

            config.AddAzureKeyVault(
                $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
                keyVaultClient,
                new DefaultKeyVaultSecretManager());
        

Then I realized it requires the package Microsoft.Azure.KeyVault which is deprecated. So I'm struggling to figure out how to do the above with SDK 4. All the documentation I find is related to SDK 3.

enter image description here

[EDIT] I have found out the following code works to get the azure KeyVault Secret using Managed Identiy with SDK 4. However I can't see how to add this to my configuration. It used to be done with config.AddAzureKeyVault() from the Microsoft.Extensions.Configuration.AzureKeyVault Package however it is not compatible with the SDK 4 SecretClient:

  return Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((context, config) =>
                {
                    var azureCredentialOptions = new DefaultAzureCredentialOptions();
                
                  
                    var credential = new DefaultAzureCredential(azureCredentialOptions);
                    var secretClient = new SecretClient(new System.Uri("https://mykeyvault.vault.azure.net/"), credential);
                    var secret = secretClient.GetSecret("StorageConnectionString");
                    config.AddAzureKeyVault()                 
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
        }
Pusan answered 21/6, 2020 at 18:48 Comment(8)
learn.microsoft.com/en-us/azure/key-vault/secrets/… This is v4 documentation.Systematic
How did you figure out it is deprecated ? Looking at nuget and the version seems to be ok, not the latest but not deprecated: nuget.org/packages/Microsoft.Azure.KeyVaultFlagg
@Flagg see the screenshot abovePusan
i guess you could right your own configuration builder ?Flagg
The package is not deprecated tho. i would stick with this one and check for new updates later.Flagg
ok, but it is still recommended to use the new version. I will ask the developers if there is an extension to configure it the new way.Pusan
added some code sample on how to do that but still i would stick with existing package and just wait for azure to implement the new way.Flagg
microsoft.azure.keyvault package is deprecatedWinegrower
P
20

As it turned out, I found the proper way to do it with SDK 4. I had to install the package azure.extensions.aspnetcore.configuration.secrets and then the code is simply :

   var credential = new DefaultAzureCredential();
               
   config.AddAzureKeyVault(new System.Uri("https://mykv.vault.azure.net/"), credential);

then to use it

configuration["StorageConnectionString"]
Pusan answered 22/6, 2020 at 18:39 Comment(2)
It worked! I've tried a lot of combinations but this single line worked. I didn't found it in any ms docs. Tks!Nieves
It works. Here's a working example github.dev/baratgabor/MyWarehouse/blob/…Lebna
F
9

AS per June 2020

First thing is that Microsoft.Azure.KeyVault is not deprecated but replaced. Using the old nuget package is still a valid option.

I imagine in the future, the Microsoft.Extensions.Configuration.AzureKeyVault nuget package will use the new Azure.Security.KeyVault.Secrets package.

In my experience I would stick with the existing library and wait for future updates.

If you really want to use the Azure.Security.KeyVault.Secrets, you can implement your own custom configuration builder.

I had a look at the existing key vault configuration code on github and here is a simplified/modified version that you could use.

First install these nuget packages Azure.Identity and Azure.Security.KeyVault.Secrets.

The new key vault secrets package uses IAsyncEnumerable so you need to update your project to target C#8.0: update you csproj file with <LangVersion>8.0</LangVersion>.

Azure Key Vault Secret configuration code:

public interface IKeyVaultSecretManager
{
    bool ShouldLoad(SecretProperties secret);

    string GetKey(KeyVaultSecret secret);
}

public class DefaultKeyVaultSecretManager : IKeyVaultSecretManager
{
    public bool ShouldLoad(SecretProperties secret) => true;

    public string GetKey(KeyVaultSecret secret)
        => secret.Name.Replace("--", ConfigurationPath.KeyDelimiter);
}

public class AzureKeyVaultConfigurationProvider : ConfigurationProvider
{
    private readonly SecretClient _client;
    private readonly IKeyVaultSecretManager _manager;

    public AzureKeyVaultConfigurationProvider(SecretClient client, IKeyVaultSecretManager manager)
    {
        _client = client ?? throw new ArgumentNullException(nameof(client));
        _manager = manager ?? throw new ArgumentNullException(nameof(manager));
    }

    public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult();

    private async Task LoadAsync()
    {
        var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

        await foreach (var secretProperties in _client.GetPropertiesOfSecretsAsync())
        {
            if (!_manager.ShouldLoad(secretProperties) || secretProperties?.Enabled != true)
                continue;

            var secret = await _client.GetSecretAsync(secretProperties.Name).ConfigureAwait(false);
            var key = _manager.GetKey(secret.Value);
            Data.Add(key, secret.Value.Value);
        }

        Data = data;
    }
}

public class AzureKeyVaultConfigurationSource : IConfigurationSource
{
    public SecretClient Client { get; set; }

    public IKeyVaultSecretManager Manager { get; set; }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new AzureKeyVaultConfigurationProvider(Client, Manager);
    }
}

public static class AzureKeyVaultConfigurationExtensions
{
    public static IConfigurationBuilder AddAzureKeyVault(
        this IConfigurationBuilder configurationBuilder,
        SecretClient client,
        IKeyVaultSecretManager manager = null)
    {
        if (configurationBuilder == null)
            throw new ArgumentNullException(nameof(configurationBuilder));

        if (client == null)
            throw new ArgumentNullException(nameof(client));

        configurationBuilder.Add(new AzureKeyVaultConfigurationSource()
        {
            Client = client,
            Manager = manager ?? new DefaultKeyVaultSecretManager()
        });

        return configurationBuilder;
    }
}

You can now use this configuration builder in your project like that:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            var azureCredentialOptions = new DefaultAzureCredentialOptions();
            var credential = new DefaultAzureCredential(azureCredentialOptions);
            var secretClient = new SecretClient(new System.Uri("https://mykeyvault.vault.azure.net/"), credential);

            config.AddAzureKeyVault(secretClient);
        })
            .UseStartup<Startup>();
}
Flagg answered 22/6, 2020 at 10:13 Comment(6)
I wish so much that Microsoft would stop half-arsing things when they create new libraries :/Saccular
@Flagg There are two parameters for AddAzureKeyVault.Lennon
There is a tiny typo I believe. I changed Data.Add(key, secret.Value.Value); to => data.Add(key, secret.Value.Value); Afterwards I was able to access IConfiguration's configuration["someKey"] elsewhere.Entablature
@Saccular Can understand the sentiment. One way to look at it is at least this way it gets out the door and allows the community to iterate, then adopt popular approaches or fill well-documented gaps. The alternative is a delayed release and then a more heavy-handed release or might eventually mean breaking changes.Territus
As mentioned by the top answer, there is an official package for providing key vault secrets configuration to ASP.NET Core apps, and you can find all packages in Azure SDK here: learn.microsoft.com/en-us/dotnet/azure/sdk/packagesIndestructible
Yeah this answer was posted before the package existsFlagg
E
1

For your info, if you're using Azure Key Vault secrets in your App Service or Azure Functions application settings, you don't have to add extra code to get the key vault value.

You just need to change your app settings values (in azure portal), with your key vault references.

For the steps, look at https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references

Setting example using key vault references: { "name": "DatabaseSettings:ConnectionString", "value": "@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/DatabaseConnectionSettingSecret/ec96f02080254fxxxxxxxxxxxxxxx)", "slotSetting": false }

But this doesn't work for a local development, instead you should use plain secret value. But for me it's okay, because your local development uses different secret value and you don't add your local.settings.json to source control.

Emptor answered 12/10, 2020 at 18:39 Comment(0)
F
1

Latest working solution with version 4 library. My stack is .netcore 3.1 and I am using it inside an Azure Web App to access a secret from Azure KeyVault.

First thing first - go through this MS Doc link

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
//..........

 var kvUri = "https://YOURVAULTNAME.vault.azure.net/";
 var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
 KeyVaultSecret secret = client.GetSecret("SECRETNAME");
                       // Can also use await.....GetSecretAsync()      

 this.ConnectionString = secret.Value.ToString();
 \\thats my internal variable, secret.Value.ToString() is required value

I assume here that you have

  1. Created keyVault in Azure
  2. created a Managed identity in App Service(Using 'Identity' option in app service)
  3. Added above identity in Azure KeyVault('Access policies' option)
Furred answered 24/5, 2021 at 21:56 Comment(0)
B
1

The string "SecretName" is the name of a secret.

The string "KeyVaultUrl" should be in appSettings.json such as

"KeyVaultUrl" : "https://theVaultName.vault.azure.net/"

You can run this code and see the difference between the oldSecretValue and newSecretValue:

using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration.AzureKeyVault;
...


string oldSecretValue = config.GetSection("SecretName").Value!; //this should return null (if such a key doesnt exist in appSettings at all, if it does then it will return that value)

var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

string keyVaultUrl = config.GetSection("KeyVaultUrl").Value!;

config.AddAzureKeyVault(keyVaultUrl, keyVaultClient, new DefaultKeyVaultSecretManager());

string newSecretValue = config.GetSection("SecretName").Value!; // let's say this returns this value "some value from secret in the key vault"
...

So it is like you have added in appSettings this:

"SecretName" : "some value from secret in the key vault"
Bessette answered 4/8, 2023 at 16:19 Comment(2)
Do you happen to have some Microsoft documentation about this approach?Sedate
Sorry, not exactly. It's been a long time so I can't quite remember. What I do remember though is that the Microsoft documentation helped me little, it helped me only to understand the processes a bit but not how to tackle every problem that arose. The code which I provided is a mix from everywhere I could find something. But there is documentation about AddAzureKeyVault on Microsoft, just do a simple google search and it will show. I don't know if it will be that helpful enough though.Bessette
S
0

I am using something like this,

var keyVaultEndpoint = GetKeyVaultEndpoint();
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
 // Pass appropriate connection string 
 var azureServiceTokenProvider = new 
 AzureServiceTokenProvider(certThumbprintConnectionString);
 var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(
 azureServiceTokenProvider.KeyVaultTokenCallback));
 config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
}
private static string GetKeyVaultEndpoint() => "https://<<key-vault-name>>.vault.azure.net";
Saffron answered 21/6, 2020 at 18:52 Comment(1)
see my edit . I want to do it with Managed Identity, which now works, however I don't know how to add it to the configurationPusan

© 2022 - 2024 — McMap. All rights reserved.