SharePoint Best Practices conferences

December 16th, 2008 by benrobb

SharePoint Best Practices 

I'm very pleased to have been selected to speak at the Best Practice conferences in San Diego in February and in London in April. I'll be doing a session on what you need to look out for when branding search UI and another the benefits of automating the build of your QA environments to streamline your deployment processes, using Powershell and Hyper-V.

In the current climate, though, many budget holders will be cutting back on conference attendence, so why should you be pushing your managers to be allowed to attend one of these conferences?

I would ask your decision makers whether they can afford *not* to send you. The SharePoint Best Practice Conference is about teaching you how you can reduce risk on projects and deliver value. It will teach you how to avoid common mistakes and give you the ability to design your solutions "right first time", avoiding costly rework.

With project budgets getting squeezed, you need to know how to eke out the limited resources to deliver the most robust solution – can you afford not to come?

 

SharePoint Best Practices conferences

December 16th, 2008 by benrobb

SharePoint Best Practices 

I'm very pleased to have been selected to speak at the Best Practice conferences in San Diego in February and in London in April. I'll be doing a session on what you need to look out for when branding search UI and another the benefits of automating the build of your QA environments to streamline your deployment processes, using Powershell and Hyper-V.

In the current climate, though, many budget holders will be cutting back on conference attendence, so why should you be pushing your managers to be allowed to attend one of these conferences?

I would ask your decision makers whether they can afford *not* to send you. The SharePoint Best Practice Conference is about teaching you how you can reduce risk on projects and deliver value. It will teach you how to avoid common mistakes and give you the ability to design your solutions "right first time", avoiding costly rework.

With project budgets getting squeezed, you need to know how to eke out the limited resources to deliver the most robust solution – can you afford not to come?

 

HowTo: Display parts of a page to anonymous users only

June 20th, 2008 by benrobb

My ex-colleague Chris O'Brien (prolific blogger at http://www.sharepointnutsandbolts.com/) has a good post on some of the hidden gems in the SharePoint web control libraries (http://www.sharepointnutsandbolts.com/2008/03/great-controls-to-be-aware-of-when.html).

In his discussion about SPSecurityTrimmedControl he mentions some issue he has when trying to set up a section of his page to show to anonymous users, but not authenticated users. He tried:

   1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" AuthenticationRestrictions="AnonymousUsersOnly">
   2:     This *should* be visible only to anonymous users but it doesn't work..    
   3: </SharepointWebControls:SPSecurityTrimmedControl> 

This wasn't working (though it looks like it should). An alternative method is to use the standard ASP.NET control LoginView:

<asp:LoginView runat="server">
      <AnonymousTemplate>
                           This will be visible only to anonymous users and does work…
       </AnonymousTemplate>
       < LoggedInTemplate>
                            This will be visible only to authenticated users and does work…
       </LoggedInTemplate>
</asp:LoginView>

This works exactly as advertised.

HowTo: Display parts of a page to anonymous users only

June 20th, 2008 by benrobb

My ex-colleague Chris O'Brien (prolific blogger at http://www.sharepointnutsandbolts.com/) has a good post on some of the hidden gems in the SharePoint web control libraries (http://www.sharepointnutsandbolts.com/2008/03/great-controls-to-be-aware-of-when.html).

In his discussion about SPSecurityTrimmedControl he mentions some issue he has when trying to set up a section of his page to show to anonymous users, but not authenticated users. He tried:

   1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" AuthenticationRestrictions="AnonymousUsersOnly">
   2:     This *should* be visible only to anonymous users but it doesn't work..    
   3: </SharepointWebControls:SPSecurityTrimmedControl> 

This wasn't working (though it looks like it should). An alternative method is to use the standard ASP.NET control LoginView:

<asp:LoginView runat="server">
      <AnonymousTemplate>
                           This will be visible only to anonymous users and does work…
       </AnonymousTemplate>
       < LoggedInTemplate>
                            This will be visible only to authenticated users and does work…
       </LoggedInTemplate>
</asp:LoginView>

This works exactly as advertised.

Using MSBuild to manage deployments in SharePoint – Part 3

