Octopus.Script exported 2024-07-23 by millerjn21 belongs to ‘Octopus’ category.
This step will kick off a runbook. The runbook can exist in the same space and project, or it can exist on a different instance altogether.
Please Note: Prompted variable values have to be text or sensitive variables. Variable types such as AWS or Azure accounts will not work.
This step should be called from a worker machine. If it is called from a target and the runbook runs on the same target you run the risk of a deadlock.
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
Runbook Name
Run.Runbook.Name =
Required
The name of the runbook to run.
Base Url
Run.Runbook.Base.Url = #{Octopus.Web.ServerUri}
Required
The base URL of your instance, IE https://samples.octopus.app. Defaults to the system variable Octopus.Web.ServerUri.
Api Key
Run.Runbook.Api.Key =
Required
The API key of a user who has permissions to run the runbook specified
Runbook Space
Run.Runbook.Space.Name = #{Octopus.Space.Name}
Required
The name of the space the child project is located in. Defaults to the current space name using the variable #{Octopus.Space.Name}
.
Project name
Run.Runbook.Project.Name =
Optional
The name of the project containing the runbook. If the project name is not specified then it will use the first runbook with the matching runbook name it can find.
Environment Name
Run.Runbook.Environment.Name = #{Octopus.Environment.Name}
Required
Name of the environment to run the runbook on. The default is the current environment name using the system variable #{Octopus.Environment.Name}
.
Tenant Name
Run.Runbook.Tenant.Name =
Optional
Name of Tenant to run the runbook for. If you want to run a runbook using the same tenant as the deployment or runbook run then use the system variable #{Octopus.Deployment.Tenant.Name}
.
Use Published Snapshot
Run.Runbook.UsePublishedSnapShot = True
Indicates if the run should use the most recent published snapshot. When not set it will use the most recent snapshot, regardless if it was published or not.
Default is to use only published snapshots.
If you select to use unpublished snapshots then the runbook will:
- If no published snapshots exist, then it will create a new unpublished snapshot on each run.
- Create a new snapshot if anything has changed since the last published snapshot.
- Use the existing published snapshot if nothing has changed since it was last published.
Please note: A runbook is considered “changed” when any of the following are true:
- Runbook process has changed.
- Project variables have changed.
- Library Variable Sets referenced by the project have changed.
- A newer version of any referenced packages is found.
Wait for finish
Run.Runbook.Waitforfinish = True
Indicates if the process should be paused and wait for the runbook to finish
Use Guided Failure
Run.Runbook.UseGuidedFailure =
Should the runbook run use guided failure (not a good idea if you are waiting for this to finish)
Cancel Seconds
Run.Runbook.CancelInSeconds = 1800
Optional
The number of seconds to wait before canceling the runbook run. Default is 1800 seconds (30 minutes).
Prompted Variable Values
Run.Runbook.PromptedVariables =
Optional
Values for any prompted variables for the runbook. Each new line represents a new variable. This will only work with string variable types, text and sensitive values.
Use the format Name::Value IE:
PromptedVariableName::My Super Awesome Value
OtherPromptedVariable::Other Super Awesome Value
Scheduling
Run.Runbook.DateTime = N/A
Optional
Schedule the runbook to run in the future. Please note, if this is set, the Wait for Finish
option is ignored.
Uses DateTime.TryParse
and specific keywords to determine the value sent in. Supported formats:
7:00 PM
will deploy at 7:00 PM today21:00
will deploy at 21:00 hours or 9 PM todayYYYY-MM-DD HH:mm:ss
or2021-01-14 21:00:00
will deploy at 9 PM on the 14th of January, 2021YYYY/MM/DD HH:mm:ss
or2021/03/20 22:00:00
will deploy at 10 PM on the 20th of March, 2021MM/DD/YYYY HH:mm:ss
or06/25/2021 19:00:00
will deploy at 7 PM on the 25th of June, 2021DD MMM YYYY HH:mm:ss
or01 Jan 2021 18:00:00
will deploy at 6 PM on the 1st of January, 2021Tomorrow HH:mm:ss
orTomorrow 18:00:00
will deploy at 6 PM tomorrow
Uses the Octopus Server’s Timezone. The queue expiry time will be set to 1 hour from the supplied date.
Default is N/A
or not applicable.
Machine List
Run.Runbook.Machines = N/A
Optional
A comma-separated list of Machine Ids or Machine Names to target with this runbook.
Please Note: The step template will remove any machines that cannot be found or are not applicable to the runbook.
The default is N/A
. Set to #{Octopus.Deployment.Machines}
if you want to target the same machines as the current runbook run or deployment.
Auto approve runbook manual interventions
Run.Runbook.AutoApproveManualInterventions = No
Optional
If the child project has manual interventions the step will look for manual interventions in the parent project or parent runbook.
When a manual intervention in the parent project or parent runbook is found it will check that user’s assigned teams. If that user’s assigned teams can approve the child project it will do so.
Please note, the user associated with the API key must be able to approve the child project manual interventions as well.
The default is No
, allow this to happen. Set it to Yes
to enable this functionality.
Custom Manual Intervention Notes Toggle
Run.Runbook.CustomNotes.Toggle = False
Check this box if you would like custom notes to be submitted with the automatic manual intervention approval.
Custom Manual Intervention Approval Notes
Run.Runbook.CustomNotes =
Use this field to supply custom manual intervention notes if the above toggle is checked.
Environment name to pull approvals from
Run.Runbook.ManualIntervention.EnvironmentToUse = #{Octopus.Environment.Name}
Optional
The name of the environment you wish to pull the approvals from for the parent project or parent runbook. It will look at all the deployments for the current release of the parent project and select the latest deployment to the specified environment. If this step is being called from a runbook, it will look at the latest runbook run for the environment specified
Used when you are deploying to Production
but want to pull the approvals from Staging
or a Prod Approval
environment.
Defaults to the current environment name.
Script body
Steps based on this template will execute the following PowerShell script.
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
# Octopus Variables
$octopusSpaceId = $OctopusParameters["Octopus.Space.Id"]
$parentTaskId = $OctopusParameters["Octopus.Task.Id"]
$parentReleaseId = $OctopusParameters["Octopus.Release.Id"]
$parentChannelId = $OctopusParameters["Octopus.Release.Channel.Id"]
$parentEnvironmentId = $OctopusParameters["Octopus.Environment.Id"]
$parentRunbookId = $OctopusParameters["Octopus.Runbook.Id"]
$parentEnvironmentName = $OctopusParameters["Octopus.Environment.Name"]
$parentReleaseNumber = $OctopusParameters["Octopus.Release.Number"]
# Step Template Parameters
$runbookRunName = $OctopusParameters["Run.Runbook.Name"]
$runbookBaseUrl = $OctopusParameters["Run.Runbook.Base.Url"]
$runbookApiKey = $OctopusParameters["Run.Runbook.Api.Key"]
$runbookEnvironmentName = $OctopusParameters["Run.Runbook.Environment.Name"]
$runbookTenantName = $OctopusParameters["Run.Runbook.Tenant.Name"]
$runbookWaitForFinish = $OctopusParameters["Run.Runbook.Waitforfinish"]
$runbookUseGuidedFailure = $OctopusParameters["Run.Runbook.UseGuidedFailure"]
$runbookUsePublishedSnapshot = $OctopusParameters["Run.Runbook.UsePublishedSnapShot"]
$runbookPromptedVariables = $OctopusParameters["Run.Runbook.PromptedVariables"]
$runbookCancelInSeconds = $OctopusParameters["Run.Runbook.CancelInSeconds"]
$runbookProjectName = $OctopusParameters["Run.Runbook.Project.Name"]
$runbookCustomNotesToggle = $OctopusParameters["Run.Runbook.CustomNotes.Toggle"]
$runbookCustomNotes = $OctopusParameters["Run.Runbook.CustomNotes"]
$runbookSpaceName = $OctopusParameters["Run.Runbook.Space.Name"]
$runbookFutureDeploymentDate = $OctopusParameters["Run.Runbook.DateTime"]
$runbookMachines = $OctopusParameters["Run.Runbook.Machines"]
$autoApproveRunbookRunManualInterventions = $OctopusParameters["Run.Runbook.AutoApproveManualInterventions"]
$approvalEnvironmentName = $OctopusParameters["Run.Runbook.ManualIntervention.EnvironmentToUse"]
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
)
if ([string]::IsNullOrWhiteSpace($SpaceId))
{
$url = "$OctopusUrl/api/$EndPoint"
}
else
{
$url = "$OctopusUrl/api/$spaceId/$EndPoint"
}
try
{
if ($null -eq $item)
{
Write-Verbose "No data to post or put, calling bog standard invoke-restmethod for $url"
return Invoke-RestMethod -Method $method -Uri $url -Headers @{"X-Octopus-ApiKey" = "$ApiKey" } -ContentType 'application/json; charset=utf-8'
}
$body = $item | ConvertTo-Json -Depth 10
Write-Verbose $body
Write-Host "Invoking $method $url"
return Invoke-RestMethod -Method $method -Uri $url -Headers @{"X-Octopus-ApiKey" = "$ApiKey" } -Body $body -ContentType 'application/json; charset=utf-8'
}
catch
{
if ($null -ne $_.Exception.Response)
{
if ($_.Exception.Response.StatusCode -eq 401)
{
Write-Error "Unauthorized error returned from $url, please verify API key and try again"
}
elseif ($_.Exception.Response.statusCode -eq 403)
{
Write-Error "Forbidden error returned from $url, please verify API key and try again"
}
else
{
Write-Error -Message "Error calling $url $($_.Exception.Message) StatusCode: $($_.Exception.Response.StatusCode )"
}
}
else
{
Write-Verbose $_.Exception
}
}
Throw "There was an error calling the Octopus API please check the log for more details"
}
function Test-RequiredValues
{
param (
$variableToCheck,
$variableName
)
if ([string]::IsNullOrWhiteSpace($variableToCheck) -eq $true)
{
Write-OctopusCritical "$variableName is required."
return $false
}
return $true
}
function GetCheckBoxBoolean
{
param (
[string]$Value
)
if ([string]::IsNullOrWhiteSpace($value) -eq $true)
{
return $false
}
return $value -eq "True"
}
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."
Exit 1
}
$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."
exit 1
}
if ($item -is [array])
{
Write-OctopusCritical "More than one item exists with the name $itemName. Exiting with an exit code of 1."
exit 1
}
return $item
}
function Get-OctopusItemFromListEndpoint
{
param(
$endpoint,
$itemNameToFind,
$itemType,
$defaultUrl,
$octopusApiKey,
$spaceId,
$defaultValue
)
if ([string]::IsNullOrWhiteSpace($itemNameToFind))
{
return $defaultValue
}
Write-OctopusInformation "Attempting to find $itemType with the name of $itemNameToFind"
$itemList = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "$($endpoint)?partialName=$([uri]::EscapeDataString($itemNameToFind))&skip=0&take=100" -spaceId $spaceId -apiKey $octopusApiKey -method "GET"
$item = Get-FilteredOctopusItem -itemList $itemList -itemName $itemNameToFind
Write-OctopusInformation "Successfully found $itemNameToFind with id of $($item.Id)"
return $item
}
function Get-MachineIdsFromMachineNames
{
param (
$targetMachines,
$defaultUrl,
$spaceId,
$octopusApiKey
)
$targetMachineList = $targetMachines -split ","
$translatedList = @()
foreach ($machineName in $targetMachineList)
{
Write-OctopusVerbose "Translating $machineName to an Id. First checking to see if it is already an Id."
if ($machineName.Trim() -like "Machines*")
{
Write-OctopusVerbose "$machineName is already an Id, no need to look that up."
$translatedList += $machineName
continue
}
$machineObject = Get-OctopusItemFromListEndpoint -itemNameToFind $machineName.Trim() -itemType "Deployment Target" -endpoint "machines" -defaultValue $null -spaceId $spaceId -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey
$translatedList += $machineObject.Id
}
return $translatedList
}
function Get-RunbookSnapshotIdToRun
{
param (
$runbookToRun,
$runbookUsePublishedSnapshot,
$defaultUrl,
$octopusApiKey,
$spaceId
)
$runbookSnapShotIdToUse = $runbookToRun.PublishedRunbookSnapshotId
Write-OctopusInformation "The last published snapshot for $runbookRunName is $runbookSnapShotIdToUse"
if ($null -eq $runbookSnapShotIdToUse -and $runbookUsePublishedSnapshot -eq $true)
{
Write-OctopusCritical "Use Published Snapshot was set; yet the runbook doesn't have a published snapshot. Exiting."
Exit 1
}
if ($runbookUsePublishedSnapshot -eq $true)
{
Write-OctopusInformation "Use published snapshot set to true, using the published runbook snapshot."
return $runbookSnapShotIdToUse
}
if ($null -eq $runbookToRun.PublishedRunbookSnapshotId)
{
Write-OctopusInformation "There have been no published runbook snapshots, going to create a new snapshot."
return New-RunbookUnpublishedSnapshot -runbookToRun $runbookToRun -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId
}
$runbookSnapShotTemplate = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "runbookSnapshots/$($runbookToRun.PublishedRunbookSnapshotId)/runbookRuns/template" -method "Get" -item $null
if ($runbookSnapShotTemplate.IsRunbookProcessModified -eq $false -and $runbookSnapShotTemplate.IsVariableSetModified -eq $false -and $runbookSnapShotTemplate.IsLibraryVariableSetModified -eq $false)
{
Write-OctopusInformation "The runbook has not been modified since the published snapshot was created. Checking to see if any of the packages have a new version."
$runbookSnapShot = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "runbookSnapshots/$($runbookToRun.PublishedRunbookSnapshotId)" -method "Get" -item $null
$snapshotTemplate = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "runbooks/$($runbookToRun.Id)/runbookSnapShotTemplate" -method "Get" -item $null
foreach ($package in $runbookSnapShot.SelectedPackages)
{
foreach ($templatePackage in $snapshotTemplate.Packages)
{
if ($package.StepName -eq $templatePackage.StepName -and $package.ActionName -eq $templatePackage.ActionName -and $package.PackageReferenceName -eq $templatePackage.PackageReferenceName)
{
$packageVersion = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "feeds/$($templatePackage.FeedId)/packages/versions?packageId=$($templatePackage.PackageId)&take=1" -method "Get" -item $null
if ($packageVersion -ne $package.Version)
{
Write-OctopusInformation "A newer version of a package was found, going to use that and create a new snapshot."
return New-RunbookUnpublishedSnapshot -runbookToRun $runbookToRun -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId
}
}
}
}
Write-OctopusInformation "No new package versions have been found, using the published snapshot."
return $runbookToRun.PublishedRunbookSnapshotId
}
Write-OctopusInformation "The runbook has been modified since the snapshot was created, creating a new one."
return New-RunbookUnpublishedSnapshot -runbookToRun $runbookToRun -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId
}
function New-RunbookUnpublishedSnapshot
{
param (
$runbookToRun,
$defaultUrl,
$octopusApiKey,
$spaceId
)
$octopusProject = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "projects/$($runbookToRun.ProjectId)" -method "Get" -item $null
$snapshotTemplate = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "runbooks/$($runbookToRun.Id)/runbookSnapShotTemplate" -method "Get" -item $null
$runbookPackages = @()
foreach ($package in $snapshotTemplate.Packages)
{
$packageVersion = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "feeds/$($package.FeedId)/packages/versions?packageId=$($package.PackageId)&take=1" -method "Get" -item $null
if ($packageVersion.TotalResults -le 0)
{
Write-Error "Unable to find a package version for $($package.PackageId). This is required to create a new unpublished snapshot. Exiting."
exit 1
}
$runbookPackages += @{
StepName = $package.StepName
ActionName = $package.ActionName
Version = $packageVersion.Items[0].Version
PackageReferenceName = $package.PackageReferenceName
}
}
$runbookSnapShotRequest = @{
FrozenProjectVariableSetId = "variableset-$($runbookToRun.ProjectId)"
FrozenRunbookProcessId = $($runbookToRun.RunbookProcessId)
LibraryVariableSetSnapshotIds = @($octopusProject.IncludedLibraryVariableSetIds)
Name = $($snapshotTemplate.NextNameIncrement)
ProjectId = $($runbookToRun.ProjectId)
ProjectVariableSetSnapshotId = "variableset-$($runbookToRun.ProjectId)"
RunbookId = $($runbookToRun.Id)
SelectedPackages = $runbookPackages
}
$newSnapShot = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint "runbookSnapshots" -method "POST" -item $runbookSnapShotRequest
return $($newSnapShot.Id)
}
function Get-ProjectSlug
{
param
(
$runbookToRun,
$projectToUse,
$defaultUrl,
$spaceId,
$octopusApiKey
)
if ($null -ne $projectToUse)
{
return $projectToUse.Slug
}
$project = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -endPoint "projects/$($runbookToRun.ProjectId)" -method "GET" -item $null
return $project.Slug
}
function Get-RunbookFormValues
{
param (
$runbookPreview,
$runbookPromptedVariables
)
$runbookFormValues = @{}
if ([string]::IsNullOrWhiteSpace($runbookPromptedVariables) -eq $true)
{
return $runbookFormValues
}
$promptedValueList = @(($runbookPromptedVariables -Split "`n").Trim())
Write-OctopusInformation $promptedValueList.Length
foreach($element in $runbookPreview.Form.Elements)
{
$nameToSearchFor = $element.Control.Name
$uniqueName = $element.Name
$isRequired = $element.Control.Required
$promptedVariablefound = $false
Write-OctopusInformation "Looking for the prompted variable value for $nameToSearchFor"
foreach ($promptedValue in $promptedValueList)
{
$splitValue = $promptedValue -Split "::"
Write-OctopusInformation "Comparing $nameToSearchFor with provided prompted variable $($promptedValue[0])"
if ($splitValue.Length -gt 1)
{
if ($nameToSearchFor -eq $splitValue[0])
{
Write-OctopusInformation "Found the prompted variable value $nameToSearchFor"
$runbookFormValues[$uniqueName] = $splitValue[1]
$promptedVariableFound = $true
break
}
}
}
if ($promptedVariableFound -eq $false -and $isRequired -eq $true)
{
Write-OctopusCritical "Unable to find a value for the required prompted variable $nameToSearchFor, exiting"
Exit 1
}
}
return $runbookFormValues
}
function Invoke-OctopusDeployRunbook
{
param (
$runbookBody,
$runbookWaitForFinish,
$runbookCancelInSeconds,
$projectNameForUrl,
$defaultUrl,
$octopusApiKey,
$spaceId,
$parentTaskApprovers,
$autoApproveRunbookRunManualInterventions,
$parentProjectName,
$parentReleaseNumber,
$approvalEnvironmentName,
$parentRunbookId,
$parentTaskId
)
$runbookResponse = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -item $runbookBody -method "POST" -endPoint "runbookRuns"
$runbookServerTaskId = $runBookResponse.TaskId
Write-OctopusInformation "The task id of the new task is $runbookServerTaskId"
$runbookRunId = $runbookResponse.Id
Write-OctopusInformation "The runbook run id is $runbookRunId"
Write-OctopusSuccess "Runbook was successfully invoked, you can access the launched runbook [here]($defaultUrl/app#/$spaceId/projects/$projectNameForUrl/operations/runbooks/$($runbookBody.RunbookId)/snapshots/$($runbookBody.RunbookSnapShotId)/runs/$runbookRunId)"
if ($runbookWaitForFinish -eq $false)
{
Write-OctopusInformation "The wait for finish setting is set to no, exiting step"
return
}
if ($null -ne $runbookBody.QueueTime)
{
Write-OctopusInformation "The runbook queue time is set. Exiting step"
return
}
Write-OctopusSuccess "The setting to wait for completion was set, waiting until task has finished"
$startTime = Get-Date
$currentTime = Get-Date
$dateDifference = $currentTime - $startTime
$taskStatusUrl = "tasks/$runbookServerTaskId"
$numberOfWaits = 0
While ($dateDifference.TotalSeconds -lt $runbookCancelInSeconds)
{
Write-OctopusInformation "Waiting 5 seconds to check status"
Start-Sleep -Seconds 5
$taskStatusResponse = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -endPoint $taskStatusUrl -method "GET" -item $null
$taskStatusResponseState = $taskStatusResponse.State
if ($taskStatusResponseState -eq "Success")
{
Write-OctopusSuccess "The task has finished with a status of Success"
exit 0
}
elseif($taskStatusResponseState -eq "Failed" -or $taskStatusResponseState -eq "Canceled")
{
Write-OctopusSuccess "The task has finished with a status of $taskStatusResponseState status, stopping the run/deployment"
exit 1
}
elseif($taskStatusResponse.HasPendingInterruptions -eq $true)
{
if ($autoApproveRunbookRunManualInterventions -eq "Yes")
{
Submit-RunbookRunForAutoApproval -createdRunbookRun $createdRunbookRun -parentTaskApprovers $parentTaskApprovers -defaultUrl $DefaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId -parentProjectName $parentProjectName -parentReleaseNumber $parentReleaseNumber -parentEnvironmentName $approvalEnvironmentName -parentRunbookId $parentRunbookId -parentTaskId $parentTaskId
}
else
{
if ($numberOfWaits -ge 10)
{
Write-OctopusSuccess "The child project has pending manual intervention(s). Unless you approve it, this task will time out."
}
else
{
Write-OctopusInformation "The child project has pending manual intervention(s). Unless you approve it, this task will time out."
}
}
}
$numberOfWaits += 1
if ($numberOfWaits -ge 10)
{
Write-OctopusSuccess "The task state is currently $taskStatusResponseState"
$numberOfWaits = 0
}
else
{
Write-OctopusInformation "The task state is currently $taskStatusResponseState"
}
$startTime = $taskStatusResponse.StartTime
if ($startTime -eq $null -or [string]::IsNullOrWhiteSpace($startTime) -eq $true)
{
Write-OctopusInformation "The task is still queued, let's wait a bit longer"
$startTime = Get-Date
}
$startTime = [DateTime]$startTime
$currentTime = Get-Date
$dateDifference = $currentTime - $startTime
}
Write-OctopusSuccess "The cancel timeout has been reached, cancelling the runbook run"
$cancelResponse = Invoke-RestMethod "$runbookBaseUrl/api/tasks/$runbookServerTaskId/cancel" -Headers $header -Method Post
Write-OctopusSuccess "Exiting with an error code of 1 because we reached the timeout"
exit 1
}
function Get-QueueDate
{
param (
$futureDeploymentDate
)
if ([string]::IsNullOrWhiteSpace($futureDeploymentDate) -or $futureDeploymentDate -eq "N/A")
{
return $null
}
$addOneDay = $false
$textToParse = $futureDeploymentDate.ToLower()
if ($textToParse -like "tomorrow*")
{
Write-Host "The future date $futureDeploymentDate supplied contains tomorrow, will add one day to whatever the parsed result is."
$addOneDay = $true
$textToParse = $textToParse -replace "tomorrow", ""
}
[datetime]$outputDate = New-Object DateTime
$currentDate = Get-Date
$currentDate = $currentDate.AddMinutes(2)
if ([datetime]::TryParse($textToParse, [ref]$outputDate) -eq $false)
{
Write-OctopusCritical "The suppplied date $textToParse cannot be parsed by DateTime.TryParse. Please verify format and try again. Please [refer to Microsoft's Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.datetime.tryparse) on supported formats."
exit 1
}
Write-Host "The proposed date is $outputDate. Checking to see if this will occur in the past."
if ($addOneDay -eq $true)
{
$outputDate = $outputDate.AddDays(1)
Write-host "The text supplied included tomorrow, adding one day. The new proposed date is $outputDate."
}
if ($currentDate -gt $outputDate)
{
Write-OctopusCritical "The supplied date $futureDeploymentDate is set for the past. All queued deployments must be in the future."
exit 1
}
return $outputDate
}
function Get-QueueExpiryDate
{
param (
$queueDate
)
if ($null -eq $queueDate)
{
return $null
}
return $queueDate.AddHours(1)
}
function Get-RunbookSpecificMachines
{
param (
$defaultUrl,
$octopusApiKey,
$runbookPreview,
$runbookMachines,
$runbookRunName
)
if ($runbookMachines -eq "N/A")
{
return @()
}
if ([string]::IsNullOrWhiteSpace($runbookMachines) -eq $true)
{
return @()
}
$translatedList = Get-MachineIdsFromMachineNames -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId -targetMachines $runbookMachines
$filteredList = @()
foreach ($runbookMachine in $translatedList)
{
$runbookMachineId = $runbookMachine.Trim().ToLower()
Write-OctopusVerbose "Checking if $runbookMachineId is set to run on any of the runbook steps"
foreach ($step in $runbookPreview.StepsToExecute)
{
foreach ($machine in $step.Machines)
{
Write-OctopusVerbose "Checking if $runbookMachineId matches $($machine.Id) and it isn't already in the $($filteredList -join ",")"
if ($runbookMachineId -eq $machine.Id.Trim().ToLower() -and $filteredList -notcontains $machine.Id)
{
Write-OctopusInformation "Adding $($machine.Id) to the list"
$filteredList += $machine.Id
}
}
}
}
if ($filteredList.Length -le 0)
{
Write-OctopusSuccess "The current task is targeting specific machines, but the runbook $runBookRunName does not run against any of these machines $runbookMachines. Skipping this run."
exit 0
}
return $filteredList
}
function Get-ParentTaskApprovers
{
param (
$parentTaskId,
$spaceId,
$defaultUrl,
$octopusApiKey
)
$approverList = @()
if ($null -eq $parentTaskId)
{
Write-OctopusInformation "The deployment task id to pull the approvers from is null, return an empty approver list"
return $approverList
}
Write-OctopusInformation "Getting all the events from the parent project"
$parentEvents = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "events?regardingAny=$parentTaskId&spaces=$spaceId&includeSystem=true" -apiKey $octopusApiKey -method "GET"
foreach ($parentEvent in $parentEvents.Items)
{
Write-OctopusVerbose "Checking $($parentEvent.Message) for manual intervention"
if ($parentEvent.Message -like "Submitted interruption*")
{
Write-OctopusVerbose "The event $($parentEvent.Id) is a manual intervention approval event which was approved by $($parentEvent.Username)."
$approverExists = $approverList | Where-Object {$_.Id -eq $parentEvent.UserId}
if ($null -eq $approverExists)
{
$approverInformation = @{
Id = $parentEvent.UserId;
Username = $parentEvent.Username;
Teams = @()
}
$approverInformation.Teams = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "teammembership?userId=$($approverInformation.Id)&spaces=$spaceId&includeSystem=true" -apiKey $octopusApiKey -method "GET"
Write-OctopusVerbose "Adding $($approverInformation.Id) to the approval list"
$approverList += $approverInformation
}
}
}
return $approverList
}
function Get-ApprovalTaskIdFromDeployment
{
param (
$parentReleaseId,
$approvalEnvironment,
$parentChannelId,
$parentEnvironmentId,
$defaultUrl,
$spaceId,
$octopusApiKey
)
$releaseDeploymentList = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "releases/$parentReleaseId/deployments" -method "GET" -apiKey $octopusApiKey -spaceId $spaceId
$lastDeploymentTime = $(Get-Date).AddYears(-50)
$approvalTaskId = $null
foreach ($deployment in $releaseDeploymentList.Items)
{
if ($deployment.EnvironmentId -ne $approvalEnvironment.Id)
{
Write-OctopusInformation "The deployment $($deployment.Id) deployed to $($deployment.EnvironmentId) which doesn't match $($approvalEnvironment.Id)."
continue
}
Write-OctopusInformation "The deployment $($deployment.Id) was deployed to the approval environment $($approvalEnvironment.Id)."
$deploymentTask = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $null -endPoint "tasks/$($deployment.TaskId)" -apiKey $octopusApiKey -Method "Get"
if ($deploymentTask.IsCompleted -eq $true -and $deploymentTask.FinishedSuccessfully -eq $false)
{
Write-Information "The deployment $($deployment.Id) was deployed to the approval environment, but it encountered a failure, moving onto the next deployment."
continue
}
if ($deploymentTask.StartTime -gt $lastDeploymentTime)
{
$approvalTaskId = $deploymentTask.Id
$lastDeploymentTime = $deploymentTask.StartTime
}
}
if ($null -eq $approvalTaskId)
{
Write-OctopusVerbose "Unable to find a deployment to the environment, determining if it should've happened already."
$channelInformation = Invoke-OctopusApi -octopusUrl $defaultUrl -endPoint "channels/$parentChannelId" -method "GET" -apiKey $octopusApiKey -spaceId $spaceId
$lifecycle = Get-OctopusLifeCycle -channel $channelInformation -defaultUrl $defaultUrl -spaceId $spaceId -OctopusApiKey $octopusApiKey
$lifecyclePhases = Get-LifecyclePhases -lifecycle $lifecycle -defaultUrl $defaultUrl -spaceId $spaceid -OctopusApiKey $octopusApiKey
$foundDestinationFirst = $false
$foundApprovalFirst = $false
foreach ($phase in $lifecyclePhases.Phases)
{
if ($phase.AutomaticDeploymentTargets -contains $parentEnvironmentId -or $phase.OptionalDeploymentTargets -contains $parentEnvironmentId)
{
if ($foundApprovalFirst -eq $false)
{
$foundDestinationFirst = $true
}
}
if ($phase.AutomaticDeploymentTargets -contains $approvalEnvironment.Id -or $phase.OptionalDeploymentTargets -contains $approvalEnvironment.Id)
{
if ($foundDestinationFirst -eq $false)
{
$foundApprovalFirst = $true
}
}
}
$messageToLog = "Unable to find a deployment for the environment $approvalEnvironmentName. Auto approvals are disabled."
if ($foundApprovalFirst -eq $true)
{
Write-OctopusWarning $messageToLog
}
else
{
Write-OctopusInformation $messageToLog
}
return $null
}
return $approvalTaskId
}
function Get-ApprovalTaskIdFromRunbook
{
param (
$parentRunbookId,
$approvalEnvironment,
$defaultUrl,
$spaceId,
$octopusApiKey
)
}
function Get-ApprovalTaskId
{
param (
$autoApproveRunbookRunManualInterventions,
$parentTaskId,
$parentReleaseId,
$parentRunbookId,
$parentEnvironmentName,
$approvalEnvironmentName,
$parentChannelId,
$parentEnvironmentId,
$defaultUrl,
$spaceId,
$octopusApiKey
)
if ($autoApproveRunbookRunManualInterventions -eq $false)
{
Write-OctopusInformation "Auto approvals are disabled, skipping pulling the approval deployment task id"
return $null
}
if ([string]::IsNullOrWhiteSpace($approvalEnvironmentName) -eq $true)
{
Write-OctopusInformation "Approval environment not supplied, using the current environment id for approvals."
return $parentTaskId
}
if ($approvalEnvironmentName.ToLower().Trim() -eq $parentEnvironmentName.ToLower().Trim())
{
Write-OctopusInformation "The approval environment is the same as the current environment, using the current task id $parentTaskId"
return $parentTaskId
}
$approvalEnvironment = Get-OctopusItemFromListEndpoint -itemNameToFind $approvalEnvironmentName -itemType "Environment" -defaultUrl $DefaultUrl -spaceId $spaceId -octopusApiKey $octopusApiKey -defaultValue $null -endpoint "environments"
if ([string]::IsNullOrWhiteSpace($parentReleaseId) -eq $false)
{
return Get-ApprovalTaskIdFromDeployment -parentReleaseId $parentReleaseId -approvalEnvironment $approvalEnvironment -parentChannelId $parentChannelId -parentEnvironmentId $parentEnvironmentId -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId
}
return Get-ApprovalTaskIdFromRunbook -parentRunbookId $parentRunbookId -approvalEnvironment $approvalEnvironment -defaultUrl $defaultUrl -spaceId $spaceId -octopusApiKey $octopusApiKey
}
function Get-OctopusLifecycle
{
param (
$channel,
$defaultUrl,
$spaceId,
$octopusApiKey
)
Write-OctopusInformation "Attempting to find the lifecycle information $($channel.Name)"
if ($null -eq $channel.LifecycleId)
{
$lifecycleName = "Default Lifecycle"
$lifecycleList = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "lifecycles?partialName=$([uri]::EscapeDataString($lifecycleName))&skip=0&take=1" -spaceId $spaceId -apiKey $octopusApiKey -method "GET"
$lifecycle = $lifecycleList.Items[0]
}
else
{
$lifecycle = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "lifecycles/$($channel.LifecycleId)" -spaceId $spaceId -apiKey $octopusApiKey -method "GET"
}
Write-Host "Successfully found the lifecycle $($lifecycle.Name) to use for this channel."
return $lifecycle
}
function Get-LifecyclePhases
{
param (
$lifecycle,
$defaultUrl,
$spaceId,
$octopusApiKey
)
Write-OctopusInformation "Attempting to find the phase in the lifecycle $($lifecycle.Name) with the environment $environmentName to find the previous phase."
if ($lifecycle.Phases.Count -eq 0)
{
Write-OctopusInformation "The lifecycle $($lifecycle.Name) has no set phases, calling the preview endpoint."
$lifecyclePreview = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "lifecycles/$($lifecycle.Id)/preview" -spaceId $spaceId -apiKey $octopusApiKey -method "GET"
$phases = $lifecyclePreview.Phases
}
else
{
Write-OctopusInformation "The lifecycle $($lifecycle.Name) has set phases, using those."
$phases = $lifecycle.Phases
}
Write-OctopusInformation "Found $($phases.Length) phases in this lifecycle."
return $phases
}
function Submit-RunbookRunForAutoApproval
{
param (
$createdRunbookRun,
$parentTaskApprovers,
$defaultUrl,
$octopusApiKey,
$spaceId,
$parentProjectName,
$parentReleaseNumber,
$parentRunbookId,
$parentEnvironmentName,
$parentTaskId
)
Write-OctopusSuccess "The task has a pending manual intervention. Checking parent approvals."
$manualInterventionInformation = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "interruptions?regarding=$($createdRunbookRun.TaskId)" -method "GET" -apiKey $octopusApiKey -spaceId $spaceId
foreach ($manualIntervention in $manualInterventionInformation.Items)
{
if ($manualIntervention.IsPending -eq $false)
{
Write-OctopusInformation "This manual intervention has already been approved. Proceeding onto the next one."
continue
}
if ($manualIntervention.CanTakeResponsibility -eq $false)
{
Write-OctopusSuccess "The user associated with the API key doesn't have permissions to take responsibility for the manual intervention."
Write-OctopusSuccess "If you wish to leverage the auto-approval functionality give the user permissions."
continue
}
$automaticApprover = $null
Write-OctopusVerbose "Checking to see if one of the parent project approvers is assigned to one of the manual intervention teams $($manualIntervention.ResponsibleTeamIds)"
foreach ($approver in $parentTaskApprovers)
{
foreach ($approverTeam in $approver.Teams)
{
Write-OctopusVerbose "Checking to see if $($manualIntervention.ResponsibleTeamIds) contains $($approverTeam.TeamId)"
if ($manualIntervention.ResponsibleTeamIds -contains $approverTeam.TeamId)
{
$automaticApprover = $approver
break
}
}
if ($null -ne $automaticApprover)
{
break
}
}
if ($null -ne $automaticApprover)
{
Write-OctopusSuccess "Matching approver found auto-approving."
if ($manualIntervention.HasResponsibility -eq $false)
{
Write-OctopusInformation "Taking over responsibility for this manual intervention."
$takeResponsiblilityResponse = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "interruptions/$($manualIntervention.Id)/responsible" -method "PUT" -apiKey $octopusApiKey -spaceId $spaceId
Write-OctopusVerbose "Response from taking responsibility $($takeResponsiblilityResponse.Id)"
}
if ([string]::IsNullOrWhiteSpace($parentReleaseNumber) -eq $false)
{
$notes = "Auto-approving this runbook run. Parent project $parentProjectName release $parentReleaseNumber to $parentEnvironmentName with the task id $parentTaskId was approved by $($automaticApprover.UserName). That user is a member of one of the teams this manual intervention requires. You can view that deployment $defaultUrl/app#/$spaceId/tasks/$parentTaskId"
}
else
{
$notes = "Auto-approving this runbook run. Parent project $parentProjectName runbook run $parentRunbookId to $parentEnvironmentName with the task id $parentTaskId was approved by $($automaticApprover.UserName). That user is a member of one of the teams this manual intervention requires. You can view that runbook run $defaultUrl/app#/$spaceId/tasks/$parentTaskId"
}
if ($runbookCustomNotesToggle -eq $true){
$notes = $runbookCustomNotes
}
$submitApprovalBody = @{
Instructions = $null;
Notes = $notes
Result = "Proceed"
}
$submitResult = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint "interruptions/$($manualIntervention.Id)/submit" -method "POST" -apiKey $octopusApiKey -item $submitApprovalBody -spaceId $spaceId
Write-OctopusSuccess "Successfully auto approved the manual intervention $($submitResult.Id)"
}
else
{
Write-OctopusSuccess "Couldn't find an approver to auto-approve the child project. Waiting until timeout or child project is approved."
}
}
}
$runbookWaitForFinish = GetCheckboxBoolean -Value $runbookWaitForFinish
$runbookUseGuidedFailure = GetCheckboxBoolean -Value $runbookUseGuidedFailure
$runbookUsePublishedSnapshot = GetCheckboxBoolean -Value $runbookUsePublishedSnapshot
$runbookCancelInSeconds = [int]$runbookCancelInSeconds
Write-OctopusInformation "Wait for Finish Before Check: $runbookWaitForFinish"
Write-OctopusInformation "Use Guided Failure Before Check: $runbookUseGuidedFailure"
Write-OctopusInformation "Use Published Snapshot Before Check: $runbookUsePublishedSnapshot"
Write-OctopusInformation "Runbook Name $runbookRunName"
Write-OctopusInformation "Runbook Base Url: $runbookBaseUrl"
Write-OctopusInformation "Runbook Space Name: $runbookSpaceName"
Write-OctopusInformation "Runbook Environment Name: $runbookEnvironmentName"
Write-OctopusInformation "Runbook Tenant Name: $runbookTenantName"
Write-OctopusInformation "Wait for Finish: $runbookWaitForFinish"
Write-OctopusInformation "Use Guided Failure: $runbookUseGuidedFailure"
Write-OctopusInformation "Cancel run in seconds: $runbookCancelInSeconds"
Write-OctopusInformation "Use Published Snapshot: $runbookUsePublishedSnapshot"
Write-OctopusInformation "Auto Approve Runbook Run Manual Interventions: $autoApproveRunbookRunManualInterventions"
Write-OctopusInformation "Auto Approve environment name to pull approvals from: $approvalEnvironmentName"
Write-OctopusInformation "Octopus runbook run machines: $runbookMachines"
Write-OctopusInformation "Parent Task Id: $parentTaskId"
Write-OctopusInformation "Parent Release Id: $parentReleaseId"
Write-OctopusInformation "Parent Channel Id: $parentChannelId"
Write-OctopusInformation "Parent Environment Id: $parentEnvironmentId"
Write-OctopusInformation "Parent Runbook Id: $parentRunbookId"
Write-OctopusInformation "Parent Environment Name: $parentEnvironmentName"
Write-OctopusInformation "Parent Release Number: $parentReleaseNumber"
$verificationPassed = @()
$verificationPassed += Test-RequiredValues -variableToCheck $runbookRunName -variableName "Runbook Name"
$verificationPassed += Test-RequiredValues -variableToCheck $runbookBaseUrl -variableName "Base Url"
$verificationPassed += Test-RequiredValues -variableToCheck $runbookApiKey -variableName "Api Key"
$verificationPassed += Test-RequiredValues -variableToCheck $runbookEnvironmentName -variableName "Environment Name"
if ($verificationPassed -contains $false)
{
Write-OctopusInformation "Required values missing"
Exit 1
}
$runbookSpace = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookSpaceName -endpoint "spaces" -spaceId $null -octopusApiKey $runbookApiKey -defaultUrl $runbookBaseUrl -itemType "Space" -defaultValue $octopusSpaceId
$runbookSpaceId = $runbookSpace.Id
$projectToUse = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookProjectName -endpoint "projects" -spaceId $runbookSpaceId -defaultValue $null -itemType "Project" -octopusApiKey $runbookApiKey -defaultUrl $runbookBaseUrl
if ($null -ne $projectToUse)
{
$runbookEndPoint = "projects/$($projectToUse.Id)/runbooks"
}
else
{
$runbookEndPoint = "runbooks"
}
$environmentToUse = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookEnvironmentName -itemType "Environment" -defaultUrl $runbookBaseUrl -spaceId $runbookSpaceId -octopusApiKey $runbookApiKey -defaultValue $null -endpoint "environments"
$runbookToRun = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookRunName -itemType "Runbook" -defaultUrl $runbookBaseUrl -spaceId $runbookSpaceId -endpoint $runbookEndPoint -octopusApiKey $runbookApiKey -defaultValue $null
$runbookSnapShotIdToUse = Get-RunbookSnapshotIdToRun -runbookToRun $runbookToRun -runbookUsePublishedSnapshot $runbookUsePublishedSnapshot -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -spaceId $octopusSpaceId
$projectNameForUrl = Get-ProjectSlug -projectToUse $projectToUse -runbookToRun $runbookToRun -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -spaceId $runbookSpaceId
$tenantToUse = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookTenantName -itemType "Tenant" -defaultValue $null -spaceId $runbookSpaceId -octopusApiKey $runbookApiKey -endpoint "tenants" -defaultUrl $runbookBaseUrl
if ($null -ne $tenantToUse)
{
$tenantIdToUse = $tenantToUse.Id
$runBookPreview = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -endPoint "runbooks/$($runbookToRun.Id)/runbookRuns/preview/$($environmentToUse.Id)/$($tenantIdToUse)" -method "GET" -item $null
}
else
{
try
{
Write-Host "Trying the new preview step"
$runBookPreview = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -endPoint "runbookSnapshots/$($runbookSnapShotIdToUse)/runbookRuns/preview/$($environmentToUse.Id)?includeDisabledSteps=true" -method "GET" -item $null
}
catch
{
Write-Host "The current version of Octopus Deploy doesn't support Runbook Snapshot Preview"
$runBookPreview = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -endPoint "runbooks/$($runbookToRun.Id)/runbookRuns/preview/$($environmentToUse.Id)" -method "GET" -item $null
}
}
$childRunbookRunSpecificMachines = Get-RunbookSpecificMachines -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -runbookPreview $runBookPreview -runbookMachines $runbookMachines -runbookRunName $runbookRunName
$runbookFormValues = Get-RunbookFormValues -runbookPreview $runBookPreview -runbookPromptedVariables $runbookPromptedVariables
$queueDate = Get-QueueDate -futureDeploymentDate $runbookFutureDeploymentDate
$queueExpiryDate = Get-QueueExpiryDate -queueDate $queueDate
$runbookBody = @{
RunbookId = $($runbookToRun.Id);
RunbookSnapShotId = $runbookSnapShotIdToUse;
FrozenRunbookProcessId = $null;
EnvironmentId = $($environmentToUse.Id);
TenantId = $tenantIdToUse;
SkipActions = @();
QueueTime = $queueDate;
QueueTimeExpiry = $queueExpiryDate;
FormValues = $runbookFormValues;
ForcePackageDownload = $false;
ForcePackageRedeployment = $true;
UseGuidedFailure = $runbookUseGuidedFailure;
SpecificMachineIds = @($childRunbookRunSpecificMachines);
ExcludedMachineIds = @()
}
$approvalTaskId = Get-ApprovalTaskId -autoApproveRunbookRunManualInterventions $autoApproveRunbookRunManualInterventions -parentTaskId $parentTaskId -parentReleaseId $parentReleaseId -parentRunbookId $parentRunbookId -parentEnvironmentName $parentEnvironmentName -approvalEnvironmentName $approvalEnvironmentName -parentChannelId $parentChannelId -parentEnvironmentId $parentEnvironmentId -defaultUrl $runbookBaseUrl -spaceId $runbookSpaceId -octopusApiKey $runbookApiKey
$parentTaskApprovers = Get-ParentTaskApprovers -parentTaskId $approvalTaskId -spaceId $runbookSpaceId -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey
Invoke-OctopusDeployRunbook -runbookBody $runbookBody -runbookWaitForFinish $runbookWaitForFinish -runbookCancelInSeconds $runbookCancelInSeconds -projectNameForUrl $projectNameForUrl -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -spaceId $runbookSpaceId -parentTaskApprovers $parentTaskApprovers -autoApproveRunbookRunManualInterventions $autoApproveRunbookRunManualInterventions -parentProjectName $projectNameForUrl -parentReleaseNumber $parentReleaseNumber -approvalEnvironmentName $approvalEnvironmentName -parentRunbookId $parentRunbookId -parentTaskId $approvalTaskId
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "0444b0b3-088e-4689-b755-112d1360ffe3",
"Name": "Run Octopus Deploy Runbook",
"Description": "This step will kick off a runbook. The runbook can exist in the same space and project, or it can exist on a different instance altogether. \n\n**Please Note**: Prompted variable values have to be text or sensitive variables. Variable types such as AWS or Azure accounts will not work.\n\nThis step should be called from a worker machine. If it is called from a target and the runbook runs on the same target you run the risk of a deadlock.\n\n",
"Version": 16,
"ExportedAt": "2024-07-23T14:07:03.309Z",
"ActionType": "Octopus.Script",
"Author": "millerjn21",
"Packages": [],
"Parameters": [
{
"Id": "e9e93cff-973a-4107-afa2-8efa30947979",
"Name": "Run.Runbook.Name",
"Label": "Runbook Name",
"HelpText": "**Required**\n\nThe name of the runbook to run.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "d998db57-3574-4598-81f9-7dd145cab81a",
"Name": "Run.Runbook.Base.Url",
"Label": "Base Url",
"HelpText": "**Required**\n\nThe base URL of your instance, IE https://samples.octopus.app. Defaults to the system variable [Octopus.Web.ServerUri](https://octopus.com/docs/projects/variables/system-variables#Systemvariables-Server).",
"DefaultValue": "#{Octopus.Web.ServerUri}",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "24884bf3-ca1d-4c17-8ee0-017339d6d87e",
"Name": "Run.Runbook.Api.Key",
"Label": "Api Key",
"HelpText": "**Required**\n\nThe API key of a user who has permissions to run the runbook specified",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Id": "bc2d33fe-d05b-49bd-b02b-eb6de4737eff",
"Name": "Run.Runbook.Space.Name",
"Label": "Runbook Space",
"HelpText": "**Required**\n\nThe name of the space the child project is located in. Defaults to the current space name using the variable `#{Octopus.Space.Name}`.",
"DefaultValue": "#{Octopus.Space.Name}",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "a1f44858-809a-48ce-9127-e59f02be40a1",
"Name": "Run.Runbook.Project.Name",
"Label": "Project name",
"HelpText": "**Optional**\n\nThe name of the project containing the runbook. If the project name is not specified then it will use the first runbook with the matching runbook name it can find.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "07bd5b03-4151-4f32-8893-417bf22c4df2",
"Name": "Run.Runbook.Environment.Name",
"Label": "Environment Name",
"HelpText": "**Required**\n\nName of the environment to run the runbook on. The default is the current environment name using the system variable `#{Octopus.Environment.Name}`.",
"DefaultValue": "#{Octopus.Environment.Name}",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "bf4ae98a-4901-474a-8984-08b0258304ca",
"Name": "Run.Runbook.Tenant.Name",
"Label": "Tenant Name",
"HelpText": "**Optional**\n\nName of Tenant to run the runbook for. If you want to run a runbook using the same tenant as the deployment or runbook run then use the system variable `#{Octopus.Deployment.Tenant.Name}`.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "9c49ba5c-337b-454a-8837-282353276aea",
"Name": "Run.Runbook.UsePublishedSnapShot",
"Label": "Use Published Snapshot",
"HelpText": "Indicates if the run should use the most recent published snapshot. When not set it will use the most recent snapshot, regardless if it was published or not.\n\nDefault is to use only published snapshots.\n\nIf you select to use unpublished snapshots then the runbook will:\n- If no published snapshots exist, then it will create a new unpublished snapshot on each run.\n- Create a new snapshot if anything has changed since the last published snapshot.\n- Use the existing published snapshot if nothing has changed since it was last published.\n\n**Please note:** A runbook is considered \"changed\" when any of the following are true:\n\n- Runbook process has changed.\n- Project variables have changed.\n- Library Variable Sets referenced by the project have changed.\n- A newer version of any referenced packages is found.",
"DefaultValue": "True",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "1a3e3ff6-456a-49e0-a0ce-83bfb30bfcaa",
"Name": "Run.Runbook.Waitforfinish",
"Label": "Wait for finish",
"HelpText": "Indicates if the process should be paused and wait for the runbook to finish",
"DefaultValue": "True",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "c36715c5-b583-43c4-b3e3-a74f44f2b2c4",
"Name": "Run.Runbook.UseGuidedFailure",
"Label": "Use Guided Failure",
"HelpText": "Should the runbook run use guided failure (not a good idea if you are waiting for this to finish)",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "6b951d28-b027-4f16-aaa6-39e91bd906d4",
"Name": "Run.Runbook.CancelInSeconds",
"Label": "Cancel Seconds",
"HelpText": "**Optional**\n\nThe number of seconds to wait before canceling the runbook run. Default is 1800 seconds (30 minutes).",
"DefaultValue": "1800",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "c847668d-b4fa-4405-a15b-f03691147597",
"Name": "Run.Runbook.PromptedVariables",
"Label": "Prompted Variable Values",
"HelpText": "**Optional**\n\nValues for any prompted variables for the runbook. Each new line represents a new variable. This will only work with string variable types, text and sensitive values. \n\nUse the format **Name::Value** IE:\n\n\nPromptedVariableName::My Super Awesome Value\n\nOtherPromptedVariable::Other Super Awesome Value",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "MultiLineText"
}
},
{
"Id": "dd65f24d-7546-4271-b78b-28101170410c",
"Name": "Run.Runbook.DateTime",
"Label": "Scheduling",
"HelpText": "**Optional**\n\nSchedule the runbook to run in the future. Please note, if this is set, the `Wait for Finish` option is ignored.\n\nUses `DateTime.TryParse` and specific keywords to determine the value sent in. Supported formats:\n\n- `7:00 PM` will deploy at 7:00 PM today\n- `21:00` will deploy at 21:00 hours or 9 PM today\n- `YYYY-MM-DD HH:mm:ss` or `2021-01-14 21:00:00` will deploy at 9 PM on the 14th of January, 2021\n- `YYYY/MM/DD HH:mm:ss` or `2021/03/20 22:00:00` will deploy at 10 PM on the 20th of March, 2021\n- `MM/DD/YYYY HH:mm:ss` or `06/25/2021 19:00:00` will deploy at 7 PM on the 25th of June, 2021\n- `DD MMM YYYY HH:mm:ss` or `01 Jan 2021 18:00:00` will deploy at 6 PM on the 1st of January, 2021\n- `Tomorrow HH:mm:ss` or `Tomorrow 18:00:00` will deploy at 6 PM tomorrow\n\nUses the Octopus Server's Timezone. The queue expiry time will be set to 1 hour from the supplied date.\n\nDefault is `N/A` or not applicable. ",
"DefaultValue": "N/A",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "22158e31-8061-4ada-b61b-a7bdacb5dd37",
"Name": "Run.Runbook.Machines",
"Label": "Machine List",
"HelpText": "**Optional**\n\nA comma-separated list of Machine Ids or Machine Names to target with this runbook. \n\n**Please Note:** The step template will remove any machines that cannot be found or are not applicable to the runbook.\n\nThe default is `N/A`. Set to `#{Octopus.Deployment.Machines}` if you want to target the same machines as the current runbook run or deployment.",
"DefaultValue": "N/A",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "95788fe2-f770-460c-b012-e6a586fe04d7",
"Name": "Run.Runbook.AutoApproveManualInterventions",
"Label": "Auto approve runbook manual interventions",
"HelpText": "**Optional**\n\nIf the child project has manual interventions the step will look for manual interventions in the parent project or parent runbook.\n\nWhen a manual intervention in the parent project or parent runbook is found it will check that user's assigned teams. If that user's assigned teams can approve the child project it will do so.\n\nPlease note, the user associated with the API key must be able to approve the child project manual interventions as well. \n\nThe default is `No`, allow this to happen. Set it to `Yes` to enable this functionality.",
"DefaultValue": "No",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "No|No\nYes|Yes"
}
},
{
"Id": "ea7c213e-380b-46ba-85b7-5c2c0f7b01d7",
"Name": "Run.Runbook.CustomNotes.Toggle",
"Label": "Custom Manual Intervention Notes Toggle",
"HelpText": "Check this box if you would like custom notes to be submitted with the automatic manual intervention approval.",
"DefaultValue": "False",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
}
},
{
"Id": "70549ad5-b451-4587-b8ed-b4afad1752f9",
"Name": "Run.Runbook.CustomNotes",
"Label": "Custom Manual Intervention Approval Notes",
"HelpText": "Use this field to supply custom manual intervention notes if the above toggle is checked.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Id": "b1cd0181-c5a8-4d4d-9746-f7cfe41f6794",
"Name": "Run.Runbook.ManualIntervention.EnvironmentToUse",
"Label": "Environment name to pull approvals from",
"HelpText": "**Optional**\n\nThe name of the environment you wish to pull the approvals from for the parent project or parent runbook. It will look at all the deployments for the current release of the parent project and select the latest deployment to the specified environment. If this step is being called from a runbook, it will look at the latest runbook run for the environment specified\n\nUsed when you are deploying to `Production` but want to pull the approvals from `Staging` or a `Prod Approval` environment.\n\nDefaults to the current environment name.",
"DefaultValue": "#{Octopus.Environment.Name}",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
}
],
"Properties": {
"Octopus.Action.Script.ScriptSource": "Inline",
"Octopus.Action.Script.ScriptBody": "[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12\n\n# Octopus Variables\n$octopusSpaceId = $OctopusParameters[\"Octopus.Space.Id\"]\n$parentTaskId = $OctopusParameters[\"Octopus.Task.Id\"]\n$parentReleaseId = $OctopusParameters[\"Octopus.Release.Id\"]\n$parentChannelId = $OctopusParameters[\"Octopus.Release.Channel.Id\"]\n$parentEnvironmentId = $OctopusParameters[\"Octopus.Environment.Id\"]\n$parentRunbookId = $OctopusParameters[\"Octopus.Runbook.Id\"]\n$parentEnvironmentName = $OctopusParameters[\"Octopus.Environment.Name\"]\n$parentReleaseNumber = $OctopusParameters[\"Octopus.Release.Number\"]\n\n# Step Template Parameters\n$runbookRunName = $OctopusParameters[\"Run.Runbook.Name\"]\n$runbookBaseUrl = $OctopusParameters[\"Run.Runbook.Base.Url\"]\n$runbookApiKey = $OctopusParameters[\"Run.Runbook.Api.Key\"]\n$runbookEnvironmentName = $OctopusParameters[\"Run.Runbook.Environment.Name\"]\n$runbookTenantName = $OctopusParameters[\"Run.Runbook.Tenant.Name\"]\n$runbookWaitForFinish = $OctopusParameters[\"Run.Runbook.Waitforfinish\"]\n$runbookUseGuidedFailure = $OctopusParameters[\"Run.Runbook.UseGuidedFailure\"]\n$runbookUsePublishedSnapshot = $OctopusParameters[\"Run.Runbook.UsePublishedSnapShot\"]\n$runbookPromptedVariables = $OctopusParameters[\"Run.Runbook.PromptedVariables\"]\n$runbookCancelInSeconds = $OctopusParameters[\"Run.Runbook.CancelInSeconds\"]\n$runbookProjectName = $OctopusParameters[\"Run.Runbook.Project.Name\"]\n$runbookCustomNotesToggle = $OctopusParameters[\"Run.Runbook.CustomNotes.Toggle\"]\n$runbookCustomNotes = $OctopusParameters[\"Run.Runbook.CustomNotes\"]\n\n$runbookSpaceName = $OctopusParameters[\"Run.Runbook.Space.Name\"]\n$runbookFutureDeploymentDate = $OctopusParameters[\"Run.Runbook.DateTime\"]\n$runbookMachines = $OctopusParameters[\"Run.Runbook.Machines\"]\n$autoApproveRunbookRunManualInterventions = $OctopusParameters[\"Run.Runbook.AutoApproveManualInterventions\"]\n$approvalEnvironmentName = $OctopusParameters[\"Run.Runbook.ManualIntervention.EnvironmentToUse\"]\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 )\n\n if ([string]::IsNullOrWhiteSpace($SpaceId))\n {\n $url = \"$OctopusUrl/api/$EndPoint\"\n }\n else\n {\n $url = \"$OctopusUrl/api/$spaceId/$EndPoint\" \n } \n\n try\n {\n if ($null -eq $item)\n {\n Write-Verbose \"No data to post or put, calling bog standard invoke-restmethod for $url\"\n return Invoke-RestMethod -Method $method -Uri $url -Headers @{\"X-Octopus-ApiKey\" = \"$ApiKey\" } -ContentType 'application/json; charset=utf-8'\n }\n\n $body = $item | ConvertTo-Json -Depth 10\n Write-Verbose $body\n\n Write-Host \"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 catch\n {\n if ($null -ne $_.Exception.Response)\n {\n if ($_.Exception.Response.StatusCode -eq 401)\n {\n Write-Error \"Unauthorized error returned from $url, please verify API key and try again\"\n }\n elseif ($_.Exception.Response.statusCode -eq 403)\n {\n Write-Error \"Forbidden error returned from $url, please verify API key and try again\"\n }\n else\n { \n Write-Error -Message \"Error calling $url $($_.Exception.Message) StatusCode: $($_.Exception.Response.StatusCode )\"\n } \n }\n else\n {\n Write-Verbose $_.Exception\n }\n }\n\n Throw \"There was an error calling the Octopus API please check the log for more details\"\n}\n\nfunction Test-RequiredValues\n{\n\tparam (\n \t$variableToCheck,\n $variableName\n )\n \n if ([string]::IsNullOrWhiteSpace($variableToCheck) -eq $true)\n {\n \tWrite-OctopusCritical \"$variableName is required.\"\n return $false\n }\n \n return $true\n}\n\nfunction GetCheckBoxBoolean\n{\n\tparam (\n \t[string]$Value\n )\n \n if ([string]::IsNullOrWhiteSpace($value) -eq $true)\n {\n \treturn $false\n }\n \n return $value -eq \"True\"\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 Exit 1\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 exit 1\n }\n \n if ($item -is [array])\n {\n \tWrite-OctopusCritical \"More than one item exists with the name $itemName. Exiting with an exit code of 1.\"\n exit 1\n }\n\n return $item\n}\n\nfunction Get-OctopusItemFromListEndpoint\n{\n param(\n $endpoint,\n $itemNameToFind,\n $itemType,\n $defaultUrl,\n $octopusApiKey,\n $spaceId,\n $defaultValue\n )\n \n if ([string]::IsNullOrWhiteSpace($itemNameToFind))\n {\n \treturn $defaultValue\n }\n \n Write-OctopusInformation \"Attempting to find $itemType with the name of $itemNameToFind\"\n \n $itemList = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"$($endpoint)?partialName=$([uri]::EscapeDataString($itemNameToFind))&skip=0&take=100\" -spaceId $spaceId -apiKey $octopusApiKey -method \"GET\" \n $item = Get-FilteredOctopusItem -itemList $itemList -itemName $itemNameToFind\n\n Write-OctopusInformation \"Successfully found $itemNameToFind with id of $($item.Id)\"\n\n return $item\n}\n\nfunction Get-MachineIdsFromMachineNames\n{\n param (\n $targetMachines,\n $defaultUrl,\n $spaceId,\n $octopusApiKey\n )\n\n $targetMachineList = $targetMachines -split \",\"\n $translatedList = @()\n\n foreach ($machineName in $targetMachineList)\n {\n Write-OctopusVerbose \"Translating $machineName to an Id. First checking to see if it is already an Id.\"\n \tif ($machineName.Trim() -like \"Machines*\")\n {\n Write-OctopusVerbose \"$machineName is already an Id, no need to look that up.\"\n \t$translatedList += $machineName\n continue\n }\n \n $machineObject = Get-OctopusItemFromListEndpoint -itemNameToFind $machineName.Trim() -itemType \"Deployment Target\" -endpoint \"machines\" -defaultValue $null -spaceId $spaceId -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey\n\n $translatedList += $machineObject.Id\n }\n\n return $translatedList\n}\n\nfunction Get-RunbookSnapshotIdToRun\n{\n param (\n $runbookToRun,\n $runbookUsePublishedSnapshot,\n $defaultUrl,\n $octopusApiKey,\n $spaceId\n )\n\n $runbookSnapShotIdToUse = $runbookToRun.PublishedRunbookSnapshotId\n Write-OctopusInformation \"The last published snapshot for $runbookRunName is $runbookSnapShotIdToUse\"\n\n if ($null -eq $runbookSnapShotIdToUse -and $runbookUsePublishedSnapshot -eq $true)\n {\n Write-OctopusCritical \"Use Published Snapshot was set; yet the runbook doesn't have a published snapshot. Exiting.\"\n Exit 1\n }\n\n if ($runbookUsePublishedSnapshot -eq $true)\n {\n Write-OctopusInformation \"Use published snapshot set to true, using the published runbook snapshot.\"\n return $runbookSnapShotIdToUse\n }\n\n if ($null -eq $runbookToRun.PublishedRunbookSnapshotId)\n {\n Write-OctopusInformation \"There have been no published runbook snapshots, going to create a new snapshot.\"\n return New-RunbookUnpublishedSnapshot -runbookToRun $runbookToRun -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId\n }\n\n $runbookSnapShotTemplate = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"runbookSnapshots/$($runbookToRun.PublishedRunbookSnapshotId)/runbookRuns/template\" -method \"Get\" -item $null\n\n if ($runbookSnapShotTemplate.IsRunbookProcessModified -eq $false -and $runbookSnapShotTemplate.IsVariableSetModified -eq $false -and $runbookSnapShotTemplate.IsLibraryVariableSetModified -eq $false)\n { \n Write-OctopusInformation \"The runbook has not been modified since the published snapshot was created. Checking to see if any of the packages have a new version.\" \n $runbookSnapShot = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"runbookSnapshots/$($runbookToRun.PublishedRunbookSnapshotId)\" -method \"Get\" -item $null\n $snapshotTemplate = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"runbooks/$($runbookToRun.Id)/runbookSnapShotTemplate\" -method \"Get\" -item $null\n\n foreach ($package in $runbookSnapShot.SelectedPackages)\n {\n foreach ($templatePackage in $snapshotTemplate.Packages)\n {\n if ($package.StepName -eq $templatePackage.StepName -and $package.ActionName -eq $templatePackage.ActionName -and $package.PackageReferenceName -eq $templatePackage.PackageReferenceName)\n {\n $packageVersion = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"feeds/$($templatePackage.FeedId)/packages/versions?packageId=$($templatePackage.PackageId)&take=1\" -method \"Get\" -item $null\n\n if ($packageVersion -ne $package.Version)\n {\n Write-OctopusInformation \"A newer version of a package was found, going to use that and create a new snapshot.\"\n return New-RunbookUnpublishedSnapshot -runbookToRun $runbookToRun -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId \n }\n }\n }\n }\n\n Write-OctopusInformation \"No new package versions have been found, using the published snapshot.\"\n return $runbookToRun.PublishedRunbookSnapshotId\n }\n \n Write-OctopusInformation \"The runbook has been modified since the snapshot was created, creating a new one.\"\n return New-RunbookUnpublishedSnapshot -runbookToRun $runbookToRun -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId\n}\n\nfunction New-RunbookUnpublishedSnapshot\n{\n param (\n $runbookToRun,\n $defaultUrl,\n $octopusApiKey,\n $spaceId\n )\n\n $octopusProject = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"projects/$($runbookToRun.ProjectId)\" -method \"Get\" -item $null\n $snapshotTemplate = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"runbooks/$($runbookToRun.Id)/runbookSnapShotTemplate\" -method \"Get\" -item $null\n\n $runbookPackages = @()\n foreach ($package in $snapshotTemplate.Packages)\n {\n $packageVersion = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"feeds/$($package.FeedId)/packages/versions?packageId=$($package.PackageId)&take=1\" -method \"Get\" -item $null\n\n if ($packageVersion.TotalResults -le 0)\n {\n Write-Error \"Unable to find a package version for $($package.PackageId). This is required to create a new unpublished snapshot. Exiting.\"\n exit 1\n }\n\n $runbookPackages += @{\n StepName = $package.StepName\n ActionName = $package.ActionName\n Version = $packageVersion.Items[0].Version\n PackageReferenceName = $package.PackageReferenceName\n }\n }\n\n $runbookSnapShotRequest = @{\n FrozenProjectVariableSetId = \"variableset-$($runbookToRun.ProjectId)\"\n FrozenRunbookProcessId = $($runbookToRun.RunbookProcessId)\n LibraryVariableSetSnapshotIds = @($octopusProject.IncludedLibraryVariableSetIds)\n Name = $($snapshotTemplate.NextNameIncrement)\n ProjectId = $($runbookToRun.ProjectId)\n ProjectVariableSetSnapshotId = \"variableset-$($runbookToRun.ProjectId)\"\n RunbookId = $($runbookToRun.Id)\n SelectedPackages = $runbookPackages\n }\n\n $newSnapShot = Invoke-OctopusApi -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -endPoint \"runbookSnapshots\" -method \"POST\" -item $runbookSnapShotRequest\n\n return $($newSnapShot.Id)\n}\n\nfunction Get-ProjectSlug\n{\n param\n (\n $runbookToRun,\n $projectToUse,\n $defaultUrl,\n $spaceId,\n $octopusApiKey\n )\n\n if ($null -ne $projectToUse)\n {\n return $projectToUse.Slug\n }\n\n $project = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -endPoint \"projects/$($runbookToRun.ProjectId)\" -method \"GET\" -item $null\n\n return $project.Slug\n}\n\nfunction Get-RunbookFormValues\n{\n param (\n $runbookPreview,\n $runbookPromptedVariables \n )\n\n $runbookFormValues = @{}\n\n if ([string]::IsNullOrWhiteSpace($runbookPromptedVariables) -eq $true)\n {\n return $runbookFormValues\n } \n \n $promptedValueList = @(($runbookPromptedVariables -Split \"`n\").Trim())\n Write-OctopusInformation $promptedValueList.Length\n \n foreach($element in $runbookPreview.Form.Elements)\n {\n \t$nameToSearchFor = $element.Control.Name\n $uniqueName = $element.Name\n $isRequired = $element.Control.Required\n \n $promptedVariablefound = $false\n \n Write-OctopusInformation \"Looking for the prompted variable value for $nameToSearchFor\"\n \tforeach ($promptedValue in $promptedValueList)\n {\n \t$splitValue = $promptedValue -Split \"::\"\n Write-OctopusInformation \"Comparing $nameToSearchFor with provided prompted variable $($promptedValue[0])\"\n if ($splitValue.Length -gt 1)\n {\n \tif ($nameToSearchFor -eq $splitValue[0])\n {\n \tWrite-OctopusInformation \"Found the prompted variable value $nameToSearchFor\"\n \t$runbookFormValues[$uniqueName] = $splitValue[1]\n $promptedVariableFound = $true\n break\n }\n }\n }\n \n if ($promptedVariableFound -eq $false -and $isRequired -eq $true)\n {\n \tWrite-OctopusCritical \"Unable to find a value for the required prompted variable $nameToSearchFor, exiting\"\n Exit 1\n }\n }\n\n return $runbookFormValues\n}\n\nfunction Invoke-OctopusDeployRunbook\n{\n param (\n $runbookBody,\n $runbookWaitForFinish,\n $runbookCancelInSeconds,\n $projectNameForUrl, \n $defaultUrl,\n $octopusApiKey,\n $spaceId,\n $parentTaskApprovers,\n $autoApproveRunbookRunManualInterventions,\n $parentProjectName,\n $parentReleaseNumber,\n $approvalEnvironmentName,\n $parentRunbookId,\n $parentTaskId\n )\n\n $runbookResponse = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -item $runbookBody -method \"POST\" -endPoint \"runbookRuns\"\n\n $runbookServerTaskId = $runBookResponse.TaskId\n Write-OctopusInformation \"The task id of the new task is $runbookServerTaskId\"\n\n $runbookRunId = $runbookResponse.Id\n Write-OctopusInformation \"The runbook run id is $runbookRunId\"\n\n Write-OctopusSuccess \"Runbook was successfully invoked, you can access the launched runbook [here]($defaultUrl/app#/$spaceId/projects/$projectNameForUrl/operations/runbooks/$($runbookBody.RunbookId)/snapshots/$($runbookBody.RunbookSnapShotId)/runs/$runbookRunId)\"\n\n if ($runbookWaitForFinish -eq $false)\n {\n Write-OctopusInformation \"The wait for finish setting is set to no, exiting step\"\n return\n }\n \n if ($null -ne $runbookBody.QueueTime)\n {\n \tWrite-OctopusInformation \"The runbook queue time is set. Exiting step\"\n return\n }\n\n Write-OctopusSuccess \"The setting to wait for completion was set, waiting until task has finished\"\n $startTime = Get-Date\n $currentTime = Get-Date\n $dateDifference = $currentTime - $startTime\n\t\n $taskStatusUrl = \"tasks/$runbookServerTaskId\"\n $numberOfWaits = 0 \n \n While ($dateDifference.TotalSeconds -lt $runbookCancelInSeconds)\n {\n Write-OctopusInformation \"Waiting 5 seconds to check status\"\n Start-Sleep -Seconds 5\n $taskStatusResponse = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -endPoint $taskStatusUrl -method \"GET\" -item $null\n $taskStatusResponseState = $taskStatusResponse.State\n\n if ($taskStatusResponseState -eq \"Success\")\n {\n Write-OctopusSuccess \"The task has finished with a status of Success\"\n exit 0 \n }\n elseif($taskStatusResponseState -eq \"Failed\" -or $taskStatusResponseState -eq \"Canceled\")\n {\n Write-OctopusSuccess \"The task has finished with a status of $taskStatusResponseState status, stopping the run/deployment\"\n exit 1 \n }\n elseif($taskStatusResponse.HasPendingInterruptions -eq $true)\n {\n if ($autoApproveRunbookRunManualInterventions -eq \"Yes\")\n {\n Submit-RunbookRunForAutoApproval -createdRunbookRun $createdRunbookRun -parentTaskApprovers $parentTaskApprovers -defaultUrl $DefaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId -parentProjectName $parentProjectName -parentReleaseNumber $parentReleaseNumber -parentEnvironmentName $approvalEnvironmentName -parentRunbookId $parentRunbookId -parentTaskId $parentTaskId\n }\n else\n {\n if ($numberOfWaits -ge 10)\n {\n Write-OctopusSuccess \"The child project has pending manual intervention(s). Unless you approve it, this task will time out.\"\n }\n else\n {\n Write-OctopusInformation \"The child project has pending manual intervention(s). Unless you approve it, this task will time out.\" \n }\n }\n }\n \n $numberOfWaits += 1\n if ($numberOfWaits -ge 10)\n {\n \tWrite-OctopusSuccess \"The task state is currently $taskStatusResponseState\"\n \t$numberOfWaits = 0\n }\n else\n {\n \tWrite-OctopusInformation \"The task state is currently $taskStatusResponseState\"\n } \n \n $startTime = $taskStatusResponse.StartTime\n if ($startTime -eq $null -or [string]::IsNullOrWhiteSpace($startTime) -eq $true)\n { \n \tWrite-OctopusInformation \"The task is still queued, let's wait a bit longer\"\n \t$startTime = Get-Date\n }\n $startTime = [DateTime]$startTime\n \n $currentTime = Get-Date\n $dateDifference = $currentTime - $startTime \n }\n \n Write-OctopusSuccess \"The cancel timeout has been reached, cancelling the runbook run\"\n $cancelResponse = Invoke-RestMethod \"$runbookBaseUrl/api/tasks/$runbookServerTaskId/cancel\" -Headers $header -Method Post\n Write-OctopusSuccess \"Exiting with an error code of 1 because we reached the timeout\"\n exit 1\n}\n\nfunction Get-QueueDate\n{\n\tparam ( \n \t$futureDeploymentDate\n )\n \n if ([string]::IsNullOrWhiteSpace($futureDeploymentDate) -or $futureDeploymentDate -eq \"N/A\")\n {\n \treturn $null\n }\n \n $addOneDay = $false\n $textToParse = $futureDeploymentDate.ToLower()\n if ($textToParse -like \"tomorrow*\")\n {\n \tWrite-Host \"The future date $futureDeploymentDate supplied contains tomorrow, will add one day to whatever the parsed result is.\"\n \t$addOneDay = $true\n $textToParse = $textToParse -replace \"tomorrow\", \"\"\n }\n \n [datetime]$outputDate = New-Object DateTime\n $currentDate = Get-Date\n $currentDate = $currentDate.AddMinutes(2)\n\n if ([datetime]::TryParse($textToParse, [ref]$outputDate) -eq $false)\n {\n Write-OctopusCritical \"The suppplied date $textToParse cannot be parsed by DateTime.TryParse. Please verify format and try again. Please [refer to Microsoft's Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.datetime.tryparse) on supported formats.\"\n exit 1\n }\n \n Write-Host \"The proposed date is $outputDate. Checking to see if this will occur in the past.\"\n \n if ($addOneDay -eq $true)\n {\n \t$outputDate = $outputDate.AddDays(1)\n \tWrite-host \"The text supplied included tomorrow, adding one day. The new proposed date is $outputDate.\"\n }\n \n if ($currentDate -gt $outputDate)\n {\n \tWrite-OctopusCritical \"The supplied date $futureDeploymentDate is set for the past. All queued deployments must be in the future.\"\n exit 1\n }\n \n return $outputDate\n}\n\nfunction Get-QueueExpiryDate\n{\n\tparam (\n \t$queueDate\n )\n \n if ($null -eq $queueDate)\n {\n \treturn $null\n }\n \n return $queueDate.AddHours(1)\n}\n\nfunction Get-RunbookSpecificMachines\n{\n param (\n $defaultUrl,\n $octopusApiKey, \n $runbookPreview,\n $runbookMachines, \n $runbookRunName \n )\n\n if ($runbookMachines -eq \"N/A\")\n {\n return @()\n }\n\n if ([string]::IsNullOrWhiteSpace($runbookMachines) -eq $true)\n {\n return @()\n }\n\n $translatedList = Get-MachineIdsFromMachineNames -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId -targetMachines $runbookMachines\n\n $filteredList = @() \n foreach ($runbookMachine in $translatedList)\n { \t\n \t$runbookMachineId = $runbookMachine.Trim().ToLower()\n \tWrite-OctopusVerbose \"Checking if $runbookMachineId is set to run on any of the runbook steps\"\n \n foreach ($step in $runbookPreview.StepsToExecute)\n {\n foreach ($machine in $step.Machines)\n {\n \tWrite-OctopusVerbose \"Checking if $runbookMachineId matches $($machine.Id) and it isn't already in the $($filteredList -join \",\")\"\n if ($runbookMachineId -eq $machine.Id.Trim().ToLower() -and $filteredList -notcontains $machine.Id)\n {\n \tWrite-OctopusInformation \"Adding $($machine.Id) to the list\"\n $filteredList += $machine.Id\n }\n }\n }\n }\n\n if ($filteredList.Length -le 0)\n {\n Write-OctopusSuccess \"The current task is targeting specific machines, but the runbook $runBookRunName does not run against any of these machines $runbookMachines. Skipping this run.\"\n exit 0\n }\n\n return $filteredList\n}\n\nfunction Get-ParentTaskApprovers\n{\n param (\n $parentTaskId,\n $spaceId,\n $defaultUrl,\n $octopusApiKey\n )\n \n $approverList = @()\n if ($null -eq $parentTaskId)\n {\n \tWrite-OctopusInformation \"The deployment task id to pull the approvers from is null, return an empty approver list\"\n \treturn $approverList\n }\n\n Write-OctopusInformation \"Getting all the events from the parent project\"\n $parentEvents = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"events?regardingAny=$parentTaskId&spaces=$spaceId&includeSystem=true\" -apiKey $octopusApiKey -method \"GET\"\n \n foreach ($parentEvent in $parentEvents.Items)\n {\n Write-OctopusVerbose \"Checking $($parentEvent.Message) for manual intervention\"\n if ($parentEvent.Message -like \"Submitted interruption*\")\n {\n Write-OctopusVerbose \"The event $($parentEvent.Id) is a manual intervention approval event which was approved by $($parentEvent.Username).\"\n\n $approverExists = $approverList | Where-Object {$_.Id -eq $parentEvent.UserId} \n\n if ($null -eq $approverExists)\n {\n $approverInformation = @{\n Id = $parentEvent.UserId;\n Username = $parentEvent.Username;\n Teams = @()\n }\n\n $approverInformation.Teams = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"teammembership?userId=$($approverInformation.Id)&spaces=$spaceId&includeSystem=true\" -apiKey $octopusApiKey -method \"GET\" \n\n Write-OctopusVerbose \"Adding $($approverInformation.Id) to the approval list\"\n $approverList += $approverInformation\n } \n }\n }\n\n return $approverList\n}\n\nfunction Get-ApprovalTaskIdFromDeployment\n{\n param (\n $parentReleaseId,\n $approvalEnvironment,\n $parentChannelId, \n $parentEnvironmentId,\n $defaultUrl,\n $spaceId,\n $octopusApiKey \n )\n\n $releaseDeploymentList = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"releases/$parentReleaseId/deployments\" -method \"GET\" -apiKey $octopusApiKey -spaceId $spaceId\n \n $lastDeploymentTime = $(Get-Date).AddYears(-50)\n $approvalTaskId = $null\n foreach ($deployment in $releaseDeploymentList.Items)\n {\n if ($deployment.EnvironmentId -ne $approvalEnvironment.Id)\n {\n Write-OctopusInformation \"The deployment $($deployment.Id) deployed to $($deployment.EnvironmentId) which doesn't match $($approvalEnvironment.Id).\"\n continue\n }\n \n Write-OctopusInformation \"The deployment $($deployment.Id) was deployed to the approval environment $($approvalEnvironment.Id).\"\n\n $deploymentTask = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $null -endPoint \"tasks/$($deployment.TaskId)\" -apiKey $octopusApiKey -Method \"Get\"\n if ($deploymentTask.IsCompleted -eq $true -and $deploymentTask.FinishedSuccessfully -eq $false)\n {\n Write-Information \"The deployment $($deployment.Id) was deployed to the approval environment, but it encountered a failure, moving onto the next deployment.\"\n continue\n }\n\n if ($deploymentTask.StartTime -gt $lastDeploymentTime)\n {\n $approvalTaskId = $deploymentTask.Id\n $lastDeploymentTime = $deploymentTask.StartTime\n }\n } \n\n if ($null -eq $approvalTaskId)\n {\n \tWrite-OctopusVerbose \"Unable to find a deployment to the environment, determining if it should've happened already.\"\n $channelInformation = Invoke-OctopusApi -octopusUrl $defaultUrl -endPoint \"channels/$parentChannelId\" -method \"GET\" -apiKey $octopusApiKey -spaceId $spaceId\n $lifecycle = Get-OctopusLifeCycle -channel $channelInformation -defaultUrl $defaultUrl -spaceId $spaceId -OctopusApiKey $octopusApiKey\n $lifecyclePhases = Get-LifecyclePhases -lifecycle $lifecycle -defaultUrl $defaultUrl -spaceId $spaceid -OctopusApiKey $octopusApiKey\n \n $foundDestinationFirst = $false\n $foundApprovalFirst = $false\n \n foreach ($phase in $lifecyclePhases.Phases)\n {\n \tif ($phase.AutomaticDeploymentTargets -contains $parentEnvironmentId -or $phase.OptionalDeploymentTargets -contains $parentEnvironmentId)\n {\n \tif ($foundApprovalFirst -eq $false)\n {\n \t$foundDestinationFirst = $true\n }\n }\n \n if ($phase.AutomaticDeploymentTargets -contains $approvalEnvironment.Id -or $phase.OptionalDeploymentTargets -contains $approvalEnvironment.Id)\n {\n \tif ($foundDestinationFirst -eq $false)\n {\n \t$foundApprovalFirst = $true\n }\n }\n }\n \n $messageToLog = \"Unable to find a deployment for the environment $approvalEnvironmentName. Auto approvals are disabled.\"\n if ($foundApprovalFirst -eq $true)\n {\n \tWrite-OctopusWarning $messageToLog\n }\n else\n {\n \tWrite-OctopusInformation $messageToLog\n }\n \n return $null\n }\n\n return $approvalTaskId\n}\n\nfunction Get-ApprovalTaskIdFromRunbook\n{\n param (\n $parentRunbookId,\n $approvalEnvironment,\n $defaultUrl,\n $spaceId,\n $octopusApiKey \n )\n}\n\nfunction Get-ApprovalTaskId\n{\n\tparam (\n \t$autoApproveRunbookRunManualInterventions,\n $parentTaskId,\n $parentReleaseId,\n $parentRunbookId,\n $parentEnvironmentName,\n $approvalEnvironmentName,\n $parentChannelId, \n $parentEnvironmentId,\n $defaultUrl,\n $spaceId,\n $octopusApiKey \n )\n \n if ($autoApproveRunbookRunManualInterventions -eq $false)\n {\n \tWrite-OctopusInformation \"Auto approvals are disabled, skipping pulling the approval deployment task id\"\n return $null\n }\n \n if ([string]::IsNullOrWhiteSpace($approvalEnvironmentName) -eq $true)\n {\n \tWrite-OctopusInformation \"Approval environment not supplied, using the current environment id for approvals.\"\n return $parentTaskId\n }\n \n if ($approvalEnvironmentName.ToLower().Trim() -eq $parentEnvironmentName.ToLower().Trim())\n {\n Write-OctopusInformation \"The approval environment is the same as the current environment, using the current task id $parentTaskId\"\n return $parentTaskId\n }\n \n $approvalEnvironment = Get-OctopusItemFromListEndpoint -itemNameToFind $approvalEnvironmentName -itemType \"Environment\" -defaultUrl $DefaultUrl -spaceId $spaceId -octopusApiKey $octopusApiKey -defaultValue $null -endpoint \"environments\"\n \n if ([string]::IsNullOrWhiteSpace($parentReleaseId) -eq $false)\n {\n return Get-ApprovalTaskIdFromDeployment -parentReleaseId $parentReleaseId -approvalEnvironment $approvalEnvironment -parentChannelId $parentChannelId -parentEnvironmentId $parentEnvironmentId -defaultUrl $defaultUrl -octopusApiKey $octopusApiKey -spaceId $spaceId\n }\n\n return Get-ApprovalTaskIdFromRunbook -parentRunbookId $parentRunbookId -approvalEnvironment $approvalEnvironment -defaultUrl $defaultUrl -spaceId $spaceId -octopusApiKey $octopusApiKey\n}\n\nfunction Get-OctopusLifecycle\n{\n param (\n $channel, \n $defaultUrl,\n $spaceId,\n $octopusApiKey\n )\n\n Write-OctopusInformation \"Attempting to find the lifecycle information $($channel.Name)\"\n if ($null -eq $channel.LifecycleId)\n {\n $lifecycleName = \"Default Lifecycle\"\n $lifecycleList = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"lifecycles?partialName=$([uri]::EscapeDataString($lifecycleName))&skip=0&take=1\" -spaceId $spaceId -apiKey $octopusApiKey -method \"GET\"\n $lifecycle = $lifecycleList.Items[0]\n }\n else\n {\n $lifecycle = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"lifecycles/$($channel.LifecycleId)\" -spaceId $spaceId -apiKey $octopusApiKey -method \"GET\"\n }\n\n Write-Host \"Successfully found the lifecycle $($lifecycle.Name) to use for this channel.\"\n\n return $lifecycle\n}\n\nfunction Get-LifecyclePhases\n{\n param (\n $lifecycle, \n $defaultUrl,\n $spaceId,\n $octopusApiKey\n )\n\n Write-OctopusInformation \"Attempting to find the phase in the lifecycle $($lifecycle.Name) with the environment $environmentName to find the previous phase.\"\n if ($lifecycle.Phases.Count -eq 0)\n {\n Write-OctopusInformation \"The lifecycle $($lifecycle.Name) has no set phases, calling the preview endpoint.\"\n $lifecyclePreview = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"lifecycles/$($lifecycle.Id)/preview\" -spaceId $spaceId -apiKey $octopusApiKey -method \"GET\"\n $phases = $lifecyclePreview.Phases\n }\n else\n {\n Write-OctopusInformation \"The lifecycle $($lifecycle.Name) has set phases, using those.\"\n $phases = $lifecycle.Phases \n }\n\n Write-OctopusInformation \"Found $($phases.Length) phases in this lifecycle.\"\n return $phases\n}\n\nfunction Submit-RunbookRunForAutoApproval\n{\n param (\n $createdRunbookRun,\n $parentTaskApprovers,\n $defaultUrl,\n $octopusApiKey,\n $spaceId,\n $parentProjectName,\n $parentReleaseNumber,\n $parentRunbookId,\n $parentEnvironmentName,\n $parentTaskId \n )\n\n Write-OctopusSuccess \"The task has a pending manual intervention. Checking parent approvals.\" \n $manualInterventionInformation = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"interruptions?regarding=$($createdRunbookRun.TaskId)\" -method \"GET\" -apiKey $octopusApiKey -spaceId $spaceId\n foreach ($manualIntervention in $manualInterventionInformation.Items)\n {\n if ($manualIntervention.IsPending -eq $false)\n {\n Write-OctopusInformation \"This manual intervention has already been approved. Proceeding onto the next one.\"\n continue\n }\n\n if ($manualIntervention.CanTakeResponsibility -eq $false)\n {\n Write-OctopusSuccess \"The user associated with the API key doesn't have permissions to take responsibility for the manual intervention.\"\n Write-OctopusSuccess \"If you wish to leverage the auto-approval functionality give the user permissions.\"\n continue\n } \n\n $automaticApprover = $null\n Write-OctopusVerbose \"Checking to see if one of the parent project approvers is assigned to one of the manual intervention teams $($manualIntervention.ResponsibleTeamIds)\"\n foreach ($approver in $parentTaskApprovers)\n {\n foreach ($approverTeam in $approver.Teams)\n {\n Write-OctopusVerbose \"Checking to see if $($manualIntervention.ResponsibleTeamIds) contains $($approverTeam.TeamId)\"\n if ($manualIntervention.ResponsibleTeamIds -contains $approverTeam.TeamId)\n {\n $automaticApprover = $approver\n break\n }\n }\n\n if ($null -ne $automaticApprover)\n {\n break\n }\n }\n\n if ($null -ne $automaticApprover)\n {\n \tWrite-OctopusSuccess \"Matching approver found auto-approving.\"\n if ($manualIntervention.HasResponsibility -eq $false)\n {\n Write-OctopusInformation \"Taking over responsibility for this manual intervention.\"\n $takeResponsiblilityResponse = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"interruptions/$($manualIntervention.Id)/responsible\" -method \"PUT\" -apiKey $octopusApiKey -spaceId $spaceId\n Write-OctopusVerbose \"Response from taking responsibility $($takeResponsiblilityResponse.Id)\"\n }\n \n if ([string]::IsNullOrWhiteSpace($parentReleaseNumber) -eq $false)\n {\n $notes = \"Auto-approving this runbook run. Parent project $parentProjectName release $parentReleaseNumber to $parentEnvironmentName with the task id $parentTaskId was approved by $($automaticApprover.UserName). That user is a member of one of the teams this manual intervention requires. You can view that deployment $defaultUrl/app#/$spaceId/tasks/$parentTaskId\"\n }\n else \n {\n $notes = \"Auto-approving this runbook run. Parent project $parentProjectName runbook run $parentRunbookId to $parentEnvironmentName with the task id $parentTaskId was approved by $($automaticApprover.UserName). That user is a member of one of the teams this manual intervention requires. You can view that runbook run $defaultUrl/app#/$spaceId/tasks/$parentTaskId\"\n }\n if ($runbookCustomNotesToggle -eq $true){\n $notes = $runbookCustomNotes\n }\n $submitApprovalBody = @{\n Instructions = $null;\n Notes = $notes\n Result = \"Proceed\"\n }\n $submitResult = Invoke-OctopusApi -octopusUrl $DefaultUrl -endPoint \"interruptions/$($manualIntervention.Id)/submit\" -method \"POST\" -apiKey $octopusApiKey -item $submitApprovalBody -spaceId $spaceId\n Write-OctopusSuccess \"Successfully auto approved the manual intervention $($submitResult.Id)\"\n }\n else\n {\n Write-OctopusSuccess \"Couldn't find an approver to auto-approve the child project. Waiting until timeout or child project is approved.\" \n }\n }\n}\n\n\n$runbookWaitForFinish = GetCheckboxBoolean -Value $runbookWaitForFinish\n$runbookUseGuidedFailure = GetCheckboxBoolean -Value $runbookUseGuidedFailure\n$runbookUsePublishedSnapshot = GetCheckboxBoolean -Value $runbookUsePublishedSnapshot\n$runbookCancelInSeconds = [int]$runbookCancelInSeconds\n\nWrite-OctopusInformation \"Wait for Finish Before Check: $runbookWaitForFinish\"\nWrite-OctopusInformation \"Use Guided Failure Before Check: $runbookUseGuidedFailure\"\nWrite-OctopusInformation \"Use Published Snapshot Before Check: $runbookUsePublishedSnapshot\"\nWrite-OctopusInformation \"Runbook Name $runbookRunName\"\nWrite-OctopusInformation \"Runbook Base Url: $runbookBaseUrl\"\nWrite-OctopusInformation \"Runbook Space Name: $runbookSpaceName\"\nWrite-OctopusInformation \"Runbook Environment Name: $runbookEnvironmentName\"\nWrite-OctopusInformation \"Runbook Tenant Name: $runbookTenantName\"\nWrite-OctopusInformation \"Wait for Finish: $runbookWaitForFinish\"\nWrite-OctopusInformation \"Use Guided Failure: $runbookUseGuidedFailure\"\nWrite-OctopusInformation \"Cancel run in seconds: $runbookCancelInSeconds\"\nWrite-OctopusInformation \"Use Published Snapshot: $runbookUsePublishedSnapshot\"\nWrite-OctopusInformation \"Auto Approve Runbook Run Manual Interventions: $autoApproveRunbookRunManualInterventions\"\nWrite-OctopusInformation \"Auto Approve environment name to pull approvals from: $approvalEnvironmentName\"\n\nWrite-OctopusInformation \"Octopus runbook run machines: $runbookMachines\"\nWrite-OctopusInformation \"Parent Task Id: $parentTaskId\"\nWrite-OctopusInformation \"Parent Release Id: $parentReleaseId\"\nWrite-OctopusInformation \"Parent Channel Id: $parentChannelId\"\nWrite-OctopusInformation \"Parent Environment Id: $parentEnvironmentId\"\nWrite-OctopusInformation \"Parent Runbook Id: $parentRunbookId\"\nWrite-OctopusInformation \"Parent Environment Name: $parentEnvironmentName\"\nWrite-OctopusInformation \"Parent Release Number: $parentReleaseNumber\"\n\n$verificationPassed = @()\n$verificationPassed += Test-RequiredValues -variableToCheck $runbookRunName -variableName \"Runbook Name\"\n$verificationPassed += Test-RequiredValues -variableToCheck $runbookBaseUrl -variableName \"Base Url\"\n$verificationPassed += Test-RequiredValues -variableToCheck $runbookApiKey -variableName \"Api Key\"\n$verificationPassed += Test-RequiredValues -variableToCheck $runbookEnvironmentName -variableName \"Environment Name\"\n\nif ($verificationPassed -contains $false)\n{\n\tWrite-OctopusInformation \"Required values missing\"\n\tExit 1\n}\n\n$runbookSpace = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookSpaceName -endpoint \"spaces\" -spaceId $null -octopusApiKey $runbookApiKey -defaultUrl $runbookBaseUrl -itemType \"Space\" -defaultValue $octopusSpaceId\n$runbookSpaceId = $runbookSpace.Id\n\n$projectToUse = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookProjectName -endpoint \"projects\" -spaceId $runbookSpaceId -defaultValue $null -itemType \"Project\" -octopusApiKey $runbookApiKey -defaultUrl $runbookBaseUrl\nif ($null -ne $projectToUse)\n{\t \n $runbookEndPoint = \"projects/$($projectToUse.Id)/runbooks\"\n}\nelse\n{\n\t$runbookEndPoint = \"runbooks\"\n}\n\n$environmentToUse = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookEnvironmentName -itemType \"Environment\" -defaultUrl $runbookBaseUrl -spaceId $runbookSpaceId -octopusApiKey $runbookApiKey -defaultValue $null -endpoint \"environments\"\n\n$runbookToRun = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookRunName -itemType \"Runbook\" -defaultUrl $runbookBaseUrl -spaceId $runbookSpaceId -endpoint $runbookEndPoint -octopusApiKey $runbookApiKey -defaultValue $null\n\n$runbookSnapShotIdToUse = Get-RunbookSnapshotIdToRun -runbookToRun $runbookToRun -runbookUsePublishedSnapshot $runbookUsePublishedSnapshot -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -spaceId $octopusSpaceId\n$projectNameForUrl = Get-ProjectSlug -projectToUse $projectToUse -runbookToRun $runbookToRun -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -spaceId $runbookSpaceId\n\n$tenantToUse = Get-OctopusItemFromListEndpoint -itemNameToFind $runbookTenantName -itemType \"Tenant\" -defaultValue $null -spaceId $runbookSpaceId -octopusApiKey $runbookApiKey -endpoint \"tenants\" -defaultUrl $runbookBaseUrl\nif ($null -ne $tenantToUse)\n{\t\n $tenantIdToUse = $tenantToUse.Id \n $runBookPreview = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -endPoint \"runbooks/$($runbookToRun.Id)/runbookRuns/preview/$($environmentToUse.Id)/$($tenantIdToUse)\" -method \"GET\" -item $null\n}\nelse\n{\n\ttry\n {\n \tWrite-Host \"Trying the new preview step\"\n \t$runBookPreview = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -endPoint \"runbookSnapshots/$($runbookSnapShotIdToUse)/runbookRuns/preview/$($environmentToUse.Id)?includeDisabledSteps=true\" -method \"GET\" -item $null\n }\n catch\n {\n \tWrite-Host \"The current version of Octopus Deploy doesn't support Runbook Snapshot Preview\"\n \t$runBookPreview = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -endPoint \"runbooks/$($runbookToRun.Id)/runbookRuns/preview/$($environmentToUse.Id)\" -method \"GET\" -item $null\n \t}\n}\n\n$childRunbookRunSpecificMachines = Get-RunbookSpecificMachines -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -runbookPreview $runBookPreview -runbookMachines $runbookMachines -runbookRunName $runbookRunName\n$runbookFormValues = Get-RunbookFormValues -runbookPreview $runBookPreview -runbookPromptedVariables $runbookPromptedVariables\n\n$queueDate = Get-QueueDate -futureDeploymentDate $runbookFutureDeploymentDate\n$queueExpiryDate = Get-QueueExpiryDate -queueDate $queueDate\n\n$runbookBody = @{\n RunbookId = $($runbookToRun.Id);\n RunbookSnapShotId = $runbookSnapShotIdToUse;\n FrozenRunbookProcessId = $null;\n EnvironmentId = $($environmentToUse.Id);\n TenantId = $tenantIdToUse;\n SkipActions = @();\n QueueTime = $queueDate;\n QueueTimeExpiry = $queueExpiryDate;\n FormValues = $runbookFormValues;\n ForcePackageDownload = $false;\n ForcePackageRedeployment = $true;\n UseGuidedFailure = $runbookUseGuidedFailure;\n SpecificMachineIds = @($childRunbookRunSpecificMachines);\n ExcludedMachineIds = @()\n}\n\n$approvalTaskId = Get-ApprovalTaskId -autoApproveRunbookRunManualInterventions $autoApproveRunbookRunManualInterventions -parentTaskId $parentTaskId -parentReleaseId $parentReleaseId -parentRunbookId $parentRunbookId -parentEnvironmentName $parentEnvironmentName -approvalEnvironmentName $approvalEnvironmentName -parentChannelId $parentChannelId -parentEnvironmentId $parentEnvironmentId -defaultUrl $runbookBaseUrl -spaceId $runbookSpaceId -octopusApiKey $runbookApiKey\n$parentTaskApprovers = Get-ParentTaskApprovers -parentTaskId $approvalTaskId -spaceId $runbookSpaceId -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey\n\nInvoke-OctopusDeployRunbook -runbookBody $runbookBody -runbookWaitForFinish $runbookWaitForFinish -runbookCancelInSeconds $runbookCancelInSeconds -projectNameForUrl $projectNameForUrl -defaultUrl $runbookBaseUrl -octopusApiKey $runbookApiKey -spaceId $runbookSpaceId -parentTaskApprovers $parentTaskApprovers -autoApproveRunbookRunManualInterventions $autoApproveRunbookRunManualInterventions -parentProjectName $projectNameForUrl -parentReleaseNumber $parentReleaseNumber -approvalEnvironmentName $approvalEnvironmentName -parentRunbookId $parentRunbookId -parentTaskId $approvalTaskId",
"Octopus.Action.Script.Syntax": "PowerShell"
},
"Category": "Octopus",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/run-octopus-runbook.json",
"Website": "/step-templates/0444b0b3-088e-4689-b755-112d1360ffe3",
"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"
}
}
Page updated on Tuesday, July 23, 2024