How to perform recursive vars[varname] expansion in template?

Hi,

In a template, I use “{{ vars[varname] }}”. “varname” is a default var declared this way “varname: {{ another_var }}”.

I would expect in the template to get the value of another_var, not the name of another_var.

I have reproduced the problem in test/integration/targets/var_blending at this gist https://gist.github.com/tlvu/ade0a36dc426eea4b9f6a28381cc1fc1 (runme.sh pass).

I am using version 2.3.0.0 (stable-2.3 c92765e26e).

The real usecase for this is I’d like to do something like {{ vars[prefix + ‘_token’] }} in a template and prefix would be declared as “prefix: {{ prod_prefix }}” or “prefix: {{ staging_prefix }}” depending on the inventory file used. I think this usecase is immensely useful so I was a bit surprised it did not work.

If there is an existing bug, please point me to it. I did search for “recursive vars expansion” and did not find anything useful.

If there is any work-around, please let me know because I am pretty stuck right now.

Thanks,
Long

Found this issue https://github.com/ansible/ansible/issues/15753 “Accessing a dictionary using {{ vars[‘dictname’] }} doesn’t interpolate variables in the dictionary elements” that is very close to mine.

Still no work-around.

To help the next person, I found a work-around: use loops over a list of dict as a way to “inject” variables into the template.

So I needed something like {{ vars[prefix + ‘readonly_token’] }}, {{ vars[prefix + ‘readwrite_token’] }}, {{ vars[prefix + ‘readonly_tokensecret’] }}, {{ vars[prefix + ‘readwrite_tokensecret’] }} in the template.

In the same play, I also needed to instantiate the template for multiple values of ‘prefix’, each prefix can be a different hostname or mapped to the same hostname like “prefix: {{ another_prefix }}”.

The fact that prefix can be mapped to another prefix is way breaks the syntax “{{ vars[prefix + ‘readonly_token’] }}”.

So the work-around is like this:

vars:
host1: { readonly_token: “blah”, readwrite_token: “blah”, readonly_tokensecret: “blah”, readwrite_tokensecret: “blah” }

host2: { readonly_token: “blah”, readwrite_token: “blah”, readonly_tokensecret: “blah”, readwrite_tokensecret: “blah” }

host3: { readonly_token: “blah”, readwrite_token: “blah”, readonly_tokensecret: “blah”, readwrite_tokensecret: “blah” }

host4: { readonly_token: “blah”, readwrite_token: “blah”, readonly_tokensecret: “blah”, readwrite_tokensecret: “blah” }

list_of_all_hosts: [ “{{ host1 }}”, “{{ host2 }}”, “{{ host3 }}”, “{{ host4 }}” ]

template:
src: template file
dest: blah
with_items: “{{ list_of_all_hosts }}”

In the template file, instead of {{ vars[prefix + ‘readonly_token’] }} I can then use {{ item.readonly_token }} and the dict mapping ensure proper values for me.

The loop inject “updated values” for me into the template for each run, as if I can re-define a variable for each template call.

I see using template with loop over a dict as a way to inject variable into the template instead of the usual way to create variable (default role var, group var, host var, role var, commandline var …)

Re-reading my own post, the vars declaration should have been like this, which will trigger the “no recursive vars[varname] expansion in template” problem which will need the work-around.

vars:
host1_ro_token: “blah”
host1_ro_tokensecret: “blah”
host1: { readonly_token: “{{ host1_ro_token }}”, readwrite_token: “blah”, readonly_tokensecret: “{{ host1_ro_tokensecret }}”, readwrite_tokensecret: “blah” }
host2: { readonly_token: “{{ host1_ro_token }}”, readwrite_token: “blah”, readonly_tokensecret: “{{ host1_ro_tokensecret }}”, readwrite_tokensecret: “blah” }
host3: { readonly_token: “{{ host1_ro_token }}”, readwrite_token: “blah”, readonly_tokensecret: “{{ host1_ro_tokensecret }}”, readwrite_tokensecret: “blah” }
host4: { readonly_token: “{{ host1_ro_token }}”, readwrite_token: “blah”, readonly_tokensecret: “{{ host1_ro_tokensecret }}”, readwrite_tokensecret: “blah” }