How to remove runs with Legacy retention Model - azure-devops

I have runs which are retained due to Legacy retention model as below:
I'm not sure where it coming from. And I want to remove them but not going through each run and removing lease. Is there a way to do that?

I didn't found an settings on Azure DevOps to turn it off so I wrote powershell script which goes through each pipeline definition and then builds and remove Legacy Retention Mode lease and build with this lease.
$AzureDevOpsAuthenicationHeader = #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":your-pat-here")) }
$organization = "org-name"
$project = "project-name"
$definitionsUrl = "https://dev.azure.com/${organization}/${project}/_apis/build/definitions?api-version=6.0"
$definitions = Invoke-RestMethod -Uri $definitionsUrl -Method Get -Headers $AzureDevOpsAuthenicationHeader
foreach ($definition in $definitions.value)
{
Write-Host $definition.name -ForegroundColor Green
$pipelineUrl = "https://dev.azure.com/${organization}/${project}/_apis/pipelines/$($definition.id)/runs?api-version=6.0-preview.1"
# Invoke the REST call
$result = Invoke-RestMethod -Uri $pipelineUrl -Method Get -Headers $AzureDevOpsAuthenicationHeader
foreach ($run in $result.value) {
$leasesUrl = "https://dev.azure.com/${organization}/${project}/_apis/build/builds/$($run.id)/leases?api-version=6.1-preview.1"
$leasesResult = Invoke-RestMethod -Uri $leasesUrl -Method Get -Headers $AzureDevOpsAuthenicationHeader
$ownerId = "Legacy Retention Model"
foreach ($lease in $leasesResult.value) {
if($lease.ownerId -Match $ownerId){
$deleteLeaseUrl = "https://dev.azure.com/${organization}/${project}/_apis/build/retention/leases?ids=$($lease.leaseId)&api-version=6.0-preview.1"
Write-Host "Removing ${ownerId} from run $($run.id) (created date $($run.createdDate))" -ForegroundColor Blue
$leasesResult = Invoke-RestMethod -Uri $deleteLeaseUrl -Method Delete -Headers $AzureDevOpsAuthenicationHeader
Write-Host "Removed lease"
$deleteBuildUrl = "https://dev.azure.com/${organization}/${project}/_apis/build/builds/$($run.id)?api-version=6.0"
$leasesResult = Invoke-RestMethod -Uri $deleteBuildUrl -Method Delete -Headers $AzureDevOpsAuthenicationHeader
Write-Host "Removed build"
}
}
}
}

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

Updating release def results in error VS402903: The specified value is not convertible to type ReleaseDefinition

I am updating release definitions using the onpremise Azure DevOps API using the following powershell script:
$listurl="https://onpremdomain/{ogr}/{proj}/_apis/release/definitions?api-version=6.0"
$PAT="Personal access token"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))
#get the releases' ids.
$result = Invoke-RestMethod -Uri $listurl -Headers #{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
#loop the ids to get each release's definition
foreach($release in $result.value){
#get each release's definition
$definitionurl="https://onpremdomain/{ogr}/{proj}/_apis/release/definitions/$($release.id)?&api-version=6.0"
$releaseDefinition = Invoke-RestMethod -Uri $definitionurl-Headers #{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
#loop through each stage
foreach( $environment in $releaseDefinition.environments){
#loop through each tasks to find the task group
foreach($task in $environment.deployPhases.workflowTasks){
# change the 'taskId' to the taskId of your task group
if($task.taskId -eq "{taskId}"){
$task.version = "2.*" # update the taskgroup version to the newest version
}
}
}
$updateurl="https://onpremdomain/{ogr}/{proj}/_apis/release/definitions?api-version=6.0"
# update the release definition
Invoke-RestMethod -Uri $updateurl -Headers #{Authorization = "Basic {0}" -f $base64AuthInfo} -ContentType "application/json" -Method PUT -Body (convertto-json $releaseDefinition -Depth 100)
}
When Invoke-RestMethod in the last line is called I get the following error:
Invoke-RestMethod : {"$id":"1","innerException":null,"message":"VS402903: The specified value is not convertible to type ReleaseDefinition. Make sure it is convertible to type ReleaseDefinition and
try again.","typeName":"Microsoft.VisualStudio.Services.ReleaseManagement.Data.Exceptions.InvalidRequestException, Microsoft.VisualStudio.Services.ReleaseManagement2.Data","typeKey":"InvalidRequestE
xception","errorCode":0,"eventId":3000}
Any idea why this error occurs?
REST API doc:
Get a list of release definitions.
Get a release definition.
Update a release definition.
Power shell script
$listurl="https://vsrm.dev.azure.com/{Org name}/{Project name}/_apis/release/definitions?api-version=6.0"
$PAT="{PAT}"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))
#get the releases' ids.
$result = Invoke-RestMethod -Uri $listurl -Headers #{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
write-host $result.value.id
#loop the ids to get each release's definition
foreach($release in $result.value){
#get each release's definition
$definitionurl="https://vsrm.dev.azure.com/{Org name}/{Project name}/_apis/release/definitions/$($release.id)?api-version=6.0"
#write-host $definitionurl
$releaseDefinition = Invoke-RestMethod -Uri $definitionurl -Headers #{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
#loop through each stage
foreach( $environment in $releaseDefinition.environments){
#loop through each tasks to find the task group
foreach($task in $environment.deployPhases.workflowTasks){
write-host $task.taskId
# change the 'taskId' to the taskId of your task group
if($task.taskId -eq "e213ff0f-5d5c-4791-802d-52ea3e7be1f1"){
write-host $task.version
$task.version = "1.*" # update the taskgroup version to the newest version
write-host $task.version
}
}
}
$updateurl="https://vsrm.dev.azure.com/{Org name}/{project name}/_apis/release/definitions?api-version=6.0"
# update the release definition
Invoke-RestMethod -Uri $updateurl -Headers #{Authorization = "Basic {0}" -f $base64AuthInfo} -ContentType "application/json" -Method PUT -Body (convertto-json $releaseDefinition -Depth 100)
}
Update
I found the issue, the second Invoke-RestMethod method is missing the space before the -Headers parameter, and the issue should be Invoke-RestMethod : A positional parameter cannot be found that accepts argument 'System.Collections.Hashtable'.
After adding a space before the parameter, it works, you could check the pic below.

