Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 54 additions & 43 deletions src/pages/docs/projects/variables/variable-substitutions.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: src/layouts/Default.astro
pubDate: 2023-01-01
modDate: 2024-08-29
modDate: 2026-02-17
title: Variable substitutions
icon: fa-solid fa-underline
description: Variable substitutions are a flexible way to adjust configuration based on your variables and the context of your deployment.
Expand All @@ -15,12 +15,12 @@ Variable substitutions are a flexible way to adjust configuration based on your

You can use Octopus's special binding syntax to reference a variable from within the value of another variable. This is sometimes referred to as using **Composite variables**, because you compose a variable value with other Octopus variables. In the following example, the `ConnectionString` variable references the variables `{Server}` and `{Database}`.

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

In regular variable declarations, binding to a non-existent value will yield an empty string, so evaluating `ConnectionString` in the *Dev* environment will yield `Server=;` because no `Database` or `Server` are defined for that environment.

Expand All @@ -30,6 +30,14 @@ If the file undergoing variable replacement includes a string that *shouldn't* b
| --------------------- | -------------------- |
| `##{NotToBeReplaced}` | `#{NotToBeReplaced}` |

Variable substitution within a hash-delimited string looks like the following. Given the variable:

| Name | Value |
| -------- | ------- |
| `Name` | `title` |

`###{Name}` would evaluate to `#title`.

:::div{.info}

