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] *******************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'foo'\n\nThe error appears to be in '/home/ec2-user/test.yml': line 9, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
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 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 once 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"
}