Example of deploying web apps using WiX

As a follow up to my recent post on deploying web applications via MSBuild, I wanted to see how tough it was to do the same thing using a WiX-built installation package. My basic aim is to deploy a simple web applications to localhost, including configuring and IIS virtual directories and application. I am going to assume that the web application has already been compiled to a directory called PrecompiledWeb (which is the default when building via MSBuild). I am using the development build of WiX 3.0 (3.0.2925.0).

The first step was creating a new WiX Setup project in my VS 2005 solution (this VS integration package is called Votive). This creates a .wixproj file, which stores the information necessary to build your installation package. As of WiX 3.0 it can be built using MSBuild. 

To this project I added references to WixIIsExtension.dll (found in C:\Program Files\Windows Installer XML v3\bin\ in my case). This is to get access to some of the IIS-related actions. The WixUtilExtension.dll is also useful for creating users (amongst other things). To get these extensions to work properly I had to work around a bug in the current build that causes the Cultures property to set incorrectly in the .wixproj file. Search for “bug” on Jeff Wharton’s post for information on this. It basically just means manually setting the culture nodes to “en-US” or your current project’s culture.

Next I edited the default .wxs file created by Votive (or create a new one using Add –> New Item –> WiX Product File).

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
     xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
  <Product Id="(your guid0 here)" Name="Sample"
           Language="1033" Version="1.0.0.0" Manufacturer="Me"
           UpgradeCode="(your guid1 here)">
    <Package InstallerVersion="200" Compressed="yes" />
    <Media Id="1" Cabinet="WixSample.cab" EmbedCab="yes" />
    <iis:WebSite Id='DefaultWebSite' Description='Default Web Site'>
      <iis:WebAddress Id='AllUnassigned' Port='80' />
    </iis:WebSite>
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="INSTALLDIR" Name="Sample">
        <Directory Id="MyWebSiteDir" Name="MyWebSite" />        
      </Directory>
    </Directory>
    <Feature Id="SampleFeature" Title="Sample" Level="1">
      <ComponentGroupRef Id="MyWebSite" />           
    </Feature>
  </Product>
</Wix>

This file sets up the basic install package structure. The iis:WebSite node is there just to provide a reference to the default web site on localhost. The Directory tags define the directory structure that will be created by the installer. In this case, it is just going to going to create a structure like C:\Sample\MyWebSite. The Feature tag defines exactly which Component we are installing. In this case we are installing the group of Components called MyWebSite. We have not defined these yet, so lets add another .wks file to do this now (Add –> New Item –> WiX File).

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
     xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
  <Fragment>
    <ComponentGroup Id="MyWebSite">
      <ComponentRef Id="MyWebSiteRoot" />
      <ComponentRef Id="MyWebSiteBin" />
      <ComponentRef Id="MyWebSiteVirtualDir" />
    </ComponentGroup>
    <DirectoryRef Id="MyWebSiteDir" FileSource="$(var.SolutionDir)\PrecompiledWeb\Sample.MyWebSite">
      <Component Id="MyWebSiteRoot" Guid="(your guid here)">
        <File Id="Default" Name="Default.aspx" />
        <File Id="Web.Config" Name="Web.Config" />
        <File Id="PrecompiledApp.Config" Name="PrecompiledApp.config" />        
      </Component>
      <Directory Id="MyWebSiteDir_Bin" Name="bin" FileSource="$(var.SolutionDir)\PrecompiledWeb\Sample.MyWebSite\bin">
        <Component Id="MyWebSiteBin" Guid="(your guid here)">
          <File Id="App_Web_gmetwu2v.dll" Name="App_Web_gmetwu2v.dll" />
          <File Id="App_Web_gmetwu2v.pdb" Name="App_Web_gmetwu2v.pdb"/>
        </Component>
      </Directory>
      <Component Id="MyWebSiteVirtualDir" Guid="(your guid here)">
        <CreateFolder />
        <iis:WebVirtualDir Id='MyWebSiteVirtualDir' Alias='MyWebSite' Directory='MyWebSiteDir'
                           WebSite='DefaultWebSite'>
          <iis:WebApplication Id='MyWebSiteApp' Name='MyWebSiteApp' Isolation='medium' />
        </iis:WebVirtualDir>
      </Component>
    </DirectoryRef>
  </Fragment>	
</Wix>

This file explicitly lists every web site file that is going to be distributed within our installer. The reason why I have done this in a separate .wks file is because it is a giant PITA to do this, especially for large web sites, and double especially as the App_Web_*.dll file name will change on every build, so ideally you would automate the creation of this file as part of the initial project build. WiX 3.0 comes with the Heat utility (formerly Tallow) that is meant to help with this.

Other than the file definitions, you will also see the node that will create the IIS web virtual directory and application. You can also do neat things like configure application pools at this step. You’ll notice the WebSite attribute of the iis:WebVirtualDir node is “DefaultWebSite”, which was the ID of the iis:WebSite node in our first file, so this virtual directory will be hosted from our default IIS website on localhost.

You can now build your .wixproj from VS, and you should end up with an installer. After running the installer, you should have the sample application available at http://localhost/MyWebSite (with source files at C:\Sample\MyWebSite). Uninstalling should remove the files and IIS configuration entirely.

Comments