Read secrets from Azure Key Vault in a .NET Core Console app

Azure Key Vault, the thing on Azure that allows you to store secrets, certificates and manage private/public keys – I reckon you heard about it? If not, go check it out by looking at this wonderful free (yes, really FREE) Microsoft Learn module: Manage secrets in your server apps with Azure Key Vault

Ok, you’re still here or back from the learn module. In either case you know now…

  • Azure Key Vault is a good place to store secrets
  • it can (for example) be used in combination with an ASP.NET Core MVC app and let the app automagically retrieve secrets

And then you’re maybe like me and think: can I use this to store a secret in then retrieve that secret from a simple .NET Core Console app? Maybe to have all secrets centralised so you can change them without having to change your app.

Or you want to implement that Console app because it should be possible to do it and you want to do it to verify you better understand how things work? (<- that’s me!)

Whatever the reason, when you are finished reading this blog post, you will be able to read secrets from your Azure Key Vault by providing a master client secret in your app.

To be successful, the following prerequisites are required:

Note: I am writing this because I was struggling to find good example code. Azure Portal changes so quickly that posts from April 2019 are already outdated.

Create an Azure Key Vault

Go to Azure Portal and create a new Azure Key Vault.

  • Once completed, select “Secrets” from the Key Vault’s menu and add a secret by clicking “Generate/Import” in the top menu.
  • Give it a name (this is the key you will use to retrieve the secret) and a value (your super secret pazzw0rd)
  • Feel free to play with the activation and expiration settings
  • Make sure to “enabled” the secret

Now, the secret is stored and we could use the Azure CLI to retrieve it but that’s not what we’re after here.

Register an “app” in Azure AD

Again, in the Portal select “Azure Active Directory” from the main menu or type it into the search box at the top.

You cannot talk to Key Vault directly and ask it to reveal a secret – that would be pretty pointless, because why have a secret then to begin with? The goal is to create “a thing” that can access Key Vault. We then talk to this “thing” and tell it to get the secrets we need. The requirements for the “thing” are:

  • It should only have the minimum permissions it needs (in our case: read the Key Vault)
  • We must be able to communicate with it programmatically from within our yet-to-be-written-Console-app

So what is that “thing”? It’s what is called an “identity”. Key Vault can grant access to identities. A user is such an identity but that won’t not help us here. We don’t want to call up user X on the phone and ask him to please get something from the vault. So users are out. Another type of identity is an “app”. I don’t like the fact they call this “app”. It’s not the kind of app developers have in mind (an iOS App or an Android App or Web App) but it’s more of an abstract “thing” (see…I don’t have a better name either).

Anyway, the point is to register such an app and then we’ll tell Key Vault that this app may read values.

  • To register an app select “App registrations” from the Azure AD’s menu.
  • Click “New registration” in the top menu.
  • Give it a name (I used “RRConsoleAppIdentity”) and copy&paste it somewhere (or write it down, if you’re old-fashioned like me). This is the name of our identity.
  • Select the appropriate account type (if you don’t know what to pick, click on “Help me choose…”)
  • Leave the Redirect URI empty.
  • Click on “Register” to save
Screenshot of app registration
Screenshot of app registration blade

The app (think of it as a user) has new been created. We need to tell this app that we want to talk to it. That’s done via a client secret.

  • In the just registered app’s menu select “Certificates & secrets”
  • Select “New client secret”
Create a new client secret

The client secret needs a name. I called mine (drum roll): ClientSecret

You can play with the expiration settings if you like. They configure how long the secret is valid. Once the secret has been created, make sure to copy&paste it because you will not be able to retrieve it later (you can however recreate it).

The generated client secret

Back on the overview page of the registered app, make sure to also copy&paste the “Application (client) ID”. 

Why are we generating this secret, you may wonder?

It will become clearer later but here’s the brief explanation. The client secret will allow our client app to claim “Hey, you registered app thingy there…I KNOW YOU! Go, get that password from the key vault!” But the app identity would just say “Yeah, that’s what they all claim. Prove to me that you REALLY know me! What’s the passphrase?” – and we would do so by submitting the secret client key. Then, the app identity will reach out to the key vault and get things on our behalf.