April 25th, 2008 by benrobb

This is the third part of a series of posts around using MSBuild for deployments of SharePoint applications, and in this post I introduce a custom MSBuild task to perform a custom Export of content from my Development environment.

Creating a custom MSBuild task to perform a controlled export from SharePoint

One of the key features of MCMS which was dropped in the MOSS platform was a tool to enable the export of the *code* part of a site. In MCMS, we could choose to export templates to an SDO, and import them into a destination website without affecting content. This gave us the ability to do full lifecycle management of our code, which is something that is hard (out of the box) with MOSS.So in order to replicate this kind of functionality, we could use a tool such as Chris O'Brien's excellent SP Content Deployment Wizard (http://www.codeplex.com/SPDeploymentWizard). This allows us to choose exactly which Export Objects we want to include in the export, and save that to (a cab) file. That's great for one off deployments, but I needed to have a way of scripting this so that I could automate it via TFS builds.

To do this, I created a class library which inherited from Microsoft.Build.Utilities.Task. I could have implemented the ITask interface, but I prefer directly inheriting from Task, since this gives me access to a Log object with no additional code.

The full code is at the end of this post, and I'm not going to step through it: it is mostly about setting up the SPExportSettings object and then executing it using SPExport.Run(). Rather than hard code these settings, you can take advantage of an inbuit XML schema in SharePoint: "deployment-exportsettings-schema", and provide an XML document with all of the settings you need. That way you can decouple your (generic) MSBuild task from the project you are working on at the current time.

In the example XML file (below), I have the Master Page Gallery and the Style Library as my two Deployment Objects, and when my custom task runs, I get an export CMP file with just these objects (and their dependencies and children – see the "IncludeDescendants" and "ExcludeChildren" flags in the configuration).

<?xml version="1.0" encoding="utf-8" ?>
<ExportSettings 
     xmlns="urn:deployment-exportsettings-schema"
     
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 
     xmlns:xsd
="http://www.w3.org/2001/XMLSchema" 
     SiteUrl
="http://ben:20010" FileLocation="Export" BaseFileName="Export.cmp" IncludeSecurity="All" 
     IncludeVersions
="CurrentVersion" ExportPublicSchema="true" ExportMethod="ExportAll" ExcludeDependencies="false" > 
<ExportObjects>
<!– Master Pages –>
    <DeploymentObject Id="323d4797-b5af-4271-92cd-eac348ebf7d5" ParentId="bab096c8-7bf6-435b-b541-4f2ecd8aace7"
                               Type
="List" IncludeDescendants="All" ExcludeChildren="false" />                      
<!– Style Library  –>                         
    <DeploymentObject Id="d6635d08-ccb0-489e-9903-6dd07b7622b4" ParentId="bab096c8-7bf6-435b-b541-4f2ecd8aace7" 
                               Type
="List" IncludeDescendants="All" ExcludeChildren="false" />            
    </ExportObjects> 
</ExportSettings>

These parameters are documented in MSDN here.

Importing this into a Build Server farm is simple too; I can simply call the “stsadm -o import” command on my build server – see Part 2 of this series for more information on remote execution of STSADM via WinRS.

This gives us a very extensible model. I can store the configuration file in a project in TFS, and then run my custom task as part of an automated build without any user interaction. My *.csproj file can have the following:

  <UsingTask AssemblyFile="C:BenTFSSPCDemoBenRobb.MSBuildinReleaseBenRobb.MSBuild.dll" TaskName="BenRobb.MSBuild.SPExportTask" />
  <Target Name="AfterBuild">
    <SPExportTask ConfigurationFile="ExportSettings.xml" />
    <Copy SourceFiles="@(ExportSourceFiles)" DestinationFolder="\mossbasicDeploymentsSPExportsDemo4" />
    <Exec Command="C:WindowsSystem32winrs -r:mossbasic C:DeploymentsCommandsDemo4Demo4_Import.bat" />
  </Target>

The <UsingTask> node imports my custom task, and then I can refer to the class I need directly by class name. You do need to be careful about naming clashes with out of the box tasks.

Hopefully this series of posts will have given people ideas about how to extend MSBuild to take care of common SharePoint development tasks, and give credence to the idea that Continuous Integration and streamlined deployment processes are possible when buidling applications for SharePoint.


Sample code:

using System;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using Microsoft.SharePoint.Deployment; 
namespace BenRobb.MSBuild
{
    public class SPExportTask : Task
    {
        private XmlDocument _settingsFile = new XmlDocument();
        private string _configurationFile = string.Empty;
        /// <summary>
        /// The ConfigurationFile is the (project relative) path to the XML file
        /// which sets up the boundaries of the SPExport. It is required for this
        /// task to run.
        /// </summary>
        [Required]
        public string ConfigurationFile
        {
            get { return _configurationFile; }
            set { _configurationFile = value; }
        }

 
        /// <summary>
        /// Execute() provides the entry point to the MSBuild task. This method calls
        /// getExportSettings to configure the SPExport, and then runs the
        /// export.
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            Log.LogMessage("Exporting from SharePoint site, using configuration file {0}", _configurationFile);
            _settingsFile.Load(_configurationFile);
            XmlElement xSettings = _settingsFile.DocumentElement;
            if (xSettings != null)
            {
                SPExportSettings exportSettings = getExportSettings(xSettings);
                using (SPExport export = new SPExport(exportSettings))
                {
                    export.Run();
                }
            }
            return true;
        }
         /// <summary>
        /// getExportSettings goes through the supplied XML fragment and uses
        /// this to manage the parameters which control the export from SharePoint.
        ///
        /// It also calls addExportObjects to build up the object list which will
        /// be exported.
 
      /// </summary>
        /// <param name="xSettings">The XML element for setting parameters for SPExport</param>
        /// <returns>A populated SPExportSettings object</returns>
        private SPExportSettings getExportSettings(XmlElement xSettings)
        {
            SPExportSettings exportSettings = new SPExportSettings();
            exportSettings.SiteUrl = xSettings.Attributes["SiteUrl"].Value;
            exportSettings.FileLocation = 
            xSettings.Attributes["FileLocation"].Value;
            exportSettings.BaseFileName = xSettings.Attributes["BaseFileName"].Value; 
           
exportSettings.FileCompression =
true;
            exportSettings.ExportMethod = (SPExportMethodType)Enum.Parse(typeof(SPExportMethodType), xSettings.Attributes["ExportMethod"].Value, true);
            exportSettings.ExcludeDependencies = Convert.ToBoolean(xSettings.Attributes["ExcludeDependencies"].Value);
            exportSettings.IncludeSecurity = (SPIncludeSecurity)Enum.Parse(typeof(SPIncludeSecurity), xSettings.Attributes["IncludeSecurity"].Value, true);
            exportSettings.IncludeVersions = (SPIncludeVersions)Enum.Parse(typeof(SPIncludeVersions), xSettings.Attributes["IncludeVersions"].Value, true);
            exportSettings.TestRun = false;
 
            SPExportObjectCollection eoc = exportSettings.ExportObjects;
            addExportObjects(eoc);
            exportSettings.Validate();
            exportSettings.OverwriteExistingDataFile = true;
            exportSettings.CommandLineVerbose = true;
            return exportSettings;
        }

        /// <summary> 
       
/// This method iterates through the ExportObject elements within the configuration XML
        /// file and adds them to the supplied SPExportObjectCollection.
        /// </summary>
        /// <param name="eoc">The SPExportObjectCollection to add the SPExportObject
        /// objects which are defined in the configuration XML file to.</param>
        private void addExportObjects(SPExportObjectCollection eoc)
        {
            XmlNode xSettings = _settingsFile.DocumentElement.GetElementsByTagName("ExportObjects")[0];
            foreach (XmlNode xExportObject in xSettings.SelectNodes("*"))
            {
                Guid guidID = new Guid(xExportObject.Attributes["Id"].Value);
                SPDeploymentObjectType deploymentObjectType = (SPDeploymentObjectType)Enum.Parse(typeof(SPDeploymentObjectType), xExportObject.Attributes["Type"].Value);
                Guid parentID = new Guid(xExportObject.Attributes["ParentId"].Value);
                bool b_excludeChildren = Convert.ToBoolean(xExportObject.Attributes["ExcludeChildren"].Value);
                SPExportObject exportObject = new SPExportObject();
                exportObject.Id = guidID;
                exportObject.ParentId = parentID;
                exportObject.Type = deploymentObjectType;
                exportObject.ExcludeChildren = b_excludeChildren;
                exportObject.IncludeDescendants = (SPIncludeDescendants)Enum.Parse(typeof(SPIncludeDescendants), xExportObject.Attributes["IncludeDescendants"].Value);
                eoc.Add(exportObject);
            }
        }
    }
}

