TFS2017.2 build definition update with REST API - custom process variable - powershell

I have a simple build definition without build steps and with just a custom process variable.
Trying to update the variable through REST API with PowerShell:
$definition = Invoke-RestMethod -Method Get -Uri $url -UseDefaultCredentials
$definition.variables.aaa = "xxx"
$j = ConvertTo-Json -Depth 3 $definition
Invoke-RestMethod -Method Put -Uri $url -Body $j -ContentType "application/json" -UseDefaultCredentials
will cause the removal of the variable from the build definition.

You should update aaa's value bbb not the variable aaa itself.
$definition.variables.aaa.value = "xxx"

Related

Powershell script Trigger an release pipeline using Devops API

I am trying to create a Powershell script create a new release using DevOps API.
I can see the pipeline information using invoke rest method but not able to trigger a pipeline. Can I get some assistance here.?
Thanks,
Venkatraman
It's not documented very well, but to start the release you have to update the status to inProgress.
$updateReleaseUri = "$($vrsmBaseUri)_apis/Release/releases/$($releaseId)/environments/$($environmentId)?api-version=6.0-preview"
$updateReleaseJsonBody = #{status = 'inProgress' }
$updateReleaseJsonBody = $updateReleaseJsonBody | ConvertTo-Json -Depth 100
Invoke-RestMethod -Uri $updateReleaseUri -Method Patch -Headers $headers -Body $updateReleaseJsonBody -ContentType 'application/json'
Ref: https://learn.microsoft.com/en-us/rest/api/azure/devops/release/releases/update-release-environment?view=azure-devops-rest-6.0
You can use the specific endpoint "Releases - Create" to trigger the release pipeline.
The required parameter you need to provide at least to this endpoint is the definitionId of the release pipeline that you want to trigger.
Below is the complete PowerShell script of a demo as reference. I have tested with this script, and it can work well as expected.
$pat = '{Personal Access Token}'
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", $pat)))
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", ("Basic {0}" -f $base64AuthInfo))
$headers.Add("Content-Type", "application/json")
$uri = "https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/releases?api-version=6.0"
$body = '{ "definitionId": {definitionId} }'
Invoke-RestMethod -Uri $uri -Headers $headers -Body $body -Method POST | ConvertTo-Json -Depth 10

Call parameters in Powershell script in Jenkins

I need to run a powershell script in a function called by Jenkins. When calling this function, two other parameters/variables are included. This is a sample of my code:
powershell '''
$Headers = #{"ApiKey"="$env:myKey"}
$jsonBody = #{
varOne= '$env:params.varOne'
varTwo = '$env:params.varTwo'} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Method Post -Uri "myUrl" -Headers $Headers -Body $jsonBody
'''
This is throwing a 'Bad Response' error. Note that if I hard code the values that are in the variable, the script works.
I also try to wrap the script with withEnv but I got the same issue:
withEnv(["varOne=${params.varOne}, varTwo=${params.varTwo}"]) {
powershell '''
$Headers = #{"ApiKey"="$env:myKey"}
$jsonBody = #{
varOne= '$env:params.varOne'
varTwo = '$env:params.varTwo'} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Method Post -Uri "myUrl" -Headers $Headers -Body $jsonBody
'''
}
Finally, I know I could call these variables successfully if I was using double quote instead of single ones
powershell """
some ps1 script
"""
However, when I do that it says:
groovy.lang.MissingPropertyException: No such property: Headers
If you're going to template variables you need """ as I said in the question you deleted, but you need to escape the $ on non templated variables
powershell """
\$Headers = #{"ApiKey"="$env:myKey"}
\$jsonBody = #{
varOne= '$env:params.varOne'
varTwo = '$env:params.varTwo'} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Method Post -Uri "myUrl" -Headers \$Headers -Body \$jsonBody
"""

How to check if a previous release has been completed before deploying a new release