Also read about [common mistakes for variables](/docs/projects/variables/sensitive-variables/#avoiding-common-mistakes) for more information
Expand All @@ -42,13 +50,13 @@ Binding syntax can be used to dynamically change the values of deployment step s
Most text fields that support binding to variables will have a variable insert button:

:::figure
![](/docs/img/projects/variables/images/3278296.png)
![Variable insert button on text fields that support variable binding](/docs/img/projects/variables/images/3278296.png)
:::

For settings that support variables but aren't text (such as drop downs or check-boxes), a button is displayed to toggle custom expression modes:

:::figure
![](/docs/img/projects/variables/images/3278297.png)
![Toggling custom expression modes for settings that support variables but aren't text](/docs/img/projects/variables/images/3278297.png)
:::

## Extended syntax \{#extended-syntax}
Expand Down Expand Up @@ -96,7 +104,7 @@ Basic mathematical calculations are supported in Octopus using the `calc` statem
:::div{.warning}
When using a variable on the left-hand-side of a divide (`/`) or subtraction (`-`) operation,
the variable name must be enclosed in braces (`{ ... }`) to ensure correct parsing, this ensures the operator symbol is recognized
as an operation, rather than part of the variable name.
as an operation, rather than part of the variable name.
:::

Given the variables:
Expand All @@ -112,7 +120,7 @@ Given the variables:
- `192.168.0.#{calc IPOffset[Primary] + 1}` would evaluate to `192.168.0.1`
- `192.168.0.#{calc IPOffset[Secondary] + 1}` would evaluate to `192.168.0.181`
- `#{calc 22 * ScaleFactor}` would evaluate to `264`
- `#{each i in Numbers}#{calc i + 5}#{/each}` would evaluate to `15 25 35 45 55 `
- `#{each i in Numbers}#{calc i + 5}#{/each}` would evaluate to `15 25 35 45 55`
- `#{calc {My/Var} / 3}` would evaluate to `5`
- `#{calc 2 * My/Var}` would evaluate to `30`
- `#{calc {My/Var} - 4}` would evaluate to `11`
Expand Down Expand Up @@ -176,19 +184,19 @@ Additional conditional statements are supported, including `==` and `!=`.

Using complex syntax you can have expressions like `#{if Octopus.Environment.Name == "Production"}...#{/if}` and `#{if Octopus.Environment.Name != "Production"}...#{/if}`, or:

```
```text
#{if ATruthyVariable}
Do this if ATruthyVariable evaluates to true
#{else}
Do this if ATruthyVariable evaluates to false
#{/if}
```

**OR Conditions**
#### OR Conditions

It's common to want to check for more than one value in an Octopus variable. To achieve this, you can create an effective `OR` statement by combining an `if` with another `else` statement:

```
```text
#{if Octopus.Environment.Name == "Development"}
Do this if it's Development
#{else}
Expand All @@ -202,37 +210,37 @@ It's common to want to check for more than one value in an Octopus variable. To

This is the equivalent of checking the Environment name for Development or Test.

**Comparing one variable value with another**
### Comparing one variable value with another

Sometimes, you might want to compare one variable value with another.
Sometimes, you might want to compare one variable value with another.

Given the variables:

| Name | Value | Scope |
| ----------- | ---------------------------------------- | ----- |
| `Base.MaxLogLevel` | `ERROR` | |
| `Environment.LogLevel` | `DEBUG` | `Dev` |
| `Environment.LogLevel` | `INFO` | `Test` |
| `Environment.LogLevel` | `ERROR` | `Staging` |
| `Environment.LogLevel` | `ERROR` | `Production` |
| Name | Value | Scope |
|-----------------------|----------|-------------|
| `Base.MaxLogLevel` | `ERROR` | |
| `Environment.LogLevel`| `DEBUG` | `Dev` |
| `Environment.LogLevel`| `INFO` | `Test` |
| `Environment.LogLevel`| `ERROR` | `Staging` |
| `Environment.LogLevel`| `ERROR` | `Production`|

Using conditional syntax, you can compare the value in the `Base.MaxLogLevel` variable with the `Environment.LogLevel` variable value.

Using the template:

```
```text
#{if Environment.LogLevel == Base.MaxLogLevel}We are at the MAX!#{else}We have room to grow!#{/if}
```

The resulting text in both _Dev and Test_ will be:
The resulting text in both *Dev and Test* will be:

```
```text
We have room to grow!
```

And in both _Staging and Production_ it will be:
And in both *Staging and Production* it will be:

```
```text
We are at the MAX!
```

Expand Down Expand Up @@ -286,45 +294,47 @@ Listening on:

#### Complex syntax with sets of values

Sometimes, you might want to compare one variable value with another contained in a set of values.
Sometimes, you might want to compare one variable value with another contained in a set of values.

Given the variables:

| Name | Value |
| ----------- | ---------------------------------------- |
| `WidgetIdSelector` | `Widget-2` |
| `MyWidgets` | `{"One":{"WidgetId":"Widget-1","Name":"Widget-One"},"Two":{"WidgetId":"Widget-2","Name":"Widget-Two"}}` |
| Name | Value |
|--------------------|---------------------------------------------------------------------------------------------------------|
| `WidgetIdSelector` | `Widget-2` |
| `MyWidgets` | `{"One":{"WidgetId":"Widget-1","Name":"Widget-One"},"Two":{"WidgetId":"Widget-2","Name":"Widget-Two"}}` |

Using complex syntax, you can iterate over the values in the `MyWidgets` variable and find the entry with the value specified in the second variable `WidgetIdSelector`.

Using the template:

```
```text
#{each w in MyWidgets}
'#{w.Value.WidgetId}': #{if w.Value.WidgetId == WidgetIdSelector}This is my Widget!#{else}No widget matched :(#{/if}
#{/each}
```

The resulting text will be:

```
```text
'Widget-1': No widget matched :(
'Widget-2': This is my Widget!
```

:::div{.hint}
**Tips:**

- Note both operands **don't** include the Octostache syntax denoting them as a variable e.g. `#{WidgetIdSelector}`. This is because within a conditional expression Octostache is already able to evaluate the operands as variable values.
- The template references `.Value` which is a property available when using [JSON repetition](/docs/projects/variables/variable-filters/#repetition-over-json).

:::

#### Iterating over comma-separated values

Given the variable:

| Name | Value | Scope |
| ----------- | ---------------------------------------- | ----- |
| `Endpoints` | `http://a.example.com,http://b.example.com` | |
| Name | Value | Scope |
| ----------- | --------------------------------------------- | ----- |
| `Endpoints` | `http://a.example.com,http://b.example.com` | |

And the template:

Expand All @@ -347,17 +357,17 @@ Listening on:

Within the context of an iteration template, some special variables are available.

| Name | Description |
| ----------------------------- | ---------------------------------------- |
| `Octopus.Template.Each.Index` | Zero-based index of the iteration count |
| Name | Description |
| ----------------------------- | ------------------------------------------------------------------------- |
| `Octopus.Template.Each.Index` | Zero-based index of the iteration count |
| `Octopus.Template.Each.First` | `"True" if the element is the first in the collection`, otherwise "False" |
| `Octopus.Template.Each.Last` | "True" if the element is the last in the collection, otherwise "False" |
| `Octopus.Template.Each.Last` | "True" if the element is the last in the collection, otherwise "False" |

Given the variable created as an index (comma separated):

| Name | Value |
| ----------- | ---------------------------------------- |
| `Endpoints` | `SV1,SV2,SV3` |
| `Endpoints` | `SV1,SV2,SV3` |

And the template:

Expand Down Expand Up @@ -418,6 +428,7 @@ The filters can be invoked in the following way:
For more information, see [Variable Filters](/docs/projects/variables/variable-filters).

## Older versions

The `calc` operator is available from Octopus Deploy **2023.2** onwards.

## Learn more
Expand Down