Expanding var with more than one hostvars reference

Unexpected behavior in variable expansion.

I have a small example of the behavior here. Not sure yet if this is a bug.

Here is a simplified version.

inventory.yml:

all:
  hosts:
    localhost:
      prefix_url: "prefix"
      inv_value_fact: "{{ hostvars['localhost'].prefix_url + '/special-value' }}"
      inv_value: "{{ inv_value_fact }}"
  vars:
    actual_value: "{{ inv_value }}"

I was expecting these two command lines to produce the same result:

ansible -i inventory.yml -m debug -a var=actual_value all

localhost | SUCCESS => {
    "actual_value": "prefix/special-value"
}

ansible -i inventory.yml -m debug -a "var=hostvars['localhost'].actual_value" all

localhost | SUCCESS => {
    "hostvars['localhost'].actual_value": "{{ inv_value }}"
}

there is also a play.yml in the github repo producing the same results.

I am trying to understand how the play was able to load the correct value from the hosts group of the play, but accessing the hostvars value directly will not complete the evaluation. Iā€™ve also determined that if I change the definition of actual_value to

actual_value: "{{ inv_value | default('some-default') }}"

the output of
`ansible -i inventory.yml -m debug -a ā€œvar=hostvars[ā€˜localhostā€™].actual_valueā€ all``
will change to

localhost | SUCCESS => {
    "hostvars['localhost'].actual_value": "some-default"
}

This was originally seen in a much more complex environment and reported because the default value was being used when the inventory provided a definition that should have overridden that default.

Any insights into why this is occurring would be appreciated.

Thanks,
Glenn

Did you ever figure this out? I am seeing very similar behavior

Caveat: I donā€™t actually know the answer. However, Iā€™m fairly sure it sits at the intersection of lazy variable evaluation and the contrast between host facts and variables, and I think itā€™s relevant that the values of facts variables ā€œnamed thingsā€ set through ansible.builtin.set_fact are fully evaluated at set_fact-time.

I could make further guesses, but the more I did the wronger Iā€™d get. I hope somebody who actually knows will give us a concise answer, because itā€™s a fascinating question. The number of people who could do that, though, is quite small.

1 Like

I think this is the same issue described in hostvars[host][variable] are not recursively templated Ā· Issue #17806 Ā· ansible/ansible Ā· GitHub. Thereā€™s a draft fix, but it doesnā€™t look like it handles this yet (it causes a recursion error). A workaround could be setting inv_value_fact anywhere other than the inventory. I tested with group_vars and extra vars, both of these seem to work. Thatā€™s not right - I inadvertently replaced inv_value_fact: "{{ hostvars['localhost'].prefix_url + '/special-value' }}" with inv_value: "{{ prefix_url + '/special-value' }}", which fixes it in inventory too.

2 Likes