Is it possible to check whether a dynamic variable is defined in Ansible?

Viewed 48

The Ansible Documentation on conditionals provides examples of how a task can check whether a variable is defined:

- name: Fail if "bar" is undefined
  ansible.builtin.fail: msg="Bailing out. This play requires bar"
  when: bar is undefined

I'm building a playbook that checks whether a list of variables is defined. My first insinct was to write the code like this:

- name: Fail if any variables are undefined
  ansible.builtin.fail: msg="Bailing out. This play requires {{ item }}"
  when: item is undefined
  with_items:
    - bar
    - foo
    - baz

However, this doesn't work, since this is checking that the item variable itself is defined.

2 Answers

Use lookup plugin varnames. See

shell> ansible-doc -t lookup varnames

For example, if none of the listed variables is defined

    - set_fact:
        my_vars: "{{ my_vars|d({})|combine({item: _defined}) }}"
      loop:
        - bar
        - foo
        - baz
      vars:
        _defined: "{{ lookup('varnames', item)|length > 0 }}"

gives

  my_vars:
    bar: false
    baz: false
    foo: false

Test it. For example, the task

    - assert:
        that: my_vars_undef|length == 0
        fail_msg: "Bailing out. This play requires: {{ my_vars_undef }}"
      vars:
        my_vars_undef: "{{ my_vars|dict2items|
                                   rejectattr('value')|
                                   map(attribute='key')|
                                   join(',') }}"

will fail

TASK [assert] ***********************************************************
fatal: [localhost]: FAILED! => changed=false 
  assertion: my_vars_undef|length == 0
  evaluated_to: false
  msg: 'Bailing out. This play requires: bar,foo,baz'

Example of a playbook

shell> cat pb.yml
- hosts: localhost
  tasks:
    - set_fact:
        my_vars: "{{ my_vars|d({})|combine({item: _defined}) }}"
      loop:
        - bar
        - foo
        - baz
      vars:
        _defined: "{{ lookup('varnames', item)|length > 0 }}"
    - assert:
        that: my_vars_undef|length == 0
        fail_msg: "Bailing out. This play requires: {{ my_vars_undef }}"
      vars:
        my_vars_undef: "{{ my_vars|dict2items|
                                   rejectattr('value')|
                                   map(attribute='key')|
                                   join(',') }}"

The playbook will continue if all variables in the list are defined

shell> ansible-playbook pb.yml -e bar=A -e foo=B -e baz=C

...

TASK [assert] **********************************************************
ok: [localhost] => changed=false 
  msg: All assertions passed

It's technically possible to do this with a template string:

- name: Fail if any variables are undefined
  ansible.builtin.fail: msg="Bailing out. This play requires {{ item }}"
  when: "{{ item }} is undefined"
  with_items:
    - bar
    - foo
    - baz

but it triggers the following warning from Ansible:

[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item }} is undefined

Related