So this is kind of like getting access to that exclusive night club where you knock on the door and to get in you have to know the secret phrase (it’s “Ken sent me!”, by the way).

Give the app access to Key Vault

Switch back to the previously created Key Vault and select “Access policies” from its menu. You will see yourself there (spooky!) – well, to be precise you will see your username there and the category states “USER” and you have full access.

Click “+ Add access policy” and either select the “Secret permission” manually or use a template from the first dropdown. This configures what the app identity will be able to do. I selected “Get” and “List”.

Note: it makes sense to limit the permissions as much as possible! If the console app you are writing is only supposed to read value, do not assign write permissions here.

  • Click the “Select principal” element and a blade will appear where you can search for names.
  • Enter the name of the app you registered before, in my case it is “RRConsoleAppIdentity”
  • Click the “Select” button at the bottom of the blade.
  • Click “Add” at the bottom of the form.
Access policy for the app

Here’s something I don’t know: there is this element “Authorized applications”. It is locked down for me. I could not find any documentation on it and would love to know what this is about. Let me know if you have an idea: rene.ruppert@microsoft.com 

Once saved, you’ll end up back on the access policies screen and the app shows up in the list. Don’t forget to click “Save” in the top menu to commit the changes we made!

Identities with access to Key Vault

Coding the console app

Now let’s implement to real app and not just some abstract app-thingy. Use your preferred way of creating a console app and add these two Nuget packages:

  • Microsoft.Azure.KeyVault
  • Microsoft.IdentityModel.Clients.ActiveDirectory

Below is all of the code required. To make it work for you will need:

  • The “Application (client) ID” which can be found when selecting the registered app under “Azure Active Directory” -> “App registrations”. It goes into APP_CLIENT_ID.
  • The client secret. This is what you had to write down because it cannot be retrieved later. If you did not believe me, now is the time to delete the old secret and create a new one. This one goes into APP_CLIENT_SECRET.
  • The base URI of the key vault. This can be found in the overview page of the key vault as “DNS Name” and goes into KEYVAULT_BASE_URI.
  • The name/key of the secret you added to the key vault. You can find this in the key vault under the menu option “Secrets” and use it as the second parameter of the call to GetSecretAsync().
using System;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace KeyVaultConsole
{
    public class KeyVaultConsoleApp
    {
        // This is the ID which can be found as "Application (client) ID" when selecting the registered app under "Azure Active Directory" -> "App registrations".
        const string APP_CLIENT_ID = "f9fd5139-6ddb-4c20-b848-7541a3d94311";
        
        // This is the client secret from the app registration process.
        const string APP_CLIENT_SECRET = "Is9G=de[L5r42lW@_qv90XA-tg3U-50]"; 
        
        // This is available as "DNS Name" from the overview page of the Key Vault.
        const string KEYVAULT_BASE_URI =  "https://rrdemokeyvault.vault.azure.net/"; 

        async static Task Main(string[] args)
        {
            // Use the client SDK to get access to the key vault. To authenticate we use the identity app we registered and
            // use the client ID and the client secret to make our claim.
            var kvc = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(
                async (string authority, string resource, string scope) => {
                    var authContext = new AuthenticationContext(authority);
                    var credential = new ClientCredential(APP_CLIENT_ID, APP_CLIENT_SECRET);
                    AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);

                    if (result == null)
                    {
                        throw new InvalidOperationException("Failed to retrieve JWT token");
                    }

                    return result.AccessToken;
                }
            ));

            // Calling GetSecretAsync will trigger the authentication code above and eventually
            // retrieve the secret which we can then read.
            var secretBundle = await kvc.GetSecretAsync(KEYVAULT_BASE_URI, "secretkey");
            Console.WriteLine("Secret:" + secretBundle.Value);
            Console.ReadKey();
        }
    }
}

 

If all works as expected you should see the secret value in the output window.

Conclusion

You have successfully retrieved a secret from Azure Key Vault by providing another secret (client ID). This is a bootstrap problem and cannot be avoided – except you are running an app on Azure directly; more on this can be found in the MS Learn modules I linked at the beginning of the blog post.

Although this example only shows how to read values it is of course possible to also let the console app write to the key vault.

Leave a Reply

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