powershellazure-devopsazure-devops-rest-apibuild-definitionbuild-triggers

Update build definition using Azure DevOps REST API in PowerShell


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?


Solution

  • 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.