IIS Express Launch Script

Usually during web development you want to run your web code locally via a local development web server and there are many options for this. In fact most development workflows provide this functionality. For example Visual Studio provides a local web server to run your code, and React/Webpack toolchains usually use NodeJS based solutions. Sometimes though you want to want to fire up something simple to run your code outside your development workflow.

Photo by Aryan Dhiman on Unsplash

The Problem

When developing ReactJS projects I often prefer to run my transpiled bundled code (produced via ‘NPM run build’) in a different way to my development code on a different local web server and, as I usually have IIS Express installed already, I prefer to use that than install new global Node based web server tools. Whilst the command line parameters for IIS Express are simple I have added them to this post to prevent me having to remember them in the future 🙂

The Solution

To run IIS Express from the commandline use:

iisexpress /path:<path_to_files>

You can also specify a port number and other options if you require (see the documentation here).

I prefer to wrap this into a RunOutputBuild.cmd batch file that I can store with the code in the repo and fire up when I want to host my transpiled build output files.

REM Script to launch IIS Express and host the build folder
echo "Launching IIS Express"
cmd /K "c:\program files (x86)\IIS Express\iisexpress.exe" /path:%~dp0build

This assumes the IIS Express installation location is the default one and that the script lives in root of the repo as does the \build folder. The useful %~dp0 key resolves to the local directory where the command script was run from.

I can then just run the command file and browse to the compiled site. Its also possible to add compilation steps and one to fire up the browser automatically if required. Alternatively you could write an NPM Script or Gulp/Grunt step that replicates this functionality to launch IIS Express.


IIS Host Headers, Secure Bindings, Wix & Custom Actions

Whilst trying to host multiple WCF Services in IIS, each within it’s own web site, I discovered an issue with secure host headers and IIS6. The requirements were to securely install each WCF service inside it’s own web site, with its own application and application pool instance. In order to setup multiple sites I needed either: multiple IP addresses, use different ports on the one IP address or use ‘host headers’. The chosen solution was ‘host headers’ and I set these up in IIS using IIS Manager (inetmgr). This is covered here:


However the UI only supports setting up unsecure bindings and not secure ones. After checking on TechNet I found this:


It turns out that whilst IIS6 does support the use of host headers this feature was not added in time for the IIS Admin UI to reflect this. The article informs us to use the handy IIS admin script “adsutil.vbs” which updates the IIS Metabase XML file, as detailed here:


The problem with this approach though is that the script rather annoyingly requires the Site Identifier for the web site you wish to update via the script. This is easy to obtain interactively by checking out IIS Manager and clicking on ‘web sites’, but I needed to set up my site via an MSI (via Wix). The installation script will not know what the identifier of the site is as it could vary on each install. Therefore I need to be able to resolve a web site name to an identifier before calling adsutil.vbs. After some Googling I found this excellent post by David Wang:


Here he explains how to enumerate through IIS entities to find the one you want. If you check out the comments there is a post by ‘Dave’ where he has included a modified script. This script iterates over the web sites and finds the one matching the right name, then calls the “adsutil.vbs” script to update the metabase using the correct site identifier. I copied this script and trimmed it down for my own purposes and now had a viable script for my installer.

Using a VB Script within an MSI is not recognised best practice, a more robust solution would be to code a Custom Action in native code (there are hidden dangers of Custom Actions written in Managed Code), however it is a viable option and in this instance it fitted my requirements perfectly (for many reasons outside the scope of this article).

Now I’m a fan of Wix and am always impressed with how much it can do out the box without customisation. In this case however I discovered that it is not that easy to just run a command or script during install. Don’t get me wrong it is possible and there are a huge number of options as to when and how to run custom actions, but I was expecting it to be easier than it turned out to be. There are many decent blog posts on the subject of Custom Actions in Wix and I’m not going to go into much detail here, however in the end my solution looked like this:

<!--Set up IIS Bindings-->
   <custom action='CAIISSecureBindingsServiceX' before='InstallFinalize'>
      NOT Installed
   <custom action='CAIISSecureBindingsServiceY' before='InstallFinalize'>
      NOT Installed

This slots our Custom Actions into the running order of an MSI installation sequence of events. It asks for the actions be run just before the installation has completed. The ‘Not Installed’ argument ensures that the actions only fire on installation and not an uninstall

We then define the Property for the path to the exe we want to run, in this case the Cscript.exe application (to run our VBS Script). After that we need to define each custom action. The ExeCommand tells it what to pass to the Cscript executable as parameters. The Execute=’deferred’ option is important as it means that the scripts will not run on the first pass through of the MSI installation. The MSI installation process involves running through all steps but not committing them, if that runs ok it then does the steps for real. If we run the script before the web sites have been committed to IIS the script will fail. Setting it to ‘deferred’ means it will be left out of the initial run through and committed in the actual “doing ” stage. For more information on the MSI sequence of events check this out (http://www.advancedinstaller.com/user-guide/standard-actions.html). I found that impersonation needs to be set to Yes for this to work correctly.

<!-- Define all the custom actions and the properties required -->
<Property Id='ScriptEngine'>C:\Windows\System32\CScript.exe</Property>
   ExeCommand='[INSTALLLOCATION]UpdateIISBindings.vbs ServiceXv1Site X.v1.default'
   ExeCommand='[INSTALLLOCATION]UpdateIISBindings.vbs ServiceYv1Site Y.v1.default'

I have set up multiple Custom Actions although only one is really needed. In my implementation I have made the VBS file merely a helper script that uses the information passed in as parameters. I then have multiple custom actions that each call the script separately passing in different parameters (web site name and host header value). It would be neater to have the VBS file include all the logic for looping all sites and setting up the bindings for each one and then only one Custom Action would be needed to run that script. The reason I have not done that here is that I didn’t want the IIS implementation details leaking out of the Wix project. With the multiple actions approach it means that the website names and host header names are not stored in the script file and are held only in this WIX project.