Re-prioritize Octopus Deploy Tasks

Octopus.Script exported 2021-02-22 by octobob belongs to ‘Octopus’ category.

This step will allow you to re-prioritize tasks in the Octopus Deploy queue.

It will check the task queue for pending tasks and if it finds a task that should be prioritized based on the matching criteria it will cancel any tasks before it to move it to the top of the queue.

The matching logic supports:

  • Task Id - you provide a list of task ids and it will move those to the top of the queue
  • Space, Environment, Project, Tenant - you provide a list of spaces, environments, projects, or tenants and the step will find matching deployments or runbook runs and move them to the top of the queue.

How it works:

  1. The step template pulls the list of unscheduled queued tasks (if you schedule a task to run at 7 PM it will exclude that from the check).
  2. Attempts to find matching tasks based on task id or space/environment/project/tenant criteria.
  3. If it finds one or more matching tasks it checks the position of those tasks in the queue. Any tasks ahead of them are cancelled.
  4. if the step template cancels any deployments or runbook runs it will resubmit them using the same criteria as before (this moves them to the bottom of the queue).

Important It will not cancel running tasks.

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Octopus API Key

TaskPriority.Api.Key =

Required

The API key of the user authorized to cancel tasks and resubmit them.

Octopus URL

TaskPriority.Octopus.Url =

Required

The URL of the Octopus Deploy instance you wish to query.

Please only pass in the base URL, eg https://samples.octopus.app

Task Id List

TaskPriority.TaskId.List =

A comma-separated or new-line separated list of task ids to move to the top of the queue.

IMPORTANT when this is provided all other matching logic is ignored, including the task type and the space list.

Space List

TaskPriority.Space.List =

List of spaces to monitor. Separate spaces by using a new line.

When left empty this step will query all spaces.

Environment List

TaskPriority.Environment.List = Production

List of environment names to monitor for. Separate environment names by a new line.

Options:

  • EnvironmentName - looks for matching environment names in all spaces in the space list.
  • EnvironmentName::SpaceName - looks for the environment name in the specified space. Use this when the same environment appears in multiple spaces and you want to filter to only apply to an item in a specific space.

The default Production.

When not specifying a value for the Task Id parameter; a entry must appear in the Environment List, Project List, OR Tenant List. Otherwise, this step will not do anything.

Project List

TaskPriority.Project.List =

List of projects to monitor. Separate project names by a new line.

Options:

  • ProjectName - looks for the specified project name in all spaces in the space list.
  • ProjectName::SpaceName - looks for that project in a specific space. Use this when the same project appears in multiple spaces and you want to filter to only apply to an item in a specific space.

When left empty it will not look for any projects.

When not specifying a value for the Task Id parameter; a entry must appear in the Environment List, Project List, OR Tenant List. Otherwise, this step will not do anything.

Tenant List

TaskPriority.Tenant.List =

List of tenant names to monitor. Separate tenant names by a new line.

Options:

  • TenantName - looks for the tenant name in any spaces in the space list
  • TenantName::SpaceName - looks for the tenant name in the specified space. Use this when the same tenant appears in multiple spaces and you want to filter to only apply to an item in a specific space.

When left empty it will not filter anything by the tenant.

When not specifying a value for the Task Id parameter; a entry must appear in the Environment List, Project List, OR Tenant List. Otherwise, this step will not do anything.

Task Match

TaskPriority.Task.Match = Or

Indicates how the matching will occur.

Options:

  • And - means the deployment has to be for the Environment AND the project AND the tenant (when they are specified)
  • Or - means the deployment can be for Environment OR the project OR the tenant

Default is Or

Task Type

TaskPriority.Task.Type = Both

The type of task to move to the top of the queue.

Options:

  • Deploy - monitors deployments only
  • RunbookRun - monitors runbook runs only
  • Both - monitors both deployments and runbooks

Defaults to Both

Script body

Steps based on this template will execute the following PowerShell script.

