Update Azure Devops Build Pipeline Variable with Rest API - azure-devops

I have an Azure Devops YAML based Build (Not Release) Pipeline. I have defined a variable called Department. My Requirement is that this variable to be updated at the end of the build using the rest API. I'm using this code.
How to modify Azure DevOps release definition variable from a release task?
The API call works fine. But I'm not sure whether this is the correct API to call. The Department will change for each build. According to the output HTTP method put is not supported.
Note: I have actually defined 5 variables including Department, Department being the last one. When the API is called it only outputs first 3 variables only.
$Department = getDepartment.ps1
$url = "https://dev.azure.com/xxx/xxx/_apis/pipelines/$(System.DefinitionId)/runs?api-version=6.0-preview.1"
$pipeline = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
$pipeline.variables.Department.value = $Department
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
Write-host "=========================================================="
Write-host "The value of Variable 'Department' is updated to $( $updatedef.variables.Department.value)"
write-host "=========================================================="

The Department will change for each build. According to the output HTTP method put is not supported.
According to the document Pipelines:
It does not provide a method for update pipeline with PUT.
To resolve this issue, you still need use the REST API Build Definitions - Update:
PUT https://dev.azure.com/{organization}/{project}/_apis/build/definitions/{definitionId}?api-version=6.0
The code sample:
$url = "https://dev.azure.com/{organization}/{project}/_apis/build/definitions/{definitionId}?api-version=6.0"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
$pipeline.variables.Test.value = "$Department"
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
write-host "=========================================================="
Write-host "The value of Varialbe 'Test' is updated to" $updatedef.variables.Test.value
The test result:

When you start the pipeline using the API, you can add the values of the parameters that should be different from the default values, to the payload of the call. So you're not updating the definition, but just the value of the variables for that instance of the pipeline run.
Payload
$JSON = #"
{
"definition": {
"id":"207"
},
"parameters": "{ \"ENVIRONMENT\":\"$($config.testEnvironmentName)\", \"API_CLIENT_SECRET\":\"$($config.testEnvironmentApiClientSecret)\" }"
}
"#
Note that the 'parameters' property has a value as an escaped json string.
This is the API that I use to start the pipeline.
/_apis/build/builds?api-version=7.1-preview
Use a 'POST' method and send the json as the body of the request (content-type application/json).

Related

azure devops release pipeline , bulk edit

