Web Deploy XML File Parameterization

Posted: Dec 17, 2012  3 comments  

Average Rating

Share this Post

Elliott Hamai's Blog

As part of its Parameterization feature, Web Deploy also supports parameterization of XML files.  If you are unfamiliar with parameterization, it's basically a feature that Web Deploy uses to change values of certain objects before committing them to a destination.  In this post, I'll walk you through some examples of how to do XML parameterization using XPath queries.

First, let's define an XML file that we're going to modify during a publish/sync called "books.xml" that looks like this:

 
<books>
  <book name="book1" author="author1" />
  <book name="book2" author="author2" />
</books>
 

Scenario 1: Replace an entire element

While syncing books.xml to a destination,let's say we want to replace the entire <book name="book1" /> element with <book name="book3" />.  To do this, we need an XPath query that could find this element in the XML file, and use that query to form a parameter replacement value.  Let's create a file called "params.xml" that has this query in it:

<parameters>
  <parameter name="MyTestParam" description="My Test Param" defaultValue="&lt;book name='book3' /&gt;">
    <parameterEntry kind="XmlFile" scope="books\.xml" match="//book[@name='book1']" />
  </parameter>
</parameters>

The <parameter> element above means that we're looking for an XmlFile with a path that matches the regular expression "books\.xml".  Within that file, we're looking to replace whatever the XPath "//book[@name='book1']" matches.  If no parameter value is set at run time, then we'll replace whatever we match in the XML file with the defaultValue defined in the conatining <parameter> element.  For now, we’ll just focus on setting a defaultValue.  Also notice that we need to escape the < and > signs within the file since we’re specifying xml within xml.

Now, if we specify use this parameter while publishing/syncing our XML file, then we should see the replacement XML in the destination file.  The easiest way to prove this is to use our command line tool which is located under "%programfiles%\IIS\Microsoft Web Deploy V3\".  From there, run the following command:

msdeploy.exe -verb:sync -source:filepath=c:\source\books.xml -dest:filepath=c:\dest\books.xml -setparamfile=c:\params.xml -verbose

Note: I added the "-verbose" tag because it will tell me if there was anything was updated due to parameterization.

If you open up the users.xml file under the destination folder, the XML should look like this:

<books>
  <book name="book3" />
  <book name="book2" author="author2" />
</books>

 

Scenario 2: Inserting a child element

Now let’s say that you wanted to add a child element <price /> within the <book name="book1"> element.  You could change the defaultValue of your parameters file so that it looks like this:

<parameters>
  <parameter name="MyTestParam" description="My Test Param" defaultValue="&lt;price /&gt;">
    <parameterEntry kind="XmlFile" scope="books\.xml" match="//book[@name='book1']" />
  </parameter>
</parameters>

Now if you do the sync again, your destination file should look like this:

<books>
  <book name="book1" author="author1">
    <price />
  </book>
  <book name="book2" author="author2"/>
</books>

The way this works if if Web Deploy sees that an element name that we're adding is the same as the matched element name, it will do a replace operation on the matched element.  But if the element name is different, then we'll insert it under the matched element.  In this scenario, "price" is not equal to "book", so we inserted it within the first book.

Scenario 3: Replacing an attribute within an element

If you would like to replace a single attribute within an element, change your params.xml file to look like the following:

<parameters>
  <parameter name="MyTestParam" description="My Test Param" defaultValue="book4">
    <parameterEntry kind="XmlFile" scope="books\.xml" match="//book[@name='book1']/@name" />
  </parameter>
</parameters>

Notice how defaultValue is no longer XML, but is instead just a regular string.  If you do the sync, the outcome should look like below, where only the "name" attribute on the first book changed.

<books>
  <book name="book4" author="author1" />
  <book name="book2" author="author2" />
</books>

 

Scenario 4: Removing an element

If you would like to remove an element, then you need to match on an element and change your defaultValue to an empty string like so:

 
<parameters>
  <parameter name="MyTestParam" description="My Test Param" defaultValue="">
    <parameterEntry kind="XmlFile" scope="books\.xml" match="//book[@name='book1']" />
  </parameter>
</parameters>

 

If you rerun the sync, the output file should look like this:

 

<books>
  <book name="book2" author="author2" />
</books>

 

Scenario 5: Getting a default value from another file using an XPath query at sync/publish time

