12

I am using sample code explain here

https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet

but they only explained how can we get single secrete not list of secrete.

so to get all secrete I'm using this code sample

var all = kv.GetSecretsAsync(url).GetAwaiter().GetResult();
foreach (var secret in all)
{
    secretlist.Add(secret.Id);
}

but it is only getting the secret id, not value. I want to get all secrets value also so can anyone help how I can do this?

Ginish Sharma
  • 141
  • 1
  • 2
  • 9

5 Answers5

10

We can find all keys like this

var secretClient = new SecretClient(new Uri($"https://{AzureKeyVaultName}.vault.azure.net/"), new DefaultAzureCredential());
var v = secretClient.GetPropertiesOfSecrets().AsPages().ToList();
  • 1
    Thanks! this idea helps me ``` await foreach (var secretProperties in secretClient.GetPropertiesOfSecretVersionsAsync(certificateName)) { //todo } await foreach (var certificateProperties in certificateClient.GetPropertiesOfCertificateVersionsAsync(certificateName)) { //todo } ``` – Tho Ho Jul 21 '22 at 10:52
  • I have added the code to get the value of the secret. var listName = new List(); string keyVaultName=""; foreach (var secretname in v) { var keyVaultProp = secretname.Values; foreach (var prop in eyVaultProp) { listName.Add(prop.Name); if (prop.Name.ToLower().Contains("mydb")) { keyVaultName = prop.Name; } } } var sec= secretClient.GetSecret(keyVaultName); var connstring= sec.Value.ToString(); – Jashvita Oct 01 '22 at 07:27
  • I have added the code above to get the value. Can you tell me what is the best practice to save the connection string for later use? This code need to run once at the start of the app after successful authentication from Azure AD Managed Identity. What would be a best to run the code? – Jashvita Oct 01 '22 at 07:32
9

Looking at the documentation, the KeyVaultClient Class doesn't contain a method to get all secrets including their values. The GetSecrets method 'List secrets in a specified key vault.' and returns a list with items of type SecretItem, which doesn't contain the value but only contains secret metadata.

This is in line with the Key Vault REST API, where there's a GetSecrets that returns... you guessed it... a list of SecretItems.

Long story short: if you want all values of all secrets, you have to iterate the list and get every one explicitly.

rickvdbosch
  • 14,105
  • 2
  • 40
  • 53
6

If you are using the newer Azure.Security.KeyVault.Secrets package then you can get all the secrets by using the GetPropertiesOfSecretsAsync method, then iterating over each result calling GetSecretAsync. Obviously this is still SELECT N+1 but there currently appears to still be no other way to do this.

Note that you will need to be using C# 8.0 to use this example:

private SecretClient _client;

// ... setup your client, for example:

_client = new SecretClient("https://mykeyvault.vault.azure.net/", new DefaultAzureCredential());
        
// ...
        
public async Task<IList<KeyVaultSecret>> GetAllAsync(CancellationToken cancellationToken = default)
{
    AsyncPageable<SecretProperties> secretProperties = _client.GetPropertiesOfSecretsAsync(cancellationToken);

    var secrets = new List<KeyVaultSecret>();

    await foreach (var secretProperty in secretProperties)
    {
        var response = await _client.GetSecretAsync(secretProperty.Name, cancellationToken: cancellationToken).ConfigureAwait(false);
        
        secrets.Add(response.Value);
    }

    return secrets;
}
bytedev
  • 8,252
  • 4
  • 48
  • 56
2

You can use listPropertiesOfSecrets method which returns all keys. This way you can iterate and get all secrets from vault.

Antoni
  • 83
  • 6
  • Thanks, this is what I am looking for and also the most up to date answer out there! – user3056928 Feb 21 '22 at 08:50
  • Thanks! the idea is help! ~~~ await foreach (var secretProperties in secretClient.GetPropertiesOfSecretVersionsAsync(certificateName)) { //todo } await foreach (var certificateProperties in certificateClient.GetPropertiesOfCertificateVersionsAsync(certificateName)) { //todo } ~~~ – Tho Ho Jul 21 '22 at 10:48
1

You have to get all secrets, returning an IPage of SecretItem, and then iterate through each one to get the SecretBundle like this. Here's my code that handles the operation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;

namespace TradingReplay.Engine
{
    public class SecurityCredentials : Serialisable<SecurityCredentials, SecurityCredentials>
    {
        public string VaultUrl { get; set; }
        public string ApplicationId {get; set;}
        private string ApplicationSecret { get; set; }
        internal Dictionary<string, string> Cache { get; set; } = new Dictionary<string, string>();

        public SecurityCredentials()
        { }

        public SecurityCredentials(string vaultUrl, string applicationId, string applicationSecret)
        {
            VaultUrl = vaultUrl;
            ApplicationId = applicationId;
            ApplicationSecret = applicationSecret;
        }

        public async Task<SecurityCredentials> InitialiseAzure()
        {
            var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessTokenAsync), new HttpClient());
            var secrets = await client.GetSecretsAsync(VaultUrl);

            foreach (var item in secrets)
                Cache.Add(item.Identifier.Name, await GetSecretAsync(client, item.Identifier.Name));

            return this;
        }

        public async Task<string> GetSecretAsync(string key)
        {
            if (Cache.TryGetValue(key, out var value))
                return value;
            else
            {
                var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessTokenAsync), new HttpClient());
                var secret = await GetSecretAsync(client, key);
                Cache.Add(key, secret);
                return secret;
            }
        }

        public async Task<string> GetSecretAsync(KeyVaultClient client, string key)
        {
            var secret = await client.GetSecretAsync(VaultUrl, key);
            return secret.Value;
        }

        private async Task<string> GetAccessTokenAsync(string authority, string resource, string scope)
        {
            var appCredentials = new ClientCredential(ApplicationId, ApplicationSecret);
            var context = new AuthenticationContext(authority, TokenCache.DefaultShared);

            var result = await context.AcquireTokenAsync(resource, appCredentials);

            return result.AccessToken;
        }
    }
}

For testing purposes I have registered an application to access my Azure instances, and to initialize the class all I do is:

var credentials = await new SecurityCredentials("<vaultUrl>", "<applicationId>", "<applicationSecret>").InitialiseAzure();

and then can call:

credentials["<secretName>"];
Liam
  • 5,033
  • 2
  • 30
  • 39