How to build and deploy a web deployment package using MSBuild

Web application packaging and deployment automation using MSBuild needs the following steps:

deployicon

Step 0. Server preparation is covered here How to prepare a Windows Server 2012 for web deployment

  1. Configure solution build profiles
  2. Configure transformation of web.config file
  3. Configure publishing profiles
  4. Create a web deployment package with MSBuild script
  5. Run deployment from command line

Sample solution that includes build script and configured publishing profiles can be downloaded here https://github.com/mchudinov/BuildWebDeployPackage

The whole build and deployment process can be shown on the drawing
deploy

Each environment a solution will be deployed to (Production, Staging and Test on the drawing) hase it’s own build configuration, publishing profile and configuration file.

1. Configure solution build profiles

Every deployment environment should have it’s own build profile.
Let’s say we have three environments: Production, Staging and Test. Every environment will have it’s own build setup. We can use Release setup for Production, Debug for Test and Stage for Staging environment.
buildrpofiles

2. Configure transformation of web.config files

Every build profile should have it’s own web.config transformation file.
Right-click on the web.config in Visual Studio Solution Explorer file and use Add Config Transform menu. Visual Studio will create transformation files one per build setup.

addconfigtransform

Edit transformation files for every build and environment. Use documentation Web.config Transformation Syntax for Web Project Deployment Using Visual Studio.

3. Configure publishing profiles

Every deployment environment should have it’s own publishing profile.
Right-click on the project that needs to be published and choose “Publish..” menu.
publish

Create new custom profile for each deployment environment.
newprofile

  • Choose “Web Deploy Method” as publish method.
  • Choose package location related to solution folder. Something like ..\temp\Debug\BuildWebDeployPackage_test.zip
  • Chose site name and application name where the web package will be deployed. Site and application must be created on a web server before publishing. Read this post about How to prepare a Windows Server 2012 for web deployment.

testprofile

Publishing profile should have the same name as build configuration. It makes MSBuild build and deployment script easier.

releaseprofile

Click Publish button and Visual Studio will create a deployment package. The publishing profile will be saved as an *.pubxml file in {ProjectName}\Properties\PublishProfiles. It should look similar to this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>Package</WebPublishMethod>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>True</ExcludeApp_Data>
    <DesktopBuildPackageLocation>..\temp\Release\BuildWebDeployPackage_release.zip</DesktopBuildPackageLocation>
    <PackageAsSingleFile>true</PackageAsSingleFile>
    <DeployIisAppPath>Default Web Site/Release</DeployIisAppPath>
    <PrecompileBeforePublish>True</PrecompileBeforePublish>
    <EnableUpdateable>True</EnableUpdateable>
    <DebugSymbols>False</DebugSymbols>
    <WDPMergeOption>DonotMerge</WDPMergeOption>
    <AllowUntrustedCertificate>True</AllowUntrustedCertificate>
  </PropertyGroup>
</Project>

 

4. Create a web deployment package with MSBuild script

The result of a build script is a web deployment package in a single .zip file named after build profile (Debug/Release/Stage etc) with a version number.
Like this: MyProject_Release_1.2.1004.12.zip

Single zip is easy to store and distribute.
Build profile in name makes it easy to differ between target environments.
Version number in name makes it easy to differ from older(newer) versions.

My build script does the following:

  1. Clean build
  2. Restore NuGet packages
  3. Set version to assemblies
  4. Build project
  5. Create a web deploy package

Steps 1 to 4 – automated building and versioning processes were covered in my previous posts:

Let’s focus on creating a web deploy package with MSBuild.
The process will include:

  • Pick up version number from assembly
  • Create web deployment package
  • Archive into zip

For manipulation with assembly version and zip archive I use MSBuild.Extension.Pack tasks MSBuild.ExtensionPack.Framework.Assembly and MSBuild.ExtensionPack.Compression.Zip accordingly. MSBuild.Extension.Pack documentation is available here.

MSBuild Package target can refer to publishing profile file. Then information about build profile and target deployment environment comes from publishing profile. I use same names for publishing profiles and build configurations: Debug build configuration has Debug.pubxml publishing profile etc.

<MSBuild Targets="Package" 
Projects="$(ProjectName)/$(ProjectName).csproj" 
ContinueOnError="False" 
Properties="PublishProfile=$(Configuration);
DesktopBuildPackageLocation=..\$(PackageFolder)\$(ZipFileName)" />

