DCSIMG
Google+ Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task - Doron's .NET Space

Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

I've been looking for the easiest possible way to deploy a web.config file to several different environments. My web.config looks something like this in development:

<configuration> <appSettings> <add key="LogEveryRequest" value="true" /> <add key="EnableCaching" value="false" /> <add key="DefaultGisServer" value="127.0.0.1" /> </appSettings> <connectionStrings> <add connectionString="UserName=scott,Password=tiger" name="DBConnection" /> </connectionStrings> <system.web> <compilation debug="true"> </compilation> </configuration>

I need the ability to change specific values in the test and production (release) environments. For instance, the connection string is obviously different, and I would also like to turn off logging and debugging in production.

I'm using a Web Deployment Project, so I have MSBuild at my disposal. Now, the WDP comes with an option to replace specific configuration sections, but that option is not powerful enough, as it requires you to replace an entire configuration section with another. For example, in the above configuration file I might want to change only the EnableCaching property. I don't want to keep 3 versions of the same appSettings section, only to be able to change just that single property.

What I needed turned out to be in the MSBuild Community Tasks project. Specifically, it contains a very powerful task called XmlMassUpdate. This task allows me to specify a substitution file that looks like this:

<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate"> <substitutions> <Debug> </Debug> <Test> <appSettings> <add xmu:key="key" key="GisServer" value="134.122.34.3" /> <add xmu:key="key" key="EnableCaching" value="true" /> </appSettings> <connectionStrings> <add connectionString="UserName=TestUser;Password=Testpass" name="DBConnection" /> </connectionStrings> <system.web> <compilation debug="false"/> </system.web> </Test> <Release> <appSettings> <add xmu:key="key" key="GisServer" value="181.3.12.123" /> <add xmu:key="key" key="EnableCaching" value="true" /> <add xmu:key="key" key="LogEveryRequest" value="false" /> </appSettings> <connectionStrings> <add connectionString="UserName=ProdUser;Password=Prodpass" name="DBConnection" /> </connectionStrings> </Release> </substitutions> </configuration>

As you can see, for debug I'm leaving everything as it is. For Test and Release I'm changing some of the properties. I want logging on in the Test environment, so I don't even mention it in my substitutions file, but for production it has to be changed to false. Other than that, everything here is pretty straight forward. I change only what I need to.

One thing to notice, though, is that the appSettings values have an attribute called xmu:key. This attribute is needed for the replacement engine to know by which attribute to locate the value to replace. For example, look at the first substitution in the test environment:

<add xmu:key="key" key="GisServer" value="134.122.34.3" />

We are telling XmlMassUpdate to find the GisServer value in the original web.config file, and replace it with this one. But XmlMassUpdate needs to know how to match our substitution value with the original value, so by saying xmu:key=key we're telling it to compare them by the key attribute. This way, it will know that it should look for an appSettings setting with a key="GisServer" attribute.

Once the substitution file is ready, we have to edit the MSBuild project file to call XmlMassUpdate. For that you would first need to right click your Web Deployment project and hit "Open Project File". Than you have to add the following lines to it:

1 <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" /> 2 <PropertyGroup> 3 <BuildDependsOn> 4 $(BuildDependsOn); 5 MyAfterBuild 6 </BuildDependsOn> 7 <SubstitutionsFilePath>$(SourceWebPhysicalPath)\substitutions.xml </SubstitutionsFilePath> 8 </PropertyGroup> 9 <ItemGroup> 10 <ExcludeFromBuild Include="$(SubstitutionsFilePath)"/> 11 </ItemGroup> 12 <Target Name="MyAfterBuild"> 13 <XmlMassUpdate 14 ContentFile="$(OutputPath)web.config" 15 SubstitutionsFile="$(SubstitutionsFilePath)" 16 ContentRoot="/configuration" 17 SubstitutionsRoot="/configuration/substitutions/$(Configuration)"/> 18 </Target>

First we import the MSBuild Community Task target file. This will allow us to use the XmlMassUpdate task (of course you have to install the community tasks project first for this to work).

In lines 3-6 we're adding a build event called MyAfterBuild. In fact the WDP project already contains a target called AfterBuild, but I found out that you can't really use it. If you specify an AfterBuild target, you will override the WDP AfterBuild target, which actually copies the web site files to the target directory. If you override that, well, your build won't work. So I'm specifying that the build should also depend on my MyAfterBuild target.

In line 7 I'm specifying that my substitution file resides in the original source web, in a file called substitutions.xml.

In line 10 I'm making sure the substitution file will not be deployed together with the web site (might be a bad idea to deploy all these connection strings).