Using MSBuild to manage deployments in SharePoint – Part 3

April 25th, 2008 by benrobb

This is the third part of a series of posts around using MSBuild for deployments of SharePoint applications, and in this post I introduce a custom MSBuild task to perform a custom Export of content from my Development environment.

Creating a custom MSBuild task to perform a controlled export from SharePoint

One of the key features of MCMS which was dropped in the MOSS platform was a tool to enable the export of the *code* part of a site. In MCMS, we could choose to export templates to an SDO, and import them into a destination website without affecting content. This gave us the ability to do full lifecycle management of our code, which is something that is hard (out of the box) with MOSS.So in order to replicate this kind of functionality, we could use a tool such as Chris O'Brien's excellent SP Content Deployment Wizard (http://www.codeplex.com/SPDeploymentWizard). This allows us to choose exactly which Export Objects we want to include in the export, and save that to (a cab) file. That's great for one off deployments, but I needed to have a way of scripting this so that I could automate it via TFS builds.

To do this, I created a class library which inherited from Microsoft.Build.Utilities.Task. I could have implemented the ITask interface, but I prefer directly inheriting from Task, since this gives me access to a Log object with no additional code.

The full code is at the end of this post, and I'm not going to step through it: it is mostly about setting up the SPExportSettings object and then executing it using SPExport.Run(). Rather than hard code these settings, you can take advantage of an inbuit XML schema in SharePoint: "deployment-exportsettings-schema", and provide an XML document with all of the settings you need. That way you can decouple your (generic) MSBuild task from the project you are working on at the current time.

In the example XML file (below), I have the Master Page Gallery and the Style Library as my two Deployment Objects, and when my custom task runs, I get an export CMP file with just these objects (and their dependencies and children – see the "IncludeDescendants" and "ExcludeChildren" flags in the configuration).

<?xml version="1.0" encoding="utf-8" ?>
<ExportSettings 
     xmlns="urn:deployment-exportsettings-schema"
     
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 
     xmlns:xsd
="http://www.w3.org/2001/XMLSchema" 
     SiteUrl
="http://ben:20010" FileLocation="Export" BaseFileName="Export.cmp" IncludeSecurity="All" 
     IncludeVersions
="CurrentVersion" ExportPublicSchema="true" ExportMethod="ExportAll" ExcludeDependencies="false" > 
<ExportObjects>
<!– Master Pages –>
    <DeploymentObject Id="323d4797-b5af-4271-92cd-eac348ebf7d5" ParentId="bab096c8-7bf6-435b-b541-4f2ecd8aace7"
                               Type
="List" IncludeDescendants="All" ExcludeChildren="false" />                      
<!– Style Library  –>                         
    <DeploymentObject Id="d6635d08-ccb0-489e-9903-6dd07b7622b4" ParentId="bab096c8-7bf6-435b-b541-4f2ecd8aace7" 
                               Type
="List" IncludeDescendants="All" ExcludeChildren="false" />            
    </ExportObjects> 
</ExportSettings>

These parameters are documented in MSDN here.

Importing this into a Build Server farm is simple too; I can simply call the “stsadm -o import” command on my build server – see Part 2 of this series for more information on remote execution of STSADM via WinRS.

This gives us a very extensible model. I can store the configuration file in a project in TFS, and then run my custom task as part of an automated build without any user interaction. My *.csproj file can have the following:

  <UsingTask AssemblyFile="C:BenTFSSPCDemoBenRobb.MSBuildinReleaseBenRobb.MSBuild.dll" TaskName="BenRobb.MSBuild.SPExportTask" />
  <Target Name="AfterBuild">
    <SPExportTask ConfigurationFile="ExportSettings.xml" />
    <Copy SourceFiles="@(ExportSourceFiles)" DestinationFolder="\mossbasicDeploymentsSPExportsDemo4" />
    <Exec Command="C:WindowsSystem32winrs -r:mossbasic C:DeploymentsCommandsDemo4Demo4_Import.bat" />
  </Target>

The <UsingTask> node imports my custom task, and then I can refer to the class I need directly by class name. You do need to be careful about naming clashes with out of the box tasks.

Hopefully this series of posts will have given people ideas about how to extend MSBuild to take care of common SharePoint development tasks, and give credence to the idea that Continuous Integration and streamlined deployment processes are possible when buidling applications for SharePoint.


Sample code:

using System;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using Microsoft.SharePoint.Deployment; 
namespace BenRobb.MSBuild
{
    public class SPExportTask : Task
    {
        private XmlDocument _settingsFile = new XmlDocument();
        private string _configurationFile = string.Empty;
        /// <summary>
        /// The ConfigurationFile is the (project relative) path to the XML file
        /// which sets up the boundaries of the SPExport. It is required for this
        /// task to run.
        /// </summary>
        [Required]
        public string ConfigurationFile
        {
            get { return _configurationFile; }
            set { _configurationFile = value; }
        }

 
        /// <summary>
        /// Execute() provides the entry point to the MSBuild task. This method calls
        /// getExportSettings to configure the SPExport, and then runs the
        /// export.
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            Log.LogMessage("Exporting from SharePoint site, using configuration file {0}", _configurationFile);
            _settingsFile.Load(_configurationFile);
            XmlElement xSettings = _settingsFile.DocumentElement;
            if (xSettings != null)
            {
                SPExportSettings exportSettings = getExportSettings(xSettings);
                using (SPExport export = new SPExport(exportSettings))
                {
                    export.Run();
                }
            }
            return true;
        }
         /// <summary>
        /// getExportSettings goes through the supplied XML fragment and uses
        /// this to manage the parameters which control the export from SharePoint.
        ///
        /// It also calls addExportObjects to build up the object list which will
        /// be exported.
 
      /// </summary>
        /// <param name="xSettings">The XML element for setting parameters for SPExport</param>
        /// <returns>A populated SPExportSettings object</returns>
        private SPExportSettings getExportSettings(XmlElement xSettings)
        {
            SPExportSettings exportSettings = new SPExportSettings();
            exportSettings.SiteUrl = xSettings.Attributes["SiteUrl"].Value;
            exportSettings.FileLocation = 
            xSettings.Attributes["FileLocation"].Value;
            exportSettings.BaseFileName = xSettings.Attributes["BaseFileName"].Value; 
           
exportSettings.FileCompression =
true;
            exportSettings.ExportMethod = (SPExportMethodType)Enum.Parse(typeof(SPExportMethodType), xSettings.Attributes["ExportMethod"].Value, true);
            exportSettings.ExcludeDependencies = Convert.ToBoolean(xSettings.Attributes["ExcludeDependencies"].Value);
            exportSettings.IncludeSecurity = (SPIncludeSecurity)Enum.Parse(typeof(SPIncludeSecurity), xSettings.Attributes["IncludeSecurity"].Value, true);
            exportSettings.IncludeVersions = (SPIncludeVersions)Enum.Parse(typeof(SPIncludeVersions), xSettings.Attributes["IncludeVersions"].Value, true);
            exportSettings.TestRun = false;
 
            SPExportObjectCollection eoc = exportSettings.ExportObjects;
            addExportObjects(eoc);
            exportSettings.Validate();
            exportSettings.OverwriteExistingDataFile = true;
            exportSettings.CommandLineVerbose = true;
            return exportSettings;
        }

        /// <summary> 
       
/// This method iterates through the ExportObject elements within the configuration XML
        /// file and adds them to the supplied SPExportObjectCollection.
        /// </summary>
        /// <param name="eoc">The SPExportObjectCollection to add the SPExportObject
        /// objects which are defined in the configuration XML file to.</param>
        private void addExportObjects(SPExportObjectCollection eoc)
        {
            XmlNode xSettings = _settingsFile.DocumentElement.GetElementsByTagName("ExportObjects")[0];
            foreach (XmlNode xExportObject in xSettings.SelectNodes("*"))
            {
                Guid guidID = new Guid(xExportObject.Attributes["Id"].Value);
                SPDeploymentObjectType deploymentObjectType = (SPDeploymentObjectType)Enum.Parse(typeof(SPDeploymentObjectType), xExportObject.Attributes["Type"].Value);
                Guid parentID = new Guid(xExportObject.Attributes["ParentId"].Value);
                bool b_excludeChildren = Convert.ToBoolean(xExportObject.Attributes["ExcludeChildren"].Value);
                SPExportObject exportObject = new SPExportObject();
                exportObject.Id = guidID;
                exportObject.ParentId = parentID;
                exportObject.Type = deploymentObjectType;
                exportObject.ExcludeChildren = b_excludeChildren;
                exportObject.IncludeDescendants = (SPIncludeDescendants)Enum.Parse(typeof(SPIncludeDescendants), xExportObject.Attributes["IncludeDescendants"].Value);
                eoc.Add(exportObject);
            }
        }
    }
}

