Building Custom Visual Studio Team Service Tasks with VSTS DevOps Task SDK

Building Custom Visual Studio Team Service Tasks with VSTS DevOps Task SDK

In this article, we will be building a simple Visual Studio Team Services build & deployment task using VSTS DevOps Task SDK and upload it to our Visual Studio Team Services account in order to use it in our build and release pipelines.

To create a custom Visual Studio Team Services task, you need some tools and libraries installed first. You’ll need Node.js and npm installed on your development machine. Then you need to have tfx-cli npm package, which is a Cross Platform CLI for Microsoft Team Foundation Server and Visual Studio Team Services. This CLI can be used to interact with TFS and VSTS and lets you create, upload, delete build tasks, package, publish and manage VSTS/TFS build tasks and interact with work items etc. We also need vsts-task-lib package installed locally in the project. It’s the VSTS DevOps Task SDK that we will use to create the custom task. It’s a set of libraries that support developing tasks using Node.js and Powershell. You can use TypeScript to create node.js tasks which will be cross-platform. But if you use PowerShell those tasks will only run on Windows build agents.

When we create a custom task, we have 4 files that are important;


package.json includes the reference to vsts-task-lib package and any other 3rd party npm packages you need to use in the custom task.

TypeScript/JavaScript Task Code File.

This typescript file includes the logic that you need for your custom task.


This is the icon file used as the logo alongside the custom task in VSTS or TFS. This file should be a 32x32 png image file.


This is the file that describes the custom task. Also, this file is used to render the configuration options on the screen.


Creating the Custom Task

Create a folder for the task and navigate into the folder using a command line. Then we need to initialize npm.

npm init

Then we need to install VSTS DevOps Task SDK locally into the project. So, using the command line execute the following command.

npm install vsts-task-lib --save

Since we are using TypeScript we need to install TypeScript definitions for node.js.

npm install @types/node --save-dev

Then we need to initialize TypeScript for the project. Type in the following command into the console.

tsc --init

I’m going to use Visual Studio Code as the code editor. Open up the folder we created using VS Code and let’s do some changes to the tsconfig.json file. We will be using some ES2015 features in the task so we need to set the ECMAScript target version to ES2015 and add es2015 to library files to be included in the TypeScript compilation.

