Passing variables to tasks correctly with loops

Viewed 178

I'm new to ansible and I'm having trouble understanding loops. I have the following ansible playbook:

---
- name: Firewall Object Group Manager
  hosts: all
  gather_facts: yes

  tasks:

    - name: Manage ASA Object Groups
      asa_og:
        name: TEST_ANSIBLE_GROUP_1
        state: replace
        group_type: 
        host_ip:
          - 127.0.0.1
          - 127.0.0.2
        ip_mask:

My goal is to be able to manage multiple ASA object groups. Without loops my play would like like this

---
- name: Firewall Object Group Manager
  hosts: all
  gather_facts: yes

  tasks:

    - name: Manage ASA Object Groups
      asa_og:
        name: TEST_ANSIBLE_GROUP_1
        state: replace
        group_type: 
        host_ip:
          - 127.0.0.1
          - 127.0.0.2
        ip_mask:

    - name: Manage ASA Object Groups
      asa_og:
        name: TEST_ANSIBLE_GROUP_2
        state: replace
        group_type: 
        host_ip:
          - 127.0.0.3
          - 127.0.0.4
        ip_mask:

    - name: Manage ASA Object Groups
      asa_og:
        name: TEST_ANSIBLE_GROUP_3
        state: replace
        group_type: 
        host_ip:
          - 127.0.0.5
          - 127.0.0.6
        ip_mask:

...and so on

My understanding about ansible loops is that I should be able to call the task once and loop through variables. After several hours of research, my play now looks like this:

---
- name: Firewall Object Group Manager
  hosts: all
  gather_facts: yes
  vars:
    Object_Groups:
      - name: 'TEST_ANSIBLE_GROUP'
        host_ip:
          - 127.0.0.1
          - 127.0.0.2
        group_type: 'network-object'

      - name: 'TEST_ANSIBLE_GROUP_2'
        host_ip:
          - 127.0.0.3
          - 127.0.0.4
        group_type: 'network-object'

      - name: 'TEST_ANSIBLE_GROUP_3'
        host_ip:
          - 127.0.0.5
          - 127.0.0.6
        group_type: 'network-object'
  tasks:

    - name: Manage ASA Object Groups
      asa_og:
        name: "{{ item.0.name }}"
        state: replace
        group_type: "{{ item.0.group_type }}"
        host_ip: "{{ item.0.host_ip }}"
        ip_mask: "{{ item.0.ip_mask | default([]) }}"
      loop:
        - "{{ Object_Groups }}"

Here is the debug from the when I run the play:

$ ANSIBLE_STDOUT_CALLBACK=debug ansible-playbook -i inventory.yml -k stackof.yml -v -l FW01
Using /etc/ansible/ansible.cfg as config file
SSH password:

PLAY [Firewall Object Group Manager] **********************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************
ok: [FW01]

TASK [Manage ASA Object Groups] ***************************************************************************************************************************************
changed: [FW01] => (item=[{u'name': u'TEST_ANSIBLE_GROUP', u'group_type': u'network-object', u'host_ip': [u'127.0.0.1', u'127.0.0.2']}, {u'name': u'TEST_ANSIBLE_GROUP_2', u'group_type': u'network-object', u'host_ip': [u'127.0.0.3', u'127.0.0.4']}, {u'name': u'TEST_ANSIBLE_GROUP_3', u'group_type': u'network-object', u'host_ip': [u'127.0.0.5', u'127.0.0.6']}]) => {
    "ansible_loop_var": "item",
    "changed": true,
    "commands": [
        "object-group network TEST_ANSIBLE_GROUP",
        "network-object host 127.0.0.2",
        "network-object host 127.0.0.1",
        "no network-object host 127.0.0.8",
        "no network-object host 127.0.0.7",
        "no network-object host 127.0.0.5"
    ],
    "item": [
        {
            "group_type": "network-object",
            "host_ip": [
                "127.0.0.1",
                "127.0.0.2"
            ],
            "name": "TEST_ANSIBLE_GROUP"
        },
        {
            "group_type": "network-object",
            "host_ip": [
                "127.0.0.3",
                "127.0.0.4"
            ],
            "name": "TEST_ANSIBLE_GROUP_2"
        },
        {
            "group_type": "network-object",
            "host_ip": [
                "127.0.0.5",
                "127.0.0.6"
            ],
            "name": "TEST_ANSIBLE_GROUP_3"
        }
    ]
}

PLAY RECAP ************************************************************************************************************************************************************
FW01                 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

It looks like Ansible can see all the variables but it's not running the task again using the variables I defined for the next object group, then again for the third. I can't seem to figure out what I'm doing wrong.

Once I can properly loop through the variables, my intensions are to place the variables in a vars/main.yml file so I can manage all the object group variables in one place.

As a reference, here is the link to the Ansible module I'm using: https://docs.ansible.com/ansible/latest/modules/asa_og_module.html#asa-og-module

2 Answers

The Object_Groups variable is a list of single item containing three dicts. So when you are looping, ansible runs only once. There may be others but posting two options here to modify the task for looping through all the dicts.


Option#1: Using with_items

- debug:
    msg: "{{ item.name }} {{ item.group_type }} {{ item.host_ip }} {{ item.ip_mask | default([]) }}"
  with_items: "{{ Object_Groups }}"

Option#2: Using loop

- debug:
    msg: "{{ item.name }} {{ item.group_type }} {{ item.host_ip }} {{ item.ip_mask | default([]) }}"
  loop: "{{ Object_Groups | flatten(1) }}"

Both options above gives:

TASK [debug] *******************************************************************************************************************************************************************************
ok: [localhost] => (item={'name': 'TEST_ANSIBLE_GROUP', 'host_ip': ['127.0.0.1', '127.0.0.2'], 'group_type': 'network-object'}) => 
  msg: TEST_ANSIBLE_GROUP network-object ['127.0.0.1', '127.0.0.2'] []
ok: [localhost] => (item={'name': 'TEST_ANSIBLE_GROUP_2', 'host_ip': ['127.0.0.3', '127.0.0.4'], 'group_type': 'network-object'}) => 
  msg: TEST_ANSIBLE_GROUP_2 network-object ['127.0.0.3', '127.0.0.4'] []
ok: [localhost] => (item={'name': 'TEST_ANSIBLE_GROUP_3', 'host_ip': ['127.0.0.5', '127.0.0.6'], 'group_type': 'network-object'}) => 
  msg: TEST_ANSIBLE_GROUP_3 network-object ['127.0.0.5', '127.0.0.6'] []

Refer the details on how to convert with_* to loop and flatten filter.

What if you change this:

loop:
 - "{{ Object_Groups }}"

To:

loop:
  - "{{ TEST_ANSIBLE_GROUP_1 }}"
  - "{{ TEST_ANSIBLE_GROUP_2 }}"
  - "{{ TEST_ANSIBLE_GROUP_3 }}"
  - "{{ TEST_ANSIBLE_GROUP_4 }}"
Related