Re-Run Package Deployment Step Multiple Times

Hi Team,

How To Re-Run Package Deployment Step Multiple Times With Different Package Versions recursively. Example :

First Time / Release : 1 . Step A - Package Deployment
a. x.x.x package Deployed.
Second Time / Release : 2 . Step A - Package Deployment
b. y.y.y package Deployed.

Note : I was able to write power shell script where I have used package version as a parameters in array ( like $pkgversion = “x.x.x,y.y.y” ) and then in for each loop to get details from octopus in-built package repo and using octo.exe to create-release and select package version against package id which serves /r solved my problem or what i supposed to want. But there were scenarios where octo.exe does not behave correctly. So i do not know it is recommend to use octo.exe for some tedious tasks or shall i use octopus.*.dll from installation directory to achieve certain tasks through scripting or same can be possible through in-built octopus deploy. I did not see that yet any suggestion or trick or tips would be very help full.

Thanks
Vivek

Hi @vivek.singh,

Welcome to the Octopus Boards!

I personally prefer using Rest API or the Octopus.Client.dll for tricky stuff. We have a repository with a bunch of examples that may be helpful.

To help you better with a solution, can I please get more detail on what you are attempting to accomplish with the above logic and why this is necessary in your use case? We may have something built in that could handle what you’re trying to do in a more elegant way.

Thanks,
Jeremy

Hi @jeremy.miller

Case Explanation :

I was trying to deploy a Package-Id "ABCD" which has multiple version and i want to deploy only selective one on target.

In order to do it, i know i can go ahead to create release two times and select each individual version for Package-Id "ABCD".

Then i thought , how i can do it through automation using octo.exe and PowerShell , then i wrote the script which do it's job by creating release one by one after each previous successfully deployment.

Problem Statement :

In octopus project variable i have mentioned a variable name called Clientelist and i am passing variable values like "ABCD|EFGH|IJKL" which are the client names where each one has it's own individual database and above deplyoed scripts will run one by one.

My concern is that in PowerShell script while selecting clientname from Clientelist, sometime I am getting the client name exactly as I have put them in octopus project variable and sometimes I really get the all the clients names from the directory instead which i have mentioned in octopus project variable.

Octo.exe and powershell snippet.

#Canary ClientList
$ClientList = “ABCD”,“EFGH”,“IJKL”

#Global Variable
$Drive = “C:”
$Package = “ABCD”
$ProjectName = “Database Upgrade”
$EnvironmentType = “CLIENTSTABLE”
$PackageIDVersions = “x.x.x”, “y.y.y”
$OctopusServerURL = “OctopusURL”
$OctopusServerApiKey = “API-xxxxxxxxxxxxxxxxxx”
$PackageIDVersions = $PackageIDVersions.Split(",")
$SourceOctopusServerPath = “$Drive\Octopus\Packages\Spaces-1\feeds-builtin”

#Go To Octo.exe Location
Set-Location -Path “$Drive:\Octopus”

foreach ($PackageIDVersion in $PackageIDVersions) {

#Base Package Information
$PackageFileName = (Get-ChildItem -Path "Microsoft.PowerShell.Core\FileSystem::$SourceOctopusServerPath\$Package" -Filter "*.$PackageIDVersion.*" | Sort-Object LastAccessTime -Descending | Select-Object -First 1).Name
$PackageFileVersion = [IO.Path]::GetFileNameWithoutExtension($PackageFileName).Substring("19")
$ReleaseNotes = "Deploy Base Database Package $PackageFileName On $EnvironmentType"

#Console Print
Write-Host "$ReleaseNotes" -ForegroundColor Green

cmd.exe /c .\Octo.exe create-release --project="$ProjectName" `
    --package="Deploy PGO Database:$PackageFileVersion" `
    --deployto="$EnvironmentType" --releasenotes="$ReleaseNotes" `
    --progress --waitfordeployment `
    --server="$OctopusServerURL" --apikey "$OctopusServerApiKey" }

$ExecuteDatabaseReleaseNotes = “Upgrade $ClientList Database With Delta Script Of Base x.x.x & y.y.y”
cmd.exe /c .\Octo.exe create-release --project="$ProjectName" --deployto="$EnvironmentType" --releasenotes="$ExecuteDatabaseReleaseNotes"
–progress --waitfordeployment `
–server="$OctopusServerURL" --apikey “$OctopusServerApiKey”

Octopus Project Steps : Get The the Client List Using PowerShell

I have folder name as websites under which following are the sub directories "CLIENTSTABLEABCDPREVIEW","CLIENTSTABLEFGHPREVIEW","CLIENTSTABLEIJKLPREVIEW",CLIENTSTABLEMNOPPREVIEW.

I am using following using PowerShell snippet to get the list of client and desired output should be only three folder instead of four. Sometimes it works and sometime it does not. Just wanted to tell you that all are getting passed through prompt variables.

$EnvironmentNameList = ((Get-ChildItem -Path "Microsoft.PowerShell.Core\FileSystem::\\$AppServer1HostName\" -Exclude "XYZ" | Where-Object { ($_.Name.ToUpper().EndsWith('PREVIEW')) -and ($_.Name.ToUpper() -match "$ClientList")) }).Name

Octopus Project Steps : Database Script Execution Steps

Part-A : Example :

  1. Deployed Package Version : x.x.x of Package-Id : “ABCD” on target database server under “C:\DatabaseScripts\x.x.x”.

  2. Using PowerShell get the names of client folder directory names and get the database information from application file.

  3. Using PowerShell “Set-Location” command go to C:\DatabaseScripts\x.x.x".

  4. Execute Database.cmd against database server & database name which is input from Point #3.

Part-B : Example :

  1. Deployed Package Version : y.y.y of Package-Id : “ABCD” on target database server under “C:\DatabaseScripts\y.y.y”.

  2. Using PowerShell get the names of client folder directory names and get the database information from application file.

  3. Using PowerShell “Set-Location” command go to C:\DatabaseScripts\y.y.y".

  4. Execute Database.cmd against database server & database name which is input from Point #3.

Note : I have tried Octopus.Client.dll using REST API and it seems to me not broking now. I guess there is some issue in octo.exe while handling some tedious tasks. But still i request to you to provide your thoughts or any alternative solution of this problem.

Hi @vivek.singh,

I had a look at this code:
$EnvironmentNameList = ((Get-ChildItem -Path "Microsoft.PowerShell.Core\FileSystem::\\$AppServer1HostName\" -Exclude "XYZ" | Where-Object { ($_.Name.ToUpper().EndsWith('PREVIEW')) -and ($_.Name.ToUpper() -match "$ClientList")) }).Name

With your 4 example folders, it looks to me that all 4 should be grabbed, not just 3. All of them have PREVIEW at the end and none of them have XYZ.

Looking at your use case as a whole, it looks like you may be able to utilize Tenants to improve your situation.

Here is a video with one of our architects giving a tutorial on tenants:

Here are our docs on tenants:

Please let me know what you think.

Thanks,
Jeremy

Hi @jeremy.miller,

Thanks for response. Yours initial solution was very help full and I am going to try that one with REST API along with multitenant one. However, REST API is looks more promising to me and I will surely post the updated for that. Multitenant option is good but certain scenario does not fit like we have one now , where we are having like hosting organization to host multiple clients and upgrade their database and website code on required days. This also involves certain clients to upgraded and certain not. In that case multitenant is going to cost more time to manage each induvial client’s directory and that is the reason I prefer to have core PowerShell scripting along with octopus deployment feature and certain tedious tasks with help of octo.exe ( OLD method for me) and now i will use or go or suggest for REST API instead ( Started Following).

Just to update that I was going through the script and I did ran it again with canary list clients and have the exact output in way I wanted to have it.

Directory Lists

Directory-Lists

PowerShell Snippet

#Canary ClientList
$ClientList = “ABCD|EFGH|IJKL”
#Get EnvironmentName From Folder ClientList
$EnvironmentNameList = ((Get-ChildItem -Path “C:\Support\WebSites” -Exclude “XYZ” `
| Where-Object { ($.Name.ToUpper().EndsWith(‘PREVIEW’)) -and ($.Name.ToUpper() -match ("$ClientList")) }).Name)
Write-Host $EnvironmentNameList -ForegroundColor Green

Output

Note : I would update the thread to with latest code using REST API structure and eventually would need any help if required.

Thanks for your great suggestion and help !!!

Thanks
Vivek

1 Like

Hi @vivek.singh,

Thank you for the update. You’re very welcome. I look forward to hearing about the results after you’ve implemented it.

Thanks,
Jeremy

Hi @jeremy.miller.

Thanks for the recommendation of REST API. I was implementing some logic using REST and I am very close to achieve what I wanted to have.

