0
votes

I'm attempting to update my build definitions in Azure DevOps using the REST API via a PowerShell script...

$header = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)"))}
$definitions = Invoke-RestMethod -Uri "https://devops.domain.com/Collection/Project/_apis/build/definitions" -Method GET -Header $header
$branchNames = 'master', 'feature'

ForEach ($definition in $definitions.value) {
    $definition | Add-Member -NotePropertyName triggers -NotePropertyValue (@{ triggerType = 'continuousIntegration'; branchFilters = $branchNames | % {"+refs/heads/$_/*"} }) -Force

    $body = $definition | ConvertTo-Json
    Write-Host $body

    Invoke-RestMethod -Uri "https://devops.domain.com/Collection/Project/_apis/build/definitions/$($definition.id)?api-version=5.0" -Method PUT -ContentType application/json -Body $body -Header $header
}

It's not particularly clear from the Azure DevOps documentation how I should update the build definition using this method, but the above results in the following error:

Invoke-RestMethod : {"$id":"1","innerException":null,"message":"Value cannot be null.\r\nParameter name: definition.Repository","typeName":"System.ArgumentNullException, mscorlib","typeKey":"ArgumentNullException","errorCode":0,"eventId":0}

This is where I'm wondering if I'm barking up the wrong tree as this should surely be simpler (I found a simple solution on SO here for creating a new build definition). In fact, all I want to do is update the trigger branch filters.

How do I achieve this using PowerShell and the REST API?

3
Try to add -Depth 10 to $body = $definition | ConvertTo-JsonShayki Abramczyk
@ShaykiAbramczyk Nope, doesn't change anything.Neo
If I did it: $body = $definition | ConvertTo-Json -Depth 10 I didn't get your error (but the triggers didn't update also, but it another issue.. you need to pass them correctly)Shayki Abramczyk
@ShaykiAbramczyk, the error occurs on the Invoke-RestMethod call. But the question is, how do I update the trigger branch filters using PowerShell and the REST API? It's not another issue, it's part of the issue.Neo
I understood, I succeeded to update the triggers :) check my answer.Shayki Abramczyk

3 Answers

3
votes

The triggers is array so you can't just edit it, you need to edit the triggers[0], the same thing it's the branchFilters, you need to edit the branchFilters[0]. also, you don't need to touch in the triggerType.

All the above it's assuming that there is already a trigger in the build and you want to edit it, not add a new trigger section.

There is also a tricky thing in the branchFilters array, if you have only 1 branch (master for example) and you want to add another branch you need to add it to the array and not just edit the branchFilters[0] value.

Last thing that should be fixed is the branches value, it should be +refs/heads/branchName and not just the branch name.

So, I have a pipeline with test branch trigger and I succeeded to edit the trigger to master and feature/* with this script:

# I get only one definition and update him, not iterate all my definitions
$definition = Invoke-RestMethod -Uri $url -Method Get

# Change the branch trigger from "test" to "master"
$definition.triggers[0].branchFilters[0] = "+refs/heads/master"

# Add another branch trigger - "feature/*"
$definition.triggers[0].branchFilters[0] += "+refs/heads/feature/*"

$body = $definition | ConvertTo-Json -Depth 10
Write-Host $body

Invoke-RestMethod -Uri $url -Method Put -ContentType application/json -Body $body
2
votes

It appears that the definitions received from the list method cannot be used directly with the update method. This is quite clear in the list response type BuildDefinitionReference which doesn't include properties such as triggers. The definitions must be obtained from the get method using the definition IDs from the list method. This returns a BuildDefinition which does indeed have the triggers property. This can then be modified and passed to the update method.

This is the working code:

$header = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)"))}
$definitions = Invoke-RestMethod -Uri "https://devops.domain.com/Collection/Project/_apis/build/definitions" -Method GET -Header $header
$branchNames = 'master', 'feature'

ForEach ($definition in $definitions.value) {
    $definitionToUpdate = Invoke-RestMethod -Uri "$($collection)$($project.name)/_apis/build/definitions/$($definition.id)" -Method GET -Header $header
    $trigger = $definitionToUpdate.triggers | Where {$_.triggerType -eq 'continuousIntegration'}

    if ($trigger) {
        $trigger.branchFilters = $branchNames | % {"+refs/heads/$_/*"}
        Invoke-RestMethod -Uri "https://devops.domain.com/Collection/Project/_apis/build/definitions/$($definition.id)?api-version=5.0" -Method PUT -ContentType application/json -Body ($definitionToUpdate | ConvertTo-Json -Depth 10) -Header $header
    }
}

The code checks that the CI trigger exists before updating its branch filters.

1
votes

Here is slide correction that works for me,

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$token = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)"))
$headers.Add("Authorization", "Basic $token")
$headers.Add("Content-Type", "application/json")
$definitions = Invoke-RestMethod -Uri "https://devops.domain.com/Collection/Project/_apis/build/definitions" -Method GET -Headers $headers

ForEach ($definition in $definitions.value) {
    $definition.triggers = (@{ triggerType = 'continuousIntegration'; branchFilters = 'master', 'feature/*' })
    $definition.revision++

    $body = $definition | ConvertTo-Json
    Write-Host $body

    Invoke-RestMethod -Uri "https://devops.domain.com/Collection/Project/_apis/build/definitions/$($definition.id)?api-version=5.0" -Method PUT -ContentType application/json -Body $body -Headers $headers
}