Search code examples
ansibleansible-2.xansible-inventory

Single task launch in a group of hosts with the same server address


I have an Inventory file with this format.

group_host:
  hosts:
    d1:
      ansible_host: address-server
    d2:
      ansible_host: address-server
    d3:
      ansible_host: address-server

The server address is the same for all host variables (d1, d2, d3). When I run the playbook, the task will be executed on each variable in this case three times on the same server. How can we make it a condition that the task is only executed once if the addresses-servers in the host group are the same? And how can I make the task run once for every unique server address?


Solution

  • Create a new inventory group in the first play and use it in the second.

    For example, get a list of variables ansible_host

    ansible_hosts: "{{ ansible_play_hosts_all|
                       map('extract', hostvars, 'ansible_host')|
                       list }}"
    

    gives

    ansible_hosts:
      - address-server
      - address-server
      - address-server
    

    Create a dictionary

    ansible_hosts_dict: "{{ dict(ansible_play_hosts_all|zip(ansible_hosts)) }}"
    

    gives

    ansible_hosts_dict:
      d1: address-server
      d2: address-server
      d3: address-server
    

    Create a new group. Convert the dictionary to a list and iterate it using the groupby filter

        - add_host:
            groups: group_host_unique
            host: "{{ item.1.0.key }}"
            ansible_host: "{{ item.0 }}"
          loop: "{{ ansible_hosts_dict|dict2items|groupby('value') }}"
    

    This creates the new inventory group group_host_unique with unique values of ansible_host. The first host is taken if there are more hosts with the same value of ansible_host. As a result, the tasks in the play below are executed only once for hosts with the same server address

    - hosts: group_host_unique
      gather_facts: false
      tasks:
        - debug:
            var: inventory_hostname
    

    gives

    ok: [d1] => 
      inventory_hostname: d1
    

    Example of a complete playbook

    - hosts: group_host
      gather_facts: false
      vars:
        ansible_hosts: "{{ ansible_play_hosts_all|
                           map('extract', hostvars, 'ansible_host')|
                           list }}"
        ansible_hosts_dict: "{{ dict(ansible_play_hosts_all|zip(ansible_hosts)) }}"
      tasks:
        - add_host:
            groups: group_host_unique
            host: "{{ item.1.0.key }}"
            ansible_host: "{{ item.0 }}"
          loop: "{{ ansible_hosts_dict|dict2items|groupby('value') }}"
          run_once: true
    
    - hosts: group_host_unique
      gather_facts: false
      tasks:
        - debug:
            var: inventory_hostname
    

    Test it with various values of ansible_host. For example,

    shell> cat hosts
    group_host:
      hosts:
        d1:
          ansible_host: address-server1
        d2:
          ansible_host: address-server1
        d3:
          ansible_host: address-server2
    

    gives (abridged)

    ok: [d1] => 
      inventory_hostname: d1
    ok: [d3] => 
      inventory_hostname: d3