Mareks Pikalovs
Mareks Pikalovs

Reputation: 93

How to iterate through subelements of complicated ansible JSON variable

I want to use ansible task, to create subnets in azure, I have following playbook

---
- hosts: localhost
  vars:
    "vnets": [
      {
        "vnet_aks_re1": [
          {
            "region": "region1",
            "resource_group_key": "aks_spoke_re1",
            "specialsubnets": [
              {
                "AzureFirewallSubnet": [
                  {
                    "cidr": [
                      "10.100.83.128/26"
                    ],
                    "name": "AzureFirewallSubnet"
                  }
                ],
                "GatewaySubnet": [
                  {
                    "cidr": [
                      "10.100.83.224/27"
                    ],
                    "name": "GatewaySubnet"
                  }
                ]
              }
            ],
            "subnets": [
              {
                "AzureBastionSubnet": [
                  {
                    "cidr": [
                      "10.100.83.32/27"
                    ],
                    "name": "AzureBastionSubnet",
                    "nsg_key": "azure_bastion_nsg"
                  }
                ],
                "aks_ingress": [
                  {
                    "cidr": [
                      "10.100.82.0/24"
                    ],
                    "name": "aks_ingress",
                    "nsg_key": "azure_kubernetes_cluster_nsg",
                    "route_table_key": "default_to_firewall_re1"
                  }
                ],
                "aks_nodepool_system": [
                  {
                    "cidr": [
                      "10.100.80.0/24"
                    ],
                    "name": "aks_nodepool_system",
                    "nsg_key": "azure_kubernetes_cluster_nsg",
                    "route_table_key": "default_to_firewall_re1"
                  }
                ],
                "aks_nodepool_user1": [
                  {
                    "cidr": [
                      "10.100.81.0/24"
                    ],
                    "name": "aks_nodepool_user1",
                    "nsg_key": "azure_kubernetes_cluster_nsg",
                    "route_table_key": "default_to_firewall_re1"
                  }
                ],
                "application_gateway": [
                  {
                    "cidr": [
                      "10.100.83.96/27"
                    ],
                    "name": "agw",
                    "nsg_key": "application_gateway"
                  }
                ],
                "jumpbox": [
                  {
                    "cidr": [
                      "10.100.83.64/28"
                    ],
                    "name": "jumpbox",
                    "nsg_key": "azure_kubernetes_cluster_nsg",
                    "route_table_key": "default_to_firewall_re1"
                  }
                ],
                "private_endpoints": [
                  {
                    "cidr": [
                      "10.100.83.0/27"
                    ],
                    "enforce_private_link_endpoint_network_policies": true,
                    "name": "private_endpoints"
                  }
                ]
              }
            ],
            "vnet": [
              {
                "address_space": [
                  "10.100.80.0/22"
                ],
                "name": "aks"
              }
            ]
          }
        ],
        "vnet_hub_re1": [
          {
            "region": "region1",
            "resource_group_key": "vnet_hub_re1",
            "specialsubnets": [
              {
                "AzureFirewallSubnet": [
                  {
                    "cidr": [
                      "100.64.101.0/26"
                    ],
                    "name": "AzureFirewallSubnet"
                  }
                ],
                "GatewaySubnet": [
                  {
                    "cidr": [
                      "100.64.100.0/27"
                    ],
                    "name": "GatewaySubnet"
                  }
                ]
              }
            ],
            "subnets": [
              {
                "AzureBastionSubnet": [
                  {
                    "cidr": [
                      "100.64.101.64/26"
                    ],
                    "name": "AzureBastionSubnet",
                    "nsg_key": "azure_bastion_nsg"
                  }
                ],
                "jumpbox": [
                  {
                    "cidr": [
                      "100.64.102.0/27"
                    ],
                    "name": "jumpbox",
                    "nsg_key": "jumpbox"
                  }
                ],
                "private_endpoints": [
                  {
                    "cidr": [
                      "100.64.103.128/25"
                    ],
                    "enforce_private_link_endpoint_network_policies": true,
                    "name": "private_endpoints"
                  }
                ]
              }
            ],
            "vnet": [
              {
                "address_space": [
                  "100.64.100.0/22"
                ],
                "name": "vnet_hub_re1"
              }
            ]
          }
        ]
      }
  ]
  tasks:
    - name: Dbg variable
      debug:
        msg: |
          Debug:
          --------------------------------
          Items:             {{ item }}
          ================================
      with_items:
        - "{{ vnets[0] | dict2items | json_query('[].value|[*]|[].subnets')  }}"

    # this would be expected result
    - name: Create a subnet
      azure_rm_subnet:
        resource_group: "{{ item.resource_group_key }}"
        virtual_network_name: "{{ item.vnet[0].name }}"
        name: "{{ combine(item.subnets[*].name,  item.specialsubnets[*].name }}"
        address_prefix_cidr: "{{ combine(item.subnets[*].cidr,  item.specialsubnets[*].cidr }}"
      with_items:
        - "{{ vnets[0] | dict2items | json_query('[].value|[*]')  }}"

