Showing posts with label Team Build. Show all posts
Showing posts with label Team Build. Show all posts

Friday, 13 July 2018

Creating CI and Deployment builds for docker images

In my last blog post, I wrote about steps to create a Docker container for running aspnet/Javascript Services. In the post, we created a Docker file to create a Docker image with a website running creating on VueJs and Asp.Net core.

In real life, we would like our docker images to be pushed out to a container registry. We would also like it to be done through a team build. We would also want to have a CI build in place so every commit is vetted. This blog post details setting up the CI and deployment builds for producing docker images.

Dockerfile

To start with, lets review the docker file we created in the last post. I have modified it slightly to parameterized the exposed port

# Stage 1 - Restoring & Compiling
FROM microsoft/dotnet:2.1-sdk-alpine3.7 as builder
WORKDIR /source
RUN apk add --update nodejs nodejs-npm
COPY *.csproj .
RUN dotnet restore
COPY package.json .
RUN npm install
COPY . .
RUN dotnet publish -c Release -o /app/

# Stage 2 - Creating Image for compiled app
FROM microsoft/dotnet:2.1.1-aspnetcore-runtime-alpine3.7 as baseimage
ARG port=8080
RUN apk add --update nodejs nodejs-npm
WORKDIR /app
COPY --from=builder /app .
ENV ASPNETCORE_URLS=http://+:${port}

EXPOSE ${port}
CMD ["dotnet", "vue2spa.dll", "--server.urls", "http://+:${port}"]

The docker file creates a docker image that can be pulled and deployed in any application.

Setting up the CI Build


We will use the new YAML build feature in VSTS to setup our CI/CD build. Our YAML CI build file would have a single step that would attempt to build the docker image. The publishing of website is performed within the docker build process as described in the Dockerfile above.

Our very simple YAML file looks like following

name: $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.rr)
steps:
  - script: |
      docker build --build-arg port=8080 --rm --compress -t sampleaspjs/web .
    workingDirectory: $(Build.Repository.LocalPath)/web
    displayName: Build Docker Image

We named the yaml file as .vsts-ci.yml. Commit it and push it out to our git repository.

Now that the yaml file is in our code repo, lets go and set up the CI build.
  1. From the Build Definitions page, click on the "+ New" button to create a build definition.
  2. Select the source to VSTS Git, select your Team Project, repository and the default branch of master 
  3. From the list of template, select option YAML in the Configuration as Code classification and click Apply
  4. In the build definition, type in the build name, select the "Hosted Linux Preview" queue and select the YAML path. Make sure Continuous Integration is enabled. Save the build definition.

Now that we have the CI build, lets turn out attention to create a deployment deployment build

Setting up the product build


The steps to create our product build is similar to that of CI build except that we will have a different YAML file. Our build process will go one step further. In addition to creating the docker image, it would also push out docker images to dockerhub

Our YAML file looks like following

name: $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.rr)
steps:
  - script: |
      docker build --build-arg port=8080 --rm --compress -t tiresias/web .
    workingDirectory: $(Build.Repository.LocalPath)/web
    displayName: Build Docker Image

  - script: |
        docker tag aspnetvuejs/web tiresias/web:$(Build.BuildNumber)
    workingDirectory: $(Build.Repository.LocalPath)/web
    displayName: Tag docker image with buil d version

  - script: |
        docker login --username your-username --password your-password
    workingDirectory: $(Build.Repository.LocalPath)/web
    displayName: Docker Login

  - script: |
        docker push aspnetvuejs/web
    workingDirectory: $(Build.Repository.LocalPath)/web
    displayName: Docker Push

We named the yaml file as .vsts-build.yml. Commit it and push it out to our git repository.

The process to set up the deployment build is same as above.
  1. From the Build Definitions page, click on the "+ New" button to create a build definition.
  2. Select the source to VSTS Git, select your Team Project, repository and the default branch of master 
  3. From the list of template, select option YAML in the Configuration as Code classification and click Apply
  4. In the build definition, type in the build name, select the "Hosted Linux Preview" queue and select the YAML path. Make sure Continuous Integration is enabled. Save the build definition.
With the product build in place, we have a mechanism of creating docker images and pushing it out to a container registry.

Next steps

