Using TFS2010 Team Build to Run a Custom Deployment Script (Without Build Customisation)
I've seen several posts about configuring TFS2010 Team Build to deploy applications to test environments, and they tend to differ based on whether you are deploying via MSI, xcopy, WebDeploy, etc.
This is all well and good, but not exactly what I was searching for. In a lot of situations, I already have my automated deployment defined in a single script (whether that be a batch file, PowerShell script, or whatever), and I'd like to just to leverage that script as is - but expose it via a Team Build Definition so that:
Anyone in the team with access to the Team Project can kick off the deployment just by queuing a build - whether from Team Explorer, Team Web Access or other.
I can still lock down access to the target environment so that only certain accounts can perform the deployment (rather than open up file system or registry access on the servers to everyone in the team).
It turns out, this is pretty straightforward. Here's what I did:
1. Check the deployment script in to Version Control
Other than getting the script somewhere that the build server can find it, this has a lot of other advantages too. For example:
The deployment script gets a full audit history of every change and every user who changed it (and if your Version Control is configured to require Work Items associated with Changesets, then you also get an audit of the task each change was related to).
There is a single source of truth for the deployment script: one source copy that all other instances are copied/derived from. This is much better than having a copy of the script floating around on each server you want to deploy to, because often people will make a change in one copy, but that doesn't get proliferated across all others.
The script may be stored with (and hence versioned with) the source code for the software that it deploys. Alternatively, it may instead be stored in a separate source control area where it can be managed by the people who know most about the environments being deployed to (e.g. Ops). Or ideally, it is split into a) the steps to deploy, with all environment specifics parameterised and b) a parent script that invokes the other with the environment details. That way one script is kept with the source code and managed by the Devs and the other(s) is managed by Ops.
2. Create a TFSBuild.proj file to invoke the script and check it in
Create an MSBuild (XML) file called TFSBuild.proj that does nothing more than invokes the script. For example:
<?xml version="1.0" encoding="utf-8"?> <!-- DO NOT EDIT the project element - the ToolsVersion specified here does not prevent the solutions and projects in the SolutionToBuild item group from targeting other versions of the .NET framework. --> <Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> <Target Name="EndToEndIteration"> <Exec Command="DeployScript.bat" /> </Target> </Project>
Just replace the Command attribute value with a command that invokes your script. The Exec task in MSBuild just runs the OS cmd.exe, so you can effectively run any command line here. In the example above, the DeployScript.bat batch file is located relative to the TFSBuild.proj file and executed.
Another example below shows how you might kick off a PowerShell deployment script:
<?xml version="1.0" encoding="utf-8"?> <!-- DO NOT EDIT the project element - the ToolsVersion specified here does not prevent the solutions and projects in the SolutionToBuild item group from targeting other versions of the .NET framework. --> <Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> <Target Name="EndToEndIteration"> <Exec Command="%systemroot%\System32\WindowsPowerShell\v1.0\PowerShell.exe .\DeployScript.ps1" /> </Target> </Project>
Once you have created your TFSBuild.proj file, check it in to Version Control alongside your deployment script.
3. Create a build definition that uses the UpgradeTemplate.xaml build process template
Create a new Build Definition in your Team Project and on the Process tab, change the build process template to "Upgrade Template (UpgradeTemplate.xaml)".
This template is shipped out-of-the-box in TFS2010 and is intended for teams upgrading from TFS2005/2008 to bring their MSBuild-based definitions along without having to rewrite them in Windows Workflow. However, since all it effectively does is call an MSBuild file called TFSBuild.proj, and since MSBuild and pretty much do anything (including just call another script), we can use this build process template as is to (indirectly) invoke our custom deployment script.
Note that you could alternatively create a custom build process template that just uses the InvokeProcess activity to launch your deployment script. However, I prefer the UpgradeTemplate approach because a) you can just use out-of-the-box artifacts without customisation and b) for me, the designer experience for Windows Workflow is too heavy handed for maintaining a simple script that does just one thing - "launch this" - for me, a small MSBuild/XML file seems more lightweight.
So once you've chosen UpgradeTemplate for your build definition, configure its Configuration Folder Path to point to the Version Control location of your TFSBuild.proj file.
A couple of important things to take note:
Your deployment script will run under the account of the Team Build service. This means you will either need to grant permissions to your target environment so that this account can deploy, or you can invoke your script as a different user (by editing the TFSBuild.proj file and using something like runas.exe). Either way, running under a specific account is better than having to open up administration of your environment to every member of the team.
The deployment script outputs will not be reported in the build log. However, they will be available in a BuildLog.txt file in your build drop folder. If you want your deployment script to fail the build (which you do), make sure you craft your deployment script to return a non-zero exit code to the OS on failure. This is recommended practice for script development anyway. When the build fails, you still won't get the result in the build log, but you will get a pretty link to the "MSBuild Log File", which will open up the BuildLog.txt file.
The next steps for me are to build a basic deployment pipeline capability over the top of this so that the deployment "builds" can be automatically triggered when a compilation/Continuous Integration build succeeds and/or when a build quality is changed (e.g. Passed Tests). I'm looking at the eventing built into TFS for this and might leverage TFSDeployer or something custom that performs a similar purpose.