BenjiFB
BenjiFB

Reputation: 4721

ASP.NET Core 3: Don't halt app startup if can't access key vault

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?

Upvotes: 1

Views: 601

Answers (3)

Assil
Assil

Reputation: 690

This is an old question, I know my answer will not matter at this point, but for others who suffer from similar problems. You need to understand how does your application authenticate to AKV in all cases, while developing and in prod and when you are signed in and not. AKV provides multiple ways to authenticate, some are:

  • Secret
  • Certificate
  • Managed Identity
  • etc.

I prefer using the default credentials and a unified way to connect and authenticate to AKV in all situations (dev, non-dev) as long as you host your app in Azure or you have a setup where Azure can generate the default credentials for you.

When you are developing on your local development, the default credentials are yours, so if you have access to the AKV then your application will. When your app is on Azure, you need to assign an identity to it and grant it access to the AKV.

Theer are many ways to do so, it depends on your application and settings etc. but main two scenarios are: If you have Startup class then your program should look like:

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, configurationBuilder) =>
        {
            var builtConfig = configurationBuilder.Build();
            configurationBuilder.AddAzureKeyVault(
                                                               new Uri($"https://{builtConfig["KeyVault:VaultUri"]}.vault.azure.net/"),
                                                               new DefaultAzureCredential(),
                                                               new FrameworkKeyVaultSecretManager());
        })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

}

But if you don't have a Startup, then you can just get that line somewhere in your program.cs

builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["Settings:KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new FrameworkKeyVaultSecretManager());

Upvotes: 0

juunas
juunas

Reputation: 58733

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();

Upvotes: 0

Tony Ju
Tony Ju

Reputation: 15609

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;
});

Upvotes: 0

Related Questions