The only challenges now I see is that , How do I enable "PROGRESS" & "WAITFORDEPLOYMENT" in code. I know it is possible and I have already implemented using as a switch with octo.exe which I have already mentioned in previous thread. Please help me with these two stuffs if possible with below respective code.

BasePackageIDVersions

$BasePackageIDVersions = “2.12,2.13”

Octopus Server URL

$OctopusServerURL = " "

Octopus Server ApiKey

$OctopusServerApiKey = " "

Octopus Space Name

$SpaceName = “Default”

Header Process

$Header = @{ “X-Octopus-ApiKey” = $OctopusServerApiKey }

Get Default Space Name & It’s ID

$SpaceList = (Invoke-WebRequest “$OctopusServerURL/api/spaces?Name=$SpaceName” - Headers $Header -UseBasicParsing).content | ConvertFrom-Json
$SpaceId = @($SpaceList.Items | Where-Object { ($_.Name -eq “$SpaceName”) })[0].id
Write-Host "The SpaceId For Space Name $SpaceName Is : "$SpaceId -ForegroundColor Green

Get Project By Name

$Projects = (Invoke-WebRequest -Uri “$OctopusServerURL/api/projects/all” -Headers $Header -UseBasicParsing).content | ConvertFrom-Json
$ProjectToExecute = $Projects | Where-Object { ($_.Name -eq $ProjectName) }
Write-Host "Project To Execute : "$ProjectToExecute.Name -ForegroundColor Green

Get EnvironmentType By Name

$EnvironmentTypes = (Invoke-WebRequest -Uri “$OctopusServerURL/api/Environments/all” -Headers $Header -UseBasicParsing).content | ConvertFrom-Json
$EnvironmentType = $EnvironmentTypes | Where-Object { ($_.Name -eq $EnvironmentType) >}
Write-Host "Project To Execute On EnvironmentType : "$EnvironmentType.Name -ForegroundColor Green

foreach ($PackageIDVersion in $($BasePackageIDVersions.Split(","))) {

# Getting Deployment Template to get Next version 
$DeploymentTemplate = Invoke-WebRequest -Uri "$OctopusServerURL/api/deploymentprocesses/deploymentprocess-$($ProjectToExecute.id)/template" -Headers $Header | ConvertFrom-Json 

Write-Host "Getting Step Package Version"
$DeploymentTemplate.Packages | ForEach-Object {
    # For Highest Or Latest Version Of Same Package Id
    # $URI = "$OctopusServerURL/api/feeds/$($_.FeedId)/packages/versions?packageId=$($_.PackageId)&includeMultipleVersions=true&take=1"

    # For List Of Multiple Version Of Same Package Id
    $URI = "$OctopusServerURL/api/feeds/$($_.FeedId)/packages/versions?packageId=$($_.PackageId)&includeMultipleVersions=true&includePreRelease=true&take=100000"
    $PacakageIDVersionList = (Invoke-WebRequest -Uri $URI -Method GET -Headers $Header -UseBasicParsing).content | ConvertFrom-Json
    $PackageIDVersion = ($PacakageIDVersionList.Items | Where-Object { ($_.ID -like "*.$PackageIDVersion.*") } | Sort-Object LastAccessTime -Descending | Select-Object -First 1).Version
}

# Creating Release
$ReleaseBody = @{ 
    Projectid        = $ProjectToExecute.Id
    version          = $DeploymentTemplate.nextversionincrement 
    ReleaseNotes     = "Deploy Base Database Package $BasePackageId With Version $PackageIDVersion On $($EnvironmentType.Name)"
    SelectedPackages = @(
        @{
            StepName = "Deploy Database"
            Version  = "$PackageIDVersion"
        }
    )
} | ConvertTo-Json
try {
    Write-Host "Creating Release For $BasePackageId With Version $PackageIDVersion" -ForegroundColor Green
    $Release = (Invoke-WebRequest -Uri "$OctopusServerURL/api/releases" -Method Post -Headers $Header -Body $ReleaseBody -ErrorVariable octoError -UseBasicParsing).content | ConvertFrom-Json
    Write-Host "Creating Release For $BasePackageId With Version $PackageIDVersion Is Created" -ForegroundColor Green
}
catch {
    $Result = $_.Exception.Response.GetResponseStream()
    $Reader = New-Object System.IO.StreamReader($Result)
    $ResponseBody = $Reader.ReadToEnd();
    $Response = $ResponseBody | ConvertFrom-Json
    $Response.Errors
    exit
}

#Creating deployment
$DeploymentBody = @{ 
    ReleaseID     = $Release.Id
    EnvironmentID = $EnvironmentType.Id
    SkipActions   = @("Pre-Deployment")
    Progress      = $true
    WaitForDeployment = $true
} | ConvertTo-Json
try {
    Write-Host "Deploying Release For $BasePackageId With Version $PackageIDVersion" -ForegroundColor Green
    $Deployment = (Invoke-WebRequest -Uri "$OctopusServerURL/api/deployments" -Method Post -Headers $Header -Body $DeploymentBody -ErrorVariable octoError -UseBasicParsing).content | ConvertFrom-Json
    $TaskIdStatus = (Invoke-WebRequest -Uri "$OctopusServerURL/api/projects/$($ProjectToExecute.id)/releases/$($Release.id)/deployments/$($Deployment.Id)" -Method Post -Headers $Header -Body $DeploymentBody -UseBasicParsing).content | ConvertFrom-Json
    Write-Host "Deploying Release For $BasePackageId With Version $PackageIDVersion Is Completed" -ForegroundColor Green
}
catch {
    $Result = $_.Exception.Response.GetResponseStream()
    $Reader = New-Object System.IO.StreamReader($Result)
    $ResponseBody = $Reader.ReadToEnd();
    $Response = $ResponseBody | ConvertFrom-Json
    $Response.Errors
    exit
} }