As part of our release pipeline we have a task (the last task) to merge the release branch back to master.
I was wondering whether there is a way to check that this task or the previous release has completed before allowing the new release to be queued. Can a gate be used for this?
Ideally, the release manager would then be able to decide whether they want to continue with the release or to cancel.
You can't use the Invoke Rest API gate with an Azure DevOps API url because for checking the last release status you need to check the environment (stage) status, and for this, you need to the release id (so you can't know what it will be and put it in the rest API gate URL).
But, you can use PowerShell to check the last release and if it is not succeeded just fail the stage.
Add a PowerShell task in your release to check the last release:
$headers = #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
# Replace {org} with your organization
# Replace {project} with your project
# Replace {defId} with your release definition id
$url = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases?definitionId={defId}&api-version=5.1"
$releases = Invoke-RestMethod -Method Get -Uri $url -Headers $headers -ContentType 'application/json'
$releaseUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases/$($releases.value[1].id)?api-version=5.1"
$releaseInfo = Invoke-RestMethod -Method Get -Uri $releaseUrl -Headers $headers -ContentType 'application/json'
$releaseEvnriomentId = $releaseInfo.environments.Where({ $_.name -eq 'THE STAGE NAME WHERE YOU DO MERGE' }).id
$envUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/Release/releases/$($releases.value[1].id)/environments/$($releaseEvnriomentId)?api-version=5.1-preview.1"
$environment = Invoke-RestMethod -Method Get -Uri $envUrl -Headers $headers -ContentType 'application/json'
$envStatus = $environment.status
if($envStatus -ne "succeeded")
{
Write-Error "Previous release not succeeded!"
}
else
{
Write-Host "Previous release succeeded :)"
}
In the agent job options you need to allow scripts to access the OAuth token:
Azure functions also support PowerShell so you do it also with Azure functions gate:
1) Create a new Azure Function with VS Code like explained here.
2) In your run.ps1 file replace the code to this code:
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
$defnitionId = $Request.Query.DefinitionId
# Generate PAT and put it in the {YOUR PAT}
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,"{YOUR PAT}")))
$headers = #{Authorization=("Basic {0}" -f $base64AuthInfo)}
# Replace {org} with your organization
# Replace {project} with your project
$url = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases?definitionId=$($defnitionId)&api-version=5.1"
$releases = Invoke-RestMethod -Method Get -Uri $url -Headers $headers -ContentType 'application/json'
Write-Debug $releases
$releaseUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases/$($releases.value[1].id)?api-version=5.1"
$releaseInfo = Invoke-RestMethod -Method Get -Uri $releaseUrl -Headers $headers -ContentType 'application/json'
Write-Debug $releaseInfo
$releaseEvnriomentId = $releaseInfo.environments.Where({ $_.name -eq 'THE STAGE NAME WHERE YOU DO MERGE' }).id
$envUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/Release/releases/$($releases.value[1].id)/environments/$($releaseEvnriomentId)?api-version=5.1-preview.1"
$environment = Invoke-RestMethod -Method Get -Uri $envUrl -Headers $headers -ContentType 'application/json'
Write-Debug $environment
$envStatus = $environment.status
Write-Debug $envStatus
if($envStatus -ne "succeeded")
{
$status = [HttpStatusCode]::BadRequest
$body = "failed"
}
else
{
$status = [HttpStatusCode]::OK
$body = "success"
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = $status
Body = $body
})
3) Publish the function to Azure.
4) Create an Invoke Azure Function gate in your release:
Another option, take the above code, convert him to C# or another language ans use Rest API, deploy to it web server and use the Invoke Rest API gate.

how to update only one field in body request?

I'm trying to update a release pipeline's description using the provide Rest API documentation.
How should I send a 'Put' request in order to update a field?
I've tried to manually modify and capture the request using sniffer, it seems that my code does exactly the same.
$theBody = ConvertTo-Json #{description='Added a description'}
$instance = "tfs:8080"
$collection = "Collection"
$project = "myProject"
$releaseID = 1234
$apiVersion = "?api-version=4.1-preview.6"
$URI = "http://"+$instance+"/"+$collection+"/"+$project+"/_apis/release/releases/"+$releaseID+$apiVersion
$res= Invoke-RestMethod -Method Put -Uri $URI -UseDefaultCredentials -Body $theBody -ContentType 'application/json'
write-output $res
I'm getting an error message:
the Id of the Release does not match the Id of the original release
resource. Make sure that you are trying to update the correct resource
The best way to do it is to get the release with the same URL but with Get method (and without body):
$release = Invoke-RestMethod -Method Get-Uri $URI -UseDefaultCredentials -ContentType 'application/json'
Then modify the description:
$release.description = "Added a description"
Convert the release to JSON:
$theBody = $release | ConvertTo-Json -Depth 10
And do the Put:
$res = Invoke-RestMethod -Method Put -Uri $URI -UseDefaultCredentials -Body $theBody -ContentType 'application/json'

How can I cancel & delete a waiting build in the queue using PowerShell

Due to long running builds various next in line builds take longer time to execute.
Is there a way I can cancel and delete waiting build in queue and give way for latest triggered build using PowerShell or REST API's?
The following code snippet goes over all TFS builds,Gets the Builds which in Progress and not started, and then, cancel them.
$tfsUrl = "http://{server}:{port}/{organization}/{collection}/{project}" # TFS Base URL
$BuildDefsUrl = "$tfsUrl/_apis/build/definitions?api-version=2.0" # TFS build definitions URL
$BuildsUrl = "$tfsUrl/_apis/build/builds" #TFS Builds URL
$Builds = (Invoke-RestMethod -Uri ($BuildDefsUrl) -Method GET -UseDefaultCredentials).value | Select id,name # get all builds
#for filtering use : | Where-Object {$_.name -like "*Your Pattern*"}
foreach($Build in $Builds)
{
$command = "$($BuildsUrl)?api-version=3.2-preview.3&resultFilter=inprogress&definitions=$($Build.id)&queryOrder=finishTimeDescending"
$Ids = (((Invoke-RestMethod -Method Get -Uri $command -UseDefaultCredentials).value) | where status -like "*notStarted*").id # get waiting builds id's
foreach($id in $Ids)
{
$uri = "$($BuildsUrl)/$($id)?api-version=2.0" # TFS URI
$body = '{"status":4}' # body
$result = Invoke-RestMethod -Method Patch -Uri $uri -UseDefaultCredentials -ContentType 'application/json' -Body $body -Verbose #cancel build
}
}
The above example is pretty old. The code snippet below working for Azure Devops-
$PATToken = "PAT_GOES_HERE"
$AuthHeader= #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($PATToken)")) }
$azureDevops = "https://dev.azure.com/{organization}/{project}"
$BuildsUrl = "$azureDevops/_apis/build/builds"
$filterBuilds = "$($BuildsUrl)?statusFilter=notStarted&api-version=6.0"
(Invoke-RestMethod -Method Get -Uri $filterBuilds -Headers $AuthHeader).value | % {
$uri = "$($BuildsUrl)/$($_.id)?api-version=6.0" # Azure Devops URI
$body = '{"status":4}' # body
$result = Invoke-RestMethod -Method Patch -Uri $uri -Headers $AuthHeader -ContentType 'application/json' -Body $body -Verbose #cancel build
Write-Output "$($_.definition.name) cancaled"
}