How can I use a variable generated from a STEP in one Environment and use in next environment under the same phase in Lifecycle

Hi guys,

I am busy automating a SNOW change automation process from Octopus Pipeline.

I have a Lifecycle with 1 Phase under which there are 2 environments(i.e SA and UK). I have a STEP on Octopus that creates a SNOW change and Outputs the Change Number when it deploys to the SA environment. I want to use that Change Number when I deploy to the UK environment.

I have already tried to update the variable set for the deployment/release but it doesn’t allow me to update a variable set for the release as Octopus doesn’t allow that based on the below error message.

- You cannot modify this variable set because the set is frozen. You’ll need to create a new variable set instead

Any help or suggestion is appreciated. Thanks

Hello Rohit,

I have a couple of questions to make sure I’m following.

My understanding is that you’re using a deployment process to target an SA environment, then a UK environment. In the SA environment, you’re using the Service Now API to create a change, and trying saving that change number to a variable in your Octopus project?

If that’s correct, can you tell me how you’re saving the variable? There’s a native way to do this with output variables using this library step, but I wanted to know more about what you are currently trying/have tried as well.

Hello Cory,

Thanks for your response.

So I have been trying to find a way about how I can share the ChangeNumber across the environment within the same phase in the Lifecycle. The only solution I could think about was setting a variable for the specific release. So I tried to update the variable snapshot for that particular deployment. To update the variable I tried using the API as well as the Octopus Client Library. Below is the code snippet.

$variableSets = $repository.VariableSets.Get(“variableset-$($OctopusParameters[“Octopus.Deployment.Id”])”)
$VariableName = “ChangeNumber”
$variableToUpdate = $variableSets.Variables | Where-Object {$_.Name -eq $VariableName}
$variableToUpdate.Value =“5241”
$repository.VariableSets.Modify($variableSets)

While using the above code to update the variable I got the error mentioned in my question.

Does the mentioned Library Step allow to update or return the output variable to be used in the same release?

Sorry Rohit, I totally overlooked the lifecycle/same release when I was suggesting that library step yesterday!

You’re correct that updating the value like I described doesn’t update the snapshot for your release, so subsequent deployments of that release (to your UK environment, in this specific case) wouldn’t have access to the correct value.

Is this deployment process isolated? As in, are these the only running steps, or are there more things happening surrounding this specific change number behavior in your process? I’m discussing additional solutions and options with my teammates, and I’m hoping to have some more information for you soon.

Hi Cory,

Thanks for claryfing it.

So this is only one of the deployments steps. The deployment has multiple steps including the deployment of artifacts to the boxes, closing the change number and some other steps.

I would really appreciate if you can suggest me a way to achieve this otherwise I may have to rethink the strategy to manage our multiple environments within the lifecycle.

Looking forward to hearing from you. Thanks.

Hello Rohit,

After discussing with the team, There is a way to use output variables here that would be beneficial for your use case. This involves setting your change number as a variable, then using the Octopus API to retrieve it later.

For example, you can run a script and use the following to set your change number as an output variable:

Step 1: Set Output Variable

Set-OctopusVariable -name "ChangeNumber" -value $someChangeNumber

Then, you can use the following API example to retrieve that value using the step name and some additional available data:

Step 2: Retrieve Output Variable

# Create an API key using a sensitive variable
$octopusAPIKey = "YourAPIKey"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }

$octopusURL = $OctopusParameters["Octopus.Web.ServerUri"]
$spaceId = $OctopusParameters["Octopus.Space.Id"]
$releaseId = $OctopusParameters["Octopus.Release.Id"]
$projectId = $OctopusParameters["Octopus.Project.Id"]

# Get last 100 deployments for the release
$deployments = Invoke-RestMethod -Uri "$octopusURL/api/$($spaceId)/releases/$($releaseId)/deployments?skip=0&take=100" -Headers $header 

#Select most recently created
$deployment = $deployments.Items | Sort-Object {$_.Created} | Select-Object -First 1
Write-Verbose "Deployment Id: $($deployment.Id)"

# Get variables
$deploymentVariablesUrl="$octopusURL$($deployment.Links.Variables)"
$deploymentVariables = Invoke-RestMethod -Uri "$deploymentVariablesUrl" -Headers $header 

# Select matching output variable
$changeNumber = $deploymentVariables.Variables | Where-Object {$_.Name -eq "Octopus.Action[Set Output Variable].Output.ChangeNumber"} | Select-Object -First 1
if($null -eq $changeNumber) {
	Write-Host "Couldn't find existing change number"
}
else {
	Write-Host "Found existing change number, value=$($changeNumber.Value)"
}

In the example above, the step where the change number output variable was set was called Set Output Variable, which you can see referenced in the variable matching script (Where-Object {$_.Name -eq "Octopus.Action[Set Output Variable].Output.ChangeNumber"})

The benefit of this approach is that the value is available in the following deployment environment - I did local testing with this script, and on first run, it outputs Couldn't find existing change number (since it hasn’t saved yet), but it correctly pulls up on subsequent deployments to other environments.

The biggest change here is that ChangeNumber isn’t a project variable, it’s specifically an output variable that your process is using. If you need to be able to keep a project variable for this value, this may not be the best way to handle things.

