I'm using the .NET Core 1.1 in my API and am struggling with a problem:
I need to have two levels of configurations: appsettings.json and environment variables.
I want to use the DI for my configurations via IOptions.
I need environment variables to override appsettings.json values.
So I do it like this so far:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Something here
services.Configure<ConnectionConfiguration>(options =>
Configuration.GetSection("ConnectionStrings").Bind(options));
// Something there
}
With my appsettings.json composed like this
{
"ConnectionStrings": {
"ElasticSearchUrl": "http://localhost:4200",
"ElasticSearchIndexName": "myindex",
"PgSqlConnectionString": "blablabla"
}
}
I get all the configurations mapped to my class ConnectionConfiguration.cs. But I cannot get the environment variables to be mapped as well. I tried the names like: ConnectionStrings:ElasticSearchUrl, ElasticSearchUrl, even tried specifying the prefix to .AddEnvironmentVariables("ConnectionStrings") without any result.
How should I name the environment variables so it can be mapped with services.Configure<TConfiguration>()?
The : separator doesn't work with environment variable hierarchical keys on all platforms. __, the double underscore, is supported by all platforms and it is automatically replaced by a :
Try to name the environment variable like so ConnectionStrings__ElasticSearchUrl
Source
Have a look at ASP.NET Core Configuration with Environment Variables in IIS
To do the same with environment variables, you just separate the levels with a colon (:), such as HitchhikersGuideToTheGalaxy:MeaningOfLife.
In your case you'll use ConnectionStrings:ElasticSearchUrl as mentioned in your question.
Also notice the following:
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".
I believe you're looking for this:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
I've 3 configurations
dev-final
dev-local
dev-test
And 4 *.json files
appsettings.json
appsettnigs.dev-final.json
appsettings.dev-local.json
appsettings.dev-test.json
appsettings.json holds global configuration values, and the other files specific ones.
Related
I'm using .NET 6 and I want to achieve 2 things:
Using a single appsettings.json file instead of maintaining multiple versions of appsettings.{environmentname}.json for different environments
Remove hard-coding from the appsettings.{environmentname}.json file to allow (1)
(Currently I'm hardcoding secrets like DB connection string, key vault configration details etc. for different environments in respective appsettings files which I want to remove and instead read the same secret values from Azure Key Vault where I'm already storing the same)
How can I achieve these?
Below code snippet is how I'm maintaining 2 appsettings.{environmentname}.json files(Development & Release):
var builder = WebApplication.CreateBuilder(args);
var hostingEnvironment = builder.Environment;
if (hostingEnvironment.IsDevelopment())
{
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.Development.json", false, true)
.AddEnvironmentVariables();
}
else
{
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.Release.json", false, true)
.AddEnvironmentVariables();
}
Note: I did some research and understood that for (1) we can probably do something like:
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable(
"ASPNETCORE_ENVIRONMENT")}.json", false, true)
.AddEnvironmentVariables();
But then, assuming the fact that the app would be automatically deployed every time leveraging CI/CD, can I set the value of ASPNETCORE_ENVIRONMENT in the pipeline?
As for, (2), I tried removing hardcoded secrets from appsettings.{environment}.json files and configured key vault like below but it doesn't work:
builder.Services.Configure<KeyVaultConfig>(options => builder.Configuration.GetSection("KeyVaultConfig").Bind(options));
if (!string.IsNullOrEmpty(builder.Configuration["KeyVaultConfig:KeyVaultUrl"]))
{
var credentials = new ClientSecretCredential(builtConfig["KeyVaultConfig:TenantID"], builtConfig["KeyVaultConfig:ClientID"], builtConfig["KeyVaultConfig:ClientKey"]);
var secretClient = new SecretClient(new Uri(builtConfig["KeyVaultConfig:KeyVaultUrl"]), credentials);
//builder.Configuration.AddAzureKeyVault...
}
If in the end you are going to use CI/CD you can only need 2 files (appsettings.json and appsettings.Development.json) and the default C# handling of these.
In the development one you hardcode all the configuration you need for dev.
In the standard one, you put keys that would be replaced during the CI/CD pipelines.
for example
in appsettings.development.json you have
"ConnectionStrings": {
"myDb": "Server=myDbServer;Database=myDb;User Id=userDev;Password=myPassword;"
}
in appsettings.json you have
"ConnectionStrings": {
"myDb": "#{connectionString}#"
}
And then replace "#{connectionString}#" with the environment value in the CI/CD pipelines configuration
If you really want to have only one file, you could try Json variable substitution like documented here documented here during the CI/CD pipelines, but the first approach allows you to handle secrets with vault more easily
I use to set some environment variable in order to overwrite appsettings values that are specific to my dev machine (ie connection strings, local endpoints etc). I set them in User variables for my account for example Variable Name: ConnectionStrings:DbConnectionString, Variable value: ******************
Then I call in Main method (of a .NET core 3.1 web app):
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builderContext, config) =>
{
var env = builderContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
config.AddCommandLine(args);
})
This works pretty fine overwriting appsettings values in connectionstrings and in few custom Options classes, but I can't make it read GUID values. I tried writing them with or without double apices, but they're are ignored, while they're correctly loaded and parsed from appsettings.
If I change the type of the property in my Options class to String then the GUID is loaded as string from env variables as expected.
So, as a workaround, I ended up loading the value in a string property and converting it to GUID in a different property of the Options class.
It works, but I'd like to understand whether there's a cleaner way to do that. Is there a special syntax to write GUID in env variables?
Thanks
After some more tests, I found out the issue was about the syntax of the Guid: while in appsettings file you can write it with hyphens (ie 0713d50d-e508-4212-b174-7582a5c90224), in windows env variable the value has to be written without hyphens and without quotes, either single or double (ie ee1b55626c8148aab62cc67dc7f6010e).
In this way it's correctly read a parsed as Guid (if it's a valid Guid of course)
In a .net core application, why I am always in "production", even if the ASPNETCORE_ENVIRONMENT is 'development'?
class Program {
static void Main(string[] args) {
var builder = new ConfigurationBuilder();
BuildConfig(builder);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Build())
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
// ...
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) => {
switch (serviceToUse) { // ... } })
.UseSerilog()
.Build();
IService svc;
// ... got the corresp service...
svc.Run();
}
static void BuildConfig(IConfigurationBuilder builder) {
string env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? "Production";
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env}.json", optional: true)
.AddUserSecrets<Program>()
.AddEnvironmentVariables();
}
}
However, when I test
I get rather unexpected values in the test setting:
[13:36:55 ERR] Environment.GetEnvironmentVariable('ASPNETCORE_ENVIRONMENT')=Development
[13:36:55 ERR] Environment.GetEnvironmentVariable('DOTNET_ENVIRONMENT')=Development
[13:36:55 ERR] The test setting is 'test value from secrets.json';
having this in projet:
My question is why does it take the Production settings if I am in Development mode?
It’s unclear to me where you are retrieving the configuration using this._config.GetValue<string>("test"). My guess is that you are retrieving it from the IConfiguration injected via DI.
In that case, you should note that the Host.CreateDefaultBuilder will also already come with its own configuration setup which does not really care about what you are doing before with your BuildConfig.
The default host builder will already set up configuration using the files appsettings.json and appsettings.<env>.json (and others) and will register that configuration as the IConfiguration singleton that is being provided to your application. In order to register the environment-specific file, it will use the environment from the IHostEnvironment which is what decides what actual environment your application is running on.
With ASP.NET Core and the web host, it is possible to configure this environment using the ASPNETCORE_ENVIRONMENT environment variable. However, since you are not using ASP.NET Core but just the host builder on its own, this environment variable is not being used. Instead, the default host builder will look for the environment variable DOTNET_ENVIRONMENT.
Alternatively, you can also call hostBuilder.UseEnvironment to set the environment explicitly if you want to take it from a different environment variable.
Finally, when you are setting up your own configuration, I would suggest you to reconfigure your host so that it will use this configuration object itself instead of building its own (which may or may not match your desired setup). You can do that by calling hostBuilder.ConfigureAppConfiguration.
I am trying to provide some configuration data for my application so that we have simple settings stored in the source control, but our deployment system will replace the Encryption Key in appsettings.json when it deploys.
For development I still want a general key, but for specific users in their secrets I would like to provide a value that they can be sure will be secure.
I have my configuration files setup like this:
appsettings.json
{
"SystemConfiguration" : {
"EncryptionKey" : "weak"
}
}
appsettings.Development.json
{
"SystemConfiguration" : {
"EncryptionKey" : "devweak"
}
}
In my user secrets
{
"SystemConfiguration" : {
"EncryptionKey" : "this is a secret"
}
}
In my controller construction I'm getting the injected IConfiguration configuration.
And then
public SysConfigController(IConfiguration configuration)
{
var result = configuration["SystemConfiguration:EncryptionKey"];
}
but the value of result is always "weak" unless the higher level settings files don't contain the value at all (: null) doesn't work either.
Is there a way that I can retrieve the lowest level value?
Seems your configuration registrations are wrong. The last registration which contains the specific key will be used.
For example in your Program.cs file (assuming you are using ASP.NET Core, otherwise Startup.cs's constructor for ASP.NET Core 1.x) you can override the registrations (or just add the ones you like when you use the default host builder method):
public static IWebHostBuilder BuildWebHost(string[] args) =>
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddUserSecrets<Startup>()
.AddEnvironmentVariables()
.AddCommandLine(args);
})
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights();
In this example the following have been registered
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true):
Global configuration file
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
Enviornmentspecific configuration file, i.e. appsettings.Development.json
.AddUserSecrets<Startup>()
The user secrets
.AddEnvironmentVariables()
Environment variables
.AddCommandLine(args);
Command line parameters
When looking up for a key, the search happens in reverse order. If its defined as command line parameter, it will override all the other keys and values (from environment variable, user secrets, environment specific and global file).
So put the least important file at the beginning and most important (overriding) one at the end.
In this case if its defined in your user secrets, it will override the appsettings.Development.json and appsettings.json provided values.
From the docs
Configuration sources are read in the order that their configuration providers are specified at startup. The configuration providers described in this topic are described in alphabetical order, not in the order that your code may arrange them. Order configuration providers in your code to suit your priorities for the underlying configuration sources.
A typical sequence of configuration providers is:
Files (appsettings.json, appsettings..json, where is the app's current hosting environment)
User secrets (Secret Manager) (in the Development environment only)
Environment variables
Command-line arguments
It's a common practice to position the Command-line Configuration Provider last in a series of providers to allow command-line arguments to override configuration set by the other providers.
You can use method ConfigureServices(IServiceCollection services) in Startup.cs.
Describe your setting property in class and bind it. For example:
services.Configure<SystemConfiguration>(options => Configuration.GetSection("SystemConfiguration").Bind(options));
public class SystemConfiguration
{
public string EncryptionKey{get;set;}
}
Then, you can use DI for geting class
public class SomeClass
{
private readonly SystemConfiguration _systemConfiguration{get;set;}
public SomeClass (IOptions<ConfigExternalService> systemConfiguration)
{
_systemConfiguration = systemConfiguration;
}
}
I have a Visual Studio 2015 solution made up of projects targeting DNX framework. I have been working locally but I plan to deploy to Azure environments (dev/test/prod). Naturally, the solution uses different database connection strings and other variables dependent on the environment. In the past I made use of cloud configuration files to set these variables, reading them with the CloudConfigurationManager.
I am given to understand that I need to use a config.json file. I have the following code in my Startup.cs file:
public Startup(IHostingEnvironment env, IApplicationEnvironment app)
{
Configuration = new ConfigurationBuilder(app.ApplicationBasePath)
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
Configuration.Set("ASPNET_ENV", "Development");
}
My config.json file is currently an empty object { }. How do I add variables to this file (syntax?), and how do I access them from code?
Note this line in your startup code:
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
This adds an additional JSON config file which is named depending on your environment name. So add a new file to your project called:
config.<environment-name>.json
And set up the details in there, such as:
{
"AppSettings": {
"SomeVariable": "Blah"
},
"Data": {
"YourConnectionString": {
"ConnectionString": "<connection string here>"
}
}
}
For reading the configuration, there's a good answer here: Using IConfiguration globally in mvc6
You can use plain old JSON for that. For example, here's how to define a connection string:
{
"Foo": "Bar"
}
To access the configuration value, you do:
config.Get("username")
or:
config["username"];
You can find more information here: http://docs.asp.net/en/latest/fundamentals/configuration.html