In this post, I described the steps to create a CI / build process for verifying, creating and distributing docker images. In my next post, I will deploy the docker images to a Kubernetes cluster created on Azure Kubernetes Service.


Tuesday, 12 December 2017

Creating a yaml CI Build for .net application

One of the great announcements from this year's Microsoft Connect() conference was YAML support for VSTS build definitions. 

For me, it's a great way forward towards "codifying" the build pipeline. The current TFS builds technology, introduced in Team Foundation Server 2015, despite all the benefits of a loose and extensible mechanism is rather difficult to maintain as code and doesn't really fit "pipeline as a code" definition. If you remember, earlier versions of Team Foundation Server (TFS 2005 and TFS 2008) used an MSBuild file to run builds. Whilst this was easy to code and maintain, extensibility was rather limited. Then, Team Foundation Server 2010 introduced XAML builds with better support for workflows but was difficult to work with. TFS 2015 simplifies XAML but the whole logic is spread across different aspects of the build. Yaml solves this shortcoming nicely.


Enable YAML Builds Preview Feature

At the time of writing this post, support for YAML builds is still in preview in VSTS. To enable it for your account, click on your profile and select option "Preview Features" from the drop down menu




Select option "from this account [projectName]". Scroll down till you find the "Build Yaml definitions" feature and set it to On



We are now ready to use YAML builds.

Creating a Yaml Build 

There are two ways in which we can set up a Yaml build. 

1) Create a file called .vsts-ci.yaml. When you push your change with this file to TFS, a build definition, using this file is created for you.

2) Explicitly create a build definition using the YAML template, providing the path of YAML file that you have committed in the repo.

We will go with option 2.

Create build.yml file


YAML file format is the format of choice for configuration files and is used by some exciting technologies like docker, Ansible, etc. It's great that VSTS now supports it as well. 

For this demo, we are creating build for a simple .Net Web Application. For the purpose of building our application, we need to do the following

1) Restore all NuGet packages
2) Build the entire solution

Our very simple yaml file looks as below

steps:
- task: nugetrestore@1
displayName: NuGet Restore
inputs:
projects: "MyWebApplication.sln"

- task: MSBuild@1
displayName: Building solution
inputs:
command: build
projects: "MyWebApplication.sln"


The file is pretty much self descriptive. As you can see we we have two tasks. The first task uses nugetrestore passing the solution as input. The second task executes MSBuild passing the applicatino's solution.

Commit the file to your local Git Repo and push to commit to TFS.

Creating the Build definition

Now that our yaml file is committed, we will create a build definition to use it. To do this, click on the New button to create build definition. For the build template, select YAML and click Apply




We will then be asked to provide the build name, agent queue and path to Yaml file. Make sure, you have selected the correct repository and branch in the "Get Sources" option for the build definition.

Please note that YAML builds are only supported for Git are not supported when TFVC is used a version control repository.

Click on the Triggers tab to make sure that Continuous Integration is selected as option

Click save to save your build. Now this build is set up as a continuous integration build for your repository and is triggered with every commit.


Conclusion:
If you compare the amount of work you had to do to create yaml build, it's really a breeze as compare to TFSBuilds. There are many use cases of using YAML for your build definitions. You can set up a complete pipeline, decoratively executing each steps which can be developed and tested locally before being used in VSTS.


Saturday, 2 December 2017

TFS 2017 Build - Partially succeed a build

At times, there is a need to explicitly set a Team build's result to be "Partially Successful". 

In Xaml, the way to forcibly build to set as partially successful is to set the build's "CompilationStatus" property to true and "TestStatus" to False, as shown below

<mtbwa:SetBuildProperties DisplayName="Set TestStatus to Failed so we get a PartiallySucceeded build" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed]" />

Setting a TFS 2017 build to partially succeed is a bit more intuitive. Simply add a powershell task with an inline script and set the task's result to "SucceededWithIssues". Make sure it's the last task in your build, so that it doesn't affect the flow of task execution. The Powershell statement is shown below

Write-Host "##vso[task.complete result=SucceededWithIssues;]DONE"

My build looks as follow


Thursday, 30 November 2017

TFS 2017 Build System - Maintain last "N' builds

