ASP.NET Core Configuration with Environment Variables in IIS

I'm kind of in love with the way ASP.NET Core handles configuration. You can basically choose any kind of store you like: JSON, User Secrets (development only), environment variables, Azure Key Vault, etc. and all you have to do is add the configuration provider and map onto one or more POCOs. Then, you can simply inject your configuration POCO(s) where you need them and access your config variables. Additionally, you can set up multiple configuration providers at once and whichever is available will just work. For example, you can use User Secrets in development and then environment variables in production simply by adding both. The Core app will just pull in whichever source is available where it's currently running.

public Startup(IHostingEnvironment env)  
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();

    if (env.IsDevelopment())
    {
        builder.AddUserSecrets<Startup>();
    }

    Configuration = builder.Build();
}

Here, we have four different configuration sources. First, it will pull from a global appsettings.json configuration. Second, it will look for any environment specific JSON files, like appsettings.Production.json. Third, it will pull in environment variables, and finally, if the environment is Development, it will use User Secrets. Priority is determined by order, so User Secrets will override anything in environment variables, which will likewise override anything in appsettings.json or any of the environment-specific JSON settings.

Then, you can simply define a POCO that maps to your settings and configure your app to use that class to house your settings:

public class MySettings  
{
    public string Foo { get; set; }
}

And:

public void ConfigureServices(IServiceCollection services)  
{
    // Add framework services.
    services.AddMvc();

    ...

    services.Configure<MySettings>(Configuration);
}

Further, you can define a hierarchy of settings and load in only the ones you care about:

{
    "HitchhikersGuideToTheGalaxy": {
        "MeaningOfLife": 42
    }
}

Then:

services.Configure<HitchhikerSettings>(Configuration.GetSection("HitchhikersGuideToTheGalaxy"));  

To do the same with environment variables, you just separate the levels with a colon (:), such as HitchhikersGuideToTheGalaxy:MeaningOfLife.

Finally, when you need these settings:

public class MyController : Controller  
{
    protected readonly MySettings mySettings;

    public MyController(IOptions<MySettings> mySettings)
    {
        this.mySettings = mySettings.Value;
    }
}

When you deploy to IIS, however, things get a little trickier, and unfortunately the documentation on using configuration with IIS is lacking. You go into your server's system settings and configure all your environment variables. Then, you deploy your app to IIS, and it... explodes, because it's missing those necessary settings.

Turns out, in order to use environment variables with IIS, you need to edit the advanced settings for your App Pool. There, you'll find a setting called "Load User Profile". Set that to True. Then, recycle the App Pool to load in the environment variables. Note: you must do this even if your environment variables are added as "System variables", rather than "User variables".

comments powered by Disqus