Last Updated: 9 September 2022

Cross Platform Custom Azure DevOps Build Task Using Typescript

You may have alread seen our article on creating custom build tasks using Powershell and a custom .Net Core / .Net 6 CLI tool written in C#. It is also possible to create a true cross-platform custom build task that uses node.js and Typescript instead of the Powershell / C# combination described in that article.

It is possible to package and publish your custom tasks to make them available for installation into any Azure DevOps organization, or to just the organizations you choose. This is appropriate if, for example, you intend to allow a more self-service approach to your DevOps teams, or if you want to share your tasks publicly. This guide assumes you already have node.js installed, and have the TFX extension. See our Powershell & C# custom task guide for how to do this.

Looking to create a custom build task using Powershell and C# instead? Check out our guide here!

Step 1 - Initialize Your Task

Create a folder called 'MyCustomTask', then from a command line within that directory, initialize your task:

        npm init --yes
        npm install @types/node --save-dev
        npm install @types/q --save-dev
        npm install mocha --save-dev -g
        npm install sync-request --save-dev
        npm install @types/mocha --save-dev
        npm install azure-pipelines-task-lib --save
        npm install typescript@4.0.2 -g --save-dev
        echo node_modules > .gitignore
        tsc --init --target es6

This adds some package dependencies, including a utility task library provided by Microsoft. This library provides functions to interact with Azure DevOps and provides logging, ways to with user parameters, and the ability to indicate success/failure. It also provides functions to run external tools and commands. If you are running these commands from Powershell and get an error from the 'tsc' comnmand around execution policies, run the following and retry:

        Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

Step 2 - Task Definition

Create a file called task.json, and save it into your MyCustomTask folder. This is broadly the same as the one we previously discussed in the Powershell / C# example, but specifies a node.js target instead:

            "$schema": "",
            "id": "YOUR-GENERATED-GUID-HERE",
            "name": "MyCustomTask",
            "friendlyName": "Demo Task",
            "description": "This is a demo task.",
	        "helpMarkDown": "For more information about this task, visit",
            "category": "Azure Pipelines",
            "author": "Joe Bloggs",
            "version": {
                "Major": 1,
                "Minor": 0,
                "Patch": 0
            "instanceNameFormat": "MyCustomTask",
            "inputs": [
                    "name": "someString",
                    "type": "string",
                    "label": "Some string",
                    "required": true,
                    "helpMarkDown": "This is an example string input"
                    "name": "anotherString",
                    "type": "string",
                    "label": "Another string",
                    "required": false,
                    "helpMarkDown": "This is another example",
                    "defaultValue": "Hello World"
            "execution": {
                "Node10": {
                    "target": "index.js"

Step 3 - Create Your Script

Create a script using the scaffolding template below, this will be where the bulk of your logic will go:

        import tl = require('azure-pipelines-task-lib/task');
        import tr = require('azure-pipelines-task-lib/toolrunner');

        async function run() {
            try {
                console.log('##[warning] This is how to log a warning');

                const someString: string | undefined = tl.getInput('someString', true);
                console.log('Some String: ', someString);

                const anotherString: string | undefined = tl.getInput('anotherString', false);
                console.log('Another String: ', anotherString);

                console.log('##[error] This is how to log an error');
            catch (err) {
                tl.setResult(tl.TaskResult.Failed, err.message);


Step 4 - Compile To Javascript

Compile your .ts file into .js using the following command (run from inside the folder you created above). If successful, you will now also have an index.js file present:


Step 5 - Upload to Azure DevOps

Use the following commands to upload your new task to Azure DevOps:

        tfx login --authType pat
        tfx build tasks upload --task-path C:\temp\MyCustomTask\Task

For further information on installing node.js, the TFX command line tool, and setting up a Personal Access Token to authenticate with Azure DevOps, see our original article on custom build tasks with Powershell and C#.