# Facts: Get the name of an interface for a given IP

**URL:** <https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712>\
**Category:** Get Help\
**Tags:** playbook, network\
**Created:** [September 14, 2023, 9:49am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712 "2023-09-14T09:49:11Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 14, 2023, 9:49am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/1 "2023-09-14T09:49:11Z")

</div>

Hello everybody,  
I’ve been searching for a solution a longer time now and hope you can help me.

In our environment we have VM’s with two interfaces, each with several ip addresses and what I’d like to do is to get the interface-name by searching an ip address, just using the ansible-facts.

My test-machine has only one interface and the facts-structure looks like this if I’m right:

```auto
    "ansible_facts": {
        "ansible_interfaces": [
            "lo",
            "ens192",
            "ens192_24"
        ]
        "ansible_ens192": {
            "active": true,
            "device": "ens192",
             "hw_timestamp_filters": [],
            "ipv4": {
                "address": "10.0.0.23",
                "broadcast": "10.0.0.255",
                "netmask": "255.255.254.0",
                "network": "10.0.0.0"
            },
            "ipv4_secondaries": [
                {
                    "address": "10.0.0.24",
                    "broadcast": "10.0.0.255",
                    "netmask": "255.255.255.0",
                    "network": "10.0.0.0"
                }
            ],
     }

```

What I’ve found until now is this solution:

```auto
    - name: Get interface for ip
      set_fact:
        interface_of_ip: "{{ ansible_interfaces | map('regex_replace', '^', 'ansible_') | map('extract', vars) | selectattr('ipv4.address', 'match', '10\\.0\\.0\\.23') | map(attribute='device') | first}}"

```

But in our case the ip address I’m searching for is the secondary ip “10.0.0.24” and replacing “ipv4.address” with “ipv4\_secondaries.address” doesn’t work.

The best solution for me would be, that I can search for an ip, no matter whether it is the first or a secondary ip and get the interface name “ens192” as result.  
But for this actual situation it would suffice to get the interface name of a secondary ip address.

Thank you very much in advance  
Best regards  
Martin

---

<div class="post-metadata">

**Author:** ![IPvSean](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/ipvsean/32/1153_2.png) [@IPvSean](https://forum.ansible.com/u/IPvSean)\
**Post date:** [September 14, 2023, 1:26pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/2 "2023-09-14T13:26:03Z")

</div>

In your ansible facts (You can do a `ansible -m setup localhost`) and see there should also be a list of all IP addresses called `ansible_all_ipv4_addresses` and `ansible_all_ipv6_addresses`

```auto
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "10.195.117.91"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::b86b:8bff:feb3:345f%anpi1",
            "fe80::b86b:8bff:feb3:3460%anpi2",
            "fe80::b86b:8bff:feb3:345e%anpi0",
            "fe80::7ce9:1eff:febb:ba6a%ap1",
            "fe80::1082:4d94:dc22:6e94%en0",
            "fe80::cce:43ff:fe95:4efb%awdl0",
            "fe80::cce:43ff:fe95:4efb%llw0",
            "fe80::5a9d:d125:300f:896d%utun0",
            "fe80::d6b:b252:b2c9:177e%utun1",
            "fe80::ce81:b1c:bd2c:69e%utun2",
            "fe80::9a53:3a0e:4b09:21c4%utun3",
            "fe80::d0e6:77b9:3d13:9e1b%utun4",
            "fe80::b3f2:c58c:7d2d:7267%utun5",
            "fe80::2d50:2c62:adc2:ff8e%utun6"
        ],

```

Will that work better in your situation?

---

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 18, 2023, 7:36am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/3 "2023-09-18T07:36:38Z")

</div>

Thanks for your reply. I was looking for a combination of an ip address and the interface name, so I think the ansible\_ens192 for example is the only place to find it.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 18, 2023, 7:06pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/4 "2023-09-18T19:06:58Z")

</div>

This is as close as I’ve been able to get. It works for me, but I’ve only defined one interface with a single secondary ipv4 address.

