Multiline Expressions in Ansible Playbooks

Another week, another Ansible quirk 🤷‍♂️ Imagine you have a long Jinja2 expression, and you want to wrap it into multiple lines to improve readability. Using multiline YAML format seems to be the ideal choice:

---
- name: Test playbook
  hosts: localhost
  tasks:
  - set_fact:
      a: >
        {{ 123 == 345 or
           123 > 345 }}

It works every time 50% of the time (this time depending on your Ansible version).

Before someone accuses me of picking on Ansible, this is a hope I’ll remember this for the next time note I wrote after dealing with this stupidity I introduced into netlab.

If you use the community version of Ansible, you’ll get the expected results: a is false:

$ ansible-playbook -v x.yml
...
TASK [set_fact] ********************************************************************************************************************
ok: [localhost] => {"ansible_facts": {"a": false}, "changed": false}

However, if you use an older Ansible core release (for example, Ansible core 2.12.10 that you could find in RHEL 9.0), you’ll get this outstanding result:

$ ansible-playbook -v x.yml
...
TASK [set_fact] ********************************************************************************************************************
ok: [localhost] => {"ansible_facts": {"a": "False\n"}, "changed": false}

Notice how the extra newline throws off Ansible? It evaluates the expression as a string; a non-empty string is true, not false.

Lesson learned: you MUST NOT use multiline YAML format in Ansible expressions.

You could enclose expressions in quotes like this:

---
- name: Test playbook
  hosts: localhost
  tasks:
  - set_fact:
      a:
        "{{ 123 == 345 or
            123 > 345 }}"

You could also use the YAML Block Chomping Indicator to remove the trailing newline:

---
- name: Test playbook
  hosts: localhost
  tasks:
  - set_fact:
      a: >-
        {{ 123 == 345 or
           123 > 345 }}

You’ll find more details on a website dedicated to multiline YAML strings.

Revision History

2024-03-07
Added Block Chomping as an alternate solution based on Miguel’s comment.

1 comments:

  1. Hello,

    First of all, thank you for all the information you share here. It is really useful.

    For several reason we have been dealing with ansible 2.10 for a while and the solution we found to this problem was to remove the endline by using YAML multiline block definition options. We always use ">-" instead of ">" when we want to define a variable using set_fact, so it removes that final endline.

    Here it is an example based on yours for this behaviour in ansible 2.10.17.

    Variable a is set using '>' and variable b using '>-'. First one is set to a string while the second one is set as intended to false.

    > cat playbook.yml
    ---
    - name: Test playbook
      hosts: localhost
      gather_facts: no
      tasks:
      - set_fact:
          a: >
            {{ 123 == 345 or
               123 > 345 }}
          b: >-
            {{ 123 == 345 or
               123 > 345 }}
    
    > ansible-playbook -v playbook.yml
    ...
    PLAY [Test playbook] ***********************************************************************************
    
    TASK [set_fact] ****************************************************************************************
    ok: [localhost] => {"ansible_facts": {"a": "False\n", "b": false}, "changed": false}
    
    PLAY RECAP *********************************************************************************************
    localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    

    Hope it helps. Best regards

Add comment
Sidebar