Web Deploy XML File Parameterization
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="<book name='book3' />"> <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="<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 /> </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>