Installing Web Applications using Web Deploy, MSBuild, and Powershell

Note: this is a guest post from Crispin Wright, who works in the media sector in the Netherlands, and has been working with Microsoft technologies since 1990. Interested in sharing your own Web Deploy story? E-mail me: baslam at microsoft.com

clip_image009I was recently asked to develop installers for some of our web applications, so they could be installed in a production environment.

I needed to give our engineers a “package” that could be run on a fresh install of Windows Server 2008 R2, and would deploy the entire solution.

I want to write about how the technologies I used to achieve this, focusing on Web Deploy, and how I arrived at an end to end solution, with help from MSBuild, Powershell, and some simple command scripts.

So let’s dive in….

Our Deployment World

Everyone’s architecture is different, but here’s ours – so you can see what we’re working with:

clip_image002

MSBuild & CI

Our first task is to build our Web Deploy packages as part of our nightly CI build. Vishal Joshi has written a great article about this here.

Here’s the part of our MSBuild project that publishes our site:

The approach we’re taking here is to compile the web application project and then publish it to a physical filepath.

clip_image004

It’s good to remember that when you can specify a configuration for compilation, you can have your specific Web.config that provides the correct parameters for that configuration.

We’re using CruiseControl.NET to deploy to a test server to verify our builds, here’s a snippet from our ccnet.config:

clip_image006

I’ll talk more about the Web Deploy syntax later, but the deployment above will sync only the content in the source path with the content on our destination server using the contentPath provider, no permissions or IIS Setup work is done here, and this is because our site is already up and running, our environment is configured (permissions,web.config e.t.c) and all we’re doing here is syncing our “old” build files with our new ones.

We execute this compilation, and it does several things:

It takes the site that we publish to our C:\build\WebAppServer\client folder, and performs a sync with the site at c:\WAS on 192.168.1.10.

It uses some skip rules to exclude the following files (because they are already configured how we want them):

· All .config files

· All .config.xml files

Once we have verified and signed off the build we can use the /T:Package command with MSBuild to package our build. This gives us the zip file we need to pass to the engineers.

Powershell

To install our web applications onto a new install of Windows Server 2008 R2, we’re going to need to configure the box and install some prerequisites, including Web Deploy itself. I’m going to demonstrate some of the parts of the Powershell script we use to do this.

Script Parameters

We can pass arguments to our Powershell script to specify the install location when we “dot source” (or execute) our script:

clip_image008

This Powershell code takes our script arguments, and steps through them adding them to a structure we can reference later on – in this case to specify the install location.

$ArgList = @{}

For($i=0; $i -lt $args.count; $i +=2)

{

      $ArgList[$args[$i]] = $args[$i+1]

}

Server Roles

The first thing the script does is configure the server, and the Powershell ServerManager cmdlet can configure the server roles and features we need for us:

#Import the server manager module to configure roles and features

Import-Module servermanager

 

#Configure the server roles

#Install the .NET Framework parts

Add-WindowsFeature Net-Framework -IncludeAllSubFeatureLogPath c:\NetFramework_Add_Role.log

#Install the Application server role

Add-WindowsFeature Application-Server -IncludeAllSubFeature -LogPath c:\AppServer_Add_Role.log

#Install the Web Server role

Add-WindowsFeature Web-Server -IncludeAllSubFeature -LogPath c:\WebServer_Add_Role.log

You can read more about the Server Manager cmdlets here:

http://technet.microsoft.com/en-us/library/cc732757.aspx#BKMK_wps

The script installs the .NET Framework, Application Server, and Web Server roles for us, and we’ve also included logging information for the engineer using the –LogPath switch.

Prerequisites

A key part of deploying the solution is installing prerequisites on the server, these include the .NET Framework 4, and Web Deploy itself. We’re going to use Powershell to start them as external processes, and wait for them to finish using this function:

function StartProcess([string] $command = $(throw "Missing: command parameter"), [string[]] $parameters, [switch] $wait, [switch] $cmd)

{

    if ($cmd.IsPresent)

    {

        cmd /C $command $parameters

    }

    else

    {

        $process = [Diagnostics.Process]::Start($command, $parameters);

        if ($wait.IsPresent)

        {

            $process.WaitForExit();

        }

    }

}

So we can install Web Deploy like this:

