0

I am using a nested loop in Ansible ("create 3 VMs for each of the 10 users"):

  - name: Add hosts to inventory
    add_host:
      name: "{{ '%s-%02d-%02d' | format(vm_prefix, item.0, item.1.number) }}"
      groups: vms
    loop: "{{userlist | product(vms_per_user) | list }}"  

My question is - do I have any way of getting the index of an item in the second list?

  - name: Add hosts to inventory
    add_host:
      name: "{{ '%s-%02d-%02d' | format(vm_prefix, item.0, item.1.number) }}"
      groups: vms
      vm_index: "{{ get the index of this particular VM in vms_per_user }}"
    loop: "{{userlist | product(vms_per_user) | list }}"  

I know about with_indexed_items and flatten + loop_control.index, but I cannot figure out how to write this so that I will get an index that loops only on the second list, and restarts from 0 for every new user (every new element in the first list).

TL;DR - I am looking for the ansible equivalent of this Python construct:

for user in users:
  for (index, vm_name) in enumerate(vms_per_user):
     do_something_with user, index, vm_name

Thank you!

Bogd
  • 673
  • 9
  • 16

3 Answers3

4

If Ansible had an enumerate filter this would be pretty easy. It doesn't, but we can give it one. I put the following content into filter_plugins/enumerate.py:

#!/usr/bin/python


def filter_enumerate(v):
    return list(enumerate(v))


class FilterModule (object):
    def filters(self):
        return {
            'enumerate': filter_enumerate,
        }

For a list [a, b, c], this will return a new list [[0,a], [1,b], [2,c]]. We can use that in your playbook like this:

---
- hosts: localhost
  gather_facts: false
  vars:
    userlist:
      - alice
      - bob
      - mallory
    vms_per_user:
      - vm1
      - vm2
      - vm3
    vm_prefix: foo-

  tasks:
    - debug:
        msg:
          add_host:
            name: "{{ vm_prefix }}{{ item.0 }}-{{ item.1.1 }}"
            groups: vms
            vm_index: "{{ item.1.0 }}"
      loop: "{{ userlist | product(vms_per_user|enumerate) | list }}"

The output of this debug task will look something like:

ok: [localhost] => (item=[u'alice', [0, u'vm1']]) => {                                                                                                                                         
    "msg": {                                                                                                                                                                                   
        "add_host": {                                                                                                                                                                          
            "groups": "vms",                                                                                                                                                                   
            "name": "foo-alice-vm1",                                                                                                                                                           
            "vm_index": "0"                                                                                                                                                                    
        }                                                                                                                                                                                      
    }                                                                                                                                                                                          
}                                                                                                                                                                                              
ok: [localhost] => (item=[u'alice', [1, u'vm2']]) => {                                                                                                                                         
    "msg": {                                                                                                                                                                                   
        "add_host": {                                                                                                                                                                          
            "groups": "vms",                                                                                                                                                                   
            "name": "foo-alice-vm2",                                                                                                                                                           
            "vm_index": "1"                                                                                                                                                                    
        }                                                                                                                                                                                      
    }                                                                                                                                                                                          
}                                                                                                                                                                                              
ok: [localhost] => (item=[u'alice', [2, u'vm3']]) => {                                                                                                                                         
    "msg": {                                                                                                                                                                                   
        "add_host": {                                                                                                                                                                          
            "groups": "vms", 
            "name": "foo-alice-vm3", 
            "vm_index": "2"
        }
    }
}

Etc.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • Now this is absolutely brilliant! :) Thank you! I actually was looking for an `enumerate` filter, but I had no idea how to add one. – Bogd Apr 09 '19 at 17:00
  • Glad to help out! If you're happy with this answer, you may wish to check the checkmark to your left. – larsks Apr 09 '19 at 17:04
  • Out of curiosity - as far as I know, `enumerate` returns a list of tuples. However, the ansible output seems to show a list of lists. Is this behavior specific to ansible, or some quirk of python that I am not aware of? To address your other comment - I will also accept the answer as soon as I get a chance to test it. Thank you once again! – Bogd Apr 09 '19 at 17:07
  • 1
    I suspect that Jinja simply converts tuples into lists, but I don't know for sure. I don't think the distinction is meaningful in this situation. – larsks Apr 09 '19 at 17:52
3

Even though larsks' answer is perfectly valid (and I have been using his solution for several months now in my playbooks - thank you once again for that, larsks!), I just found out there is another, more "ansible-native" solution to my problem. So I am adding it here, just in case someone else stumbles across the problem in the future

In Ansible 2.5, the index_var setting has been introduced under loop_control. (see docs ). You can use that directly, without the need for additional custom filters.

The syntax looks like this:

- name: count our fruit
  debug:
    msg: "{{ item }} with index {{ my_idx }}"
  loop:
    - apple
    - banana
    - pear
  loop_control:
    index_var: my_idx
Bogd
  • 673
  • 9
  • 16
1

Since Ansible 2.8, there are extended loops which add support for list indices. Check https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#extended-loop-variables

Basically add:

loop_control:
  extended: yes

and use ansible_loop.index0 or ansible_loop.index

Thanks to Bogd for guiding me in that direction.

aardbol
  • 2,147
  • 3
  • 31
  • 42