我能在ARM模板中循环遍历属性吗?

14

我有一个ARM模板,在其中设置了一个负载均衡器,我想通过向LB添加规则和探针来添加一些端口开放。

这是我目前拥有的模板:

    {
        "type": "Microsoft.Network/loadBalancers",
        "name": "LB-front",
        "apiVersion": "2016-03-30",
        "location": "westeurope",
        "tags": { },
        "properties": {
            "frontendIPConfigurations": [
                {
                    "name": "LoadBalancerIPConfig",
                    "properties": {
                        "privateIPAllocationMethod": "Dynamic",
                        "publicIPAddress": {
                            "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_lbipdev_0_name'))]"
                        }
                    }
                }
            ],
            "backendAddressPools": [
                {
                    "name": "LoadBalancerBEAddressPool"
                }
            ],
            "loadBalancingRules": [
                {
                    "name": "AppPortLBRule1",
                    "properties": {
                        "frontendIPConfiguration": {
                            "id": "[parameters('loadBalancers_LB_dev_id_6')]"
                        },
                        "frontendPort": 80,
                        "backendPort": 80,
                        "enableFloatingIP": false,
                        "idleTimeoutInMinutes": 5,
                        "protocol": "Tcp",
                        "loadDistribution": "Default",
                        "backendAddressPool": {
                            "id": "[parameters('loadBalancers_LB_dev_id_7')]"
                        },
                        "probe": {
                            "id": "[parameters('loadBalancers_LB_dev_id_8')]"
                        }
                    }
                },
                {
                    "name": "AppPortLBRule2",
                    "properties": {
                        "frontendIPConfiguration": {
                            "id": "[parameters('loadBalancers_LB_dev_id_9')]"
                        },
                        "frontendPort": 81,
                        "backendPort": 81,
                        "enableFloatingIP": false,
                        "idleTimeoutInMinutes": 5,
                        "protocol": "Tcp",
                        "loadDistribution": "Default",
                        "backendAddressPool": {
                            "id": "[parameters('loadBalancers_LB_dev_id_10')]"
                        },
                        "probe": {
                            "id": "[parameters('loadBalancers_LB_dev_id_11')]"
                        }
                    }
                },
                {
                    "name": "AppPortLBRule3",
                    "properties": {
                        "frontendIPConfiguration": {
                            "id": "[parameters('loadBalancers_LB_dev_id_12')]"
                        },
                        "frontendPort": 82,
                        "backendPort": 82,
                        "enableFloatingIP": false,
                        "idleTimeoutInMinutes": 5,
                        "protocol": "Tcp",
                        "loadDistribution": "Default",
                        "backendAddressPool": {
                            "id": "[parameters('loadBalancers_LB_dev_id_13')]"
                        },
                        "probe": {
                            "id": "[parameters('loadBalancers_LB_dev_id_14')]"
                        }
                    }
                }
            ],
            "probes": [
                {
                    "name": "AppPortProbe1",
                    "properties": {
                        "protocol": "Tcp",
                        "port": 80,
                        "intervalInSeconds": 5,
                        "numberOfProbes": 2
                    }
                },
                {
                    "name": "AppPortProbe2",
                    "properties": {
                        "protocol": "Tcp",
                        "port": 81,
                        "intervalInSeconds": 5,
                        "numberOfProbes": 2
                    }
                },
                {
                    "name": "AppPortProbe3",
                    "properties": {
                        "protocol": "Tcp",
                        "port": 82,
                        "intervalInSeconds": 5,
                        "numberOfProbes": 2
                    }
                }
            ],
            "inboundNatRules": [],
            "outboundNatRules": [],
            "inboundNatPools": []
        },
        "resources": [],
        "dependsOn": [
            "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_lbipdev_1_name'))]"
        ]
    },

(一些细节被省略)

我想做的是拥有一个端口号的数组,我希望能够为这些端口创建规则和探针,并循环遍历这些端口,而不是明确地将每个规则和探针作为资源的属性来编写。