In my last blog post, I described retention policies in the TFS 2017 build system. I described how different it is from the the retention policies we get in XAML build system. 

One of the limitations I found in the new style retention policy is that I couldn't retain a specific number of builds for each status. We needed to do it for some builds that are triggered very frequently (once every couple of minutes) and check if there is some work to be done. If it found work, it would do it, otherwise it will reschedule a build for itself after a couple of minutes. Another scenario, where you might have a lot of builds is when you it is triggered by a commit of a very busy repository.

So, in order for us to retain only "N" builds for each status, we created a Powershell Module to clean up builds. In the module, we create a command-let that takes as parameter the name of the build, the number of builds to keep, the result filter and tag filter. Our command-let looks as following



***************************************************
.SYNOPSIS
 Cleans up all builds for the given build definition keeping the latest N number of builds where n is passed a parameter
 If a status is provided, it would only keep N builds with the given status

.DESCRIPTION
 PATCH https://{instance}/DefaultCollection/{project}/_apis/build/builds/{buildId}?api-version={version}
 Uses api-version 2.0 to update the build result
***************************************************
function Cleanup-Builds([string] $tfsCollection,
                    [string] $tfsProject,
                    [string] $buildDefinitionName,
                    [int] $numberOfBuildsToKeep = 10,
                    [string] $result="",
                    [string] $tagsFilter = "")
{
    if (${env:system.debug} -eq $true) {
        $VerbosePreference="Continue"
    }

    if ($status -eq ""){
        Write-Verbose "Deleting all but the latest $numberOfBuildsToKeep builds for definition $buildDefinitionName."
    }
    else{
        Write-Verbose "Deleting all but the latest $numberOfBuildsToKeep builds for definition $buildDefinitionName with status $status."
    }

    $buildDefinition = Find-BuildDefinition($buildDefinitionName)
    if ($buildDefinition -eq $null) {
        Write-Error "No build definition found $buildDefinitionName"
        return
    }

    $buildDefinitionId = $buildDefinition.id
    $query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds?api-version=2.0&definitions=$buildDefinitionId&queryOrder=2&resultFilter=$result&tagFilters=$tagsFilter&`$top=5000")

    $builds = Invoke-RestMethod -Method GET -UseDefaultCredentials -ContentType "application/json" -Uri $query
    $retainedBuild = 0
    $deletedBuildCount = 0
    for ($i = $builds.Count - 1; $i -gt -1; $i--) {
        $build = $builds.value[$i]
        $buildId = $build.id
        $buildNumber = $build.buildNumber
        
        try {
            $query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds/$buildId/tags?api-version=2.0")
            $tagFound = $false

            # Not delete the latest numberOfBuildsToKeep builds
            if ( ($retainedBuild -lt $numberOfBuildsToKeep)) {
                $retainedBuild = $retainedBuild + 1
            }
            else {
                Write-Verbose "Deleting build $buildNumber"
                $query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds/$buildId`?api-version=2.0")
                Invoke-RestMethod -Method DELETE -UseDefaultCredentials -ContentType "application/json" -Uri $query
                $deletedBuildCount = $deletedBuildCount + 1
            }
        }
        catch {
            Write-Error "StatusCode:" + $_.Exception.Response.StatusCode.value__ +
                        "`r`nStatusDescription:" + $_.Exception.Response.StatusDescription
        }
    }
        
    Write-Output "Deleted $deletedBuildCount builds for build definition $buildDefinitionName"
}

We create a PowerShell Module file for the above command let. To set up the Powershell module, we modified the PSModulePath environment variable as first step of our build to include the module path. Then to set-it all up we added a PowerShell task group calling the Cleanup-Builds command in an inline script as shown below




Our build definition looks like below





Friday, 10 November 2017

Retention Policies for TFS 2017 Build System

TFS build system has had a major overhaul since TFS 2015. For people working on team builds since TFS 2010, there is some major learning curve. One of the things the people often find confusing is the retention policy in the new build system. In earlier versions of TFS, you could specify how many builds you want to retain for each status as shown in the screenshot below


Retention Policies for Xaml Builds

The retention policy is quite obvious and you have a deterministic number of builds retained at each status. It's not quite the case in the new build system. A sample retention policy in the new system looks like following


Retention Policies for TFS Builds


