Every so often I discover a new or forgotten feature. Today was such a day when I was tasked to figure out a way to make 50+ of the same build definitions on different VSO projects. The way I used to address this is was to write a piece of C# and program against the TFS API. Today I discovered the new Build 2.0 API REST service where I can use a JSON file to set up a new build definition. the 1.0 version was only used to fire GET requests but in 2.0 it’s possible to use POST as well.
I found the VSO REST API Reference to get more insights about the REST service and some examples. How great would it be to use PowerShell to set up a build definition and later to combine it with actions such as branching in SourceControl.
https://{account}.visualstudio.com/defaultcollection/{project}/_apis/build/definitions?api-version={version}
To use this in a PowerShell script I only need to use the Invoke-RestMethod to get it running.
Let’s figure this out
First problem I encounter is how to log into VSO, I don’t want to enter my credentials every time. To solve this I need to enable “alternative authentication credentials” for the account I want to use. This enables me to log in via username password via the REST service. I can also use the newly introduced personal access tokens (you can read more about that in this post of Rene van Osnabrugge).
Now that’s out of the way I can start to setup the header for the Invoke-RestMethod. This method needs the credentials to authenticate via the just enabled Alternate Authentication Credentials.
$username = "MyTest" $password = "P@ssw0rd!" $basicauth = ("{0}:{1}"-f $username, $password) $basicauth = [System.Text.Encoding]::UTF8.GetBytes($basicauth) $basicauth = [System.Convert]::ToBase64String($basicauth) $header = @{Authorization=("Basic {0}" -f $basicauth)}
With the just created header I can now invoke the REST service and get all kinds of information about build definitions on my VSO. For instance to retrieve a specific build definition I used.
$response = Invoke-RestMethod -uri https://$vsoAccount/defaultcollection/$teamProject/_apis/build/definitions/3?api-version2.0") -Headers $header -Method Get
Always use the api-version parameter to specify the service, otherwise you could run into code breaks when Microsoft releases new versions. The /3 refers to the id of an already existing build definition, this makes it possible to convert the object to JSON, it realy helps to get the JSON part right when you have an example. Make sure you use -Depth in your conversion the default value is 2 and the API doesn’t accept the JSON back unless its entirely there.
$json = ConvertTo-Json -InputObject $response -Depth 999
I now have a starter JSON file and can change whatever I feel necessary. A few things to point out:
- Under build there is an array of tasks. Each have their own GUIDS and properties, the easiest way to figure this out is to make a build definition and retrieve them from VSO via a GET command.
"build": [ { "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Build solution **\\*.sln", "task": { "id": "71a9a2d3-a98a-4caa-96ab-affca411ecda", "versionSpec": "*" }, "inputs": { "solution": "**\\*.sln", "msbuildArgs": "", "platform": "$(platform)", "configuration": "$(config)", "clean": "false", "restoreNugetPackages": "true", "vsLocationMethod": "version", "vsVersion": "latest", "vsLocation": "", "msbuildLocationMethod": "version", "msbuildVersion": "latest", "msbuildArchitecture": "x86", "msbuildLocation": "", "logProjectEvents": "true" } } ]
- Under repository ther is SourceControl data including mapping files. The repository section is different for Git so be aware of this.
"repository": { "properties": { "labelSources": "0", "labelSourcesFormat": "$(build.buildNumber)", "tfvcMapping": "{\"mappings\":[{\"serverPath\":\"$/MyTrial\",\"mappingType\":\"map\",\"localPath\":\"\\\\\"},{\"serverPath\":\"$/MyTrial/Drops\",\"mappingType\":\"cloak\",\"localPath\":\"\\\\\"}]}" }, "id": "$/", "type": "TfsVersionControl", "name": "MyTrial", "url": "https://xxx.visualstudio.com/DefaultCollection/", "defaultBranch": "$/MyTrial", "rootFolder": "$/MyTrial", "clean": "undefined", "checkoutSubmodules": false }
After my modifications I can pass the JSON-file back in the $JSON and use the statement below to create the new build definition.
Invoke-RestMethod -uri https://$vsoAccount/defaultcollection/$teamProject/_apis/build/definitions?api-version=2.0 -Headers $header -Method Post -Body $json -ContentType "application/json"
The end result is great, the build definition fully set up the way I expected.
Wat’s next?
This is a very simple example but imagine what I could do with this technique in combination with Azure Resource Manager. Whenever someone makes a feature branch based on a PBI it would be possible to spin up a corresponding build, release and a test environment via Azure Resource Manager. It’s still easier said then done but it could be possible in my opinion. I will certainly give this concept a try, stay tuned for more.
Till next time!
Links
Visual Studio Integrate (https://www.visualstudio.com/en-us/integrate/api/build/definitions )
My JSON file (http://www.ericksegaar.com/wp-content/uploads/2015/10/BuildDefinition.zip)
RoadToALM by Rene van Osnabrugge (http://roadtoalm.com)
Final powershell CreateBuildDefinition
Updated
Added full powershell file to put a JSON builddefinition to VSO in links.
Unfortunately the Final powershell link does not work:( Can you fix it?
Apart from that very good article!
Thnx for the feedback. I fixed the link.
Thanks, download link works now. I plan to expand your idea a bit. My issue is that I need to keep in sync ~20 similar build definitions and I don’t want to do it manually. Those definitions are basically the same, and differ only by disable\enable task and some variable values. I am going to prepare a template build definition, save it’s .xml, tokenize this xml and then use the API to create/update build definitions based on template and token values.
Hi Erick,
I also need to create a build using VSTS REST API but using typescript. I saw your recent post regarding creating VSTS extension using typescript.
There is “createDefinition” method in Rest client which takes parameter of type “BuildDefinition”. The problem is that this interface is so deeply nested that I end up setting hundred of properties to create a simple build.
Would it be possible for you to shed some light on it?
Thank You.
What is the reason you need to use the rest api. Isn’t it more easy to use YAML implementation in GIT on VSTS? Perhaps this post will help you Json via rest to create builddefinitions
I believe YAML only supports Git and GitHub, it does not support TFVC, BitBucket, Extenal Git and SVN. I need support at least for TFVC.
I able to create build using powershell script as you mentioned however when I tried using same JSON in Typescript, I am getting some weird errors.
Nevertheless I can move forward with Powershell script. To better manage build definition, can I create an object using JSON so that I can manipulate properties as per requirement and later again convert to JSON. Is it possible in powershell? I am a bit new in this technology.
Many thanks to you for your prompt replies.
You can also look into “Save as Template” option in your build definition properties. This will enable you to use a already made build as a template for new builds. Might be easier than the JSON Create build definition.
With ConvertTo-Json and ConvertFrom-Json you can transform powershell object into and from json. remember to include the -depth 99 option because default is 2 I believe.