如何比较 Azure Policy 中的多个数组?

How do I compare multiple arrays in azure policy?

我正在尝试为 应用程序网关 创建策略。政策说明如下

Http listener should not be attached to a public IP frontend configuration

所以根据我的理解,我必须检查 frontendConfiguration 是否有 public IP,然后检查 public IP 的名称在任何 httpListeners

中被引用

由于 frontendIPConfigurations 和 httpListeners 都是数组,我想到了对这个复杂的查询使用计数并尝试了以下方法

"if": {
                "count": {
                    "field": "Microsoft.Network/applicationGateways/frontendIPConfigurations[*]",
                    "where": {
                        "allOf": [
                            {
                                "field": "Microsoft.Network/applicationGateways/frontendIPConfigurations[*].publicIPAddress.id",
                                "exists": true
                            },
                            {
                                "field": "Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id",
                                "like": "[concat('*', field('Microsoft.Network/applicationGateways/frontendIPConfigurations[*].publicIPAddress.name'))]"
                            }
                        ]
                    }
                },
                "greaterOrEquals": 1
            },

但是我得到以下错误

The alias: 'Microsoft.Network/applicationGateways/httpListeners[].frontendIPConfiguration.id' used inside the 'count.where' expression is targeting an array outside of the count scope: 'Microsoft.Network/applicationGateways/frontendIPConfigurations[]'

你能告诉我是否有办法解决这个问题吗?

这里有一个示例 ARM 模板供参考:

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "location": {
            "type": "string"
        },
        "applicationGatewayName": {
            "type": "string"
        },
        "tier": {
            "type": "string"
        },
        "skuSize": {
            "type": "string"
        },
        "capacity": {
            "type": "int",
            "defaultValue": 2
        },
        "subnetName": {
            "type": "string"
        },
        "zones": {
            "type": "array"
        },
        "virtualNetworkName": {
            "type": "string"
        },
        "virtualNetworkPrefix": {
            "type": "array"
        },
        "publicIpAddressName": {
            "type": "string"
        },
        "sku": {
            "type": "string"
        },
        "allocationMethod": {
            "type": "string"
        },
        "publicIpZones": {
            "type": "array"
        },
        "privateIpAddress": {
            "type": "string"
        },
        "autoScaleMaxCapacity": {
            "type": "int"
        }
    },
    "variables": {
        "vnetId": "[resourceId('policy-tester','Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
        "publicIPRef": "[resourceId('Microsoft.Network/publicIPAddresses/', parameters('publicIpAddressName'))]",
        "subnetRef": "[concat(variables('vnetId'), '/subnets/', parameters('subnetName'))]",
        "applicationGatewayId": "[resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName'))]"
    },
    "resources": [
        {
            "name": "[parameters('applicationGatewayName')]",
            "type": "Microsoft.Network/applicationGateways",
            "apiVersion": "2019-09-01",
            "location": "[parameters('location')]",
            "zones": "[parameters('zones')]",
            "dependsOn": [
                "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
                "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIpAddressName'))]"
            ],
            "tags": {},
            "properties": {
                "sku": {
                    "name": "[parameters('skuSize')]",
                    "tier": "[parameters('tier')]"
                },
                "gatewayIPConfigurations": [
                    {
                        "name": "appGatewayIpConfig",
                        "properties": {
                            "subnet": {
                                "id": "[variables('subnetRef')]"
                            }
                        }
                    }
                ],
                "frontendIPConfigurations": [
                    {
                        "name": "appGwPrivateFrontendIp",
                        "properties": {
                            "subnet": {
                                "id": "[variables('subnetRef')]"
                            },
                            "privateIPAddress": "[parameters('privateIpAddress')]",
                            "privateIPAllocationMethod": "Static"
                        }
                    },
                    {
                        "name": "appGwPublicFrontendIp",
                        "properties": {
                            "PublicIPAddress": {
                                "id": "[variables('publicIPRef')]"
                            }
                        }
                    }
                ],
                "frontendPorts": [
                    {
                        "name": "port_80",
                        "properties": {
                            "Port": 80
                        }
                    },
                    {
                        "name": "port_8080",
                        "properties": {
                            "Port": 8080
                        }
                    }
                ],
                "backendAddressPools": [
                    {
                        "name": "samplebend",
                        "properties": {
                            "backendAddresses": []
                        }
                    }
                ],
                "backendHttpSettingsCollection": [
                    {
                        "name": "sample-http",
                        "properties": {
                            "Port": 80,
                            "Protocol": "Http",
                            "cookieBasedAffinity": "Disabled",
                            "requestTimeout": 20
                        }
                    }
                ],
                "httpListeners": [
                    {
                        "name": "sample-list",
                        "properties": {
                            "frontendIPConfiguration": {
                                "id": "[concat(variables('applicationGatewayId'), '/frontendIPConfigurations/appGwPublicFrontendIp')]"
                            },
                            "frontendPort": {
                                "id": "[concat(variables('applicationGatewayId'), '/frontendPorts/port_80')]"
                            },
                            "protocol": "Http",
                            "sslCertificate": null
                        }
                    },
                    {
                        "name": "sample-priv-http",
                        "properties": {
                            "frontendIPConfiguration": {
                                "id": "[concat(variables('applicationGatewayId'), '/frontendIPConfigurations/appGwPrivateFrontendIp')]"
                            },
                            "frontendPort": {
                                "id": "[concat(variables('applicationGatewayId'), '/frontendPorts/port_8080')]"
                            },
                            "protocol": "Http",
                            "sslCertificate": null
                        }
                    }
                ],
                "requestRoutingRules": [
                    {
                        "Name": "sample-rule",
                        "properties": {
                            "RuleType": "Basic",
                            "httpListener": {
                                "id": "[concat(variables('applicationGatewayId'), '/httpListeners/sample-list')]"
                            },
                            "backendAddressPool": {
                                "id": "[concat(variables('applicationGatewayId'), '/backendAddressPools/samplebend')]"
                            },
                            "backendHttpSettings": {
                                "id": "[concat(variables('applicationGatewayId'), '/backendHttpSettingsCollection/sample-http')]"
                            }
                        }
                    },
                    {
                        "Name": "sample-priv-http-rule",
                        "properties": {
                            "RuleType": "Basic",
                            "httpListener": {
                                "id": "[concat(variables('applicationGatewayId'), '/httpListeners/sample-priv-http')]"
                            },
                            "backendAddressPool": {
                                "id": "[concat(variables('applicationGatewayId'), '/backendAddressPools/samplebend')]"
                            },
                            "backendHttpSettings": {
                                "id": "[concat(variables('applicationGatewayId'), '/backendHttpSettingsCollection/sample-http')]"
                            }
                        }
                    }
                ],
                "enableHttp2": false,
                "sslCertificates": [],
                "probes": [],
                "autoscaleConfiguration": {
                    "minCapacity": "[parameters('capacity')]",
                    "maxCapacity": "[parameters('autoScaleMaxCapacity')]"
                }
            }
        },
        {
            "apiVersion": "2019-09-01",
            "type": "Microsoft.Network/virtualNetworks",
            "name": "[parameters('virtualNetworkName')]",
            "location": "[parameters('location')]",
            "properties": {
                "addressSpace": {
                    "addressPrefixes": "[parameters('virtualNetworkPrefix')]"
                },
                "subnets": [
                    {
                        "name": "default",
                        "properties": {
                            "addressPrefix": "10.0.0.0/24"
                        }
                    }
                ]
            }
        },
        {
            "apiVersion": "2019-02-01",
            "type": "Microsoft.Network/publicIPAddresses",
            "name": "[parameters('publicIpAddressName')]",
            "location": "[parameters('location')]",
            "sku": {
                "name": "[parameters('sku')]"
            },
            "zones": "[parameters('publicIpZones')]",
            "properties": {
                "publicIPAllocationMethod": "[parameters('allocationMethod')]"
            }
        }
    ]
}

