Using Variables

Variables are values that can be substituted in templates (via the template module) and logic throughout Jet's playbook language. Most strings in Jet are templated for evaluation, so variables can be used almost anywhere in the system.
Variables can be set in inventory -- in which case they can come from on disk or discovered from cloud parameters or in playbooks.

Use In Tasks

Variables set elsewhere in the playbook can be directly used in playbook statements like so:
- !shell
cmd: /usr/bin/example --argument1 {{ argval }} --argument2 asdf
The templates are processed by the Rust handlebars library, passing through all variables that are defined in the Jet system.
Where Variables Can Be Used
Templating in Jet is very deliberate by design to encourage predictable and highly auditable content.
Only module parameters in Jet are templated. For reasons of clarity, it is not possible to use variable expressions in the "Play" object (like the vars keyword, or in paths to vars_files) or nest the values of variables inside other variables. In other words, variable evaluation is not recursive.
All of these decisions were made to make playbooks extremely predictable, avoiding many possibilities of hidden errors or surprises.
Use variables when needed, but it is important to also realize that Jet is not meant to be a programming language, and the system is designed to enforce "best practices" in automation design by sometimes limiting the ability to do confusing things. There may be times when you might want to generate a vars_files file for Jet from a script if you need data denormalized in many different ways.
Use of templates parameters in unsupported areas will produce largely obvious and up-front errors messages, such as a YAML boolean field not accepting a string value, allowing for easy correction.

Use In Templates

The template module also uses Rust's handlebars library to describe the files it wants to write on the remote machines. An example source template might use basic variables but it can also use other constructs.
# foo.config template example
[config section]
xyz = {{ xyz }}
backup_enabled = true
{{#if (eq abcd debian}}
tribbles = auto
tribbles = 5000
The Rust handlebars library provides the some compatible block expressions that are described in the Javascript handelbarjs documentation. To keep things simple, these are the most commonly used and appropriate.
  • {{{{raw}}}} ... {{{{/raw}}}}
  • {{#if ...}} ... {{else}} ... {{/if}}
  • {{#unless ...}} ... {{else}} .. {{/unless}}
  • {{#each ...}} ... {{/each}}
  • {{#with ...}} ... {{/with}}
As described with cond in Tasks, if statements can use the following boolean expressions:
  • eq
  • ne
  • gt
  • gte
  • lt
  • lte
  • and
  • or
  • not
The 'if' template example above shows the example of eq, which must be surrounded in parentheses. This is also shown in the examples for the 'cond' task keyword. The other expressions work the same way.
No Recursive Evaluation
To encourage predictability and easily auditable content, templates in Jet are not evaluated recursively, but only a single time. If you think you need variable indirection where a variable has another variable name in it, consider restructuring your data. Where neccessary, variables such as paths that were stored this way...
base_dir: /opt/apps
app_path: "{{ base_dir }}/foo/bar"
Can be recomposed like so:
base_dir: "/opt/apps"
app_sub_dir: "/foo/bar"
And then inside of the template file or module, just do this:
app_path={{ base_dir}/{{ app_sub_dir }}
Users familiar with Jinja2 will note that the variable dereferencing (double curly brackets) are the same but the other operations are not. Handlebars is a deliberately more minimal system, and is not designed to be used in a 'programming' type capacity. Over time we may provide some additional helper functions for templates, but will try to keep this very constrained to encourage predictable read-throughs of automation content.

Variable Precedence

It is possible to define a variable in many locations throughout Jet. When a variable is referenced, how do we decide what value is used? The variable source closer to the top of this list wins:
  1. 1.
    Variables defined in the 'vars' or 'vars_files' keyword of a Play always win.
  2. 2.
    Inventory variables have middle priority:
    1. 1.
      Variables defined on a Host system will beat out Group variables. Host variables include variables stored as the result of a 'save' task action when running a module.
    2. 2.
      Variables defined on a Group will be used if the same variable isn't set on the Host. If there are subgroups that repeat variable names, the most specific group wins over the larger parent group. For example values in "london" (a city group) would win over "england" (a country group).
  3. 3.
    Default variables (lowest priority):
    1. 1.
      Variables defined in the 'default' keyword of a playbook
    2. 2.
      Variables defined in the 'default' folder for a Role