1
votes

I've got an ASP.NET Core 3 application that makes use of Azure Key Vault. It works fine in production, and also works fine debugging in vs2019, as long as I'm signed into my Microsoft account in Visual Studio. But when I'm not signed into VS, it fails and prevents the app from starting up, rather than continuing with app startup. I'd like this to work when a developer is not signed into VS (as other developers work this way). Here's the relevant startup code:

public static IHostBuilder CreateHostBuilder(string[] args)
    {
        {
            static string GetKeyVaultEndpoint() => "https://[myappvalut].vault.azure.net";

            var azureServiceTokenProvider = new AzureServiceTokenProvider();
            var keyVaultClient = new KeyVaultClient(
                new KeyVaultClient.AuthenticationCallback(
                    azureServiceTokenProvider.KeyVaultTokenCallback));
            return Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .ConfigureHostConfiguration(c => c.AddAzureKeyVault(GetKeyVaultEndpoint(), keyVaultClient, new DefaultKeyVaultSecretManager()));
        }
    }

and here's the exception I get on startup:

Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: 'Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/[guid]. Exception Message: Tried the following 3 methods to get an access token, but none of them worked.

Is there any graceful way to either recover from this and continue with app startup, or only add Key Vault if I know I have a connection string?

2

2 Answers

0
votes

You can use service principal to access your key vault. Here is the document.

string clientId = Environment.GetEnvironmentVariable("akvClientId");
string clientSecret = Environment.GetEnvironmentVariable("akvClientSecret");

KeyVaultClient kvClient = 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;
});
0
votes

I have an example in my article that can be adapted: https://joonasw.net/view/aspnet-core-azure-keyvault-msi.

Essentially you construct the configuration from the providers registered until then, and check if we have a key vault URL. You should be able to take a similar approach in your case.

Note the sample was made for ASP.NET Core 2.1:

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureAppConfiguration((ctx, builder) =>
        {
            //Build the config from sources we have
            var config = builder.Build();
            var keyvaultUrl = config["KeyVault:BaseUrl"];
            if (!string.IsNullOrEmpty(keyVaultUrl))
            {
                //Add Key Vault to configuration pipeline
                builder.AddAzureKeyVault(keyvaultUrl);
            } 
        })
        .Build();