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

308 views
Skip to first unread message

Long Vu

unread,
Apr 19, 2017, 12:48:54 PM4/19/17
to Ansible Project
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

Long Vu

unread,
Apr 19, 2017, 1:25:45 PM4/19/17
to Ansible Project
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.

Long Vu

unread,
Apr 20, 2017, 1:11:29 PM4/20/17
to Ansible Project
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 ....)

Long Vu

unread,
Apr 25, 2017, 11:48:39 AM4/25/17
to Ansible Project
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" }
Reply all
Reply to author
Forward
0 new messages