Search code examples
ansiblejinja2

Loop over a counter across hosts in ansible


I am trying to create a configuration file in a few target machines. In the local machine, I am supposed to assign a private IP to each host incrementally.

My stupid brain thought this was simple, so I did this but this failed miserably. I think this is a variable scope problem between the hosts. So, incrementing them within the template is not going to help. Need a way to use a variable that could be present in all hosts, and can be incremented from within the template.

Any thoughts?

- name: mock Playbook
  hosts: all
  vars:
    counter: 1
  tasks:
    - name: Template configuration
      template:
        src: server.j2
        dest: /tmp/server.conf

Here is my template file(server.j2):

Address = {{ '10.0.0.' + counter |string }}

{% set counter = counter + 1 %}

Now, all the hosts have the same IP assigned in the config file that is:

#in the 1st host
cat /tmp/server.conf
Address = 10.0.0.1

#in the 2nd host
cat /tmp/server.conf
Address = 10.0.0.1

#in the 3rd host
cat /tmp/server.conf
Address = 10.0.0.1

I was hoping something like this:

#in the 1st host
cat /tmp/server.conf
Address = 10.0.0.1

#in the 2nd host
cat /tmp/server.conf
Address = 10.0.0.2

#in the 3rd host
cat /tmp/server.conf
Address = 10.0.0.3

inventory file:

all:
  children:
    server:
      hosts:
        vm1:
          ansible_host: 192.168.122.173
          ansible_user: ansibleprime
          ansible_ssh_private_key_file: /home/ansibleprime/.ssh/id_ed25519

    client:
      hosts:
        vm2:
          ansible_host: 192.168.122.87
          ansible_user: ansibleprime
          ansible_ssh_private_key_file: /home/ansibleprime/.ssh/id_ed25519

        vm3:
          ansible_host: 192.168.122.233
          ansible_user: ansibleprime
          ansible_ssh_private_key_file: /home/ansibleprime/.ssh/id_ed25519

Solution

  • There are many options on how to assign an index to a host.

    1. For example, you can take an index from the list of all hosts
      idx0: 1
      idx: "{{ ansible_play_hosts_all.index(inventory_hostname) + idx0 }}"
    

    Given the inventory

    test_01
    test_02
    test_03
    

    you get

      ansible_play_hosts_all:
      - test_01
      - test_02
      - test_03
    
    ok: [test_01] => 
      idx: '1'
    ok: [test_02] => 
      idx: '2'
    ok: [test_03] => 
      idx: '3'
    

    Then, you can use the filter ansible.utils.ipaddr and get the IP address. For example,

      net: 10.0.0.0/24
      ip: "{{ net|ansible.utils.ipaddr(idx) }}"
    

    gives

    ok: [test_02] => 
      ip: 10.0.0.2/24
    ok: [test_01] => 
      ip: 10.0.0.1/24
    ok: [test_03] => 
      ip: 10.0.0.3/24
    

    Example of a complete playbook for testing

    - hosts: all
    
      vars:
    
        net: 10.0.0.0/24
        idx0: 1
        idx: "{{ ansible_play_hosts_all.index(inventory_hostname) + idx0 }}"
        ip: "{{ net|ansible.utils.ipaddr(idx) }}"
    
      tasks:
    
        - debug:
            var: ansible_play_hosts_all
          run_once: true
    
        - debug:
            var: idx
    
        - debug:
            var: ip
    

    1. The next option is to create a dictionary with the indexes
      idx0: 1
      idx: "{{ dict(ansible_play_hosts_all|
                    zip(range(idx0, ansible_play_hosts_all|length + idx0))) }}"
    

    gives

      idx:
        test_01: 1
        test_02: 2
        test_03: 3
    

    Then, use this dictionary to get the IP address

      net: 10.0.0.0/24
      ip: "{{ net|ansible.utils.ipaddr(idx[inventory_hostname]) }}"
    

    gives

    ok: [test_01] => 
      ip: 10.0.0.1/24
    ok: [test_02] => 
      ip: 10.0.0.2/24
    ok: [test_03] => 
      ip: 10.0.0.3/24
    

    Example of a complete playbook for testing

    - hosts: all
    
      vars:
    
        net: 10.0.0.0/24
        idx0: 1
        idx: "{{ dict(ansible_play_hosts_all|
                      zip(range(idx0, ansible_play_hosts_all|length + idx0))) }}"
        ip: "{{ net|ansible.utils.ipaddr(idx[inventory_hostname]) }}"
    
      tasks:
    
        - debug:
            var: idx
          run_once: true
    
        - debug:
            var: ip