首页 > 解决方案 > ARM resource iteration failing with an empty array

问题描述

It seems that all expressions in an ARM template are evaluated up-front, so even if you have condition false for a resource, expressions within that resource are evaluated.

This appears to be the case regardless of whether condition is explicitly set to false or if it's an expression that evaluates to false.

This behavior is problematic in the case resource iteration, because expressions within the resource may refer to the copyIndex() of a parameter or variable. However, that parameter of variable is an empty array, evaluation of those expressions will fail with a message similar to the following -

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

Is there any way I can amend my template to work regardless of whether or not there are any resources to add?

Example template

Note that I've already had to 'hack' the count parameter of the copy object. If it's 0 (as the expression length(variables('productsJArray')) may evaluation to), template validation fails with the following error -

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

I feel that it's a reasonable expectation that if count is 0, then no resource is added - but that's a whole different subject!

{
    "$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)]"
            }
        }
    ]
}

Example parameters file that will work

Note that while this may seem fine, there may be instances in which the productsJson parameter value is any empty array, [], and this is where my issue has arisen.

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

Example parameters file that will fail

{
        "$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": "[]"
                }
        }
}

Updated template

Derived from one of the suggestions by user '4c74356b41'.

This template is generating the following error -

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

标签: azureazure-resource-managerarm-template

解决方案


解决此问题的两种方法:

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

推荐阅读