ansiblejinja2ansible-templatefortigate

Ansible: assign and loop through list dynamically


I'm new at Ansible and trying to automate a Fortigate configuration using the fortinet.fortios modules.

I'm having a problem with fortios_firewall_addrgrp specifically that does not support the append of a firewall address to a group.

I have this set in my variables:

addresses:
  host_0:
    subnet: 192.168.1.10/24
    group: group_0
  host_1:
    subnet: 192.168.1.11/24
    group: group_0
  host_2:
    subnet: 192.168.10.10/24
    group: group_1
  host_3:
    subnet: 192.168.10.11/24
    group: group_1

And I'm running this task to generate an address group.

- name: Configure IPv4 address groups.
  fortios_firewall_addrgrp:
    vdom: '{{ vdom }}'
    state: present
    firewall_addrgrp:
      name: '{{ item.value.group }}'
      member:
        - name: '{{ item.key }}'
  loop: '{{ addresses | dict2items }}'

It loops through each host and generates an address group, but it results in having 2 groups containing only the last hostname in the list.

Ideally, the module should support the append of a hostname to an existing group, but it doesn't, so I'm trying to work around it to make the following happening:

member:
  - name: host_0
  - name: host_1

The example above would work, but I cannot know in advance groups and hostnames in the variable.

I could generate or filter the input variable into a dictionary of host and groups and give it to members. Still, I cannot understand how to loop through it dynamically.


Solution

  • The problem you are having is related to the data structure you are using for the loop. As you mentioned, the fortios_firewall_addrgrp module expects a list of dictionaries for the members key, representing each host.

    So, you need to create a new data structure that fits the input of the fortios_firewall_addrgrp module. Here is an example of how to do it:

    ---
    - hosts: localhost
      gather_facts: 'no'
      vars:
        addresses:
          host_0:
            subnet: 192.168.1.10/24
            group: group_0
          host_1:
            subnet: 192.168.1.11/24
            group: group_0
          host_2:
            subnet: 192.168.10.10/24
            group: group_1
          host_3:
            subnet: 192.168.10.11/24
            group: group_1
      tasks:
        - set_fact:
            addresses_by_group: |
              {{
                addresses_by_group | default({}) | combine({
                  item.value.group: (addresses_by_group[item.value.group] | default([])) + [{"name": item.key}]
                })
              }}
          loop: '{{ addresses | dict2items }}'
        - name: Configure IPv4 address groups.
          fortios_firewall_addrgrp:
            vdom: '{{ vdom }}'
            state: present
            firewall_addrgrp:
              name: '{{ item.key }}'
              member: '{{ item.value }}'
          loop: '{{ addresses_by_group | dict2items }}'
    

    We create a new variable called addresses_by_group that will store a list of hosts for each group. The combine filter allows you to combine two different dictionaries, while the default filter sets a default value for an undefined variable. We use the + operator to concatenate two lists. If you debug the value of the variable addresses_by_group you'll see this:

    TASK [debug] *******************************************************************
    ok: [localhost] => 
      addresses_by_group:
        group_0:
        - name: host_0
        - name: host_1
        group_1:
        - name: host_2
        - name: host_3
    

    Which is just what we need. Bear in mind that we didn't touch the addresses variables so that you can use them later on.