```yaml
    - name: Get interface for ip '192.168.254.102'
      ansible.builtin.debug:
        msg: |
          {% set ivals = [] %}
          {% for ifval in ansible_interfaces | map('regex_replace', '^', 'ansible_') | map('extract', vars) %}
          {% set _ = ivals.append({'device': ifval.device,
                                     'ipv4s': [[ifval.ipv4.address | default('0.0.0.0')] +
                                               ifval.ipv4_secondaries | default([]) | map(attribute='address')] | flatten
                                    }
                                   ) %}
          {% endfor %}{{ ivals | subelements('ipv4s') | selectattr('1', 'equalto', '192.168.254.102') | map(attribute='0.device') | first }}

```

---

<div class="post-metadata">

**Author:** ![IPvSean](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/ipvsean/32/1153_2.png) [@IPvSean](https://forum.ansible.com/u/IPvSean)\
**Post date:** [September 18, 2023, 8:39pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/5 "2023-09-18T20:39:52Z")

</div>

Would something like this work?

```auto
---
- hosts: all
  tasks:
    - name: Find the interface with IP 192.168.86.46
      set_fact:
        interface_with_ip: "{{ item.key }}"
      when: "(item.value.ipv4 | selectattr('address', 'equalto', '192.168.86.46') | list) | length > 0"
      with_dict: "{{ ansible_interfaces }}"
      loop_control:
        label: "{{ item.key }}"
    
    - name: Display the interface name
      debug:
        var: interface_with_ip

```

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 18, 2023, 10:04pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/7 "2023-09-18T22:04:52Z")

</div>

Using `{% … %}` always feels like cheating, so I thought I’d give it another go sticking with pure filters. And by “pure” I don’t mean “sinless”, as what I came up with is abominable. But I really don’t see how else to attack such a problem given the handicap Jinja2 puts on coders. It’s like it can’t decide whether it’s a data manipulation language or not. It touts itself as being very powerful, but whenever I apply it to much beyond the simplest problems I end up with, well, stuff like this:

```yaml
    - name: Get interface for ip '192.168.254.102'
      ansible.builtin.debug:
        msg: |
          {{ ( ansible_interfaces
               | zip(
                      ansible_interfaces
                      | map('regex_replace', '^', 'ansible_')
                      | map('extract', vars)
                      | map('dict2items')
                      | map('selectattr', 'key', 'in', ['device', 'ipv4'])
                      | map('items2dict')
                      | product([{'ipv4': {'address': '0.0.0.0'}}])
                      | map('reverse')
                      | map('combine')
                      | map(attribute='ipv4.address')
                    )
             +
               ansible_interfaces
               | zip(
                      ansible_interfaces
                      | map('regex_replace', '^', 'ansible_')
                      | map('extract', vars)
                      | map('dict2items')
                      | map('selectattr', 'key', 'in', ['device', 'ipv4_secondaries'])
                      | map('items2dict')
                      | product([{'ipv4_secondaries': [{'address': '0.0.0.0'}]}])
                      | map('reverse')
                      | map('combine')
                      | subelements('ipv4_secondaries')
                      | map(attribute='1.address')
                    )
             )
             | selectattr('1', 'equalto', '192.168.254.102')
             | flatten
             | first
           }}

```

A large part of it is simply ensuring each interface has non-missing `ipv4` or `ipv4_secondaries` attributes so that the `map(attribute=…)` invocations won’t blow up. The IP address I’m searching for is hard-coded; replace it with a variable if you’re tempted to run it, or worse, use it “for real”.

Hard problems should have elegant solutions. My Jinja2 solutions are typically harder than the original problem, which tells me there’s something wrong with at least one of Jinja2 and me.

---

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 19, 2023, 8:29am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/8 "2023-09-19T08:29:05Z")

</div>

Thank you very much for your replies, I will try it out and give a response 😀

---

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 19, 2023, 11:24am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/9 "2023-09-19T11:24:21Z")

</div>

Trying your code I get this error message:

```auto
msg: with_dict expects a dict

```

---

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 19, 2023, 11:47am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/10 "2023-09-19T11:47:20Z")

</div>

You’re right, I’m also surprised how complicated it is to do what I want while it’s really simple to see as a human looking at the dictionary.

Honestly I don’t understand every detail of your code, but your approach looks good to me.

When I’ve tried it in my playbook, I get this error message:

```auto
items2dict requires a list, got <type 'generator'> instead

```

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 19, 2023, 12:05pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/11 "2023-09-19T12:05:37Z")

</div>

I used to get that a lot. Newer Ansibles do the Right Thing in most of those cases, and I’ve been pleasantly spoiled.  
If your Ansible is old enough to throw that error, toss in a `| list |` in front of `items2dict`.