基本上,我希望在我的模板中像这样有一个参数或变量:

"ports": [ 80, 81, 82, ...]

我希望能够像这样遍历它:https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple

4个回答

24

当然可以!复制确实与属性配合使用!

像这样创建一个参数或变量(此示例将使用参数数组):

"lbRules": {
  "type": "array",
  "defaultValue": [
    {
      "name": "httpPort",
      "frontendPort": "80",
      "backendPort": "80",
      "protocol": "tcp"
    },
    {
      "name": "customAppPort",
      "frontendPort": "8080",
      "backendPort": "8888",
      "protocol": "tcp"
    },
    {
      "name": "httpsPort",
      "frontendPort": "443",
      "backendPort": "443",
      "protocol": "tcp"
    }
  ]
}

在Loadbalancer资源中使用此参数,使用copy,像这样创建您在参数数组中定义的那么多探测和规则:

{
  "apiVersion": "[variables('lbApiVersion')]",
  "type": "Microsoft.Network/loadBalancers",
  "name": "[parameters('myLoadBalancer')]",
  "location": "[parameters('computeLocation')]",
  "dependsOn": [
    "[concat('Microsoft.Network/publicIPAddresses/',concat(parameters('lbIPName'),'-','0'))]"
  ],
  "properties": {
    "frontendIPConfigurations": [
      {
        "name": "LoadBalancerIPConfig",
        "properties": {
          "publicIPAddress": {
            "id": "[resourceId('Microsoft.Network/publicIPAddresses',concat(parameters('lbIPName'),'-','0'))]"
          }
        }
      }
    ],
    "backendAddressPools": [
      {
        "name": "LoadBalancerBEAddressPool",
        "properties": {}
      }
    ],

    "copy": [
      {
        "name": "probes",
        "count": "[length(parameters('lbRules'))]",
        "input": {
          "name": "[concat(parameters('lbRules')[copyIndex('probes')].name,'Probe')]",
          "properties": {
            "intervalInSeconds": 5,
            "numberOfProbes": 2,
            "port": "[parameters('lbRules')[copyIndex('probes')].backendPort]",
            "protocol": "[parameters('lbRules')[copyIndex('probes')].protocol]"

          }
        }
      },

      {
        "name": "loadBalancingRules",
        "count": "[length(parameters('lbRules'))]",
        "input": {
          "name": "[parameters('lbRules')[copyIndex('loadBalancingRules')].name]",
          "properties": {
            "frontendIPConfiguration": {
              "id": "[concat(resourceId('Microsoft.Network/loadBalancers', parameters('myLoadBalancer')),'/frontendIPConfigurations/LoadBalancerIPConfig')]"
            },
            "frontendport": "[parameters('lbRules')[copyIndex('loadBalancingRules')].frontendport]",
            "backendport": "[parameters('lbRules')[copyIndex('loadBalancingRules')].backendport]",
            "enableFloatingIP": false,
            "idleTimeoutInMinutes": "5",
            "protocol": "[parameters('lbRules')[copyIndex('loadBalancingRules')].protocol]",
            "backendAddressPool": {
              "id": "[concat(resourceId('Microsoft.Network/loadBalancers', parameters('myLoadBalancer')),'/backendAddressPools/LoadBalancerBEAddressPool')]"
            },
            "probe": {
              "id": "[concat(variables('lbID0'),'/probes/', parameters('lbRules')[copyIndex('loadBalancingRules')].name,'Probe')]"
            }
          }
        }
      }
    ],


    "inboundNatPools": []
  },

  }
}

更多信息请点击以下链接:


1
我之前为一个客户做过这个,但我记不清语法了。这是一个很好的例子,完美地概述了功能。感谢分享! - Rogala
1
我发现https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/copy-properties是一个更好的资源。 - Ben

5

您只能将复制对象应用于顶级资源。