So what does it mean? 

Well, to say it simply, it means exactly what it says on the tin!! In the example above, the build would keep all builds from the last 4 days and keep a minimum of 20 builds. That is if there are less than 20 builds present for the given build definition, it would keep older builds until there are a minimum of 20 builds. Lets ignore the options with the lock sign, we will come back to it later. Note there is are no maximum count. It means that you can't control how many builds you keep for your build definition. This is a major shift from earlier retention policy where the number of builds kept for a build definition was deterministic. 


When are builds actually cleaned up?

If you are using an on-premise version of TFS (I am using TFS 2017 Update 2), the builds are actually not cleaned until 3:00 AM every day. For VSTS, it happens several times in a day but the time is not deterministic. The actually explains why there is only a "Minimum to Keep" option in the retention policy.

If you have a build definitions that is triggered very frequently, you will need to find a solution of actually deleting the build definitions. I will explain it in the next post.


What about Keep For 356 days, 100 good builds?

This is the option you see below your policy in the screenshot shown above. This is in fact a TFS Project Collection wide policy and enforces the maximum retention policy. So, in the example above, you can't set "Days to Keep" to more than 365 and "Minimum to Keep" to more than 100. In fact, if you have appropriate permissions, you can change it for the entire Team Project Collection.



TFS Project Collection Retention Policy


Multiple Policies

If you want, you can add retention multiple policies for your build definition. It is very useful, if you have build definition that builds different code branches (release branches for instance). You can use the retention policies to keep different number of builds from each branch. 


Multiple Policies


If you have multiple retention policies for the same branch, the retention would be the most lenient of all the retention, so whatever retains the most builds.

In the next blog post, I will show how we are keeping a lid on the number of builds for builds which are build very frequently, every couple of minutes in our case.

Thursday, 12 September 2013

BuildStepDuration activity - TFS Build Extensions

The September 2013 release of TFS Build Extensions is available for download now. There are some cool new activities added to the library. Amongst them is the BuildStepDuration activity. It is a very simple to use activity that allows you to view a break down of build steps and the time each build step took in a completed build. This is really useful for build masters who are looking to optimise their build and are having a hard time in getting the information from Visual Studio build details window, which shows all information in a hierarchical way.

More information can be read from the activity documentation http://bit.ly/15VuILe.

The latest version of TFS Build Extensions can be downloaded from here.

Thursday, 18 July 2013

Uploading files to an ftp site from your Team Build

The July release of TFS Build Extensions were released yesterday. Amongst other improvements and features there library not contains an FTP Activity that allows the team build to interact with an FTP. You can for instance upload the contents of your drop location to an Ftp Site. This blog post details how to do so.

To use the Ftp activity activity, simply drag and drop it on the build process template. Once dropped, set the appropriate values for the properties required and if all the information is correct, the activity will be able to do the required task on your ftp server. Regardless of the ftp action, the following two properties are required and must be set
  • Action: Must be set to one possible values of FTPAction enumeration.
  • Host: Must be set to the host name of the ftp site.
To illustrate the use of the activity, we take a a very simple scenario that all files produced by the team build should be uploaded to an ftp site. Moreover, we also require that the files of each build should be created in a separate directory.

To start with, we need to take drop a sequence activity at an appropriate location in the build template. I chose to do it just above the “Check In Gated Changes for CheckInShelveset Builds”.


Sequence1_thumb1


The next step is to drop the activities to find files, create directory and upload files. My final “Upload Drop Files to Ftp” looks as follows


Unplaod-Drop-Files-to-Ftp_thumb1


Note that in the workflow I am using the FindMatchingFile activity, which looks for all files in the drop location and put the result in a variable “DropFiles”,  of type IEnumarable<string>.
Next the workflow checks if there are any files in the drops folder, if there are it will
First create a directory on the FTP site using the Ftp activity with action “FtpAction.CreateDirectory” using the name of the build. The value of all properties for this activity are shown below


FtpCreateDirectory_thumb1


The last step is to upload all files. The workflow has another FTP activity with action set to “FtpAction.UploadFiles”, passing in the enumeration “DropFiles”. The value of all properties of this activity are shown below.


Ftp-Upload-Files_thumb1


