Creating an Ansible Inventory file using Jinja templating

While there are lots of tools in Ansible for generating an inventory file dynamically, in a system like this, you might want to be able to perform additional operations against the same cluster. For example, once the cluster has been running for a few months, you might want to do a Yum update. Eventually, you want to de-provision. Thus, having a remote record of what machines make up a particular cluster can be very useful. Dynamic inventories can be OK, but often it takes time to regenerate the inventory, and that may slow down an already long process, especially during iterated development.

So, I like to generate inventory files. These are fairly simple files, but they are not one of the supported file types in Ansible. Ansible does support ini files, but the inventory files have maybe lines that are not in key=value format.

Instead, I use Jinja formatting to generate inventory files, and they are pretty simple to work with.

UPDATE: I jumped the gun on the inventory file I was generating. The template and completed inventory have been corrected.

To create the set of hosts, I use the OpenStack server (os_server) task, like this:

- name: create servers
  os_server:
    cloud: "{{ cloudname }}"
    state: present
    name: "{{ item }}.{{ clustername }}"
 
    image: rhel-guest-image-7.4-0
    key_name: ayoung-pubkey
    timeout: 200
    flavor: 2
    security_groups:
      - "{{ securitygroupname }}"
    nics:
      -  net-id:  "{{ osnetwork.network.id }}"
         net-name: "{{ netname }}_network" 
    meta:
      hostname: "{{ netname }}"
  with_items: "{{ cluster_hosts }}"
  register: osservers
 
- file:
    path: "{{ config_dir }}"
    state: directory
    mode: 0755
 
- file:
    path: "{{ config_dir }}/deployments"
    state: directory
    mode: 0755
 
- file:
    path: "{{ cluster_dir }}"
    state: directory
    mode: 0755
 
- template:
    src: inventory.ini.j2
    dest: "{{ cluster_dir }}/inventory.ini"
    force: yes
    backup: yes

A nice thing about this task is, whether it is creating new server or not, it produces the same output, which is a json object that has the server data in an array.

The following template is my current fragment.

[all]
{% for item in osservers.results %}
{{ item.server.interface_ip }}
{% endfor %}
 
{% for item in osservers.results %}
[{{ item.server.name }}]
{{ item.server.interface_ip  }}
 
{% endfor %}
 
 
[ipa]
{% for item in osservers.results %}
{% if item.server.name.startswith('idm')  %}
{{ item.server.interface_ip  }}
{% endif %}
{% endfor %}
 
 
 
[all:vars]
ipa_server_password={{ ipa_server_password }}
ipa_domain={{ clustername }}
deployment_dir={{ cluster_dir }}
ipa_realm={{ clustername|upper }}
cloud_user=cloud-user
ipa_admin_user_password={{  ipa_admin_password }}
ipa_forwarder={{ ipa_forwarder }}
lab_nameserver1={{ lab_nameserver1 }}
lab_nameserver2={{ lab_nameserver2 }}

I keep the variable definitions in a separate file. This produces an inventory file that looks like this:

[all]
10.11.95.161
10.11.95.149
10.11.95.152
10.11.95.159
 
[idm.ayoung.rdusalab]
10.11.95.161
 
[master.ayoung.rdusalab]
10.11.95.149
 
[node0.ayoung.rdusalab]
10.11.95.152
 
[node1.ayoung.rdusalab]
10.11.95.159
 
 
 
[ipa]
10.11.95.161
 
 
 
[all:vars]
ipa_server_password=FreeIPA4All
ipa_domain=ayoung.rdusalab
deployment_dir=/home/ayoung/rippowam/deployments/ayoung.rdusalab
ipa_realm=AYOUNG.RDUSALAB
cloud_user=cloud-user
ipa_admin_user_password=FreeIPA4All
ipa_forwarder=192.168.52.3
lab_nameserver1=8.8.8.8
lab_nameserver2=8.8.8.7

My next step is to create a host group for all of the nodes (node0 node1) based on a shared attribute. I probably will do that by converting the list of hosts to a dictionary keyed by hostname, and have the name of the groups as the value.

3 thoughts on “Creating an Ansible Inventory file using Jinja templating

  1. Hi Adam,

    Great Post. Very helpful and very useful. This saved a day of work for me 🙂
    Another quick question :

    This is my host file :
    [dev]
    server1
    server2
    server3
    server4
    server5

    [dev-z]
    server1
    server2
    server3

    [dev-bus]
    server1
    server2

    [dev-bus_ui]
    server1

    [dev-super]
    server4
    server5

    Using the host file, How to create a template to get the resultant something like this ?

    z.servers:
    – “server1”
    – “server2”
    – “server3”
    n.servers: [“server1″,”server2”]

    Any help/suggestion ?

  2. Hi,

    Thanks for the post! It helped a lot.

    This works fine if I am using two playbooks; one for generating inventory and second one where I use this inventory and run my tasks. But I am getting stuck when I am trying incorporate all this in one playbook as ansible parses the inventory at the start of the first play. Since the inventory is only generated after first play, this method is giving me an error.

    Please let me know if it is possible or what I am doing wrong here. Thanks again!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.