0
votes

In order to avoid storing sensitive data into source control, we have User Secrets that can be used for development and Environment that can be used for production.

I'm trying to set up my project correctly. On my development machine, I want to be able to switch between Development and Production configuration values, and for Integration Tests, it must get access to Development configuration.

Currently, the Integration Tests configuration is hard-coded in the class library test project, and repeated in the appsettings.Development.json file, and I avoided creating appsettings.Production.json. That's not ideal.

Normally, User Secrets should be used for Development and Environment Variables for Production. If I set both, then Production configuration defined in Environment Variables will always overwrite the Development values. That won't work.

The other solution is creating appsettings.Staging.json with production values, and excluding that file from source control.

Another problem. Applying User Secrets and Environment variables on a class library won't be read by default. Only the configuration defined on the Web project is being read. Also, the Integration Tests project won't be able to access the configuration defined on the Web project.

This leads to 2 additional problems:

First, if I have 3 different Web projects that rely on the same class library that requires an API key, the API key needs to be configured 3 times.

Second, the Integration Test cannot use any of those configurations and needs to repeat the configuration a 4th time.

What would be the best way to configure the project? It's not looking like Host.CreateDefaultBuilder will provide an ideal solution. Creating a custom configuration builder might be the way to go, but before heading into that direction, I'd appreciate some input.

User Secrets are nice but they don't offer a way of switching them; and not using User Secrets means that all API keys and passwords are stored in plain text in source control. Perhaps the solution will be to force reading User Secrets for the class libraries, and depending on ASPNETCORE_ENVIRONMENT, read Secrets Manager for Development and Environment Variables for Staging or Production, but I don't know how to configure it in such a way.

2
how do you set environment variables so that secrets do not work for youYegor Androsov
Environment variables set in project settings always override other settings, so that doesn't offer me a way to switch from one configuration to the other. Out of all the configuration options, the only one offering such an option are the appsettings.Development.json / appsettings.Staging.json files.Etienne Charland

2 Answers

0
votes

No reply. OK here's what I ended up doing.

For integration tests, I read this article carefully: Integration tests in ASP.NET Core

Using the Microsoft.AspNetCore.Mvc.Testing package allows building the Dependency Injection container and thus testing the service configuration chain just like if you were running live.

This avoids duplicating the configuration for integration tests. One thing solved.

As for Secrets Manager, they're great for GitHub class libraries that are shared with others, but not so great for private projects that need switching environments.

For private code, I don't mind having the sandbox accounts in source control. However, I don't want live credit card processing credentials to be in there. Even if I'm working on the code myself, at some point I'll share the code with another programmer and I don't want them to access that data.

I've put the sandbox accounts in appsettings.Development.json, and the live accounts in appsettings.Staging.json. Then, make sure Staging config doesn't go into source control and add appsettings.Staging.json into .gitignore. Changing the environment variable from project properties allows me to switch environments. This seems to be the best I can do.

Just have to be careful for appsettings.Staging.json to be kept secret.

0
votes

Here's a better answer. You can create 2 sections: ApiKeys and ApiKeysDev, which means that the configurations don't overwrite each other. You just need a switch to read one or the other.

You can then store all the keys into Secrets Manager, but that only works when Environment is set to Development. Which means you can't use Env.IsDevelopment() as a switch.

I ended creating new environment variables: OntraportDev and BluePayDev. When set to "1" or "true", I read OntraportDev section instead of Ontraport. This allows me to switch individual services between Live and Sandbox accounts!

if (Environment.GetEnvironmentVariable("BluePayDev") == "1" || Environment.GetEnvironmentVariable("BluePayDev") == "true")
{
    services.AddOptions<BluePayConfig>()
        .Bind(configuration.GetSection("BluePayDev"))
        .ValidateDataAnnotations();
}
else
{
    services.AddOptions<BluePayConfig>()
        .Bind(configuration.GetSection("BluePay"))
        .ValidateDataAnnotations();
}

Another option would be to have the environment variable BluePayEnv, and when you set it to "Dev", it appends "Dev" to the configuration section. This would allow you configure several environments and would be a more flexible solution. One issue with this approach is that Visual Studio doesn't recognize "" as a valid environment variable value... so you can't configure for no suffix without deleting the variable.