您不能将其应用于资源类型上的属性或子资源

"resources": [
  {
    "type": "{provider-namespace-and-type}",
    "name": "parentResource",
    "copy": {  
      /* yes, copy can be applied here */
    },
    "properties": {
      "exampleProperty": {
        /* no, copy cannot be applied here */
      }
    },
    "resources": [
      {
        "type": "{provider-type}",
        "name": "childResource",
        /* copy can be applied if resource is promoted to top level */ 
      }
    ]
  }
] 

引用来源:在 Azure Resource Manager 模板中部署多个资源实例

在 ARM 模板中,只有将复制对象应用于顶级资源(在本例中为“Microsoft.Network/loadBalancers”)时,您才能循环处理属性,但这也会创建所述资源的多个副本。

如果这不是您想要实现的内容,我建议您保留现有方法,直到 ARM 模板在未来支持将 copy 对象复制到资源类型的属性为止。


如果一个资源在技术上是“具体”资源的子级,我该如何将其提升到顶层?例如,WebSite作为顶层,WebSite/Extension作为子级。我能否将扩展资源作为顶层声明,并对网站进行某种形式的依赖关系? - Lee G.
顺便说一句,谢谢你的回答。 - Lee G.
是的,您可以通过在子资源类型/名称前加上父资源类型/名称来在顶层声明子资源。请参见此处: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-templates-resources#child-resources. 这样做后,您还可以再次使用属性迭代。 - Torben Knerr
1
不再是有效的答案。 - Dmitry Gorshkov

3
现在可以根据https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#property-iterationhttps://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#create-multiple-instances-of-a-child-resource上所述的方法在属性或子资源上循环。通过按照以下类型格式引用子资源扩展(例如WebSite/Extension),您可以将其作为顶级资源: {resource-provider-namespace}/{parent-resource-type}/{child-resource-type}. 例如: Microsoft.Web/sites/siteextensions 您还必须通过concat在子资源中引用父资源。例如: "name": "[concat('mywebsite', '/', 'myextension', copyIndex())]"

1
您想要实现的目标可以通过使用take函数来实现。您自己已经链接了正确的文档网站。请前往您发布的链接,并查看"在无法使用复制时创建多个实例"部分。
在您的情况下,代码如下:
"variables": {
    "probeArray": [                    
           {
             "name": "AppPortProbe1",
             "properties": {
                 "protocol": "Tcp",
                 "port": 80,
                 "intervalInSeconds": 5,
                 "numberOfProbes": 2
             }
           },
           {
             "name": "AppPortProbe2",
             "properties": {
                 "protocol": "Tcp",
                 "port": 81,
                 "intervalInSeconds": 5,
                 "numberOfProbes": 2
             }
           },
           {
             "name": "AppPortProbe3",
             "properties": {
                 "protocol": "Tcp",
                 "port": 82,
                 "intervalInSeconds": 5,
                 "numberOfProbes": 2
             }
           }
    ],

您需要创建一个参数来指定您想要的探测器数量。
"parameters": {
...
"numProbes": {
  "type": "int",
  "maxValue": 3,
  "metadata": {
    "description": "This parameter allows you to select the number of probes you want"
  }
}

最终你在资源中使用了take
"resources": [
...
{
  "type": "Microsoft.Network/loadBalancers",
  "properties": {
      ...
      "probes": "[take(variables('probeArray'),parameters('numProbes'))]"
    },
    ...
  }
  ...
}
]

如果您继续阅读文档,您会发现您可以变得更加疯狂,并将复制和获取与链接模板结合使用。

这是否意味着我仍然需要声明所有的探针,但只是将它们放在变量中?我希望有一种方法可以只声明它们一次,然后循环遍历唯一变化的东西,即端口号。 - Lee G.
是的,当你这样做时,你必须定义探针。没有简单的方法只是通过copyIndex()迭代属性。 - TobiWi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接