Using Environment Variables to Populate JSON Templates in .NET
I have a JSON document template for authenticating with a remote API. I don’t want to store actual secret credentials in the template. Instead, I want to read the secrets from the environment, populate the JSON payload using the template, and pass in the secret credentials.
I have been doing web development for a while but don’t have a lot of experience with .NET. While there may be better approaches and alternative designs that avoid this solution entirely, I saw this as a valuable learning opportunity and wanted to explore it.
My Approach
applicationSettings.json
This file stores the settings structure without any secret values, making it safe to commit to source control.
{
"MyAppSettings": {
"credentials": {
"credentialsKey1": {
"username": "",
"password": "",
"clientId": ""
}
}
}
}
Environment variables
Environment variables store the actual secret values. The .NET configuration provider nests the data using double underscores __
as separators.
Best practices for loading environment variables in my development environment was another thing I struggled with but will save that for a separate topic of discussion.
MyAppSettings__credentials__credentialsKey1__username=TheUsername
MyAppSettings__credentials__credentialsKey1__password=Password1
MyAppSettings__credentials__credentialsKey1__clientId=123456
Program.cs
In our Program.cs
file we can tell the configuration provider to load application settings from environment variables last so that they override anything in application settings.
builder.Configuration
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables();
Example
private object GetCredentialsObject(string credentialsKey, string jsonTemplate, IConfiguration configuration)
{
string json = jsonTemplate;
IEnumerable<System.Collections.Generic.KeyValuePair<string, string?>> credentials = configuration.GetSection("MyAppSettings").GetSection("credentials").GetSection(credentialsKey).AsEnumerable(true);
foreach ((string key, string? value) in credentials)
{
json = json.Replace( $"{{{key}}}", value);
}
return JsonSerializer.Deserialize<dynamic>(json);
}
public object TestGetCredentialsObject(IConfiguration configuration)
{
var credentialsKey = "credentialsKey1";
string jsonTemplate = @"
{
""grant_type"": ""password"",
""username"": ""{username}"",
""password"": ""{password}"",
""clientId"": ""{clientId}""
}";
return GetCredentialsObject(credentialsKey,jsonTemplate,configuration);
}
An important note is that this is case-sensitive so the case used between environment variables and application settings has to match.
Alternative Approaches
String Interpolation
I would need to hard code the json template for string interpolation to work as it is done at compilation. For this solution I wanted the json template to be able to change at runtime.
Using String.Format
This was a possible option that I explored but seemed more complex.
Others
I am sure there are alternative designs I could use. One may be to implement a class for each explicitly define the object and populate it with secret values.