Azure Key Vault is a pretty handy way of centrally managing access to secrets and logging what process has requested access to them. The best way to use it is for Azure hosted resources such as Web Applications or VMs for which you can assign a managed identity to the resource and grant this identity access to the vault. However, if you want to access vault secrets from a console application running on a local server you’ll need to do it via a service principle.

A service principle can be created in the Azure Active Directory blade in the portal, this is done by registering an app (it doesn’t need an endpoint) and noting down the Application (client) ID and Directory (tenant) ID as well as the name you gave it. This service principle then needs to be granted rights to access the key vault, this can be done in the Access policies blade in the Key Vault where rights can be granted separately for keys, secrets and certificates. Annoyingly it’s not possible to assign access to only specific keys in the vault to a service principle with the service principle having access to everything there provided you’ve granted it the relevant permissions.

The below code is an example of how to access Key Vault keys in a console application that will run from a local server, I have granted my service principle AzureResourceReport get permissions on both keys and secrets. In a real application the client ID and secret obviously shouldn’t be hard coded in the code!


using AzureResourceReport.Models;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace AzureResourceReport
    class Program
        static void Main(string[] args)
            // Authenticate
            string clientId = "MYCLIENTID";
            string clientSecret = "MYCLIENTSECRET";

            AzureCredentials credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(clientId, clientSecret, tenantId, AzureEnvironment.AzureGlobalCloud).WithDefaultSubscription(subscriptionId);

            var keyClient = new KeyVaultClient(async (authority, resource, scope) =>
                var adCredential = new ClientCredential(clientId, clientSecret);
                var authenticationContext = new AuthenticationContext(authority, null);
                return (await authenticationContext.AcquireTokenAsync(resource, adCredential)).AccessToken;

			// Connect to Key Vault using Client ID and Secret
            KeyVaultCache keyVaultCache = new KeyVaultCache("", clientId, clientSecret);
			// Or use Managed Identity
            //AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
            //KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
            var cacheSecret = keyVaultCache.GetCachedSecret("BlogConnection");

            string connectionString = cacheSecret.Result;


EDIT: The below code has been updated to reflect the breaking changes from MSAL.NET 2 to MSAL.NET 3.

EDIT 2: I’ve updated the constructor to accept a KeyVaultClient, this means a client can be authenticated with Managed Identity if desired.

using Microsoft.Azure.KeyVault;
using Microsoft.Identity.Client;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace AzureResourceReport.Models
    public class KeyVaultCache
        public KeyVaultCache(string baseUri, string clientId, string clientSecret)
            BaseUri = baseUri;
            ClientId = clientId;
            ClientSecret = clientSecret;

        public KeyVaultCache(string baseUri, KeyVaultClient keyVaultClient)
            BaseUri = baseUri;
            _KeyVaultClient = keyVaultClient;

        public static string BaseUri { get; set; }

        public static string ClientId { get; set; }

        public static string ClientSecret { get; set; }

        private static KeyVaultClient _KeyVaultClient = null;

        private static Dictionary<string, string> SecretsCache = new Dictionary<string, string>();

        public async Task<string> GetCachedSecret(string secretName)
            if (!SecretsCache.ContainsKey(secretName))
                if (_KeyVaultClient is null)
                    _KeyVaultClient = new KeyVaultClient(async (authority, resource, scope) =>
                        IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder

                        AuthenticationResult authenticationResult = await confidentialClientApplication
                            .AcquireTokenForClient(new string[] { "" })

                        return authenticationResult.AccessToken;

                        //var adCredential = new ClientCredential(ClientId, ClientSecret);
                        //var authenticationContext = new AuthenticationContext(authority, null);
                        //return (await authenticationContext.AcquireTokenAsync(resource, adCredential)).AccessToken;

                var secretBundle = await _KeyVaultClient.GetSecretAsync($"{BaseUri}{secretName}").ConfigureAwait(false);
                SecretsCache.Add(secretName, secretBundle.Value);

            return SecretsCache.ContainsKey(secretName) ? SecretsCache[secretName] : string.Empty;

The KeyVaultCache is used to cache previously retrieved secrets to improve performance as there is some overhead with fetching them from the Key Vault. The above code is based on that from this post.


james · 24th March 2019 at 1:59 pm

Can you elaborate on how to get tenant id and subscription id? and if you can’t put your client id and client secret in this app and you don’t yet have access to key vault, what’s the solution for storing these particular secrets?

Shinigami · 24th March 2019 at 2:21 pm

The easiest way I’ve found to get subscription ID and tenant ID is from the portal. Subscription ID is visible in the “Subscriptions” blade and tenant ID is available from the “Directory + Subscription” filter on the top right.

If you can’t use key vault then you could use an appsettings file.

Though the secrets here are obviously in the clear so it’ll depend where you’re app is running as to if this is a good idea or not.

mezmiro · 12th June 2019 at 7:53 pm

Excellent post! Thanks for sharing – this helped me out a lot, especially with the cache.

I discovered a bug where this code was trying to add kvp’s to the dictionary when they already existed though, so I had to add a check into the GetCachedSecret() function to account for this. This is likely because I’m using this in a multi-threaded context, but others might run into this bug as well.

    Shinigami · 13th June 2019 at 9:54 am

    Glad this helped you out. I hadn’t thought about using it in a multithreaded context, I’ll update the code and the post accordingly to check the keys before adding them. Thanks!

Christine · 10th July 2019 at 8:05 pm

Hi! Thank you for your post! This was helpful.
I had the same question as James. How do I retrieve the needed client secret “secretly”? It feels like I’m swapping one exposed password for another.
I’ve added the Azure Key Vault to a few of my ASP.NET Core (2.2) apps, which works like a charm. In the Core Azure Key Vault package, I have access to an Azure token provider that uses the developer’s Azure CLI to authenticate against azure. That way when running locally, my developer profile is used, and in prod, the app service’s identity is used.
Do you know of a similar mechanism when not using the WebHostBuilder?

    Shinigami · 11th July 2019 at 10:04 am

    Sorry, I’m not sure how to integrate this with managed identities as in web applications, though I don’t see why this wouldn’t be possible so I may investigate when I get some spare time.

    My use case is that these console applications will be running as an automated process on a local server so my developer profile can’t be used for authentication, I’m mainly using KeyVault here as a way of centrally managing secrets so if a connection string changes for example, I don’t have to go through and update all applications that use it.

Mia · 24th April 2020 at 1:54 pm

Hello, thank you for the post! My question will be a related to the mentioned client ID, client secret, which you have hard-coded in the example, what are the ways to keep it out? Many thanks for your help.

Brian Hoskins · 27th December 2020 at 2:27 pm

I can’t understand the benefit to using Azure Key Vault in this manner.

Let’s say we are trying to store a SQL connection string in the vault because we don’t want these secrets hard coded or in plain text config files on the machine.

Fine, so we put the connection strings in the vault. But now I have the problem of securing the client ID and secret, which provides access to the keys in the vault. I can either hard code them or I can have them in config files, and now I have exactly the same problem as when I was trying to secure my connection strings in the first place.

Am I missing something?

PS: many thanks for the tutorial, I am not at all criticising it I am just struggling to see the value in Key Vault for common problems like securing connection strings in a desktop app.

    Shinigami · 27th December 2020 at 7:22 pm

    Hi, you’re completly correct. When I initially set this up I was hoping there was a secure credendial stash that I could use to store the key vault credentials. However, although there are some semi-secure locations which can be accessed by the hosting enviroment such as the secret manager ( these shouldn’t be assumed to be properly secure. This is therefore more of a proof of concept rather than a viable solution unfortunatly 🙁

    Where this would be useful though is for secret management, as if you’re trying to manage a number of console applications all using the same secrets then it becomes much simpler to manage these in a central location rather than in the appsettings file for each application.

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *