Rewriting the inventory file using ranges

Hi folks,

I would like to figure out a way to rewrite my inventory file after processing. In theory it shouldn’t be hard to simply dump the ansible_play_hosts_all variable, but it won’t produce the results I need.

Consider this inventory file:

[group_a]
host0[10:50].example.com
[group_a:vars]
this=that
[group_b]
host0[60:90].example.com

I want do be able to load this inventory, do the needful, figure out which hosts to exclude, then write the inventory file back. So the end result should be something like that:

[group_a]
host0[10:21].example.com
host0[29:35].example.com
host0[37:50].example.com
[group_a:vars]
this=that
[group_b]
host0[60:80].example.com

The problem with simply dumping ansible_play_hosts_all is that it will miss the file formatting, specially in what regards to the numbers ranges.

Is there a smart way to do that, other that writing a bunch of custom Python? (I’m fine if the inventory needs to be converted to YAML as well.)

Ive done this before, but it involved writing custom python. Im not sure theres an existing “thing” out there to do this.

ansible-inventory -i <inventory> --yaml --export --list will give you a decompressed yaml inventory. That would be helpful, but passing the hosts in and maintaining group membership might be challenging. Plus decompressed inventories can get long

You could also consider moving the group variables out to dedicated group_vars/ files so you only need to worry about hosts instead of hosts + vars

Yes, moving the variables to different files did cross my mind, but keeping up with the host list is the challenging part. I’m really trying to not use decompressed host lists.

You showed how you got your decompressed inventory, but did you figure out how to “compress” it back?

ive been messing with a playbook. This works, but i think theres room for improvement

- hosts: all
  gather_facts: false
  tasks:
    - command: ansible-inventory -i {{ inventory_file }} --export --list
      register: _old_inventory_output
      run_once: true
      delegate_to: localhost

    # assuming you do something to change the host list here.
    # im just filtering out hosts in group_a without 2 in the name
    - set_fact:
        _old_inventory: "{{ _old_inventory_output.stdout | from_json }}"
      run_once: true
      delegate_to: localhost
    - set_fact:
        _new_group_a: >-
          {{ _old_inventory['group_a']['hosts'] |
          map('regex_search', '.*\d+2.*') | select('string') |
          zip_longest([], fillvalue={}) | community.general.dict
          }}
        _new_group_b: >-
          {{ _old_inventory['group_b']['hosts'] |
          zip_longest([], fillvalue={}) | community.general.dict
          }}
      run_once: true
      delegate_to: localhost

    # merge the new hosts into a new inventory
    - set_fact:
        _new_inventory:
          all:
            children:
              group_a: {hosts: "{{ _new_group_a }}"}
              group_b: {hosts: "{{ _new_group_b }}"}

      run_once: true
      delegate_to: localhost

    # write out and load the new inventory
    - copy:
        dest: /tmp/new_inv
        content: "{{ _new_inventory | to_nice_yaml }}"
      run_once: true
      delegate_to: localhost
    - command: ansible-inventory -i /tmp/new_inv --export --list
      register: _new_inventory_output
      run_once: true
      delegate_to: localhost
    - debug:
        var: _new_inventory_output.stdout | from_json
      run_once: true
      delegate_to: localhost
1 Like

That is the part that Im not sure how to do

While there’s probably a way to do this, it’s going to be very messy any way you do it.

I would rather ask instead, that if you have so many hosts in such a large range, where do your hosts live? Presumably, they are VM’s running on a enterprise grade virtualization cluster, or on a cloud solution. They might also be part of a management system of some sort. Even if it isn’t “enterprise grade”, if any of these things are true, there’s probably a much easier and more reliable way to dynamically source these hosts and construct their groups ahead of time instead of processing them after the fact.

Sure, I know I could use some kind of dynamic inventory, but that’'s when the hosts already exist. In a situation that I need to regularly provision and de-provision them, I need the hostnames (and the build variables) in a static place, not in a provider API.

Even when the host doesn’t exist in a dynamic inventory, that doesn’t mean it can’t be provisioned dynamically. And the variables you need can still be static and assigned programmatically to the dynamic sources.

That said, if you really want a “static” generated list, you might consider using the ansible.builtin.script inventory plugin. This would let you generate your static inventory in whatever way you wish.

The script module is what I am trying to avoid. I wanted something that is “Ansible native”, instead of writing my own.

If there isn’t one, maybe I need to re-think the overall project.

Thanks.