Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expand environment variables in appSettings.json file

Is there a way "out of the box" to have environment variables in appsettings.json values expanded automatically?

To take a contrived example:

{
  ...
  "MyPath1": "C:/MyApp/%ENV%/Myfolder1/MyFile1.dat",
  "MyConnectionString":  "Server=MyServer%ENV%..."
  ...
}

My objective is to be able to switch my environment by changing a single environment variable, and have it affect multiple appsetting values, rather than having per-environment configuration files.

UPDATE

I've looked through the source code of JsonConfigurationProvider and as far as I can see there is no such feature out of the box.

I can see that it should be possible by deriving a custom class from JsonConfigurationProvider and overriding the Load() method:

public override void Load()
{
    base.Load();
    foreach(var key in Data.Keys)
    {
        Data[key] = Environment.ExpandEnvironmentVariables(key);
    }
}

But I'm quite new to .NET Core configuration, which leads to a second question:

How do I get this custom implementation to replace the standard one? I.e. to remove the default appsettings.json and appsettings.environment.json providers and replace by the custom one. Presumably something to be added in Program.cs:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureAppConfiguration(config =>
        {
            config.??? what do I need here?
        }
        );
like image 695
Joe Avatar asked Mar 14 '20 12:03

Joe


People also ask

What is the difference between Appsettings json and Appsettings development json?

NET Core and as far as I see from my search on the web, appsettings.Development. json is used for development config while developing the app and appsettings. Production. json is used on the published app in production server.

Can we have multiple Appsettings json?

Of course, we can add and use multiple appsettings. json files in ASP.NET Core project. To configure and read data from your custom json files, you can refer to the following code snippet.


2 Answers

To create and use a custom configuration source, two implementations are needed:

  1. IConfigurationSource
  2. IConfigurationProvider

It's the IConfigurationSource implementation that gets added to the IConfigurationBuilder's sources. This implementation is also responsible for creating its own IConfigurationProvider, which is reponsible for loading the data from the source.

Here's both a custom implementation of IConfigurationSource (ExpandJsonConfigurationSource) and IConfigurationProvider (ExpandJsonConfigurationProvider):

public class ExpandJsonConfigurationSource : JsonConfigurationSource
{
    public override IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        EnsureDefaults(builder);
        return new ExpandJsonConfigurationProvider(this);
    }
}

public class ExpandJsonConfigurationProvider : JsonConfigurationProvider
{
    public ExpandJsonConfigurationProvider(ExpandJsonConfigurationSource source)
        : base(source) { }

    public override void Load()
    {
        base.Load();
        Data = Data.ToDictionary(
            x => x.Key,
            x => Environment.ExpandEnvironmentVariables(x.Value),
            StringComparer.OrdinalIgnoreCase);
    }
}

There's not a lot going on here. These implementations just follow the same approach as the existing JsonConfigurationSource and JsonConfigurationProvider implementations, with custom processing for expanding the environment variables.

To replace the preconfigured JSON-based sources, use the following:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureAppConfiguration((ctx, builder) =>
        {
            var jsonConfigurationSources = builder.Sources
                .OfType<JsonConfigurationSource>()
                .ToList();

            foreach (var jsonConfigurationSource in jsonConfigurationSources)
            {
                var indexOfJsonConfigurationSource = builder.Sources
                    .IndexOf(jsonConfigurationSource);

                builder.Sources.RemoveAt(indexOfJsonConfigurationSource);
                builder.Sources.Insert(
                    indexOfJsonConfigurationSource,
                    new ExpandJsonConfigurationSource
                    {
                        FileProvider = jsonConfigurationSource.FileProvider,
                        Path = jsonConfigurationSource.Path,
                        Optional = jsonConfigurationSource.Optional,
                        ReloadOnChange = jsonConfigurationSource.ReloadOnChange
                    });
            }
        });

The delegate passed in to ConfigureAppConfiguration does the following:

  1. Locate all configured instances of JsonConfigurationSource. There should be two instances: for appsettings.json and for e.g. appsettings.Development.json.
  2. For each instance:
    1. Determine the position of the instance within the preconfigured sources.
    2. Remove the instance.
    3. Replace the removed instance with an instance of ExpandJsonConfigurationSource, copying across the properties to ensure it has the same path, optional flag, etc.
like image 189
Kirk Larkin Avatar answered Oct 17 '22 14:10

Kirk Larkin


I created a library to perform variable substitution, it substitutes from any configuration source, not just environment variables. See: https://github.com/molinch/ConfigurationSubstitutor

You just need to register it as another configuration source, this is done by calling .EnableSubstitutions() on the ConfigurationBuilder.

With your example:

  • if in appsettings.json you have: "MyPath": "C:/MyApp/%ENV%/Myfolder1/MyFile1.dat"
  • and you have the following environment variable: ENV = Foo
  • then you should enable substitutions so:
.EnableSubstitutions("%", "%")

Finally when requesting MyPath from the configuration you will get: C:/MyApp/Foo/Myfolder1/MyFile1.dat

like image 23
Fabien Avatar answered Oct 17 '22 16:10

Fabien