Using MSBuild to manage deployments in SharePoint – Part 2

March 11th, 2008 by benrobb

 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.

Using MSBuild to manage deployments in SharePoint – Part 2

March 11th, 2008 by benrobb

 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.

Using MSBuild to manage deployments in SharePoint – Part 1

March 10th, 2008 by benrobb

This series of blog posts is taken from a session I delivered at the SharePoint Conference 2008, in March.

Part 1: Why would you want to have build automation in SharePoint Solutions?

For simple solutions, where you have little custom code, and a single, linear set of projects, you can get away with not formalising your deployment process. However, if you have a large intranet or public facing site, you will probably be faced with a solution which has:

  • Frequent changes to functionality.
  • Multiple teams working on different streams
  • Cross-project dependencies
  • Different release schedules
  • Tight control over the production environment.

A typical site will probably start off being simple: a foundation project will determine the network topology, it will do the initial build and configuration of the servers, and it will have the initial development of the core components of the site – a master page, some custom controls and some page layouts and CSS. Typically this will have a single team of developers working towards a "big bang" launch. All being well, they will launch the site and have a few beers to celebrate. If they are contractors, or working for an agency, they may well go off to other jobs.

But then what? 

On a large scale website, that's never the end of the story. Large websites will never stay static: either in their content or their functionality. There will be ongoing maintainence and bug fixing. There will be change requests to amend the existing functionality. There will also be brand new requirements to provide the site with completely new features.