[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

$octopusApiKey = $OctopusParameters["TaskPriority.Api.Key"]
$spaceList = $OctopusParameters["TaskPriority.Space.List"]
$environmentList = $OctopusParameters["TaskPriority.Environment.List"]
$projectList = $OctopusParameters["TaskPriority.Project.List"]
$tenantList = $OctopusParameters["TaskPriority.Tenant.List"]
$matchType =  $OctopusParameters["TaskPriority.Match.Type"]
$taskType = $OctopusParameters["TaskPriority.Task.Type"]
$octopusUrl = $OctopusParameters["TaskPriority.Octopus.Url"]
$taskIdList = $OctopusParameters["TaskPriority.TaskId.List"]

$cachedResults = @{}

function Write-OctopusVerbose
{
    param($message)
    
    Write-Verbose $message  
}

function Write-OctopusInformation
{
    param($message)
    
    Write-Host $message  
}

function Write-OctopusSuccess
{
    param($message)

    Write-Highlight $message 
}

function Write-OctopusWarning
{
    param($message)

    Write-Warning "$message" 
}

function Write-OctopusCritical
{
    param ($message)

    Write-Error "$message" 
}

function Invoke-OctopusApi
{
    param
    (
        $octopusUrl,
        $endPoint,
        $spaceId,
        $apiKey,
        $method,
        $item,
        $ignoreCache     
    )

    $octopusUrlToUse = $OctopusUrl
    if ($OctopusUrl.EndsWith("/"))
    {
        $octopusUrlToUse = $OctopusUrl.Substring(0, $OctopusUrl.Length - 1)
    }

    if ([string]::IsNullOrWhiteSpace($SpaceId))
    {
        $url = "$octopusUrlToUse/api/$EndPoint"
    }
    else
    {
        $url = "$octopusUrlToUse/api/$spaceId/$EndPoint"    
    }  

    try
    {        
        if ($null -ne $item)
        {
            $body = $item | ConvertTo-Json -Depth 10
            Write-OctopusVerbose $body

            Write-OctopusInformation "Invoking $method $url"
            return Invoke-RestMethod -Method $method -Uri $url -Headers @{"X-Octopus-ApiKey" = "$ApiKey" } -Body $body -ContentType 'application/json; charset=utf-8' 
        }

        if (($null -eq $ignoreCache -or $ignoreCache -eq $false) -and $method.ToUpper().Trim() -eq "GET")
        {
            Write-OctopusVerbose "Checking to see if $url is already in the cache"
            if ($cachedResults.ContainsKey($url) -eq $true)
            {
                Write-OctopusVerbose "$url is already in the cache, returning the result"
                return $cachedResults[$url]
            }
        }
        else
        {
            Write-OctopusVerbose "Ignoring cache."    
        }

        Write-OctopusVerbose "No data to post or put, calling bog standard invoke-restmethod for $url"
        $result = Invoke-RestMethod -Method $method -Uri $url -Headers @{"X-Octopus-ApiKey" = "$ApiKey" } -ContentType 'application/json; charset=utf-8'

        if ($cachedResults.ContainsKey($url) -eq $true)
        {
            $cachedResults.Remove($url)
        }
        Write-OctopusVerbose "Adding $url to the cache"
        $cachedResults.add($url, $result)

        return $result

               
    }
    catch
    {
        if ($null -ne $_.Exception.Response)
        {
            if ($_.Exception.Response.StatusCode -eq 401)
            {
                Write-OctopusCritical "Unauthorized error returned from $url, please verify API key and try again"
            }
            elseif ($_.Exception.Response.statusCode -eq 403)
            {
                Write-OctopusCritical "Forbidden error returned from $url, please verify API key and try again"
            }
            else
            {                
                Write-OctopusVerbose -Message "Error calling $url $($_.Exception.Message) StatusCode: $($_.Exception.Response.StatusCode )"
            }            
        }
        else
        {
            Write-OctopusVerbose $_.Exception
        }
    }

    Throw "There was an error calling the Octopus API please check the log for more details"
}

function Get-FilteredOctopusItem
{
    param(
        $itemList,
        $itemName
    )

    if ($itemList.Items.Count -eq 0)
    {
        Write-OctopusCritical "Unable to find $itemName.  Exiting with an exit code of 1."
        return $null
    }  

    $item = $itemList.Items | Where-Object { $_.Name -eq $itemName}      

    if ($null -eq $item)
    {
        Write-OctopusCritical "Unable to find $itemName.  Exiting with an exit code of 1."
        return $null
    }

    return $item
}

function Get-OctopusItemByName
{
    param(
        $itemName,
        $itemType,
        $endpoint,        
        $spaceId,
        $octopusUrl,
        $octopusApiKey
    )

    if ([string]::IsNullOrWhiteSpace($itemName))
    {
        return $null
    }

    Write-OctopusInformation "Attempting to find $itemType with the name of $itemName"
    
    $itemList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "$($endPoint)?partialName=$([uri]::EscapeDataString($itemName))&skip=0&take=100" -spaceId $spaceId -apiKey $octopusApiKey -method "GET"    
    $item = Get-FilteredOctopusItem -itemList $itemList -itemName $itemName

    if ($null -eq $item)
    {
        Write-OctopusInformation "Unable to find $itemType $itemName"    
        return $null
    }
    
    Write-OctopusInformation "Successfully found $itemType $itemName with an id of $($item.Id)"

    return $item
}

function Get-SplitItemIntoArray
{
    param (
        $itemToSplit
    )

    if ($itemToSplit -like "*`n*")
    {
        return @(($itemToSplit -Split "`n").Trim())
    }

    if ($itemToSplit -like "*,*")
    {
        return @(($itemToSplit -Split ",").Trim())
    }

    return @($itemToSplit)
}

function Get-OctopusSpaceList
{
	param(
    	$spaceList,        
        $octopusUrl,
        $octopusApiKey    
    )
    
    if ([string]::IsNullOrWhiteSpace($spaceList))
    {
        $rawOctopusSpaceList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "spaces?skip=0&take=10000" -spaceId $null -apiKey $octopusApiKey -method "GET"    

        return $rawOctopusSpaceList.Items
    }
    
    $spaceListSplit = @(Get-SplitItemIntoArray -itemToSplit $spaceList)
    $returnList = @()

    foreach ($spaceName in $spaceListSplit)
    {
        if ([string]::IsNullOrWhiteSpace($spaceName) -eq $false)
        {
            $octopusSpace = Get-OctopusItemByName -itemName $spaceName -itemType "Space" -endpoint "spaces" -spaceId $null -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey

            if ($null -ne $octopusSpace)
            {
                $returnList += $octopusSpace
            }            
        }        
    }    
    
    return $returnList
}

function Get-OctopusItemList
{
    param(
        $octopusSpaceList,
        $itemList,
        $itemType,
        $endpoint,
        $octopusApiKey,
        $octopusUrl
    )

    if ([string]::IsNullOrWhiteSpace($itemList))
    {
        Write-Host "The list for $itemType was empty"        
        return @()
    }

    $itemListSplit = @(Get-SplitItemIntoArray -itemToSplit $itemList)
    $returnList = @()
    
    foreach ($itemName in $itemListSplit)
    {
        $splitItem = $itemName -split "::"
        if ($splitItem.Count -gt 1 -and [string]::IsNullOrWhiteSpace($splitItem[1]) -eq $false)
        {
            Write-OctopusInformation "The item $itemName included a space name, only pulling back the information for that space"
            $spaceId = $octopusSpaceList | Where-Object { $_.Name.ToLower().Trim() -eq $splitItem[1].ToLower().Trim() }

            if ($null -eq $spaceId)
            {
                Write-OctopusInformation "The space name $($splitItem[1]) was not included in the space filter.  Skipping this option."
                continue
            }

            $octopusItem = Get-OctopusItemByName -itemName $splitItem[0] -itemType $itemType -endpoint $endpoint -spaceId $spaceId -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey

            if ($null -ne $octopusItem)
            {
                $returnList += $octopusItem
            }

            continue
        }

        foreach ($space in $octopusSpaceList)
        {
            $octopusItem = Get-OctopusItemByName -itemName $itemName -itemType $itemType -endpoint $endpoint -spaceId $space.Id -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey

            if ($null -ne $octopusItem)
            {
                $returnList += $octopusItem
            }
        }
    }

    return $returnList
}

function Get-QueuedOctopusTasks
{
    param (
        $octopusApiKey,
        $octopusUrl
    )

    $queuedTasks = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "Tasks?states=Queued&skip=0&take=1000" -spaceId $null -apiKey $octopusApiKey -method "GET" -ignoreCache $true

    $returnList = @()
    $currentTime = $(Get-Date).ToUniversalTime()

    Write-OctopusInformation "Looping through the found items in reverse order because the Queue is FIFO but the return object is ordered by date DESC"

    for($i = $queuedTasks.Items.Count - 1; $i -ge 0; $i--)    
    {
        $task = $queuedTasks.Items[$i]
        
        if ($null -ne $task.QueueTime)
        {
            $compareTime = [DateTime]::Parse($task.QueueTime)
            $compareTime = $compareTime.ToUniversalTime()

            Write-OctopusVerbose "Checking to see if $compareTime is ahead of the $currentTime"
            if ($compareTime -gt $currentTime)
            {
                Write-OctopusInformation "The queued task $($task.Id) has a queue time $($task.QueueTime) in the future.  That means this is a scheduled deployment.  Skipping this task."
                continue
            }
        }

        if ($null -ne $task.StartTime)
        {
            Write-OctopusInformation "The queued task $($task.Id) has a start time, meaning it was picked up, work was done, then it was added back to the queue.  Skipping."
            continue
        }

        if ($true -eq $task.HasPendingInterruptions)
        {
            Write-OctopusInformation "The task $($task.Id) has pending interruptions, this means the deployment has started and is awaiting someone to respond.  Skipping this task."
            continue
        }

        $returnList += $task
    }

    return $returnList
}

function Test-OctopusListHasId
{
    param (
        $octopusList,
        $octopusId
    )

    $findItem = $octopusList | Where-Object { $_.Id -eq $octopusId }

    if ($null -eq $findItem)
    {
        return $false
    }

    return $true
}

function Get-RunbookRunDetailsFromTask
{
    param (
        $runbookTask,
        $octopusUrl,
        $octopusApiKey
    )

    return Invoke-OctopusApi -endPoint "runbookRuns/$($runbookTask.Arguments.RunbookRunId)" -octopusUrl $octopusUrl -spaceId $runbookTask.SpaceId -apiKey $octopusApiKey -method "GET"
}

function Get-DeploymentDetailsFromTask
{
    param (
        $deploymentTask,
        $octopusUrl,
        $octopusApiKey
    )

    return Invoke-OctopusApi -endPoint "deployments/$($deploymentTask.Arguments.DeploymentId)" -octopusUrl $octopusUrl -spaceId $deploymentTask.SpaceId -apiKey $octopusApiKey -method "GET"
}

Write-OctopusInformation "Space List: $spaceList"
Write-OctopusInformation "Environment List: $environmentList"
Write-OctopusInformation "Project List: $projectList"
Write-OctopusInformation "Tenant List: $tenantList"
Write-OctopusInformation "Octopus URL: $octopusUrl"
Write-OctopusInformation "Match Type: $matchType"
Write-OctopusInformation "Task Id List: $taskIdList"

$queuedTasks = @(Get-QueuedOctopusTasks -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)

if ($queuedTasks.Length -eq 0)
{
    Write-OctopusSuccess "No queued tasks found that can block a deployment.  Exiting."
    exit 0
}

$octopusInformation = @{
    TaskIdList = @(Get-SplitItemIntoArray -itemToSplit $taskIdList)
}

if ([string]::IsNullOrWhiteSpace($taskIdList))
{
    $octopusInformation.SpaceList = @(Get-OctopusSpaceList -spaceList $spaceList -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey)

    $octopusInformation.EnvironmentList = @(Get-OctopusItemList -octopusSpaceList $octopusInformation.SpaceList -itemList $environmentList -itemType "Environment" -endpoint "environments" -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)
    $octopusInformation.HasEnvironmentFilter = $octopusInformation.EnvironmentList.Count -gt 0

    $octopusInformation.ProjectList = @(Get-OctopusItemList -octopusSpaceList $octopusInformation.SpaceList -itemList $projectList -itemType "Project" -endpoint "projects" -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)
    $octopusInformation.HasProjectFilter = $octopusInformation.ProjectList.Count -gt 0

    $octopusInformation.TenantList = @(Get-OctopusItemList -octopusSpaceList $octopusInformation.SpaceList -itemList $tenantList -itemType "Tenant" -endpoint "tenants" -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)
    $octopusInformation.HasTenantFilter = $octopusInformation.TenantList.Count -gt 0

    if ($octopusInformation.EnvironmentList.Count -eq 0 -and $octopusInformation.ProjectList.Count -eq 0 -and $octopusInformation.TenantList.Count -eq 0)
    {
        Write-OctopusCritical "No environments OR projects OR tenants provided to filter on.  You must provide at least one environment OR project OR tenant."
        exit 1
    }

    Write-OctopusSuccess "Going to look for any $taskType in the spaces ($(($octopusInformation.SpaceList | Select-Object -ExpandProperty Id) -join ", ")) matching "
    Write-OctopusSuccess "Environments ($(($octopusInformation.EnvironmentList | Select-Object -ExpandProperty Id) -join " OR ")) $matchType"
    Write-OctopusSuccess "Projects ($(($octopusInformation.ProjectList | Select-Object -ExpandProperty Id) -join " OR ")) $matchType"
    Write-OctopusSuccess "Tenants ($(($octopusInformation.TenantList | Select-Object -ExpandProperty Id) -join " OR "))"
}
else
{
    Write-OctopusSuccess "Going to look for the tasks ($($octopusInformation.TaskIdList -join ", "))"    
}

$matchingTasks = @()

Write-OctopusInformation "Attempting to find any matching tasks based on the filtering criteria."
foreach ($task in $queuedTasks)
{
    if ($octopusInformation.TaskIdList -contains $task.Id)
    {
        Write-OctopusInformation "The task $($task.Id) was found in the list of task ids.  Adding to list."
        $matchingTasks += $task

        continue
    }

    if ($task.Name -ne "Deploy" -and $task.Name -ne "RunbookRun")
    {
        Write-Information "The task not a deployment or a runbook run.  It is $($task.Description).  Moving onto next task."
        continue
    }

    if ($taskType -ne "Both" -and $taskType -ne $task.Name)
    {
        Write-Information "You have selected to filter on $taskType only and this task is a $($task.Name).  Moving onto the next task."
        continue
    }

    if ((Test-OctopusListHasId -octopusList $octopusInformation.SpaceList -octopusId $task.SpaceId) -eq $false)
    {
        Write-Information "The task is not for any spaces specified.  Moving onto the next task."
        continue
    }

    if ($task.Name -eq "RunbookRun")
    {
        $itemDetails = Get-RunbookRunDetailsFromTask -runbookTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey
    }
    else
    {
        $itemDetails = Get-DeploymentDetailsFromTask -deploymentTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey
    }        

    $matchesEnvironmentFilter = $octopusInformation.HasEnvironmentFilter -eq $true -and (Test-OctopusListHasId -octopusList $octopusInformation.EnvironmentList -octopusId $itemDetails.EnvironmentId)
    Write-OctopusInformation "$($task.Name) $($itemDetails.Id) Matches Environment Filter $matchesEnvironmentFilter"

    $matchesProjectFilter = $octopusInformation.HasProjectFilter -eq $true -and (Test-OctopusListHasId -octopusList $octopusInformation.ProjectList -octopusId $itemDetails.ProjectId)
    Write-OctopusInformation "$($task.Name) $($itemDetails.Id) Matches Project Filter $matchesProjectFilter"

    $matchesTenantFilter = $octopusInformation.HasTenantFilter -eq $true -and $null -ne $itemDetails.TenantId -and (Test-OctopusListHasId -octopusList $octopusInformation.TenantList -octopusId $itemDetails.TenantId)
    Write-OctopusInformation "$($task.Name) $($itemDetails.Id) Matches Tenant Filter $matchesTenantFilter"

    if ($matchType -eq "Or" -and ($matchesTenantFilter -eq $true -or $matchesProjectFilter -eq $true -or $matchesEnvironmentFilter -eq $true))
    {
        Write-OctopusInformation "The match type was OR and one of the filters matched, adding this task to the matching list"
        $matchingTasks += $task

        continue
    }

    Write-OctopusInformation "The match type is AND, checking to see if the task matches all the filters"

    if ($octopusInformation.HasEnvironmentFilter -eq $true -and $matchesEnvironmentFilter -eq $false)
    {
        Write-OctopusInformation "The environment filter was provided and the environment $($itemDetails.EnvironmentId) didn't match any environments.  Moving onto next task."
        continue
    }

    if ($octopusInformation.HasProjectFilter -eq $true -and $matchesProjectFilter -eq $false)
    {
        Write-OctopusInformation "The project filter was provided and the project $($itemDetails.ProjectId) didn't match any projects.  Moving onto next task."
        continue
    }

    if ($octopusInformation.HasTenantFilter -eq $true -and $matchesTenantFilter -eq $false)
    {
        Write-OctopusInformation "The tenant filter was provided and the tenant $($itemDetails.TenantId) didn't match any tenants.  Moving onto next task."
        continue
    }

    $matchingTasks += $task
}

if ($matchingTasks.Count -eq 0)
{
    Write-OctopusSuccess "No matching tasks found, exiting."
    exit 0
}

Write-OctopusSuccess "Matching tasks found, checking where they are in the queue."

$matchingTaskCounter = 0

Write-OctopusInformation "Looping through all the queued tasks again to find which tasks to cancel."
foreach ($task in $queuedTasks)
{        
    if ((Test-OctopusListHasId -octopusList $matchingTasks -octopusId $task.Id))
    {
        $matchingTaskCounter += 1
        Write-OctopusInformation "The task $($task.Id) is one we want to move to the top of queue, leaving as is."

        if ($matchingTaskCounter -eq $matchingTasks.Count)
        {
            Write-OctopusSuccess "All the matching tasks we want to move to the top of the queue have been found, exiting"
            break
        }

        continue
    }

    $updatedTask = Invoke-OctopusApi -endPoint "tasks/$($task.Id)" -octopusUrl $octopusUrl -spaceId $null -apiKey $octopusApiKey -method "GET" -ignoreCache $true

    if ($updatedTask.HasBeenPickedUpByProcssor -eq $true)
    {
        Write-OctopusInformation "The task $($task.Id) has already been picked up and started processing, moving on."
        continue
    }

    $canceledTaskResult = Invoke-OctopusApi -endPoint "tasks/$($task.Id)/cancel" -octopusUrl $octopusUrl -spaceId $null -apiKey $octopusApiKey -method "POST" -ignoreCache $true

    Write-OctopusSuccess "Task $($canceledTaskResult.Description) has been successfully cancelled" 

    if ($task.Name -eq "Deploy")
    {
        Write-OctopusInformation "Task $($task.Id) is a deployment, setting up a redeploy."

        $deploymentInfo = Get-DeploymentDetailsFromTask -deploymentTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey

        $bodyRaw = @{
            EnvironmentId = $deploymentInfo.EnvironmentId
            ExcludedMachineIds = $deploymentInfo.ExcludedMachineIds
            ForcePackageDownload = $deploymentInfo.ForcePackageDownload
            ForcePackageRedeployment = $deploymentInfo.ForcePackageRedeployment
            FormValues = $deploymentInfo.FormValues
            QueueTime = $null
            QueueTimeExpiry = $null
            ReleaseId = $deploymentInfo.ReleaseId
            SkipActions = $deploymentInfo.SkipActions
            SpecificMachineIds = $deploymentInfo.SpecificMachineIds
            TenantId = $deploymentInfo.TenantId
            UseGuidedFailure = $deploymentInfo.UseGuidedFailure
        } 

        $newDeployment = Invoke-OctopusApi -endPoint "deployments" -spaceId $task.SpaceId -octopusUrl $octopusUrl -apiKey $octopusApiKey -method "POST" -item $bodyRaw

        Write-OctopusSuccess "$($task.Description) has been successfully resubmitted with the new id $($newDeployment.TaskId)"        
    }

    if ($task.Name -eq "RunbookRun")
    {
        Write-OctopusInformation "Task $($task.Id) is a runbook run, configuring a re-run."

        $runbookInfo = Get-RunbookRunDetailsFromTask -runbookTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey

        $bodyRaw = @{
            EnvironmentId = $runbookInfo.EnvironmentId
            ExcludedMachineIds = $runbookInfo.ExcludedMachineIds
            ForcePackageDownload = $runbookInfo.ForcePackageDownload
            ForcePackageRedeployment = $runbookInfo.ForcePackageRedeployment
            FormValues = $runbookInfo.FormValues
            QueueTime = $null
            QueueTimeExpiry = $null
            RunbookId = $runbookInfo.RunbookId
            SkipActions = $runbookInfo.SkipActions
            SpecificMachineIds = $runbookInfo.SpecificMachineIds
            TenantId = $runbookInfo.TenantId
            UseGuidedFailure = $runbookInfo.UseGuidedFailure
            FrozenRunbookProcessId = $runbookInfo.FrozenRunbookProcessId
            RunbookSnapshotId = $runbookInfo.RunbookSnapshotId            
        } 

        $newDeployment = Invoke-OctopusApi -endPoint "runbookRuns" -spaceId $task.SpaceId -octopusUrl $octopusUrl -apiKey $octopusApiKey -method "POST" -item $bodyRaw

        Write-OctopusSuccess "$($task.Description) has been successfully resubmitted with the new id $($newDeployment.TaskId)" 
    }
}

Write-OctopusSuccess "Finished re-prioritizing tasks"

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "c9d5c96f-f731-4e6c-b9b3-d93f84a9bb74",
  "Name": "Re-prioritize Octopus Deploy Tasks",
  "Description": "This step will allow you to re-prioritize tasks in the Octopus Deploy queue.  \n\nIt will check the task queue for pending tasks and if it finds a task that should be prioritized based on the matching criteria it will cancel any tasks before it to move it to the top of the queue.  \n\nThe matching logic supports:\n- Task Id - you provide a list of task ids and it will move those to the top of the queue\n- Space, Environment, Project, Tenant - you provide a list of spaces, environments, projects, or tenants and the step will find matching deployments or runbook runs and move them to the top of the queue.  \n\nHow it works:\n1) The step template pulls the list of unscheduled queued tasks (if you schedule a task to run at 7 PM it will exclude that from the check).\n2) Attempts to find matching tasks based on task id or space/environment/project/tenant criteria.\n3) If it finds one or more matching tasks it checks the position of those tasks in the queue.  Any tasks ahead of them are cancelled.\n4) if the step template cancels any deployments or runbook runs it will resubmit them using the same criteria as before (this moves them to the bottom of the queue).\n\n**Important** It will not cancel running tasks.",
  "Version": 1,
  "ExportedAt": "2021-02-22T15:40:24.604Z",
  "ActionType": "Octopus.Script",
  "Author": "octobob",
  "Packages": [],
  "Parameters": [
    {
      "Id": "f7357d18-33c3-4f1e-883d-613e13e098cd",
      "Name": "TaskPriority.Api.Key",
      "Label": "Octopus API Key",
      "HelpText": "*Required* \n\n\nThe API key of the user authorized to cancel tasks and resubmit them.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "82ef3721-aef1-4624-a306-9f20ad2ef64d",
      "Name": "TaskPriority.Octopus.Url",
      "Label": "Octopus URL",
      "HelpText": "*Required*\n\nThe URL of the Octopus Deploy instance you wish to query.  \n\nPlease only pass in the base URL, eg `https://samples.octopus.app`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "b2e14858-7309-46ab-b624-ec811ad95618",
      "Name": "TaskPriority.TaskId.List",
      "Label": "Task Id List",
      "HelpText": "A comma-separated or new-line separated list of task ids to move to the top of the queue.\n\n**IMPORTANT** when this is provided all other matching logic is ignored, including the task type and the space list.\n",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "2434bd05-791b-45e9-9ffa-7fbde9524e5f",
      "Name": "TaskPriority.Space.List",
      "Label": "Space List",
      "HelpText": "List of spaces to monitor.  Separate spaces by using a new line.\n\nWhen left empty this step will query all spaces.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "87a7ec4a-128b-4b74-8f6b-ea66c9261dec",
      "Name": "TaskPriority.Environment.List",
      "Label": "Environment List",
      "HelpText": "List of environment names to monitor for.  Separate environment names by a new line.\n\nOptions:\n- `EnvironmentName` - looks for matching environment names in all spaces in the space list.\n- `EnvironmentName::SpaceName` - looks for the environment name in the specified space.  Use this when the same environment appears in multiple spaces and you want to filter to only apply to an item in a specific space.\n\nThe default `Production`.  \n\nWhen not specifying a value for the Task Id parameter; a entry must appear in the Environment List, Project List, OR Tenant List.  Otherwise, this step will not do anything.",
      "DefaultValue": "Production",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "b94bd70b-6554-4ea5-86f1-fbfb1827dfc5",
      "Name": "TaskPriority.Project.List",
      "Label": "Project List",
      "HelpText": "List of projects to monitor.  Separate project names by a new line.\n\nOptions:\n- `ProjectName` - looks for the specified project name in all spaces in the space list.\n- `ProjectName::SpaceName` - looks for that project in a specific space.  Use this when the same project appears in multiple spaces and you want to filter to only apply to an item in a specific space.\n\nWhen left empty it will not look for any projects.\n\nWhen not specifying a value for the Task Id parameter; a entry must appear in the Environment List, Project List, OR Tenant List.  Otherwise, this step will not do anything.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "0958b7b3-94b6-4a3e-b78c-f2436950728a",
      "Name": "TaskPriority.Tenant.List",
      "Label": "Tenant List",
      "HelpText": "List of tenant names to monitor.  Separate tenant names by a new line.\n\nOptions:\n- `TenantName` - looks for the tenant name in any spaces in the space list\n- `TenantName::SpaceName` - looks for the tenant name in the specified space.  Use this when the same tenant appears in multiple spaces and you want to filter to only apply to an item in a specific space.\n\nWhen left empty it will not filter anything by the tenant.\n\nWhen not specifying a value for the Task Id parameter; a entry must appear in the Environment List, Project List, OR Tenant List.  Otherwise, this step will not do anything.\n",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "a6382a5c-df7f-4dee-bdc9-df89459e1d14",
      "Name": "TaskPriority.Task.Match",
      "Label": "Task Match",
      "HelpText": "Indicates how the matching will occur.\n\nOptions:\n- `And` - means the deployment has to be for the Environment AND the project AND the tenant (when they are specified)\n- `Or` - means the deployment can be for Environment OR the project OR the tenant\n\nDefault is `Or`\n",
      "DefaultValue": "Or",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "Or|Or\nAnd|And"
      }
    },
    {
      "Id": "1e8d3fc8-36b5-4c84-bfe5-4bfc5b4f5869",
      "Name": "TaskPriority.Task.Type",
      "Label": "Task Type",
      "HelpText": "The type of task to move to the top of the queue.\n\nOptions:\n- `Deploy` - monitors deployments only\n- `RunbookRun` - monitors runbook runs only\n- `Both` - monitors both deployments and runbooks\n\nDefaults to `Both`",
      "DefaultValue": "Both",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "Both|Both\nDeploy|Deploy\nRunbookRun|RunbookRun"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12\n\n$octopusApiKey = $OctopusParameters[\"TaskPriority.Api.Key\"]\n$spaceList = $OctopusParameters[\"TaskPriority.Space.List\"]\n$environmentList = $OctopusParameters[\"TaskPriority.Environment.List\"]\n$projectList = $OctopusParameters[\"TaskPriority.Project.List\"]\n$tenantList = $OctopusParameters[\"TaskPriority.Tenant.List\"]\n$matchType =  $OctopusParameters[\"TaskPriority.Match.Type\"]\n$taskType = $OctopusParameters[\"TaskPriority.Task.Type\"]\n$octopusUrl = $OctopusParameters[\"TaskPriority.Octopus.Url\"]\n$taskIdList = $OctopusParameters[\"TaskPriority.TaskId.List\"]\n\n$cachedResults = @{}\n\nfunction Write-OctopusVerbose\n{\n    param($message)\n    \n    Write-Verbose $message  \n}\n\nfunction Write-OctopusInformation\n{\n    param($message)\n    \n    Write-Host $message  \n}\n\nfunction Write-OctopusSuccess\n{\n    param($message)\n\n    Write-Highlight $message \n}\n\nfunction Write-OctopusWarning\n{\n    param($message)\n\n    Write-Warning \"$message\" \n}\n\nfunction Write-OctopusCritical\n{\n    param ($message)\n\n    Write-Error \"$message\" \n}\n\nfunction Invoke-OctopusApi\n{\n    param\n    (\n        $octopusUrl,\n        $endPoint,\n        $spaceId,\n        $apiKey,\n        $method,\n        $item,\n        $ignoreCache     \n    )\n\n    $octopusUrlToUse = $OctopusUrl\n    if ($OctopusUrl.EndsWith(\"/\"))\n    {\n        $octopusUrlToUse = $OctopusUrl.Substring(0, $OctopusUrl.Length - 1)\n    }\n\n    if ([string]::IsNullOrWhiteSpace($SpaceId))\n    {\n        $url = \"$octopusUrlToUse/api/$EndPoint\"\n    }\n    else\n    {\n        $url = \"$octopusUrlToUse/api/$spaceId/$EndPoint\"    \n    }  \n\n    try\n    {        \n        if ($null -ne $item)\n        {\n            $body = $item | ConvertTo-Json -Depth 10\n            Write-OctopusVerbose $body\n\n            Write-OctopusInformation \"Invoking $method $url\"\n            return Invoke-RestMethod -Method $method -Uri $url -Headers @{\"X-Octopus-ApiKey\" = \"$ApiKey\" } -Body $body -ContentType 'application/json; charset=utf-8' \n        }\n\n        if (($null -eq $ignoreCache -or $ignoreCache -eq $false) -and $method.ToUpper().Trim() -eq \"GET\")\n        {\n            Write-OctopusVerbose \"Checking to see if $url is already in the cache\"\n            if ($cachedResults.ContainsKey($url) -eq $true)\n            {\n                Write-OctopusVerbose \"$url is already in the cache, returning the result\"\n                return $cachedResults[$url]\n            }\n        }\n        else\n        {\n            Write-OctopusVerbose \"Ignoring cache.\"    \n        }\n\n        Write-OctopusVerbose \"No data to post or put, calling bog standard invoke-restmethod for $url\"\n        $result = Invoke-RestMethod -Method $method -Uri $url -Headers @{\"X-Octopus-ApiKey\" = \"$ApiKey\" } -ContentType 'application/json; charset=utf-8'\n\n        if ($cachedResults.ContainsKey($url) -eq $true)\n        {\n            $cachedResults.Remove($url)\n        }\n        Write-OctopusVerbose \"Adding $url to the cache\"\n        $cachedResults.add($url, $result)\n\n        return $result\n\n               \n    }\n    catch\n    {\n        if ($null -ne $_.Exception.Response)\n        {\n            if ($_.Exception.Response.StatusCode -eq 401)\n            {\n                Write-OctopusCritical \"Unauthorized error returned from $url, please verify API key and try again\"\n            }\n            elseif ($_.Exception.Response.statusCode -eq 403)\n            {\n                Write-OctopusCritical \"Forbidden error returned from $url, please verify API key and try again\"\n            }\n            else\n            {                \n                Write-OctopusVerbose -Message \"Error calling $url $($_.Exception.Message) StatusCode: $($_.Exception.Response.StatusCode )\"\n            }            \n        }\n        else\n        {\n            Write-OctopusVerbose $_.Exception\n        }\n    }\n\n    Throw \"There was an error calling the Octopus API please check the log for more details\"\n}\n\nfunction Get-FilteredOctopusItem\n{\n    param(\n        $itemList,\n        $itemName\n    )\n\n    if ($itemList.Items.Count -eq 0)\n    {\n        Write-OctopusCritical \"Unable to find $itemName.  Exiting with an exit code of 1.\"\n        return $null\n    }  \n\n    $item = $itemList.Items | Where-Object { $_.Name -eq $itemName}      \n\n    if ($null -eq $item)\n    {\n        Write-OctopusCritical \"Unable to find $itemName.  Exiting with an exit code of 1.\"\n        return $null\n    }\n\n    return $item\n}\n\nfunction Get-OctopusItemByName\n{\n    param(\n        $itemName,\n        $itemType,\n        $endpoint,        \n        $spaceId,\n        $octopusUrl,\n        $octopusApiKey\n    )\n\n    if ([string]::IsNullOrWhiteSpace($itemName))\n    {\n        return $null\n    }\n\n    Write-OctopusInformation \"Attempting to find $itemType with the name of $itemName\"\n    \n    $itemList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint \"$($endPoint)?partialName=$([uri]::EscapeDataString($itemName))&skip=0&take=100\" -spaceId $spaceId -apiKey $octopusApiKey -method \"GET\"    \n    $item = Get-FilteredOctopusItem -itemList $itemList -itemName $itemName\n\n    if ($null -eq $item)\n    {\n        Write-OctopusInformation \"Unable to find $itemType $itemName\"    \n        return $null\n    }\n    \n    Write-OctopusInformation \"Successfully found $itemType $itemName with an id of $($item.Id)\"\n\n    return $item\n}\n\nfunction Get-SplitItemIntoArray\n{\n    param (\n        $itemToSplit\n    )\n\n    if ($itemToSplit -like \"*`n*\")\n    {\n        return @(($itemToSplit -Split \"`n\").Trim())\n    }\n\n    if ($itemToSplit -like \"*,*\")\n    {\n        return @(($itemToSplit -Split \",\").Trim())\n    }\n\n    return @($itemToSplit)\n}\n\nfunction Get-OctopusSpaceList\n{\n\tparam(\n    \t$spaceList,        \n        $octopusUrl,\n        $octopusApiKey    \n    )\n    \n    if ([string]::IsNullOrWhiteSpace($spaceList))\n    {\n        $rawOctopusSpaceList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint \"spaces?skip=0&take=10000\" -spaceId $null -apiKey $octopusApiKey -method \"GET\"    \n\n        return $rawOctopusSpaceList.Items\n    }\n    \n    $spaceListSplit = @(Get-SplitItemIntoArray -itemToSplit $spaceList)\n    $returnList = @()\n\n    foreach ($spaceName in $spaceListSplit)\n    {\n        if ([string]::IsNullOrWhiteSpace($spaceName) -eq $false)\n        {\n            $octopusSpace = Get-OctopusItemByName -itemName $spaceName -itemType \"Space\" -endpoint \"spaces\" -spaceId $null -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n\n            if ($null -ne $octopusSpace)\n            {\n                $returnList += $octopusSpace\n            }            \n        }        \n    }    \n    \n    return $returnList\n}\n\nfunction Get-OctopusItemList\n{\n    param(\n        $octopusSpaceList,\n        $itemList,\n        $itemType,\n        $endpoint,\n        $octopusApiKey,\n        $octopusUrl\n    )\n\n    if ([string]::IsNullOrWhiteSpace($itemList))\n    {\n        Write-Host \"The list for $itemType was empty\"        \n        return @()\n    }\n\n    $itemListSplit = @(Get-SplitItemIntoArray -itemToSplit $itemList)\n    $returnList = @()\n    \n    foreach ($itemName in $itemListSplit)\n    {\n        $splitItem = $itemName -split \"::\"\n        if ($splitItem.Count -gt 1 -and [string]::IsNullOrWhiteSpace($splitItem[1]) -eq $false)\n        {\n            Write-OctopusInformation \"The item $itemName included a space name, only pulling back the information for that space\"\n            $spaceId = $octopusSpaceList | Where-Object { $_.Name.ToLower().Trim() -eq $splitItem[1].ToLower().Trim() }\n\n            if ($null -eq $spaceId)\n            {\n                Write-OctopusInformation \"The space name $($splitItem[1]) was not included in the space filter.  Skipping this option.\"\n                continue\n            }\n\n            $octopusItem = Get-OctopusItemByName -itemName $splitItem[0] -itemType $itemType -endpoint $endpoint -spaceId $spaceId -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n\n            if ($null -ne $octopusItem)\n            {\n                $returnList += $octopusItem\n            }\n\n            continue\n        }\n\n        foreach ($space in $octopusSpaceList)\n        {\n            $octopusItem = Get-OctopusItemByName -itemName $itemName -itemType $itemType -endpoint $endpoint -spaceId $space.Id -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n\n            if ($null -ne $octopusItem)\n            {\n                $returnList += $octopusItem\n            }\n        }\n    }\n\n    return $returnList\n}\n\nfunction Get-QueuedOctopusTasks\n{\n    param (\n        $octopusApiKey,\n        $octopusUrl\n    )\n\n    $queuedTasks = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint \"Tasks?states=Queued&skip=0&take=1000\" -spaceId $null -apiKey $octopusApiKey -method \"GET\" -ignoreCache $true\n\n    $returnList = @()\n    $currentTime = $(Get-Date).ToUniversalTime()\n\n    Write-OctopusInformation \"Looping through the found items in reverse order because the Queue is FIFO but the return object is ordered by date DESC\"\n\n    for($i = $queuedTasks.Items.Count - 1; $i -ge 0; $i--)    \n    {\n        $task = $queuedTasks.Items[$i]\n        \n        if ($null -ne $task.QueueTime)\n        {\n            $compareTime = [DateTime]::Parse($task.QueueTime)\n            $compareTime = $compareTime.ToUniversalTime()\n\n            Write-OctopusVerbose \"Checking to see if $compareTime is ahead of the $currentTime\"\n            if ($compareTime -gt $currentTime)\n            {\n                Write-OctopusInformation \"The queued task $($task.Id) has a queue time $($task.QueueTime) in the future.  That means this is a scheduled deployment.  Skipping this task.\"\n                continue\n            }\n        }\n\n        if ($null -ne $task.StartTime)\n        {\n            Write-OctopusInformation \"The queued task $($task.Id) has a start time, meaning it was picked up, work was done, then it was added back to the queue.  Skipping.\"\n            continue\n        }\n\n        if ($true -eq $task.HasPendingInterruptions)\n        {\n            Write-OctopusInformation \"The task $($task.Id) has pending interruptions, this means the deployment has started and is awaiting someone to respond.  Skipping this task.\"\n            continue\n        }\n\n        $returnList += $task\n    }\n\n    return $returnList\n}\n\nfunction Test-OctopusListHasId\n{\n    param (\n        $octopusList,\n        $octopusId\n    )\n\n    $findItem = $octopusList | Where-Object { $_.Id -eq $octopusId }\n\n    if ($null -eq $findItem)\n    {\n        return $false\n    }\n\n    return $true\n}\n\nfunction Get-RunbookRunDetailsFromTask\n{\n    param (\n        $runbookTask,\n        $octopusUrl,\n        $octopusApiKey\n    )\n\n    return Invoke-OctopusApi -endPoint \"runbookRuns/$($runbookTask.Arguments.RunbookRunId)\" -octopusUrl $octopusUrl -spaceId $runbookTask.SpaceId -apiKey $octopusApiKey -method \"GET\"\n}\n\nfunction Get-DeploymentDetailsFromTask\n{\n    param (\n        $deploymentTask,\n        $octopusUrl,\n        $octopusApiKey\n    )\n\n    return Invoke-OctopusApi -endPoint \"deployments/$($deploymentTask.Arguments.DeploymentId)\" -octopusUrl $octopusUrl -spaceId $deploymentTask.SpaceId -apiKey $octopusApiKey -method \"GET\"\n}\n\nWrite-OctopusInformation \"Space List: $spaceList\"\nWrite-OctopusInformation \"Environment List: $environmentList\"\nWrite-OctopusInformation \"Project List: $projectList\"\nWrite-OctopusInformation \"Tenant List: $tenantList\"\nWrite-OctopusInformation \"Octopus URL: $octopusUrl\"\nWrite-OctopusInformation \"Match Type: $matchType\"\nWrite-OctopusInformation \"Task Id List: $taskIdList\"\n\n$queuedTasks = @(Get-QueuedOctopusTasks -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)\n\nif ($queuedTasks.Length -eq 0)\n{\n    Write-OctopusSuccess \"No queued tasks found that can block a deployment.  Exiting.\"\n    exit 0\n}\n\n$octopusInformation = @{\n    TaskIdList = @(Get-SplitItemIntoArray -itemToSplit $taskIdList)\n}\n\nif ([string]::IsNullOrWhiteSpace($taskIdList))\n{\n    $octopusInformation.SpaceList = @(Get-OctopusSpaceList -spaceList $spaceList -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey)\n\n    $octopusInformation.EnvironmentList = @(Get-OctopusItemList -octopusSpaceList $octopusInformation.SpaceList -itemList $environmentList -itemType \"Environment\" -endpoint \"environments\" -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)\n    $octopusInformation.HasEnvironmentFilter = $octopusInformation.EnvironmentList.Count -gt 0\n\n    $octopusInformation.ProjectList = @(Get-OctopusItemList -octopusSpaceList $octopusInformation.SpaceList -itemList $projectList -itemType \"Project\" -endpoint \"projects\" -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)\n    $octopusInformation.HasProjectFilter = $octopusInformation.ProjectList.Count -gt 0\n\n    $octopusInformation.TenantList = @(Get-OctopusItemList -octopusSpaceList $octopusInformation.SpaceList -itemList $tenantList -itemType \"Tenant\" -endpoint \"tenants\" -octopusApiKey $octopusApiKey -octopusUrl $octopusUrl)\n    $octopusInformation.HasTenantFilter = $octopusInformation.TenantList.Count -gt 0\n\n    if ($octopusInformation.EnvironmentList.Count -eq 0 -and $octopusInformation.ProjectList.Count -eq 0 -and $octopusInformation.TenantList.Count -eq 0)\n    {\n        Write-OctopusCritical \"No environments OR projects OR tenants provided to filter on.  You must provide at least one environment OR project OR tenant.\"\n        exit 1\n    }\n\n    Write-OctopusSuccess \"Going to look for any $taskType in the spaces ($(($octopusInformation.SpaceList | Select-Object -ExpandProperty Id) -join \", \")) matching \"\n    Write-OctopusSuccess \"Environments ($(($octopusInformation.EnvironmentList | Select-Object -ExpandProperty Id) -join \" OR \")) $matchType\"\n    Write-OctopusSuccess \"Projects ($(($octopusInformation.ProjectList | Select-Object -ExpandProperty Id) -join \" OR \")) $matchType\"\n    Write-OctopusSuccess \"Tenants ($(($octopusInformation.TenantList | Select-Object -ExpandProperty Id) -join \" OR \"))\"\n}\nelse\n{\n    Write-OctopusSuccess \"Going to look for the tasks ($($octopusInformation.TaskIdList -join \", \"))\"    \n}\n\n$matchingTasks = @()\n\nWrite-OctopusInformation \"Attempting to find any matching tasks based on the filtering criteria.\"\nforeach ($task in $queuedTasks)\n{\n    if ($octopusInformation.TaskIdList -contains $task.Id)\n    {\n        Write-OctopusInformation \"The task $($task.Id) was found in the list of task ids.  Adding to list.\"\n        $matchingTasks += $task\n\n        continue\n    }\n\n    if ($task.Name -ne \"Deploy\" -and $task.Name -ne \"RunbookRun\")\n    {\n        Write-Information \"The task not a deployment or a runbook run.  It is $($task.Description).  Moving onto next task.\"\n        continue\n    }\n\n    if ($taskType -ne \"Both\" -and $taskType -ne $task.Name)\n    {\n        Write-Information \"You have selected to filter on $taskType only and this task is a $($task.Name).  Moving onto the next task.\"\n        continue\n    }\n\n    if ((Test-OctopusListHasId -octopusList $octopusInformation.SpaceList -octopusId $task.SpaceId) -eq $false)\n    {\n        Write-Information \"The task is not for any spaces specified.  Moving onto the next task.\"\n        continue\n    }\n\n    if ($task.Name -eq \"RunbookRun\")\n    {\n        $itemDetails = Get-RunbookRunDetailsFromTask -runbookTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n    }\n    else\n    {\n        $itemDetails = Get-DeploymentDetailsFromTask -deploymentTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n    }        \n\n    $matchesEnvironmentFilter = $octopusInformation.HasEnvironmentFilter -eq $true -and (Test-OctopusListHasId -octopusList $octopusInformation.EnvironmentList -octopusId $itemDetails.EnvironmentId)\n    Write-OctopusInformation \"$($task.Name) $($itemDetails.Id) Matches Environment Filter $matchesEnvironmentFilter\"\n\n    $matchesProjectFilter = $octopusInformation.HasProjectFilter -eq $true -and (Test-OctopusListHasId -octopusList $octopusInformation.ProjectList -octopusId $itemDetails.ProjectId)\n    Write-OctopusInformation \"$($task.Name) $($itemDetails.Id) Matches Project Filter $matchesProjectFilter\"\n\n    $matchesTenantFilter = $octopusInformation.HasTenantFilter -eq $true -and $null -ne $itemDetails.TenantId -and (Test-OctopusListHasId -octopusList $octopusInformation.TenantList -octopusId $itemDetails.TenantId)\n    Write-OctopusInformation \"$($task.Name) $($itemDetails.Id) Matches Tenant Filter $matchesTenantFilter\"\n\n    if ($matchType -eq \"Or\" -and ($matchesTenantFilter -eq $true -or $matchesProjectFilter -eq $true -or $matchesEnvironmentFilter -eq $true))\n    {\n        Write-OctopusInformation \"The match type was OR and one of the filters matched, adding this task to the matching list\"\n        $matchingTasks += $task\n\n        continue\n    }\n\n    Write-OctopusInformation \"The match type is AND, checking to see if the task matches all the filters\"\n\n    if ($octopusInformation.HasEnvironmentFilter -eq $true -and $matchesEnvironmentFilter -eq $false)\n    {\n        Write-OctopusInformation \"The environment filter was provided and the environment $($itemDetails.EnvironmentId) didn't match any environments.  Moving onto next task.\"\n        continue\n    }\n\n    if ($octopusInformation.HasProjectFilter -eq $true -and $matchesProjectFilter -eq $false)\n    {\n        Write-OctopusInformation \"The project filter was provided and the project $($itemDetails.ProjectId) didn't match any projects.  Moving onto next task.\"\n        continue\n    }\n\n    if ($octopusInformation.HasTenantFilter -eq $true -and $matchesTenantFilter -eq $false)\n    {\n        Write-OctopusInformation \"The tenant filter was provided and the tenant $($itemDetails.TenantId) didn't match any tenants.  Moving onto next task.\"\n        continue\n    }\n\n    $matchingTasks += $task\n}\n\nif ($matchingTasks.Count -eq 0)\n{\n    Write-OctopusSuccess \"No matching tasks found, exiting.\"\n    exit 0\n}\n\nWrite-OctopusSuccess \"Matching tasks found, checking where they are in the queue.\"\n\n$matchingTaskCounter = 0\n\nWrite-OctopusInformation \"Looping through all the queued tasks again to find which tasks to cancel.\"\nforeach ($task in $queuedTasks)\n{        \n    if ((Test-OctopusListHasId -octopusList $matchingTasks -octopusId $task.Id))\n    {\n        $matchingTaskCounter += 1\n        Write-OctopusInformation \"The task $($task.Id) is one we want to move to the top of queue, leaving as is.\"\n\n        if ($matchingTaskCounter -eq $matchingTasks.Count)\n        {\n            Write-OctopusSuccess \"All the matching tasks we want to move to the top of the queue have been found, exiting\"\n            break\n        }\n\n        continue\n    }\n\n    $updatedTask = Invoke-OctopusApi -endPoint \"tasks/$($task.Id)\" -octopusUrl $octopusUrl -spaceId $null -apiKey $octopusApiKey -method \"GET\" -ignoreCache $true\n\n    if ($updatedTask.HasBeenPickedUpByProcssor -eq $true)\n    {\n        Write-OctopusInformation \"The task $($task.Id) has already been picked up and started processing, moving on.\"\n        continue\n    }\n\n    $canceledTaskResult = Invoke-OctopusApi -endPoint \"tasks/$($task.Id)/cancel\" -octopusUrl $octopusUrl -spaceId $null -apiKey $octopusApiKey -method \"POST\" -ignoreCache $true\n\n    Write-OctopusSuccess \"Task $($canceledTaskResult.Description) has been successfully cancelled\" \n\n    if ($task.Name -eq \"Deploy\")\n    {\n        Write-OctopusInformation \"Task $($task.Id) is a deployment, setting up a redeploy.\"\n\n        $deploymentInfo = Get-DeploymentDetailsFromTask -deploymentTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n\n        $bodyRaw = @{\n            EnvironmentId = $deploymentInfo.EnvironmentId\n            ExcludedMachineIds = $deploymentInfo.ExcludedMachineIds\n            ForcePackageDownload = $deploymentInfo.ForcePackageDownload\n            ForcePackageRedeployment = $deploymentInfo.ForcePackageRedeployment\n            FormValues = $deploymentInfo.FormValues\n            QueueTime = $null\n            QueueTimeExpiry = $null\n            ReleaseId = $deploymentInfo.ReleaseId\n            SkipActions = $deploymentInfo.SkipActions\n            SpecificMachineIds = $deploymentInfo.SpecificMachineIds\n            TenantId = $deploymentInfo.TenantId\n            UseGuidedFailure = $deploymentInfo.UseGuidedFailure\n        } \n\n        $newDeployment = Invoke-OctopusApi -endPoint \"deployments\" -spaceId $task.SpaceId -octopusUrl $octopusUrl -apiKey $octopusApiKey -method \"POST\" -item $bodyRaw\n\n        Write-OctopusSuccess \"$($task.Description) has been successfully resubmitted with the new id $($newDeployment.TaskId)\"        \n    }\n\n    if ($task.Name -eq \"RunbookRun\")\n    {\n        Write-OctopusInformation \"Task $($task.Id) is a runbook run, configuring a re-run.\"\n\n        $runbookInfo = Get-RunbookRunDetailsFromTask -runbookTask $task -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey\n\n        $bodyRaw = @{\n            EnvironmentId = $runbookInfo.EnvironmentId\n            ExcludedMachineIds = $runbookInfo.ExcludedMachineIds\n            ForcePackageDownload = $runbookInfo.ForcePackageDownload\n            ForcePackageRedeployment = $runbookInfo.ForcePackageRedeployment\n            FormValues = $runbookInfo.FormValues\n            QueueTime = $null\n            QueueTimeExpiry = $null\n            RunbookId = $runbookInfo.RunbookId\n            SkipActions = $runbookInfo.SkipActions\n            SpecificMachineIds = $runbookInfo.SpecificMachineIds\n            TenantId = $runbookInfo.TenantId\n            UseGuidedFailure = $runbookInfo.UseGuidedFailure\n            FrozenRunbookProcessId = $runbookInfo.FrozenRunbookProcessId\n            RunbookSnapshotId = $runbookInfo.RunbookSnapshotId            \n        } \n\n        $newDeployment = Invoke-OctopusApi -endPoint \"runbookRuns\" -spaceId $task.SpaceId -octopusUrl $octopusUrl -apiKey $octopusApiKey -method \"POST\" -item $bodyRaw\n\n        Write-OctopusSuccess \"$($task.Description) has been successfully resubmitted with the new id $($newDeployment.TaskId)\" \n    }\n}\n\nWrite-OctopusSuccess \"Finished re-prioritizing tasks\""
  },
  "Category": "Octopus",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/re-prioritize-octopus-deploy-tasks.json",
  "Website": "/step-templates/c9d5c96f-f731-4e6c-b9b3-d93f84a9bb74",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRFT6Tl////L5Pg8vj9Y67omsvwPJrisdfzfbzs5fL7y+T32Ov5isLucLXqvt31CJPHWwAABMJJREFUeNrs3deW4jAMAFDF3U75/89dlp0ZhiU4blJEjvQ8hYubLJsA00UCBCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEIhD8kJm+t+QprfdKfB9HbYpx6CWfspj8HMi+gMgHL/AmQA8W3JTKH+ALFvzCeL0RbpyoCPE9IJeNOSQwh5Z3qd6yRGWQ2qi2cZQWxqj1WzQYSjeoJmJlAklOd4VlArOqPhQEkqBERToeMcfRJBkC0Uep8CfBpjz4JsHJ0zF3dkEWNje0kiB/sUC6eApndaIiCMyAa1PiwJ0AWhRGJHJJQHG2dC7h1rNbO1QOxSA7lNCkkKrQIpJCAB1GREILYIC1NAiwbpKFJgGWDNExcwGstfExcZBCHC6nOglshHtmhViLIig1RNBCN7qjtW8C0Z1UvJcC1Z9XmwMBzzvobmgAyEzgq91dtEEsBsQSQQAFZCSBAATEEEApHZbrVBIkkEIUPSVeB+KtALA0kXQUSrwKZBCIQBnk8Y4i5CsReBeKvkqLM+BCSDWJlrZFvGk9SRTHshkgjZCGAaArIxm3H3grhVzFlW2msfl1ca79UJ1bofYvsDHHlNdTZnlh5MghuPd5NdBDUNZHyCkfktIh03XzALGRPlBDPac7qgWjHZzWcmF5zmmkhidMQ6boKiDXcDTUEaylZqCGJ0Vjvu/fLJtHqhSANEvqb2OYqkOUqEHuVMbJcZdZCGiPhKhC4yjqiIjEE7XThMp8fAWII3mY3kUIQD+AMKQTzPiBhgQ63HlT/KSvgtoi0dq5mCPah1UIE0eh3sT0NhOByvKeAkFzi8PgQomumFhsyOxpIzZN4gLOj5plVwNpR0b2AuePWKBEHQu24pSsJA+LVCeHHQxZ1SiyDIdqok8IOhSSnTottHEQTdyt4ettAj4KkzA4dMikk2Dht2S5ptm1vswnPDxn0YyDZ5oDM3iToo2T5voWaYe+Q+vdjH80QyAzZhCgcDtLMI1Tmtz9w++XHgziHQHJJu/OZ3bs9Xn8gQ72NcP3dKqEfkp10F51xhoIi2I91R+LurXV/5q7pH+wx061CzO16oSQleMyr8fXvwMA0Pro8432DPD/ySx8XrHfSuDAM8n6UhnjQabaiXf5Bq/lREHvEeNtn1rJ08+C/uXkQZHeguxAPC3UvtcJYUogLzZX5hhZZvS6onG5lxXtzWGaygwb79vT/IXhdlNibwlKYOR6T8xjI7W8n+xV7T+GH4tMzWwR+lZhRkJYSsC0thpmCYqyngOz3rN2FLBZ2wZflBCggUHF0Vnp88JKienzIXLSEZCZqU7IKr/gQW9yx3pzV7Y9kvWZWTRRIqDmTtRUnU7b2lLcTYmoqHqnmiO1poER0SPkAeZMAZxaJx0Y3TCdAclsIqDz03ALcyxfTCZBsthoGXWmigGyVhWPLFJJfuuKQWycoEFdXbH4dJJoJxNR1eD/kshz6yn48cF8yW8sFoitflB1w6Q8n+/15Za7oA17/pYNmYgP5fmWm8L1NOHPWgK8kuFew1/JXtOA0yJCv7ah7X8ObUuT5kObU30+fDZm8+zqP+HTIpK0xQ796b5Kv2hSIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIpBf8UeAAQAEjtYmlDTcCgAAAABJRU5ErkJggg==",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Monday, February 22, 2021