TFS Build 2015 (vNext) – Custom Tasks

This post is part of a series of articles on the new TFS Build vNext System.

So I’ve talked before about how the new cross platform build system in TFS 2015 and Visual Studio Online is fully extensible and allows you to create your own custom build tasks but it isn’t immediately obvious how to do it.

I’ll would like to walk through an end to end example creating a custom vnext build task to run a SQL script as part of the build

The tasks for build vNext are open source and Microsoft is accepting contributions so if you write a really nice and useful task it could be included in every Visual Studio Online account.

The first port of call is the GitHub page that hosts the code for the standard Microsoft Build Tasks, fork and clone this and you will find the code for all the existing build tasks. I created a new folder, SqlScript, for my new sql script build task, and it is in here I’ll add the code to create the task.

Anatomy of a vNext Build Task


Each task is defined by a .json file. This file defines the task meta data such as name, version etc and also defines the task GUI that is displayed in the web portal when editing builds. Lets take a look at the task.json file for the sql script task.

    "id": "A1849E9F-AA94-40A9-A392-5414F3F739B0",
    "name": "SqlScript",
    "friendlyName": "Run sql script",
    "description": "Run a sql script against a sql server, uses sql auth or integrated security",
    "helpMarkDown": "[More Information](",
    "category": "Utility",
    "author": "Shining Dragon Software",
    "version": {
        "Major": 0,
        "Minor": 1,
        "Patch": 2
    "demands" : [
    "minimumAgentVersion": "1.83.0",
  "inputs": [
      "name": "SqlScript",
      "type": "filePath",
      "label": "Sql Script",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "Sql script to execute"
      "name": "SqlServer",
      "type": "string",
      "label": "Sql Server",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "The Sql Server to execute the script against"
      "name": "DatabaseName",
      "type": "string",
      "label": "DatabaseName",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "The name of the database to execute the script against"
        "name": "DatabaseUser",
        "type": "string",
        "label": "Sql User",
        "defaultValue": "",
        "required": false,
        "helpMarkDown": "Sql Auth User, if blank Integrated security will be used"
      "name": "DatabasePassword",
      "type": "string",
      "label": "Password",
      "defaultValue": "",
      "required": false,
      "helpMarkDown": "Sql Auth Password, if blank Integrated security will be used"
  "instanceNameFormat": "Execute Sql Script against $(DatabaseName)",
    "execution": {
        "Node": {
            "target": "sqlscript.js",
            "argumentFormat": ""
        "PowerShell": {
            "target": "$(currentDirectory)\\sqlscript.ps1",
            "argumentFormat": "",
            "workingDirectory": "$(currentDirectory)"

The first section defines some simple meta data such as a unique guid identifier, name, version, author etc.

Then we start to define the GUI with the ‘inputs’ section, we can also organise inputs into custom group sections using the ‘groups’ section. It’s quite simple and the best way to get an idea is to just look at other tasks and see how they have been defined.

Finally we have the execution section that defines what scripts will be ran to execute our task. Note here, the scripts come in two flavours, a powershell script and a node based javascript file. The powershell script will run when the task is run on a Windows build agent, the javascript file will run when the task is run on a cross platform build agent (including a cross platform build agent running on a Windows machine). If you want your build task to be truly cross platform then you’ll have to implement it in both powershell and javascript.

Powershell script

Here’s my implementation of the sql script task in powershell. Note how the script parameters I use match the inputs I defined in the task.json file.

    [String] [Parameter(Mandatory = $true)] $SqlScript,
    [String] [Parameter(Mandatory = $true)] $SqlServer,
    [String] [Parameter(Mandatory = $true)] $DatabaseName,
    [string] $DatabaseUser,
    [string] $DatabasePassword

Write-Verbose "Entering script $MyInvocation.MyCommand.Name"
Write-Verbose "Parameter Values"
foreach($key in $PSBoundParameters.Keys)
	if($key -ne 'DatabasePassword')
		Write-Verbose ($key + ' = ' + $PSBoundParameters[$key])

Write-Verbose "Importing sqlps module"
Import-Module sqlps

if([string]::IsNullOrEmpty($DatabaseUser) -or [string]::IsNullOrEmpty($DatabasePassword))
	Write-Verbose "Execute script using sql authentication"
	Invoke-SQLCMD -ServerInstance $SqlServer  -Database $DatabaseName -InputFile $SqlScript -Username $DatabaseUser  -Password $DatabasePassword
	Write-Verbose "Execute script using integrated security"
	Invoke-SQLCMD -ServerInstance $SqlServer  -Database $DatabaseName -InputFile $SqlScript

Building your Task

The build process for Build vNext tasks uses Gulp. So if you haven’t already, set up node on your machine. Then run

npm install

from the root of the project directory, this will pull down all the node and gulp dependencies for the web. Finally, simply run


from a command line at the root of the project directory. This will create a _build directory where all the tasks will be copied to ready for uploading into TFS/VSO. Note it also creates a task.loc.json file and country specific resource files that will be used for language localisation, if you need to support that.

Uploading your Task

It’s not obvious how to do this, but looking at the project I can see a TaskUploader folder and script (written in typescript) and further looking in the gulpfile.js I can see a build task called simply ‘uploader’. So I try running

gulp uploader

This will build the typescript file and copy the result to the _build\TaskUploader folder. Looking the resultant javascript file I can see it is a node file and I can also see the lines

var accountUrl = process.argv[2];
var taskFolder = process.argv[3];

Which leads me to try the following to upload my task into Visual Studio Online

node _build\TaskUploader\taskUploader.js _build/Tasks/MyTask

Doing so, you should get prompted for your alternate credentials and finally you will see your new task available from the web portal.


A few things to note:

  • Tasks are server wide (shared across all projects and project collections)
  • When you update an existing task be sure to increase the version number. Failing to do so means your agents will not downland the latest version
  • Builds will always use the latest version of your task. If you need to support multiple versions of your task you will need to upload several versions of it under different names and guids.

Sql Script Task in Action

We can configure the task as follows. Instead of leaving the database password in plain text in the task, I create an encrypted variable for the password and reference the variable in the task.



One thought on “TFS Build 2015 (vNext) – Custom Tasks”

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s