Ansible: Get all the IP addresses of a group

AnsibleJinja2Ansible Playbook

Ansible Problem Overview


Let's imagine an inventory file like this:

node-01 ansible_ssh_host=192.168.100.101
node-02 ansible_ssh_host=192.168.100.102
node-03 ansible_ssh_host=192.168.100.103
node-04 ansible_ssh_host=192.168.100.104
node-05 ansible_ssh_host=192.168.100.105

[mainnodes]
node-[01:04]

In my playbook I now want to create some variables containing the IP addresses of the group mainnodes:

vars:
  main_nodes_ips: "192.168.100.101,192.168.100.102,192.168.100.103,192.168.100.104"
  main_nodes_ips_with_port: "192.168.100.101:3000,192.168.100.102:3000,192.168.100.103:3000,192.168.100.104:3000"

This is what I got so far:

vars:
  main_nodes_ips: "{{groups['mainnodes']|join(',')}}"
  main_nodes_ips_with_port: "{{groups['mainnodes']|join(':3000,')}}"

but that would use the host names instead of the IP addresses.

Any ideas how this could be done?

Update:

looking at the docs for a while, I think this would allow me to loop through all the ip adresses:

{% for host in groups['mainnodes'] %}
    {{hostvars[host]['ansible_ssh_host']}}
{% endfor %}

But I just can't figure out how to create an array that holds all these IPs. So that I can use the |join() command on them.

Update2:
I just thought I had figured it out... but it turns out that you cannot use the {% %} syntax in the playbook... or can I? Well in the vars section it didn't. :/

vars:
  {% set main_nodes_ip_arr=[] %}
  {% for host in groups['mesos-slave'] %}
     {% if main_nodes_ip_arr.insert(loop.index,hostvars[host]['ansible_ssh_host']) %} {% endif %}
  {% endfor %}
  main_nodes_ips: "{{main_nodes_ip_arr|join(',')}}"
  main_nodes_ips_with_port: "{{main_nodes_ip_arr|join(':3000,')}}"

Ansible Solutions


Solution 1 - Ansible

I find the magic map extract here.

main_nodes_ips: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_host']) | join(',') }}"
main_nodes_ips_with_port: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_host']) | join(':3000,') }}:3000"

An alternative(idea comes from here):

main_nodes_ips: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_eth0', 'ipv4', 'address']) | join(',') }}"

(Suppose the interface is eth0)

Solution 2 - Ansible

i came across this problem a while back and this is what i came up with (not optimal, but it works)

---
# playbook.yml
  - hosts: localhost
    connection: local

    tasks:
      - name: create deploy template
        template:
          src: iplist.txt
          dest: /tmp/iplist.txt
      - include_vars: /tmp/iplist.txt

      - debug: var=ip

and the template file is

ip:
{% for h in groups['webservers'] %}
 - {{ hostvars[h].ansible_ssh_host }}
{% endfor %}

Solution 3 - Ansible

This do the trick for me. Not relying on the interface name

- main_nodes_ips: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_default_ipv4', 'address']) | join(',') }}"

Solution 4 - Ansible

- name: Create List of nodes to be added into Cluster
  set_fact: nodelist={%for host in groups['mygroup']%}"{{hostvars[host].ansible_eth0.ipv4.address}}"{% if not loop.last %},{% endif %}{% endfor %}

 - debug: msg=[{{nodelist}}]

 - name: Set Cluster node list in config file
   lineinfile:
         path: "/etc/myfonfig.cfg"
         line: "hosts: [{{ nodelist }}]"

as results you will have the following line in config file:

hosts: ["192.168.126.38","192.168.126.39","192.168.126.40"]

Solution 5 - Ansible

I got it to work on my own now. I'm not too happy about the solution, but it will do:

