I have an MVC application with a Dev, Staging, and Production environment. Dev and Staging are essentially the same thing (same VM, IIS, DB etc.); however, Production is hosted on 4 VMs behind a load balancer. Each VM has it's own DB. For example, the instance deployed to VM1 communicates with the PROD1 DB, VM2->PROD2, etc.
For deployment to Dev and Staging, I do a simple File System deployment from VS2013 to the VM using Debug/Release web.config transforms. For Production deployments, a SysAdmin will copy the bits deployed and tested in Staging to each Production VM. This is to ensure that what was tested and verified by QA in Staging is what we promote to Production -- I don't want to do another build between Staging and Production. Because of this, our SysAdmin is responsible for (with DevOps guidance) editing each web.config between Staging and Production. This basically consists of changing connectionString
values from "Data Source=STAGINGDB"
to "Data Source=PROD1"
(and PROD2, PROD3, PROD4).
What I ultimately want is when I publish to Staging, I want to deploy my web.config using standard Release web.config transform; however, alongside this file I want to also create and drop 4 additional files (web.config.PROD1, .PROD2, etc.). This will allow us to create scripts which ignore the existing web.config (with Staging settings) and copy/rename the appropriate .PROD config.
I am able to (sort of) achieve this with MSBuild:
<Project ToolsVersion="12.0" DefaultTargets="Build">
...
<Target Name="Build">
<TransformXml Source="Web.config" Transform="Web.PROD1.config" Destination="Web.config.PROD1" />
<TransformXml Source="Web.config" Transform="Web.PROD2.config" Destination="Web.config.PROD2" />
...
</Target>
</Project>
My main issue with this approach is that I have to create 4 essentially redundant solution configurations to wire up to the Transform. Every setting is the same except the DB connectionString
. Seems like there should be a more efficient way.
Can I execute individual transforms without solution configurations by simply calling the appropriate transform via MSBuild, like:
<add name="connectionString" connectionString="PROD1" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
Should I be using another process altogether? I'd rather not use a 3rd party nuget solution if I can stay way from it. Should I be using a .wpp.targets file? XmlPoke?
Everything I've read leads me to believe that I should be writing custom MSBuild steps, but I don't know what I should be doing (or how). Here's some pseudo-code:
<Project ToolsVersion="12.0" DefaultTargets="Build">
...
<Target Name="Build">
<TransformXml Source="Web.config" Transform="[Do-Basic-Transform-On-Conection-String]" Destination="Web.config.PROD1" />
<TransformXml Source="Web.config" Transform="[Do-Basic-Transform-On-Conection-String]" Destination="Web.config.PROD2" />
<IncludeFilesInPublish>
<FileToInclude>Web.config.PROD1</FileToInclude>
<FileToInclude>Web.config.PROD2</FileToInclude>
</IncludeFilesInPublish>
</Target>
</Project>
[Do-Basic-Transform-On-Connection-String]
inline here without a solution configuration? I'll only be changing 2 connectionString
values. If I need to create a solution config, that's fine... I just don't think it's totally necessary especially if I can do it inline. Maybe I'm wrong?<IncludeFilesInPublish>
bit so that whatever I do get's packaged up during the Publish, so my Staging deployment has my release candidate code and web.configs ready for promotions.You can add the extra transform files to your solution without adding a new solution configuration, and run the transform as in your own example (except for the target. 'Build' didn't work for me):
<Project ToolsVersion="12.0" DefaultTargets="Build">
...
<Target Name="BeforeBuild">
<TransformXml Source="Web.config" Transform="Web.PROD1.config" Destination="Web.config.PROD1" />
<TransformXml Source="Web.config" Transform="Web.PROD2.config" Destination="Web.config.PROD2" />
...
</Target>
</Project>
If you are only changing a connectionstring, your transform file will be pretty minimal:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=PROD1SQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
</configuration>
After adding the above, and compiling, you will need to add the newly generated Web.config.PRODX files to your solution. After adding them, you simply open properties for each file and ensure that their compile action is set to 'Content'. This will mean that they are included in your deployments.
As the web.PRODX.config transform files are not part of a solution configuration, you could stick them in a folder to reduce clutter.