August 2016

Volume 31 Number 8


Applying DevOps to a Software Development Project

By Willy-Peter Schaub, Wouter de Kort, Mattias Sköld | August 2016

“DevOps is the union of people, process, and products to enable continuous delivery of value to our end users.”—Donovan Brown in the book, “DevOps on the Microsoft Stack” (Wouter de Kort, 2016).

Every DevOps journey begins with an idea that you want to turn into a solution based on a culture of learning and continuous delivery of value. The goal of this article is to share the learnings we gathered during the exploration of implementing an automated release pipeline for our development, user acceptance testing and production environments. We’ll walk you through an automated release pipeline, which you can adopt “as is,” or evolve to meet your requirements.

So why did we decide to embrace DevOps? As we became intimately familiar with the “why” (goal), the “what” (features), and the “how” (technology, code) of building extensions, we needed a culture and an environment that lets us build, test, release, and monitor the rapidly evolving and growing family of extensions. The promise of DevOps encouraged us to explore and embrace the processes and tools offered by Visual Studio Team Services (Team Services for short). Our self-organized and autonomous teams were empowered to evolve a culture and a pipeline that reduced release cycle times from chaotic, error-prone and manually intensive days to minutes. A similar story and explanation of the pipeline is told in another article in this issue (“From Code to Customer: Exploring Mobile DevOps”).

If you’re unfamiliar with the Rangers, we’re a community of internal and external engineers who work with the product group to provide professional guidance, practical experience and gap-filling solutions to the developer community. It’s the latter—gaps—that got us excited about and committed to extensions.

Extensions provide you with an integration and extensibility experience for Team Services and Team Foundation Server (TFS). Extension points include the work item form, build and release tasks, dashboard widgets, menu actions, Agile and other hubs. These let you provide gap-filling solutions, blending into and enhancing the product, UX and productivity.

