Sending Email Notification from VSTS/TFS Build

Visual Studio Team Services provides a number of extensibility options enabling users to enrich their DevOps environment or integrate with other applications and services. In previous versions, extensibility was supported using TFS object model. Today, a number of options are available such as REST API, Service Hooks and Extensions. In terms of build system, custom build activities, pre-build, post-build, pre-test and post-test scripts were the common ways to extend the build functionality.

In VSTS/TFS 2015 Build, extensibility is provided by creating build tasks. The process of creating build tasks is remarkably simpler as compared to it's predecessor. In this post, we will create one such task.

The Problem

Assuming you want to send out an email from build definition. This is usually done to notify users of a build failure or to give information like results from a test run triggered by the build. While you may think about email Alerts as a simple way to do so, you assume the user will configure alerts per build definition, but we need a more flexible (push) model for the alerts. For example, you might want to send emails out to the person who triggered the build.

The Solution

In this post, we will create a VSTS Build task that allows sending emails to the build requestor. Build tasks are Node.js based. So, to create a new build task, the following needs to be installed on the machine

  1. Node.js 4.0x or later
  2. Node Package Manager aka npm
  3. The tfx-cli utility, which is a node module that provide functionality to create and package build tasks
  4. A simple text editor, such as Visual Studio Code

Creating the build task

We start with using tfx-cli to create the task shell i.e. all the files that the tasks needs. To do this, type in the following on windows command prompt

 tfx build tasks create

The tool prompts for details such as task name, friendly name, description and author. The following details are provided for our task: createTask_1

A new folder bearing the same name as the task name is created with the following four files

  • task.json: It is the task manifest file that task properties, inputs, demands and files.
  • icon.png: The icon displayed for the task.
  • sample.js: The node implementation of task.
  • sample.ps1: The PowerShell implementation of the task.

Since, we only use the node implementation for our task, it’s safe to delete the PowerShell file sample.ps1. Our task is called emailNotifier, so the file sample.js is renamed to emailNotifier.js.

Modifications in task.json file

The task.json file contains following information

  • The task properties, which include the values given in the command line.
  • Task’s version information.
  • The agent requirements such as demands and minimum version.
  • The input parameters
  • The files included in the build task.

The contents of the default task.json look like following

 {
  "id": "92fa37d0-b5c7-11e6-9c14-0dffa48df7cc",
  "name": "emailNotifier",
  "friendlyName": "Email Notification Task",
  "description": "A task that send notification emails to requestor",
  "author": "Visual Studio ALM Rangers",
  "helpMarkDown": "Replace with markdown to show in help",
  "category": "Utility",
  "visibility": [
    "Build",
    "Release"
  ],
  "demands": [],
  "version": {
    "Major": "0",
    "Minor": "1",
    "Patch": "0"
  },
  "minimumAgentVersion": "1.95.0",
  "instanceNameFormat": "emailNotifier $(message)",
  "inputs": [
    {
      "name": "cwd",
      "type": "filePath",
      "label": "Working Directory",
      "defaultValue": "",
      "required": false,
      "helpMarkDown": "Current working directory when emailNotifier is run."
    },
    {
      "name": "msg",
      "type": "string",
      "label": "Message",
      "defaultValue": "Hello World",
      "required": true,
      "helpMarkDown": "Message to echo out"
    }
  ],
  "execution": {
    "Node": {
      "target": "sample.js",
      "argumentFormat": ""
    },
    "PowerShell3": {
      "target": "sample.ps1"
    }
  }
}

We want to keep our task as simple as possible and the only thing unknown for our task are the details of SMTP server, so we will add the following 4 parameters to our task.

  • smtpAddress – the URL of the SMTP server.
  • port – The port used by SMTP server
  • username – The login to authenticate with SMTP server.
  • password – The users’ password to authenticate with SMTP server

