5

I'm trying to pass a fact from host1 to host2, but when ansible reaches hosts2 it returns "undefined variable":

- name: some playbook
  gather_facts: false
  hosts: host1
  tasks:
    - set_fact:
        fact1: "foo"

- hosts: host2
  gather_facts: false
  tasks:
    - debug:
        msg: "{{ fact1 }}"
Kevin C
  • 4,851
  • 8
  • 30
  • 64
Bugs Bunny
  • 365
  • 5
  • 19

2 Answers2

6

set_fact module sets host variables, You could access to those variable with hostvars.

- name: some playbook
  gather_facts: no
  hosts: host1
  tasks:

  - set_fact:
      fact1: "foo"

- hosts: host2
  gather_facts: no
  tasks:

  - debug:
      msg: "{{ hostvars['host1']['fact1'] }}"
gary lopez
  • 1,823
  • 7
  • 15
  • This doesn't work I'm getting "The task includes an option with an undefined variable. The error was: "hostvars['host1']" is undefined" – Bugs Bunny Jul 07 '21 at 08:22
  • this is exactly what i needed, to grab multiple hosts data to combine for use on each host it ran on. thanks! @bugs bunny, i think maybe you didnt specify your real hostname in place of `host1`? – blamb Nov 01 '22 at 20:36
1

In general, this use case might be solved in three steps. Create, share, and use the variables. For example, given the inventory

shell> cat hosts
host1
host2

the playbook works as expected

- name: Run single host and create facts
  hosts: host1
  tasks:
    - set_fact:
        fact1: foo

- name: Share facts among all hosts
  hosts: all
  tasks:
    - set_fact:
        fact1: "{{ hostvars.host1.fact1 }}"
      run_once: true

- name: Use shared facts
  hosts: host2
  tasks:
    - debug:
        var: fact1

If you extend the use case to "passing facts from a group of hosts to another group of hosts" the problem is how to efficiently share the variables? Create a playbook dynamically. For example, given the inventory

shell> cat hosts
host1
host2
host3
host4

let host1 and host4 create the variables and host2 and host3 use them. Create the template

shell> cat share-vars.yml.j2
- name: Share facts among all hosts
  hosts: all
  tasks:
    - set_fact:
{% for i in share_vars %}
        {{ i.var }}: "{{ lbr }} hostvars.{{ i.host }}.{{ i.var }} {{ rbr }}"
{% endfor %}
      run_once: true

and the playbook

shell> cat create-share-vars.yml
- name: Create playbook share-vars.yml
  hosts: localhost
  vars:
    share_vars:
      - {host: host1, var: fact1}
      - {host: host4, var: fact4}
    lbr: "{{ '{{' }}"
    rbr: "{{ '}}' }}"
  tasks:
    - template:
        src: share-vars.yml.j2
        dest: share-vars.yml

gives

shell> cat share-vars.yml
- name: Share facts among all hosts
  hosts: all
  tasks:
    - set_fact:
        fact1: "{{ hostvars.host1.fact1 }}"
        fact4: "{{ hostvars.host4.fact4 }}"
      run_once: true

Import this playbook, for example

shell> cat playbook.yml
- hosts: host1
  tasks:
    - set_fact:
        fact1: foo

- hosts: host4
  tasks:
    - set_fact:
        fact4: bar

- import_playbook: share-vars.yml

- hosts: host2,host3
  tasks:
    - debug:
        var: fact1
    - debug:
        var: fact4

gives (abridged)

PLAY [host2,host3] *********************************************************

TASK [debug] ***************************************************************
ok: [host2] => 
  fact1: foo
ok: [host3] => 
  fact1: foo

TASK [debug] ***************************************************************
ok: [host2] => 
  fact4: bar
ok: [host3] => 
  fact4: bar

Notes

  • What is the advantage of the template? Change the variable share_vars and rebuild the imported play when needed. Don't change the code.
  • In Ansible, it's not possible to substitute the left-hand side of an assignment, i.e. no symbolic reference.
  • Put "gather_facts: false" to the plays if you don't need it.
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63