So you have a freshly built .NET Web Application and, perhaps by following my last post, you’ve deployed it using Web Deploy to an IIS server somewhere on the internet.

Now it’s time for an update. You’ve added that sweet new /giphy plugin everyone has been asking for. You check-in the code, your tests pass, and your build server automatically publishes it back out to the web.

The next day support is swamped with angry calls about missing image attachments, and the log files have been wiped!

I guess that isn’t THAT big of a burden, but it’s the best scenario could come up with…

So what happened?

Web Deploy overwrote everything, including the contents of the folders the app uses to store logs and attachments.

Let’s go over what needs to be done to ignore files and folders during deployment aka leave what is there alone.

A first thought might be to just exclude the folder you want to keep from the project. Unfortunately, the deployment process is attempting to publish the same exact folder contents into wwwroot, so if your folder isn’t part of the web application project, it will be deleted from wwwroot upon deployment. This point will be important to remember shortly.

In comes the Web Publishing Pipeline (WPP).

The WPP is a series of XML based instructions the Web Deploy executes as it is publishing your application.

In order to prevent files from being overwritten (or deleted entirely) we have to place our own set of custom instructions into the pipeline using a .wpp.TARGETS file.

The XML is a bit noisy, but let’s go over it:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <UseMsDeployExe>true</UseMsDeployExe>
    <AfterAddIisSettingAndFileContentsToSourceManifest>OnAfterAddIisSettingAndFileContentsToSourceManifest</AfterAddIisSettingAndFileContentsToSourceManifest>
  </PropertyGroup>

  <!-- We do not want to OVERWRITE the web.config or attachments folder,
       if they are present at the destination.
       We also don't want to delete them so we can't just exclude them -->
  
  <Target Name="ExcludeWebConfigAndAttachments">

    <Message Text="Excluding web.config and attachments folder From Publish.  Be sure to manually deploy any new settings." Importance="high" />

    <ItemGroup>

      <MsDeploySkipRules Include="SkipAttachmentsDelete">
        <SkipAction>Delete</SkipAction>
        <ObjectName>dirPath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Attachments</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

      <MsDeploySkipRules Include="SkipWebConfigDelete">
        <SkipAction>Delete</SkipAction>
        <ObjectName>filePath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Web.config</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

      <MsDeploySkipRules Include="SkipWebConfigUpdate">
        <SkipAction>Update</SkipAction>
        <ObjectName>filePath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Web.config</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

    </ItemGroup>

  </Target>

  <Target Name="OnAfterAddIisSettingAndFileContentsToSourceManifest" DependsOnTargets="ExcludeWebConfigAndAttachments" />

</Project>

We have our typical namespacing, along with the root Project node used for targets files.

The PropertyGroup elements specify which pipeline target we will be inserting into and to use the MSDeploy.exe. The latter part is a bit beyond my knowledge but it is necessary if you are publishing from Visual Studio and need to skip certain actions (as you’ll see next.)

The Target element is where most of the magic is. We’ve included three MsDeploySkipRules elements. The first of which prevents the deletion of the Attachments folder at the destination. We want it to be left alone on the server.

The second two skip rules prevent the web.config from being both deleted and updated. You don’t have to do this, but it might be useful if you have complex configurations on multiple servers and would rather hand update the changes.

The final Target element specifies again the existing target in the pipeline and adds a dependency on our newly created target, appropriately named ExcludeWebConfigAndAttachments (you can name it whatever you want).

You’ll want to name this file YourProjectName.wpp.targets and place it in the root of your web application project. While you’re at it, set the build action to none so it doesn’t get pushed out to your server.

That’s it!

A huge shout out to @SayedIHashimi. His blog has been a huge help in uncovering these mysteries.

Hopefully that’s helpful to some of you. Send me a tweet or an email if you’re stuck or if you have any comments.