6

I need to generate some unique id in ansible. The generation works but accessing the variable results in some "not expected" behaviour.

The playbook is very simple:

---
- hosts: localhost
  vars:
    - var1: "{{ 99999999 | random }}"
  tasks:
    - debug: msg="{{ var1 }}"
    - debug: msg="{{ var1 }}"
    - debug: msg="{{ var1 }}"

I expected to always have the same output but the reality is different:

ansible-playbook -i localhost setup-env-test.yml
[WARNING]: Unable to parse .... as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************* ok: [localhost]

TASK [debug] ******************************************************************************************************************************* ok: [localhost] => { "msg": "23317042" }

TASK [debug] ******************************************************************************************************************************* ok: [localhost] => { "msg": "23320954" }

TASK [debug] ******************************************************************************************************************************* ok: [localhost] => { "msg": "96866238" }

PLAY RECAP ********************************************************************************************************************************* localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

I looks like the variable is reevaluated on each access.

I know that reevaluation takes places when the context changes, e.g. entering a role, but this is not the case here. I also know that by using set_fact this behaviour changes and the variable content is not evaluated again.

Can anybody give me a hint why the reevaluation is taking place? It would be nice to find the ansible documentation which explains this.

1 Answers1

9

See Lazy Evaluation. Quoting:

In general, Ansible evaluates any variables in playbook content at the last possible second, which means that if you define a data structure that data structure itself can define variable values within it, and everything “just works” as you would expect. This also means variable strings can include other variables inside of those strings.

See also Discovering variables. Quoting from Information about Ansible: magic variables:

With hostvars, you can access variables defined for any host in the play, at any point in a playbook. You can access Ansible facts using the hostvars variable too, but only after you have gathered (or cached) facts. Note that variables defined at play objects are not defined for specific hosts and therefore are not mapped to hostvars.

The below play

- hosts: localhost
  vars:
    var1: "{{ 99999999 | random }}"
  tasks:
    - debug:
        var: hostvars.localhost.var1

shows there is no var1 in the localhost hostvars

  hostvars.localhost.var1: VARIABLE IS NOT DEFINED!

When you evaluate var1

    - debug: msg="{{ var1 }}"
    - debug: msg="{{ var1 }}"
    - debug: msg="{{ var1 }}"

This evaluates the variable var1 three times. Gives (abridged)

  msg: '60020602'
  msg: '40068913'
  msg: '13406670'

Use set_fact if you want to evaluate a variable once

    - set_fact:
        var1: "{{ var1 }}"
    - debug:
        var: hostvars.localhost.var1
- debug: msg="{{ var1 }}"
- debug: msg="{{ var1 }}"
- debug: msg="{{ var1 }}"

This will create ("instantiate") hostvars.localhost.var1. Afterward, the instantiated hostvar will be used

  hostvars.localhost.var1: '15550984'
  msg: '15550984'
  msg: '15550984'
  msg: '15550984'

Example of a complete playbook for testing

- hosts: localhost

vars:

var1: "{{ 99999999 | random }}"

tasks:

- debug:
    var: hostvars.localhost.var1

- debug: msg="{{ var1 }}"
- debug: msg="{{ var1 }}"
- debug: msg="{{ var1 }}"
- debug:
    var: hostvars.localhost.var1

- set_fact:
    var1: "{{ var1 }}"
- debug:
    var: hostvars.localhost.var1

- debug: msg="{{ var1 }}"
- debug: msg="{{ var1 }}"
- debug: msg="{{ var1 }}"