This script demonstrates how to programmatically find usages of a variable in all project variable sets (either a named match, or referenced in another variable), and optionally any deployment process or runbook processes.
Limitations: Please note the limitations with this example:
- It’s not possible to use the REST API to search through sensitive variable values, as these values will be returned as
null
. - Variables that are referenced inside of any packages included as part of a deployment or runbook are not searched.
Usage
Provide values for the following:
- Octopus URL
- Octopus API Key
- Name of the space to search
- Name of the variable to search for
- Boolean value to toggle searching in a project’s deployment process
- Boolean value to toggle searching in a project’s runbook processes
- (Optional) Boolean value to toggle searching in variable sets
- (Optional) path to export the results to a csv file
Script
PowerShell (REST API)
$ErrorActionPreference = "Stop";
# Define working variables
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }
# Specify the Space to search in
$spaceName = "Default"
# Specify the Variable to find, without OctoStache syntax
# e.g. For #{MyProject.Variable} -> use MyProject.Variable
$variableToFind = "MyProject.Variable"
# Search through Project's Deployment Processes?
$searchDeploymentProcesses = $True
# Search through Project's Runbook Processes?
$searchRunbooksProcesses = $True
# Search through Variable Set values?
$searchVariableSets = $False
# Optional: set a path to export to csv
$csvExportPath = ""
$variableTracking = @()
$octopusURL = $octopusURL.TrimEnd('/')
# Get space
$space = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/spaces/all" -Headers $header) | Where-Object { $_.Name -eq $spaceName }
Write-Host "Looking for usages of variable named $variableToFind in space: '$spaceName'"
# Function to process deployment steps
function Process-DeploymentSteps {
param(
$steps,
$project,
$gitRef = $null
)
$results = @()
# Loop through steps
foreach ($step in $steps) {
$props = $step | Get-Member | Where-Object { $_.MemberType -eq "NoteProperty" }
foreach ($prop in $props) {
$propName = $prop.Name
$json = $step.$propName | ConvertTo-Json -Compress -Depth 10
if ($null -ne $json -and ($json -like "*$variableToFind*")) {
$result = [pscustomobject]@{
Project = $project.Name
VariableSet = $null
MatchType = "Step"
Context = $step.Name
Property = $propName
AdditionalContext = $null
Link = "$octopusURL$($project.Links.Web)/deployments/process/steps?actionId=$($step.Actions[0].Id)"
}
if ($gitRef) {
$result | Add-Member -MemberType NoteProperty -Name "GitRef" -Value $gitRef
}
$results += $result
}
}
}
return $results
}
# Function to process runbook steps
function Process-RunbookSteps {
param(
$steps,
$project,
$runbook,
$gitRef = $null
)
$results = @()
# Loop through steps
foreach ($step in $steps) {
$props = $step | Get-Member | Where-Object { $_.MemberType -eq "NoteProperty" }
foreach ($prop in $props) {
$propName = $prop.Name
$json = $step.$propName | ConvertTo-Json -Compress -Depth 10
if ($null -ne $json -and ($json -like "*$variableToFind*")) {
$result = [pscustomobject]@{
Project = $project.Name
VariableSet = $null
MatchType = "Runbook Step"
Context = $runbook.Name
Property = $propName
AdditionalContext = $step.Name
Link = "$octopusURL$($project.Links.Web)/operations/runbooks/$($runbook.Id)/process/$($runbook.RunbookProcessId)/steps?actionId=$($step.Actions[0].Id)"
}
if ($gitRef) {
$result | Add-Member -MemberType NoteProperty -Name "GitRef" -Value $gitRef
}
$results += $result
}
}
}
return $results
}
# Get all projects
$projects = Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/projects/all" -Headers $header
# Loop through projects
foreach ($project in $projects) {
Write-Host "Checking project '$($project.Name)'"
# Get project variables
$projectVariableSet = Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/variables/$($project.VariableSetId)" -Headers $header
# Get all GitRefs for CaC project
if ($project.IsVersionControlled) {
$gitBranches = Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/projects/$($project.Id)/git/branches" -Headers $header
$gitTags = Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/projects/$($project.Id)/git/tags" -Headers $header
$gitRefs = @()
foreach($branch in $gitBranches.Items) {
$gitRefs += $branch.CanonicalName
}
foreach($tag in $gitTags.Items) {
$gitRefs += $tag.CanonicalName
}
}
# Check to see if variable is named in project variables.
$matchingNamedVariables = $projectVariableSet.Variables | Where-Object { $_.Name -ieq "$variableToFind" }
if ($null -ne $matchingNamedVariables) {
foreach ($match in $matchingNamedVariables) {
$result = [pscustomobject]@{
Project = $project.Name
VariableSet = $null
MatchType = "Named Project Variable"
Context = $match.Name
Property = $null
AdditionalContext = $match.Value
Link = "$octopusURL$($project.Links.Web)/variables"
}
# Add and de-dupe later
$variableTracking += $result
}
}
# Check to see if variable is referenced in other project variable values.
$matchingValueVariables = $projectVariableSet.Variables | Where-Object { $_.Value -like "*#{$variableToFind}*" }
if ($null -ne $matchingValueVariables) {
foreach ($match in $matchingValueVariables) {
$result = [pscustomobject]@{
Project = $project.Name
VariableSet = $null
MatchType = "Referenced Project Variable"
Context = $match.Name
Property = $null
AdditionalContext = $match.Value
Link = "$octopusURL$($project.Links.Web)/variables"
}
# Add and de-dupe later
$variableTracking += $result
}
}
# Search Deployment process if enabled
if ($searchDeploymentProcesses -eq $True) {
if ($project.IsVersionControlled) {
# For CaC Projects, loop through GitRefs
foreach ($gitRef in $gitRefs) {
$escapedGitRef = [Uri]::EscapeDataString($gitRef)
$processUrl = "$octopusURL/api/$($space.Id)/projects/$($project.Id)/$($escapedGitRef)/deploymentprocesses"
# Get project deployment process
$deploymentProcess = (Invoke-RestMethod -Method Get -Uri $processUrl -Headers $header)
# Add and de-dupe later
$variableTracking += Process-DeploymentSteps -steps $deploymentProcess.Steps -project $project -gitRef $gitRef
}
}
else {
# Get project deployment process
$deploymentProcess = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/deploymentprocesses/$($project.DeploymentProcessId)" -Headers $header)
# Add and de-dupe later
$variableTracking += Process-DeploymentSteps -steps $deploymentProcess.Steps -project $project
}
}
# Search Runbook processes if enabled
if ($searchRunbooksProcesses -eq $True) {
# Get project runbooks
$runbooks = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/projects/$($project.Id)/runbooks?skip=0&take=5000" -Headers $header)
# Loop through each runbook
foreach ($runbook in $runbooks.Items) {
# For CaC Projects, loop through GitRefs
if ($project.IsVersionControlled) {
foreach ($gitRef in $gitRefs) {
$escapedGitRef = [Uri]::EscapeDataString($gitRef)
$processUrl = "$octopusURL/api/$($space.Id)/projects/$($project.Id)/$($escapedGitRef)/runbookprocesses/$($runbook.RunbookProcessId)"
# Get runbook process
$runbookProcess = (Invoke-RestMethod -Method Get -Uri $processUrl -Headers $header)
# Add and de-dupe later
$variableTracking += Process-RunbookSteps -steps $runbookProcess.Steps -project $project -runbook $runbook -gitRef $gitRef
}
}
else {
# Get runbook process
$runbookProcess = (Invoke-RestMethod -Method Get -Uri "$octopusURL$($runbook.Links.RunbookProcesses)" -Headers $header)
# Add and de-dupe later
$variableTracking += Process-RunbookSteps -steps $runbookProcess.Steps -project $project -runbook $runbook
}
}
}
}
if ($searchVariableSets -eq $True) {
$VariableSets = (Invoke-RestMethod -Method Get "$OctopusURL/api/libraryvariablesets?contentType=Variables" -Headers $header).Items
foreach ($VariableSet in $VariableSets) {
Write-Host "Checking Variable Set: $($VariableSet.Name)"
$variables = (Invoke-RestMethod -Method Get "$OctopusURL/$($VariableSet.Links.Variables)" -Headers $header).Variables | Where-Object { $_.Value -like "*#{$variableToFind}*" }
$link = ($VariableSet.Links.Self -replace "/api", "app#") -replace "/libraryvariablesets/", "/library/variables/"
foreach ($variable in $variables) {
$result = [pscustomobject]@{
Project = $null
VariableSet = $VariableSet.Name
MatchType = "Variable Set"
Context = $variable.Name
Property = $null
AdditionalContext = $variable.Value
Link = "$octopusURL$($link)"
}
# Add and de-dupe later
$variableTracking += $result
}
}
}
# De-dupe
$variableTracking = @($variableTracking | Sort-Object -Property * -Unique)
if ($variableTracking.Count -gt 0) {
Write-Host ""
Write-Host "Found $($variableTracking.Count) results:"
$variableTracking
if (![string]::IsNullOrWhiteSpace($csvExportPath)) {
Write-Host "Exporting results to CSV file: $csvExportPath"
$variableTracking | Export-Csv -Path $csvExportPath -NoTypeInformation
}
}
PowerShell (Octopus.Client)
# Load assembly
Add-Type -Path 'path:\to\Octopus.Client.dll'
$octopusURL = "https://your-octopus-url"
$octopusAPIKey = "API-YOUR-KEY"
$spaceName = "Default"
$variableToFind = "MyProject.Variable"
$searchDeploymentProcesses = $true
$searchRunbookProcesses = $true
$csvExportPath = "path:\to\CSVFile.csv"
$variableTracking = @()
$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)
Write-Host "Looking for usages of variable named $variableToFind in space $($space.Name)"
# Get all projects
$projects = $repositoryForSpace.Projects.GetAll()
# Loop through projects
foreach ($project in $projects)
{
Write-Host "Checking $($project.Name)"
# Get variable set
$projectVariableSet = $repositoryForSpace.VariableSets.Get($project.VariableSetId)
# Find any name matches
$matchingNamedVariable = $projectVariableSet.Variables | Where-Object {$_.Name -ieq "$variableToFind"}
if ($null -ne $matchingNamedVariable)
{
foreach ($match in $matchingNamedVariable)
{
# Create new hash table
$result = [pscustomobject]@{
Project = $project.Name
MatchType = "Named Project Variable"
Context = $match.Name
Property = $null
AdditionalContext = $match.Value
Link = $project.Links["Variables"]
}
$variableTracking += $result
}
}
# Find any value matches
$matchingValueVariables = $projectVariableSet.Variables | Where-Object {$_.Value -like "*#{$variableToFind}*"}
if ($null -ne $matchingValueVariables)
{
foreach ($match in $matchingValueVariables)
{
$result = [pscustomobject]@{
Project = $project.Name
MatchType = "Referenced Project Variable"
Context = $match.Name
Property = $null
AdditionalContext = $match.Value
Link = $project.Links["Variables"]
}
$variableTracking += $result
}
}
if ($searchDeploymentProcesses -eq $true)
{
if ($project.IsVersionControlled -ne $true)
{
# Get deployment process
$deploymentProcess = $repositoryForSpace.DeploymentProcesses.Get($project.DeploymentProcessId)
# Loop through steps
foreach ($step in $deploymentProcess.Steps)
{
foreach ($action in $step.Actions)
{
foreach ($property in $action.Properties.Keys)
{
if ($action.Properties[$property].Value -like "*$variableToFind*")
{
$result = [pscustomobject]@{
Project = $project.Name
MatchType = "Step"
Context = $step.Name
Property = $property
AdditionalContext = $null
Link = "$octopusURL$($project.Links.Web)/deployments/process/steps?actionid=$($action.Id)"
}
$variableTracking += $result
}
}
}
}
}
else
{
Write-Host "$($project.Name) is version controlled, skipping searching the deployment process."
}
}
if ($searchRunbookProcesses -eq $true)
{
# Get project runbooks
$runbooks = $repositoryForSpace.Projects.GetAllRunbooks($project)
# Loop through runbooks
foreach ($runbook in $runbooks)
{
# Get Runbook process
$runbookProcess = $repositoryForSpace.RunbookProcesses.Get($runbook.RunbookProcessId)
foreach ($step in $runbookProcess.Steps)
{
foreach ($action in $step.Actions)
{
foreach ($property in $action.Properties.Keys)
{
if ($action.Properties[$property].Value -like "*$variableToFind*")
{
$result = [pscustomobject]@{
Project = $project.Name
MatchType = "Runbook Step"
Context = $runbook.Name
Property = $property
AdditionalContext = $step.Name
Link = "$octopusURL$($project.Links.Web)/operations/runbooks/$($runbook.Id)/process/$($runbook.RunbookProcessId)/steps?actionId=$($action.Id)"
}
$variableTracking += $result
}
}
}
}
}
}
}
# De-duplicate
$variableTracking = @($variableTracking | Sort-Object -Property * -Unique)
if ($variableTracking.Count -gt 0)
{
Write-Host ""
Write-Host "Found $($variableTracking.Count) results:"
$variableTracking
if(![string]::IsNullOrWhiteSpace($csvExportPath))
{
Write-Host "Exporting results to CSV file: $csvExportPath"
$variableTracking | Export-Csv -Path $csvExportPath -NoTypeInformation
}
}
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.Linq;
class VariableResult
{
// Define private variables
public string Project
{
get;
set;
}
public string MatchType
{
get; set;
}
public string Context
{
get;set;
}
public string Property
{
get;set;
}
public string AdditionalContext
{
get;set;
}
public string Link
{
get;
set;
}
}
var octopusURL = "https://your-octopus-url";
var octopusAPIKey = "API-YOUR-KEY";
var spaceName = "Default";
string variableToFind = "MyProject.Variable";
bool searchDeploymentProcess = true;
bool searchRunbookProcess = true;
string csvExportPath = "path:\\to\\variable.csv";
System.Collections.Generic.List<VariableResult> variableTracking = new System.Collections.Generic.List<VariableResult>();
// 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);
Console.WriteLine(string.Format("Looking for usages of variable named {0} in space {1}", variableToFind, space.Name));
// Get all projects
var projects = repositoryForSpace.Projects.GetAll();
// Loop through projects
foreach (var project in projects)
{
Console.WriteLine(string.Format("Checking {0}", project.Name));
// Get the project variable set
var projectVariableSet = repositoryForSpace.VariableSets.Get(project.VariableSetId);
var matchingNameVariable = projectVariableSet.Variables.Where(v => v.Name.ToLower().Contains(variableToFind.ToLower()));
// Match on name
if (matchingNameVariable != null)
{
// Loop through results
foreach (var match in matchingNameVariable)
{
VariableResult result = new VariableResult();
result.Project = project.Name;
result.MatchType = "Named Project Variable";
result.Context = match.Name;
result.Property = null;
result.AdditionalContext = match.Value;
result.Link = project.Links["Variables"];
if (!variableTracking.Contains(result))
{
variableTracking.Add(result);
}
}
}
// Match on value
var matchingValueVariable = projectVariableSet.Variables.Where(v => v.Value != null && v.Value.ToLower().Contains(variableToFind.ToLower()));
if (matchingValueVariable != null)
{
// Loop through results
foreach (var match in matchingValueVariable)
{
VariableResult result = new VariableResult();
result.Project = project.Name;
result.MatchType = "Referenced Project Variable";
result.Context = match.Name;
result.Property = null;
result.AdditionalContext = match.Value;
result.Link = project.Links["Variables"];
if (!variableTracking.Contains(result))
{
variableTracking.Add(result);
}
}
}
if (searchDeploymentProcess)
{
if(!project.IsVersionControlled)
{
// Get deployment process
var deploymentProcess = repositoryForSpace.DeploymentProcesses.Get(project.DeploymentProcessId);
// Loop through steps
foreach (var step in deploymentProcess.Steps)
{
// Loop through actions
foreach (var action in step.Actions)
{
// Loop through properties
foreach (var property in action.Properties.Keys)
{
if (action.Properties[property].Value != null && action.Properties[property].Value.ToLower().Contains(variableToFind.ToLower()))
{
VariableResult result = new VariableResult();
result.Project = project.Name;
result.MatchType = "Step";
result.Context = step.Name;
result.Property = property;
result.AdditionalContext = null;
result.Link = string.Format("{0}{1}/deployments/process/steps?actionid={2}", octopusURL, project.Links["Web"], action.Id);
if (!variableTracking.Contains(result))
{
variableTracking.Add(result);
}
}
}
}
}
}
else
{
Console.WriteLine(string.Format("{0} is version controlled, skipping searching the deployment process.", project.Name));
}
}
if (searchRunbookProcess)
{
// Get project runbooks
var runbooks = repositoryForSpace.Projects.GetAllRunbooks(project);
// Loop through runbooks
foreach (var runbook in runbooks)
{
// Get runbook process
var runbookProcess = repositoryForSpace.RunbookProcesses.Get(runbook.RunbookProcessId);
// Loop through steps
foreach (var step in runbookProcess.Steps)
{
foreach (var action in step.Actions)
{
foreach (var property in action.Properties.Keys)
{
if (action.Properties[property].Value != null && action.Properties[property].Value.ToLower().Contains(variableToFind.ToLower()))
{
VariableResult result = new VariableResult();
result.Project = project.Name;
result.MatchType = "Runbook Step";
result.Context = runbook.Name;
result.Property = property;
result.AdditionalContext = step.Name;
result.Link = string.Format("{0}{1}/operations/runbooks/{2}/process/{3}/steps?actionId={4}", octopusURL, project.Links["Web"], runbook.Id, runbookProcess.Id, action.Id);
if (!variableTracking.Contains(result))
{
variableTracking.Add(result);
}
}
}
}
}
}
}
}
Console.WriteLine(string.Format("Found {0} results", variableTracking.Count.ToString()));
if (variableTracking.Count > 0)
{
foreach (var result in variableTracking)
{
System.Collections.Generic.List<string> header = new System.Collections.Generic.List<string>();
System.Collections.Generic.List<string> row = new System.Collections.Generic.List<string>();
var isFirstRow = variableTracking.IndexOf(result) == 0;
var properties = result.GetType().GetProperties();
foreach (var property in properties)
{
Console.WriteLine(string.Format("{0}: {1}", property.Name, property.GetValue(result)));
if (isFirstRow)
{
header.Add(property.Name);
}
row.Add((property.GetValue(result) == null ? string.Empty : property.GetValue(result).ToString()));
}
if (!string.IsNullOrWhiteSpace(csvExportPath))
{
using (System.IO.StreamWriter csvFile = new System.IO.StreamWriter(csvExportPath, true))
{
if (isFirstRow)
{
// Write header
csvFile.WriteLine(string.Join(",", header.ToArray()));
}
// Write result
csvFile.WriteLine(string.Join(",", row.ToArray()));
}
}
}
}
Python3
import json
import requests
import csv
octopus_server_uri = 'https://your-octopus-url/api'
octopus_api_key = 'API-YOUR-KEY'
headers = {'X-Octopus-ApiKey': octopus_api_key}
def get_octopus_resource(uri):
response = requests.get(uri, headers=headers)
response.raise_for_status()
return json.loads(response.content.decode('utf-8'))
def get_by_name(uri, name):
resources = get_octopus_resource(uri)
return next((x for x in resources if x['Name'] == name), None)
# Specify the Space to search in
space_name = 'Default'
# Specify the Variable to find, without OctoStache syntax
# e.g. For #{MyProject.Variable} -> use MyProject.Variable
variable_name = 'MyProject.Variable'
# Search through Project's Deployment Processes?
search_deployment_processes = True
# Search through Project's Runbook Processes?
search_runbook_processes = True
# Optional: set a path to export to csv
csv_export_path = ''
variable_tracker = []
octopus_server_uri = octopus_server_uri.rstrip('/')
octopus_server_base_uri = octopus_server_uri.rstrip('api')
space = get_by_name('{0}/spaces/all'.format(octopus_server_uri), space_name)
print('Looking for usages of variable named \'{0}\' in space \'{1}\''.format(variable_name, space_name))
projects = get_octopus_resource('{0}/{1}/projects/all'.format(octopus_server_uri, space['Id']))
for project in projects:
project_name = project['Name']
project_web_uri = project['Links']['Web'].lstrip('/')
print('Checking project \'{0}\''.format(project_name))
project_variable_set = get_octopus_resource('{0}/{1}/variables/{2}'.format(octopus_server_uri, space['Id'], project['VariableSetId']))
# Check to see if variable is named in project variables.
matching_named_variables = [variable for variable in project_variable_set['Variables'] if variable_name in variable['Name']]
if matching_named_variables is not None:
for variable in matching_named_variables:
tracked_variable = {
'Project': project_name,
'MatchType': 'Named Project Variable',
'Context': variable['Name'],
'AdditionalContext': None,
'Property': None,
'Link': '{0}{1}/variables'.format(octopus_server_base_uri, project_web_uri)
}
if tracked_variable not in variable_tracker:
variable_tracker.append(tracked_variable)
# Check to see if variable is referenced in other project variable values.
matching_value_variables = [variable for variable in project_variable_set['Variables'] if variable['Value'] is not None and variable_name in variable['Value']]
if matching_value_variables is not None:
for variable in matching_value_variables:
tracked_variable = {
'Project': project_name,
'MatchType': 'Referenced Project Variable',
'Context': variable['Name'],
'AdditionalContext': variable['Value'],
'Property': None,
'Link': '{0}{1}/variables'.format(octopus_server_base_uri, project_web_uri)
}
if tracked_variable not in variable_tracker:
variable_tracker.append(tracked_variable)
# Search Deployment process if enabled
if search_deployment_processes == True:
deployment_process = get_octopus_resource('{0}/{1}/deploymentprocesses/{2}'.format(octopus_server_uri, space['Id'], project['DeploymentProcessId']))
for step in deployment_process['Steps']:
for step_key in step.keys():
step_property_value = str(step[step_key])
if step_property_value is not None and variable_name in step_property_value:
tracked_variable = {
'Project': project_name,
'MatchType': 'Step',
'Context': step['Name'],
'Property': step_key,
'AdditionalContext': None,
'Link': '{0}{1}/deployments/process/steps?actionId={2}'.format(octopus_server_base_uri, project_web_uri, step['Actions'][0]['Id'])
}
if tracked_variable not in variable_tracker:
variable_tracker.append(tracked_variable)
# Search Runbook processes if configured
if search_runbook_processes == True:
runbooks_resource = get_octopus_resource('{0}/{1}/projects/{2}/runbooks?skip=0&take=5000'.format(octopus_server_uri, space['Id'], project['Id']))
runbooks = runbooks_resource['Items']
for runbook in runbooks:
runbook_processes_link = runbook['Links']['RunbookProcesses']
runbook_process = get_octopus_resource('{0}/{1}'.format(octopus_server_base_uri, runbook_processes_link))
for step in runbook_process['Steps']:
for step_key in step.keys():
step_property_value = str(step[step_key])
if step_property_value is not None and variable_name in step_property_value:
tracked_variable = {
'Project': project_name,
'MatchType': 'Runbook Step',
'Context': runbook['Name'],
'Property': step_key,
'AdditionalContext': step['Name'],
'Link': '{0}{1}/operations/runbooks/{2}/process/{3}/steps?actionId={4}'.format(octopus_server_base_uri, project_web_uri, runbook['Id'], runbook['RunbookProcessId'], step['Actions'][0]['Id'])
}
if tracked_variable not in variable_tracker:
variable_tracker.append(tracked_variable)
results_count = len(variable_tracker)
if results_count > 0:
print('')
print('Found {0} results:'.format(results_count))
for tracked_variable in variable_tracker:
print('Project : {0}'.format(tracked_variable['Project']))
print('MatchType : {0}'.format(tracked_variable['MatchType']))
print('Context : {0}'.format(tracked_variable['Context']))
print('AdditionalContext : {0}'.format(tracked_variable['AdditionalContext']))
print('Property : {0}'.format(tracked_variable['Property']))
print('Link : {0}'.format(tracked_variable['Link']))
print('')
if csv_export_path:
with open(csv_export_path, mode='w') as csv_file:
fieldnames = ['Project', 'MatchType', 'Context', 'AdditionalContext', 'Property', 'Link']
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for tracked_variable in variable_tracker:
writer.writerow(tracked_variable)
Go
package main
import (
"bufio"
"fmt"
"log"
"net/url"
"os"
"reflect"
"strconv"
"strings"
"github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy"
)
type VariableResult struct {
Project string
MatchType string
Context string
Property string
AdditionalContext string
Link string
}
func main() {
apiURL, err := url.Parse("https://your-octopus-url")
if err != nil {
log.Println(err)
}
APIKey := "API-YOUR-KEY"
spaceName := "Default"
variableToFind := "MyProject.Variable"
searchDeploymentProcess := true
searchRunbookProcess := true
csvExportPath := "path:\\to\\variable.csv"
// Create client object
client := octopusAuth(apiURL, APIKey, "")
// Get space
space := GetSpace(apiURL, APIKey, spaceName)
client = octopusAuth(apiURL, APIKey, space.ID)
variableTracking := []VariableResult{}
// Get projects
projects, err := client.Projects.GetAll()
if err != nil {
log.Println(err)
}
// Loop through projects
for _, project := range projects {
fmt.Printf("Checking %[1]s \n", project.Name)
// Get variables
projectVariables, err := client.Variables.GetAll(project.ID)
if err != nil {
log.Println(err)
}
for _, variable := range projectVariables.Variables {
nameMatch := strings.Contains(variable.Name, variableToFind)
if err != nil {
log.Println(err)
}
if nameMatch {
result := VariableResult{}
result.Project = project.Name
result.MatchType = "Named Project Variable"
result.Context = variable.Name
result.Property = ""
result.AdditionalContext = variable.Value
result.Link = project.Links["Variables"]
if !arrayContains(variableTracking, result) {
variableTracking = append(variableTracking, result)
}
}
valueMatch := strings.Contains(variable.Value, variableToFind)
if err != nil {
log.Println(err)
}
if valueMatch {
result := VariableResult{}
result.Project = project.Name
result.MatchType = "Referenced Project Variable"
result.Context = variable.Name
result.Property = ""
result.AdditionalContext = variable.Value
result.Link = project.Links["Variables"]
if !arrayContains(variableTracking, result) {
variableTracking = append(variableTracking, result)
}
}
}
if searchDeploymentProcess {
if !project.IsVersionControlled {
// Get deployment process
deploymentProcess, err := client.DeploymentProcesses.GetByID(project.DeploymentProcessID)
if err != nil {
log.Println(err)
}
for _, step := range deploymentProcess.Steps {
for _, action := range step.Actions {
for property := range action.Properties {
if strings.Contains(action.Properties[property].Value, variableToFind) {
result := VariableResult{}
result.Project = project.Name
result.MatchType = "Step"
result.Context = step.Name
result.Property = property
result.AdditionalContext = ""
result.Link = apiURL.String() + project.Links["Web"] + "/deployments/process/steps?actionId=" + action.ID
if !arrayContains(variableTracking, result) {
variableTracking = append(variableTracking, result)
}
}
}
}
}
} else {
fmt.Printf("%[1]s is version controlled, skipping searching deployment process", project.Name)
}
}
if searchRunbookProcess {
// Get project runbooks
runbooks := GetRunbooks(client, project)
// Loop through runbooks
for _, runbook := range runbooks {
// Get runbook process
runbookProcess, err := client.RunbookProcesses.GetByID(runbook.RunbookProcessID)
if err != nil {
log.Println(err)
}
for _, step := range runbookProcess.Steps {
for _, action := range step.Actions {
for property := range action.Properties {
if strings.Contains(action.Properties[property].Value, variableToFind) {
result := VariableResult{}
result.Project = project.ID
result.MatchType = "Runbook Step"
result.Context = runbook.Name
result.Property = property
result.AdditionalContext = step.Name
result.Link = apiURL.String() + project.Links["Web"] + "/operations/runbooks/" + runbook.ID + "/process/" + runbook.RunbookProcessID + "/steps?actionId=" + action.ID
if !arrayContains(variableTracking, result) {
variableTracking = append(variableTracking, result)
}
}
}
}
}
}
}
}
if len(variableTracking) > 0 {
fmt.Printf("Found %[1]s results \n", strconv.Itoa(len(variableTracking)))
for i := 0; i < len(variableTracking); i++ {
row := []string{}
header := []string{}
isFirstRow := false
if i == 0 {
isFirstRow = true
}
e := reflect.ValueOf(&variableTracking[i]).Elem()
for j := 0; j < e.NumField(); j++ {
if isFirstRow {
header = append(header, e.Type().Field(j).Name)
}
row = append(row, e.Field(j).Interface().(string))
}
if csvExportPath != "" {
file, err := os.OpenFile(csvExportPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
log.Println(err)
}
dataWriter := bufio.NewWriter(file)
if isFirstRow {
dataWriter.WriteString(strings.Join(header, ",") + "\n")
}
dataWriter.WriteString(strings.Join(row, ",") + "\n")
dataWriter.Flush()
file.Close()
}
}
}
}
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 arrayContains(array []VariableResult, result VariableResult) bool {
for _, v := range array {
if v == result {
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
}
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
}
Help us continuously improve
Please let us know if you have any feedback about this page.
Page updated on Sunday, January 1, 2023