I’ll show how to use the MS Release Management REST API from Powershell to start a new Release, so that you can have a build script (or any external process, really) start a release without relying on the TFS-Release Management integration.
Our goal is to end up with a powershell script that starts a new release based on:
– The release template name
– The build name
– And the name of the target stage to deploy to
We’ll be using MS Release Management 2013, Update 4 with the vNext templates doing an on-premise deployment. The components should be configured as “builds externally”.
This post exands on what Anand Gaurav has written on this subject in Trigger Release from build with Release Management for Visual Studio 2013.
Overview
MS Release Management provides a set of REST API endpoints that are used by the MSRM Client. These are hosted in the Release Management website in IIS and can be reached via:
http://<rm server>:1000/account/releaseManagementService/_apis/releaseManagement/<servicename>
The most important of these for our purposes is the Orchestrator service, as it allows us to actually create the release via the InitiateRelease
operation. The signature of this operation is as follows:
../OrchestratorService/InitiateRelease?releaseTemplateName=[release template name]&deploymentPropertyBag=[property bag]&api-version=3.0
…where the property bag is a url-encoded JSON dictionary with the details about this release. The following diagram shows how the MSRM dialog maps to the InitiateRelease
parameters:
As you can see, it requires an internal identifier (the TargetStageId), as well as the names and builds of all Components so MSRM knows which packages to deploy. Hardcoding these values is one option, but luckily, all this information can also be obtained through the REST API, which is what I’m going to demonstrate below.
Starting a release from Powershell
To start off with, we’ll define variables for who will request the release and what we want to release:
$credentials = Get-Credential -Message "Enter credentials" -UserName "RMSERVER\leon" $rmServer = "localhost" $releaseTemplateName = "Acme" $targetStageName = "Acceptance stage" $build = "v1.0 build 56"
Then we define the endpoint urls:
$baseEndpointUrl = "http://$($rmServer):1000/account/releaseManagementService/_apis/releaseManagement" $configurationServiceUrl = "$baseEndpointUrl/ConfigurationService" $releaseDefinitionServiceUrl = "$baseEndpointUrl/ReleaseDefinitionService" $orchestratorServiceUrl = "$baseEndpointUrl/OrchestratorService"
Our first call to MSRM will be to translate the calling user to an internal UserId, e.g. “9004”.
$escapedUsername = [System.Uri]::EscapeDataString($credentials.UserName) $responseXml = Invoke-RestMethod -Method Post -Credential $credentials ` -Uri "$configurationServiceUrl/GetUserByUserName?userName=$escapedUsername&api-version=3.0" $userId = $responseXml.Result.User.Id
Next, we’ll ask MSRM for all active ReleaseDefinitions that this user can release, and use it to translate the $releaseTemplateName
into an ApplicationVersionId
and a ReleasePathId
. An example response of this method is included after the code fragment.
$body = "<Filter StatusId='2' IsDeleted='0' UserId='$userId' />" $responseXml = Invoke-RestMethod -Method Post -Credential $credentials ` -Uri "$releaseDefinitionServiceUrl/ListReleaseDefinitions?api-version=3.0" -Body $body $applicationVersionElt = $responseXml.ApplicationVersionList.ApplicationVersion | Where-Object { $_.Name -eq $releaseTemplateName } $applicationVersionId = $applicationVersionElt.Id $releasePathId = $applicationVersionElt.ReleasePathId
Using the $applicationVersionId
, we can get a list of all Components that are used in this release template:
$body = "<Filter ApplicationVersionId='$applicationVersionId' LockRequested='1' LockRequestedById='$userId' />" $responseXml = Invoke-RestMethod -Method Post -Credential $credentials ` -Uri "$configurationServiceUrl/GetApplicationVersion?api-version=3.0" -Body $body $componentNames = $responseXml.ApplicationVersion.Components.Component.Name
Because we need the Id of the Stage we want to target, let’s request the Release Path details and look it up by name:
$responseXml = Invoke-RestMethod -Method Post -Credential $credentials ` -Uri "$configurationServiceUrl/GetReleasePath?id=$releasePathId&api-version=3.0" $targetStageElt = $responseXml.ReleasePath.Stages.Stage | Where-Object { $_.StageTypeName -eq $targetStageName } $targetStageId = $targetStageElt.Id
Now we have all the required information to compose the JSON property bag:
$releaseName = "Release: $([DateTime]::Now.ToString('G'))" $genericProperties = @( """ReleaseName"":""$releaseName""", """ReleaseBuild"":null", """ReleaseBuildChangeset"":null", """TargetStageId"":""$targetStageId""" ) $componentProperties = ($componentNames | ForEach-Object { """$($_):Build"":""$build""" }) $json = "{" + ($genericProperties + $componentProperties -join ",`n") + "}"
Finally, we can call InitiateRelease with this information (but be sure that the passed credentials have permission to start a release). If all went well, it will start the Release and return a release Id with which we can monitor progress.
$escapedReleaseTemplateName = [System.Uri]::EscapeDataString($releaseTemplateName) $propertyBag = [System.Uri]::EscapeDataString($json) $releaseId = Invoke-RestMethod -Method Post -Credential $credentials ` -Uri "$orchestratorServiceUrl/InitiateRelease?releaseTemplateName=$escapedReleaseTemplateName&deploymentPropertyBag=$propertyBag&api-version=3.0"
By the way, I have no idea why the propertyBag is passed on the url instead of in the HTTP body, as it can produce some nasty errors when the query string gets too long.
For good measure, lets verify that the release is actually in progress:
$statusId = Invoke-RestMethod -Credential $credentials ` -Uri "$orchestratorServiceUrl/ReleaseStatus?releaseId=$releaseId" $statusMapping = @("?", "NotStarted", "InProgress", "Released", "Stopped", "Rejected", "Abandoned" ) $statusMapping[$statusId]
That’s all there is to it, really!
3 comments
Thanks, tried a lot of tutorials but this one works great.
One remark: i found the build name under Configure Apps, Release Template, click on New Release and select “Latest”. Then copy the Build name and paste it in the script.
RalJans
Thanks, this script is great. Do you have an update to this script for Release Management for Visual Studio 2015? I found the version needed to be updated to 7.0 and InitiateRelease is now CreateRelease but I am having issues with formatting. I am having issues with formatting.
Chris Cochran
Since Release Management 2013 I’ve switched to the TFS2015 Release Management which in my experience is just a more robust and user-friendly product all around, if you have the opportunity I would recommend you do the same 🙂
And it too can accessed via a REST API (e.g. see https://blogs.infosupport.com/modifying-tfs2015-build-definitions-using-powershell/ and https://www.visualstudio.com/en-us/docs/integrate/api/overview).
Léon Bouquiet