StartProcess -command:$PATH_WebDeployInstall -wait:$true -parameters:"/q"

Where $PATH_WebDeployInstall contains the full path and filename of our Web Deploy msi file.

The /q parameter is a “quiet” switch which means the installations run silently as background processes with default settings.

NOTE – Web Deploy installed with default settings will NOT include the remote management service if you need to deploy sites remotely to that server – although hedge my bets that there is a switch to pass to the msi which will install in a “Complete” fashion.

Registry Operations

We can also use Powershell registry commands to store the install path of the application so that when we come to “upgrade” our application, we know the physical sync path to pass to Web Deploy.


Set-ItemProperty
-path "HKLM:\Software\CompanyName\ApplicationName" -name "InstallLocation" -value $location

So we’ve now configured the server, installed all the prerequisites, and stored our install location.

Clean Install or Upgrade?

We’re at a stage now where we can deploy our web application, we’re going to use two different command scripts to do this.

We first want to decide if we need to install or upgrade our application, so we turn to Powershell again, and ask it to find out if our web application already exists – if it does, then we run the upgrade script, if not, then we run a clean install. This is the Powershell function we use to do it:

#Detect if the Site is already installed - if it is do an upgrade - else brand new install

function Get-IISSiteExists([string]$AppSiteName)

{

      $root = New-Object System.DirectoryServices.DirectoryEntry("IIS://localhost/W3SVC")

     

      $site = $null

 

      $site = $root.get_Children() |Where-Object { $_.ServerComment -eq $AppSiteName }

 

      if ($site -eq $null)

      {

            return $false

      }

      else

      {

            return $true

      }

}

There are other functions we use in our Powershell script to do things like event logging, checking if the installer is in the administrators group, and display a gui for application parameter configuration. There is a ton of information out there on Powershell, here are a couple of sites I found useful on my travels:

http://blogs.msdn.com/b/powershell/

http://powershell.com/cs/

Over to Web Deploy

Now our server is configured we can install our web application using Web Deploy.

You can use the deploy cmd scripts that the package build generates, and you can pass any number of Web Deploy arguments to those standard scripts. I’ve written a slightly different script here.

This is our script:

"%ProgramFiles%\IIS\Microsoft Web Deploy\msdeploy.exe" -verb:sync -presync:runCommand="md %1 & %SYSTEMROOT%\System32\inetsrv\appcmd add site /name:WebAppServer /bindings:http/*:85: /physicalPath:%1" -source:package=WebAppServer.zip -dest:Auto -setParamFile="was_params.xml" -verbose > webappserversync.log

So broken down – this is what the script does:

The first thing we do prior to running our Web Deploy sync is execute some run commands, these create our site in IIS, it’s physical directory, and it’s bindings:

-presync:runCommand="md %1 & %SYSTEMROOT%\System32\inetsrv\appcmd add site /name:WebAppServer /bindings:http/*:85: /physicalPath:%1"

Next the Web Deploy sync command will run, so without the presync commands it looks like this:

"%ProgramFiles%\IIS\Microsoft Web Deploy\msdeploy.exe" -verb:sync -source:package=WebAppServer.zip -dest:Auto -setParamFile="was_params.xml" -verbose > webappserversync.log

The command will install our web application into the location we specified, installing dependencies, configuring ACLs, certificates, and gladly doing all the heavy lifting for us.

Note that we include logging here again using the verbose switch, this benefits the installation engineers a lot, they check this post install for any problems:

-verbose >webappserversync.log

Upgrade or Downgrade?

Because of the Web Deploy “sync” behaviour, the approach above can be used to roll back a site to a specific version just as easily as installing a new version, and in a live environment, if something goes wrong, it can be extremely useful to have that ability.

Conclusion

There are many ways to skin a cat with Web Deploy. You can use the API with VB.NET or C#, you can use it inside IIS to export or import applications, and you can use it as I’ve done here, in a scripted manner with Powershell and command files.

Hopefully this gives an example of the latter approach, and how you can use Web Deploy in your CI integration and testing, through to delivering maintainable installers for production environments.

I’d like to say a huge thanks to Bilal Aslam, Vishal Joshi and the whole Web Deploy team for all their assistance. I came to Web Deploy knowing nothing about it, and they were extremely helpful – thanks guys.

3 Comments

Comments have been disabled for this content.