The only other changes we make in the file are to rename the execution target to emailNotifier.js and removing the PowerShell file reference. Our final task.json file looks as following

 {
  "id": "92fa37d0-b5c7-11e6-9c14-0dffa48df7cc",
  "name": "emailNotifier",
  "friendlyName": "Email Notification Task",
  "description": "A task that send notification emails to requestor",
  "author": "Visual Studio ALM Rangers",
  "helpMarkDown": "Replace with markdown to show in help",
  "category": "Utility",
  "visibility": [
    "Build",
    "Release"
  ],
  "demands": [],
  "version": {
    "Major": "0",
    "Minor": "1",
    "Patch": "0"
  },
  "minimumAgentVersion": "1.95.0",
  "instanceNameFormat": "emailNotifier $(message)",
  "inputs": [
    {
      "name": "smtpAddress",
      "type": "string",
      "label": "Smtp Address",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "The Smtp server to use"
    },
    {
      "name": "port",
      "type": "string",
      "label": "Port",
      "defaultValue": "25",
      "required": true,
      "helpMarkDown": "The Smtp port to use"
    },
    {
      "name": "username",
      "type": "string",
      "label": "UserName",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "The User name to use"
    },
    {
      "name": "pwd",
      "type": "string",
      "label": "Password",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "The Password to use"
    }
  ],
  "execution": {
    "Node": {
      "target": "emailnotifier.js",
      "argumentFormat": ""
    }   
  }
}

Update emailnotifier.js file

Now that the manifest file has all the details, the next step is to write the body of our task. Since, we chose to use Node.js to write our functionality, we start with initializing the node package module for our task by typing in following on command line

 npm init

Select default options for each of the prompts. A package.json file is generated as a result. The next step is to include any node modules that we need in our project. We need the following two modules for our task

  1. vsts-task-lib – The module provides some core functionality to get details from build system.
  2. Nodemailer – The module provides functionality to send an email.

To include the above modules, type in the following on the command prompt

 npm install vsts-task-lib
npm install nodemailer

The mode package manager will download the desired modules and store them in the node_modules folder. At this stage, we have all what we need. So now, it’s time to write our code. To do that, open the file emailnotifier.js and type in the following code

 const tl = require('vsts-task-lib/task');

var requestor = tl.getVariable("Build.RequestedForEmail");
var host = tl.getInput("smtpAddress", true);
var port = tl.getInput("port", true);
var usr = tl.getInput("username", true);
var pwd = tl.getInput("pwd", true);

const t2 = require('nodemailer');

var connection = {
    host: host,
    port: port,
    secure: true,
    auth: {
        user: usr,
        pass: pwd
    },
    logger: true
};

var transporter = t2.createTransport(connection);
var mailOptions = {
    from: usr,
    to: requestor,
    subject: 'Emai from Build ' + t1.getVariable("Build.DefinitionName"),
    html: 'The build '+ t1.getVariable("Build.BuildNumber") + ' created this email.'
};

transporter.sendMail(mailOptions);

We start with adding reference to task module and using its getVariable and getInput variables to fetch the email address of the Build requester and details of the smtp server respectively. The next step is to add reference to the nodemailer module and use it’s transport object to send the email.

Observe the use of built-in variables like “Build.DefintionName”. These are the new build environment variables. The list of all build variables is available at https://www.visualstudio.com/en-us/docs/build/define/variables.

Deploying the new build task

To deploy the build task, we make use of the tfx-cli utility again. If you are not already logged into your VSTS account, type in the following to authenticate yourself

 tfx login

You will be prompted to enter your service URL and personal access token (PAT). Once you are logged in, simply type in the following at the directory above your task’s directory.

 tfx build tasks upload

The task would be packaged and deployed to the specified VSTS project and available for use.

Using the newly created task

To use the new task, click on the “Add Build Step” button on the build definition. Since, we set the category for the task as utility, the new task would be available in the “Utility” page of the “Task Catalog” dialog:
Email Notification Task on list

Click on the add button to use the task. It is added to the build with the input options to specify the SMTP server used for sending out emails. It can be used anywhere in your build definition. email notification task properties

Next Steps / Enhancements

In this post, we have demonstrated how easy it is to send emails by creating a simplest of all build tasks. The functionality in task can be extended further to include options such as customizing emails, select different recipient types, etc. The use of Node.js means that we have a choice to use rich already existing modules and extend as we like.