dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.61k stars 25.3k forks source link

Need Clarification that Custom Prefixed Environment Variables DO NOT replace json settings #16171

Closed alwilton closed 4 years ago

alwilton commented 4 years ago

I spent considerable time reading this section over and over and the section on environment variables is not clear on the order of setting priorities. Actually is very misleading. The section in question is .ConfigureAppConfiguration((hostingContext, config) => { // Call additional providers here as needed. // Call AddEnvironmentVariables last if you need to allow // environment variables to override values from other // providers. config.AddEnvironmentVariables(prefix: "PREFIX_"); }) }

If I add the line as config.AddEnvironmentVariables(prefix: "MYAPP_"); as the last line and I have a secrets json file that includes "ConnectionStrings": { "IdentityServer4": "Server=192.168.1.1;Uid=xyz;Pwd=password;Database=IdentityServer4" } and I set an environment setting of MYAPP_ConnectionStrings__IdentityServer4="IdentityServer4": "Server=192.168.2.2;Uid=xyz;Pwd=password;Database=IdentityServer4"

The environment variable is not used, the secrets json file has priority - at least on .NET Core 3.1. If I remove the json file setting the environment setting is used.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

guardrex commented 4 years ago

Hello @alwilton ...

and I set an environment setting of MYAPP_ConnectionStrings__IdentityServer4="IdentityServer4": "Server=192.168.2.2;Uid=xyz;Pwd=password;Database=IdentityServer4"

Can we clarify your env var key first ... that key doesn't look right. Did you mean ...

Name: MYAPP_ConnectionStrings__IdentityServer4 Value: Server=192.168.2.2;Uid=xyz;Pwd=password;Database=IdentityServer4

Otherwise if that's ok, the order for setting config values is last setting wins, and loading config from env vars here (ConfigureAppConfiguration) should do the trick.

alwilton commented 4 years ago

Yes – your correction is correct. BUT NO (!) the “last setting wins, and loading config from env vars here (ConfigureAppConfiguration) should do the trick” does not win in after my afternoon of work today. In my testing unless I remove the secret json setting it was the winner

Thanks for your time on this.

From: Luke Latham notifications@github.com Sent: Thursday, December 12, 2019 5:38 PM To: aspnet/AspNetCore.Docs AspNetCore.Docs@noreply.github.com Cc: Al Wilton alwilton@azpcsuccess.com; Mention mention@noreply.github.com Subject: Re: [aspnet/AspNetCore.Docs] Need Clarification that Custom Prefixed Environment Variables DO NOT replace json settings (#16171)

Hello @alwiltonhttps://github.com/alwilton ...

and I set an environment setting of MYAPP_ConnectionStrings__IdentityServer4="IdentityServer4": "Server=192.168.2.2;Uid=xyz;Pwd=password;Database=IdentityServer4"

Can we clarify your env var key first ... that key doesn't look right. Did you mean ...

Name: MYAPP_ConnectionStrings__IdentityServer4 Value: Server=192.168.2.2;Uid=xyz;Pwd=password;Database=IdentityServer4

Otherwise if that's ok, the order for setting config values is last setting wins, and loading config from env vars here (ConfigureAppConfiguration) should do the trick.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/aspnet/AspNetCore.Docs/issues/16171?email_source=notifications&email_token=ABGU43VOF4A22LTHTWW6HDTQYLKQFA5CNFSM4J2F6IP2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGYQUFY#issuecomment-565250583, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ABGU43QK527OAKCESH6L2YTQYLKQFANCNFSM4J2F6IPQ.

guardrex commented 4 years ago

Paste your Program class into a comment here.

alwilton commented 4 years ago

[EDIT by guardrex to update the code block]

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;
using System;
using System.Linq;

namespace MYAPP
{
    public class Program
    {
        public static int Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("System", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
                .Enrich.FromLogContext()
                .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate)
                .CreateLogger();

            try
            {
                var host = CreateHostBuilder(args).Build();
                Log.Information("Starting host...");
                host.Run();
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly.");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    // Call additional providers here as needed.
                    // Call AddEnvironmentVariables last if you need to allow
                    // environment variables to override values from other 
                    // providers.
                    config.AddEnvironmentVariables(prefix: "MYAPP_");
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseSerilog();
                });
    }
}
guardrex commented 4 years ago

That looks good.

I just ran a test here with the Secret Manager, the Env Var Config Provider, a prefixed env var, and a double-underscore key ... I can't repro the behavior. It works as it should ... the Secret Manager supplies the value without the env var, and setting the env var causes the Secret Manager value to be overridden with the env var value.

The Secret Manger secrets are loaded here ...

https://github.com/aspnet/Extensions/blob/master/src/Hosting/Hosting/src/Host.cs#L82

... via CreateDefaultBuilder, and every configuration item adheres to last setting wins by design. When env vars are loaded in ConfigureAppConfiguration, existing values with the same key are overridden.

I'm not sure what the problem is. You can try a support forum, such as Stack Overflow, to get support. We can't offer individual devs support here. We just work on the docs. There's no error or omission that I'm aware of in this topic (or in the Generic Host topic for CreateDefaultBuilder), so there's nothing actionable at this time given that I can't repro the problem and don't know what the problem is.

Try Stack Overflow with your full set of steps. If you find out that we are missing a gotcha! point that should be called out in the topic, ping me back over here, and we can re-open this issue to discuss and possibly cover it.

alwilton commented 4 years ago

thanks for the look. I'll try a very simple test case and follow your advice if the behavior persists.

guardrex commented 4 years ago

Good idea ... start with a fresh minimal test app and run your steps from scratch.

I set up a MYAPP_Something__AnotherThing env var in my launch.json file (I'm on VSC) ...

"env": {
  "ASPNETCORE_ENVIRONMENT": "Development",
  "MYAPP_Something__AnotherThing": "THE ENV VAR VALUE"
},

I added the Env Var Config Provider as you did with that prefix (config.AddEnvironmentVariables(prefix: "MYAPP_");). I initialized the Secret Manager for the app and put a Something:AnotherThing secret in with a different value ("THE SECRET VALUE"). I had a Razor page show me the configuration that it was loading. First run: env var value came up because it was loaded last. Yanked the env var from launch.json and ran again: secret value came up confirming that the Secret Manager was working ok.

For quick-and-dirty look at what it thinks its configuration is, you can use something like this in a Razor page ...

@page
@model IndexModel
@using Microsoft.Extensions.Configuration
@using Microsoft.Extensions.Hosting
@inject IConfiguration Configuration
@inject IHostEnvironment Environment

<p>Config values:</p>
<p>
    <ul>
        @if (!@Environment.IsProduction())
        {
            foreach (var kvp in Configuration.AsEnumerable().OrderBy(c => c.Key))
            {
                <li>@kvp.Key : @kvp.Value</li>
            }
        }
    </ul>
</p>