Find target usage

This script will accept a target name and loop through all the projects in the space and determines:

  • Does the target role(s) and environments match any steps in the deployment process.
  • Does the target role(s) and environments match any steps in any runbook processes.

Any matching projects or runbooks will be printed at the end of the script.

Please Note: This script reflects the current state of the runbooks and deployment process. It does not look at release snapshots or previously published runbooks. It also does not take tenants into account.

This script should work with any version of Octopus 4.0 or later. It has guard clauses in place to exclude spaces and runbooks based on the version.

Usage

Provide values for the following:

  • Octopus URL
  • Octopus API Key - the user associated with the API key will need read-only permissions on all spaces
  • Space Name - name of the space to search for, if left blank it will use the default space
  • Target Name - name of the target to search for

Script

PowerShell (REST API)
$ErrorActionPreference = "Stop"
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

$octopusUrl = "https://your-octopus-url"
$octopusApiKey = "API-YOUR-KEY"
$spaceName = "YOUR SPACE NAME"
$targetName = "YOUR TARGET NAME"

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

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

    if ($endPoint.ToLower().StartsWith("/api"))
    {
        $url = $octopusUrl + $endPoint
    }
    elseif ($endPoint.ToLower().StartsWith("api"))
    {
        $url = "$octopusUrlToUse/$EndPoint"
    }
    elseif ([string]::IsNullOrWhiteSpace($SpaceId))
    {
        $url = "$octopusUrlToUse/api/$EndPoint"
    }
    else
    {
        $url = "$octopusUrlToUse/api/$spaceId/$EndPoint"    
    }  

    $methodToUse = $method
    if ([string]::IsNullOrWhiteSpace($methodToUse) -eq $true)
    {
        $methodToUse = "GET"
    }

    try
    {        
        if ($null -ne $item)
        {
            $body = $item | ConvertTo-Json -Depth 10
            Write-Verbose $body
            Write-Host "Invoking $methodToUse $url"
            return Invoke-RestMethod -Method $methodToUse -Uri $url -Headers @{"X-Octopus-ApiKey" = "$ApiKey" } -Body $body -ContentType 'application/json; charset=utf-8' 
        }

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

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

function Test-OctopusObjectHasProperty
{
    param(
        $objectToTest,
        $propertyName
    )

    $hasProperty = Get-Member -InputObject $objectToTest -Name $propertyName -MemberType Properties

    if ($hasProperty)
    {
        Write-Verbose "$propertyName property found."
        return $true
    }
    else
    {
        Write-Verbose "$propertyName property missing."
        return $false
    }    
}

function Get-ProjectEnvironmentIds
{
    param (
        $project,
        $octopusUrl,
        $octopusApiKey,
        $spaceId
    )

    $channelList = Invoke-OctopusApi -octopusUrl $octopusUrl -apiKey $octopusApiKey -endPoint "projects/$($project.Id)/channels?skip=0&take=10000" -spaceId $spaceId
    $lifeCycleIds = @()
    $lifeCycleIds += $project.LifecycleId    
    foreach ($channel in $channelList)
    {
        if ($null -ne $channel.LifecycleId -and $lifeCycleIds -notcontains $channel.LifecycleId)
        {
            $lifeCycleIds += $lifeCycleIds
        }
    }

    $environmentIds = @()
    foreach ($lifecycleId in $lifeCycleIds)
    {
        $lifecycle = Invoke-OctopusApi -octopusUrl $octopusUrl -spaceId $spaceId -apiKey $octopusApiKey -endPoint "lifecycles/$lifecycleId"

        $phases = $lifecycle.Phases

        if ($lifecycle.Phases.Count -eq 0)
        {
            $lifecyclePreview = Invoke-OctopusApi -octopusUrl $octopusUrl -spaceId $null -endPoint $lifecycle.Links.Preview -apiKey $octopusApiKey
            $phases = $lifecyclePreview.Phases
        }

        foreach ($phase in $phases)
        {
            foreach ($environmentId in $phase.AutomaticDeploymentTargets)
            {
                if ($environmentIds -notcontains $environmentId)
                {
                    $environmentIds += $environmentId
                }
            }

            foreach ($environmentId in $phase.OptionalDeploymentTargets)
            {
                if ($environmentIds -notcontains $environmentId)
                {
                    $environmentIds += $environmentId
                }
            }
        }
    }

    return $environmentIds
}

function Get-TargetIsScopedToProcess
{
    param (
        $process,
        $target,
        $projectEnvironmentList
    )

    foreach ($step in $process.Steps)
    {
        $roleFound = $false
        if (Test-OctopusObjectHasProperty -objectToTest $step.Properties -propertyName "Octopus.Action.TargetRoles")
        {
            $roleList = @($step.Properties.'Octopus.Action.TargetRoles' -split ",")            
            foreach ($role in $roleList)
            {
                if ($target.Roles -contains $role)
                {
                    Write-Host "Role $role on the step '$($step.Name)' matches a role in the $targetName"
                    $roleFound = $true
                    break
                }
            }
        }

        if ($roleFound -eq $false)
        {
            continue
        }

        Write-Host "Matching role was found, now checking the scoping of the step."
        foreach ($action in $step.Actions)
        {
            $hasEnvironmentScoping = $action.Environments.Count -gt 0            
            if ($hasEnvironmentScoping -eq $true)
            {
                foreach ($environment in $action.Environments)
                {
                    if ($target.EnvironmentIds -contains $environment)
                    {
                        Write-Host "The environments the step was assigned to and the target roles match the target, target is associated with the process"
                        return $true
                    }
                }

                continue
            }

            $hasExcludedEnvironmentScoping = $false
            $allEnvironmentsExcluded = $false 
            if (Test-OctopusObjectHasProperty -objectToTest $action -propertyName "ExcludedEnvironments")
            {
                $hasExcludedEnvironmentScoping = $action.ExcludedEnvironments.Count -gt 0 
                if ($hasExcludedEnvironmentScoping -eq $true)
                {
                    $environmentsTargetCanStillDeployTo = @()
                    foreach ($environmentId in $target.EnvironmentIds)
                    {
                        if ($action.ExcludedEnvironments -notcontains $environmentId -and $projectEnvironmentList -contains $environmentId)
                        {
                            $environmentsTargetCanStillDeployTo += $environmentId
                        }
                    }

                    $allEnvironmentsExcluded = $environmentsTargetCanStillDeployTo.Count -eq 0
                }
            }

            if ($hasEnvironmentScoping -eq $false -and $hasExcludedEnvironmentScoping -eq $false)
            {
                Write-Host "Target role matches the step and no step scoping was configured, target is associated with process"
                return $true            
            }

            if ($hasExcludedEnvironmentScoping -eq $true -and $allEnvironmentsExcluded -eq $false)
            {
                Write-Host "The step role matches the target, and exclusion environments were found, but not all environments associated with the target were excluded.  The target is associated with the process."
                return $true
            }
        }
    }

    return $false
}

$octopusApiInformation = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "api" -spaceId $null -apiKey $octopusApiKey
$splitVersion = $octopusApiInformation.Version -split "\."
$majorVersion = [int]$splitVersion[0]
$minorVersion = [int]$splitVersion[1]

$hasSpaces = $majorVersion -ge 2019
Write-Host "This version of Octopus has Spaces $hasSpaces"

$hasRunbooks = ($majorVersion -ge 2019 -and $minorVersion -ge 11) -or $majorVersion -ge 2020
Write-Host "This version of Octopus has runbooks $hasRunbooks"

$spaceId = $null
if ($hasSpaces -eq $true -and [string]::IsNullOrWhiteSpace($spaceName) -eq $false)
{
    $spaceList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "spaces?partialName=$([uri]::EscapeDataString($spaceName))&skip=0&take=100" -spaceId $null -apiKey $octopusApiKey
    $space = $spaceList.Items | Where-Object {$_.Name.ToLower().Trim() -eq $spaceName.ToLower().Trim()}

    if ($null -eq $space)
    {
        Write-Error "Unable to find $spaceName on $octopusUrl"
        Exit 1
    }

    $spaceId = $space.Id

    Write-Host "The id for the space $spaceName is $spaceId"
}
else
{
    Write-Host "This instance has no spaces, the space id will be null"    
}

$targetList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "machines?partialName=$([uri]::EscapeDataString($targetName))&skip=0&take=100" -spaceId $spaceId -apiKey $octopusApiKey
$target = $targetList.Items | Where-Object {$_.Name.ToLower().Trim() -eq $targetName.ToLower().Trim()} 

if ($null -eq $target)
{
    Write-Error "Unable to find $targetName on $octopusUrl"
    Exit 1
}

Write-Host "The id for $targetName is $($target.Id)"

$environmentList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "environments?skip=0&take=10000" -spaceId $spaceId -apiKey $octopusApiKey
$allEnvironmentIds = @($environmentList.Items |Select-Object -ExpandProperty Id)

$targetResults = @()

$projectList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "projects?skip=0&take=10000" -spaceId $spaceId -apiKey $octopusApiKey
foreach ($project in $projectList.Items)
{
    $projectEnvironmentList = Get-ProjectEnvironmentIds -project $project -spaceId $spaceId -octopusUrl $octopusUrl -octopusApiKey $octopusApiKey 
    
    $targetHasMatchingEnvironment = $false
    foreach ($environmentId in $projectEnvironmentList)
    {
        if ($target.EnvironmentIds -contains $environmentId)    
        {
            $targetHasMatchingEnvironment = $true
            break
        }
    }

    Write-Host "The project $($project.Name) can deploy to the same environments as the target $targetName $targetHasMatchingEnvironment"
    if ($targetHasMatchingEnvironment -eq $false)
    {
        continue
    }
    
    $deploymentProcess = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint $project.Links.DeploymentProcess -apiKey $octopusApiKey -spaceId $spaceId

    $targetIsScopedToDeploymentProcess = Get-TargetIsScopedToProcess -process $deploymentProcess -target $target -projectEnvironmentList $projectEnvironmentList
    if ($targetIsScopedToDeploymentProcess -eq $true)
    {
        $targetResults += "$($Project.Name) - Deployment Process"
    }   
    
    if ($hasRunbooks)
    {
        $runbookList = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint "projects/$($project.Id)/runbooks?skip=0&take=10000" -spaceId $spaceId -apiKey $octopusApiKey
        foreach ($runbook in $runbookList.Items)
        {
            $runbookProcess = Invoke-OctopusApi -octopusUrl $octopusUrl -endPoint $runbook.Links.RunbookProcesses -spaceId $null -apiKey $octopusApiKey
            $environmentListToFilterOn = $allEnvironmentIds

            if (Test-OctopusObjectHasProperty -objectToTest $runbook -propertyName "EnvironmentScope")
            {
                if ($runbook.EnvironmentScope -eq "FromProjectLifecycles")
                {
                    $environmentListToFilterOn = $projectEnvironmentList
                }
                elseif ($runbook.EnvironmentScope -eq "Specified")
                {
                    $environmentListToFilterOn = $runbook.Environments
                }
            }           

            $runbookHasMatchingEnvironments = $false
            foreach ($environmentId in $environmentListToFilterOn)
            {
                if ($target.EnvironmentIds -contains $environmentId)    
                {
                    $runbookHasMatchingEnvironments = $true
                    break
                }
            }

            if ($runbookHasMatchingEnvironments -eq $false)
            {
                continue
            }
            
            $targetIsScopedToRunbookProcess = Get-TargetIsScopedToProcess -process $runbookProcess -target $target -projectEnvironmentList $environmentListToFilterOn
            if ($targetIsScopedToRunbookProcess -eq $true)
            {
                $targetResults += "$($Project.Name) - $($Runbook.Name) Runbook"
            }
        }
    }
}

if ($targetResults.Count -eq 0)
{
    Write-Host "The target $targetName is not associated with any projects or runbooks."
}
else
{
    Write-Host ""
    Write-Host ""
    Write-Host "The target $targetName is associated with the following projects and runbooks"    
    foreach ($result in $targetResults)
    {
        Write-Host "    $result"
    }
}
PowerShell (Octopus.Client)
# Load octopus.client assembly
Add-Type -Path "c:\octopus.client\Octopus.Client.dll"

function Get-EnvironmentsScopedToProject
{
    param (
        $project,
        $octopusApiKey,
        $octopusUrl,
        $spaceId
    )

    $scopedEnvironmentList = @()

    $projectChannels = $repositoryForSpace.Projects.GetAllChannels($project)

    foreach ($channel in $projectChannels)
    {
        $lifecycleId = $channel.LifecycleId
        if ($null -eq $lifecycleId)
        {
            $lifecycleId = $project.LifecycleId
        }

        $lifecyclePreview = $repositoryForSpace.Lifecycles.Get($lifeCycleId)
        
        if (($null -eq $lifecyclePreview.Phases) -or ($lifecyclePreview.Phases.Count -eq 0))
        {
            # Lifecycle has no defined phases and uses environments, manually create the phases
            foreach ($environment in $repositoryForSpace.Environments.GetAll())
            {
                $phase = New-Object Octopus.Client.Model.PhaseResource
                $phase.Name = $environment.Name
                $phase.OptionalDeploymentTargets.Add($environment.Id)
                $lifecyclePreview.Phases.Add($phase)
            }
        }

        foreach ($phase in $lifecyclePreview.Phases)
        {
            foreach ($environmentId in $phase.AutomaticDeploymentTargets)
            {
                if ($scopedEnvironmentList -notcontains $environmentId)
                {
                    Write-Host "Adding $environmentId to $($project.Name) environment list"
                    $scopedEnvironmentList += $environmentId
                }
            }

            foreach ($environmentId in $phase.OptionalDeploymentTargets)
            {
                if ($scopedEnvironmentList -notcontains $environmentId)
                {
                    Write-Host "Adding $environmentId to $($project.Name) environment list"
                    $scopedEnvironmentList += $environmentId
                }
            }
        }
    }

    return $scopedEnvironmentList
}

function Get-TargetIsScopedToProcess
{
    param (
        $deploymentProcess,
        $target,
        $projectEnvironmentList
    )

    $roleFound = $false

    # Loop through steps
    foreach ($step in $deploymentProcess.Steps)
    {
        $roles = $step.Properties["Octopus.Action.TargetRoles"]
        if ($null -ne $roles)
        {
            $roles = $roles.Value.Split(",")
            foreach ($role in $roles)
            {
                # Check to see if target is in the list of roles
                if ($target.Roles -contains $role)
                {
                    Write-Host "Role $role on the step '$($step.Name)' matches a role in the $targetName"
                    $roleFound = $true
                    break                  
                }
            }
        }

        if ($roleFound -eq $false)
        {
            continue
        }

        Write-Host "Matching role was found, now checking the scoping of the step."
        foreach ($action in $step.Actions)
        {
            $hasEnvironmentScoping = $action.Environments.Count -gt 0
            if ($hasEnvironmentScoping -eq $true)
            {
                foreach ($environment in $action.Environment)
                {
                    if ($target.EnvironmentIds -contains $environment)
                    {
                        Write-Host "The environments the step was assigned to and the target roles match the target, target is associated with process."
                        return $true
                    }
                }

                continue
            }

            $hasExcludedEnvironmentScoping = $false
            $allEnvironmentsExcluded = $false 
            if (($null -ne $action.ExcludedEnvironments))
            {
                $hasExcludedEnvironmentScoping = $action.ExcludedEnvironments.Count -gt 0
                if ($hasExcludedEnvironmentScoping -eq $true)
                {
                    $environmentsTargetCanStillDeployTo = @()
                    foreach ($environmentId in $target.EnvironmentIds)
                    {
                        if ($action.ExcludedEnvironments -notcontains $environmentId -and $projectEnvironmentList -contains $environmentId)
                        {
                            $environmentsTargetCanStillDeployTo += $environmentId
                        }
                    }

                    $allEnvironmentsExcluded = $environmentsTargetCanStillDeployTo.Count -eq 0
                }
            }

            if ($hasEnvironmentScoping -eq $false -and $hasExcludedEnvironmentScoping -eq $false)
            {
                Write-Host "Target role matches the step and no step scoping was configured, target is associated with process"
                return $true
            }

            if ($hasExcludedEnvironmentScoping -eq $true -and $allEnvironmentsExcluded -eq $false)
            {
                Write-Host "The step role matches the target, and exclusion environments were found, but not all environments associated with the target were excluded.  The target is associated with the process."
                return $true
            }
        }
    }

    return $false
}

# Octopus variables
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$spaceName = "default"
$targetName = "YourMachine"

$endpoint = New-Object Octopus.Client.OctopusServerEndpoint $octopusURL, $octopusAPIKey
$repository = New-Object Octopus.Client.OctopusRepository $endpoint
$client = New-Object Octopus.Client.OctopusClient $endpoint

# Get space
$space = $repository.Spaces.FindByName($spaceName)
$repositoryForSpace = $client.ForSpace($space)

# Get projects
$projects = $repositoryForSpace.Projects.GetAll()

# Get target
$target = $repositoryForSpace.Machines.FindByName($targetName)

# Get environments
$environments = $repositoryForSpace.Environments.GetAll()
$allEnvironments = @()
foreach ($environment in $environments)
{
    $allEnvironments += $environment.Id
}

$targetResults = @()

# Loop through projects
foreach ($project in $projects)
{
    # Get project environments
    $projectEnvironments = Get-EnvironmentsScopedToProject -project $project -octopusApiKey $APIKey -octopusUrl $octopusURL -spaceId $space.Id

    $targetHasMatchingEnvironment = $false
    
    # Loop through environments
    foreach ($environmentId in $projectEnvironments)
    {
        if ($target.EnvironmentIds -contains $environmentId)
        {
            $targetHasMatchingEnvironment = $true
            break
        }
    }

    Write-Host "The project $($project.Name) can deploy to the same environments as the target $targetName $targetHasMatchingEnvironment"

    if($targetHasMatchingEnvironment -eq $false)
    {
        continue
    }

    
    if ($project.IsVersionControlled -eq $false)
    {
        # Get project deployment process
        $deploymentProcess = $repositoryForSpace.DeploymentProcesses.Get($project.DeploymentProcessId)

        $targetIsScopedToDeploymentProcess = Get-TargetIsScopedToProcess -deploymentProcess $deploymentProcess -target $target -projectEnvironmentList $projectEnvironments
        
        if ($targetIsScopedToDeploymentProcess -eq $true)
        {
            $targetResults += "$($project.Name) - DeploymentProcess"
        }
        
        # Get all runbooks
        $runbookList = $repositoryForSpace.Projects.GetAllRunbooks($project)

        # Loop through runbooks
        foreach ($runbook in $runbookList)
        {
            # Get the runbook process
            $runbookProcess = $repositoryForSpace.RunbookProcesses.Get($runbook.RunbookProcessId)
            $environmentListToFilterOn = $allEnvironments

            # Check to see if runbook has an environment scope
            if ($null -ne $runbook.EnvironmentScope)
            {
                if ($runbook.EnvironmentScope -eq "FromProjectLifecycles")
                {
                    $environmentListToFilterOn = $projectEnvironments
                }
                elseif ($runbook.EnvironmentScope -eq "Specified")
                {
                    $environmentListToFilterOn = $runbook.Environments
                }
            }

            $runbookHasMatchingEnvironments = $false
            foreach ($environmentId in $environmentListToFilterOn)
            {
                if ($target.EnvironmentIds -contains $environmentId)
                {
                    $runbookHasMatchingEnvironments = $true
                    break
                }
            }

            if ($runbookHasMatchingEnvironments -eq $false)
            {
                continue
            }

            $targetIsScopedToRunbookProcess = Get-TargetIsScopedToProcess -deploymentProcess $runbookProcess -target $target -projectEnvironmentList $environmentListToFilterOn

            if ($targetIsScopedToRunbookProcess -eq $true)
            {
                $targetResults += "$($project.Name) - $($runbook.Name) Runbook"
            }
            
        }
    }
    else
    {
        Write-Host "Project $($project.Name) is under version control, skipping."
        continue
    }
}

if ($targetResults.Count -eq 0)
{
    Write-Host "The target $targetName is not associated with any projects or runbooks."
}
else
{
    Write-Host "The target $targetName is associated with the following projects and runbooks:"
    foreach ($result in $targetResults)
    {
        Write-Host "`t$result"
    }
}
C#
// If using .net Core, be sure to add the NuGet package of System.Security.Permissions
#r "path\to\Octopus.Client.dll"

using Octopus.Client;
using Octopus.Client.Model;
using System;
using System.Linq;

var octopusURL = "https://your-octopus-url";
var octopusAPIKey = "API-YOUR-KEY";
string spaceName = "Default";
string targetName = "YourTarget";

// Create repository object
var endpoint = new OctopusServerEndpoint(octopusURL, octopusAPIKey);
var repository = new OctopusRepository(endpoint);
var client = new OctopusClient(endpoint);

// Get space repository
var space = repository.Spaces.FindByName(spaceName);
var repositoryForSpace = client.ForSpace(space);

// Get all projects
var projects = repositoryForSpace.Projects.GetAll();

// Get the target
var target = repositoryForSpace.Machines.FindByName(targetName);

// Get all environments
var environments = repositoryForSpace.Environments.GetAll();
System.Collections.Generic.List<string> allEnvironments = new System.Collections.Generic.List<string>();
foreach (var environment in environments)
{
    allEnvironments.Add(environment.Id);
}

System.Collections.Generic.List<string> targetResults = new System.Collections.Generic.List<string>();

// Loop through projects
foreach (var project in projects)
{
    // Get environments scoped to project
    var projectEnvironmentList = GetEnvironmentsScopedToProject(project, space, repositoryForSpace);
    bool targetHasMatchingEnvironment = false;

    // Loop through project environments
    foreach (string environmentId in projectEnvironmentList)
    {
        if (target.EnvironmentIds.Contains(environmentId))
        {
            targetHasMatchingEnvironment = true;
            break;
        }
    }

    Console.WriteLine(string.Format("The project {0} can deploy to the same environments as the target {1} {2}", project.Name, targetName, targetHasMatchingEnvironment.ToString()));

    if (!targetHasMatchingEnvironment)
    {
        continue;
    }

    if (!project.IsVersionControlled)
    {
        // Get the deployment process
        var deploymentProcess = repositoryForSpace.DeploymentProcesses.Get(project.DeploymentProcessId);
        bool targetIsScopedToDeploymentProcess = GetTargetIsScopedToProcess(deploymentProcess, target, projectEnvironmentList);

        if(targetIsScopedToDeploymentProcess)
        {
            targetResults.Add(string.Format("{0} - DeploymentProcess", project.Name));
        }

        // Get the runbooks associated with project
        var runbookList = repositoryForSpace.Projects.GetAllRunbooks(project);

        // Loop through the runbooks
        foreach (var runbook in runbookList)
        {
            // Get the runbook process
            var runbookProcess = repositoryForSpace.RunbookProcesses.Get(runbook.RunbookProcessId);
            System.Collections.Generic.List<string> environmentListToFilterOn = new System.Collections.Generic.List<string> (allEnvironments.ToArray());

            switch(runbook.EnvironmentScope)
            {
                case Octopus.Client.Model.RunbookEnvironmentScope.FromProjectLifecycles:
                    {
                        environmentListToFilterOn = projectEnvironmentList;
                        break;
                    }
                case Octopus.Client.Model.RunbookEnvironmentScope.Specified:
                    {
                        environmentListToFilterOn.Clear();
                        foreach (var runbookEnvironment in runbook.Environments.ToArray())
                        {
                            environmentListToFilterOn.Add(runbookEnvironment);
                        }
                        break;
                    }
            }

            bool runbookHasMatchingEnvironments = false;

            // Loop through the environment filter
            foreach (var environment in environmentListToFilterOn.ToArray())
            {
                if (target.EnvironmentIds.Contains(environment))
                {
                    runbookHasMatchingEnvironments = true;
                    break;
                }
            }
                
            if(!runbookHasMatchingEnvironments)
            {
                continue;
            }


            bool targetIsScopedToRunbookProcess = GetTargetIsScopedToProcess(runbookProcess, target, projectEnvironmentList);

            if (targetIsScopedToRunbookProcess)
            {
                targetResults.Add(string.Format("{0} - {1} Runbook", project.Name, runbook.Name));
            }
            
        }
    }
    else
    {
        Console.WriteLine(string.Format("Project {0} is under version control, skipping", project.Name));
        continue;
    }
}

if(targetResults.Count == 0)
{
    Console.WriteLine(string.Format("The target {0} is not associated with any projects or runbooks", target.Name));
}
else
{
    Console.WriteLine(string.Format("The target {0} is associated with the following projects and runbooks:", target.Name));
    foreach (string result in targetResults)
    {
        Console.WriteLine(string.Format("\t{0}", result));
    }
}

static System.Collections.Generic.List<string> GetEnvironmentsScopedToProject(ProjectResource Project, SpaceResource Space, IOctopusSpaceRepository RepositoryForSpace)
{
    System.Collections.Generic.List<string> scopedEnvironments = new System.Collections.Generic.List<string>();

    var projectChannels = RepositoryForSpace.Projects.GetAllChannels(Project);

    foreach (var channel in projectChannels)
    {
        string lifeCycleId = string.Empty;

        if (string.IsNullOrEmpty(channel.LifecycleId))
        {
            lifeCycleId = Project.LifecycleId;
        }

        var lifecyclePreview = RepositoryForSpace.Lifecycles.Get(lifeCycleId);

        if ((lifecyclePreview.Phases == null) || (lifecyclePreview.Phases.Count == 0))
        {
            foreach (var environment in RepositoryForSpace.Environments.GetAll())
            {
                PhaseResource phase = new PhaseResource();
                phase.Name = environment.Name;
                phase.OptionalDeploymentTargets.Add(environment.Id);
                lifecyclePreview.Phases.Add(phase);
            }
        }

        foreach (var phase in lifecyclePreview.Phases)
        {
            foreach (var environmentId in phase.AutomaticDeploymentTargets)
            {
                if (!scopedEnvironments.Contains(environmentId))
                {
                    Console.WriteLine(string.Format("Adding {0} to {1} environment list", environmentId, Project.Name));
                    scopedEnvironments.Add(environmentId);
                }
            }

            foreach (var environmentId in phase.OptionalDeploymentTargets)
            {
                if (!scopedEnvironments.Contains(environmentId))
                {
                    Console.WriteLine(string.Format("Adding {0} to {1} environment list", environmentId, Project.Name));
                    scopedEnvironments.Add(environmentId);
                }
            }
        }
    }

    return scopedEnvironments;
}

static bool GetTargetIsScopedToProcess(Octopus.Client.Model.IProcessResource process, Octopus.Client.Model.MachineResource target, System.Collections.Generic.List<string> projectEnvironmentList)
{
    // Loop through steps
    foreach (var step in process.Steps)
    {
        bool roleFound = false;
        
        // Get the roles assigned to step
        Octopus.Client.Model.PropertyValueResource roles = step.Properties.ContainsKey("Octopus.Action.TargetRoles") ? step.Properties["Octopus.Action.TargetRoles"] : null;

        // Check for null
        if (roles != null)
        {
            // Get the role list int a list
            System.Collections.Generic.List<string> octopusRoles = new System.Collections.Generic.List<string> (roles.Value.Split(","));

            // Loop through roles
            foreach (string role in octopusRoles)
            {
                // Check to see if target has the role
                if (target.Roles.Contains(role))
                {
                    Console.WriteLine(string.Format("Role {0} on the step {1} matches a role in {2}", role, step.Name, target.Name));
                    roleFound = true;
                    break;
                }
            }

            if(!roleFound)
            {
                continue;
            }

            Console.WriteLine(string.Format("Matching role was found, now checking the scoping of the step."));
            foreach (var action in step.Actions)
            {
                bool hasEnvironmentScoping = action.Environments.Count > 0;
                if (hasEnvironmentScoping)
                {
                    foreach(var environment in action.Environments)
                    {
                        if (target.EnvironmentIds.Contains(environment))
                        {
                            Console.WriteLine(string.Format("The environments the step was assigned to and the target roles match the target, target is associated with process"));
                            return true;
                        }
                    }

                    continue;
                }

                bool hasExcludedEnvironmentScoping = false;
                bool allEnvironmentsExcluded = false;

                if (action.ExcludedEnvironments != null)
                {
                    hasExcludedEnvironmentScoping = action.ExcludedEnvironments.Count > 0;
                    if(hasExcludedEnvironmentScoping)
                    {
                        System.Collections.Generic.List<string> environmentsTargetCanStillDeployTo = new System.Collections.Generic.List<string>();
                        foreach (var environmentId in target.EnvironmentIds)
                        {
                            if ((!action.ExcludedEnvironments.Contains(environmentId)) && (projectEnvironmentList.Contains(environmentId)))
                            {
                                environmentsTargetCanStillDeployTo.Add(environmentId);
                            }
                        }

                        allEnvironmentsExcluded = environmentsTargetCanStillDeployTo.Count == 0;
                    }
                }

                if (!hasEnvironmentScoping && !hasExcludedEnvironmentScoping)
                {
                    Console.WriteLine(string.Format("Target role matches the step and no step scoping was configured, target is associated with process."));
                    return true;
                }

                if (hasExcludedEnvironmentScoping && !allEnvironmentsExcluded)
                {
                    Console.WriteLine(string.Format("The step role matches the target, and exclusion environments were found, but not all environments associated with the target were excluded.  The target is associated with the process."));
                    return true;
                }
            }
        }
    }

    return false;
}
Python3
import json
import requests
from requests.api import get, head
import re

def get_octopus_resource(uri, headers, skip_count = 0):
    items = []
    skip_querystring = ""

    if '?' in uri:
        skip_querystring = '&skip='
    else:
        skip_querystring = '?skip='

    response = requests.get((uri + skip_querystring + str(skip_count)), headers=headers)
    response.raise_for_status()

    # Get results of API call
    results = json.loads(response.content.decode('utf-8'))

    # Store results
    if hasattr(results, 'keys') and 'Items' in results.keys():
        items += results['Items']

        # Check to see if there are more results
        if (len(results['Items']) > 0) and (len(results['Items']) == results['ItemsPerPage']):
            skip_count += results['ItemsPerPage']
            items += get_octopus_resource(uri, headers, skip_count)

    else:
        return results

    
    # return results
    return items

def get_environments_scoped_to_project (octopus_server_uri, headers, project, space):
    scoped_environment_list = []

    # Get project channels
    uri = '{0}/api/{1}/projects/{2}/channels'.format(octopus_server_uri, space['Id'], project['Id'])
    channels = get_octopus_resource(uri, headers)

    # Loop through channels
    for channel in channels:
        lifecycleId = channel['LifecycleId']

        if None == lifecycleId:
            # Channel inherits lifecycle from project
            lifecycleId = project['LifecycleId']

        # Get lifecycle preview - using the preview returns implied phases if the lifecycle doesn't have any phases defined
        uri = '{0}/api/{1}/lifecycles/{2}/preview'.format(octopus_server_uri, space['Id'], lifecycleId)
        lifecycle_preview = get_octopus_resource(uri, headers)

        # Loop through phases
        for phase in lifecycle_preview['Phases']:
            for environmentId in phase['AutomaticDeploymentTargets']:
                if environmentId not in scoped_environment_list:
                    print ('Adding {0} to {1} environment list'.format(environmentId, project['Name']))
                    scoped_environment_list.append(environmentId)
            
            for environmentId in phase['OptionalDeploymentTargets']:
                if environmentId not in scoped_environment_list:
                    print ('Adding {0} to {1} environment list'.format(environmentId, project['Name']))
                    scoped_environment_list.append(environmentId)

    return scoped_environment_list

def get_target_is_scoped_to_process (process, target, project_environment_list):
    # Loop through steps
    for step in process['Steps']:
        role_found = False
        if 'Octopus.Action.TargetRoles' in step['Properties']:
            role_list = step['Properties']['Octopus.Action.TargetRoles'].split(',')
            for role in role_list:
                if role in target['Roles']:
                    print("Role {0} on the step {1} matches a role in {2}".format(role, step['Name'], target['Name']))
                    role_found = True
                    break

        if role_found == False:
            continue

        print('Matching role was found, now checking the scoping of the step')
        for action in step['Actions']:
            has_environment_scoping = len(action['Environments']) > 0
            if has_environment_scoping == True:
                for environment in action['Environments']:
                    if environment in target['EnvironmentIds']:
                        print('The environments the step was assigned to and the target roles match the target, target is associated with the process')
                        return True

                continue

            has_excluded_environment_scoping = False
            all_environments_excluded = False

            if 'ExcludedEnvironments' in action:
                has_excluded_environment_scoping = len(action['ExcludedEnvironments']) > 0
                if has_excluded_environment_scoping:
                    environments_target_can_still_deploy_to = []
                    for environmentId in target['EnvironmentIds']:
                        if environmentId not in action['ExcludedEnvironments'] and environmentId in project_environment_list:
                            environments_target_can_still_deploy_to.append(environmentId)
                        
                    
                    all_environments_excluded = len(environments_target_can_still_deploy_to) == 0

            if has_environment_scoping == False and has_excluded_environment_scoping == False:
                print("Target role matches the step and no step scoping was configured, target is associated with process")
                return True

            if has_excluded_environment_scoping == True and all_environments_excluded == False:
                print('The step role matches the target, and exclusion environments were found, but not all environments associated with the target were excluded.  The target is associated with the process')
                return True
            
        return False

octopus_server_uri = 'https://your-octopus-url'
octopus_api_key = 'API-YOUR-KEY'
headers = {'X-Octopus-ApiKey': octopus_api_key}
space_name = "Default"
target_name = "YourTarget"

# Get space
uri = '{0}/api/spaces'.format(octopus_server_uri)
spaces = get_octopus_resource(uri, headers)
space = next((x for x in spaces if x['Name'] == space_name), None)

# Get projects
uri = '{0}/api/{1}/projects'.format(octopus_server_uri, space['Id'])
projects = get_octopus_resource(uri, headers)

# Get the target
uri = '{0}/api/{1}/machines'.format(octopus_server_uri, space['Id'])
machines = get_octopus_resource(uri, headers)
target = next((x for x in machines if x['Name'] == target_name), None)

# Get the environments
uri = '{0}/api/{1}/environments'.format(octopus_server_uri, space['Id'])
allEnvironments = []
environments = get_octopus_resource(uri, headers)
for environment in environments:
    allEnvironments.append(environment['Id'])

target_results = []

for project in projects:
    # Get environments scoped to the project
    project_environment_list = get_environments_scoped_to_project(octopus_server_uri, headers, project, space)

    target_has_matching_environment = False

    # Loop through environments
    for environmentId in project_environment_list:
        if environmentId in target['EnvironmentIds']:
            target_has_matching_environment = True
            break
    
    print ('The project {0} can deploy to the sam environments as the target {1}: {2}'.format(project['Name'], target['Name'], target_has_matching_environment))

    if target_has_matching_environment == False:
        continue

    if project['IsVersionControlled'] == False:
        # Get the deployment process
        uri = '{0}/api/{1}/deploymentprocesses/{2}'.format(octopus_server_uri, space['Id'], project['DeploymentProcessId'])
        deployment_process = get_octopus_resource(uri, headers)

        target_is_scoped_to_deployment_process = get_target_is_scoped_to_process(deployment_process, target, project_environment_list)

        if target_is_scoped_to_deployment_process == True:
            target_results.append('{0} - DeploymentProcess'.format(project['Name']))

        # Get runbooks
        uri = '{0}/api/{1}/projects/{2}/runbooks'.format(octopus_server_uri, space['Id'], project['Id'])
        runbook_list = get_octopus_resource(uri, headers)

        # Loop through runbooks
        for runbook in runbook_list:
            uri = '{0}/api/{1}/runbookprocesses/{2}'.format(octopus_server_uri, space['Id'], runbook['RunbookProcessId'])
            runbook_process = get_octopus_resource(uri, headers)
            environment_list_to_filter_on = allEnvironments

            if 'EnvironmentScope' in runbook:
                if runbook['EnvironmentScope'] == 'FromProjectLifeCycles':
                    environment_list_to_filter_on = project_environment_list
                elif runbook['EnvironmentScope'] == 'Specified':
                    environment_list_to_filter_on = runbook['Environments']
            
            runbook_has_matching_environments = False

            for environmentId in environment_list_to_filter_on:
                if environmentId in target['EnvironmentIds']:
                    runbook_has_matching_environments = True
                    break

            if runbook_has_matching_environments == False:
                continue

            target_is_scoped_to_runbook_process = get_target_is_scoped_to_process(runbook_process, target, project_environment_list)

            if target_is_scoped_to_runbook_process == True:
                target_results.append('{0} - {1} Runbook'.format(project['Name'], runbook['Name']))
    else:
        print ('Project {0} is under version control, skipping'.format(project['Name']))

if len(target_results) == 0:
    print ('The target {0} is not associated with any projects or runbooks'.format(target['Name']))
else:
    print ('The target {0} is associated with the following projects and runbooks:'.format(target['Name']))
    for result in target_results:
        print('\t{0}'.format(result))
Go
package main

import (
	"fmt"
	"log"
	"net/url"
	"strings"

	"github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy"
)

func main() {

	apiURL, err := url.Parse("https://your-octopus-url")
	if err != nil {
		log.Println(err)
	}
	APIKey := "API-YOUR-KEY"
	spaceName := "Default"
	targetName := "MyTarget"

	// Get reference to space
	space := GetSpace(apiURL, APIKey, spaceName)

	// Create client object
	client := octopusAuth(apiURL, APIKey, space.ID)

	// Get target
    machine := GetTarget(apiURL, APIKey, space, targetName)

	if err != nil {
		log.Println(err)
	}
	// Get events
	projects, err := client.Projects.GetAll()

	if err != nil {
		log.Println(err)
	}

	// Get all environments
	environments, err := client.Environments.GetAll()
	if err != nil {
		log.Println(err)
	}

	allEnvironments := []string{}
	for i := 0; i < len(environments); i++ {
		allEnvironments = append(allEnvironments, environments[i].ID)
	}

	targetResults := []string{}

	// Loop through projects
	for _, project := range projects {
		// Get environments scoped to project
		projectEnvironmentList := GetEnvironmentsScopedToProject(client, project, space)

		targetHasMatchingEnvironment := false

		for _, environment := range projectEnvironmentList {
			if contains(target.EnvironmentIDs, environment) {
				targetHasMatchingEnvironment = true
				break
			}
		}

		fmt.Printf("The project %[1]s can deploy to the same environments as the target %[2]s: %[3]v \n", project.Name, target.Name, targetHasMatchingEnvironment)

		if !targetHasMatchingEnvironment {
			continue
		}

		if !project.IsVersionControlled {
			// Get deployment process
			deploymentProcess, err := client.DeploymentProcesses.GetByID(project.DeploymentProcessID)

			if err != nil {
				log.Println(err)
			}

			if project.Name == "WingtipToys" {
				fmt.Println("hi")
			}

			targetIsScopedToDeploymentProcess := GetTargetIsScopedToDeploymentProcess(deploymentProcess, target, projectEnvironmentList)

			if targetIsScopedToDeploymentProcess {
				targetResults = append(targetResults, (project.Name + " - DeploymentProcess"))
			}

			// Get all runbooks
			runbookList := GetRunbooks(client, project)

			// Loop through runbooks
			for _, runbook := range runbookList {
				runbookProcess, err := client.RunbookProcesses.GetByID(runbook.RunbookProcessID)

				if err != nil {
					log.Println(err)
				}

				environmentListToFilterOn := allEnvironments

				if runbook.EnvironmentScope != "" {
					if runbook.EnvironmentScope == "FromProjectLifecycles" {
						environmentListToFilterOn = projectEnvironmentList
					} else if runbook.EnvironmentScope == "Specified" {
						environmentListToFilterOn = runbook.Environments
					}
				}

				runbookHasMatchingEnvironments := false
				for _, environmentId := range environmentListToFilterOn {
					if contains(target.EnvironmentIDs, environmentId) {
						runbookHasMatchingEnvironments = true
						break
					}
				}

				if !runbookHasMatchingEnvironments {
					continue
				}

				targetIsScopedToRunbookProcess := GetTargetIsScopedToRunbookProcess(runbookProcess, target, projectEnvironmentList)

				if targetIsScopedToRunbookProcess {
					targetResults = append(targetResults, (project.Name + " - " + runbook.Name + " Runbook"))
				}
			}

		} else {
			fmt.Printf("Project %[1]s is under version control, skipping \n", project.Name)
		}

	}

	if len(targetResults) == 0 {
		fmt.Printf("The target %[1]s is not associated with any projects or runbooks \n", target.Name)
	} else {
		fmt.Printf("The target %[1]s is associated with the following projects and runbooks: \n", target.Name)
		for _, result := range targetResults {
			fmt.Println("\t" + result)
		}
	}
}

func octopusAuth(octopusURL *url.URL, APIKey, space string) *octopusdeploy.Client {
	client, err := octopusdeploy.NewClient(nil, octopusURL, APIKey, space)
	if err != nil {
		log.Println(err)
	}

	return client
}

func GetSpace(octopusURL *url.URL, APIKey string, spaceName string) *octopusdeploy.Space {
	client := octopusAuth(octopusURL, APIKey, "")

	spaceQuery := octopusdeploy.SpacesQuery{
		Name: spaceName,
	}

	// Get specific space object
	spaces, err := client.Spaces.Get(spaceQuery)

	if err != nil {
		log.Println(err)
	}

	for _, space := range spaces.Items {
		if space.Name == spaceName {
			return space
		}
	}

	return nil
}

func contains(s []string, str string) bool {
	for _, v := range s {
		if v == str {
			return true
		}
	}

	return false
}

func GetEnvironmentsScopedToProject(client *octopusdeploy.Client, project *octopusdeploy.Project, space *octopusdeploy.Space) []string {
	scopedEnvironmentList := []string{}

	// Get channels for project
	channels := GetChannels(client, project)

	// Loop through channels
	for i := 0; i < len(channels); i++ {
		lifecycleId := channels[i].LifecycleID

		// Check for nil
		if lifecycleId == "" {
			// Channel inherits lifecycle from project
			lifecycleId = project.LifecycleID
		}

		// Get the lifecycle
		lifecycle, err := client.Lifecycles.GetByID(lifecycleId)
		if err != nil {
			log.Println(err)
		}

		// Check phases
		if (lifecycle.Phases == nil) || (len(lifecycle.Phases) == 0) {
			// There are no defined phases, create them manually from the environment list
			environments, err := client.Environments.GetAll()
			if err != nil {
				log.Println(err)
			}

			// Loop through environments and add phases
			for e := 0; e < len(environments); e++ {
				phase := octopusdeploy.Phase{}
				phase.OptionalDeploymentTargets = append(phase.OptionalDeploymentTargets, environments[e].ID)
				lifecycle.Phases = append(lifecycle.Phases, phase)
			}
		}

		// Loop through phases
		for p := 0; p < len(lifecycle.Phases); p++ {
			for e := 0; e < len(lifecycle.Phases[p].AutomaticDeploymentTargets); e++ {
				if !contains(scopedEnvironmentList, lifecycle.Phases[p].AutomaticDeploymentTargets[e]) {
					fmt.Println("Adding " + lifecycle.Phases[p].AutomaticDeploymentTargets[e] + " to " + project.Name + " environment list")
					scopedEnvironmentList = append(scopedEnvironmentList, lifecycle.Phases[p].AutomaticDeploymentTargets[e])
				}
			}

			for e := 0; e < len(lifecycle.Phases[p].OptionalDeploymentTargets); e++ {
				if !contains(scopedEnvironmentList, lifecycle.Phases[p].OptionalDeploymentTargets[e]) {
					fmt.Println("Adding " + lifecycle.Phases[p].OptionalDeploymentTargets[e] + " to " + project.Name + " environment list")
					scopedEnvironmentList = append(scopedEnvironmentList, lifecycle.Phases[p].OptionalDeploymentTargets[e])
				}
			}
		}
	}

	return scopedEnvironmentList
}

func GetChannels(client *octopusdeploy.Client, project *octopusdeploy.Project) []*octopusdeploy.Channel {
	channelQuery := octopusdeploy.ChannelsQuery{
		Skip: 0,
	}

	results := []*octopusdeploy.Channel{}

	for true {
		// Call for results
		channels, err := client.Channels.Get(channelQuery)

		if err != nil {
			log.Println(err)
		}

		// Check returned number of items
		if len(channels.Items) == 0 {
			break
		}

		// append items to results
		results = append(results, channels.Items...)

		// Update query
		channelQuery.Skip += len(channels.Items)
	}

	return results
}

func GetTarget(octopusURL *url.URL, APIKey string, space *octopusdeploy.Space, targetName string) *octopusdeploy.DeploymentTarget {
	// Create client
	client := octopusAuth(octopusURL, APIKey, space.ID)

	machinesQuery := octopusdeploy.MachinesQuery{
		Name: targetName,
	}

	// Get specific machine object
	machines, err := client.Machines.Get(machinesQuery)

	if err != nil {
		log.Println(err)
	}

	for _, machine := range machines.Items {
		if machine.Name == targetName {
			return machine
		}
	}

	return nil
}

func GetTargetIsScopedToDeploymentProcess(process *octopusdeploy.DeploymentProcess, target *octopusdeploy.DeploymentTarget, projectEnvironmentList []string) bool {

	roleFound := false

	for _, step := range process.Steps {

		if _, found := step.Properties["Octopus.Action.TargetRoles"]; found {
			roles := strings.Split(step.Properties["Octopus.Action.TargetRoles"].Value, ",")

			for _, role := range roles {
				if contains(target.Roles, role) {
					fmt.Printf("Role %[1]s on the step %[2]s matches a role in the target %[3]s \n", role, step.Name, target.Name)
					roleFound = true
					break
				}
			}

			if !roleFound {
				continue
			}

			fmt.Printf("Matching role was found, now checking the scoping of the step \n")

			for _, action := range step.Actions {
				hasEnvironmentScoping := len(action.Environments) > 0
				if hasEnvironmentScoping {
					for _, environmentId := range action.Environments {
						if contains(target.EnvironmentIDs, environmentId) {
							fmt.Printf("The environments the step was assigned to and the target roles match the target, target is associated with process \n")
							return true
						}
					}

					continue
				}

				hasExcludedEnvironmentScoping := false
				allEnvironmentsExcluded := false

				if action.ExcludedEnvironments != nil {
					hasExcludedEnvironmentScoping = len(action.Environments) > 0
					if hasExcludedEnvironmentScoping {
						environmentsTargetCanStillDeployTo := []string{}
						for _, environmentId := range target.EnvironmentIDs {
							if !contains(action.ExcludedEnvironments, environmentId) && contains(projectEnvironmentList, environmentId) {
								environmentsTargetCanStillDeployTo = append(environmentsTargetCanStillDeployTo, environmentId)
							}
						}

						allEnvironmentsExcluded = len(environmentsTargetCanStillDeployTo) == 0
					}
				}

				if !hasEnvironmentScoping && !hasExcludedEnvironmentScoping {
					fmt.Printf("Target role matches the step and no step scoping was configured, target is associated with process \n")
					return true
				}

				if hasExcludedEnvironmentScoping && !allEnvironmentsExcluded {
					fmt.Printf("The step role matches, the target, and exclusion environments were found, but not all environments associated with the target were excluded.  The target is associated with the process. \n")
					return true
				}
			}
		}
	}
	return false
}

func GetTargetIsScopedToRunbookProcess(process *octopusdeploy.RunbookProcess, target *octopusdeploy.DeploymentTarget, projectEnvironmentList []string) bool {

	roleFound := false

	for _, step := range process.Steps {

		if _, found := step.Properties["Octopus.Action.TargetRoles"]; found {
			roles := strings.Split(step.Properties["Octopus.Action.TargetRoles"].Value, ",")

			for _, role := range roles {
				if contains(target.Roles, role) {
					fmt.Printf("Role %[1]s on the step %[2]s matches a role in the target %[3]s \n", role, step.Name, target.Name)
					roleFound = true
					break
				}
			}

			if !roleFound {
				continue
			}

			fmt.Printf("Matching role was found, now checking the scoping of the step \n")

			for _, action := range step.Actions {
				hasEnvironmentScoping := len(action.Environments) > 0
				if hasEnvironmentScoping {
					for _, environmentId := range action.Environments {
						if contains(target.EnvironmentIDs, environmentId) {
							fmt.Printf("The environments the step was assigned to and the target roles match the target, target is associated with process \n")
							return true
						}
					}

					continue
				}

				hasExcludedEnvironmentScoping := false
				allEnvironmentsExcluded := false

				if action.ExcludedEnvironments != nil {
					hasExcludedEnvironmentScoping = len(action.Environments) > 0
					if hasExcludedEnvironmentScoping {
						environmentsTargetCanStillDeployTo := []string{}
						for _, environmentId := range target.EnvironmentIDs {
							if !contains(action.ExcludedEnvironments, environmentId) && contains(projectEnvironmentList, environmentId) {
								environmentsTargetCanStillDeployTo = append(environmentsTargetCanStillDeployTo, environmentId)
							}
						}

						allEnvironmentsExcluded = len(environmentsTargetCanStillDeployTo) == 0
					}
				}

				if !hasEnvironmentScoping && !hasExcludedEnvironmentScoping {
					fmt.Printf("Target role matches the step and no step scoping was configured, target is associated with process \n")
					return true
				}

				if hasExcludedEnvironmentScoping && !allEnvironmentsExcluded {
					fmt.Printf("The step role matches, the target, and exclusion environments were found, but not all environments associated with the target were excluded.  The target is associated with the process. \n")
					return true
				}
			}
		}
	}
	return false
}

func GetRunbooks(client *octopusdeploy.Client, project *octopusdeploy.Project) []*octopusdeploy.Runbook {
	// Get runbook
	runbooks, err := client.Runbooks.GetAll()
	projectRunbooks := []*octopusdeploy.Runbook{}

	if err != nil {
		log.Println(err)
	}

	for i := 0; i < len(runbooks); i++ {
		if runbooks[i].ProjectID == project.ID {
			projectRunbooks = append(projectRunbooks, runbooks[i])
		}
	}

	return projectRunbooks
}

Help us continuously improve

Please let us know if you have any feedback about this page.

Send feedback

Page updated on Sunday, January 1, 2023