You’re using TFS 2013 to build your application continuously, or on a schedule or both giving you feedback on how good your team are doing at integrating their work with one another. Using Release Management (RM) you can deploy your application into your Dev environment, then UAT and finally Production all at the click of a button. This post describes how you can trigger that build from a TFS 2013 build.
How could you use this and why? A product team wants to deploy to a Dev environment on every commit to the central repository. This environment is used by the developers to test the integration of all of the different components of a product before releasing into UAT. This can be described as Continuous Deployment. If the product has many components to it you may not want to deploy on every commit instead you could deploy daily as a result of a scheduled build e.g. a nightly build.
You can do this by triggering a release from a TFS build by modifying the build process template, used by your build definition, of which there are several:
Installing the new build process template
Install RM and new versions of the above templates are installed on your server for you to use. On the server where you have installed RM Server go to C:\Program Files (x86)\ Microsoft Visual Studio 12.0\ReleaseManagement\bin\ where you’ll find the following files:
- ReleaseDefaultTemplate.xaml – TFS 2010
- ReleaseDefaultTemplate.11.1.xaml – TFS 2012
- ReleaseGitTemplate.12.xaml – TFS 2013
- ReleaseTfvcTemplate.12.xaml – TFS 2013
- ReleaseUpgradeTemplate.xaml – TFS 2010
You can also find these files on a machine where you have installed the RM client application, the location is C:\Program Files (x86)\Microsoft Visual Studio 12.0\Release Management\Client\bin (just be aware that some have reported the templates in the RM Client directory might not work). The RM client can be downloaded from your MSDN subscription or from here which is a limited trial version. Now add these files to source control by creating a folder called BuildProcessTemplates at the root of your Team Project repository and drop these files into there and commit (or checkin) so the path looks something like (for TFSVS) $/MyTeamProject/BuildProcessTemplates/ReleaseTfvcTemplate.12.xaml.
Configure your build
Create your new build and select one of the new templates:
You’ll want to take note of the following RM-specific properties:
- Configuration to Release – String array, normally set to Any CPU|Release
- Release Build – Boolean value (true or false), true if you want to trigger the release
- Release Target Stage – String value, if you set this to the name of one of your RM stages e.g. DEV, PROD if you want to deploy into that enviornment only otheriwse leave blank
The new release process templates trigger RM by making a call to ReleaseManagementBuild.exe, which you’ll get when you install the RM client. This means wherever you have a TFS Build agent installed you will need to have the RM client also installed on that machine.
Trigger a new build and confirm that your release was created and then triggered ReleaseManagementBuild.exe. Using the RM client you should see a new release created.
Modifying an existing build process template
You may have modified one of the default templates or created your own version of it in which case you will want to add the RM capabilities to it. There is a post over at MSDN on the Visual Studio ALM blog that details exactly how to do this.
We’re in a little bit of transition phase for these two products as the integration gets tighter and tighter. The story began with the acquisition of Release Management from InCycle in 2013 and will soon become one product with little trace of the WPF client app which is used to configure and manage deployment workflows.
TFS build has the continuous integration story covered. Now you want to deploy your application you need to switch over to Release Management and trigger a release. Triggering a release was a manual action in earlier versions of the product but it can now be triggered automatically opening up continuous deployment strategies to you.
The information below outlines a series of how-to articles I’ll be posting in the next month of stuff I’ve learnt whilst integrating the two products. Not everyone has the luxury of upgrading to the latest versions of Visual Studio or TFS so I’m hoping this will be useful for someone looking for a consolidated source on this stuff.
Using a subset of the complete options Jakob Ehn wrote about in his post Triggering Releases in Visual Studio Release Management I’m focusing on the on-premise options available to you.
|# || |
|Post (links will added as I publish posts)|
|1 ||TFS 2013 Build ||RM 2013 Update 2 ||Agent-Based ||ReleaseManagementBuild.exe |
ReleaseBuildTemplate for Xaml build
|Trigger an Agent Release from TFS 2013 Build|
|2 ||TFS 2013 Build ||RM 2013 Update 2 ||Agent-Based ||ReleaseManagementBuild.exe |
Post build PowerShell script for Xaml build
|Trigger an Agent Release from TFS 2013 Build|
|3 ||TFS 2013 Build ||RM 2013 Update 3 ||vNext ||Rest API |
Post build PowerShell script for Xaml build
|Trigger a vNext Release from TFS 2013 Build|
|4 ||TFS 2015 Build ||RM 2013 Update 2 ||Agent-Based ||ReleaseManagementBuild.exe |
|Trigger an Agent Release from TFS 2015 Build|
|5 ||TFS 2015 Build ||RM 2013 Update 3 ||vNext ||Rest API |
|Trigger a vNext Release from TFS 2015 Build|
Some of the terminology can be confusing so here is an explanation of the top level functions in Release Management.
- Agent Release – Deployment template using pre-defined functional building blocks and using Windows Workflow to manage the workflow. Requires a deployment agent to be installed on the target server.
- vNext Release – Deployment template using PowerShell and using Windows Workflow to manage the workflow. Does not require a deployment agent however will require an account with local admin permissions on the target server.
- PowerShell – Scripts that will have been authored by yourself for deploying components such as a xcopy job, web deploy or dacpac package.
- PowerShell DSC – Scripts utilising Desired State Configuration.
The future – PowerShell all the things!
The Release Management Service, now available in public preview in Visual Studio Team Services, will replace the current WPF client app and related architecture. RMS has been basically re-built and works in a similar way to the new build system in TFS 2015. A summary of the new features can be found here and this is what it looks like.
This means Agent based deployments will be a thing of the past as the way forward is paved with PowerShell scripts. If you’re currently using Agent based deployments then the best thing you can do is to start using vNext templates and paths to start deploying using PowerShell. These PowerShell scripts can then be re-used in Release Management Service when it becomes available to on-premise. Interestingly there is a tool, created by the ALM Rangers, which will attempt to migrate your Agent based deployment artifacts and over to vNext templates.
In a previous post I presented a powershell script to attach a timestamp suffix to files or folders backed up prior to a deployment. At the point of writing that post I wasn’t aware of a collection of runtime variables that are available in your powershell script.
Below is alternate script to the last one I presented. The suffix in this script is not using the current date and time but the $ReleaseId variable which is a number. Each time a new released is created and executed this variable will contain a new value but remember a release maybe re-tried in which case this value will not change. You could also use the $BuildNumber variable (alter line 7) which is the unique Id TFS assigns to the build and is a number.
Deploying a vNext release means you can take advantage of global variables in your powershell script. A complete description of what is available and how you can configure your own is available here.
Here is a simple script which I found useful when you simply want to know\confirm the values being pushed to your script. The end of the script will also dump out all variables (undocumented ones) to the logs too.
TLDR: Download and use this powershell script in your deployments to append a timestamp to a file or folder.
Typically you’ll backup a database prior to upgrading it through Release Management or you’ll copy a website folder to a backups folder as part of your rollback strategy. Both tasks are easily achieved using the Backup SQL Database and Copy File or Folder tasks.
You’ll most likely want to version your backup copies using a timestamp strategy. This would involve passing some sort of token which can be evaluated at runtime and your input value modified to accommodate this token and this is where we find RM wanting. For example you can’t use a token such as __BuildID__ in the DestinationFileFolder (Copy File or Folder task) like so D:\Sites\InvoiceApp\Backups\InvoiceWebapp__BuildID__. The input fields simply won’t recognise any token so our only option is to follow up the task with a new one that will rename the newly created file or folder.
PowerShell to the rescue
Here is a powershell script which can be can be used to rename either a file or a folder. By default it will append a timestamp in the format yyyyMMddhhmmss but you can override that to match your requirements.
To begin to use this in RM you will need to create a Tool like this:
And then create a couple of Actions like this:
And finally use it in your template like this:
An approach that didn’t work
For completeness I’m including an approach that didn’t work me and I never got to the bottom of why it never worked.
From the command line I can rename a file or folder using xcopy whilst at the same time I can use %date% and %time% to create a timestamp. A typical command would look like this:
xcopy D:\Sites\AppA\Current D:\Sites\AppA\Backups\AppA_%date:~-4,4%%date:~-7,2%%date:~-10,2%%time:~-11,2%%time:~-8,2%%time:~-5,2%%time:~-2,2% /I /E
You’d think then if I used this command in the Run Command Line task that we have the desired behaviour well RM doesn’t like this command. An error occurred from the task which looked like it was trimming down the command. I gave up after a few hours and pursued the solution above.