Fri
Aug 3
2007

MSBuild Automated Deploy Scripts for Web Applications

These are suggested steps for creating batch scripts that will deploy a solution to DEV, TEST and PROD. If this article looks like it is more complicated than it should be, I agree. I think batch scripts with an XML read/write utility or NAnt would be a better choice:

  • The syntax for batch files and NAnt are both easier to read/understand
  • NAnt allows custom tasks can be written in C# directly in the build file. MSBuild requires a separate assembly (and associated solution and project files).
  • The big advantage with MSBuild is it it built-in. However this advantage is eliminated as we need to use the MSBuildCommunityTasks DLL to read/write config files.

Here is the MyApp.build file:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <!--
       MY WEB APPLICATION DEPLOY SCRIPT
 
       To deploy this application use the batch files DeployToXXX.bat
 -->
 <Import Project="..\..\Utilities\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
 
 <PropertyGroup>
 
   <TempFolder>..\..\_tempfolder</TempFolder>
   <DevFolder>\\mydevserver\wwwroot\myapp</DevFolder>
   <TestFolder>\\mytestserver\wwwroot\myapp</TestFolder>
 
 </PropertyGroup>
 
 
 
 <Target Name="DeployToDev">
 
   <AspNetCompiler
       VirtualPath="MyApp"
       PhysicalPath="..\ "
       TargetPath="$(TempFolder)"
       Clean="true"
       Force="true"
       Debug="false"
       Updateable="false" />
 
   <XmlRead XmlFileName="$(TempFolder)\web.config"
            XPath="/configuration/appSettings/add[@key='DEV.Log4NetConnectionString']/@value">
     <Output TaskParameter="Value" PropertyName="Log4NetConnectionString"/>
   </XmlRead>
 
   <XmlUpdate XmlFileName="$(TempFolder)\web.config"
              XPath="/configuration/appSettings/add[@key='Environment']/@value"
              Value="DEV" />
 
   <XmlUpdate XmlFileName="$(TempFolder)\web.config"
              XPath="/configuration/log4net/appender[@type='log4net.Appender.ADONetAppender']/connectionString/@value"
              Value="$(Log4NetConnectionString)" />
 
   <XmlUpdate XmlFileName="$(TempFolder)\web.config"
              XPath="/configuration/system.web/compilation/@debug"
              Value="false" />
 
   <CreateItem
       Include="$(DevFolder)\**\*"
     <Output TaskParameter="Include" ItemName="FilesToCleanFromTarget" />
   </CreateItem>
 
   <Delete Files="@(FilesToCleanFromTarget)" />
 
   <CreateItem
     Include="$(TempFolder)\**\*"
     Exclude="$(TempFolder)\Deploy\*">
     <Output TaskParameter="Include" ItemName="FilesToDeploy"/>
   </CreateItem>
 
   <Copy SourceFiles="@(FilesToDeploy)"
         DestinationFiles="@(FilesToDeploy->'$(DevFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
   />
 
   <RemoveDir Directories="$(TempFolder)" />
 
 </Target>
 
 
 
 <Target Name="DeployToTest">
 
   <CreateItem
       Include="$(DevFolder)\**\*"
       Exclude="$(DevFolder)\Deploy\*">
     <Output TaskParameter="Include" ItemName="FilesToDeploy"/>
   </CreateItem>
 
   <Copy SourceFiles="@(FilesToDeploy)"
         DestinationFiles="@(FilesToDeploy->'$(TestFolder)\%(RecursiveDir)%(Filename)%(Extension)')" />
 
   <XmlRead XmlFileName="$(TestFolder)\web.config"
            XPath="/configuration/appSettings/add[@key='TEST.Log4NetConnectionString']/@value">
     <Output TaskParameter="Value" PropertyName="Log4NetConnectionString"/>
   </XmlRead>
 
   <XmlUpdate XmlFileName="$(TestFolder)\web.config"
              XPath="/configuration/appSettings/add[@key='Environment']/@value"
              Value="TEST" />
 
   <XmlUpdate XmlFileName="$(TestFolder)\web.config"
              XPath="/configuration/log4net/appender[@type='log4net.Appender.ADONetAppender']/connectionString/@value"
              Value="$(Log4NetConnectionString)" />
 
   <XmlUpdate XmlFileName="$(TestFolder)\web.config"
              XPath="/configuration/system.web/compilation/@debug"
              Value="false" />
 
 </Target>
 
 
 
 <Target Name="DeployToProd">
 
   <Copy SourceFiles="$(TestFolder)\web.config"
         DestinationFiles="$(TestFolder)\Web.config.test" />
 
   <XmlRead XmlFileName="$(TestFolder)\web.config"
            XPath="/configuration/appSettings/add[@key='PROD.Log4NetConnectionString']/@value">
     <Output TaskParameter="Value" PropertyName="Log4NetConnectionString"/>
   </XmlRead>
 
   <XmlUpdate XmlFileName="$(TestFolder)\web.config"
              XPath="/configuration/appSettings/add[@key='Environment']/@value"
              Value="PROD" />
 
   <XmlUpdate XmlFileName="$(TestFolder)\web.config"
              XPath="/configuration/log4net/appender[@type='log4net.Appender.ADONetAppender']/connectionString/@value"
              Value="$(Log4NetConnectionString)" />
 
   <XmlUpdate XmlFileName="$(TestFolder)\web.config"
              XPath="/configuration/system.web/compilation/@debug"
              Value="false" />
 
   <Prompt Text="Please ask the production administrator to copy MyApp from test to production. Press Enter when complete."/>
 
   <Copy SourceFiles="$(TestFolder)\Web.config.test"
         DestinationFiles="$(TestFolder)\Web.config" />
   <Delete Files="$(TestFolder)\Web.config.test" />
 
 </Target>
 
</Project>

Then to run use the following batch script:

@echo off
echo.
echo --------------------------------------------------
echo.
echo   Deploy the Application to Dev
echo.
echo   Ensure you have retrieved the latest version
echo   from source control.
echo.
echo --------------------------------------------------
echo.
 
call "%VS80COMNTOOLS%\vsvars32.bat" 
 
set MSBuildCommunityTasksPath=..\..\Utilities\MSBuildCommunityTasks
 
msbuild deploy.build /target:DeployToDev
 
pause

This requires the MSBuildCommunityTasks DLLs. Download, install, copy the DLLs to your project directory under Utilities, checkin to VSS and then uninstall. Placing the DLLs in VSS ensures subsequent developers can use the project without hacking their way through a bunch of dependencies.

1 Comment so far

  1. marc lang July 10th, 2009 1:44 am

    Looks useful,

    having real problems selecting nodes though, always returns saying

    same for update.

    This is using the exact code you are using, only changing my path to the web.config file.

Leave a reply

© 2009 Brian Low. All rights reserved.