The whole Pack MSBuild target looks like this:

  <UsingTask AssemblyFile="packages/MSBuild.Extension.Pack.1.8.0/tools/net40/MSBuild.ExtensionPack.dll" TaskName="Assembly"/>
  <UsingTask AssemblyFile="packages/MSBuild.Extension.Pack.1.8.0/tools/net40/MSBuild.ExtensionPack.dll" TaskName="Zip"/>
  <Target Name="Pack">	
    <PropertyGroup>
      <BinaryFolder>$(ProjectName)/bin</BinaryFolder>
      <MainExecutable>$(BinaryFolder)/$(ProjectName).dll</MainExecutable>
    </PropertyGroup>
    <Assembly TaskAction="GetInfo" NetAssembly="$(MainExecutable)"> 
      <Output TaskParameter="OutputItems" ItemName="Info"/> 
    </Assembly>
    <Message Text="Identity: %(Info.Identity)" /> 
    <Message Text="FullName: %(Info.FullName)" /> 
    <Message Text="FileVersion: %(Info.FileVersion)" /> 
    <Message Text="AssemblyVersion: %(Info.AssemblyVersion)" />  
  
    <PropertyGroup>
      <PackageFolder>$(TemporaryFolder)\$(ProjectName)_$(Configuration)_%(Info.AssemblyVersion)</PackageFolder>
      <ZipFileName>$(ProjectName)_$(Configuration)_%(Info.AssemblyVersion).zip</ZipFileName>
    </PropertyGroup>	
  
    <MSBuild Targets="Package" Projects="$(ProjectName)/$(ProjectName).csproj" ContinueOnError="False" 
      Properties="PublishProfile=$(Configuration);DesktopBuildPackageLocation=..\$(PackageFolder)\$(ZipFileName)" />
    
    <CreateItem Include="$(PackageFolder)\*.*" >
      <Output ItemName="ZipFiles" TaskParameter="Include"/>
    </CreateItem>
  
    <ConvertToAbsolutePath Paths="$(TemporaryFolder)">
      <Output TaskParameter="AbsolutePaths" PropertyName="TemporaryFolderAbsolute"/>
    </ConvertToAbsolutePath>
  
    <Zip TaskAction="Create" CompressFiles="@(ZipFiles)" RemoveRoot="$(TemporaryFolderAbsolute)" ZipFileName="$(ZipFileName)"/>
    <RemoveDir Directories="$(TemporaryFolder)" ContinueOnError="True"/>
  </Target>

Run this script in the Developer Command Prompt for VS:

devcommandpromt

Use key /p:Configuration={config_name} to choose build and publishing configuration:

msbuild Build.proj /p:Configuration=Stage

Script result is a .zip file created in the solution folder.

package

5. Run deployment from command line

Created zip archive contains a command file that can be used while deployment to the target environment, another archive with project binaries and some xml configuration files:

archivepackage

BuildWebDeployPackage_Debug_1.0.916.2.deploy.cmd 
/Y 
/M:https://172.17.5.32:8172/msdeploy.axd 
/U:deployuser 
/P:deploypassword 
/A:Basic 
-allowUntrusted

When deploy through https TCP 8172 use basic authentication /A:Basic
Use -allowUntrusted to allow untrusted https certificate.
If user account for deploy is domain account then domain name must be used
/U:domain\username

Alternative deployment using MsDeployAgentService:

BuildWebDeployPackage_Debug_1.0.916.2.deploy.cmd 
/Y 
/M:http://172.17.5.32/MsDeployAgentService 
/U:deployuser 
/P:deploypassword

These commands will generate the kind of following msdeploy command

"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe"
-source:package='D:\temp\Release_1.0.916.1\BuildWebDeployPackage_Release_1.0.916.1.zip'
-dest:auto,computerName="http://172.17.5.32/MSDeployAgentService",userName="VIRTMIKAEL\Administrator",password="password",includeAcls="False"
-verb:sync
-disableLink:AppPoolExtension
-disableLink:ContentExtension
-disableLink:CertificateExtension
-skip:objectname='dirPath',absolutepath='obj\\Release\\Package\\PackageTmp\\App_Data$'
-skip:objectname='dirPath',absolutepath='Default\ Web\ Site\\Release\\App_Data$'
-setParamFile:"D:\temp\Release_1.0.916.1\BuildWebDeployPackage_Release_1.0.916.1.SetParameters.xml"

 

Some of web deploy errors are logged into Windows Event viewer under “Microsoft Web Deploy” section:

deployerrorevents

 

Here’s a list I’ve compiled for my own reference, along with some of the legal values that can be used. Note that these are passed into MSBuild using the /p:<PropertyName>=<Value>.

  • DeployOnBuild
    • True
    • False
  • DeployTarget
    • MsDeployPublish
    • Package
  • Configuration
    • Name of a valid solution configuration
  • CreatePackageOnPublish
    • True
    • False
  • DeployIisAppPath
    • <Web Site Name>/<Folder>
  • MsDeployServiceUrl
    • Location of MSDeploy installation you want to use
  • MsDeployPublishMethod
    • WMSVC (Web Management Service)
    • RemoteAgent
  • AllowUntrustedCertificate (used with self-signed SSL certificates)
    • True
    • False
  • UserName
  • Password
  • SkipExtraFilesOnServer (leave existing non-conflicting files alone)
    • True
    • False

External links:
Web Deploy error codes
Web Deployment Tool Now Works With Credential Store
MSDN article: How to: Create a Web Deployment Package in Visual Studio