6 min read

Building Azure DevOps Extension on Azure DevOps - Design

Justin Yoo

Azure DevOps provides an end-to-end service that takes care of the entire ALM (Application Lifecycle Management) process. DevOps itself takes a big part of this ALM journey. While the service naming indicates that only DevOps related features are offered, it comprises all the ALM cycle from request analysis to delivery, via development and test. There's no doubt that it's such a powerful tool. In addition to this, Azure DevOps offers a strong extension model so that anyone can publish extensions to Visual Studio Marketplace. If your Azure DevOps instance doesn't have a necessary extension that you're looking for, you can search it on the marketplace and install it. If you can't find it, then publish it by yourself publicly or privately. Throughout this series of posts, I'm going to discuss how to build an Azure DevOps extension from various perspectives.

Table of Contents

This series consists of those six posts:

  1. Building Azure DevOps Extension - Design
  2. Building Azure DevOps Extension - Implementation
  3. Building Azure DevOps Extension - Publisher Registration
  4. Building Azure DevOps Extension - Manual Publish
  5. Building Azure DevOps Extension - Automated Publish 1
  6. Building Azure DevOps Extension - Automated Publish 2

Use Case Scenario

I'm interested in using a static website generator, called Hugo, to publish a website. There's an extension already published in the marketplace so that I'm able to install it for my Azure DevOps organisation. To publish this static website, I wanted to use Netlify. However, it doesn't yet exist, unfortunately. Therefore, I'm going to build an extension for Netlify and at the end of this series, you will be able to write an extension like what I did.

Actually, this Netlify extension has already been published, which you can use it straight away. This series of posts is a sort of reflection that I fell into situations – some are from the official documents but the others are not, but very important to know during the development. The source code of this extension can be found at this GitHub repository.

Designing Azure DevOps Extension

There are two different SDKs for Azure DevOps extension development. One is for Web Extensions SDK, and the other i for Pipelines Task SDK. As a rule of thumb, the former is generally used for Azure Boards and Azure Repos related extension development, and the latter is generally used for Azure Pipelines related extension development. I'm going to use the latter because my extension will only be used for Azure Pipelines. You can start from this document to understand the basic approach.

To use Netlify CLI to publish your static websites, you might need two tasks – one is to install the netlify-cli from npm, and the other is to deploy your website to Netlify through netlify-cli installed. So, I'm going to create two tasks named install and deploy.

Scaffolding Folder Structure

First of all, let's scaffold the folder structure. I've set it up like the image below – there are images, src, test folders, and under the src folder, I created install and deploy folder. These very two folders are the ones I'm working on. I'm going to touch the other folders later in this series.

Designing install Task

The netlify-cli is installed into the pipeline through this task. The first thing I need to do is to create a task.json file in the install folder and put the JSON content in the file:

https://gist.github.com/justinyoo/baf3ecf3240df3037be0f84fe43b5425?file=install-task.json

Here are attributes you need to understand at the first place:

  • id: A unique GUID value. This website can easily get the GUID value.
  • name: Name of the task. This only allows alpha-numeric letters. It's recommended to have the same name as the folder name.
  • friendlyName: The name displayed in the Azure DevOps pipeline UI.
  • preview: Indicator that this task is a preview. This usually is set to false, unless it's a preview.
  • showEnvironmentVariables: Environment variables only scoped to this task. If necessary, set this value to true.
  • runsOn: List of agents that the task can run. The possible values are Agent (Hosted Agent), MachineGroup (Deployment Group), Server (Azure DevOps Server). In general, set those three values at the same time.
  • category: Azure DevOps category. As my use case is only responsible for Azure Pipelines, I just set the value here to Azure Pipelines.
  • instanceNameFormat: The initial task name when the task is brought to the pipeline.
  • execution: The file name that this task invokes. It can be either a PowerShell script or JavaScript. In my use case, I'm going to use JavaScript, so I set Node and index.js.
  • inputs: This is the most crucial part of designing the task.json. It defines the user input flow. Based on this, the UI of the task is determined. This task only requires the netlify-cli version. My intention around this will be to take the version value. If it's omitted, the latest version of netlify-cli will be installed.

Based on the design, the actual UI will look like:

If you want to know more details on the task.json structure, have a look at task.schema.json as a reference.

Designing deploy Task

This task is to deploy websites through netlify-cli installed from the previous task. If you're not familiar with Netlify command, this will be the command I'm going to use:

https://gist.github.com/justinyoo/baf3ecf3240df3037be0f84fe43b5425?file=netlify-deploy.cmd

If you want to know more about the netlify-cli command, visit this official page.

As the command requires several parameters, I need to reflect them into the design. Under the deploy folder, create a task.json file and enter the following JSON object:

https://gist.github.com/justinyoo/baf3ecf3240df3037be0f84fe43b5425?file=deploy-task.json

I'm going to discuss only the inputs attribute here. As you can see, it needs more parameters than the install task.

  • authToken: (Required) PAT (Personal Access Token) of your Netlify account.
  • siteId: (Required) Site ID of your Netlify website, which is unique to every website on Netlify.
  • sourceDirectory: (Required) The folder location of the static website artifact. Default value is $(System.DefaultWorkingDirectory).
  • isValidationOnly: (Optional) If selected, this task only validates deployment. if not selected the task publishes the website.
  • message: (Optional) A short blurb for logging during the deployment.
  • functionsDirectory: (Optional) The folder location of AWS Lambda functions, if they exist.

Based on this design, the UI will look like:

Setting up Icon for Task

When you see the Extension Layout page, each task needs its icon to display on the pipeline UI. This is the only information on the official document, but this is not enough to publish. Fortunately, this Stack Overflow page provides more details. To sum up:

  • The icon name MUST be icon.png.
  • The icon size MUST be 32x32 pixels.
  • The icon MUST be placed at the same location as the task.json file.

If you can't meet this condition, your extension won't be displaying the task icon properly.

Of course, the extension icon is different from this task icon, which will be discussed in later of this series.


Now, we've completed designing the extension tasks. As a result, you'll be able to see the folder and file structure like below:

In the next post, I'll discuss how to implement the index.js that is invoked by the task.json.

More Readings