免责声明:我在微软工作,但这不是官方回答

以下政策应该可以解决问题:

{
  "if": {
    "allOf": [
      {
        "field": "type",
        "equals": "Microsoft.Network/applicationGateways"
      },
      {
        "count": {
          "field": "Microsoft.Network/applicationGateways/frontendIPConfigurations[*]",
          "where": {
            "allOf": [
              {
                "field": "Microsoft.Network/applicationGateways/frontendIPConfigurations[*].publicIPAddress.id",
                "exists": true
              },
              {
                "value": "[format('{0}/frontendIPConfigurations/{1}', field('id'), current('Microsoft.Network/applicationGateways/frontendIPConfigurations[*].name'))]",
                "in": "[field('Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id')]"
              }
            ]
          }
        },
        "greater": 0
      }
    ]
  },
  "then": {
    "effect": "deny"
  }
}

详情

条件:

{
    "value": "[format('{0}/frontendIPConfigurations/{1}', field('id'), current('Microsoft.Network/applicationGateways/frontendIPConfigurations[*].name'))]",
    "in": "[field('Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id')]"
}

检查当前 frontendIPConfiguration(具有 public IP)的资源 ID 是否被任何 httpListeners 引用。

value 表达式通过组合应用 GW 资源的资源 ID + 当前 frontendIPConfiguration 资源的名称来构建当前正在评估的 frontendIPConfiguration 的资源 ID。我们必须从头开始构建它,因为在创建新的应用程序 gw 资源时,frontendIPConfiguration 的 id 属性 不存在于请求内容中。

另请注意,当您想要访问由 count 表达式枚举的当前数组成员的值时,您 should use the current() function instead of field().

关于您遇到的错误消息,这是因为表达式:

{
  "field": "Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id",
  "like": "[concat('*', field('Microsoft.Network/applicationGateways/frontendIPConfigurations[*].publicIPAddress.name'))]"
}

隐式检查 all Microsoft.Network/applicationGateways/httpListeners[*].frontendIPConfiguration.id 的值是否匹配条件(参见 here)。这基本上是计数表达式的一种不同形式,正如错误消息所说,您不能从枚举不同数组的表达式内部枚举一个数组。