azure - 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)]"
}
}
]
}
}
}
]
}
解决方案
解决此问题的两种方法:
if()
破解,所以如果length == 0
,length = 1
. 使用这种方法,您需要有一个要引用的代理对象,因为引用一个不存在的对象是行不通的。- 使用嵌套部署,您可以在部署上设置一个条件,以便它不会启动 if
length == 0
(并且您在嵌套部署中运行循环)。这样你就不用关心零的长度了。
推荐阅读
- vb.net - 如何根据picturebox.sizemode从picturebox中保存图像?
- react-native - 已弃用的 Gradle 功能不兼容
- arrays - 如何将一个 json 文件中的 json 元素存储到另一个 json 文件中?
- spring - Spring AOP:After vs AfterReturning 优先级
- php - 在 Symfony 4.4 中使用注解测试控制器
- android - 通过 Github Actions 的 android 单元测试覆盖率报告
- git - 使分叉项目与上游项目保持同步的最佳 git 工作流程是什么?
- testing - 使用 Metamask 进行测试的赛普拉斯测试方法
- python - 如何删除 matplotlib 折线图中的轴刻度?
- swift4 - 如何使用 swift xcuitest 从 pdf 文件中读取数据