Ansible: Use hostvars to Generate Hosts File

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"

8 thoughts on “Ansible: Use hostvars to Generate Hosts File

  1. 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)!

  2. 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 %}

Leave a Reply

Your email address will not be published. Required fields are marked *