Search code examples
ansiblejinja2ansible-2.x

not defined versus undefined in Ansible


In Ansible, is there a difference between a variable that is 'undefined' and one that is 'not defined'?
I've seen both terms used interchangeably, but I want to make sure that there isn't some subtle difference that I'm missing. From what I understand, both terms refer to a variable that has not been defined or has no value assigned to it.
Is this correct, or are there some cases where they should be used differently? Can someone provide a clear explanation or point to relevant documentation?

I have tried running the following play-book because I thought the difference will be in the declaration of the variable but it didn't work as expected.

---
- name: Example play-book
  hosts: localhost
  gather_facts: no
  vars:
    # This variable is defined with a value assigned to it
    defined_var: "Hello, world!"
    # This variable is defined but has no value assigned to it
    undefined_var:
    # This variable is not defined at all
    # not_defined_var:

  tasks:
    - name: Task 1
      debug:
        var: defined_var

    - name: Task 2
      debug:
        msg: "VARIABLE IS UNDEFINED"
      when: undefined_var is undefined

    - name: Task 3
      debug:
        var: not_defined_var
      when: not_defined_var is undefined

Output:

PLAY [Example playbook] *******************************************

TASK [Task 1] *****************************************************
ok: [localhost] => {
    "defined_var": "Hello, world!"
}

TASK [Task 2] *****************************************************
skipping: [localhost]

TASK [Task 3] *****************************************************
ok: [localhost] => {
    "not_defined_var": "VARIABLE IS NOT DEFINED!"
}  

Solution

  • Ansible templating system is Jinja, and in the Jinja documentation we can read:

    jinja-tests.undefined(value: Any) → bool
    Like defined() but the other way round.

    Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-tests.undefined

    So, yes, one is simply the not variant of the other.

    {{ var is not defined == var is undefined }} {# => will always be true #}
    {{ var is not undefined == var is defined }} {# => will always be true #}
    

    The difference can be done when using the filter default(), that is referenced in the defined() test documentation to handled undefined variables:

    {{ '' | default('this is a placeholder') }} 
    

    will render a blank string, while

    {{ '' | default('this is a placeholder', true) }} 
    

    will render the placeholder, this is a placeholder, as the second parameter will help you to also control variables that evaluate to false (empty values — '' string, () tuple, {} dictionary, [] list —, the boolean false, None, 0).


    If you want to go further and peak at Jinja's code you can see that the defined test is defined as such:

    def test_defined(value: t.Any) -> bool:
        return not isinstance(value, Undefined)
    

    Source: https://github.com/pallets/jinja/blob/main/src/jinja2/tests.py

    And the undefined one as such:

    def test_undefined(value: t.Any) -> bool:
        return isinstance(value, Undefined)
    

    Source: https://github.com/pallets/jinja/blob/main/src/jinja2/tests.py