In JSON variable vnet and subnet keys can have any value, so I can not access them by name, but use * to iterate through all of them. Meanwhile subnets, vnets and specialsubnets are fixed names.

To create and azure subnet I need not only content of subnets and specialsubnets, but also region and resource_group_key from the same level.

I could not find a way how to iterate through all elements of subnets and specialsubnets, using with_items, there are always two items printed, no meter what I put in json_query.

Expected result would be that loop creates 13 subnets

# Hub  VNET with 5 subnets
- name: Create a subnet
  azure_rm_subnet:
    resource_group: "vnet_hub_re1"
    virtual_network_name: "vnet_hub_re1"
    name: "GatewaySubnet"
    address_prefix_cidr: "["100.64.100.0/27"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "vnet_hub_re1"
    virtual_network_name: "vnet_hub_re1"
    name: "AzureFirewallSubnet"
    address_prefix_cidr: "["100.64.101.0/26"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "vnet_hub_re1"
    virtual_network_name: "vnet_hub_re1"
    name: "AzureBastionSubnet"
    address_prefix_cidr: "["100.64.101.64/26"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "vnet_hub_re1"
    virtual_network_name: "vnet_hub_re1"
    name: "jumpbox"
    address_prefix_cidr: "["100.64.102.0/27"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "vnet_hub_re1"
    virtual_network_name: "vnet_hub_re1"
    name: "private_endpoints"
    address_prefix_cidr: "["100.64.103.128/25"]"

# Spoke vnet with 8 subnets

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "AzureFirewallSubnet"
    address_prefix_cidr: "["10.100.83.128/26"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "GatewaySubnet"
    address_prefix_cidr: "["10.100.83.224/27"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "AzureBastionSubnet"
    address_prefix_cidr: "["10.100.83.32/27"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "aks_ingress"
    address_prefix_cidr: "["10.100.82.0/24"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "aks_nodepool_system"
    address_prefix_cidr: "["10.100.80.0/24"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "aks_nodepool_user1"
    address_prefix_cidr: "["10.100.81.0/24"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "agw"
    address_prefix_cidr: "["10.100.83.96/27"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "jumpbox"
    address_prefix_cidr: "["10.100.83.64/28"]"

- name: Create a subnet
  azure_rm_subnet:
    resource_group: "aks_spoke_re1"
    virtual_network_name: "aks"
    name: "private_endpoints"
    address_prefix_cidr: "["10.100.83.0/27"]"

Upvotes: 1

Views: 402

Answers (2)

Frenchy
Frenchy

Reputation: 17007

when i have complex things to do with a file, iprefer to use a custom filter:

you create a folder filter_plugins in your playbook folder (i have named the file myfilters.py and the filter custom)

myfilters.py in folder filter_plugins:

