ARM 资源迭代失败,数组为空

ARM resource iteration failing with an empty array

似乎 ARM 模板中的所有表达式都是预先计算的,所以即使您有资源的条件 false,也会计算该资源中的表达式。

无论条件是否显式设置为 false 或者它是否是计算结果为 false.

的表达式,情况似乎都是如此

这种行为在 case 资源迭代中是有问题的,因为资源中的表达式可能引用参数或变量的 copyIndex()。但是,该变量的参数是一个空数组,这些表达式的计算将失败,并显示类似于以下内容的消息 -

The language expression property array index '0' is out of bounds.. Please see https://aka.ms/arm-template-expressions for usage details.

有什么方法可以修改我的模板以使其工作,而不管是否有任何资源要添加?

示例模板

请注意,我已经必须 'hack' copy 对象的 count 参数。如果它是 0(因为表达式 length(variables('productsJArray')) 可能计算为),模板验证失败并出现以下错误 -

The copy count must be a postive integer value and cannot exceed '800'.

我觉得如果 count0,那么不添加任何资源是一个合理的期望 - 但这是一个完全不同的主题!

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "apiManagementServiceName": {
            "type": "string",
            "metadata": {
                "description": "The name of the API Management instance."
            }
        },
        "productsJson": {
            "type": "string",
            "metadata": {
                "description": "A JSON representation of the Products to add."
            }
        }
    },
    "variables": {
        "productsJArray": "[json(parameters('productsJson'))]"
    },
    "resources": [
        {
            "condition": "[greater(length(variables('productsJArray')), 0)]",
            "type": "Microsoft.ApiManagement/service/products",
            "name": "[concat(parameters('apiManagementServiceName'), '/', variables('productsJArray')[copyIndex()].name)]",
            "apiVersion": "2018-06-01-preview",
            "properties": {
                "displayName": "[variables('productsJArray')[copyIndex()].displayName]",
                "description": "[variables('productsJArray')[copyIndex()].description]",
                "state": "[variables('productsJArray')[copyIndex()].state]",
                "subscriptionRequired": "[variables('productsJArray')[copyIndex()].subscriptionRequired]",
                "approvalRequired": "[variables('productsJArray')[copyIndex()].approvalRequired]"
            },
            "copy": {
                "name": "productscopy",
                "count": "[if(greater(length(variables('productsJArray')), 0), length(variables('productsJArray')), 1)]"
            }
        }
    ]
}

可用的示例参数文件

请注意,虽然这看起来不错,但在某些情况下 productsJson 参数值可能是任何空数组,[],这就是我的问题出现的地方。

{
        "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
                "apiManagementServiceName": {
                        "value": "my-api-management"
                },
                "productsJson": {
                        "value": "[{\"name\":\"my-product\",\"displayName\":\"My Product\",\"description\":\"My product is awesome.\",\"state\":\"published\",\"subscriptionRequired\":true,\"approvalRequired\":false}]"
                }
        }
}

将失败的示例参数文件

{
        "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
                "apiManagementServiceName": {
                        "value": "lmk-bvt-conveyorbot"
                },
                "productsJson": {
                        "value": "[]"
                }
        }
}

已更新模板

源自用户“4c74356b41”的其中一项建议。

此模板生成以下错误 -

Unable to process template language expressions for resource '/subscriptions/**********/resourceGroups/**********/providers/Microsoft.Resources/deployments/api-management-products' at line '1' and column '839'. 'The template function 'copyIndex' is not expected at this location. The function can only be used in a resource with copy specified.

