Ansible: Get all the IP addresses of a group
Asked Answered
S

10

70

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,')}}"
Springwood answered 31/3, 2016 at 9:7 Comment(2)
For anyone using version 2.0+ ansible_ssh_host has been deprecated. This solution still works but you need to replace it with ansible_hostPollaiuolo
You wrote you are not too happy with your own anser. You have been pretty close to the best practice. Please set McKelvins as the accepted one now. https://mcmap.net/q/278189/-ansible-get-all-the-ip-addresses-of-a-group Thank you.Exaltation
S
2

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,')}
Springwood answered 31/3, 2016 at 11:9 Comment(1)
@McKelvin's answer should be the accepted solution, because it's far superior to this one and the recommended way by the Ansible official documentation to do it.Chantalchantalle
C
100

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)

Cree answered 8/10, 2016 at 13:8 Comment(4)
Note that the alternative only works if all servers have all the main interface set to eth0Incorporating
and note also that ansible_ssh_host has been renamed ansible_host since ansible 2.0Incorporating
If you need to add some suffix to each item in the result list (for example, the port number) you can use: "{{ groups['backend_servers'] | map('extract', hostvars, ['ansible_host']) | map('regex_replace', '^(.*)', '\\1:80') | list }}"Cotter
For the second part ansible_default_ipv4 might be more reliable, since interface names have become unpredictable....Discommode
I
15

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 %}
Inhumanity answered 31/3, 2016 at 11:41 Comment(1)
Creating a task for that seems a bit overkill.Springwood
U
9

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(',') }}"
Upchurch answered 1/5, 2020 at 23:9 Comment(0)
M
3

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
Meant answered 11/4, 2018 at 17:3 Comment(0)
R
3
- 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"]
Restraint answered 16/10, 2018 at 16:33 Comment(0)
S
2

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,')}
Springwood answered 31/3, 2016 at 11:9 Comment(1)
@McKelvin's answer should be the accepted solution, because it's far superior to this one and the recommended way by the Ansible official documentation to do it.Chantalchantalle
B
1

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.

Bigoted answered 6/9, 2017 at 14:32 Comment(0)
S
1

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

Schnozzle answered 25/2, 2020 at 15:39 Comment(0)
T
1

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 ?

Trumpery answered 17/11, 2021 at 9:19 Comment(1)
strangely enough I was not able to get this to work elsewhere. In the end I used a solution using hostvars taken from serverfault.com/questions/962040/…Trumpery
T
0

##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
Twelfth answered 3/1, 2022 at 7:9 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Subversion

© 2022 - 2024 — McMap. All rights reserved.