0

I have an application that is going to use some external microservices I've built. The application and the microservices are hosted on Azure App Services. The microservices' functionality is needed inside the Startup class, and so I'm registering those dependencies early (while creating the IWebHostBuilder). The microservices also depend on configuration values (supplied by the application), so I'm also doing some IOptions Pattern configuration while creating the IWebHostBuilder to get settings from appsettings.json. That settings file contains settings that will be used in all environment, as well as a few that are environment-specific. It was my intention to use the settings from the file for local development, and to override the environment-specific settings using the Configuration blade on the App Service. In other applications I've built, the Configuration blade settings have indeed overridden the values from appsettings.json.

However, it appears that when you do an early configuration, as in the below code, the App Service fails to override the settings, and so my local development settings end up being used in the App Service instead (and so everything blows up). I took a shot in the dark and moved the environment-specific settings out of appsettings.json and into their own JSON file with a different name. Now I'm only loading that file early. This did nothing to solve my problem.

So my question is, what techniques are there for having configuration settings available before Startup, but still being able to override those settings in Azure App Services? I've scoured the .NET Core documentation on configuration, but there are so many options and approaches that they make my head spin. I'm currently using .NET Core 2.2. Thanks in advance!

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

    private static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        return WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, builder) => { builder.AddEarlyRequiredConfiguration(); })
            .ConfigureServices((context, services) => { services.AddEarlyRequiredServices(context); })
            .UseStartup<Startup>();
    }

    private static void AddEarlyRequiredServices(this IServiceCollection services, WebHostBuilderContext context)
    {
        // This extension method is in a separate assembly, and registers the microservices.
        // It also configures the IOptions stuff for the configuration values.
        services.AddMyMicroservices(context);
    }

    private static void AddEarlyRequiredConfiguration(this IConfigurationBuilder builder)
    {
        builder.AddJsonFile("appsettings.json");
    }
}
bubbleking
  • 3,329
  • 3
  • 29
  • 49
  • This is tricky for sure, but sort of situation requires this? Perhaps if you share some more detail around the larger process, and what necessitates this, there might be an alternative solution – Patrick Goode Apr 17 '20 at 02:08
  • It it too early in the startup process to access blob storage? – Patrick Goode Apr 17 '20 at 02:09
  • Pls read this post, I think this is useful for you. (https://stackoverflow.com/questions/52684767/load-appsettings-envname-json-file-in-asp-net-core-2-1) – Jason Pan Apr 17 '20 at 06:56
  • @PatrickGoode - Our applications generally need a few early service registrations because our Startup class builds a lot of authorization policies based on information in AAD (via Graph) and a custom RBAC system. We register those dependencies early so that Startup can build the policies. – bubbleking Apr 17 '20 at 13:14
  • @Jason - Thanks. I am going through it, and trying to extract just what will work for me. Do you have a specific part in mind? I only ask because there are some garbage responses and a few meaningful ones that are different approaches. – bubbleking Apr 17 '20 at 13:16

1 Answers1

1

Feel free to post a better answer if you have one. This may not be perfect, but it's how I ended up dealing with it.

I went with creating settings files in the suggested form of appsettings.{Environment}.json. I didn't use Microsoft's out-of-the-box environment names (Development, Staging, Production). Instead I customized them to my organization's environments. In launchSettings.json, I set up a value for the ASPNETCORE_ENVIRONMENT environment variable that indicates my local development environment. On the App Services, I set up that same environment variable to have relevant values for those environments. Since anything in appsettings.json will not be overridden when registering early, I put all settings that needed overriding into those environment specific files. Any setting I wanted to only specify in the App Service Configuration, I left out of the setting files altogether. Finally, I changed my early registration method to look like this:

private static void AddEarlyRequiredConfiguration(
        this IConfigurationBuilder builder,
        WebHostBuilderContext context)
    {
        builder.AddJsonFile("appsettings.json");
        builder.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json");
    }
}

I just made sure that between appsettings.json, appsettings.{Environment}.json and the App Service Configuration, no settings were duplicated. This way, I got the settings registered early, as needed, but none of the settings from any of those three layers had to be overridden.

bubbleking
  • 3,329
  • 3
  • 29
  • 49