Ansible pass fact between hosts in the same playbook
Asked Answered
G

3

6

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 }}"
Gillian answered 6/7, 2021 at 18:31 Comment(0)
D
7

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'] }}"
Discontent answered 6/7, 2021 at 18:49 Comment(2)
This doesn't work I'm getting "The task includes an option with an undefined variable. The error was: "hostvars['host1']" is undefined"Gillian
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?Ploy
R
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.
Rubricate answered 7/7, 2021 at 6:30 Comment(0)
W
0

TL;DR

Amazing answer by Gary Lopez.
Supplying a bit more information to whoever finds this useful:
Please note that in order to reference other hosts' variables, they need to be defined first.
This is so ansible can access the cache and actually access those variables, because they were collected (e.g. Gathering Facts task was run on hosts).

This information is displayed in the Ansible documentation here : https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_vars_facts.html#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

So, make sure to plan your playbooks with variable caching in mind, to allow them to be referenced later on, just like Gary Lopez wrote :

- name: First Playbook
  gather_facts: no
  hosts: host1
  tasks:
  - set_fact:
      fact1: "foo"

- name: Second Playbook
  hosts: host2
  gather_facts: no
  tasks:
  - debug:
      msg: "{{ hostvars['host1']['fact1'] }}"

It took me time to find and understand this information, probably due to language gap :)

Wargo answered 12/9, 2024 at 18:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.