Example of deploying web apps using MSBuild

Here is a sample MSBuild script that will deploy two web apps from Sample.sln to localhost. It is very nasty, but worked ok for me.

First, we create our XML .build file and import SDCTasks for our IIS operations. The remaining code snippets go in the <Project /> node.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\SDCTasks\Microsoft.Sdc.Common.tasks"/> 
  ...
</Project>

Next we declare and define a DeploymentPath, and also set the solution file (SLN) we are going to be building. I have then defined two WebProject items. This is used for batching, which is the MSBuild way of repeating a single task with different inputs each time. Each WebProject item includes the name of the IIS web that will be created, as well as the SourceFolder that contains the ASPX files and web /bin after building the SLN (which by default builds to a PrecompiledWeb directory). All these variable names are arbitrary – the only native MSBuild nodes are PropertyGroup and ItemGroup.

  <PropertyGroup>
    <DeploymentPath>C:\InetPub\wwwroot</DeploymentPath>    
  </PropertyGroup>
  <ItemGroup>
    <ProjectReferences Include="Sample.sln" />
  </ItemGroup>
  <ItemGroup>
    <WebProject Include="MyWebService">
      <WebName>Sample_MyWebService</WebName>
      <SourceFolder>$(MSBuildProjectDirectory)\PrecompiledWeb\Sample.MyWebService</SourceFolder>      
    </WebProject>
    <WebProject Include="MyWebSite">
      <WebName>Sample_MyWebSite</WebName>
     <SourceFolder>$(MSBuildProjectDirectory)\PrecompiledWeb\Sample.MyWebSite</SourceFolder>      
    </WebProject>
  </ItemGroup>

The next step is defining the build target. This delegates the build/compilation step to msbuild itself (rather than explicitly calling the CSC compiler which can also be done), by calling msbuild on the SLN file we defined in the ProjectReferences variable.

  <Target Name="Build">
    <MSBuild Projects="@(ProjectReferences)" Targets="Build" />
  </Target>

Next we have the deployment step. This replaces the web.config in the compiled app with a web.config.local file we have (so you can change configuration for deployment), does a basic file copy to move the files to the deployment path, then creates a virtual directory and web application at the deployment path to allow the ASPX files to execute.

  <Target Name="Deploy">
    <!-- Replace web configs in build directory with deployment configuration -->
    <Delete Files="%(WebProject.SourceFolder)\Web.Config" />
    <Copy
      SourceFiles="%(WebProject.SourceFolder)\Web.Config.local"
      DestinationFiles="%(WebProject.SourceFolder)\Web.Config" />
    <!-- Copy webs from build to deployment-->
    <Folder.Copy
    Source="%(WebProject.SourceFolder)"
    Destination="$(DeploymentPath)\%(WebProject.WebName)" />
    <!-- Create Webs on Localhost -->
    <Web.WebSite.CreateVirtualDirectory
        VirtualDirectoryName="%(WebProject.WebName)"
        Path="$(DeploymentPath)\%(WebProject.WebName)"
        AppCreate="true" />
  </Target>

Note the %(WebProject.property) style references. These are the batches I mentioned earlier, which means that tasks containing %(..) within attributes will repeat for each WebProject ItemGroup defined. This just saves us from duplicating the Folder.Copy task for each web in the SLN (for example), which can be helpful if you are deploying several web applications in the one deployment step.

For good measure we can also clean up after ourselves.

  <Target Name="Clean">
    <Web.WebSite.DeleteVirtualDirectory VirtualDirectoryName="%(WebProject.WebName)" />
    <RemoveDir Directories="$(DeploymentPath)\%(WebProject.WebName)" />
    <RemoveDir Directories="$(MSBuildProjectDirectory)\PrecompiledWeb" />
  </Target>

You can execute this from the command prompt:

msbuild sample.build /target:clean,build,deploy

This was mainly done for a proof-of-concept, and after running it I had my two web apps deployed and running on localhost as expected (er, hoped). You can obviously do a lot more useful stuff than this, like checking out from source control first, creating remote webs, virtual directories and application pools, running unit tests, and varying deployment options based on command line arguments. The main drawback of this approach is that you do not get a nice, self-contained package/installer for the deployment, and as such you will need direct network access to the deployment server as well as the relevant permissions.

Now to try similar stuff using WiX

Comments