Ansible - Getting a list of IP addresses for a specific group

Posted on Sat 22 October 2016 in Computing

I had a very simple problem. I needed to get a list of IP addresses from a group of slave servers in inventory to use as part of a firewall ruleset task in a playbook for a master server (so access was only allowed to the master from the slaves). I knew the IP addresses were accessible via hostvars but as I only run this playbook on the master (I don't use a site.yml type god playbook) I knew I needed to run something on the slaves first to gather the facts that build hostvars. Also I wasn't using a template, so that ruled out using for loops in jinja2.

My approach to solving this was:

  • Create a playbook the runs on the slave servers but does nothing

  • Include this playbook in the master server playbook

  • Filter the hostvars data to produce a list consisting only of the IPv4 addresses for a specific host group

Note: You need ansible >= 2.1 for this to work for the map extract feature

slave_do_nothing.yml

---
# Does nothing but will gather facts for later use
- hosts: slave
  tasks: []


master_server_test.yml
---
- include: slave_do_nothing.yml
- hosts: master
  tasks:
    - name: Do something with the slave ip address now in item
      set_fact:
         n00b: "{{ n00b| default([]) + [item] }}"
      with_items:
        "{{ groups['slave']|map('extract', hostvars,
        ['ansible_eth0', 'ipv4', 'address'])|list }}"

    - debug: msg="{{ n00b }}"

I've included something else I also discovered along the way which was how to create a list and append items to it. You can see that fact n00b is created and defaults to a empty list and we add each item that is passed from with_items. We then use debug to print it at the end (a list of IP addresses). This should be very useful for debugging tasks in the future.

As a further example here is the task I use in a playbook for an icecast master server to allow inbound connections from the icecast relays.

- name: Allow inbound from icecast relays
  firewalld:
    zone: public
    state: enabled
    permanent: true
    immediate: true
    rich_rule:
      "rule family=ipv4 source address={{ item }}
      port port={{ icecast_port }} protocol=tcp accept"
  with_items:
    "{{ groups['icecast-relay']|map('extract', hostvars,
    ['ansible_eth0', 'ipv4', 'address'])|list }}"