We have a bunch of
appSettings in our ASP.NET web application, not to mention connection strings, that change on a per-environment basis. Not just for different environments to which the web application is deployed, but across different developer machines too. Therefore, changing these with a
Web.config transform isn't good enough because these transforms aren't carried out when developers debug the application.
At the moment the settings are hard-coded into
Web.config but if a developer wants to change them during development, this obviously means the change is flagged up in source control which is undesirable. These changes shouldn't be checked into source control.
How can we move these settings out so that they can be changed without affecting source control? In the past I have worked on projects where we used
configSource to point to an external config file which was then excluded from source control, and a
.config.example file was put in its place which had to be copied to the
.config file. The trouble with this is that by default, it gives a broken build because this config file is missing until it's created. We want a working build that can be pushed to VSTS for deployment. How can we do this while keeping the per-environment settings separate from source control?
It is not the job of the application to know the configuration for each and every environment. Instead, the environment should provide its configuration. That might happen through config files or environment variables, or any other mechanism.
If you were to provide a default configuration file, this config will always be wrong in some case. I once worked on a project where the default config was set up for a test environment. One day a colleague walked up to my desk: “Could you perhaps change your database connection? When you run your tests, this overwrites changes in my database instance.” Oops. They had committed their configuration, and I didn't know I had to change the defaults.
If the config were for a production environment, we have the additional difficulty that it might contain plaintext passwords (or other secrets). Even when you trust everyone who has access to the source, this is a bad practice and introduces unnecessary risk. For example, the risk of accidentally using parts of the production environment when running tests.
The only winning option is not to play. Do not provide a default config.
Instead, it is part of the build/deployment process to supply the correct configuration. You say “The trouble with this is that by default, it gives a broken build because this config file is missing until it's created. We want a working build that can be pushed to VSTS for deployment.” No. Part of the build is a pre-build script that creates the configuration file. This might be as simple as
copy config.buildServer config. It is unreasonable to expect the code to run without any environment-specific steps.