If you would like to pull your default value from another file at sync/publish time, you can change the default value in your parameters file to specify a filepath and XPath query.  For example, lets say you have another XML file that you would like to grab your data from, called prices.xml.  Lets say prices.xml looks like the following and is stored at the root of my drive (this is a bad example I know, but it gets the point accross):

<books>
  <book name="book1" >
    <price>$5.00</price>
  </book>
</books>

Now I change my defaultValue to include the prices.xml file, as well as an XPath query to the value I want to insert.  (Remember that the same insertion rules from previous scenarios apply here, so make sure your query is matching what you expect for replacing or inserting data):

<parameters>
  <parameter name="MyTestParam" description="My Test Param" defaultValue="\prices.xml:://book[@name='book1']/price">
    <parameterEntry kind="XmlFile" scope="books\.xml" match="//book[@name='book1']" />
  </parameter>
</parameters>

Now if you do the sync again, your destination file should look like this:

<books>
  <book name="book1" author="author1">
    <price>$5.00</price>
  </book>
  <book name="book2" author="author2" />
</books>

 

Scenario 6: Declaring parameters in packages and setting them at sync/publish time.

Notice how so far we've only been dealing with the "defaultValue" setting to determine what to replace with?  Well, Web Deploy allows you to store your parameters in packages which can be explicitly set at sync/publish time so that you can specify a value other than the default value.  This is helpful when you want to use a single package that needs to be deployed to different endpoints that have different values.  To create the package and store your params.xml file within it, run the following:

msdeploy.exe -verb:sync -source:filepath=c:\source\books.xml -dest:package=c:\params.zip -declareparamfile:params.xml

If you open up the zip package, you’ll see a file in there called parameters.xml that has the parameters you defined stored within it.  Now when you sync your package to another endpoint, you can explicitly set the value of a parameter within the package like so:

msdeploy.exe -verb:sync -source:package=c:\books.zip -dest:filePath=c:\dest\books.xml -setparam:name=mytestparam,value=book5

If you don't explicitly call set param, the replacement value will use the defaultValue specified in the xml file.  NOTE: The destination path I used only works because there is a single filePath provider stored in the package. If you have multiple providers in your source package, you need to either specify a destination manifest (using the manifest provider) which has a 1:1 relationship with the providers in the source package, or use the “auto” provider which copies the providers and their paths from the source package.  If you do this, you’ll need to use parameterization to modify the destination paths during a sync.  Here’s an example of what a parameters xml file might look like to accomplish the “auto” provider option:

<parameters>
  <parameter name="MyTestParam" description="My Test Param" defaultValue="book4">
    <parameterEntry kind="XmlFile" scope="books\.xml" match="//book[@name='book1']/@name" />
  </parameter>
 
  <parameter name="providerPathParam" defaultValue="c:\dest\books.xml">
    <parameterEntry kind="providerPath" scope="filePath" match=".*" />
  </parameter>
</parameters>

Comments

Hey Elliot,

Thanks, this post is very informative.  I was wondering if there is any way to reference the SOURCE web.config in the defaultValue?  

My use case: I have an element with many nested child elements that I want to parameterize, however, I want the defaultValue to be the same as original element from the source.

Jul 10 2013 by snoopdiggy

Just FYI that I answered this question on the forums here.  forums.iis.net/.../10

Aug 21 2013 by elliott_h

Is there a way to add/remove an attribute using msdeploy parameterization?

My case is this (web.config):

for one environment, I'd like to set a sessionIDManagerType attribute on sessionState element:

<sessionState mode="SQLServer" sqlConnectionString="Server=.\SQLExpress;Trusted_Connection=Yes;" cookieless="false" timeout="60" sessionIDManagerType="MySessionIDManagerType" />

in another environment, my custom sessionID manager type is not used, so I'd like it to look like this (removing that attribute entirely):

<sessionState mode="SQLServer" sqlConnectionString="Server=.\SQLExpress;Trusted_Connection=Yes;" cookieless="false" timeout="60" />

The reason this is coming up is that if I try to give a null or empty value for the sessionIDManagerType attribute, msdeploy throws an exception, saying that I can't set that attribute to null or empty (even though the app works fine when you manually set the value to empty like this: sessionIDManagerType="" )

Mar 11 2014 by jamesrnail

Submit a Comment

  • Plain text is accepted.
  • URLs starting with http:// are converted to links.