Octopus Deploy Powershell Scripts
Update variables in all releases
$headers = @{
'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY
}
# Update variables in all releases
$releases = Invoke-RestMethod -Uri "https://robota.octopus.app/api/releases?take=1000" -Headers $headers | Select-Object -ExpandProperty Items
foreach ($release in $releases) {
$message = "[$(1 +[Array]::IndexOf($releases, $release)) of $($releases.Count)] $($release.Id) - $($release.Version)"
try {
Invoke-RestMethod -Method Post -Uri "https://robota.octopus.app/api/releases/$($release.Id)/snapshot-variables" -Headers $headers | Out-Null
Write-Host $message -ForegroundColor Green
}
catch {
Write-Host $message -ForegroundColor Red
}
}Redeploy all IIS sites to dev1
$headers = @{
'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY
}
$target = "dev1"
$environments = Invoke-RestMethod "https://robota.octopus.app/api/environments" -Headers $headers | Select-Object -ExpandProperty Items
$environment = $environments | Where-Object Name -EQ $target
Write-Host "Environment for $target has id $($environment.Id)"
$projects = Invoke-RestMethod -Uri "https://robota.octopus.app/api/projects?skip=0&take=1000" -Headers $headers | Select-Object -ExpandProperty Items
$projects = $projects | Where-Object IsDisabled -EQ $false
Write-Host "Got $($projects.Count) projects"
# Optional: filter frontend projects
$projects = $projects | Where-Object Name -NotLike "alliance-*"
# Optional: filter only IIS projects
$iisProjects = @()
foreach ($project in $projects) {
$process = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers
$hasIisStep = $false
foreach ($step in $process.Steps) {
if (($step.Properties.'Octopus.Action.TargetRoles' -eq 'iis') -or ($step.Properties.'Octopus.Action.TargetRoles' -eq 'admin')) {
foreach ($action in $step.Actions) {
if ($action.IsDisabled -eq $false) {
$hasIisStep = $true
break
}
}
}
}
if ($hasIisStep -eq $true) {
$iisProjects += $project
}
}
Write-Host "Filtered $($iisProjects.Count) IIS projects"
$projects = $iisProjects
$releases = @()
foreach($project in $projects) {
$progression = Invoke-RestMethod "https://robota.octopus.app/api/progression/$($project.Id)" -Headers $headers
foreach($release in $progression.Releases) {
if ($release.Deployments.$($environment.Id)) {
$releases += $release
break
}
}
}
Write-Host "Got $($releases.Count) releases"
Write-Host "Going to update release variables for $($releases.Count) releases"
foreach($release in $releases) {
$projectName = $projects | Where-Object Id -EQ $release.Release.ProjectId | Select-Object -First 1 -ExpandProperty Name
try {
Invoke-RestMethod -Method Post -Uri "https://robota.octopus.app/api/releases/$($release.Release.Id)/snapshot-variables" -Headers $headers | Out-Null
Write-Host "$($projectName): $($release.Release.Version)" -ForegroundColor Green
}
catch {
Write-Host "$($projectName): $($release.Release.Version)" -ForegroundColor Red
}
}
$counter = 0
$errors = @()
Write-Host "Going to redeploy $($releases.Count) releases"
foreach($release in $releases) {
$projectName = $projects | Where-Object Id -EQ $release.Release.ProjectId | Select-Object -First 1 -ExpandProperty Name
try {
$deployment = Invoke-RestMethod -Method Post "https://robota.octopus.app/api/deployments" -Headers $headers -ContentType 'application/json' -Body (@{ ReleaseId = $release.Release.Id; EnvironmentId = $environment.Id } | ConvertTo-Json)
Write-Host "$($projectName): $($release.Release.Version)" -ForegroundColor Cyan
$task = Invoke-RestMethod "https://robota.octopus.app/api/tasks/$($deployment.TaskId)" -Headers $headers
while($task.IsCompleted -ne $true) {
$task = Invoke-RestMethod "https://robota.octopus.app/api/tasks/$($deployment.TaskId)" -Headers $headers
if ($task.State -eq "Success") {
Write-Host "$($task.State) $($task.Duration)" -ForegroundColor Green
break
} else {
Write-Host "$($task.State) $($task.Duration)"
}
Start-Sleep -Seconds 30
}
if ($task.FinishedSuccessfully -ne $true) {
Write-Host "$($projectName): $($release.Release.Version) - unsuccessfull deployment" -ForegroundColor Red
$errors += New-Object PSObject -Property @{
project = $projectName
version = $release.Release.Version
}
}
}
catch {
Write-Host "$($projectName): $($release.Release.Version) - unable to create deployment" -ForegroundColor Red
$errors += New-Object PSObject -Property @{
project = $projectName
version = $release.Release.Version
}
}
<#
$counter += 1
if ($counter -ge 3) {
break
}
#>
}
if ($errors.Count) {
Write-Host "Take a look at failed projects"
$errors | Out-Host
} else {
Write-Host "Everything is OK, go take your 🍺" -ForegroundColor Yellow
}PowerShell Octopus Check All Kubernetes Deployment Have Revision History Limit
$headers = @{
'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY
}
$projects = Invoke-RestMethod "https://robota.octopus.app/api/projects?skip=0&take=1000" -Headers $headers | Select-Object -ExpandProperty Items
foreach($project in $projects) {
$process = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers
foreach($step in $process.Steps) {
foreach($action in $step.Actions) {
if ($action.ActionType -ne 'Octopus.KubernetesDeployContainers') {
continue
}
$limit = $action.Properties.'Octopus.Action.KubernetesContainers.RevisionHistoryLimit'
if ($limit -ne '#{RevisionHistoryLimit}') {
Write-Host "project: $($project.Name), step: $($step.Name), url: https://robota.octopus.app/app#/Spaces-1/projects/$($project.Slug)/deployments/process"
}
}
}
}PowerShell Octopus set deployment setting in all projects
$headers = @{
'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY
}
$projects = Invoke-RestMethod "https://robota.octopus.app/api/projects?skip=0&take=1000" -Headers $headers | Select-Object -ExpandProperty Items
$projects = $projects | Sort-Object -Property Name
# $project = $projects |? Name -EQ 'adjwt'
foreach($project in $projects) {
$process = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers
$shouldUpdate = $false
foreach($step in $process.Steps) {
foreach($action in $step.Actions) {
if ($action.ActionType -ne 'Octopus.KubernetesDeployContainers') {
continue
}
if ($action.Properties.'Octopus.Action.KubernetesContainers.RevisionHistoryLimit' -ne '#{RevisionHistoryLimit}') {
if (-not $action.Properties.'Octopus.Action.KubernetesContainers.RevisionHistoryLimit') {
$action.Properties | Add-Member -NotePropertyName 'Octopus.Action.KubernetesContainers.RevisionHistoryLimit' -NotePropertyValue '#{RevisionHistoryLimit}'
} else {
$action.Properties.'Octopus.Action.KubernetesContainers.RevisionHistoryLimit' = '#{RevisionHistoryLimit}'
}
$shouldUpdate = $true
}
}
}
$status = 'unknown'
if ($shouldUpdate) {
try {
Invoke-RestMethod -Method Put "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers -Body ($process | ConvertTo-Json -Depth 100) | Out-Null
Write-Host "$($project.Name) - updated" -ForegroundColor Green
$status = 'updated'
} catch {
Write-Host "$($project.Name) - failed" -ForegroundColor Red
$status = 'failed'
}
} else {
Write-Host "$($project.Name) - skipped" -ForegroundColor Cyan
$status = 'skipped'
}
Write-Progress -Activity $project.Name -Status $status -PercentComplete ( [Array]::IndexOf($projects, $project) / $projects.Count * 100 )
}PowerShell Octopus Create and Deploy Release (sync dev environments with production)
<#
Problem: after releasing new version engineers are often forget to sync dev environments
Fact: deployment to production happens only after deployment to staging, so there is no need to sync them, only devX environments should be synced
Idea: after releasing to production, take its selected versions, create release for dev channel with same version, deploy
Inputs: project name to sync
Outputs: released deployments to all dev environments
Improvements for v2: ability to sync all projects
Links: https://github.com/OctopusDeploy/OctopusDeploy-Api/blob/master/REST/PowerShell/Deployments/CreateReleaseAndDeployment.ps1
#>
$name = 'adjwt'
$headers = @{
'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY
}
$projects = Invoke-RestMethod "https://robota.octopus.app/api/projects?skip=0&take=1000" -Headers $headers | Select-Object -ExpandProperty Items | Select-Object Id, Name
$project = $projects |? Name -EQ $name
$environments = Invoke-RestMethod "https://robota.octopus.app/api/environments" -Headers $headers | Select-Object -ExpandProperty Items | Select-Object Id, Name
$productionEnvironmentId = $environments | Where-Object Name -EQ "Production" | Select-Object -ExpandProperty Id
$idOfLastSuccessfullDeploymentToProduction = Invoke-RestMethod -Uri "https://robota.octopus.app/api/tasks?take=1&environment=$($productionEnvironmentId)&state=Success&name=Deploy&project=$($project.Id)" -Headers $headers | Select-Object -ExpandProperty Items | Select-Object -ExpandProperty Arguments -First 1 | Select-Object -ExpandProperty DeploymentId
$idOfRelease = Invoke-RestMethod -Uri "https://robota.octopus.app/api/deployments/$idOfLastSuccessfullDeploymentToProduction" -Headers $headers | Select-Object -ExpandProperty ReleaseId
$lastRelease = Invoke-RestMethod -Uri "https://robota.octopus.app/api/releases/$idOfRelease" -Headers $headers | Select-Object Version, SelectedPackages
$channels = Invoke-RestMethod "https://robota.octopus.app/api/projects/$($project.Id)/channels" -Headers $headers | Select-Object -ExpandProperty Items | Where-Object Id -NE $releaseOfLastSuccessfullDeploymentToProduction.ChannelId | Select-Object Id, Name
foreach($channel in $channels) {
$release = Invoke-RestMethod -Method Post -Uri "https://robota.octopus.app/api/releases" -Headers $headers -Body (@{
ChannelId = $channel.Id
ProjectId = $project.Id
Version = $lastRelease.Version + "-sync"
SelectedPackages = $lastRelease.SelectedPackages
} | ConvertTo-Json)
foreach($environment in $environments | Where-Object Name -NotIn @('Production', 'Staging')) {
$deployment = Invoke-RestMethod -Method Post -Uri "https://robota.octopus.app/api/deployments " -Headers $headers -Body (@{
ReleaseId = $release.Id
EnvironmentId = $environment.Id
} | ConvertTo-Json)
# optional
Write-Host "$($project.Name)@$($environment.Name): created" -ForegroundColor Cyan
do {
$task = Invoke-RestMethod -Uri "https://robota.octopus.app/api/tasks/$($deployment.TaskId)" -Headers $headers
if ($task.State -eq "Success") {
Write-Host "$($task.State) $($task.Duration)" -ForegroundColor Green
break
} else {
Write-Host "$($task.State) $($task.Duration)"
}
Start-Sleep -Seconds 30
} while($task.IsCompleted -ne $true)
if ($task.FinishedSuccessfully -ne $true) {
Write-Host "$($project.Name)@$($environment.Name): failed" -ForegroundColor Red
} else {
Write-Host "$($project.Name)@$($environment.Name): deployed" -ForegroundColor Green
}
}
}Mass fix for HPA tab issue
$headers = @{'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY}
$projects = Invoke-RestMethod -Uri "https://robota.octopus.app/api/projects?skip=0&take=1000" -Headers $headers | Select-Object -ExpandProperty Items
$projects = $projects | Where-Object IsDisabled -EQ $false
Write-Host "Got $($projects.Count) projects"
foreach($project in $projects) {
# $project = $projects | Where-Object Name -EQ 'ApplyApi'
Write-Host "Project: $($project.Name)"
$process = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers
$shouldUpdate = $false
foreach($step in $process.Steps) {
# $process.Steps | Select-Object name
# $step = $process.Steps[2]
Write-Host "Step: $($step.Name)"
if ($step.Properties.'Octopus.Action.TargetRoles' -ne 'kube-azure') {
Write-Host "skipping '$($step.Properties.'Octopus.Action.TargetRoles')' non kubernetes step..." -ForegroundColor Yellow
continue
}
foreach($action in $step.Actions) {
# $step.Actions | Select-Object name
# $action = $step.Actions[0]
Write-Host "Action: $($action.Name)"
if ($action.IsDisabled) {
Write-Host "skipping disabled action..." -ForegroundColor Yellow
continue
}
if ($action.ActionType -ne 'Octopus.KubernetesDeployContainers') {
Write-Host "skipping '$($action.ActionType)' non kubernetes action..." -ForegroundColor Yellow
continue
}
if (-not $action.Properties.'Octopus.Action.KubernetesContainers.CustomResourceYaml') {
Write-Host "skipping action without custom resource yaml..." -ForegroundColor Yellow
continue
}
if ($action.Properties.'Octopus.Action.KubernetesContainers.CustomResourceYaml' -notmatch 'HorizontalPodAutoscaler') {
Write-Host "skipping action without hpa in custom resource yaml..." -ForegroundColor Yellow
continue
}
if ($action.Properties.'Octopus.Action.KubernetesContainers.CustomResourceYaml' -notmatch "`t") {
Write-Host "skipping action tabs not found..." -ForegroundColor Yellow
continue
}
$action.Properties.'Octopus.Action.KubernetesContainers.CustomResourceYaml' = ($action.Properties.'Octopus.Action.KubernetesContainers.CustomResourceYaml' -replace "`t", ' ')
$shouldUpdate = $true
Write-Host "should be updated..." -ForegroundColor Yellow
}
}
if ($shouldUpdate) {
try {
Invoke-RestMethod -Method Put "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers -Body ($process | ConvertTo-Json -Depth 100) | Out-Null
Write-Host $project.Name -ForegroundColor Green
} catch {
Write-Host $project.Name -ForegroundColor Red
}
} else {
Write-Host $project.Name -ForegroundColor Cyan
}
}Project variables CRUD
$headers = @{'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY}
# Invoke-RestMethod -Uri "https://robota.octopus.app/api/environments" -Headers $headers | Select-Object -ExpandProperty Items | Select-Object Id, Name
# Environments-3 Production
# Environments-2 Staging
# Environments-1 dev
$projects = Invoke-RestMethod -Uri "https://robota.octopus.app/api/projects?skip=0&take=1000" -Headers $headers | Select-Object -ExpandProperty Items
$project = $projects | Where-Object Name -EQ 'adjwt'
# Add global variable
$variables = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.Variables)" -Headers $headers
$variables.Variables += [PSCustomObject]@{
Name = 'Demo'
Value = 'Hello from PowerShell'
IsEditable = $true
IsSensitive = $false
Type = 'String'
}
Invoke-RestMethod -Method Put -Uri "https://robota.octopus.app$($project.Links.Variables)" -Headers $headers -Body ($variables | ConvertTo-Json -Depth 100)
# Add environment variable
$variables = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.Variables)" -Headers $headers
$variables.Variables += [PSCustomObject]@{
Name = 'Demo'
Value = 'default'
IsEditable = $true
IsSensitive = $false
Type = 'String'
}
$variables.Variables += [PSCustomObject]@{
Name = 'Demo'
Value = 'test2'
IsEditable = $true
IsSensitive = $false
Type = 'String'
Scope = @{
Environment = @('Environments-2')
}
}
$variables.Variables += [PSCustomObject]@{
Name = 'Demo'
Value = 'prod'
IsEditable = $true
IsSensitive = $false
Type = 'String'
Scope = @{
Environment = @('Environments-3')
}
}
Invoke-RestMethod -Method Put -Uri "https://robota.octopus.app$($project.Links.Variables)" -Headers $headers -Body ($variables | ConvertTo-Json -Depth 100)
# Delete variable by its name
$variables = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.Variables)" -Headers $headers
$variables.Variables = $variables.Variables | Where-Object Name -NE 'Demo'
Invoke-RestMethod -Method Put -Uri "https://robota.octopus.app$($project.Links.Variables)" -Headers $headers -Body ($variables | ConvertTo-Json -Depth 100)Mass add TZ environment variable to all octopus projects, powershell
$headers = @{'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY }
$projects = Invoke-RestMethod -Uri "https://robota.octopus.app/api/projects/all" -Headers $headers
$projects = $projects | Where-Object IsDisabled -EQ $false
Write-Host "Got $($projects.Count) projects"
foreach ($project in $projects) {
<#
$project = $projects | Where-Object Name -EQ 'banner-api'
#>
Write-Host "Project: $($project.Name)"
$process = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers
$shouldUpdate = $false
foreach ($step in $process.Steps) {
<#
$process.Steps | Select-Object name
$step = $process.Steps[1]
#>
Write-Host "Step: $($step.Name)"
if ($step.Properties.'Octopus.Action.TargetRoles' -ne 'kube-azure') {
Write-Host "skipping '$($step.Properties.'Octopus.Action.TargetRoles')' non kubernetes step..." -ForegroundColor Yellow
continue
}
foreach ($action in $step.Actions) {
<#
$step.Actions | Select-Object name
$action = $step.Actions[0]
#>
Write-Host "Action: $($action.Name)"
if ($action.IsDisabled) {
Write-Host "skipping disabled action..." -ForegroundColor Yellow
continue
}
if ($action.ActionType -ne 'Octopus.KubernetesDeployContainers') {
Write-Host "skipping '$($action.ActionType)' non kubernetes action..." -ForegroundColor Yellow
continue
}
$containers = $action.Properties.'Octopus.Action.KubernetesContainers.Containers' | ConvertFrom-Json
foreach ($container in $containers) {
<#
$containers | Select-Object name
$container = $containers[0]
#>
if (-not ($container.EnvironmentVariables | Where-Object key -EQ 'TZ' | Where-Object value -In @('Europe/Kiev', 'UTC'))) {
Write-Host "Container: $($container.Name) - has no TZ env variable - should be updated..." -ForegroundColor Yellow
$container.EnvironmentVariables += [PSCustomObject]@{
key = 'TZ'
value = 'Europe/Kiev' # TODO: chose 'Europe/Kiev' or 'UTC'
}
$shouldUpdate = $true
}
}
# WTF powershell?! Powershell always messes up with single item arrays! Keep that in mind
$action.Properties.'Octopus.Action.KubernetesContainers.Containers' = ConvertTo-Json -Depth 100 -InputObject @($containers)
}
}
if ($shouldUpdate) {
try {
# # Uncomment me when you ready
# Invoke-RestMethod -Method Put "https://robota.octopus.app$($project.Links.DeploymentProcess)" -ContentType "application/json" -Headers $headers -Body ($process | ConvertTo-Json -Depth 100) | Out-Null
Write-Host $project.Name -ForegroundColor Green
}
catch {
Write-Host $project.Name -ForegroundColor Red
}
}
else {
Write-Host $project.Name -ForegroundColor Cyan
}
}Mass owners sync from Octopus to Kubernetes
$octopusHeaders = @{ "X-Octopus-ApiKey" = $env:OCTOPUS_TOKEN; "Content-Type" = "application/json" }
$items = kubectl get "deployment,statefulset,cronjob" -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
<#
$item = $items | Where-Object { $_.metadata.name -eq 'candidates-auto-screening-answer-stats-cronjob' }
#>
foreach ($item in $items) {
$projectId = $item.metadata.labels.'Octopus.Project.Id'
if ([string]::IsNullOrEmpty($projectId)) {
#Write-Host "$($project.Name) | Octopus.Project.Id label not found for deployment" -ForegroundColor Red
continue
}
if (-not $item.metadata.annotations.owner) {
#Write-Host "$($project.Name) | Owner annotation not found for deployment" -ForegroundColor Red
continue
}
$variables = Invoke-RestMethod -Uri "https://robota.octopus.app/api/Spaces-1/projects/$($projectId)/variables" -Headers $octopusHeaders
$octo_owner = $variables.Variables | Where-Object Name -eq "Owner" | Select-Object -ExpandProperty Value -First 1
if (-not $octo_owner) {
#Write-Host "$($project.Name) | Owner repository variable not found for project" -ForegroundColor Red
continue
}
$octo_owner_normalized = $octo_owner.Trim().ToLower()
if ($item.metadata.annotations.owner.Trim().ToLower() -eq $octo_owner_normalized) {
#Write-Host "$($item.metadata.name) | Owner is up to date" -ForegroundColor Green
continue
}
if ($item.kind -eq "CronJob") {
$patch = ConvertTo-Json -Compress -Depth 100 -InputObject @{
metadata = @{
annotations = @{
owner = $octo_owner_normalized
}
}
spec = @{
jobTemplate = @{
metadata = @{
annotations = @{
owner = $octo_owner_normalized
}
}
spec = @{
template = @{
metadata = @{
annotations = @{
owner = $octo_owner_normalized
}
}
}
}
}
}
}
}
else {
$patch = ConvertTo-Json -Compress -Depth 100 -InputObject @{
metadata = @{
annotations = @{
owner = $octo_owner_normalized
}
}
spec = @{
template = @{
metadata = @{
annotations = @{
owner = $octo_owner_normalized
}
}
}
}
}
}
Write-Host "kubectl patch $($item.kind.ToLower()) $($item.metadata.name) -p '$patch'"
if ($item.kind -ne "cronjob") {
Write-Host "kubectl rollout status $($item.kind.ToLower()) $($item.metadata.name) --timeout=60s"
}
}Octopus Powershell List Variable Usage by Projects
find all projects that use some variable
$headers = @{'X-Octopus-ApiKey' = $env:OCTOPUS_CLI_API_KEY }
$projects = Invoke-RestMethod -Uri "https://robota.octopus.app/api/projects/all" -Headers $headers
$projects = $projects | Where-Object IsDisabled -EQ $false
$variables = @()
foreach ($project in $projects) {
<#
$project = $projects | Where-Object Name -EQ 'RbacApi'
#>
$process = Invoke-RestMethod -Uri "https://robota.octopus.app$($project.Links.DeploymentProcess)" -Headers $headers
foreach ($step in $process.Steps) {
<#
$process.Steps | Select-Object name
$step = $process.Steps[1]
#>
foreach ($action in $step.Actions) {
<#
$step.Actions | Select-Object name
$action = $step.Actions[0]
#>
if ($action.IsDisabled) { continue }
$json = $action | ConvertTo-Json -Depth 100 # trick: instead of iteratin over all possible step kinds, serialize it as json and search for #{SomeVariable}
foreach ($variable in [regex]::Matches($json, '#\{[^\}]+\}')) {
$variables += [PSCustomObject]@{
project = $project.Name
step = $step.Name
variable = $variable.Value
}
}
}
}
}
$variables = $variables | Select-Object project, step, variable -Unique
$variables