Pipeline Run Retention
Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020
Retaining a pipeline run for longer than the configured project settings is handled by the creation of retention leases. Temporary retention leases are often created by automatic processes and more permanent leases by manipulating the UI or when Release Management retains artifacts, but they can also be manipulated through the REST API. Here are some examples of tasks that you can add to your yaml pipeline for retention.
Prerequisites
By default, members of the Contributors, Build Admins, Project Admins, and Release Admins groups can manage retention policies.
Example: Override a short project-level retention window
In this example, the project is configured to delete pipeline runs after only 30 days.
If a pipeline in this project is important and runs should be retained for longer than 30 days, this task ensures the run is valid for two years by adding a new retention lease.
- task: PowerShell@2
condition: and(succeeded(), not(canceled()))
name: RetainOnSuccess
displayName: Retain on Success
inputs:
failOnStderr: true
targetType: 'inline'
script: |
$contentType = "application/json";
$headers = @{ Authorization = 'Bearer $(System.AccessToken)' };
$rawRequest = @{ daysValid = 365 * 2; definitionId = $(System.DefinitionId); ownerId = 'User:$(Build.RequestedForId)'; protectPipeline = $false; runId = $(Build.BuildId) };
$request = ConvertTo-Json @($rawRequest);
$uri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases?api-version=6.0-preview.1";
Invoke-RestMethod -uri $uri -method POST -Headers $headers -ContentType $contentType -Body $request;
Question: can a pipeline be retained for less than the configured project values?
No, leases don't work in the reverse. If a project is configured to retain for two years, pipeline runs the system won't remove runs for two years. To delete these runs earlier, manually delete them or use the equivalent REST API.
Example: Only runs on branches named releases/*
should be retained for a long time
This is similar to above, only the condition needs to change:
- task: PowerShell@2
condition: and(succeeded(), not(canceled()), startsWith(variables['Build.SourceBranch'], 'releases/'))
name: RetainReleaseBuildOnSuccess
displayName: Retain Release Build on Success
inputs:
failOnStderr: true
targetType: 'inline'
script: |
$contentType = "application/json";
$headers = @{ Authorization = 'Bearer $(System.AccessToken)' };
$rawRequest = @{ daysValid = 365 * 2; definitionId = $(System.DefinitionId); ownerId = 'User:$(Build.RequestedForId)'; protectPipeline = $false; runId = $(Build.BuildId) };
$request = ConvertTo-Json @($rawRequest);
$uri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases?api-version=6.0-preview.1";
Invoke-RestMethod -uri $uri -method POST -Headers $headers -ContentType $contentType -Body $request;
Example: Updating the retention window for a multi-stage pipeline based on stage success
Consider a two-stage pipeline that first runs a build and then a release. When successful, the Build
stage retains the run for three days, but the project administrator wants a successful Release
stage to extend the lease to one year.
The Build
stage can retain the pipeline as in the above examples, but with one addition: by saving the new lease's Id
in an output variable, the lease can be updated later when the release stage runs.
- task: PowerShell@2
condition: and(succeeded(), not(canceled()))
name: RetainOnSuccess
displayName: Retain on Success
inputs:
failOnStderr: true
targetType: 'inline'
script: |
$contentType = "application/json";
$headers = @{ Authorization = 'Bearer $(System.AccessToken)' };
$rawRequest = @{ daysValid = 365; definitionId = $(System.DefinitionId); ownerId = 'User:$(Build.RequestedForId)'; protectPipeline = $false; runId = $(Build.BuildId) };
$request = ConvertTo-Json @($rawRequest);
$uri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases?api-version=6.0-preview.1";
$newLease = Invoke-RestMethod -uri $uri -method POST -Headers $headers -ContentType $contentType -Body $request;
$newLeaseId = $newLease.Value[0].LeaseId
echo "##vso[task.setvariable variable=newLeaseId;isOutput=true]$newLeaseId";
To update a retention lease requires a different REST API call.
- stage: Release
dependsOn: Build
jobs:
- job: default
variables:
- name: NewLeaseId
value: $[ stageDependencies.Build.default.outputs['RetainOnSuccess.newLeaseId']]
steps:
- task: PowerShell@2
condition: and(succeeded(), not(canceled()))
name: RetainOnSuccess
displayName: Retain on Success
inputs:
failOnStderr: true
targetType: 'inline'
script: |
$contentType = "application/json";
$headers = @{ Authorization = 'Bearer $(System.AccessToken)' };
$rawRequest = @{ daysValid = 365 };
$request = ConvertTo-Json $rawRequest;
$uri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases/$(newLeaseId)?api-version=7.1-preview.2";
Invoke-RestMethod -uri $uri -method PATCH -Headers $headers -ContentType $contentType -Body $request;
Next steps
With these examples, you learned how to use custom pipeline tasks to manage run retention.
You learned how to:
- Override a short retention window
- Override retention for runs on specific branches
- Update a retention lease when a run should be retained even longer