$Organization = "y"
$Project = "x"
$DefinitionID = "81"
$url = "https://dev.azure.com/$Organization/$Project/_apis/build/definitions/$DefinitionID?api-version=6.0"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
$pipeline.variables.v1030.value = "xyz"
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
write-host "=========================================================="
Write-host "The value of Varialbe 'v1030' is updated to"
$updatedef.variables.v1030.value
error:
2022-03-17T13:05:24.9981259Z Invoke-RestMethod : {"$id":"1","innerException":null,"message":"Build pipeline
2022-03-17T13:05:24.9982421Z 81 was not found.","typeName":"Microsoft.TeamFoundation.Build.WebApi.Definition
2022-03-17T13:05:24.9982939Z NotFoundException, Microsoft.TeamFoundation.Build2.WebApi","typeKey":"Definitio
2022-03-17T13:05:24.9983313Z nNotFoundException","errorCode":0,"eventId":3000}
Hi I get this error in Logs,
I think I can not find the release pipeline number
The $ DefinitionID
I want to do it on 100 releases pipelines I'll wrap it with 'for' later, but first I want to see I can do on one

How can I parse JSON from Azure DevOps work items in PowerShell

I have a PS script to get info about work items from the DevOps API. Specifically when I call the work item API there's a property returned called "fields". When I use ConvertFromJson it doesn't seem to be able to parse that property. Is there some way ConvertFromJson can be configured or do I need to do text parsing?
Here's the code:
$uri = "https://dev.azure.com/H.......s/B....o/_apis/build/builds/$env:RELEASE_ARTIFACTS_DOCMGMT_BUILDID/workitems"
Write-Host "Uri $uri"
Write-Host "AccessToken: $env:SYSTEM_ACCESSTOKEN"
$result = Invoke-WebRequest -Uri $uri -Method GET -Headers #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } -UseBasicParsing | ConvertFrom-Json
$itemId = $result.value[0].id
Write-Host $itemId
$uri = "https://dev.azure.com/H.....s/B....o/_apis/wit/workItems/$($itemId)?`$expand=All"
Write-Host "Uri $uri"
$result = Invoke-WebRequest -Uri $uri -Method GET -Headers #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } -UseBasicParsing | ConvertFrom-Json -Depth 5
Write-Host $result
Here's the screenshot of the output. Notice the "fields" property is empty?
Here's what the same call looks like in Postman

How to delete variable in azure devops release through powershell

I have azure DevOps release and I would like to delete release variables post one stage of deployment. could you please provide some pointers on how to do this? I know how to update the value of the variable using API but am unable to delete the variable. Any pointers are highly appreciated
To delete the release pipeline variable during the release process, you could use the Definitions - Update Rest API.
Here is the PowerShell example:
$url = "$($env:SYSTEM_TEAMFOUNDATIONSERVERURI)$env:SYSTEM_TEAMPROJECTID/_apis/Release/definitions/$($env:RELEASE_DEFINITIONID)?api-version=5.0-preview.3"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
$pipeline.variables.PSObject.Properties.Remove('variablename')
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
write-host "=========================================================="
Write-host "The value of Varialbe 'test' is deleted"
write-host "=========================================================="
The following code is used to delete the variable.
$pipeline.variables.PSObject.Properties.Remove('variablename')
For more detailed info, you could refer to this ticket: How to modify Azure DevOps release definition variable from a release task?

Set Azure devops Release pipeline variable using REST API

I am able to update the variable in the build pipeline using the below json body
$body = '
{
"definition": {
"id": 25
},
"parameters": "{\"var\":\"value\"}"
}
'
The same json is not working with Release pipeline . Is there any way to pass the variable through same way through release pipeline
Set Azure devops Release pipeline variable using REST API
We could use the REST API Definitions - Get to get all the info about this definition in the body, then we could update the body and use the (Definitions - Update) to update the value of the release definition variable from a release pipeline:
PUT https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions/{definitionId}?api-version=5.0
Following is my test inline powershell scripts:
$url = "https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions/{definitionId}?api-version=5.1"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Method Get -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
# Update an existing variable named TestVar to its new value 2
$pipeline.variables.TestVar.value = "789"
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
write-host "=========================================================="
Write-host "The value of Varialbe 'TestVar' is updated to" $updatedef.variables.TestVar.value
As test result, the variable TestVar updated to 789:
Update:
But I want to achieve it without updating\changing the definition
The answer is yes. You could use the Releases - Create with request body:
{
"definitionId": Id,
"environments": [
{
"variables": {
"TestVar": {
"value": "xxxx"
},
"TestVar2": {
"value": "xxxx"
}
},
}
],
}
For more information refer the post here.
Hope this helps.
Old topic, but there is a better way now and I believe it deserves a new answer (maybe it was even available since the very beginning, don't know.)
Instead of updating the very definition of the pipeline which only works for future releases, you can now update the currently running release only and that solves your problem.
This is how I set up the tasks in the pipeline:
And here's a snippet from the Powershell task:
(it updates delay_minutes release variable based on deploy_time variable which specifies time in HH:mm format)
if(!"$(deploy_time)") {
Write-Host "deploy_time empty, won't delay deployment"
return
}
$url = "$(System.TeamFoundationServerUri)/$(System.TeamProjectId)/_apis/release/releases/$(Release.ReleaseId)?api-version=5.0"
# Uncomment for debugging
# Write-Host "URL: $url"
$delayMinutes = [int](New-TimeSpan -start (Get-Date) -end "$(deploy_time)").TotalMinutes
if($delayMinutes -lt 0) { $delayMinutes = 0 }
$pipeline = Invoke-RestMethod -Uri $url -Method Get -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
# Uncomment for debugging
# Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
$pipeline.variables.delay_minutes.value = $delayMinutes
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
The URL in the snippet uses only always available predefined variables so it should be 100% copy-pastable.
Also make sure to set this on the first agent job:
So that the SYSTEM_TOKEN variable is available in the script.

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.