In the different applications development areas that I’ve worked in, the typical process for getting a product installed is something like this:

Typical workflow of an MSI

  1. Developers are responsible for building the installation package (a WiX MSI is preferred)
  2. Then it’s left up to IT to update the config files
  3. And IT deploy the installer to a server or end-users.

Step #2 is where things usually come undone. Some common mistakes I’ve seen people make are:

  • There’s no configuration screens in the installer to specify configuration.
  • Sometimes the IT guys rip apart the package and re-package it.
  • Sometimes the installer doesn’t deal with configuration at all - and the IT guys are left hand-editing XML configuration files on servers.

One of the really nice ways you can avoid these mistakes is to make use of Windows Installer Transform Files for environment-specific configuration settings.

A transform is a collection of changes applied to an installation. By applying a transform to a base installation package, the installer can add or replace data in the installation database.

Credits go to Alex Shevch’s blog post - From MSI to WiX, Part 7 Customising installation using Transforms for describing this technique in detail.

A typical output from the developers now looks like this:

MSI package and it's related Transforms

Step 1 - Parameterising your WiX script

Within your WiX script, you can refer to properties by placing square-brackets around them. e.g. [CONFIG_CONNECTIONSTRING]

<util:XmlFile Id=”SetConnString” Action=”setValue” ElementPath=”//appSettings/add[\[]@key=’MyConnectionString’[\]]/@value” Value=”[CONFIG_CONNECTIONSTRING]” File=”[INSTALLDIR]ConsoleApp.exe.config” />

Now that a property is defined in the MSI, we can set it from the command line like so:

msiexec.exe /i MyInstaller.msi CONFIG_CONNECTIONSTRING=”Data Source=(LOCAL);etc…”

By default, Windows Installer doesn’t do any verification of properties. If a property isn’t set, then it will just replace it with nothing when it comes across it. If you want to ensure that all your required properties are set, you can use a launch condition. e.g.

<Condition Message=”CONFIG_CONNECTIONSTRING variable is not set. Try setting the property from the command line or using a transform.”>Installed OR CONFIG_CONNECTIONSTRING</Condition>

Step 2 - Creating a transform file

If you were to create a transform manually, the tool you would use is ORCA. (Not to be confused with “Orcas” - the code name for Visual Studio 2008). Orca is distributed in the (large >1GB) Platform SDK download. Fortunately Aaron Stebner has made just the ORCA installer available through is blog.

Aaron Stebner’s blog - Download Orca install package (direct)

You can quickly access Orca by right-clicking any MSI file and selecting “Edit with Orca” from the context menu:

Edit with Orca context menu

Once you’ve opened the MSI editing utility, select the “Property” table and have a look at the different properties you can set. If you’ve configured your WiX script correctly, you should be able to see the properties that you made available.

Editing MSI with Orca

Step 2a - Manually creating a transform

  • From the menu, select Transform | New Transform
  • Make changes to any properties with your environment-specific configuration
  • select Transform | Generate Transform…
  • Type the filename of the *.mst file you want to save your differences as
  • Repeat the process for each transform/environment that you need to

Creating a transform in Orca

Now when you run your installation, you specify the transform file as a parameter:

msiexec.exe /i MyInstaller.msi TRANSFORMS=Development.mst

Conclusion

This is an excellent way to reuse a single MSI as you promote it through your dev/test/qa/prod environments, without having to know the values up-front and embed them in the MSI.

In my next post I show you how I’ve extended this technique to automatically generate transform files based on an XML configuration file using the Windows Installer COM Automation object and an MSBuild task.