Programme managers would, in an ideal world, see that these projects are carefully managed into phases, and ensure that each code drop goes through the same process as the first phase. Developers would work together to build their bits of functionality, and then at the end, have integration and testing tasks to form a release schedule. But back in the real world, how does this actually pan out? When bugs are found post launch, your team may well have SLAs to meet. Regardless, you need to fix the problem as quickly as possible. That means that you need to deploy your fixes to your site while your other projects are still running, and often while those projects have breaking changes to your site. This means that you need to start branching your code base – potentially in your configuration management, your file system / .NET assets and your SharePoint artefacts.

Branching

Branching is often feared by developers, but there are some key lessons which should be learnt when determining your branching strategy. This primer on branching and merging, from MSDN explains how different strategies can affect your team, but the bottom line is that you need to think carefully about your branching strategy. If your merges are taking too long (i.e. you have developers sitting round doing nothing for a week), then you are not being granular enough. You need to branch only when you really need to, and merge as soon as possible back into the main build. Leaving your merge to the end of a project is asking for trouble.

Automate your builds 

There are a number of key reasons why build automation should save you time in the long run.

  1. Leaving deployment testing to the end of a project increases the risk of it going wrong.
  2. Having regular builds means you test your deployment every night, rather than only a couple of times through the project. This means that deployment issues are flagged early in the dev cycle.
  3. You can easily ensure that your dev teams are always working with the most up to date version of the build.
  4. You can implement Test Driven Development techniques to aid integration testing and regression testing.