In lines 12-18 we're finally calling XmlMassUpdate.

  • ContentFile tells it where to find the web.config to edit (in the output location, usually).
  • SubstitutionsFile tells it where to find our substitutions.xml.
  • ContentRoot tells it that in the original web.config, the xml path to start replacing stuff at is the configuration node.
  • SubstitutionsRoot tells it that in our substitutions.xml, the task should take values to replace from the specified xml path. For example, when we compile in debug it will take values from /configuration/substitutions/debug, and when we compile in release it will be taken from /configuration/substitutions/release. In order to enable our Test environment substitutions, we will have to add another build configuration. That's as easy as going to Build->Configuration Manager and adding a new solution configuration called Test.

And there you go. Save and recompile your project, and you will find that the web.config in your output folder has the correct values, depending on the build configuration you chose.

Go ahead and download the MSBuild Community Tasks now. They have a pretty good documentation, which helped me a lot in finding this elegant solution and writing this post.

Comments

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Thursday, January 31, 2008 4:10 PM by jerschmidt14@hotmail.com

Hi,

  It does not seem that the $(OutputPath) is returning the correct path to my _publishedwebsites\ITMonitoring_deploy directory.  I tried changing it to point to the source, but as TFSBuild is only doing a get on the files, they are read only at the time.  In the deployment directory, the file are not read only, so it would work.  Problem is that the outputpath is not pointing to the correct place, so i get a file not found.  Any ideas?  I am using a web site rather than a web project, and have added a web deployment project to it.  

Cheers,

Jeremy

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Thursday, January 31, 2008 4:37 PM by jerschmidt14

Hi,  

I have some more information.  Seems that TFSBuild is substituting in a new outputpath without changing the value of the variable.  The error message I am getting is

Solution: ITMonitoring.sln, Project: ITMonitoring_deploy.wdproj, Compilation errors and warnings

e:\NETWebApps\DevTools\ITMonitoring\Sources\ITMonitoring_deploy\ITMonitoring_deploy.wdproj(60,6): error :

Unable to load content file e:\NETWebApps\DevTools\ITMonitoring\Sources\ITMonitoring_deploy\web.config

=====

The Correct path to the web.config file is

e:\NetWebApps\DevTools\ITMonitoring\Binaries\Mixed Platforms\Release\_PublishedWebsites\ITMonitoring_deploy

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Thursday, January 31, 2008 7:45 PM by dorony

That's weird, it seems to work for me on both a Web Application and on a Web Site.

You seem to mention specifically TFSBuild. Is this working for you when you compile within Visual Studio?

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Wednesday, April 09, 2008 3:12 PM by Juan

It isn't working for me either. In my case, I'm building/compiling a web service and the OutputPath points to the /bin were the final dlls are put. The web.config doesn't exist there so it fails.

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Thursday, April 10, 2008 9:54 PM by Mike Powell

This was a great walkthrough on the initially-confusing XmlMassUpdate task.  Thanks for getting me up to speed!

One thing I discovered was that using the  ExcludeFromBuild item triggers an extra site copy operation that almost doubles the build time.  Instead I used a Delete task after the XmlMassUpdate to delete the substitutions file from the output folder, and my build is a lot faster now.

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Saturday, April 12, 2008 2:20 PM by dorony

Hey Juan,

I'm not sure why is that the case for you, but you can point the task to wherever the web.config is. If OutputPath doesn't work there is probably some other property you can use, or you can simply try something like $(OutputPath)\..\web.config.

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Wednesday, June 25, 2008 5:13 PM by Alex Nilsson

Hi, having problem with multipe connectionstrings, what´s the syntax if I have many connectionsstrings, like this:

web.config:

<connectionStrings>

<add name="A" connectionString="Database=ADb;Server=server;

UID=a;PWD=a;" providerName="System.Data.SqlClient"/>

<add name="B" connectionString="Database=BDb;Server=server;

UID=a;PWD=a;" providerName="System.Data.SqlClient"/>

</connectionStrings>

xml file:

<connectionStrings>

<add xmu:name="name" name="A" connectionString="Database=ADb;Server=localhost;UID=a;PWD=a;" />

<add xmu:name="name" name="B" connectionString="Database=BDb;Server=localhost;UID=a;PWD=a;" />

</connectionStrings>

I have tried a lot of things including the sample above but that only works with one connectionstring

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Thursday, June 26, 2008 11:46 AM by Alex Nilsson

Fixed it:

    <add xmu:key="name" name="A" connectionString="Database=ADb;Server=localhost;UID=a;PWD=a;" providerName="System.Data.SqlClient"/>

<add xmu:key="name" name="B" connectionString="Database=BDb;Server=localhost;UID=a;PWD=a;" providerName="System.Data.SqlClient"/>    

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Friday, August 22, 2008 2:54 AM by mga911

I saw that you recently commented on Matt Berseth's blog here: mattberseth.com/.../single_config_file_multiple_de.html.  You said that xmlMassUpdate could achieve what he was trying to do.  Your example here shows how to substitute static sections of web.config but what if the substitution you wanted to make was dynamic.  Such as inserting the svnVersion that is retrieved during the MSBuild into web.config as an appSetting.  How would you accomplish this?

