10

Is there a way to change the environment settings when deploying ASP.NET Core application (like config file transformations using debug/release build)?

What would be the best approach for maintaining multiple environment settings in .NET Core applications (something similar to <appSettings file="local.config"> for local, staging and production)?

Tseng
  • 61,549
  • 15
  • 193
  • 205
Soma Yarlagadda
  • 2,875
  • 3
  • 15
  • 37

2 Answers2

26

The central configuration file is the appsettings.json and you can have multiple files, like appsettings.Production.json etc, which will be loaded and override settings from the appsettings.json.

For example

        // Set up configuration sources.
        var builder = new ConfigurationBuilder()
            .SetBasePath(hostEnv.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{hostEnv.EnvironmentName}.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

All you need to get this working is the environment variable for setting the environment type (see documentation here).

You can also have environment variables that override, if you add AddEnvironmentVariables() to your configuration builder. So if you have a appsettings.json of

{
    "Data"  {
         "Default" {
              "ConnectionString" : "..."
         }
    }
}

and want to override that via environment Variable, you'd set up an environment variable called "Data:Default:ConnectionString" and it's value will override the settings in the appsettings.config and appsettings.Production.config (assuming your .AddEnvironmentalVariables() is called after .AddJsonFile() - Last registration with the matching key wins) with the value from the environment variable.

You can find more in the official documentation here.

Update

Since in the comments some understand this as the only way to set the environment, there are many ways to set environment variable (most of it is documented in Use multiple environments in ASP.NET Core), all ultimately boiling down to being an environment variable, just within a different scope:

  1. Environment Variable (globally, Windows cmd.exe set ASPNETCORE_ENVIRONMENT=Development or $Env:ASPNETCORE_ENVIRONMENT = "Development" on powershell, export ASPNETCORE_ENVIRONMENT = Development on linux)
  2. Per command environment variable (i.e. linux: ASPNETCORE_ENVIRONMENT=Production dotnet MyApp.dll)
  3. Docker container, i.e. via docker-compose.yaml

    web:
        environment:
        - ASPNETCORE_ENVIRONMENT=Debugging
    
  4. Docker container via command line docker run -e ASPNETCORE_ENVIRONMENT=Debugging
  5. in IIS via web.config.

    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" stdoutLogEnabled="true" >
      <environmentVariables>
        <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
      </environmentVariables>
    </aspNetCore>
    
  6. On IIS set it per AppPool (see here)
  7. On Linux via service definition files (see docs)
  8. Azure App Service via Environment variables, can be set per slot and having different slots for Staging, Development, Production and i.e. deploying to staging, doing warm up and swapping with Production
  9. Per execution via dotnet run --launch-profile Development

They all change/set the environment variable in a specific scope (globally, locally to a container, inside the app pool, per execution etc.). Choose one which suits your needs.

Tseng
  • 61,549
  • 15
  • 193
  • 205
  • This makes sense, but, how would you switch between environments when deploying? How to dynamically change the value of hostEnv.EnvironmentName when deploying to dev/ staging /production? Thank you. – Soma Yarlagadda Jun 17 '16 at 18:46
  • 1
    Simply set the variable. On windows `setx Hosting:Environment Staging` or `export Hosting__Environment=Staging` (note the double underscore!) on Linux. Make sure you have the `AddEnvironmentVariables()` call when building the config, otherwise it won't work – Tseng Jun 17 '16 at 19:37
  • Thank you. Where can I find some documentation (links to some articles or blogs) on this topic? – Soma Yarlagadda Jun 17 '16 at 20:00
  • Setting variables in Windows or how ASP.NET Core configuration gets its values? I linked two articles in my post, one for configuration and one for how to run ASP.NET Core in multiple environments – Tseng Jun 17 '16 at 20:02
  • 1
    How to run multiple environments using ASP.NET Core. The official documentation has no mention of "setx" command. – Soma Yarlagadda Jun 17 '16 at 20:32
  • 1
    It's a windows command! https://technet.microsoft.com/en-us/library/cc755104(WS.10).aspx?f=255&MSPPError=-2147217396 – Tseng Jun 17 '16 at 20:34
  • May be I am confused, but, if env variables are read from Windows env variables, why are they saved to launchsettings.json part of the solution? – Soma Yarlagadda Jun 19 '16 at 21:36
  • 3
    Read the linked article, it's stated there!!! __This file holds settings specific to each profile Visual Studio is configured to use to launch the application, including any environment variables that should be used.__ It's used only within Visual Studio when you hit F5 for start/debug the application. For production you use the real environmental variables – Tseng Jun 19 '16 at 23:38
  • I read that, but misunderstood that the env variables are actually stored in this file. Now this makes total sense, thank you for your time clarifying the concept. – Soma Yarlagadda Jun 20 '16 at 06:21
  • How to switch environment when deploying to azure? – dragonfly02 Oct 14 '17 at 09:23
  • @stt106: You don't. The environment is read when the application is started, so you just change the environment variable on your azure web app or VM (where ever you deploy it) before or after deploying it (if app already runs, an app restart is required). Typically in Azure Web Apps you use multiple slots, One with Production environment variable, the other as Development or Staging. You deploy to the inactive slot and test, then you switch the slots via powershell or Azure Portal. https://learn.microsoft.com/en-us/azure/app-service/web-sites-staged-publishing – Tseng Oct 14 '17 at 09:31
  • This can't be the real solution, surely. If so that is the most brittle thing I have ever even heard of. The operating system of the server has to declare itself to be of a particular use. Really? If so, ASP.NET core can jump off a cliff. – Serialize Apr 26 '18 at 23:34
  • 1
    @Serialize: Where is the problem? you can set the variable per execution (at least on linux), you can put it into docker containers which are isolated from other apps, you can set the variable within IIS or globaly for the whole machine and in azure each of the vm slots (staging, production, etc.) can have its own variables and with 2.0 also with `dotnet run --launch-profile Development`. Don't see an issue here – Tseng Apr 26 '18 at 23:42
  • The problem is that DevOps workflows don't include a step "alter the global configuration of the host server" as a matter of sanity. Docker is not an option for everyone. The CI tool-chain should not be in charge of altering the host environment in any case. Its a terrible idea for all but the most micromanaged use cases. – Serialize Apr 27 '18 at 00:08
  • @Serialize So how would you suggest it work? In the past, the CI would handle it by choosing the proper config transform. This is really no different other than how it does that. – Joe Phillips May 07 '18 at 14:03
  • @JoePhillips You think there is no difference between applying an XML transform to web.config and modifying the target servers operating system variables, possibly affecting any number of other applications in the process..? Maybe I've been sheltered, but I would never expect to modify the publish server's internals from the CI application. There is a huge difference between modifying a config that only IIS will ever read as opposed to setting an OS level variable that could affect sibling applications. I would suggest it work via any method that did not alter the host globally. – Serialize May 08 '18 at 18:37
  • @Serialize The comments above you say you can pass in an environment/profile instead of setting env vars on the system. Just read them. – Joe Phillips May 08 '18 at 18:46
  • @Serialize: Look at my comment above, there are other options too, which differ a bit depending on where you deploy it. There is not one single definite way to set it up. Multiple ways doing it in command line, i.e on linux you could `ASPNETCORE_ENVIRONMENT=Production dotnet MyApp.dll`. It will set the environment variable for this single command only, no global setting. Or you can set up a service file (i.e. ubuntu, see https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.1&tabs=aspnetcore2x#monitoring-the-app). There are dozens of ways to set it – Tseng May 08 '18 at 18:46
  • @Serialize: Updated the question to avoid further confusion, choose one which suits your needs :) – Tseng May 08 '18 at 18:59
  • @Tseng Thanks. After reading everything linked here, it was this: "Environment variables can be specified for the **process** in the processPath attribute. Specify an environment variable with the environmentVariable child element of an environmentVariables collection element. Environment variables set in this section take precedence over system environment variables." [link](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.1#setting-environment-variables) that helped me understand. Maybe others will find this useful as well. – Serialize May 08 '18 at 19:24
  • @JoePhillips My concern wasn't whether there were other ways to solve the problem, it was that it seemed to me that the accepted answer was implying that a operating system level command was being applied to enable an application instance level behavior (if you want to use this method). I was ignorant of the variable's scope. – Serialize May 08 '18 at 19:34
  • @Serialize That's fair. I'm on the same page as you -- I think using env variables was an odd decision but it does allow more flexibility once you're aware of the alternative ways of using it – Joe Phillips May 08 '18 at 19:38
0

Using additional appsettings.*.json files is a good way to go. The * fragment can be used to mix in any unique environment property that distinguishes between machines, users or deployment scenarios.

But instead of building your config object from scratch using new ConfigurationBuilder() (as shown in many sources on the web) I recommend another approach. The following code will NOT replace your existing config, but ADD to it:

    public IHostingEnvironment _environment { get; }
    public IConfiguration _configuration { get; }

    public Startup(IConfiguration configuration, IHostingEnvironment environment)
    {
        _environment = environment;

        // use the default config and add config from appsettings.COMPUTERNAME.json (if it exists)
        var builder = new ConfigurationBuilder()
            .SetBasePath(environment.ContentRootPath)
            .AddConfiguration(configuration)
            .AddJsonFile($"appsettings.{System.Environment.GetEnvironmentVariable("COMPUTERNAME")}.json", optional: true);
        _configuration = builder.Build();

    }

Background:

When you create your project based on a dotnet new template, then your project already comes with a useful config that is automatically built through the CreateDefaultBuilder() method. This default config combines information from multiple sources: appsettings.json, appsettings.{Environment}.json, Secret Manager, environment variables, and command-line arguments.

If you completely rebuild the config yourself, you will lose all that magic.

Hint:

In the above example appsettings.COMPUTERNAME.json is only an example. You can compose your own json filename from any data in _environment or System.Environment that clearly discriminates your different development and deployment scenarios.

Jpsy
  • 20,077
  • 7
  • 118
  • 115