如何使用 ARM 模板副本有条件地为存储帐户提供 N 个角色分配?
How to conditionally provision N role assignments for storage accounts with ARM Template copy?
我正在尝试预配 N 个存储帐户、N 个角色分配(每个存储帐户 1 个)以授予对特定身份的访问权限,但仅有条件地部署角色分配。存储帐户和标识已经存在,它们的模板逻辑已经运行了一段时间。
如果我尝试部署以下模板片段,我会在“'[concat(parameters('BackupStorageAccountRoleAssignmentsDeployment') [0].AccountName, '/Microsoft.Authorization/', guid(parameters('BackupStorageAccountRoleAssignmentsDeployment')[0].Name))]'" 当角色分配的输入数组为空时.
我已经在使用技巧将长度为 0 的副本强制为长度 = 1,然后根据条件进行保护部署。我已经尝试使用大小为 1 的默认数组进行变体,将我的角色分配部分移动到嵌套模板中,并手动将我的循环展开为 4 个角色分配。无论如何,我遇到了同样的错误。这段代码有什么问题?
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleAssignments": {
"type": "array"
},
"IdentityName": {
"type": "string"
},
"StorageAccounts": {
"type": "array"
}
},
"variables": {
"IdentityResourceId": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('IdentityName'))]"
},
"resources": [
{ // Everything related to role assignments chokes completely, even if I unroll the loop
"dependsOn": [
"[variables('IdentityResourceId')]",
"storageaccountcopy"
],
"copy": {
"name": "RoleAssignmentsCopy",
"count": "[max(length(parameters('BackupStorageAccountRoleAssignmentsDeployment')), 1)]"
},
"condition": "[greater(length(parameters('RoleAssignments')), 0)]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2020-04-01-preview",
"name": "[concat(parameters('RoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(parameters('RoleAssignments')[copyIndex()].Name))]",
"properties": {
"roleDefinitionId": "[parameters('RoleAssignments')[copyIndex()].RoleDefinitionId]",
"principalId": "[if(and(empty(parameters('RoleAssignments')[copyIndex()].ResourceId), not(empty(parameters('RoleAssignments')[copyIndex()].PrincipalId))),
parameters('RoleAssignments')[copyIndex()].PrincipalId,
reference(parameters('RoleAssignments')[copyIndex()].ResourceId, '2018-11-30').PrincipalId)]",
"scope": "[parameters('RoleAssignments')[copyIndex()].Scope]",
"principalType": "[parameters('RoleAssignments')[copyIndex()].PrincipalType]"
}
},
{ // Everthing related to storage account provisioning works fine, this was existing code
"condition": "[and(greater(length(parameters('StorageAccounts')), 0), not(parameters('SkipStorageAccountProvisioning')), equals(parameters('StorageAccountProvisioningDefault'), 'true'))]",
"copy": {
"name": "storageaccountcopy",
"count": "[length(parameters('StorageAccounts'))]"
},
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('StorageAccounts')[copyIndex()].AccountName]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[if(equals(parameters('StorageAccounts')[copyIndex()].AccountTypeOverride, parameters('StorageAccountTypeOverrideDefault')), parameters('StorageAccounts')[copyIndex()].AccountType, parameters('StorageAccounts')[copyIndex()].AccountTypeOverride)]",
"tier": "Standard"
},
"kind": "[parameters('StorageAccounts')[copyIndex()].AccountKind]",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
}
]
}
编辑:
根据反馈,我尝试将代码段移动到嵌套模板中。失败并出现完全相同的错误,The template resource '[concat(parameters('RoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(parameters('BackupStorageAccountRoleAssignments')[copyIndex()].Name))]' at line '1' and column '837' is not valid: The language expression property array index '0' is out of bounds..
{
"dependsOn": [
"[variables('IdentityResourceId')]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "RoleAssignmentsDeployment",
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"parameters": {
"RoleAssignments": {
"value": "[parameters('RoleAssignmentsDeployment')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleAssignments": {
"type": "array"
}
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('RoleAssignments')[copyIndex()].AccountName)]"
],
"copy": {
"name": "RoleAssignmentsDeploymentCopy",
"count": "[max(length(parameters('RoleAssignments')), 1)]"
},
"condition": "[greater(length(parameters('RoleAssignments')), 0)]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2020-10-01",
"name": "[concat(parameters('RoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(parameters('RoleAssignments')[copyIndex()].Name))]",
"properties": {
"scope": "[parameters('RoleAssignments')[copyIndex()].Scope]",
"principalType": "[parameters('RoleAssignments')[copyIndex()].PrincipalType]",
"roleDefinitionId": "[parameters('RoleAssignments')[copyIndex()].RoleDefinitionId]",
"principalId": "[if(and(empty(parameters('RoleAssignments')[copyIndex()].ResourceId), not(empty(parameters('RoleAssignments')[copyIndex()].PrincipalId))),
parameters('RoleAssignments')[copyIndex()].PrincipalId,
reference(parameters('RoleAssignments')[copyIndex()].ResourceId, '2020-10-01').PrincipalId)]"
}
}
]
}
}
}
零长度数组上的复制循环问题是已知的,Microsoft 团队已在其路线图上修复它。
目前的解决方案是将循环移动到嵌套部署中,将数组作为参数传递,并在嵌套部署条件下检查数组是否不为空。虽然我不确定部署是否必须在内部范围内。当然,它将变量范围设置为内部。
所以有几种方法可以解决这个问题。我很难让 Miq 的建议按照我想要的方式工作。不过,我确实找到了另一种解决方法;在模板中使用一个变量来确保输入数组总是被填充
备注:
参数上的常规“默认值”属性 将不起作用,如果传递空数组,则不会应用默认值。您需要使用一些 if 块来计算单元素数组作为无操作部署的默认值
ARM 在“编译”时扩展模板。因此,您会根据模板的替换值而不是在实际部署开始时收到“0 超出范围”错误。问题是我试图使用零长度副本并且我在特殊的 ARM functions/items 中引用 copyindex()
像 reference
和 dependsOn
.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleAssignments": {
"type": "array"
},
"IdentityName": {
"type": "string"
},
"StorageAccounts": {
"type": "array"
},
"Enabled": {
"type: "bool"
}
},
"variables": {
"IdentityResourceId": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('IdentityName'))]",
"ComputedRoleAssignments": "[if(greater(length(parameters('RoleAssignments')), 0),
parameters('RoleAssignments'),
createArray(createObject('AccountName', 'dummy', 'Name', 'dummy', 'ResourceId', 'dummy', 'RoleDefinitionId', 'dummy', 'PrincipalType', 'dummy', 'Scope', 'dummy', 'PrincipalId', 'dummy')))]"
},
"resources": [
{ // Everything related to role assignments chokes completely, even if I unroll the loop
"dependsOn": [
"[variables('IdentityResourceId')]",
"storageaccountcopy"
],
"copy": {
"name": "RoleAssignmentsCopy",
"count": "[length(variables('ComputedRoleAssignments'))]"
}, // Conditionally deploy based on parameters, but use the value from computed variables
"condition": "[and(greater(length(parameters('RoleAssignments')), 0), parameters('Enabled'))]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2020-04-01-preview",
"name": "[concat(variables('ComputedRoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(variables('ComputedRoleAssignments')[copyIndex()].Name))]",
"properties": {
"roleDefinitionId": "[variables('ComputedRoleAssignments')[copyIndex()].RoleDefinitionId]",
"principalId": "[if(and(empty(parameters('ComputedRoleAssignments')[copyIndex()].ResourceId), not(empty(parameters('ComputedRoleAssignments')[copyIndex()].PrincipalId))),
variables('ComputedRoleAssignments')[copyIndex()].PrincipalId,
reference(variables('ComputedRoleAssignments')[copyIndex()].ResourceId, '2018-11-30').PrincipalId)]",
"scope": "[variables('ComputedRoleAssignments')[copyIndex()].Scope]",
"principalType": "[variables('ComputedRoleAssignments')[copyIndex()].PrincipalType]"
}
},
{ // Everthing related to storage account provisioning works fine, this was existing code
"condition": "[and(greater(length(parameters('StorageAccounts')), 0), not(parameters('SkipStorageAccountProvisioning')), equals(parameters('StorageAccountProvisioningDefault'), 'true'))]",
"copy": {
"name": "storageaccountcopy",
"count": "[length(parameters('StorageAccounts'))]"
},
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('StorageAccounts')[copyIndex()].AccountName]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[if(equals(parameters('StorageAccounts')[copyIndex()].AccountTypeOverride, parameters('StorageAccountTypeOverrideDefault')), parameters('StorageAccounts')[copyIndex()].AccountType, parameters('StorageAccounts')[copyIndex()].AccountTypeOverride)]",
"tier": "Standard"
},
"kind": "[parameters('StorageAccounts')[copyIndex()].AccountKind]",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
}
]
}
我正在尝试预配 N 个存储帐户、N 个角色分配(每个存储帐户 1 个)以授予对特定身份的访问权限,但仅有条件地部署角色分配。存储帐户和标识已经存在,它们的模板逻辑已经运行了一段时间。
如果我尝试部署以下模板片段,我会在“'[concat(parameters('BackupStorageAccountRoleAssignmentsDeployment') [0].AccountName, '/Microsoft.Authorization/', guid(parameters('BackupStorageAccountRoleAssignmentsDeployment')[0].Name))]'" 当角色分配的输入数组为空时.
我已经在使用技巧将长度为 0 的副本强制为长度 = 1,然后根据条件进行保护部署。我已经尝试使用大小为 1 的默认数组进行变体,将我的角色分配部分移动到嵌套模板中,并手动将我的循环展开为 4 个角色分配。无论如何,我遇到了同样的错误。这段代码有什么问题?
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleAssignments": {
"type": "array"
},
"IdentityName": {
"type": "string"
},
"StorageAccounts": {
"type": "array"
}
},
"variables": {
"IdentityResourceId": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('IdentityName'))]"
},
"resources": [
{ // Everything related to role assignments chokes completely, even if I unroll the loop
"dependsOn": [
"[variables('IdentityResourceId')]",
"storageaccountcopy"
],
"copy": {
"name": "RoleAssignmentsCopy",
"count": "[max(length(parameters('BackupStorageAccountRoleAssignmentsDeployment')), 1)]"
},
"condition": "[greater(length(parameters('RoleAssignments')), 0)]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2020-04-01-preview",
"name": "[concat(parameters('RoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(parameters('RoleAssignments')[copyIndex()].Name))]",
"properties": {
"roleDefinitionId": "[parameters('RoleAssignments')[copyIndex()].RoleDefinitionId]",
"principalId": "[if(and(empty(parameters('RoleAssignments')[copyIndex()].ResourceId), not(empty(parameters('RoleAssignments')[copyIndex()].PrincipalId))),
parameters('RoleAssignments')[copyIndex()].PrincipalId,
reference(parameters('RoleAssignments')[copyIndex()].ResourceId, '2018-11-30').PrincipalId)]",
"scope": "[parameters('RoleAssignments')[copyIndex()].Scope]",
"principalType": "[parameters('RoleAssignments')[copyIndex()].PrincipalType]"
}
},
{ // Everthing related to storage account provisioning works fine, this was existing code
"condition": "[and(greater(length(parameters('StorageAccounts')), 0), not(parameters('SkipStorageAccountProvisioning')), equals(parameters('StorageAccountProvisioningDefault'), 'true'))]",
"copy": {
"name": "storageaccountcopy",
"count": "[length(parameters('StorageAccounts'))]"
},
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('StorageAccounts')[copyIndex()].AccountName]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[if(equals(parameters('StorageAccounts')[copyIndex()].AccountTypeOverride, parameters('StorageAccountTypeOverrideDefault')), parameters('StorageAccounts')[copyIndex()].AccountType, parameters('StorageAccounts')[copyIndex()].AccountTypeOverride)]",
"tier": "Standard"
},
"kind": "[parameters('StorageAccounts')[copyIndex()].AccountKind]",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
}
]
}
编辑:
根据反馈,我尝试将代码段移动到嵌套模板中。失败并出现完全相同的错误,The template resource '[concat(parameters('RoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(parameters('BackupStorageAccountRoleAssignments')[copyIndex()].Name))]' at line '1' and column '837' is not valid: The language expression property array index '0' is out of bounds..
{
"dependsOn": [
"[variables('IdentityResourceId')]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "RoleAssignmentsDeployment",
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"parameters": {
"RoleAssignments": {
"value": "[parameters('RoleAssignmentsDeployment')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleAssignments": {
"type": "array"
}
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('RoleAssignments')[copyIndex()].AccountName)]"
],
"copy": {
"name": "RoleAssignmentsDeploymentCopy",
"count": "[max(length(parameters('RoleAssignments')), 1)]"
},
"condition": "[greater(length(parameters('RoleAssignments')), 0)]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2020-10-01",
"name": "[concat(parameters('RoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(parameters('RoleAssignments')[copyIndex()].Name))]",
"properties": {
"scope": "[parameters('RoleAssignments')[copyIndex()].Scope]",
"principalType": "[parameters('RoleAssignments')[copyIndex()].PrincipalType]",
"roleDefinitionId": "[parameters('RoleAssignments')[copyIndex()].RoleDefinitionId]",
"principalId": "[if(and(empty(parameters('RoleAssignments')[copyIndex()].ResourceId), not(empty(parameters('RoleAssignments')[copyIndex()].PrincipalId))),
parameters('RoleAssignments')[copyIndex()].PrincipalId,
reference(parameters('RoleAssignments')[copyIndex()].ResourceId, '2020-10-01').PrincipalId)]"
}
}
]
}
}
}
零长度数组上的复制循环问题是已知的,Microsoft 团队已在其路线图上修复它。
目前的解决方案是将循环移动到嵌套部署中,将数组作为参数传递,并在嵌套部署条件下检查数组是否不为空。虽然我不确定部署是否必须在内部范围内。当然,它将变量范围设置为内部。
所以有几种方法可以解决这个问题。我很难让 Miq 的建议按照我想要的方式工作。不过,我确实找到了另一种解决方法;在模板中使用一个变量来确保输入数组总是被填充
备注:
参数上的常规“默认值”属性 将不起作用,如果传递空数组,则不会应用默认值。您需要使用一些 if 块来计算单元素数组作为无操作部署的默认值
ARM 在“编译”时扩展模板。因此,您会根据模板的替换值而不是在实际部署开始时收到“0 超出范围”错误。问题是我试图使用零长度副本并且我在特殊的 ARM functions/items 中引用
copyindex()
像reference
和dependsOn
.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleAssignments": {
"type": "array"
},
"IdentityName": {
"type": "string"
},
"StorageAccounts": {
"type": "array"
},
"Enabled": {
"type: "bool"
}
},
"variables": {
"IdentityResourceId": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('IdentityName'))]",
"ComputedRoleAssignments": "[if(greater(length(parameters('RoleAssignments')), 0),
parameters('RoleAssignments'),
createArray(createObject('AccountName', 'dummy', 'Name', 'dummy', 'ResourceId', 'dummy', 'RoleDefinitionId', 'dummy', 'PrincipalType', 'dummy', 'Scope', 'dummy', 'PrincipalId', 'dummy')))]"
},
"resources": [
{ // Everything related to role assignments chokes completely, even if I unroll the loop
"dependsOn": [
"[variables('IdentityResourceId')]",
"storageaccountcopy"
],
"copy": {
"name": "RoleAssignmentsCopy",
"count": "[length(variables('ComputedRoleAssignments'))]"
}, // Conditionally deploy based on parameters, but use the value from computed variables
"condition": "[and(greater(length(parameters('RoleAssignments')), 0), parameters('Enabled'))]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2020-04-01-preview",
"name": "[concat(variables('ComputedRoleAssignments')[copyIndex()].AccountName, '/Microsoft.Authorization/', guid(variables('ComputedRoleAssignments')[copyIndex()].Name))]",
"properties": {
"roleDefinitionId": "[variables('ComputedRoleAssignments')[copyIndex()].RoleDefinitionId]",
"principalId": "[if(and(empty(parameters('ComputedRoleAssignments')[copyIndex()].ResourceId), not(empty(parameters('ComputedRoleAssignments')[copyIndex()].PrincipalId))),
variables('ComputedRoleAssignments')[copyIndex()].PrincipalId,
reference(variables('ComputedRoleAssignments')[copyIndex()].ResourceId, '2018-11-30').PrincipalId)]",
"scope": "[variables('ComputedRoleAssignments')[copyIndex()].Scope]",
"principalType": "[variables('ComputedRoleAssignments')[copyIndex()].PrincipalType]"
}
},
{ // Everthing related to storage account provisioning works fine, this was existing code
"condition": "[and(greater(length(parameters('StorageAccounts')), 0), not(parameters('SkipStorageAccountProvisioning')), equals(parameters('StorageAccountProvisioningDefault'), 'true'))]",
"copy": {
"name": "storageaccountcopy",
"count": "[length(parameters('StorageAccounts'))]"
},
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('StorageAccounts')[copyIndex()].AccountName]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[if(equals(parameters('StorageAccounts')[copyIndex()].AccountTypeOverride, parameters('StorageAccountTypeOverrideDefault')), parameters('StorageAccounts')[copyIndex()].AccountType, parameters('StorageAccounts')[copyIndex()].AccountTypeOverride)]",
"tier": "Standard"
},
"kind": "[parameters('StorageAccounts')[copyIndex()].AccountKind]",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
}
]
}