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
anddownload_file
in different variables trees, like: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 onlyliquibase_version
andliquibase_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 withversion
anddownload_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:
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:
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.