# MSBuild Community Tasks - wszystko czego brakuje w MSBuild

Sunday, September 28, 2008 2:40 PM by Bartek Szafko

msbuildtasks.tigris.org to ciekawy projekt dodający taski do msbuilda pozwalające wykonywać naprawdę

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Wednesday, October 08, 2008 8:45 AM by Hilton Giesenow

This helped a lot, thanks.

With reference to mga911's question, this is possible using the ProjectExtensions tag. It depends what he's trying to accomplish.

- HG

# Visual Studio Projects, and Multiple Environments | The Geekery

Wednesday, October 08, 2008 10:44 PM by Visual Studio Projects, and Multiple Environments | The Geekery

Pingback from  Visual Studio Projects, and Multiple Environments | The Geekery

# XMLMassUpdate - how to keep environment in place with MSBuild

Wednesday, November 26, 2008 2:46 AM by Marcus Hammarberg

XMLMassUpdate - how to keep environment in place with MSBuild

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Thursday, February 05, 2009 10:21 PM by Bill

The issue without looking deeper to see if it could be resolved was that XmlMassUpdate cannot work with non-standard web.config sections.

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Saturday, May 02, 2009 1:10 AM by Punit

This was really helpful..thanks so much!!

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Wednesday, November 25, 2009 2:19 AM by John

I'm having some trouble with this. XmlMassUpdate uses the key for items in the appSettings section just fine... for example, this works.

<add xmu:key="key" key="Settings Keygroup" value="***" />

However, in the connectionStrings section, it seems to ignore the key and always just overwrites the first node. In this case "Track" is the fourth node in the section, but it overwrites the first.

<add xmu:key="name" key="Track" connectionString="***" />

Any idea what might be wrong here? Thanks!

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Wednesday, November 25, 2009 5:05 PM by John

Got it!

<add xmu:key="name" name="Track" connectionString="***" />

Changing "key" to "name" did it. Because the examples were all for the appSettings section, I made the assumption that after you specified the key name "xmu:key="name"", that you would then specify the value for that key with key=... Turns out you don't do anything special for that. I was overthinking it!

This really works slick now!

Thanks!

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Friday, September 17, 2010 11:33 AM by Mark Pawelek

It seems to me that this will only work if I have use a deployment project as well. I want to run MSBuild from NAnt. The build must run independently of Visual Studio.

Can I do that and use this XMLMassUpdate trick? [otherwise I'll be reduced to the uglier NAnt tricks.]  I have 4 web.config variations: Local.Debug, Local.Release, QA, Production.

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Monday, January 24, 2011 11:52 PM by Sree

How can achieve this by using a shell script? I mean use msbuild in the a .cmd file and pass the appropriate parameter to select "Test", "Debug" or "Release"?

# How do I update an XML attribute from an MSBuild script? - Programmers Goodies

Pingback from  How do I update an XML attribute from an MSBuild script? - Programmers Goodies

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Monday, August 22, 2011 7:10 PM by Vijay

Substitutions.xml

<RootNode xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">

<substitutions>

<SubSection1>

<MyNode id="1"  MyNodeName="AAA"></MyNode>

<MyNode id="0"  MyNodeName="BBB"></MyNode>

</SubSection1>

</substitutions>

</RootNode>

myConfig.xml

<Section1>

<SubSection1>

</SubSection1>

<SubSection2>

<MyNode id="3"  MyNodeName="ZZ"></MyNode>

<MyNode id="4"  MyNodeName="ZZ"></MyNode>

<MyNode id="5"  MyNodeName="ZZ"></MyNode>

<MyNode id="6"  MyNodeName="ZZ"></MyNode>

</SubSection2>

</Section1>

TestBuild.xml

<XmlMassUpdate

  ContentFile="$(ConfigFName)"

  SubstitutionsFile="$(SubstitutionsFilePath)"

  ContentRoot="Section1"

  SubstitutionsRoot="Section1/substitutions"

  />

when I runt he msbuild then only the last entry from Substitutions file is substituted, what is the problem.

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Monday, August 22, 2011 9:10 PM by Vijay

ok, found that I need to provide a unique key in substitutions file if the elements are same xmu:key="id"

<MyNode xmu:key="id" id="1"  MyNodeName="AAA"></MyNode>

<MyNode xmu:key="id" id="0"  MyNodeName="BBB"></MyNode>

# MSBuild by jayempee - Pearltrees

Monday, December 05, 2011 2:11 PM by MSBuild by jayempee - Pearltrees

Pingback from  MSBuild by jayempee - Pearltrees

# re: Easy Configuration Deployment with MSBuild and the XmlMassUpdate Task

Friday, May 18, 2012 7:44 PM by 1

-1'

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: