Using MSBuild to manage deployments in SharePoint – Part 2

 This series of blog posts is taken from a session I delivered at the SharePoint Conference 2008, in March. See here to read part 1.

Part 2 – Using MSBuild to perform simple deployment tasks

SharePoint solutions quickly start to have lots of different types of artifact which require transfer to your destination farm. These include:

  • Master pages and page layouts
  • CSS, XSLT and image files in the Style Library
  • Server controls in the form of DLLs
  • User Controls in the CONTROLTEMPLATE directory
  • SharePoint Solutions and Features
  • Non-SharePoint filesystem assets (such as Control Adapters)

If you had to package and deploy these manually, you would be faced with something like this:

  • Create a copy of the production environment (content, code and configuration)
  • Get latest code from TFS
  • Compile assemblies and wrap into solutions
  • Wrap up SharePoint assets into solutions and features
  • Copy the files to the correct location on the test server
  • Un-deploy pre-existing solutions
  • Add solutions to SharePoint
  • Deploy solutions to the web application
  • Perform any other manual configuration required (feature activation etc)

And this would have to be done for each deployment, and on each environment. It is easy to see that this will cause problems – human error will become a significant factor, you won't be able to do automated testing easily, and you won't have a repeatable process that you can hand over to IT to implement on production environments.

This is where build automation comes into its own. To provide a repeatable, testable deployment, you really have to wrap this into some kind of build script. Some people are happy to just use batch files, making use of stsadm commands, robocopy (now that xcopy is depreciated) and powershell commands. This is fine, but to make sure you are successfully tracking errors, you either have to manually run those batch files and be there when they complete, or you start using some kind of automation tool.

I use TFS for source control, and so I tend to use MSBuild and TFSBuild to perform my automated builds, but other tools are out there, such as NAnt, CruiseControl and FinalBuilder.

So let's go through a couple of simple, out of the box tasks in MSBuild.

1. Where to place your custom tasks

If you open a standard csproj file in Notepad, you will see the following block:

  <!– To modify your build process, add your task inside one of the targets below and uncomment it.
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  –>

These pretty much do what they do on the tin. You can also add additional targets, or override out of the box ones (such as the default "Build" target). We will be adding our bits into the AfterBuild target.

2. How to copy files

MSBuild comes with many out of the box tasks, including "Copy". To use this, you can add the task to an action:

<Target Name="AfterBuild">
    <Copy SourceFiles="App_BrowsersCSSFriendlyAdapters.browser" DestinationFolder="\[path to destination web application]App_Browsers"/>
</Target>

As you would expect, you wouldn't want to have to add all of the files in your application individually – you can use item groups. For example, files you have added to your project via Visual Studio will appear in your project file in a <ItemGroup> node:

  <ItemGroup>
    <Compile Include="App_CodeAdaptersChangePasswordAdapter.cs" />
    <Compile Include="App_CodeAdaptersCompositeDataBoundControlAdapter.cs" />
    <Compile Include="App_CodeAdaptersCreateUserWizardAdapter.cs" />
    <Compile Include="App_CodeAdaptersDataListAdapter.cs" />
    <Compile Include="App_CodeAdaptersDetailsViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersFormViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersGridViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersLoginAdapter.cs" />
    <Compile Include="App_CodeAdaptersLoginStatusAdapter.cs" />
    <Compile Include="App_CodeAdaptersMenuAdapter.cs" />
    <Compile Include="App_CodeAdaptersPasswordRecoveryAdapter.cs" />
    <Compile Include="App_CodeAdaptersTreeViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersWebControlAdapterExtender.cs" />
  </ItemGroup>

Your copy task can easily take all of these files and copy them over to another machine:

<Copy SourceFiles="@(Compile)" DestinationFolder="\[path to destination web application]App_CodeAdapters" />

 
Finally, if you want to push some output into your build log, you can add the Output task:

<Copy SourceFiles="App_BrowsersCSSFriendlyAdapters.browser"
DestinationFolder="\[path to destination web
application]App_Browsers">
    <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles" />
</Copy>

3. How to run commands on the local machine

OK, so copying files is all well and good, but sometimes we need to run commands. For example, stsadm commands are often useful… There is an out of the box task we can use here:

<Exec Command="stsadm -o installfeature -name MyFeature"/>

This will run the command, and return, in the log for the msbuild job, the outcome of the stsadm command – hopefully "Operation completed successfully".

4. How to run commands on a remote machine

That is great, but we need to also run remote operations. There are a number of ways of doing this, including using PsExec (a SysInternals tool), but I have found this to be flaky – particularly when run through TFS Build. The main issue is that the control goes to the remote operation, but never returns to the MSBuild process, which just sits there until you kill it.

I've found that Windows Remote Shell (part of Windows Remote Management) in Windows Server 2008 to be much more stable, and also significantly more efficient when running objects on remote servers. There is some configuration you need to do on both the source and target machines, and things are generally happier if you are running in a Kerberos environment, but it is all fairly straighforward. See http://msdn2.microsoft.com/en-us/library/aa384372.aspx for more information on how to configure this.

Once you have this configured, it is trivial to run applications on remote servers from MSBuild:

<Exec Command="C:WindowsSystem32winrs -r:SERVERNAME CMD [ARGS]" />

If you are not using Kerberos, you will need to supply a username and password:

 <Exec Command="C:WindowsSystem32winrs -r:SERVERNAME -u:USERNAME -p:PASSWORD CMD [ARGS]" />

To run stsadm, for example, you could run the following task:

<Exec Command="C:WindowsSystem32winrs -r:buildserver stsadm -o installfeature -name MyFeature" />

If you have many commands you want to wrap up (for example you want to undeploy a solution, remove it, add it and redeploy it), you can easily wrap these into a batch file, which you store within your solution. You can then copy the file to the build server and run it remotely:

<Target Name="AfterBuild">
     <Copy SourceFiles="Demo2_CallStsAdm.bat" DestinationFolder="\BUILDSERVERc$DeploymentsCommandsDemo2">
      <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles" />
    </Copy>
    <Exec Command="C:WindowsSystem32winrs -r:SERVERNAME C:DeploymentsCommandsDemo2Demo2_CallStsAdm.bat" />
</Target>

Hopefully this post will give you a grounding in using MSBuild to perform deployment tasks. The next installment of this blog series will focus on creating custom tasks to automate building SharePoint solutions.

Leave a Reply


Using MSBuild to manage deployments in SharePoint – Part 2

 This series of blog posts is taken from a session I delivered at the SharePoint Conference 2008, in March. See here to read part 1.

Part 2 – Using MSBuild to perform simple deployment tasks

SharePoint solutions quickly start to have lots of different types of artifact which require transfer to your destination farm. These include:

  • Master pages and page layouts
  • CSS, XSLT and image files in the Style Library
  • Server controls in the form of DLLs
  • User Controls in the CONTROLTEMPLATE directory
  • SharePoint Solutions and Features
  • Non-SharePoint filesystem assets (such as Control Adapters)

If you had to package and deploy these manually, you would be faced with something like this:

  • Create a copy of the production environment (content, code and configuration)
  • Get latest code from TFS
  • Compile assemblies and wrap into solutions
  • Wrap up SharePoint assets into solutions and features
  • Copy the files to the correct location on the test server
  • Un-deploy pre-existing solutions
  • Add solutions to SharePoint
  • Deploy solutions to the web application
  • Perform any other manual configuration required (feature activation etc)

And this would have to be done for each deployment, and on each environment. It is easy to see that this will cause problems – human error will become a significant factor, you won't be able to do automated testing easily, and you won't have a repeatable process that you can hand over to IT to implement on production environments.

This is where build automation comes into its own. To provide a repeatable, testable deployment, you really have to wrap this into some kind of build script. Some people are happy to just use batch files, making use of stsadm commands, robocopy (now that xcopy is depreciated) and powershell commands. This is fine, but to make sure you are successfully tracking errors, you either have to manually run those batch files and be there when they complete, or you start using some kind of automation tool.

I use TFS for source control, and so I tend to use MSBuild and TFSBuild to perform my automated builds, but other tools are out there, such as NAnt, CruiseControl and FinalBuilder.

So let's go through a couple of simple, out of the box tasks in MSBuild.

1. Where to place your custom tasks

If you open a standard csproj file in Notepad, you will see the following block:

  <!– To modify your build process, add your task inside one of the targets below and uncomment it.
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  –>

These pretty much do what they do on the tin. You can also add additional targets, or override out of the box ones (such as the default "Build" target). We will be adding our bits into the AfterBuild target.

