Ansible Pitfall: Accessing Variables using vars
tl;dr: vars is an undocumented internal variable with surprising
behaviour. Use foo instead of vars['foo'] and lookup('vars', 'foo' ~ bar) instead of vars['foo' ~ bar].
For a long time the only supported way of retrieving
variables based on an expression was to use
hostvars.
This generally works, but not all variables that are in scope are
present in hostvars. At some point people learned that you could use
a dictionary called vars to access more variables than are present
in hostvars[inventory_hostname]:
- hosts: localhost
vars:
foo: pisces
tasks:
- debug:
msg: "{{ vars['foo'] }}"
- debug:
msg: "{{ hostvars[inventory_hostname]['foo'] }}"
TASK [debug] ******************************************************************* ok: [localhost] => msg: pisces TASK [debug] ******************************************************************* [ERROR]: Task failed: Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': object of type 'HostVarsVars' has no attribute 'foo' Task failed: Finalization of task args for 'ansible.builtin.debug' failed. Origin: test.yml:8:7 6 msg: "{{ vars['foo'] }}" 7 8 - debug: ^ column 7 <<< caused by >>> Error while resolving value for 'msg': object of type 'HostVarsVars' has no attribute 'foo' Origin: test.yml:9:14 7 8 - debug: 9 msg: "{{ hostvars[inventory_hostname]['foo'] }}" ^ column 14 fatal: [localhost]: FAILED! => {"msg": "Task failed: Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': object of type 'HostVarsVars' has no attribute 'foo'"}
The problem is that this variable was never intended to be used this
way. As such, it exhibits some surprising behaviour that you might
not encounter in simple tests but will definitely trip you up at some
point. For instance, values in vars are not always templated:
- hosts: localhost
vars:
foo: pisces
bar: "{{ foo[0:2] }}"
tasks:
- debug:
msg: "{{ bar }}"
- debug:
msg: "{{ vars['bar'] }}"
TASK [debug] ******************************************************************* ok: [localhost] => msg: pi TASK [debug] ******************************************************************* ok: [localhost] => msg: '{{ foo[0:2] }}'
Because people are using this in the wild the Ansible developers have not removed this variable, but they have expressed a desire to do so now that variable deprecation is implemented.
In its place they have provided the vars
lookup,
which can access all variables that are in scope and does not exhibit
any of the unexpected behaviour of the vars variable.
- hosts: localhost
vars:
foo: pisces
bar: "{{ foo[0:2] }}"
tasks:
- debug:
msg: "{{ lookup('vars', 'b' ~ 'ar') }}"
TASK [debug] ******************************************************************* ok: [localhost] => msg: pi