So the tsconfig.json file should look like this.

  "compilerOptions": {
    "target": "ES2015",
    "module": "commonjs",
    "strict": true

The custom task we are going to create has 2 configuration inputs (For simplicity, configuration values are First Name and Last Name). The first name is required. Also, there are 2 configuration groups with the name Group 1 and Group 2. We need to have the task.json file created to describe this task and for VSTS build engine to render the configuration options in the UI. Let’s create the task.json file for the custom task. The following is the task.json we need to create.

  "$schema": "",
  "id": "9175fd51-4f41-459e-8c8d-b01e2b85bdcb",
  "name": "TheKvkBlogTask",
  "friendlyName": "The KVK Blog Task",
  "description": "Simple build/deployment task for Visual Studio Team Services",
  "helpMarkDown": "[More Information](",
  "category": "Utility",
  "visibility": [
  "runsOn": [
  "author": "Kasun Kodagoda",
  "version": {
    "Major": 1,
    "Minor": 0,
    "Patch": 0
  "demands": [
  "minimumAgentVersion": "1.92.0",
  "groups": [
      "name": "group1",
      "displayName": "Group 1",
      "isExpanded": true
      "name": "group2",
      "displayName": "Group 2",
      "isExpanded": false
  "inputs": [
      "name": "FirstName",
      "type": "string",
      "label": "First Name",
      "required": true,
      "defaultValue": "",
      "groupName": "group1",
      "helpMarkDown": "Add your *First Name* here. (required)"
      "name": "LastName",
      "type": "string",
      "label": "Last Name",
      "required": false,
      "defaultValue": "",
      "groupName": "group2",
      "helpMarkDown": "Add your *Last Name* here."
  "instanceNameFormat": "The KVK Blog Custom Task",
  "execution": {
    "Node": {
      "target": "the-kvk-blog-task.js",
      "argumentFormat": ""
  "messages": { }

In this task.json id is a GUID that unique. The name is the name of the custom task and should not contain any spaces. The friendlyName is the name that will be displayed in the Task list. The property description contains the description for the task that will appear in the task list. helpMarkdown can contain markdown syntax and will be displayed in the tool tip that appears when hovering over the information icon when the task is added to the build or release definition. The category is the task category where the custom task will appear on when uploaded or installed in Visual Studio Team Services. The visibility property lists where the task can be added to, in our case we can add the task to Builds and Releases.

The runsOn property dictated where the custom task can run on. Next, we have the author, version which needs to be updated every time you do some changes and upload the task to Visual Studio Team Services. The demands property can be used to list that build agent should have in order to run the custom task.

Next, we have 2 important sections. The groups contain the groups that the configurations options will be logically grouped in to. A group contains 3 mandatory properties, a name to reference the group when you need to add a configuration option into a group, the displayName is the name that we see rendered on screen. isExpanded property dictated if the group is expanded or not when initially shown.

The inputs section describes the configuration options we need to provide, each configuration option has some properties as well. The name is what we use to reference the configuration options from the task code. The type defines the type of the configuration value. The label is the display name of the configuration option. The required property makes the configuration option required and if the configuration value is not provided it will not allow you to continue. We can use the defaultValue to include a default value for the configuration option. The groupName can be used to group the configuration input related sections. And also, the helpMarkDown can be used to add a tool tip that supports markdown syntax.

The instanceNameFormat will show how the build step will be displayed in the task list and execution section describe the execution options.

In addition to this, there are more options available for the task.json file depending on how you want to create the task. You can see the full list of options using the following documentation link.

Next, we need to add the logic to use the configuration options to perform the actions we need to be doing with the task. Add a typescript file with the name the-kvk-blog-task.ts and add the following code to the file.

import task = require('vsts-task-lib');
import path = require('path');

task.setResourcePath(path.join(__dirname, 'task.json'));

async function run() {
  let firstName: string = task.getInput('FirstName', true);
  let lastName: string = task.getInput('LastName');

  // Logic for the custom task
  console.log(`Hello, ${firstName} ${lastName}`);'Custom Task Completed.');


In this code segment, we import the vsts-task-lib into the custom task along with path module. Then we need to set the resource path, to point to the task.json file. Then we have the async function which inside we write the functionality for the task.

Next, we get the configuration values using getInput() method. We need to pass in the name of the input (which we described in the task.json file). This method also has an optional Boolean parameter to denote that the input is required or not. If the required configuration option is not provided the task will throw an exception. There are more helper methods available in the vsts-task-lib. You can read the documentation link for more information. Finally, we have the logic for the task which in this case we are concatenating the input strings and printing it out to the output console.

Next, we need to add the logo for the task. I’ll add the icon.png which is a 32x32 pixel png image. Add it to the same location where the task.json.

Finally, we need to compile the typescript file into JavaScript and upload the task into Visual Studio Team Services. Execute the following command in the console to compile the typescript file.

tsc the-kvk-blog-task.ts --lib es2015

Uploading the Custom Task to Visual Studio Team Services

To upload the custom task, we created to Visual Studio Team Services, we need to install tfx-cli globally and use it. To install tfx-cli globally execute the following command.

npm install -g tfx-cli

After installing the tfx-cli you need to login to your Visual Studio Team Services account using the cli. To do that you need 2 things, the Service URL for Visual Studio Team Services and a Personal Access Token (PAT). To get the Personal Access Token, navigate to your Visual Studio Team Services account and click on your profile avatar on the top right corner.


In the personal access token click on add button to create the new access token.


Add a name to the PAT and select all scopes and click on Create Token to create the personal access token.


One created copy the access token and go to the command line to login to Visual Studio Team Services.


Type in the following command to log in to Visual Studio Team Services and enter the Service URL and the Personal Access Token into the command line.

tfx login --authType pat


Now it’s time to upload the task to Visual Studio Team Services, use the following command to upload the custom task.

tfx build tasks upload --task-path .\the-kvk-blog-task


Now the task is uploaded to your Visual Studio Team Services account. Now we can test it out using it in a build definition.


Search for the task in the available task list, and you will find the task. The display name is there with the description. Add the task to the build definition.


After adding the task, you will see that the instanceNameFormat we added is set as the Display Name and we also see the 2 groups we added and the 2 configuration values, First Name and Last Name. The First name is required. Add the values for First Name and Last Name, then queue a new build.


After the build is complete you can see the custom task has concatenated the values we entered and printed it out to the console.

What’s Next?

Let’s talk about what we can do after we create the task. At the moment, this task is only available privately for your Visual Studio Team Services account. No one else can use it. If you want to use it on Some Other Visual Studio Team Services account, you have to re-upload it after login into the second VSTS account.

But, you can publish the task to Visual Studio Marketplace if you want to share it with other users. On Visual Studio Marketplace, you can share it privately with selected Visual Studio Team Services accounts or you can make it available to the public so all Visual Studio Team Services and TFS users can use it by installing it. But to make it publicly available, you need to be a verified Visual Studio Marketplace Publisher. Also, you can associate a payment model with your custom tasks. You can easily make it available for free for other users or you can decide to make the custom task available only if you pay a certain amount. You can also associate trial periods for yours to try the task out, before buying.


In this article, we talked about how to use VSTS DevOps Task SDK to create custom Visual Studio Team Services tasks using Node.js and TypeScript and finally upload it to the Visual Studio Team Services account to be used in your custom build and deployment tasks. You can find the sample code in the GitHub Repository. I hope you enjoyed this article and I’ll see you in the next one.

You Might Also Like