2. How to copy files

MSBuild comes with many out of the box tasks, including "Copy". To use this, you can add the task to an action:

<Target Name="AfterBuild">
    <Copy SourceFiles="App_BrowsersCSSFriendlyAdapters.browser" DestinationFolder="\[path to destination web application]App_Browsers"/>
</Target>

As you would expect, you wouldn't want to have to add all of the files in your application individually – you can use item groups. For example, files you have added to your project via Visual Studio will appear in your project file in a <ItemGroup> node:

  <ItemGroup>
    <Compile Include="App_CodeAdaptersChangePasswordAdapter.cs" />
    <Compile Include="App_CodeAdaptersCompositeDataBoundControlAdapter.cs" />
    <Compile Include="App_CodeAdaptersCreateUserWizardAdapter.cs" />
    <Compile Include="App_CodeAdaptersDataListAdapter.cs" />
    <Compile Include="App_CodeAdaptersDetailsViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersFormViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersGridViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersLoginAdapter.cs" />
    <Compile Include="App_CodeAdaptersLoginStatusAdapter.cs" />
    <Compile Include="App_CodeAdaptersMenuAdapter.cs" />
    <Compile Include="App_CodeAdaptersPasswordRecoveryAdapter.cs" />
    <Compile Include="App_CodeAdaptersTreeViewAdapter.cs" />
    <Compile Include="App_CodeAdaptersWebControlAdapterExtender.cs" />
  </ItemGroup>

Your copy task can easily take all of these files and copy them over to another machine:

<Copy SourceFiles="@(Compile)" DestinationFolder="\[path to destination web application]App_CodeAdapters" />

 
Finally, if you want to push some output into your build log, you can add the Output task:

<Copy SourceFiles="App_BrowsersCSSFriendlyAdapters.browser"
DestinationFolder="\[path to destination web
application]App_Browsers">
    <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles" />
</Copy>

3. How to run commands on the local machine

OK, so copying files is all well and good, but sometimes we need to run commands. For example, stsadm commands are often useful… There is an out of the box task we can use here:

<Exec Command="stsadm -o installfeature -name MyFeature"/>

This will run the command, and return, in the log for the msbuild job, the outcome of the stsadm command – hopefully "Operation completed successfully".

4. How to run commands on a remote machine

That is great, but we need to also run remote operations. There are a number of ways of doing this, including using PsExec (a SysInternals tool), but I have found this to be flaky – particularly when run through TFS Build. The main issue is that the control goes to the remote operation, but never returns to the MSBuild process, which just sits there until you kill it.

I've found that Windows Remote Shell (part of Windows Remote Management) in Windows Server 2008 to be much more stable, and also significantly more efficient when running objects on remote servers. There is some configuration you need to do on both the source and target machines, and things are generally happier if you are running in a Kerberos environment, but it is all fairly straighforward. See http://msdn2.microsoft.com/en-us/library/aa384372.aspx for more information on how to configure this.

Once you have this configured, it is trivial to run applications on remote servers from MSBuild:

<Exec Command="C:WindowsSystem32winrs -r:SERVERNAME CMD [ARGS]" />

If you are not using Kerberos, you will need to supply a username and password:

 <Exec Command="C:WindowsSystem32winrs -r:SERVERNAME -u:USERNAME -p:PASSWORD CMD [ARGS]" />

To run stsadm, for example, you could run the following task:

<Exec Command="C:WindowsSystem32winrs -r:buildserver stsadm -o installfeature -name MyFeature" />

If you have many commands you want to wrap up (for example you want to undeploy a solution, remove it, add it and redeploy it), you can easily wrap these into a batch file, which you store within your solution. You can then copy the file to the build server and run it remotely:

<Target Name="AfterBuild">
     <Copy SourceFiles="Demo2_CallStsAdm.bat" DestinationFolder="\BUILDSERVERc$DeploymentsCommandsDemo2">
      <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles" />
    </Copy>
    <Exec Command="C:WindowsSystem32winrs -r:SERVERNAME C:DeploymentsCommandsDemo2Demo2_CallStsAdm.bat" />
</Target>

Hopefully this post will give you a grounding in using MSBuild to perform deployment tasks. The next installment of this blog series will focus on creating custom tasks to automate building SharePoint solutions.

Leave a Reply