Thanks
Vivek

Hi Vivek,

Do you mean that you want to wait on the deployment until it’s finished checking its status?

If so I think I may have found an example that implements this logic. The logic I’m looking at starts on line 65 and ends on 77.

Let me know if this works for you or if you meant something else.

Thanks,
Jeremy

Hi @jeremy.miller,

Thanks for the response and help. Yes, That worked for me while creating release for package version 2.12 and it's deployment. Before you go to latest modified snippet, I have another challenge and it is related to How to skip particular step during deployment in second iteration in for each loop?

It is bit tricky approach or requirement. If you see in snippet I have one step name as @("Pre-Deployment") as skipped in Deployment Body variable. But I wanted to skip another step name as @("Post-Deployment") along with @("Pre-Deployment") in second iteration in for each loop while package version will be 2.13 as per previous post where variable name is $BasePackageIDVersions = "2.12,2.13"

Any Suggestion Or Trick will be very helpful. This steps we can do it through manual process by just selecting specfic steps during our second release for different package version like in our case it is going to be "2.13".

Note : I have updated only try & catch block with latest wait of deployment logic where as the rest of the code will remain same as previous post.

#Creating deployment
$DeploymentBody = @{
ReleaseID = $Release.Id
EnvironmentID = $EnvironmentType.Id
SkipActions = @(“Pre-Deployment”) — 1st Iteration In Loop - Successful.
SkipActions = @(“Pre-Deployment”,“Post-Deployment”) — Needed in 2nt Iteration In Loop.
Progress = $true
WaitForDeployment = $true
} | ConvertTo-Json
try {
Write-Host “Deploying Release For $BasePackageId With Version $PackageIDVersion” -ForegroundColor Green
$Deployment = (Invoke-WebRequest -Uri “$OctopusServerURL/api/deployments” -Method Post -Headers $Header -Body $DeploymentBody -ErrorVariable octoError -UseBasicParsing).content | ConvertFrom-Json
$TaskId = $Deployment.TaskId
$DeploymentIsActive = $true

    do {
        $DeploymentStatus = (Invoke-WebRequest -Uri "$OctopusServerURL/api/tasks/$TaskId/details?verbose=false" -Method GET -Headers $Header -UseBasicParsing).content | ConvertFrom-Json
        $DeploymentStatusState = $DeploymentStatus.Task.State

        if ($DeploymentStatusState -eq "Success" -or $DeploymentStatusState -eq "Failed") {
            $DeploymentIsActive = $false
        }
        else {
            Write-Host "Deployment Is Still Active...Checking Again In 5 Seconds"
            Start-Sleep -Seconds 5
        }
    } While ($DeploymentIsActive)
    
    Write-Host "Deploying Release For $BasePackageId With Version $PackageIDVersion Is Completed" -ForegroundColor Green
}
catch {
    $Result = $_.Exception.Response.GetResponseStream()
    $Reader = New-Object System.IO.StreamReader($Result)
    $ResponseBody = $Reader.ReadToEnd();
    $Response = $ResponseBody | ConvertFrom-Json
    $Response.Errors
    exit
}