How can I reuse variables in other variables in Ansible (2.1.x) without causing recursive loops?
Setup
Consider this roles/<role>/defaults/main.yml
file:
---
liquibase:
version: "3.5.3"
download_file: "liquibase-{{liquibase.version}}-bin.tar.gz"
# I also tried this alternative with a similar result:
# download_file: "liquibase-{{liquibase[version]}}-bin.tar.gz
...
and this roles/<role>/tasks/main.yml
file:
---
- name: Liquibase | Download
debug:
msg: "download_file: {{liquibase.download_file}}"
...
Error
I'd expect the variable liquibase.download_file
to have the value liquibase-3.5.3-bin.tar.gz
but when I run a playbook using this role, I get the following error:
...
TASK [liquibase : Liquibase | Download] *******************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "...: recursive loop detected in template string: liquibase-{{liquibase.version}}-bin.tar.gz"}
...
My Use Case
Obviously I want to download Liquibase and I want to let the role's user decide which version to use. I also want to give the possibility to completely override the download location (file, URL, etc.), e.g. for using a company's FTP server or similar.
Referencing other dict keys in the same parent dict is not supported. See this issue.
You can only refactor your variables to make version
and download_file
in different variables trees, like:
liquibase_version: "3.5.3"
liquibase_download_file: "liquibase-{{liquibase_version}}-bin.tar.gz"
P.S. if this your role's defaults, separating liquibase_version
into standalone variable make even more sence. This way user would have to redefine only liquibase_version
and liquibase_download_file
will get the changes; while in case of dict (as in your question), you can't override only one key, user whould have to set full dict with version
and download_file
keys.
I found this frustrating when trying to blend the elegance of YAML structures with the flexibility of Jinja2. As it isn't supported, one way around it (which I admit is something of a kludge) is to introduce a pseudo-private section to help break the cycle. First we declare anything that might cause a dependency:
# Pseudo private KV pairs, used to work around recursion issues.
_liquibase_version: "3.5.3"
We only have to declare anything that would be declared within the dictionary and forms a dependency for another dictionary key. Next we can declare the dictionary as we wanted but use the 'private' key value pairs where necessary:
liquibase:
version: "{{_liquibase_version}}"
download_file: "liquibase-{{_liquibase_version}}-bin.tar.gz"
Of course, _liquibase_version
is not really private, but acts as a helper so we only have to define the value once but still get the benefit of being able to refer to {{liquibase.version}}
and {{liquibase.download_file}}
later on.