Search code examples
azureazure-web-app-serviceasp.net-core-3.1azure-keyvaultazure-authentication

Starting my website in Debug not working after Azure Key Vault Configuration ASP.NET Core 3.1


I have a full functioning website. Yesterday I have bound my secrets using Azure Key Vault Service. In my secrets I'm only storing the SendGridKey and EmailKey. The following dependencies have been added in my csproj file:

<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.0.2" />
<PackageReference Include="Azure.Identity" Version="1.3.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />

My Program.cs file has been automatically updated as follows:

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
                {
                    var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("MyKeyVault"));
                    config.AddAzureKeyVault(
                    keyVaultEndpoint,
                    new DefaultAzureCredential());
                })
            .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();

                });

Publishing the website in Azure still works perfectly, but I'm not more able to start my website in Debug modus.

I'm getting the following error:

An error occurred while starting the application.
ArgumentNullException: Value cannot be null. (Parameter 'uriString')
System.Uri..ctor(string uriString)
ArgumentNullException: Value cannot be null. (Parameter 'uriString')

The problem is:

var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("MyKeyVault"));

because Environment.GetEnvironmentVariable("MyKeyVault") is returning NULL

My Startup file is:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        var options = new RewriteOptions()
            .AddRedirectToWwwPermanent()
            .AddRedirectToHttpsPermanent();

        app.UseRewriter(options);

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

Any suggestion?

Update

I have a similar project with the following code block:

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
            {
                var keyVaultEndpoint = GetKeyVaultEndpoint();
                if (!string.IsNullOrEmpty(keyVaultEndpoint))
                {
                    var azureServiceTokenProvider = new AzureServiceTokenProvider();
                    var keyVaultClient = new KeyVaultClient(
                        new KeyVaultClient.AuthenticationCallback(
                            azureServiceTokenProvider.KeyVaultTokenCallback));
                    config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
                }
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    private static string GetKeyVaultEndpoint() => "https://mykeyvault.vault.azure.net/";

I've used the following packages:

<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.0.0" />
<PackageReference Include="Azure.Identity" Version="1.1.1" />
<PackageReference Include="Microsoft.Azure.KeyVault.Core" Version="3.0.5" />
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.8" />
<PackageReference Include="SendGrid" Version="9.21.0" />

Microsoft.Azure.Services.AppAuthentication has been deprecated and the above code works no longer in my new project. It works indeed in my old one.


Solution

  • With this few lines of code, it works again both in development and production:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((context, config) =>
                    {
                        if (context.HostingEnvironment.IsDevelopment())
                        {
    
                            // Do simply nothing
                        }
                        else
                        {
                            var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("MyKeyVault"));
                            config.AddAzureKeyVault(
                            keyVaultEndpoint,
                            new DefaultAzureCredential());
                        }
                                               
                    })
                .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });
    

    In development, the secrets are retrieved from secrets.json, in production from Azure KeyVault.