#!/usr/bin/python
class FilterModule(object):
    def filters(self):
        return {
            'custom': self.custom
        }

    def trapvalues(self, items, vnet):
        result = []
        virtual_network_name = vnet['vnet'][0]['name']
        resource_group = vnet['resource_group_key']
        for item in items:
            address_prefix_cidr = items[item][0]['cidr']
            name = items[item][0]['name']
            result.append({'resource_group': resource_group, 
                           'virtual_network_name': virtual_network_name,
                           'name': name,
                           'address_prefix_cidr': address_prefix_cidr})
        return result

    def custom(self, vnets):
        result = []
        for it in vnets:
            special = self.trapvalues(vnets[it][0]['specialsubnets'][0], vnets[it][0])
            result.append(special)            
            sub = self.trapvalues(vnets[it][0]['subnets'][0], vnets[it][0])
            result.append(sub)
        #print(result)
        return result

and you call the custom filter in task:

  tasks:
    - name: create variable
      set_fact:
        result: "{{ vnets[0] | custom  }}"

result displayed: its a list so you could iterate over easily...

"result": [
    [
        {
            "address_prefix_cidr": [
                "10.100.83.128/26"
            ],
            "name": "AzureFirewallSubnet",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.83.224/27"
            ],
            "name": "GatewaySubnet",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        }
    ],
    [
        {
            "address_prefix_cidr": [
                "10.100.83.32/27"
            ],
            "name": "AzureBastionSubnet",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.82.0/24"
            ],
            "name": "aks_ingress",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.80.0/24"
            ],
            "name": "aks_nodepool_system",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.81.0/24"
            ],
            "name": "aks_nodepool_user1",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.83.96/27"
            ],
            "name": "agw",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.83.64/28"
            ],
            "name": "jumpbox",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        },
        {
            "address_prefix_cidr": [
                "10.100.83.0/27"
            ],
            "name": "private_endpoints",
            "resource_group": "aks_spoke_re1",
            "virtual_network_name": "aks"
        }
    ],
    [
        {
            "address_prefix_cidr": [
                "100.64.101.0/26"
            ],
            "name": "AzureFirewallSubnet",
            "resource_group": "vnet_hub_re1",
            "virtual_network_name": "vnet_hub_re1"
        },
        {
            "address_prefix_cidr": [
                "100.64.100.0/27"
            ],
            "name": "GatewaySubnet",
            "resource_group": "vnet_hub_re1",
            "virtual_network_name": "vnet_hub_re1"
        }
    ],
    [
        {
            "address_prefix_cidr": [
                "100.64.101.64/26"
            ],
            "name": "AzureBastionSubnet",
            "resource_group": "vnet_hub_re1",
            "virtual_network_name": "vnet_hub_re1"
        },
        {
            "address_prefix_cidr": [
                "100.64.102.0/27"
            ],
            "name": "jumpbox",
            "resource_group": "vnet_hub_re1",
            "virtual_network_name": "vnet_hub_re1"
        },
        {
            "address_prefix_cidr": [
                "100.64.103.128/25"
            ],
            "name": "private_endpoints",
            "resource_group": "vnet_hub_re1",
            "virtual_network_name": "vnet_hub_re1"
        }
    ]
]

Upvotes: 1

Vladimir Botka
Vladimir Botka

Reputation: 68034

Q: "Iterate through all elements of subnets and specialsubnets."

A: Select the lists, e.g.

  _subnets: "{{ vnets|json_query('[].*[][].subnets') }}"
  _specialsubnets: "{{ vnets|json_query('[].*[][].specialsubnets') }}"

and iterate them, e.g.

    - debug:
        msg: "{{ item.AzureBastionSubnet.0.cidr.0 }}"
      loop: "{{ _subnets|flatten }}"

gives

  msg: 10.100.83.32/27
  msg: 100.64.101.64/26

and

    - debug:
        msg: "{{ item.AzureFirewallSubnet.0.cidr.0 }}"
      loop: "{{ _specialsubnets|flatten }}"

gives

  msg: 10.100.83.128/26
  msg: 100.64.101.0/26

Upvotes: 0

Related Questions