{
        "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
                "apiManagementServiceName": {
                        "type": "string",
                        "metadata": {
                                "description": "The name of the API Management instance."
                        }
                },
                "productsJson": {
                        "type": "string",
                        "metadata": {
                                "description": "A JSON representation of the Products to add."
                        }
                }
        },
        "variables": {
                "productsJArray": "[json(parameters('productsJson'))]"
        },
        "resources": [
                {
                        "condition": "[greater(length(variables('productsJArray')), 0)]",
                        "type": "Microsoft.Resources/deployments",
                        "name": "api-management-products",
                        "apiVersion": "2017-05-10",
                        "properties": {
                                "mode": "Incremental",
                                "template": {
                                        "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                                        "contentVersion": "1.0.0.0",
                                        "resources": [
                                                {
                                                        "type": "Microsoft.ApiManagement/service/products",
                                                        "name": "[concat(parameters('apiManagementServiceName'), '/', variables('productsJArray')[copyIndex('productscopy')].name)]",
                                                        "apiVersion": "2018-06-01-preview",
                                                        "properties": {
                                                                "displayName": "[variables('productsJArray')[copyIndex('productscopy')].displayName]",
                                                                "description": "[variables('productsJArray')[copyIndex('productscopy')].description]",
                                                                "state": "[variables('productsJArray')[copyIndex('productscopy')].state]",
                                                                "subscriptionRequired": "[variables('productsJArray')[copyIndex('productscopy')].subscriptionRequired]",
                                                                "approvalRequired": "[variables('productsJArray')[copyIndex('productscopy')].approvalRequired]"
                                                        },
                                                        "copy": {
                                                                "name": "productscopy",
                                                                "count": "[if(greater(length(variables('productsJArray')), 0), length(variables('productsJArray')), 1)]"
                                                        }
                                                }
                                        ]
                                }
                        }
                }
        ]
}

解决此问题的两种方法:

  1. if() hack,所以如果 length == 0length = 1。使用这种方法,你需要有一个你将引用的代理对象,因为引用一个不存在的对象是行不通的。
  2. 使用嵌套部署,您可以在部署上设置条件,这样如果 length == 0(并且您 运行 嵌套部署中的循环)它就不会启动。这样你就不会关心零的长度。

这是我将使用的最终模板。这允许我将对象数组或空数组传递给 productsJson 参数。

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "apiManagementServiceName": {
            "type": "string",
            "metadata": {
                "description": "The name of the API Management instance."
            }
        },
        "productsJson": {
            "type": "string",
            "metadata": {
                "description": "A JSON representation of the Products to add."
            }
        }
    },
    "variables": {
        "productsJArray": "[json(parameters('productsJson'))]"
    },
    "resources": [
        {
            "condition": "[greater(length(variables('productsJArray')), 0)]",
            "type": "Microsoft.Resources/deployments",
            "name": "[concat('api-management-products-', copyIndex())]",
            "apiVersion": "2017-05-10",
            "copy": {
                "name": "productscopy",
                "count": "[if(greater(length(variables('productsJArray')), 0), length(variables('productsJArray')), 1)]",
                "mode": "Serial"
            },
            "properties": {
                "mode": "Incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "resources": [
                        {
                            "type": "Microsoft.ApiManagement/service/products",
                            "name": "[concat(parameters('apiManagementServiceName'), '/', variables('productsJArray')[copyIndex()].name)]",
                            "apiVersion": "2018-06-01-preview",
                            "properties": {
                                "displayName": "[variables('productsJArray')[copyIndex()].displayName]",
                                "description": "[variables('productsJArray')[copyIndex()].description]",
                                "state": "[variables('productsJArray')[copyIndex()].state]",
                                "subscriptionRequired": "[variables('productsJArray')[copyIndex()].subscriptionRequired]",
                                "approvalRequired": "[variables('productsJArray')[copyIndex()].approvalRequired]"
                            }
                        }
                    ]
                }
            }
        }
    ]
}

您还可以在空数组中有一个占位符元素,并使用条件忽略占位符元素。像这样

{
"parameters": {
    "yourArray": {
        "defaultValue": [
            {
                "name": "placeholder"
            }
        ],
        "type": "array"
    }
},
"resources": [
    {
        "condition": "[not(equals(parameters('yourArray')[copyIndex()].name,'placeholder'))]",
        "copy": {
            "name": "yourArray",
            "count": "[length(parameters('yourArray'))]"
        }
    }
]
}