There’s another option to handle this using deployment artifacts - if you’re interested in discussing more or the output variables above don’t quite meet your needs, let me know and we can walk through how it would work using artifacts. It’s reasonably similar, just a little different on how it’s handled and the necessary API calls to retrieve it!

Let me know if you have any questions or need any additional help with this, hope this gets you moving in the right direction.

Hello Cory,

Thank you so much for your response.

The above-given script has worked for me.

We are actually setting up an Output variable when we create a SNOW change and set a change number. I was not sure if there is a way to access the Output variable from one environment to another. But this has worked for me.

I have implemented a step with the new script for another environment and I can access the output variable from there. This is really helpful and good to know this.

Thanks again for your help and support. Really appreciate that.

Kind regards,
Rohit

Glad this got it solved for you Rohit!

Let me know if you have any other questions or need a hand with anything else.

Sincerely,
Cory

Hello Cory,

Just last one question regarding accessing the Output variable value. Does the previous deployment has to be completed before we start the new deployment to another environment.

So In our scenario, my deployment to SA has to wait until the given timeslot. I am using a python script to pause the deployment and wait for the given time slot, which means the first step creates a SNOW ticket and set the output variable, and wait for the timeslot to continue the deployment. Meanwhile, If I go and run the deployment for our UK environment, the script is not able to find the output variable.

I am still investigating my script and trying to resolve but if you got any idea regarding this then please let me know.

Thanks and regards,
Rohit

Hi Cory,

So I tried my scenario with the sample application doing similar stuff and found out that the deployments needs to be completed for the first environment in order to access the Output variable in the second environment.

Hello Rohit,

I tested this a bit earlier and got the same results you described. This solution does work with manual interventions, but not with scripts with defined wait times. I believe this is related to how manual interventions consider tasks “completed” (awaiting further input), where a scripted wait is still part of the deployment process that’s in progress.

I have another idea utilizing the deployment artifacts like I mentioned above, but I need to complete some more testing and refining. Hoping to have a sample script for you a little later today that shows that method.

Rohit,

Here’s a possible solution using deployment artifacts that may help you here.

Similar to the example above, consider the following:

  1. Your initial deployment to your UK environment will generate an artifact (e.g. Releases-123-ChangeNumber.txt
  2. In your subsequent deployment to your SA environment, you’ll be able to use the API to retrieve the artifacts

Step 1: Create Octopus Artifact

For this example, this step is specifically scoped to the first environment to ensure that there’s only one change number created/retrieved per release.

$ReleaseId = $OctopusParameters["Octopus.Release.Id"]

# Set your ServiceNow Change Number
$someChangeNumber = "12345"

Write-Host "Change Number value is $someChangeNumber, creating Octopus Artifact"

# Create an identifiable text file to store the variable and set the content to the change number
$FileName = "$($ReleaseId)-ChangeNumber.txt"
New-Item ./$FileName

Set-Content ./$FileName $someChangeNumber

Get-ChildItem ./$FileName | New-OctopusArtifact

Step 2 - Retrieve Artifact Content

# Create an API key using a sensitive variable
$octopusAPIKey = "YourAPIKey"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }

$octopusURL = $OctopusParameters["Octopus.Web.ServerUri"]
$SpaceId = $OctopusParameters["Octopus.Space.Id"]
$ReleaseId = $OctopusParameters["Octopus.Release.Id"]
$FileName = "$($ReleaseId)-ChangeNumber.txt"

Write-Host "Octopus Url: $OctopusUrl"
Write-Host "SpaceId: $SpaceId"
Write-Host "ReleaseId: $ReleaseId"
Write-Host "Filename to retrieve: $FileName"

# Get last 100 deployments for the release
$DeploymentList = Invoke-RestMethod -Uri "$OctopusURL/api/$SpaceId/releases/$ReleaseId/deployments?skip=0&take=100" -Headers $header 
# Select oldest deployment (first environment)
$Deployment = $DeploymentList.Items | Sort-Object {$_.Created} | Select-Object -First 1
$DeploymentId = $Deployment.Id


$artifactUrl = "$OctopusURL/api/$SpaceId/artifacts?take=2147483647&regarding=$DeploymentId&order=desc"
Write-Host "Getting the artifacts from $artifactUrl"
$artifactResponse = Invoke-RestMethod $artifactUrl -Headers $header

$changeNumberArtifact = $artifactResponse.Items | Where-Object {$_.Filename -eq $FileName} | Select-Object -First 1

	
$artifactId = $changeNumberArtifact.Id
$artifactContentUrl = "$OctopusUrl/api/$SpaceId/artifacts/$artifactId/content"
Write-Host "Pulling the content from $artifactContentUrl"
$fileContent += Invoke-RestMethod $artifactContentUrl -Headers $header

Write-Host "File Content (Change Number): $fileContent"

I tested this locally with a step running after that had a wait timer, and it worked as expected! Give it a whirl, hopefully it suits your current process a bit better.

Hello Cory,

Thank you so much for investing your time here. I will test this today and will let you know. Seems like this will work for our scenario but I will confirm once I have a working pipeline for the same.

Really appreciate your support here. Thanks.

Kind regards,
Rohit

Hello Cory,

So I have tested this on my local with the deployments and it is working as expected. I really appreciate the support from you. Thanks you for your time and the solution you provided.

Kind Regards,
Rohit Verma

I’m glad to hear it Rohit, let me know if there’s anything else you need help with!