Get all users under all projects from Azure DevOps

I am trying to get all users under couple of project. The case is that under users there are many and I need to to use continuationToken. The result is messy because my logic is bad. I cannot figure out how to use the second foreach..
$outputItems = #()
foreach ($project in $projects) {
$uriProjectDescriptor = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/descriptors/$($project.id)?api-version=5.0-preview.1"
$projectDescriptor = Invoke-RestMethod -Uri $uriProjectDescriptor -Method Get -Headers $AzureDevOpsAuthenicationHeader
$descriptors = $projectDescriptor.value
foreach ($descriptor in $descriptors) {
do
{
$uriUsers="https://vssps.dev.azure.com/$OrganizationName/_apis/graph/users?continuationToken=$continuationToken&scopeDescriptor=$($descriptor)&api-version=6.0-preview.1"
$responseUsers=Invoke-WebRequest -Uri $uriUser -Method Get -ContentType "application/json" -Headers $AzureDevOpsAuthenicationHeader -UseBasicParsing -MaximumRedirection 0
$continuationToken = $responseUsers.Headers.'x-ms-continuationtoken'
$userSet = $responseUsers.content | ConvertFrom-Json
$users += $userSet.value.Count
$arealList = New-Object -TypeName PSObject -Property #{
CountUsers = $users.Count
} | Select-Object "CountUsers"
$outputItems += $arealList
$arealList = $null
}
while (($continuationToken))
$ProgressPreference = 'Continue'
}
} $outputItems
You could try the following Powershell Script:
$token = "PAT"
$url="https://dev.azure.com/{Organizationname}/_apis/projects?api-version=6.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$responses = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
ForEach ($response in $responses.value.id)
{
$url1="https://vssps.dev.azure.com/{Organizationname}/_apis/graph/descriptors/$($response)?api-version=6.0-preview.1"
$Descriptor = Invoke-RestMethod -Uri $url1 -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
ForEach ($Descriptor1 in $Descriptor.value)
{
do
{
$url2="https://vssps.dev.azure.com/{Organizationname}/_apis/graph/users?scopeDescriptor=$($Descriptor1)&api-version=6.0-preview.1"
$UserLists = Invoke-RestMethod -Uri $url2 -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
$continuationToken = $UserLists.Headers.'x-ms-continuationtoken'
}
while ($continuationToken -ne $null)
Write-Host "result = $($UserLists | ConvertTo-Json -Depth 100)"
}
}
Explaination:
The first Rest API is used to get the Project name.
Then the second Rest API is used to get the descriptors.
The third Rest api is used to get the userlist.
I use foreach nesting to realize the loop of response to Rest API.
Update:
Test with the continuationToken and the maximum value of objects returned by a project is 500.
I found another Rest Api User Entitlements - List to get the users.
https://vsaex.dev.azure.com/Organization Name/_apis/userentitlements?top=10000&select=project&api-version=4.1-preview.1
This Rest APi could directly return the users under the organization(Project Scope). The max value is 10000.

Scheduling a Azure DevOps release using Powershell

I am able to trigger a Azure DevOps release using Rest API call. But when i tried to do it by adding the scheduling it is not working. The code i used is given below:
$PATtoken= 'PAT'
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PATtoken)"))
$header=#{authorization= "Basic $token" }
$defurl = "https://vsrm.dev.azure.com/Orgnization/Project/_apis/release/definitions/13?api-version=5.1"
$definition = Invoke-RestMethod -Uri $defurl -Method Get -Headers $header
$triggers = '
[{
"schedule": {
"timeZoneId": "India Standard Time",
"startHours": 20,
"startMinutes": 40,
"daysToRelease": 31
}
}]'
$definition | Add-Member -NotePropertyName "triggers" -NotePropertyValue (Convertfrom-Json $triggers) -Force
$json = #($definition) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $defurl -Method Put -Body $json -ContentType "application/json" -Headers $header
Write-Host ($updatedef.triggers | ConvertTo-Json -Depth 99)
But unfortunately the scheduling or triggering is not happening.
You can do it in this way:
...
$definition = Invoke-RestMethod -Uri $defurl -Method Get -Headers $header
$hash = #(
#{ triggerType="schedule";
schedule = #{"daysToRelease"="31";"timeZoneId"="India Standard Time";"startHours"="20";"startMinutes"="40"}
}
)
$definition.triggers = $hash
$json = $definition | ConvertTo-Json -Depth 99
Invoke-RestMethod -Uri $defurl -Method Put -Body $json -ContnetType application/json -Headers $header
You should not add schedule into triggers to configure the release pipeline schedule execute.
You can use Postman as test. See below pic:
Even I have configure the schedule script in request body, the response body still has no content in the trigger.
To set the schedule, you need apply above schedule script to schedules which is nested under environment. Also, you could using Fiddler to capture the records to confirm this by configuring the schedule release from UI.

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"
}