C
C#3w ago
sl8er

Overriding appsettings.json section with environment variable

Given a configuration section called Tenants, I'd like to override it with a whole JSON structure via an environment variable. Given I want this
"Tenants": {
"ByCode": {
"11": {
"Name": "acme"
},
"42": {
"Name": "silver"
}
}
}
"Tenants": {
"ByCode": {
"11": {
"Name": "acme"
},
"42": {
"Name": "silver"
}
}
}
I'm doing
export Tenants='{"ByCode": {"11": {"Name": "acme"}, "42": {"Name": "silver"}}}'
export Tenants='{"ByCode": {"11": {"Name": "acme"}, "42": {"Name": "silver"}}}'
I'm trying to bind it against an object with the options pattern:
builder.Services.Configure<TenantsOptions>(
builder.Configuration.GetSection(TenantsOptions.SectionName));
builder.Services.Configure<TenantsOptions>(
builder.Configuration.GetSection(TenantsOptions.SectionName));
And the classes look like:
public class TenantsOptions
{
public const string SectionName = "Tenants";

public Dictionary<string, Tenant> ByCode { get; set; } = new();
}

public class Tenant
{
public string Name { get; set; }
}
public class TenantsOptions
{
public const string SectionName = "Tenants";

public Dictionary<string, Tenant> ByCode { get; set; } = new();
}

public class Tenant
{
public string Name { get; set; }
}
When I run the ASP.NET Core app, the ByCode dictionary is empty. But if I just paste the JSON section in appsettings.json, it's correctly populated. For the record, I can see the whole structure if I just log builder.Configuration.GetSection(TenantsOptions.SectionName).Value;, but for some reason, I guess it's only interpreted as a string, and not correctly deserialized and bound to TenantOptions? So how do you actually override a whole section with a JSON value through an environment variable?
16 Replies
FusedQyou
FusedQyou3w ago
I don't see how environment variables apply here
sl8er
sl8erOP3w ago
How's that?
FusedQyou
FusedQyou3w ago
I mean I don't see where environment variables are related to your question Is it that you load appsettings based on the environment? I suppose you should first check if the resulting configuration from builder.Configuration.GetSection(TenantsOptions.SectionName) returns the data you expect
sl8er
sl8erOP3w ago
I'm setting an env variable externally before running the app, like I mentioned: export Tenants='{"ByCode": {"11": {"Name": "acme"}, "42": {"Name": "silver"}}}'
FusedQyou
FusedQyou3w ago
You can also try to use GetRequiredSection
sl8er
sl8erOP3w ago
It does return the data I want, it's just that it doesn't bind to the options object.
sl8er
sl8erOP3w ago
Even the documentation on this seems to only mention cases where fields in a JSON object are set independently, rather than setting the whole object via a JSON string. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-9.0#evcp
set Position__Title=Environment_Editor
set Position__Name=Environment_Rick
set Position__Title=Environment_Editor
set Position__Name=Environment_Rick
Configuration in ASP.NET Core
Learn how to use the Configuration API to configure AppSettings in an ASP.NET Core app.
sl8er
sl8erOP3w ago
Like instead of doing export Position='{ "Title": "Environment_Editor", "Name": "Environment_Rick" }'
becquerel
becquerel3w ago
I don't think what you want is supported. My understanding is IConfiguration takes an environment variable as representing a single atom of configuration, not as something that can potentially represent an entire configuration section. If you set the env var to a JSON string, then IConfiguration will consider it as just a string. I think the 'right' way to do this would be to set up multiple environment variables to override each property of the structure. e.g. export TENANTS__ByCode = ["foo", "bar"]
Sehra
Sehra3w ago
you would have to specify multiple
export Tenants__ByCode__11__Name=acme
export Tenants__ByCode__42__Name=silver
export Tenants__ByCode__11__Name=acme
export Tenants__ByCode__42__Name=silver
but limitations are that it will get merged, by default with env overriding json, args overriding env
sl8er
sl8erOP3w ago
I really feared it wouldn't be supported. Doing export Tenants__ByCode__11__Name=acme for the huge actual structure I'm dealing with is really crazy. Maybe it can be suggested as a potential improvement.
Sehra
Sehra3w ago
you can add more json files if you want, add them as optional
becquerel
becquerel3w ago
Yeah, I feel like this is a bit of an x/y problem. why do you want to override a big json object with environment variables?
sl8er
sl8erOP3w ago
It's a Kubernetes secret injected into the environment. It's not such a novel idea. Other frameworks support this, like Spring. You can override the whole application config with a JSON-inline env variable SPRING_APPLICATION_JSON. Or just subkeys.
Sehra
Sehra3w ago
you could handle it in code by reading that envvar coming in to a dictionary and adding an InMemoryCollection that would override any existing. not possible to delete keys though, it would be merged
wasabi
wasabi3w ago
You can certainly write your own ICOnfiguratioSource and Provider. BUt if this is K8s, it would be better to just use KubernetesConfigurationProvider.

Did you find this page helpful?