Search code examples
dynamicansiblejinja2hostvars

Select hosts from Ansible inventory based on value of host variable


I am trying to set up a client and server using Ansible that need to connect to each other (Wireguard client and server in this case). Since the server needs to know the peers that will connect to it, I'm looking to gather a list of hosts that the server will be able to create a peer entry for in its configuration. The list should be dynamically generated (there could be multiple clients connecting to the same server) and generated based on the target_server variable assigned to each client, the value of which should be equal to the inventory_hostname of the server it needs to connect to. The jinja2 expression I am using in the MWE below unfortunately keeps coming up empty, but I'm unsure how to improve it.

Playbook

- name: Set up Wireguard Server
  hosts: servers
  gather_facts: true
  tasks:
    - name: Figure out peers for this server
      become: false
      ansible.builtin.debug:
        var: item
      loop: "{{ groups['servers'] | select('match', 'target_server={{ inventory_hostname }}') | map('extract', hostvars, 'inventory_hostname') | list }}"

Inventory

servers:
  hosts:
    Wireguard-Server:
      ansible_host: 2.3.4.5

clients:
  hosts:
    Wireguard-Client-1:
      ansible_host: 1.2.3.4
      target_server: Wireguard-Server
    Wireguard-Client-2:
      ansible_host: 4.3.2.1
      target_server: Different-Server

Log

TASK [Figure out peers for this server] ********************************************
skipping: [Wireguard-Server] =>
  skipped_reason: No items in the list

Solution

  • Create the dictionary of the clients and target_server

      clients: "{{ dict(groups.clients|
                        zip(groups.clients|
                            map('extract', hostvars, 'target_server'))) }}"
    

    gives

      clients:
        Wireguard-Client-1: Wireguard-Server
        Wireguard-Client-2: Different-Server
    

    Use it to select the matching clients

      my_clients: "{{ clients|dict2items|
                      selectattr('value', 'eq', inventory_hostname)|
                      map(attribute='key') }}"
    

    gives

      my_clients:
      - Wireguard-Client-1
    

    Example of a complete playbook for testing

    - hosts: servers
    
      vars:
    
        clients: "{{ dict(groups.clients|
                          zip(groups.clients|
                              map('extract', hostvars, 'target_server'))) }}"
        my_clients: "{{ clients|dict2items|
                        selectattr('value', 'eq', inventory_hostname)|
                        map(attribute='key') }}"
    
      tasks:
    
        - debug:
            var: clients
    
        - debug:
            var: my_clients
    

    You can select all hosts from the inventory that match target_server

      my_clients: "{{ hostvars|dict2items|
                      selectattr('value.target_server', 'defined')|
                      selectattr('value.target_server', 'eq', inventory_hostname)|
                      map(attribute='key') }}"