如何遍历复杂的ansible JSON变量的子元素

How to iterate through subelements of complicated ansible JSON variable

我想使用 ansible 任务,在 azure 中创建子网,我有以下剧本

---
- 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|[*]')  }}"

在 JSON 中,变量 vnet 和 subnet 键可以有任何值,所以我不能通过名称访问它们,而是使用 * 遍历它们。同时subnets、vnets和specialsubnets是固定名称。

要创建 azure 子网,我不仅需要子网和特殊子网的内容,还需要同一级别的区域和 resource_group_key。

我无法找到一种方法如何遍历子网和特殊子网的所有元素,使用 with_items,总是打印两个项目,我在 json_query 中输入的内容没有计量。

预期结果是循环创建 13 个子网

# 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"]"

问:"遍历子网和特殊子网的所有元素。"

A:Select 列表,例如

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

并迭代它们,例如

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

给予

  msg: 10.100.83.32/27
  msg: 100.64.101.64/26

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

给予

  msg: 10.100.83.128/26
  msg: 100.64.101.0/26

当我对文件有复杂的事情时,我更喜欢使用自定义过滤器:

您在剧本文件夹中创建了一个文件夹 filter_plugins(我已将文件命名为 myfilters.py 并且过滤器 自定义)

myfilters.py 文件夹 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

并且您在任务中调用了自定义过滤器:

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

显示的结果:它是一个列表,因此您可以轻松地遍历...

"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"
        }
    ]
]