There are cases when you need to access variables for another host, including facts that have been gathered about that host.
The Problem
I have 5 Ansible managed hosts in my homelab that I use for testing. I want to use a template to generate the /etc/hosts
file for each managed node. The content of the file that I want to end up with is below:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 10.11.1.62 ansible2.hl.local ansible2 10.11.1.63 ansible3.hl.local ansible3 10.11.1.64 ansible4.hl.local ansible4 10.11.1.65 ansible5.hl.local ansible5 10.11.1.66 ansible6.hl.local ansible6
While I can easily retrieve facts about the host the playbook is run on, I need a way of accessing facts that have been gathered about other hosts as well.
The Solution: Magic Variables
The hostvars
magic variable allows you access variables for another host, including facts that have been gathered about that host. You can access host variables at any point in a playbook.
If a server needs to use the value of a “fact” from another node, it’s easy to do so within a template templates/hosts.j2
:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 {{ hostvars['ansible2.hl.local']['ansible_default_ipv4']['address'] }} {{ hostvars['ansible2.hl.local']['ansible_fqdn'] }} {{ hostvars['ansible2.hl.local']['ansible_hostname'] }} {{ hostvars['ansible3.hl.local']['ansible_default_ipv4']['address'] }} {{ hostvars['ansible3.hl.local']['ansible_fqdn'] }} {{ hostvars['ansible3.hl.local']['ansible_hostname'] }} {{ hostvars['ansible4.hl.local']['ansible_default_ipv4']['address'] }} {{ hostvars['ansible4.hl.local']['ansible_fqdn'] }} {{ hostvars['ansible4.hl.local']['ansible_hostname'] }} {{ hostvars['ansible5.hl.local']['ansible_default_ipv4']['address'] }} {{ hostvars['ansible5.hl.local']['ansible_fqdn'] }} {{ hostvars['ansible5.hl.local']['ansible_hostname'] }} {{ hostvars['ansible6.hl.local']['ansible_default_ipv4']['address'] }} {{ hostvars['ansible6.hl.local']['ansible_fqdn'] }} {{ hostvars['ansible6.hl.local']['ansible_hostname'] }}
The playbook:
--- - name: Generate Hosts File hosts: all become: true gather_facts: true vars: my_file: /etc/hosts my_template: templates/hosts.j2 tasks: - name: Create "{{ my_file }}" template: src: "{{ my_template }}" dest: "{{ my_file }}" owner: root group: root mode: "0644"
can you add to your example your hosts. yml otherwise it is hard to understand what is what
The playbook runs on all managed hosts. What’s hard to understand exactly?
you are assuming that all readers are at a higher level of understanding. There are students also learning from your posts. So it is very naive to assume that all readers are at the same level.
Thanks for your feedback, much appreciated. I am indeed making an assumption that a person leaving a comment has some understanding about the topic, however, we are all different and obviously have different experience when it comes to technology. If you are using my material to study and learn then that’s terrific, I’m really glad, and thank you for that! However, in all honesty, I don’t personally think that it’s the best place to start if you’re new to the subject. There are better guides/training materials online that will cover the basics, explain the subject in more details and even provide you with step-by-step instructions. I can tell from experience because I used some of them myself when learning Ansible.
Hi Tomas,
Thanks for sharing this tutorial, it really helped me. However, allow me to do a suggestion?
On my inventory file (/etc/ansible/hosts), I have 3 groups:
—————–
[lb]
lb01
[www]
www01
www02
[db]
db01
—————–
And on the template file templates/hosts.j2, I did a for loop, which is quite handy… specially for a real world situation where many times I have more groups and more hosts. Adding host line by line on the template kinda of annoys me, sorry! :X
——————
templates/hosts.j2
#
# Managed by Ansible. DO NOT edit it by hand!
#
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# Load Balancer Server
{% for host in groups[‘lb’] -%}
{{ hostvars[host][‘ansible_default_ipv4’][‘address’] }} {{ hostvars[host][‘ansible_fqdn’] }} {{ hostvars[host][‘ansible_hostname’] }}
{% endfor %}
# Web Server
{% for host in groups[‘www’] -%}
{{ hostvars[host][‘ansible_default_ipv4’][‘address’] }} {{ hostvars[host][‘ansible_fqdn’] }} {{ hostvars[host][‘ansible_hostname’] }}
{% endfor %}
# Database Server
{% for host in groups[‘db’] -%}
{{ hostvars[host][‘ansible_default_ipv4’][‘address’] }} {{ hostvars[host][‘ansible_fqdn’] }} {{ hostvars[host][‘ansible_hostname’] }}
{% endfor %}
——————
Hope it may help someone! :)
Cheers,
Jhonatan Rampin
Thanks! Yea, you can easily use a loop. I found several examples on docs.ansible.com, they are really handy in real life situations, but a bit overkill for my 5 node homelab. And it’s less typing (copy/paste)!
Hi Jhonatan,
You can use:
{% for host in groups[βallβ] }
Awesome!
Tomas please give refference where you have found that information on docs.ansible.com. I’d love to see some if and for templates to better understand how to use them.
Hi, sure, absolutely. This is what I was talking about:
https://docs.ansible.com/ansible/2.7/user_guide/playbooks_variables.html#accessing-information-about-other-hosts-with-magic-variables
Here is my jinja2 template:
{% for host in groups.all %}
{{ hostvars[host].ansible_default_ipv4.address }} {{ hostvars[host].ansible_fqdn }} {{ hostvars[host].ansible_hostname }}
{% endfor %}
More awesome!
Hi sir,
I got a fatal error while running . yml file,
hostvars[‘host’] is undefined
Can you please tell me the reason for this error.
There is no syntax errors
Hi please remove ‘ ‘ from host
hostvar[host]
Mention the group in the yml file you mentioned in the jinja2 file
could you pls write more about
Mention the group in the yml file you mentioned in the jinja2 file
regarding:
Hi please remove β β from host
Hi everyone – How can I put configuration to Catalyst 9300 which connected to my laptop via COM3 port. ThanksS
This is my’s jinja2 templates for vagrant virtual machine.
{% for host in groups.all %}
{{ hostvars[host].ansible_all_ipv4_addresses | difference([“10.0.2.15”]) | ipaddr | first }} {{ hostvars[host].ansible_fqdn }} {{ hostvars[host].ansible_hostname }}
{% endfor %}
I’ve been working through Sander’s course for RHCE 8, and he points his students to one of the FAQs in Ansibles docs:
This was a great help in my studies for the Red Hat Certified Engineer EX294 exam.