A typical extension consists of a set of JavaScript, HTML and CSS files, as shown in the 2015 blog by Willy P. Schaub, “Extensions 101—Attempting to Visualize the Main Processing Flow” ( One or more JSON manifest files describe basic information, files included with and contributions provided by the extension. Refer to the open source Folder Management extension sample, which lets you quickly create a folder in your Team Services source repositories from the Web without cloning the repository locally or installing extra tools.

Each extension is described in a JSON-based manifest, called vss-extension.json, by default. For consistency, we have made a choice to base all future extensions on the Team Services Project Template, and TypeScript for all our JavaScript code. Node Package Manager (NPM) is used for downloading external dependencies, such as the Typings library, required for TypeScript IntelliSense. We leverage the ability of NPM to define a basic set of scripts for initializing the development environment. A dose of consistency ensures that team members can easily move between teams and that issues can be investigated and resolved much easier. It enables shorter cycle times!

Publishing an Extension Manually

If you clone the Folder Management repository to your local development machine, you can quickly package and publish the extension to your marketplace account manually. Here’s how:

  • Use the NPM Scripts Task Runner ( or run the commands from the command line.
  • Open the solution and run the setup task: npm run setup. This will download the NPM packages, initialize the Typings required for TypeScript and put your external dependencies in the correct location.
  • Use Visual Studio to compile the TypeScript files and generate the output JavaScript.
  • Run the package NPM task to create an output VSIX file based on the manifest in the project.
  • Upload the generated VSIX to the Marketplace or run npm run publish to automatically package and publish your extension.

But first, some background. Team Services is made up of isolated accounts. The Marketplace is global and made up of publishers, and created and managed by the gallery publisher (you). An extension is published as private and explicitly shared with Team Services accounts. Alternatively, an extension can be published as public once it has been validated, making it visible to everyone. We strongly recommend that you never publish this or other sample extensions as public in order to avoid a confusing and potentially bad UX.

To publish your extension, ensure that the publisher field in the manifest matches the name of your Marketplace publisher. If your publisher isn’t verified by Microsoft, you can only publish an extension as private. To publish your extension from the command line or the NPM Task Runner, you also need a personal access token that grants permission to manage your Marketplace publisher. See the article, “Publish an Extension to the Marketplace” (, for more details.

An Automated Build and Release Pipeline

As the family of extensions and revisions grows, these seemingly simple manual steps will quickly become laborious and error-prone. Therefore, we started working on the automated pipeline, using scripts and build tasks to automate the manual steps.

You can use Windows PowerShell and the Command Line build tasks to package the extension manifest and revise the version number, as shown in Figure 1. This is described more fully in the article, “Our First Steps of Embracing DevOps When Building Visual Studio Team Services Extensions” (—simple and effective!

Figure 1 Windows PowerShell Build Task (Top) and Command Build Task (Bottom)

Tool Windows PowerShell
Arguments cha -command "(Get-Content vss-extension.json).replace('0.0.1', ('%BUILD_BUILDNUMBER%').replace('SampleData ',"))  | Set-Content vss-extension.json" rt
Tool tfx
Arguments extension publish –token $(PublishExtToken) –overrides-file $(ManifestOverrideFile) –share-with $(SharedAccounts)

Alternatively, you can use the Build and Release Tasks for Extensions to fine-tune your build-and-release process. The rest of this article is based on this extension.

Let’s explore how we used the Build and Release Tasks for Extensions and implemented a blueprint for all other extension projects. Each extension starts its journey in an isolated Team Services account, which is the first stage of the deployment of an extension. Release Management refers to these stages as environments; therefore, we’re calling it the development (DEV) environment. It then goes through a series of design, coding, feature, UX and performance validations in a separate user and product owner acceptance (BETA) environment. Similar to the development environment, this is an isolated Team Services account, and the second stage of the extension deployment. Once an extension meets our definition of done (, it’s deployed to the Marketplace and visible to everyone. You may recognize the DEV → BETA → PROD stages of the release pipeline taking shape.

To prepare the extension project for the automated pipeline, we recommend the following changes to the extension manifest, as shown in Figure 2:

  • Set your version to 0.0.0 and your publisher to an empty string (“”)
  • Mark the extension as private (public: false)
  • Remove the galleryFlags attribute

Figure 2 Manifest File Extract

{  "manifestVersion": 1,
  "id": "FolderManagement",
  "version": "0.0.0",
  "publisher": "",
  "name": "Folder Management",
  "description": "Quickly create a folder in your Visual Studio Team
    Services source repositories from the web. No need to clone the
    repository locally or install extra tools.",
  "public": false,
  "icons": {
    "default": "images/VSO-Folder-196x.png"
  "categories": [
  snipped rest of manifest file ...

These values will be updated during the release deployment and the defaults will ensure that the extension is not deployed or made public by accident.

 Adopting a consistent naming convention will simplify traceability through your various environments. If, for example, you suffix the ID with your environment during the release, FolderManagementBeta would be the Folder Management extension running in the BETA environment.

Continuous Integration (CI) is a practice that enables you to have a production-ready build of the latest code in a source control repository ( This is done by automated build and tests are run on every commit.

Our extension projects are typically stored in a Team Services hosted Git repository, or on GitHub for open source projects, such as the Folder Management extension. The pipeline starts with a build definition triggered with every commit made to the repo, then it builds the VSIX extension package and triggers the release definition to deploy it to the DEV, BETA and PROD environments.

As shown in Figure 3, ensure you enable continuous integration and optionally batch changes to reduce the number of running builds.

Build Trigger
Figure 3 Build Trigger

The astute reader might have noticed that our CI Build outputs only a VSIX package and does not copy the extension source files. The reason we do this is one of the foundational principals of a delivery pipeline: build once and only once. Instead of compiling and packaging the extension files at each deployment step, we package only once at the beginning of the pipeline with different configurations per environment. We do this to be absolutely certain that we deploy exactly the same extension to each different environment. 

Versioning your extension and build tasks is important. In Team Services, the latest version wins. If you install a public and a beta version of the same extension to a Team Services account, it must be clear which version will activate.

What options are there for versioning? The Team Services developer tools let you use any three-part version number as your extension. Foremost, for the simplicity and clear traceability, we’ve decided to use the Build.BuildNumber as our version number, as shown in Figure 4.

Build Number Format
Figure 4 Build Number Format

Alternatively, you can use the Query Extension Version task, which gives greater control over the version numbers you publish by taking the current version from the marketplace and incrementing a specific part each time the extension is published. While reducing the traceability between the marketplace version and artifacts in Team Services, it provides a nicer sequential numbering toward your customers in the Marketplace.

What about self-testing? The CI Build is also a good place to run things like unit tests. The Folder Management extension doesn’t use any unit tests because all logic places calls to the Team Services REST APIs. Other extensions, such as the Countdown Widget (, include unit tests that validate the logic for calculating the time difference. These unit tests are run as part of the build. Other automated tests that we want to add in the future are Selenium Web Tests. This lets us not only run unit tests, but also automate UI testing.

Figure 5 shows the build steps. The NPM dependencies are installed with the npm install (1) and the setup script processed with the npm exec tasks. As an alternative, you could use NuGet and the NuGet restore activity. The Visual Studio Build task (2) then builds the solution, followed by the Package Extension task (3) which produces the VSIX extension package.

Build Definition
Figure 5 Build Definition

Optionally, you can configure the publisher and extension IDs, tag, and name to override the manifest values. The pipeline only configures the version number (4) as part of the build, setting it to the build number to ensure that every instance of the pipeline can be uniquely identified and tracked.

What’s the PowerShell script task for? At the time of this writing, the following script was needed to extract the version information (future versions of the extension developer tools—build tasks [] will make the script obsolete):

$bldVerD =("$env:BUILD_BUILDNUMBER").replace("$env:BUILD_DEFINITIONNAME","").Trim();
Write-Verbose "Extracted buildVersion $bldVer";
Write-Host "##vso[task.setvariable variable=ExtensionVersion;]$bldVer"

Continuous Delivery is the ability to use the output from the CI to build and deploy the new known good build to one or more environments automatically ( There is a subtle difference between Continuous Delivery and Continuous Deployment. The latter is to a single environment. A small team might only implement Continuous Deployment because each change goes directly to production. Continuous Delivery is moving code through several environments, ultimately ending up in production, which may include automated UI, load and performance tests and approvals along the way.

The deployment to the DEV environment is fully automated and occurs frequently. This means that every successful CI build is deployed to DEV without manual steps. As shown in Figure 6, after DEV succeeds, a pre-­approval is requested before deploying to the BETA environment. This stage is approved by the project lead or the program manager. Finally, there’s a pre-approval step for the public release to production. This needs to be approved by both the project lead and the program manager. For simplicity, we chose to use only pre-approval steps and automate the post-approval step because there’s no reason for us to approve a post-deployment step and then not deploy it to the next environment.

Release Trigger
Figure 6 Release Trigger

Figure 7 shows the release definition, which deploys the VSIX extension package to three environments. The first environment, DEV (1), is owned and managed by the team that builds the extension. The extension is deployed as private and shared with a development sandbox account.

Release Definition
Figure 7 Release Definition

Once the release is tested and approved, the deployment continues to the second environment, BETA (2). The extension is still deployed as private and shared with a user acceptance sandbox account.

Once the user acceptance testing is complete and approved, the deployment continues by changing the publisher, setting the visibility to public and deploying the extension to the marketplace (3).

The Publish Extension task (4) is the heart of the deployment process and the secret sauce of the pipeline. This task updates the VSIX file by unzipping the content, updating the configuration values and zipping all the files. The task then deploys it to the configured Marketplace publisher account, such as the alm-rangers publisher configured for the Beta environment, as shown.

The extension ID Tag (5) and name are overridden to ensure that we have a unique instance of the extension running in each environment, that the Dev and Beta extensions are automatically shared with development and user acceptance testing Team Services accounts, and that the DEV and BETA releases are private.

You need a Personal Access Token (6) to publish an extension using the Publish Extension task or the Command Line. To securely store the token, you can create a Team Services Service connection to the Marketplace. The connection defines a key/value pair where the key is the name of your connection and the value is the access token.

Some of the other tasks you should explore include:

  • Query Extension Version: Checks the Marketplace for the version number of a published extension. The version can be stored in a variable and used by the pipeline to create a new version, such as incrementing the major, minor or patch version number.
  • Install Extension: Deploys an extension to the Marketplace without installing it into an account.
  • Share Extensions: Shares a private extension to the specified accounts.

At this point, the pipeline consistently builds, packages, and updates the extension and, more important, protects environments from common failures. Examples of build failures are when there are errors in the TypeScript code or if there are missing files. The deployment fails when the VSIX is invalid, if access to environments is restricted, or if one of the approvers rejects the release.

Wrapping Up

Now that you have an automated build-and-release pipeline, you can speed up your development process, reduce errors introduced by human intervention, and, more important, evolve your solutions, as well as continuously improve through continuous reflection, measuring and learning.

However, that’s a topic for another day.

The automated CI and CD pipeline has reduced our manual and error-prone extensions build-and-release process from days to minutes. It’s an invigorating experience and enables our development team to focus their time and effort on what is truly important.

The DevOps practices illustrated here can be applied to your own projects, as well. Team Services enables DevOps for any language targeting any platform, including on-premises, cloud, hybrid cloud and mobile. With features that allow you to plan, version, build, deploy and monitor your application, Team Services has everything you need to turn an idea into a working piece of software. Armed with this information, we’re excited to see the extensions the community creates to enhance the capabilities of Team Services as you embark on your DevOps journey.

DevOps Resources


Willy-Peter Schaub is a senior program manager with the Visual Studio ALM Rangers at the Microsoft Canada Excellence Centre. His blog is at, and you can also follow him on Twitter: @wpschaub.

Wouter de Kort is the principal consultant Microsoft at Ordina in the Netherlands where he helps companies stay on the cutting edge of software development. He blogs at You can follow him on Twitter: @wouterdekort.

Mattias Sköld is a DevOps/ALM coach at Sogeti Sweden, helping customers to improve their software’s practices and driving adoption of ALM/DevOps practices internally at Sogeti. He blogs at and you can follow him on Twitter: @mattiasskold.

Thanks to the following Microsoft technical experts for reviewing this article: Donovan Brown, Jess Houwing and Will Smythe

Discuss this article in the MSDN Magazine forum