The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

Azure Pipelines 無法刪除 Build Pipeline 的處理方法

有時候我們想要在 Azure DevOps Services 上面刪除一些早期的 Pipeline (Build),但是刪除時卻會遇到 One or more builds associated with the requested pipeline(s) are retained by a release. The pipeline(s) and builds will not be deleted. 的問題,導致我們怎樣都無法刪除。這篇文章我打算來說明一下幾個重要觀念,以及如何才能成功刪除這些用不到的 Pipelines。

發生錯誤的過程

  1. 刪除 Pipeline (Build)

    刪除 Pipeline

  2. 刪除時出現以下錯誤訊息

    One or more builds associated with the requested pipeline(s) are retained by a release. The pipeline(s) and builds will not be deleted.

    One or more builds associated with the requested pipeline(s) are retained by a release. The pipeline(s) and builds will not be deleted.

我簡單翻譯一下這段錯誤訊息:

  • One or more builds associated with the requested pipeline(s) are retained by a release.

    你所要求刪除的 pipeline(s) 的一個或多個 builds (建置) 被一個 release (發行) 所保留(retained)。

  • The pipeline(s) and builds will not be deleted.

    這些 pipeline(s) 以及 builds 將不會被刪除。

我們在 Azure DevOps Services 之中的重要觀念如下:

  1. 一個 pipeline 會包含一個或多個 builds (有時候 builds 也會說成 runs)

  2. 一個 release 可能會從 pipeline 取得 builds 來發行

  3. 所以 releasepipeline 產生的 build 之間會有一個或多個 leases(租約) 關係

  4. release 之中可以對 builds 設定 Retain (保留) 的條件

    Release - Retention

  5. 早期 release 的設定預設會永久保留所有的 builds,但現在的 release 最多只會保留 365 天,而且在專案設定中有明確的預設值設定!

    Project Settings - Retention policy settings

簡單來說,就是你只要有設定 release (CD),我們通常都會關聯一個 Pipelines (CI) 的 build (通常是最新版的 build artifact),所以這些 buildsruns 會預設保留下來一段時間,保留多久是由一條一條的 lease 決定的。重點是,只要這些 builds保留住,你就無法刪除 pipeline

如何才能順利刪除 Pipeline

事實上,就算你手動刪除 Release 也是無法解鎖這些 Builds 的,因為刪除 Release 之後會被放到「資源回收匣」達 30 天,所以關聯的 Builds 依然無法被刪除。

Deleted pipelines

所以重點就是如何讓這些 Builds 不要被保留(Retain),方法如下:

  1. 進入特定 Pipeline 並顯示所有 Builds,對每一個 Builds 選擇 View retention leases (檢視保留租約) 設定

    View retention leases

  2. 你可以在 Run retention 對話框看到這個 Build 的保留原因,並且可以按下 Remove all 刪除所有保留設定

    Run retention

  3. 當你把所有 Builds 的 Leases 都移除,就可以順利刪除 Pipeline 了!

    是的,就是要一筆一筆刪除,有點麻煩!

批次刪除 Leases 的方法

如果要批次刪除,只能透過 Leases - REST API (Azure DevOps Build) 來刪除了。

我已經寫好一份了,超好用:

  • 刪除特定專案所有 Pipelines (Build Definition) 底下所有的 builds 底下所有的 leases

    $personalAccessToken = "[PAT]"
    
    $organization = "[OrgId]"
    $project = "[ProjectId]"
    
    $apiVersion = "6.0"
    
    $token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)"))
    $header = @{authorization = "Basic $token"}
    
    function DeleteLease($definitionId) {
        $url = "https://dev.azure.com/$organization/$project/_apis/build/retention/leases?api-version=$($apiVersion)&definitionId=$definitionId"
        $leases = (Invoke-RestMethod -Method GET -Uri $url -ContentType "application/json" -Headers $header )
    
        foreach ($lease in $leases.value) {
            $leaseId = $lease.leaseId
            $url = "https://dev.azure.com/$organization/$project/_apis/build/retention/leases?ids=$($leaseId)&api-version=$($apiVersion)"
            $ignore = Invoke-RestMethod -Method DELETE -Uri $url -ContentType "application/json" -Headers $header
            Write-Host "DELETE $url"
        }
    }
    
    $url = "https://dev.azure.com/$organization/$project/_apis/build/definitions?api-version=$apiVersion"
    Write-Host $url
    
    $buildDefinitions = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $header
    
    foreach ($def in $builddefinitions.value) {
        Write-Host $def.id $def.queueStatus $def.name
        DeleteLease $def.id
    }
    
  • 刪除特定專案特定一個 Pipelines (Build Definition) 底下所有 buildsleases

    $personalAccessToken = "[PAT]"
    
    $organization = "[OrgId]"
    $project = "[ProjectId]"
    $definitionId = "[BuildDefinitionId]"
    
    $apiVersion = "6.0"
    
    $token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)"))
    $header = @{authorization = "Basic $token"}
    
    function DeleteLease($definitionId) {
        $url = "https://dev.azure.com/$organization/$project/_apis/build/retention/leases?api-version=$($apiVersion)&definitionId=$definitionId"
        $leases = (Invoke-RestMethod -Method GET -Uri $url -ContentType "application/json" -Headers $header )
    
        foreach ($lease in $leases.value) {
            $leaseId = $lease.leaseId
            $url = "https://dev.azure.com/$organization/$project/_apis/build/retention/leases?ids=$($leaseId)&api-version=$($apiVersion)"
            $ignore = Invoke-RestMethod -Method DELETE -Uri $url -ContentType "application/json" -Headers $header
            Write-Host "DELETE $url"
        }
    }
    
    $url = "https://dev.azure.com/$organization/$project/_apis/build/definitions?api-version=$apiVersion"
    Write-Host $url
    
    $buildDefinitions = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $header
    
    DeleteLease $definitionId
    

    你可以從網址列上取得 $definitionId,如下圖示:

    definitionId

相關連結