---

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 19, 2023, 12:34pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/12 "2023-09-19T12:34:01Z")

</div>

yes, unfortunately I will not get a newer version in the near future 😐

I still get the same error:

```auto
  msg: |-
    Unexpected templating type error occurred on ({{ ( ansible_interfaces
         | zip(
                ansible_interfaces
                | map('regex_replace', '^', 'ansible_')
                | map('extract', vars)
                | map('dict2items')
                | map('selectattr', 'key', 'in', ['device', 'ipv4'])
                | list
                | map('items2dict')
                | product([{'ipv4': {'address': '0.0.0.0'}}])
                | map('reverse')
                | map('combine')
                | map(attribute='ipv4.address')
              )
       +
         ansible_interfaces
         | zip(
                ansible_interfaces
                | map('regex_replace', '^', 'ansible_')
                | map('extract', vars)
                | map('dict2items')
                | map('selectattr', 'key', 'in', ['device', 'ipv4_secondaries'])
                | list
                | map('items2dict')
                | product([{'ipv4_secondaries': [{'address': '0.0.0.0'}]}])
                | map('reverse')
                | map('combine')
                | subelements('ipv4_secondaries')
                | map(attribute='1.address')
              )
       )
       | selectattr('1', 'match', '10.0.0.23')
       | flatten
       | first
     }}
    ): items2dict requires a list, got <type 'generator'> instead.

```

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 19, 2023, 3:06pm UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/13 "2023-09-19T15:06:07Z")

</div>

[_Shakes tiny head_] I had forgotten how horrid this could be in Ansible 2.9, but I was able to find an old box around here that has 2.9.18 on it, and got it working there. Not only does it require lots of extra `|list` terms, but also some `|map('list')` terms, and their order matters. Here’s the complete playbook, with a variable `findme` for the IP address to search for. And this one doesn’t blow up when the IP address isn’t associated with any interface. (It just returns `''` in that case.) It still works with newer Ansibles, too. Cheers!

```yaml
---
# interface-name.yml
- name: Determine interface name given primary or secondary ip
  hosts: localhost
  gather_facts: true
  vars:
    findme: 172.27.45.33
  tasks:
    - name: Get interface for ip {{ findme }}
      # All these extra "| list" and "| map('list')" terms are
      # for Ansible 2.9. If there was ever a case for upgrading...!
      ansible.builtin.debug:
        msg: |-
          {{ ( ansible_interfaces
               | zip(
                      ansible_interfaces
                      | map('regex_replace', '^', 'ansible_')
                      | map('extract', vars)
                      | map('dict2items')
                      | map('selectattr', 'key', 'in', ['device', 'ipv4'])
                      | list
                      | map('list')
                      | map('items2dict')
                      | product([{'ipv4': {'address': '0.0.0.0'}}])
                      | map('reverse')
                      | map('list')
                      | map('combine')
                      | map(attribute='ipv4.address')
                    ) | list
             +
               ansible_interfaces
               | zip(
                      ansible_interfaces
                      | map('regex_replace', '^', 'ansible_')
                      | map('extract', vars)
                      | map('dict2items')
                      | map('selectattr', 'key', 'in', ['device', 'ipv4_secondaries'])
                      | list
                      | map('list')
                      | map('items2dict')
                      | product([{'ipv4_secondaries': [{'address': '0.0.0.0'}]}])
                      | list
                      | map('reverse')
                      | list
                      | map('list')
                      | list
                      | map('combine')
                      | list
                      | subelements('ipv4_secondaries')
                      | map(attribute='1.address')
                      | list
                    ) | list
             )
             | selectattr('1', 'equalto', findme)
             | flatten
             | first | default('')
           }}

```

---

<div class="post-metadata">

**Author:** ![mariit](https://avatars.discourse-cdn.com/v4/letter/m/439d5e/32.png) [@mariit](https://forum.ansible.com/u/mariit)\
**Post date:** [September 21, 2023, 9:02am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/14 "2023-09-21T09:02:37Z")

</div>

That looks really crazy and is functioning now for me. Thank you really, really much 😀

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [October 21, 2023, 9:03am UTC](https://forum.ansible.com/t/facts-get-the-name-of-an-interface-for-a-given-ip/712/15 "2023-10-21T09:03:34Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