These advantages may not pan out so much with the initial build of a site, but as you move on to maintain and enhance that site you really need to make sure you start taking advantage of build automation. 

Build masters

How is this going to affect your teams? What you need to do is create a new position within your team – a build master – to manage the deployment and builds. That person needs to know where all the branches are, what the deployment schedule is and every detail about the solution. This is a key, pivotal role in your project team, and should be senior. This is probably not something that can be fitted in around other work, and could well be a full-time job.

Really big projects which themselves contain branches may require additional build masters.

Now that the scene is set, in the next few posts I will walk through some examples of how to use MSBuild tasks, together with TFS, to automate your builds. 

Using MSBuild to manage deployments in SharePoint – Part 1

March 10th, 2008 by benrobb

This series of blog posts is taken from a session I delivered at the SharePoint Conference 2008, in March.

Part 1: Why would you want to have build automation in SharePoint Solutions?

For simple solutions, where you have little custom code, and a single, linear set of projects, you can get away with not formalising your deployment process. However, if you have a large intranet or public facing site, you will probably be faced with a solution which has:

  • Frequent changes to functionality.
  • Multiple teams working on different streams
  • Cross-project dependencies
  • Different release schedules
  • Tight control over the production environment.

A typical site will probably start off being simple: a foundation project will determine the network topology, it will do the initial build and configuration of the servers, and it will have the initial development of the core components of the site – a master page, some custom controls and some page layouts and CSS. Typically this will have a single team of developers working towards a "big bang" launch. All being well, they will launch the site and have a few beers to celebrate. If they are contractors, or working for an agency, they may well go off to other jobs.

