Elliott Hamai's Blog

  • Basics on Using the Web Deploy API

    I’ve seen a lot of posts on how to use Web Deploy through various tools and clients, but wasn’t able to find a good resource on doing these things through our API.  So I decided that I would try to help alleviate some of the confusion here. To begin using the Web Deploy API, it helps to understand a little bit about terminology, components, and its general workflow before diving in:

    • Provider – Web Deploy uses a provider model, where each provider is responsible for syncing specific types of data.  When performing a sync, you can specify built-in providers, or 3rd party providers if they’re installed.  (writing a custom provider is not covered by this post)
    • Parameters – Used for changing the values of an object while it is being synchronized. (see parameterization)
    • Rule Handlers – Run on the destination while a sync operation is in progress to affect the result of an operation based on different criteria.  Some rules are on by default, while others need to be manually enabled.  Rules may either be built-in, or installed by a 3rd party.  (writing a custom rule is not covered by this post)
    • Web Deployment Agent Service (aka msdepsvc) – A service that Web Deploy installs which allows us to perform remote publishing/synchronization.  The “Agent” is typically used in administrator-only scenario’s (where you have full privileges on the remote machine) with NTLM authentication.
    • Web Management Service (aka wmsvc) – An optional service that gets installed with IIS for remote server management of IIS machines.
    • Web Deployment Handler – A handler which hooks into the Web Management Service to handle publishing to IIS.  The handler is run in a low-privileged environment which is why it is usually used by hosters to allow their users to publish to IIS.

     

    Basic Workflow

    When Web Deploy begins a sync, it first compares its source object to its destination object.  The way it performs the comparison is based off of the implementation of whichever provider is being used.  If there are child providers of the current provider, Web Deploy will also compare all of its children to see if they exist, need to be updated, or need to be deleted on the destination.  For every CRUD operation that a provider performs on the destination, there are also rule handlers that get checked to see if there is something special that needs to happen.  If so, then a rule can either block a sync entirely, skip a change that may occur, or modify the data being synchronized somehow.  If Web Deploy is synchronizing to a remote endpoint via the msdepsvc or wmsvc, then it will make an HTTP connection to that endpoint in order to synchronize its data.  Web Deploy will only send over data that is necessary to complete a sync in order to minimize the amount of network traffic that goes over the wire.

     

    Command Line Basics 

    To begin using the Web Deploy API, I find it’s often easiest to reference the usage of the msdeploy.exe tool since it is probably the lowest level wrapper you could use with the highest amount of functionality. If you have installed Web Deploy, then you should have a copy of it under "%programfiles%\IIS\Microsoft Web Deploy V3”.  A typical command has the following structure:

    msdeploy.exe -verb:<verb> -source:<providerName>=<sourcePath>,<providerOptions> -dest:<providerName>=<destPath>,<providerOptions>

    • Verb – The most common verbs are “sync” and “dump”.
    • ProviderName – The name of a built-in or custom provider.
    • ProviderOptions – Settings for whatever provider you’re using.

    Command line help gets more specific based on the options that you enter.  For instance, if I want a list of all providers, rule handlers, and other options, I could simply run:

    msdeploy.exe -?

    But if I wanted specific help on a specific provider’s options, I could run:

    msdeploy.exe -verb:<verb> -source:<providerName> –?

    More advanced commands which includes publishing to a remote endpoint with rules and parameters may look something like this:

    msdeploy.exe -verb:<verb> -source:<providerName>=<sourcePath>,<providerOptions> -dest:<providerName>=<destPath>,<providerOptions>,computername=<remoteComputerName> -enablerule:<ruleName> -declareParam:name=ParamName,type=ProviderPath,scope=<providerName>,match=<sourcePath>

    As you can see, the command line can easily get pretty hairy.  But having a basic understanding of it will help you to understand how the API works and help you to understand what’s possible without writing any code.  For a complete command line reference, see our Technet documentation

     

    Synchronizing Two directories With Tracing

    Let’s start simple.  To sync one directory to another on the same machine, I could run the following command:

    msdeploy -verb:sync -source:dirpath=c:\source -dest:dirpath=c:\dest

    This will perform a ONE-WAY sync from source to destination.  Web Deploy always performs a diff before making any changes, so if no changes are necessary, it will not make them.  To achieve this through the API, you can use the following code.

  • 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="&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>