dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.35k stars 9.99k forks source link

ASPNETCORE_Environment overrides cmd parameter #17963

Closed inech closed 1 year ago

inech commented 4 years ago

Describe the bug

We've noticed a very strange behavior of hosting environment setting. When using ASPNETCORE_Environment="fromEnv" environment variable and --Environment=fromCmd command-line argument then for some reason the environment variable wins.

This is very inconsistent given that:

  1. ASPNETCORE_urls vs --urls - command-line argument wins 📗
  2. DOTNET_ENVIRONMENT vs --Environment - command-line argument wins 📗
  3. ASPNETCORE_ENVIRONMENT vs --Environment - environment variable wins 📙

To Reproduce

  1. (This one works well) When I don't set any environment variables, the command-line argument is applied:
    dotnet .\WebApplication_TestEnvironment.dll --Environment=fromCmd
    Current environment: fromCmd
  2. (This one works well too) When I set DOTNET_ENVIRONMENT environment variable, the command-line argument wins:
    $ENV:DOTNET_ENVIRONMENT="fromDotnetEnv"
    dotnet .\WebApplication_TestEnvironment.dll --Environment=fromCmd
    Current environment: fromCmd
  3. (This one doesn't work) When I set ASPNETCORE_ENVIRONMENT environment variable, then hosting environment is defined by the environment variable and not by cmd:
    $ENV:ASPNETCORE_ENVIRONMENT="fromAspEnv"
    dotnet .\WebApplication_TestEnvironment.dll --Environment=fromCmd
    Current environment: fromAspEnv

I am able to reproduce it on a newly created ASP.NET Core 3.1 API project. The only small change I did is writing the current environment to the Console .

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

   ....................................................................

    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.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            Console.WriteLine($"Current environment: {env.EnvironmentName}");

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

Further technical details

dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.100
 Commit:    cd82f021f4

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18362
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.1.100\

Host (useful for support):
  Version: 3.1.0
  Commit:  65f04fb6db

.NET Core SDKs installed:
  1.1.9 [C:\Program Files\dotnet\sdk]
  2.1.201 [C:\Program Files\dotnet\sdk]
  2.1.508 [C:\Program Files\dotnet\sdk]
  2.1.509 [C:\Program Files\dotnet\sdk]
  2.1.701 [C:\Program Files\dotnet\sdk]
  2.1.801 [C:\Program Files\dotnet\sdk]
  2.2.109 [C:\Program Files\dotnet\sdk]
  2.2.401 [C:\Program Files\dotnet\sdk]
  3.1.100 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 1.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 1.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
vid83 commented 4 years ago

I noticed a similar behavior with the ASPNETCORE_URLS variable: the value of the "Urls" section of the appsettings.json file always overrides the value of the environment variable.

I stumbled across this problem working on a asp.net 3.1 core application with docker support enabled, where the environment variable was defined in the docker-compose.override.yml file. Anyway, the same happens in a simple asp.net core 3.1 application, with the environment variable defined in the debug properties of the VS project. In both cases the host was built with the default builder.

Please note that the precedence works exactly as expected with the other variables: environment always wins on the appsettings.json file.

KoshelevS commented 3 years ago

Any news on this?

I've created the following extension method as a workaround, but it has not been battle-tested yet

        public static IWebHostBuilder FixEnvironmentParam(this IWebHostBuilder webBuilder, string[] args)
        {
            var configBuilder = new ConfigurationBuilder();
            configBuilder.AddCommandLine(args);
            var config = configBuilder.Build();

            var env = config[HostDefaults.EnvironmentKey];
            if (string.IsNullOrWhiteSpace(env))
            {
                return webBuilder;
            }

            return webBuilder.ConfigureAppConfiguration((host, _) => host.HostingEnvironment.EnvironmentName = env)
                             .UseEnvironment(env);
        }
wtgodbe commented 3 years ago

We will take a look at this in the new year once folks are back from vacation

ghost commented 3 years ago

Thanks for contacting us. We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

mguinness commented 3 years ago

In the documentation under Environment variable configuration provider there is the following sentence which probably explains the behavior reported by the OP.

The default configuration loads environment variables and command-line arguments prefixed with DOTNET_. The DOTNET_ prefix is used by .NET for host and app configuration, but not for user configuration.

halter73 commented 2 years ago

25273 seems related. It has to do with it not being obvious to what URL binding config overrides what.

adityamandaleeka commented 2 years ago

Looks like this behavior is documented here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-6.0#default-builder-settings

Admittedly, it could be a bit more clear about the fact that config loaded later in the order overrides earlier config.

adityamandaleeka commented 2 years ago

We should add a clear explanation in our docs of how the config is loaded from various places, in what order, and which things override others.

ghost commented 2 years ago

Thanks for contacting us. We're moving this issue to the .NET 7 Planning milestone for future evaluation / consideration. Because it's not immediately obvious that this is a bug in our framework, we would like to keep this around to collect more feedback, which can later help us determine the impact of it. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

halter73 commented 2 years ago

I think one option here is to give ASPNET_ environment variables the lowest precedence by default. So instead of adding GenericWebHostBuilder's chained config source at the end of the IConfigurationBuilder.Sources, we could add it to the beginning.

https://github.com/dotnet/aspnetcore/blob/b56f55330562bec2e69151e2f65ed93c127db7e9/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs#L36-L45

This would be a breaking change that would likely need to be announced as this would cause DOTNET_ environment variables to override ASPNETCORE_ environment variables instead of the opposite which happens today, but hopefully there aren't many people relying on that. I'm considering a similar change to WebApplicationBuilder in 7.0 when it's replatted on HostApplicationBuilder or whatever we end up calling it. https://github.com/dotnet/runtime/issues/61634.

adityamandaleeka commented 2 years ago

This should now be addressed in 7 since we did what @halter73 mentioned above.

halter73 commented 1 year ago

Reopening to consider changing the GenericWebHostBuilder to match WebApplicationBuilder's new behavior of loading ASPNET_-prefixed environment variables with the lowest precedence. We've already made this breaking change to WebApplicationBuilder. See https://github.com/aspnet/Announcements/issues/498

halter73 commented 1 year ago

I moved this to preview 1 since it's a relatively small change that we should introduce early since it's breaking.

adityamandaleeka commented 1 year ago

Triage: this seems low priority and the current behavior is now documented better. Closing this issue.