But then what? 

On a large scale website, that's never the end of the story. Large websites will never stay static: either in their content or their functionality. There will be ongoing maintainence and bug fixing. There will be change requests to amend the existing functionality. There will also be brand new requirements to provide the site with completely new features.

Programme managers would, in an ideal world, see that these projects are carefully managed into phases, and ensure that each code drop goes through the same process as the first phase. Developers would work together to build their bits of functionality, and then at the end, have integration and testing tasks to form a release schedule. But back in the real world, how does this actually pan out? When bugs are found post launch, your team may well have SLAs to meet. Regardless, you need to fix the problem as quickly as possible. That means that you need to deploy your fixes to your site while your other projects are still running, and often while those projects have breaking changes to your site. This means that you need to start branching your code base – potentially in your configuration management, your file system / .NET assets and your SharePoint artefacts.

Branching

Branching is often feared by developers, but there are some key lessons which should be learnt when determining your branching strategy. This primer on branching and merging, from MSDN explains how different strategies can affect your team, but the bottom line is that you need to think carefully about your branching strategy. If your merges are taking too long (i.e. you have developers sitting round doing nothing for a week), then you are not being granular enough. You need to branch only when you really need to, and merge as soon as possible back into the main build. Leaving your merge to the end of a project is asking for trouble.

Automate your builds 

There are a number of key reasons why build automation should save you time in the long run.

  1. Leaving deployment testing to the end of a project increases the risk of it going wrong.
  2. Having regular builds means you test your deployment every night, rather than only a couple of times through the project. This means that deployment issues are flagged early in the dev cycle.
  3. You can easily ensure that your dev teams are always working with the most up to date version of the build.
  4. You can implement Test Driven Development techniques to aid integration testing and regression testing.

These advantages may not pan out so much with the initial build of a site, but as you move on to maintain and enhance that site you really need to make sure you start taking advantage of build automation. 

Build masters

How is this going to affect your teams? What you need to do is create a new position within your team – a build master – to manage the deployment and builds. That person needs to know where all the branches are, what the deployment schedule is and every detail about the solution. This is a key, pivotal role in your project team, and should be senior. This is probably not something that can be fitted in around other work, and could well be a full-time job.

Really big projects which themselves contain branches may require additional build masters.

Now that the scene is set, in the next few posts I will walk through some examples of how to use MSBuild tasks, together with TFS, to automate your builds.