From 470799de76df5d56d913939b8174ec159d341199 Mon Sep 17 00:00:00 2001 From: Bob Walker Date: Wed, 29 Jan 2025 09:03:50 -0600 Subject: [PATCH] Updating the run a runbook step to support CaC runbooks --- step-templates/run-octopus-runbook.json | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/step-templates/run-octopus-runbook.json b/step-templates/run-octopus-runbook.json index 9318c3980..8cf4b07f5 100644 --- a/step-templates/run-octopus-runbook.json +++ b/step-templates/run-octopus-runbook.json @@ -3,13 +3,13 @@ "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", "ActionType": "Octopus.Script", - "Version": 16, + "Version": 17, "Author": "bobjwalker", "Packages": [], "GitDependencies": [], "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.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$parentBranchName = $OctopusParameters[\"Run.Runbook.CaCBranchName\"]\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 try \n {\n Write-Highlight $message \n }\n catch \n {\n Write-Host $message ## Using a try-catch block so we can test this locally\n }\n \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-OctopusInformation \"Invoking $method $url\"\n return Invoke-RestMethod -Method $method -Uri $url -Headers @{\"X-Octopus-ApiKey\" = \"$ApiKey\" } -Body $body -ContentType 'application/json; charset=utf-8'\n }\n 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 = Get-RunbookPackages -snapshotTemplate $snapshotTemplate -octopusUrl $defaultUrl -apiKey $octopusApiKey -spaceId $spaceId -usingCaC $false\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-RunbookPackages\n{\n param(\n $snapshotTemplate,\n $octopusUrl,\n $apiKey,\n $spaceId,\n $usingCaC\n )\n\n $runbookPackages = @()\n foreach ($package in $snapshotTemplate.Packages)\n {\n $packageVersion = Invoke-OctopusApi -octopusUrl $octopusUrl -apiKey $apiKey -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\n return $runbookPackages\n}\n\nfunction Get-RunbookGitReferences\n{\n param(\n $snapshotTemplate\n )\n\n $runbookReferences = @()\n foreach ($reference in $snapshotTemplate.SelectedGitResources)\n { \n $gitReference = $reference.DefaultBranch\n if (($gitReference -contains \"refs/heads/\") -eq $false)\n {\n $gitReference = \"refs/heads/$gitReference\"\n }\n\n $runbookReferences += @{\n StepName = $reference.ActionName\n GitReferenceResource = @{\n GitRef = $gitReference\n }\n GitResourceReferenceName = \"\" \n }\n }\n\n return $runbookReferences\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 $usingCaC,\n $cacRunbookEndpoint,\n $runbookNameForUrl \n )\n\n if ($usingCaC -eq $true)\n {\n $runbookResponse = Invoke-OctopusApi -octopusUrl $defaultUrl -spaceId $spaceId -apiKey $octopusApiKey -item $runbookBody -method \"POST\" -endPoint \"$($cacRunbookEndpoint)/$runbookNameForUrl/run/v1\"\n\n $runbookServerTaskId = $runBookResponse.Resources[0].TaskId\n Write-OctopusInformation \"The task id of the new task is $runbookServerTaskId\"\n\n $runbookRunId = $runbookResponse.Resources[0].Id\n Write-OctopusInformation \"The runbook run id is $runbookRunId\" \n }\n else \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\n Write-OctopusSuccess \"Runbook was successfully invoked, you can access the launched runbook [here]($defaultUrl/app#/$spaceId/tasks/$($runbookServerTaskId))\" \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.\"\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-OctopusInformation \"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-OctopusInformation \"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-OctopusInformation \"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-OctopusInformation \"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\nfunction Get-NonTenantedRunbookPreview\n{\n param(\n $octopusUrl,\n $spaceId, \n $apiKey,\n $runbookSnapShotIdToUse,\n $runbookEnvironmentId,\n $runbookToRun,\n $usingCaC,\n $cacRunbookEndPoint\n )\n\n $runbookPreview = $null\n\n try\n {\n $runbookPreviewEndpoint = \"runbookSnapshots/$($runbookSnapShotIdToUse)/runbookRuns/preview/$($runbookEnvironmentId)?includeDisabledSteps=true\"\n if ($usingCaC -eq $true)\n {\n Write-OctopusInformation \"Using the CaC non-tenanted preview step\"\n \t $runbookPreviewEndpoint = \"$($cacRunbookEndPoint)/$($runbookToRun.Slug)/runbookRuns/preview/$($runbookEnvironmentId)?includeDisabledSteps=true\"\n } \n\n $runBookPreview = Invoke-OctopusApi -octopusUrl $octopusUrl -spaceId $spaceId -apiKey $apiKey -endPoint $runbookPreviewEndpoint -method \"GET\" -item $null\n \t\n }\n catch\n {\n \tWrite-OctopusInformation \"The current version of Octopus Deploy doesn't support Runbook Snapshot Preview\"\n \t$runBookPreview = Invoke-OctopusApi -octopusUrl $octopusUrl -spaceId $spaceId -apiKey $apiKey -endPoint \"runbooks/$($runbookToRun.Id)/runbookRuns/preview/$($environmentToUse.Id)\" -method \"GET\" -item $null\n \t}\n\n return $runbookPreview\n}\n\nfunction Get-TenantedRunbookPreview \n{\n param(\n $octopusUrl,\n $spaceId, \n $apiKey,\n $runbookSnapShotIdToUse,\n $runbookEnvironmentId,\n $runbookToRun,\n $tenantIdToUse,\n $usingCaC,\n $cacRunbookEndPoint\n )\n\n $runbookPreview = $null\n\n if ($usingCaC -eq $true)\n {\n Write-OctopusInformation \"Using the CaC tenanted preview step that requires a POST instead of GET\" \n $runbookPreviewBody = @{\n DeploymentPreviews = @( \n @{\n EnvironmentId = $runbookEnvironmentId;\n TenantId = $tenantIdToUse;\n }\n )\n } \n \n $runBookGroupedPreview = Invoke-OctopusApi -octopusUrl $octopusUrl -spaceId $spaceId -apiKey $apiKey -endPoint \"$($runbookEndPoint)/$($runbookToRun.Slug)/runbookRuns/previews\" -method \"POST\" -item $runbookPreviewBody \n $runbookPreview = $runBookGroupedPreview[0]\n }\n else\n {\n $runBookPreview = Invoke-OctopusApi -octopusUrl $octopusUrl -spaceId $spaceId -apiKey $apiKey -endPoint \"runbooks/$($runbookToRun.Id)/runbookRuns/preview/$($runbookEnvironmentId)/$($tenantIdToUse)\" -method \"GET\" \n } \n\n return $runbookPreview\n}\nfunction Get-RunbookEndPoint\n{\n param ( \n $cacBranchName,\n $projectToUse,\n $usingCaC\n )\n\n if ($usingCaC -eq $true)\n {\n return \"projects/$($projectToUse.Id)/$([uri]::EscapeDataString($cacBranchName))/runbooks\" \n }\n else\n {\n return \"projects/$($projectToUse.Id)/runbooks\"\n }\n}\n\nfunction Get-IsProjectUsingCaCRunbooks\n{\n param(\n $projectToUse\n )\n\n $hasPersistenceSettings = Get-Member -InputObject $projectToUse -Name \"PersistenceSettings\" -MemberType Properties\n if (!$hasPersistenceSettings)\n {\n return $false; \n }\n\n Write-OctopusInformation \"The project has the PersistenceSettings object.\"\n $hasConversionState = Get-Member -InputObject $projectToUse.PersistenceSettings -Name \"ConversionState\" -MemberType Properties \n if (!$hasConversionState)\n {\n return $false;\n }\n\n Write-OctopusInformation \"The project has the PersistenceSettings object.\"\n $hasRunbooksInGit = Get-Member -InputObject $projectToUse.PersistenceSettings.ConversionState -Name \"RunbooksAreInGit\" -MemberType Properties \n \n if (!$hasRunbooksInGit)\n {\n return $false;\n }\n\n $isUsingCaC = $projectToUse.PersistenceSettings.ConversionState.RunbooksAreInGit \n Write-OctopusInformation \"The project is using CaC: $isUsingCaC\"\n\n return $isUsingCaC\n}\n\nfunction Get-CaCBranchName\n{\n param(\n $usingCac,\n $projectToUse,\n $providedBranchName\n )\n\n if ($usingCac -eq $false)\n {\n Write-OctopusInformation \"The project isn't using CaC, so no need to pull the branch name\"\n return $null\n }\n\n if ([string]::IsNullOrWhiteSpace($providedBranchName) -eq $false)\n {\n if ($providedBranchName -like \"refs/heads/*\")\n {\n Write-OctopusInformation \"The provided branch name is $providedBranchName, using that\"\n return $providedBranchName\n }\n\n Write-OctopusInformation \"The provided branch name $providedBranchName doesn't include refs/heads, adding it.\"\n return \"refs/heads/$providedBranchName\"\n }\n\n $returnedBranchName = \"refs/heads/$($projectToUse.PersistenceSettings.DefaultBranch)\"\n Write-OctopusInformation \"The branch name wasn't provided, using the default branch name $returnedBranchName\"\n return $returnedBranchName\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\"\nWrite-OctopusInformation \"Parent Branch Name: $parentBranchName\"\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\n$projectVerificationPassed = Test-RequiredValues -variableToCheck $runbookProjectName -variableName \"Project Name\"\nif ($projectVerificationPassed -eq $false)\n{\n Write-OctopusCritical \"Project Name previously wasn't required in earlier versions, but you need to include it now. Without it, this step will find the first runbook that matches the name. And it is required for CaC going forward. Please add it to the step and try again.\" \n $verificationPassed += $projectVerificationPassed\n}\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\n\n$usingCaC = Get-IsProjectUsingCaCRunbooks -projectToUse $projectToUse\n$cacBranchName = Get-CaCBranchName -usingCac $usingCac -projectToUse $projectToUse -providedBranchName $parentBranchName\n$runbookEndPoint = Get-RunbookEndPoint -cacBranchName $cacBranchName -projectToUse $projectToUse -usingCaC $usingCaC\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\nif ($usingCaC -eq $false)\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}\nelse\n{\n $runbookSnapShotIdToUse = $null\n $projectNameForUrl = $projectToUse.Slug\n}\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 = Get-TenantedRunbookPreview -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -runbookSnapShotIdToUse $runbookSnapShotIdToUse -runbookEnvironmentId $environmentToUse.Id -runbookToRun $runbookToRun -tenantIdToUse $tenantToUse.Id -usingCaC $usingCaC -cacRunbookEndPoint $runbookEndPoint \n}\nelse\n{\n\t$runbookPreview = Get-NonTenantedRunbookPreview -octopusUrl $runbookBaseUrl -spaceId $runbookSpaceId -apiKey $runbookApiKey -runbookSnapShotIdToUse $runbookSnapShotIdToUse -runbookEnvironmentId $environmentToUse.Id -runbookToRun $runbookToRun -usingCaC $usingCaC -cacRunbookEndPoint $runbookEndPoint\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$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\nif ($usingCaC -eq $true)\n{\n $snapshotTemplate = Invoke-OctopusApi -octopusUrl $runbookBaseUrl -apiKey $runbookApiKey -spaceId $runbookSpaceId -endPoint \"$runbookEndPoint/$($runbookToRun.Slug)/runbookSnapShotTemplate\" -method \"Get\"\n $selectedGitResources = @(Get-RunbookGitReferences -snapshotTemplate $snapshotTemplate)\n $selectedPackages = @(Get-RunbookPackages -snapshotTemplate $snapshotTemplate -octopusUrl $runbookBaseUrl -apiKey $runbookApiKey -spaceId $runbookSpaceId -usingCaC $usingCaC)\n\n $runbookBody = @{\n SelectedGitResources = $selectedGitResources;\n SelectedPackages = $selectedPackages;\n Runs= @(\n @{\n EnvironmentId = $($environmentToUse.Id);\n ExcludedMachineIds = @(); \n ForcePackageDownload = $false;\n FormValues = $runbookFormValues;\n QueueTime = $queueDate;\n QueueTimeExpiry = $queueExpiryDate;\n SpecificMachineIds = @($childRunbookRunSpecificMachines);\n SkipActions = @();\n TenantId = $tenantIdToUse;\n UseGuidedFailure = $runbookUseGuidedFailure; \n }\n ); \n }\n\n 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 -usingCaC $true -cacRunbookEndpoint $runbookEndPoint -runbookNameForUrl $runbookToRun.Slug\n}\nelse\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 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\n}", "Octopus.Action.Script.Syntax": "PowerShell" }, "Parameters": [ @@ -57,8 +57,8 @@ "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": "", + "HelpText": "**Required**\n\nThe name of the project containing the runbook. Previous versions of this step allowed this parameter to be optional. With the addition of CaC, this is now a required step for everyone.", + "DefaultValue": "#{Octopus.Project.Name}", "DisplaySettings": { "Octopus.ControlType": "SingleLineText" } @@ -83,6 +83,16 @@ "Octopus.ControlType": "SingleLineText" } }, + { + "Id": "ffc33a7a-2a8e-4f7e-969f-bf5a377a078a", + "Name": "Run.Runbook.CaCBranchName", + "Label": "Runbook CaC Branch Name", + "HelpText": "**Optional**\n\nThe branch name to be used when invoking a CaC enabled runbook. \n\n**Tip:** If you call this step from a deployment, you can use `#{Octopus.Release.Git.BranchName}`.\n\n**Warning:** It will use the project's default branch if you do not provide a branch name for a CaC-enabled runbook. ", + "DefaultValue": "", + "DisplaySettings": { + "Octopus.ControlType": "SingleLineText" + } + }, { "Id": "9c49ba5c-337b-454a-8837-282353276aea", "Name": "Run.Runbook.UsePublishedSnapShot", @@ -195,10 +205,10 @@ } } ], - "LastModifiedBy": "millerjn21", + "LastModifiedBy": "bobjwalker", "$Meta": { - "ExportedAt": "2024-07-23T14:07:03.309Z", - "OctopusVersion": "2024.2.9274", + "ExportedAt": "2025-01-29T14:07:03.309Z", + "OctopusVersion": "2025.1.7770", "Type": "ActionTemplate" }, "Category": "octopus"