main_nodes_ips: "{% set IP_ARR=[] %}{% for host in groups['mainnodes'] %}{% if IP_ARR.insert(loop.index,hostvars[host]['ansible_ssh_host']) %}{% endif %}{% endfor %}{{IP_ARR|join(',')}}"
main_nodes_ips_with_port: "{% set IP_ARR=[] %}{% for host in groups['mainnodes'] %}{% if IP_ARR.insert(loop.index,hostvars[host]['ansible_ssh_host']) %}{% endif %}{% endfor %}{{IP_ARR|join(':3000,')}

Solution 6 - Ansible

I've done this by using ansible facts in a playbook. This playbook takes ansible_all_ipv4_addresses list and ansible_nodename (which is actually fully qualified domain name), iterates through all hosts and saves the data in localpath_to_save_ips file on your localhost. You can change localpath_to_save_ips to the absolute path on your localhost.

---
- hosts: all
  become: yes
  gather_facts: yes

  tasks:
  - name: get ip
    local_action: shell echo {{ ansible_all_ipv4_addresses }} {{ ansible_nodename }} >> localpath_to_save_ips

Solution 7 - Ansible

I found the "only way" to acceess other group's ip's, when any of the following is true:

  • some members are not bootstrapped by ansible yet
  • using serial
  • group is not part of playbook

Is as follows:

{% set ips=[] %}{% for host in groups['othergroup'] %}{% if ips.append(lookup('dig', host)) %}{% endif %}{% endfor %}{{ ips }}

Requires dnspython on the machine running ansible, install via

sudo apt-get install python-dnspython

If anyone knows a better way given the conditions, I'd love to get rid of this abomination.

Solution 8 - Ansible

this is what I did in order to not be relied on eth0 (thanks to ADV-IT's answer):

- name: gathering facts
  hosts: mainnodes
  gather_facts: true

- hosts: mainnodes
  tasks:
    - name: Create List of nodes
      set_fact: nodelist={%for host in groups['mainnodes']%}"{{hostvars[host]['ansible_env'].SSH_CONNECTION.split(' ')[2]}}"{% if not loop.last %},{% endif %}{% endfor %}

Solution 9 - Ansible

I ran into a similar problem getting the IP address of a node in another group. Using a construct like: the_ip: "{{ hostvars[groups['master'][0]]['ansible_default_ipv4'].address }}" works only when running the group master, which was not part of my playbook (I was running on localhost). I have overcome the problem by adding an extra play to playbook, like:

- hosts: master
  gather_facts: yes
  become: no
  vars: 
    - the_master_ip: "{{ hostvars[groups['master'][0]]['ansible_default_ipv4'].address }}"
  tasks:
  - debug: var=the_master_ip
  - set_fact: the_ip={{ the_master_ip }}

After which I can use the the_ip in the next play of the playbook.

This may also solve the abomination mentioned by @Petroldrake ?

Solution 10 - Ansible

##Just fetch Ip's using -ansible_default_ipv4.address- & redirect to a local file & then use it

  • name: gathering_facts hosts: hosts gather_facts: true tasks:

    • name: Rediret to the file shell: echo "{{ansible_default_ipv4.address}}" >>ipss.txt delegate_to: localhost

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionForivinView Question on Stackoverflow
Solution 1 - AnsiblemckelvinView Answer on Stackoverflow
Solution 2 - Ansibleuser2599522View Answer on Stackoverflow
Solution 3 - AnsibleScandinaveView Answer on Stackoverflow
Solution 4 - AnsibleADV-ITView Answer on Stackoverflow
Solution 5 - AnsibleForivinView Answer on Stackoverflow
Solution 6 - AnsibleIlshat KarazbayevView Answer on Stackoverflow
Solution 7 - AnsiblePetroldrakeView Answer on Stackoverflow
Solution 8 - Ansibleahmadali shafieeView Answer on Stackoverflow
Solution 9 - AnsibleMonger39View Answer on Stackoverflow
Solution 10 - AnsibleVishalView Answer on Stackoverflow