And that is it. At the end of the sequence execution everything in the root of drop folder will be uploaded to the Ftp site provided.

Running the Activity
When your run the build workflow, you should see the Ftp activity being executed and the files in the drop location being uploaded to the ftp directory.
successfulbuild_thumb1

Saturday, 13 July 2013

Team Build Customisation - Running StyleCop on a Hosted Build Controller

For companies and individuals who don't or can't use an on premise version of Team Foundation Server, the hosted TFS service http://tfs.visualstudio.com is a great alternative. Once signed up, you can connect to the hosted version from visual studio just as you would connect to an on premise version and it provides the same if not a better experience.

However, when it comes to Team Builds, there is some restrictions on the type of customisation that you can do on your hosted build controller. You can, of course, create your own Build Machine and connect with the tfs.visualstudio.com service, but here we are looking a purely hosted service.

In this post, I will demonstrate how to customise your TFS build template to run StyleCop on all your code files. We will use the StyleCop activity present in TFS Build Extensions.

The first step is to get the two libraries need for customisation
1. StyleCop (http://stylecop.codeplex.com/). The version I am using is 4.7.44.0
2. Community TFS Build Extensions  (http://tfsbuildextensions.codeplex.com/). The version I am using is January 2013 release.

Changes in Build Controller
The first thing you need to do is to make your build controller aware of the location of the custom assemblies. This is the place from where the build controller will load assemblies to load and execute activities while running team build. To do so create a folder in your team project where you would want to keep all the build assemblies. Typically, I keep it separate from the project's main branch and other branches. The next step is to inform build controller of this location. For this

  1. Click on the build link in your team explorer window. Then click Actions and select Manage Build Controllers.



2. The following dialog appears. Select "Hosted Build Controller" and click on the properties button.


3. In the Build Controller properties dialog, set the "version control path to custom assemblies" property to the folder in team project that you created earlier


Click Ok and close the properties dialog. At this point, the build controller is configured to load your custom activities. The next step is to customise your build's template.

Custom Build Templates
When you create a team project, a folder called BuildProcessTemplate is created by default containing a number of build templates.


Its best practice to make a copy of the template before modifying it. I copied the file DefaulitTemplate.11.1.xaml and renamed it to CustomTemplate.xaml. This will be our custom build template. If you double click this file, you will see the team build workflow. 

The next step is to add the custom StyleCop Activity to this workflow. To do so you will need to do the following

1. Create a Workflow activity project in Visual Studio. This will create a project with all the assemblies required to create a workflow already added in the reference.


2. Add reference to the libraries StyleCop.dll, TfsBuildExtensions.Activities.dll and TfsBuildExntesions.StyleCop.dll


3. Add a link to the CustomTemplate.xaml to the project. Do to so, right click on the project, click Add and then Existing Item, browse to the file CustomTemplate.xaml, click on the arrow with Add button and select Add as Link. 


4.  The fourth and final step of setting up your environment to be able to modify custom template is to add the activities of TFS Build Extensions to the toolbox. To do so, first double click on your CustomTemplate.xaml file to load it up. In the Toolbox window, right click on the area below activities and select the option Add Tab. Name the new tab "TFS Build Extensions". Right click on the tab name and select choose items. Browse to the assemblies "TfsBuildExtensions.Activities.dll" and "TfsBuildExtensions.Activities.Stylecop.dll". The activities are loaded up on Toolbox and you are ready to add the StyleCop activity.

5.  Where do you want to run style cop in your build process is a matter for you decide. I typically want to do it first thing before anything gets compiled. So, I do it as part of "Initialize Variables" sequence in the workflow. I dragged a sequence activity at the end of it and named it "Run StyleCop" as shown.

 6. My Run StyleCop Activity looks like following

As you can see, in the sequence after using the usual write build messages, I use the activity FindMatchingFiles looking for all c# files. The activity properties are set as follows



Note that I am looking for all .cs files and storing the result in a variable called "SourceFilesToStylecop", which takes the type IEnumerable. "Execute StyleCop" is the "Community TFS Build Extension activity" and it's properties are set as following




This completes our changes to the CustomTemplate.xaml file. Save it and check it in.

Adding required assemblies to TFS
At this stage, our custom build template is all ready to be used. But before running it, we need to make sure that the custom assemblies and any related files are added to the TFS folder which was set in the  "Version control path to custom assemblies" option for our build controller. In this case, the assemblies we would want to load are the TFS build extension assemblies, StyleCop assemblies and it's dictionary and settings file. My custom assemblies folder look as follows.


Now, we are all ready to use our custom build template.

Using Custom Template in Build Definition
Open your build definition, click on process, expand Build Process Template and click on New button.



In the New Process Template dialog, select the option "Select an existing XAML file" and browse to the CustomTemplate.xaml file checked-in earlier. Click OK




Now your custom build is ready to be used. Run a sample build and you would see StyleCop being executed in your build doing static code analysis.


Wednesday, 26 June 2013

Are my changes in the release?

One question often asked from a release manager is "Are my changes in the release". This happens often especially when it comes to the crunch i.e. near the release deadline or at times of sending a patch to fix of a urgent high priority issue.

Team Foundation Server behaves slightly differently in selecting changesets to include in the build based on the build trigger. So, if you are pondering that a changeset you were not expecting in the build is not part of it, it might be useful to look at the build trigger option.

There are 5 options to trigger a team build and here is how changesets are chosen in each build case:

Manual:
As named, the build with a manual trigger option will only occur a user queues a build manually. For builds triggered manually the executing build will fetch all the changes in the Active working folders until the point of time when the build fetches source files.

For example, let's say that the build was queued at 8:45 am but remained in the queue for 10 minutes So it started running at 8:55 am. Meanwhile, a couple of changes (changesets 11495 and 11497) were checked in at 8:47 am and 8:51 am respectively. The build would include both the changesets.

Continuous Integration :
If this trigger is selected, a build is queued every time a changeset is checked in. In this case, the build request also contains the changeset number that queued the build. For all CI builds, all changes in the Active working folder up to the changeset that queued the build would be in included in the build.

For example, if the build was queued at 8:45 am by a changeset 11495 and remained in the queue for 10 minutes i.e. it started running at 8:55 am. Meanwhile, another changeset 11497 was checked-in at 8:47 am. Another build was triggered. The first build would not include the changes present in the changeset 11497 even though the changes were already in source control at the time when the build was fetching source files.

Rolling Build:
With this trigger option, Team Foundation Server still triggers a build on a check-in but only if there are no build already in the queue or in progress. If a build is already in queue or in progress, it accumulates all changes and queues a new build once the first build is completed.6

Similar to Continuous Integration, the build request contains the changeset that queued the build and all changes up to that build number are included in the build.

For example, if the build was queued at 8:45 am by a changeset 11495 and remained in the queue for 10 minutes i.e. it started building at 8:55 am. Meanwhile, two changesets 11497 and 11498 were checked in checked-in at 8:47 am and 8:51 am respectively. Another build would be triggered after the first one is completed. The first build would not include changes in the changesets 11497 and 11498 even though the changes were in source control at the time when the build fetch source files.

Gated Check-in
With Gated check-in, a build is triggered for each changeset and only committed or check-in once the build is successful. With this option a build is queued for each changeset (assuming the option to merge and build gated builds is not selected).

Like Continuous integration, every build request has a changeset that triggered the build. However, unlike CI the build will include all changes which are check-in until the build fetches the source control and not when the build was queued.

For example, if the build was queued at 8:45 am by a changeset 11495 and remained in the queue for 10 minutes i.e. it started running at 8:55 am. Meanwhile, another changeset 11497 was checked-in at 8:47 am. Another build was triggered. The first build did not include changes in the changeset 11497 since they are checked-in yet. However, the second build did include changes checked-in as a result of the successful completion of previous build even though at the time of queuing the build these changes were not checked-in.

Schedule:
The scheduled build is triggered repeatedly on a particular day / time. For scheduled builds, TFS picks up all the changesets until the time when the build was queued.

For example, let's say that the build was queued at 8:45 am but remained in the queue for 10 minutes and start started running at 8:55 am. A couple of changesets 11495 and 11497 were checked in at 8:47 am and 8:51 am respectively. The build would NOT include both the changesets.

PS: To read more about the build triggers and the scenarios and the optimal scenarios to use them, read the Build Customization Guide produced by Microsoft ALM rangers.