Skip rules are a feature of MSDeploy that allow specific parts of a synchronisation to be, you guessed it, skipped. While a conceptually simple concept, there are subtleties in its options that have resulted in "bug reports" from a number of people (myself included) that were actually just misunderstandings of its design.

The aim of this post is to demystify skip rules in hope of easing frustration and preventing accidental deletes in production environments.

Anatomy of a skip rule

The command line syntax of a skip rule is:

-skip:attribute1=value1[,attribute2=value2[.. ,attributeN=valueN]]

Attributes act as a filter to figure out what to skip. They can all be specified in a single skip, but the same attribute cannot be specified more than once.

The main attributes are:

  • objectName is the provider being skipped (eg. dirPath)
  • absolutePath is a regular expression (don't forget to escape it!) of the provider path being skipped (eg. file system path for filePath/dirPath)
  • skipAction specified under what circumstances should the object be skipped (eg. Delete, Update)

The other, less common, attributes are:

  • xPath is an XPath expression that locates the object within the provider hierarchy
  • attributes.attributeName filters on an arbitrary provider attribute but cannot be used with skipAction
  • keyAttribute filters a regular expression against the providers "key attribute". Since the key attribute is "path" more often that not, you're unlikely to use it. It also cannot be used with skipAction.

Now that we know what a skip rules are, let's take a look at how they work.

Skip rules and hierarchies

At its core, MSDeploy only supports synchronising a single provider from a source to a target, it's just that providers actually exist in hierarchy, each with a very specific purpose. For example, dirPath contains other dirPath and filePath providers, iisApp contains a contentPath and a createApp provider, and manifest/package/archiveDir all contain abitrary providers.

During synchronisation, the metadata for source\target provider hierarchies from the source and target are loaded into memory and compared. Based on that comparison, items from the source are added, updated or deleted at the destination.

The important thing to understand with regard to hierarchies is that delete rules on a child are only processed if the parent is not being deleted. So if you skip a file but it's containing directory doesn't exist on the source, the directory (and thus the file) will be deleted anyway.

Skip Actions

  • Delete prevents the object being deleted from the destination (unless its parent is being deleted as mentioned above)
  • AddChild prevents the object from being added to the destination
  • Update prevents the destination object from being updated

Omitting a skipAction entirely prevents the object's metadata from even being considered for comparison and is processed at a separate phase than skip directives with skip actions. While this may seem like skipAction=*, in reality it's almost never what you want. Since each provider in the hierarchy is responsible for it's own deletion, skipping a child without a skipAction will cause the parent to fail during a delete action. For example, deleting a dirPath with an omitted child filePath results in a "Directory is not empty" error because the child filePath was not there to handle it's deletion.

Examples

Let's see how this all fits together in a real life test. We're going to sync these two folders:

source dest
Source Destination

Below are the results of the various operations options. I've added emphasis on the important bits, and strikethrough means I'm pointing out that the line is missing:

Skip attributes Result
None Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).
Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).
objectName=filePath
absolutePath=data\.bin$
Info: Object filePath (App_Data\data.bin) skipped due to skip directive 'CommandLine
SkipDirective 1'.
Info: Deleting directory (App_Data).

Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).
Error: An error was encountered when processing operation 'Delete Directory' on 'App_Data'.
Error: The directory is not empty
objectName=filePath
absolutePath=data\.bin$
skipAction=Delete
Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).

Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).
objectName=dirPath
absolutePath=App_Data$
skipAction=Delete
Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).

Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).
objectName=filePath
absolutePath=Index\.cshtml$
Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).
Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Object filePath (Views\Home\Index.cshtml) skipped due to skip directive 'CommandLineSkipDirective 1'.
Info: Updating file (Web.config).
objectName=filePath
absolutePath=Index\.cshtml$
skipAction=AddChild
Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).
Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).
objectName=filePath
absolutePath=Index\.cshtml$
skipAction=Update
Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).
Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).
objectName=filePath
absolutePath=Web\.config$
skipAction=Update
Info: Deleting file (App_Data\data.bin).
Info: Deleting directory (App_Data).
Info: Adding directory (bin).
Info: Adding file (bin\App.dll).
Info: Adding directory (Views).
Info: Adding directory (Views\Home).
Info: Adding file (Views\Home\Index.cshtml).
Info: Updating file (Web.config).