﻿# Variable substitution syntax

Octopus lets you reference variables in scripts, configuration files, and step settings, using a binding syntax powered by [Octostache](https://github.com/OctopusDeploy/Octostache), Octopus's open-source templating engine.

This page explains that syntax: how Octopus binds and evaluates a reference, how to compose a value from other variables, how to work with collections and conditionals, and how to reshape a value with a filter.

For the built-in variables you can reference, see [System variables](/docs/projects/variables/system-variables).

## Variable substitution

Variable substitution is how Octopus replaces a reference in your text with the value of a variable at deployment or runbook run time. Understanding substitution lets you write one deployment process, script, or configuration file that adapts to each environment, target, or tenant instead of maintaining a copy for each.

You reference a variable by wrapping its name in `#{` and `}`:

```text
#{VariableName}
```

When Octopus runs the step, it evaluates the reference and replaces it with the variable's value. **Every value is a string**, even when it looks like a number or a boolean, because Octopus stores and substitutes all variables as text.

Octopus resolves each reference using the most specifically [scoped](/docs/projects/variables/getting-started/#scoping-variables) value that applies to the current deployment, so the same reference can produce a different value in Development than it does in Production.

## Composite variables

A composite variable is a variable whose own value contains a reference to another variable, using the same `#{...}` binding syntax. Composing values this way lets you tame variable complexity by building a value out of simpler variables instead of maintaining a full string for every scope.

Given the variables:

| Name | Value | Scope |
| --- | --- | --- |
| `Server` | `SQL` | Production, Test |
| `Database` | `PDB001` | Production |
| `Database` | `TDB001` | Test |
| `ConnectionString` | `Server=#{Server}; Database=#{Database}` | |

Evaluating `ConnectionString` in Production yields `Server=SQL; Database=PDB001`. Binding to a value that isn't defined for the current scope yields an empty string, so evaluating `ConnectionString` in a Dev environment (where neither `Server` nor `Database` is scoped) yields `Server=; Database=`.

:::div{.info}
Also read about [common mistakes for variables](/docs/projects/variables/sensitive-variables/#avoiding-common-mistakes) for more information.
:::

### Escaping variable syntax

If the text undergoing substitution contains a literal sequence that looks like a reference but shouldn't be replaced, such as `#{NotToBeReplaced}`, add an extra `#` to escape it:

| Expression | Value |
| --- | --- |
| `##{NotToBeReplaced}` | `#{NotToBeReplaced}` |

Escaping compounds with the length of the hash sequence. Given the variable `Name` with the value `title`, `###{Name}` evaluates to `#title`: the leading `##` collapses to a literal `#`, and the remaining `#{Name}` is substituted normally.

## Variable references

A variable reference names the value you want Octopus to substitute, ranging from a plain variable name to a specific value selected out of a structured object. You can combine a reference with surrounding text to compose a new value:

```text
#{Octopus.Environment.Name}-#{Octopus.Project.Name}
```

Some variables are structured, and you select a value from them using indexer notation, where the key inside the square brackets identifies the entry you want. Many system variables use this form to expose per-action, per-machine, or per-package values. For example, this reference selects the target roles for a specific action:

```text
#{Octopus.Action[Website].TargetRoles}
```

The index itself can be another variable reference, which lets you look up a value dynamically rather than naming the key literally. Given the variables:

| Name | Value |
| --- | --- |
| `MyPassword[Rob]` | `passwordX` |
| `MyPassword[Steve]` | `passwordY` |
| `MyPassword[Mary]` | `passwordZ` |
| `UserName` | `Mary` |

`#{MyPassword[#{UserName}]}` evaluates to `passwordZ`.

Output variables extend indexer notation with a second index for the machine that produced the value. An [output variable](/docs/projects/variables/output-variables) set in one step is available to later steps using the action name and the machine name:

```text
#{Octopus.Action[Website].Output[WEBSVR01].Package.InstallationDirectoryPath}
```

## Collection variables

<span id="extended-syntax">A collection variable</span> holds many entries rather than a single value. Several system variables are collections, including the packages, builds, commits, and work items associated with a release, and the changes included in a deployment; see [System variables](/docs/projects/variables/system-variables) for the full list. Knowing how to read a collection lets you work with all of them, because they share the same iteration and indexing syntax.

You iterate over a collection with an `#{each}` block. Octopus repeats the content between `#{each}` and `#{/each}` once per entry, binding each entry to the name you choose so you can reference its properties:

```text
#{each package in Octopus.Release.Package}
    This release contains #{package.PackageId} #{package.Version}
#{/each}
```

Collections can be nested. An entry in one collection can itself hold a collection, which you iterate the same way:

```text
#{each package in Octopus.Release.Package}
    #{each commit in package.Commits}
        - #{commit.CommitId}: #{commit.Comment}
    #{/each}
#{/each}
```

You can also select a single entry directly using indexer notation. Some collections are keyed by an identifier, such as a package ID:

```text
#{Octopus.Release.Package[Acme.Web].Version}
```

Others are keyed by a zero-based integer index, so the first entry is at index 0:

```text
#{Octopus.Release.Builds[0].BuildUrl}
```

A collection doesn't have to come from a system variable. You can define your own keyed collection using multiple variables that share an index prefix, then iterate it the same way. Given the variables:

| Name | Value |
| --- | --- |
| `Endpoint[A].Address` | `http://a.example.com` |
| `Endpoint[A].Description` | `Primary` |
| `Endpoint[B].Address` | `http://b.example.com` |
| `Endpoint[B].Description` | `Replica` |

```text
#{each endpoint in Endpoint}
 - #{endpoint} at #{endpoint.Address} is #{endpoint.Description}
#{/each}
```

A variable containing a comma-separated list also iterates directly, without needing an index at all:

```text
#{each endpoint in "http://a.example.com,http://b.example.com"}
 - #{endpoint}
#{/each}
```

### JSON variables

Octostache parses a JSON-formatted variable value natively, exposing its properties for substitution the same way a system-variable collection does. Given the variable `Custom.MyJson` with the value `{Name: "t-shirt", Sizes: [{size: "small", price: 15.00}, {size: "large", price: 20.00}]}`, `#{Custom.MyJson.Name}` evaluates to `t-shirt`, and `#{Custom.MyJson.Sizes[0].price}` evaluates to `15.00`.

A JSON array or object iterates using the same `#{each}` syntax as any other collection:

```text
#{each item in Custom.MyJson.Sizes}
 - #{item.size}: #{item.price}
#{/each}
```

When iterating a JSON object rather than an array, each entry exposes `.Key` and `.Value` properties for the object's key and the value at that key.

## Loop iteration variables

Inside an `#{each}` block, Octopus makes a set of variables available that report the position of the current entry in the collection being iterated. This topic lists them. All values are strings.

| Variable | Description |
| --- | --- |
| `Octopus.Template.Each.Index` | The zero-based index of the current entry in the iteration. |
| `Octopus.Template.Each.First` | `True` if the entry is the first in the collection, otherwise `False`. |
| `Octopus.Template.Each.Last` | `True` if the entry is the last in the collection, otherwise `False`. |

Given the variable `Endpoints` with the comma-separated value `SV1,SV2,SV3`, the following template prints a marker on only the first and last entries:

```text
#{each endpoint in Endpoints}
#{if Octopus.Template.Each.First}First: #{endpoint}#{/if}
#{if Octopus.Template.Each.Last}Last: #{endpoint}#{/if}
#{/each}
```

This produces:

```text
First: SV1
Last: SV3
```

## Calculations

Octopus supports basic arithmetic on variable values using the `calc` statement, so you can derive a value like an IP offset or a scaled count without pre-computing it yourself. Four operators are supported: addition (`+`), subtraction (`-`), multiplication (`*`), and division (`/`).

Given the variables:

| Name | Value |
| --- | --- |
| `IPOffset[Primary]` | `0` |
| `ScaleFactor` | `12` |
| `Numbers` | `10,20,30,40,50` |

- `192.168.0.#{calc IPOffset[Primary] + 1}` evaluates to `192.168.0.1`
- `#{calc 22 * ScaleFactor}` evaluates to `264`
- `#{each i in Numbers}#{calc i + 5}#{/each}` evaluates to `15 25 35 45 55`

When a variable appears on the left-hand side of a divide or subtract operation, enclose its name in braces so the operator symbol isn't parsed as part of the variable name: `#{calc {IPOffset[Primary]} - 4}`.

## Conditionals

A conditional includes or excludes text based on a variable's value, letting you vary a script or configuration file by context without maintaining a separate copy for each case. Octopus evaluates the condition when the step runs and keeps only the matching branch.

Two conditional statements are supported: `if`, which evaluates its content when the variable is truthy, and `unless`, which evaluates when the variable is falsy. A value is falsy if it's undefined, an empty string, or (ignoring case and surrounding whitespace) `False`, `No`, or `0`; every other value is truthy.

```text
#{if VariableName}conditional statements#{/if}
#{unless VariableName}conditional statements#{/unless}
```

An `#{if}` block can include an `#{else}` branch for the case where the condition doesn't hold:

```text
#{if Octopus.Environment.Name == "Production"}
  ProductionValue
#{else}
  DefaultValue
#{/if}
```

### Complex syntax

Beyond a truthy/falsy check, a conditional can compare values directly using `==` and `!=`, for example `#{if Octopus.Environment.Name == "Production"}...#{/if}`. Neither operand uses `#{...}` syntax inside the comparison — Octostache already evaluates each operand as a variable's value there, and the same comparison works against another variable's value instead of a literal: `#{if Environment.LogLevel == Base.MaxLogLevel}...#{/if}`.

Combining `if` and `else` lets you express an effective OR across more than two cases:

```text
#{if Octopus.Environment.Name == "Development"}
  Do this if it's Development
#{else}
  #{if Octopus.Environment.Name == "Test"}
    Do this if it's Test
  #{else}
    Do this if it's neither
  #{/if}
#{/if}
```

It's possible to use [variable filters](/docs/projects/variables/variable-filters) to help create both complex run conditions and variable expressions, but there are limitations to be aware of.

:::div{.warning}
Using variable filters *inline* in the two [conditional statements](/docs/projects/variables/variable-substitutions/#conditionals) `if` and `unless` are **not supported**.
:::

If you wanted to include a variable run condition to run a step *only* when the release had a prerelease tag matching `my-branch`, you might be tempted to use the `VersionPreReleasePrefix` [extraction filter](/docs/projects/variables/variable-filters/#extraction-filters) to write a condition like this:

```
#{if Octopus.Release.Number | VersionPreReleasePrefix == "my-branch"}true#{/if}
```
However, the evaluation of the statement would always return `False` as the syntax is not supported.

Instead, you need to create a variable that includes the variable filter you want to use. For this example, let's assume it's named `PreReleaseBranch` with the value:

```
#{Octopus.Release.Number | VersionPreReleasePrefix}
```

Once you have created your variable, you can use it in your run condition like this:

```
#{if PreReleaseBranch == "my-branch"}True#{/if}
```

### Run conditions

A conditional expression can also control whether a step in a deployment process runs at all — for example, `#{if Octopus.Environment.Name == "Production"}true#{/if}` runs a step only in Production. See [Conditions](/docs/projects/steps/conditions) for how run conditions are configured on a step.

## Filters

A filter transforms a variable's value as Octopus substitutes it, so you can reshape a value for its destination without changing the underlying variable. Apply a filter with a pipe after the variable name:

```text
#{Octopus.Release.Notes | Markdown}
```

Filters cover common transformations such as changing case, escaping values for HTML or JSON, and rendering Markdown. For the full set of filters and what each one does, see [Variable filters](/docs/projects/variables/variable-filters).

## Version notes

- The `calc` operator is available from Octopus Server 2023.2.

## Related links

- [System variables](/docs/projects/variables/system-variables)
- [Variable filters](/docs/projects/variables/variable-filters)
- [Output variables](/docs/projects/variables/output-variables)
- [Custom scripts](/docs/deployments/custom-scripts)
- [Variables](/docs/projects/variables)
