I decided to achieve the same result in a different way (having the opportunity to have different configuration settings for different target environments). So, here is how I implemented it, and it works great. I read some of the posts here in SO about the XmlMassUpdate task from the MSBuild Community Tasks and decided to use it. Here is what I did:
1) For each project, which should have different settings depending on the target environment, I added an xml file called app.config.substitutions.xml or web.config.substitutions.xml to the project. So the project looked like
Project A app.config app.config.substitutions.xml
The app.config.substitutions.xml file contains parameter substitutions that XmlMassUpdate will process and apply to the app.config file. The following is an example of the replacement file I am using:
<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate"> <substitutions> <Development> <appSettings> <add xmu:key="key" key="SomeSetting" value="DevValue" /> </appSettings> </Development> <Test> <appSettings> <add xmu:key="key" key="SomeSetting" value="TestValue" /> </appSettings> </Test> <Release> <appSettings> <add xmu:key="key" key="SomeSetting" value="ReleaseValue" /> </appSettings> </Release> </substitutions> </configuration>
For more information on how to specify substitutions, see the documentation for XmlMassUpdate or just search on it.
2) Now I need to run XmlMassUpdate as part of the build automation (TeamBuild / MSBuild). Therefore, in the BeforeCompile file in the TeamBuild assembly definition file (mainly in the proj file), I added the following to run XmlMassUpdate in the configuration files that have the corresponding .substitution.xml file
<PropertyGroup> <SubstitutionFileExtension>.substitutions.xml</SubstitutionFileExtension> <TargetEnvironment>Test</TargetEnvironment> </PropertyGroup> <Target Name="BeforeCompile" Condition="'$(IsDesktopBuild)'!='true'"> <CreateItem Include="$(SolutionRoot)\**\app.config;$(SolutionRoot)\**\web.config"> <Output ItemName="ConfigurationFiles" TaskParameter="Include"/> </CreateItem> <CreateItem Include="@(ConfigurationFiles)" Condition="Exists('%(FullPath)$(SubstitutionFileExtension)')"> <Output ItemName="ConfigFilesWithSubstitutions" TaskParameter="Include"/> </CreateItem> <Message Text="Updating configuration files with deployment target specific settings..."/> <XmlMassUpdate ContentFile="%(ConfigFilesWithSubstitutions.FullPath)" SubstitutionsFile="%(ConfigFilesWithSubstitutions.FullPath)$(SubstitutionFileExtension)" ContentRoot="/configuration" SubstitutionsRoot="/configuration/substitutions/$(TargetEnvironment)"/> </Target>
Please note that the configuration files are read-only during build, so you must set them up for writing before completing this task. Actually, I have another MSBuild custom task that runs before XmlMassUpdate, which handles general settings in all configuration files, such as connection strings. This task allows you to write configuration files. I also do not check the changed configuration files back to the original control. They (the appropriate configuration file for deployment purposes) are included in the installer.