Cowboy in the desert.

Fun with output variables

In Octopus 2.4 we’ve added the ability for variables in one step to be available in another step. For example, you might have a standalone PowerShell step called StepA that does something like this:

Set-OctopusVariable -name "TestResult" -value "Passed"

You can then use it in a subsequent deployment step (in the same deployment) like this:

$TestResult = $OctopusParameters["Octopus.Action[StepA].Output.TestResult"]

Built-in output variables

After a step runs, Octopus captures the output variables, and keeps them for use in subsequent steps. In addition to variables that you create yourself using Set-OctopusVariable, Octopus also makes a number of built-in variables available:

  • For NuGet package steps:
    • Octopus.Action[StepName].Output.Package.InstallationDirectoryPath: the path that the package was deployed to.
  • For manual intervention steps:
    • Octopus.Action[StepName].Output.Manual.Notes: notes entered in response to the manual step.
    • Octopus.Action[StepName].Output.Manual.ResponsibleUser.Id
    • Octopus.Action[StepName].Output.Manual.ResponsibleUser.Username
    • Octopus.Action[StepName].Output.Manual.ResponsibleUser.DisplayName
    • Octopus.Action[StepName].Output.Manual.ResponsibleUser.EmailAddress

Scoping and indexing of output variables

It’s important to remember that, unlike a build server, Octopus runs steps across many machines in parallel. This means that each machine might produce a different value for the same output variable.

For example, imagine we have two machines, App01 and App02, and we run this script on both of them:

Set-OctopusVariable -name "MyMachineName" -value [System.Environment]::MachineName

Obviously, we’re going to have two different values available, since both machines have a different hostname. To handle this, Octopus creates the variables and scopes them to a machine. In this example, Octopus would store two variables:

Octopus.Action[StepA].Output.MyMachineName = App01     (Scope: App01)  # Value from App01 machine
Octopus.Action[StepA].Output.MyMachineName = App02     (Scope: App02)  # Value from App02 machine

From now on, any step in the deployment that runs on either of these machines will get the applicable output variable from that machine. This means you can do:

$name = $OctopusParameters["Octopus.Action[StepA].Output.MyMachineName"]

Sometimes you might need to access variables from one machine that are produced by a different machine. For this situation, Octopus also stores non-scoped variables that are instead indexed by the machine:

Octopus.Action[StepA].Output[App01].MyMachineName = App01              # Value from App01 machine
Octopus.Action[StepA].Output[App02].MyMachineName = App02              # Value from App02 machine

This means that in a subsequent step running on App03 for example, you could do:

$app01Name = $OctopusParameters["Octopus.Action[StepA].Output[App01].MyMachineName"]
$app02Name = $OctopusParameters["Octopus.Action[StepA].Output[App02].MyMachineName"]
# Do something with $app01Name and $app02Name

Keep in mind that $OctopusParameters is a just a Dictionary<string,string>. This means you can do things like this:

$MatchRegex = "Octopus\.Action\[StepA\]\.Output\[(.*?)\]\.MyMachineName"

Write-Host "Machine names:"
$OctopusParameters.GetEnumerator() | Where-Object { $_.Key -match $MatchRegex } | % { 
  Write-Host "$_.Value"
}

Here, we’re iterating all the key/value pairs in the dictionary, and finding the ones that match our regex, which has a wildcard on the machine name component of the variable key.

Finding where a previous package was installed

This is such a common use-case for output variables that I want to call it out explicitly.

By default, to avoid various issues around broken deployments and file locks, Tentacles automatically extracts packages to a new, clean directory. If you deploy the exact same package multiple times, you’ll end up with something like:

C:\Octopus\Applications\Production\MyApp\1.0.0
C:\Octopus\Applications\Production\MyApp\1.0.0_1
C:\Octopus\Applications\Production\MyApp\1.0.0_2
C:\Octopus\Applications\Production\MyApp\1.0.0_3

Suppose you deploy a NuGet package, but then want to write a standalone PowerShell script that runs on the same server in the directory the package was extracted to, but isn’t part of the NuGet package. You can use this:

$packageDir = $OctopusParameters["Octopus.Action[MyApp].Output.Package.InstallationDirectoryPath"]
cd $packageDir

# Do your custom logic

Summary

Application deployments often involve running deploying packages and executing code on many different machines. Output variables in Octopus provide a very powerful way to share these values between different steps, and different machines. I hope you’ll find this feature useful!

Learn more


Tagged with: Walkthrough FAQ
Loading...