Is it possible to use nested arrays in Properties? Assuming top array would be a parent resource and nested array are child resources for each parent. I would like then to iterate all parents and then all childs associated for each parent.
Here's an example ARM Template. Where I'm creating a ServiceBus with Topics and Subscriptions. Each topic would have at least one subscription associated with it. It would be the easiest to define properties for Topics - Subscriptions in a nested arrays, like in example below.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBus": {
"defaultValue": {
"location": "uksouth",
"name": "myNewSB1",
"skuCapacity": 1,
"skuName": "Standard",
"skuTier": "Standard"
},
"type": "object"
},
"serviceBusTopics": {
"defaultValue": [
{
"name": "topic1",
"subscriptions": [
"topic1-sub1",
"topic1-sub2"
]
},
{
"name": "topic2",
"subscriptions": [
"topic2-subAbc"
]
},
{
"name": "topicOther",
"subscriptions": [
"topicOther-subDef1",
"topicOther-subDef2",
"topicOther-subDef3",
"topicOther-subDef4"
]
}
],
"type": "array"
}
},
"resources": [
{
"apiVersion": "2017-04-01",
"location": "[parameters('serviceBus').location]",
"name": "[parameters('serviceBus').name]",
"sku": {
"capacity": "[parameters('serviceBus').skuCapacity]",
"name": "[parameters('serviceBus').skuName]",
"tier": "[parameters('serviceBus').skuTier]"
},
"type": "Microsoft.ServiceBus/namespaces"
},
{
"apiVersion": "2017-04-01",
"copy": {
"count": "[length(parameters('serviceBusTopics'))]",
"mode": "Parallel",
"name": "topicsLoop"
},
"dependsOn": [
"[resourceId('Microsoft.ServiceBus/namespaces', parameters('serviceBus').name)]"
],
"name": "[concat(parameters('serviceBus').name, '/', parameters('serviceBusTopics')[copyIndex()].name)]",
"type": "Microsoft.ServiceBus/namespaces/topics"
},
{
"apiVersion": "2017-04-01",
"copy": {
"count": "[length(<...>)]",
"mode": "Parallel",
"name": "subscriptionsLoop"
},
"dependsOn": [
"topicsLoop"
],
"name": "[concat(parameters('serviceBus').name, '/', <...>, '/', <...>)]",
"type": "Microsoft.ServiceBus/namespaces/topics/subscriptions"
}
]
}
so you have 2 options:
Hardcode each resource, so create 1 resource for 1 object in the array and that way you will be able to iterate, but when you add\remove objects from the array - you will need to adjust the template (not optimal, obviously)
Use a nested template. Let me elaborate a bit:
not sure how to reset the list counter, lol
Create a nested deployment in the template that iterates the base array so serviceBusTopics
pass current iteration to the nested template: "[parameters('serviceBusTopics')[copyIndex()]]"
in the nested template you are only dealing with a single object that has one of the properties of type array and you can iterate that.
Related
I have an arm template that creates a key vault and a secrets array that creates secrets. I am trying to make the template ignore secrets creation if the array is empty.
Here is what I have.
Parameter
"secretsArray": {
"type": "array",
"defaultValue": [
{
"secretName": "secrets",
"secretValue": "value"
}
]
},
Resource:
{
"apiVersion": "2019-09-01",
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[concat(parameters('keyVaultName'), '/', parameters('secretsArray')[copyIndex()].secretName)]",
"dependsOn": [
"[variables('keyVaultId')]"
],
"copy": {
"name": "secretsCopy",
"count": "[if(equals(length(parameters('secretsArray')),0),1, length(parameters('secretsArray')))]"
},
"properties": {
"value": "[if(equals(length(parameters('secretsArray')),0),json('null'),parameters('secretsArray')[copyIndex()].secretName)]"
}
},
When I try to make the array empty like so:
"secretsArray": {
"type": "array",
"defaultValue": []
},
I get the error: The template resource [concat(parameters('keyVaultName'), '/', parameters('secretsArray')[copyIndex()].secretName)] is not valid: The language expression property array index '0' is out of bounds...
This is my first attempt at conditions so I could be way off. Any help would be much appreciated. Thanks
I am building an ARM template that creates multiple storage accounts and each one of them will contain multiple containers “blobs” but apparently still not supported.
Is there any other way to do this beside specify each of them separately?
example of what I am trying to achieve:
StorageAcct_1: must contain 10 blobs
StorageAcct_2 : must contain 6 blobs
I am not able to achieve that without duplicating my storage account and container templates.
You can do it - there are multiple ways (nesting, inline, variable loops), it really depends on what you want the code to look like and what your input format is... but a simple n*m loop could use this:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"variables": {
"numberOfAccounts": 2,
"blobsPerAccount": 3,
"saprefix": "[uniqueString(resourceGroup().id)]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}', variables('saprefix'), copyIndex())]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"copy": {
"name": "storageAccountLoop",
"count": "[variables('numberOfAccounts')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}/default', variables('saprefix'), copyIndex())]",
"copy": {
"name": "blobServiceLoop",
"count": "[variables('numberOfAccounts')]"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', format('{0}{1}', variables('saprefix'), copyIndex()))]"
]
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}/{2}/{3}{4}', variables('saprefix'), mod(copyIndex(), variables('numberOfAccounts')), 'default', 'container', mod(copyIndex(), variables('blobsPerAccount')))]",
"copy": {
"name": "containerLoop",
"count": "[mul(variables('numberOfAccounts'), variables('blobsPerAccount'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', format('{0}{1}', variables('saprefix'), mod(copyIndex(), variables('numberOfAccounts'))))]",
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', format('{0}{1}', variables('saprefix'), mod(copyIndex(), variables('numberOfAccounts'))), 'default')]"
]
}
]
}
That help?
I am using output from a linked template in my ARM template for deployment below are my templates :
Link template :
"resources": [
{
"name": "[variables('clusterName')]",
"type": "Microsoft.Kusto/clusters",
"sku": {
"name": "Standard_D13_v2",
"tier": "Standard",
"capacity": 2
},
"apiVersion": "2020-09-18",
"location": "[parameters('location')]",
"properties": {
"trustedExternalTenants": [],
"optimizedAutoscale": {
"version": 1,
"isEnabled": true,
"minimum": 2,
"maximum": 10
},
"enableDiskEncryption": false,
"enableStreamingIngest": true,
"enablePurge": false,
"enableDoubleEncryption": false,
"engineType": "V3"
}
}
],
"outputs": {
"clusterNameResult": {
"type": "string",
"value": "[variables('clusterName')]"
}
}
Template using this linked template:
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "linkedTemplate",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(uri(deployment().properties.templateLink.uri, 'Dataexplorer_Deployment_Template.json'))]",
"contentVersion": "1.0.0.0"
}
},
"copy": {
"name": "databasecopy",
"count": "[length(parameters('databaseNameList'))]"
}
},
{
"type": "Microsoft.Kusto/Clusters/Databases",
"apiVersion": "2020-09-18",
"name": "[variables('databaseNameList').databaseNames[copyIndex()]]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Kusto/Clusters', reference('linkedTemplate').outputs['clusterNameResult'].value)]"
],
"kind": "ReadWrite",
"properties": {
"softDeletePeriod": "P5D",
"hotCachePeriod": "P1D"
},
"copy": {
"name": "databasecopy",
"count": "[length(parameters('databaseNameList'))]"
}
},
{
"type": "Microsoft.Kusto/Clusters/Databases/PrincipalAssignments",
"apiVersion": "2020-09-18",
"name": "[variables('databaseNameList').databaseNames[copyIndex()]]",
"dependsOn": [
"[resourceId('Microsoft.Kusto/Clusters/Databases', variables('databaseNameList').databaseNames[copyIndex()])]",
"[resourceId('Microsoft.Kusto/Clusters', reference('linkedTemplate').outputs['clusterNameResult'].value)]"
],
"properties": {
"principalId": "abc.def#gmail.com",
"role": "Viewer",
"principalType": "User",
"tenantId": "523547f7-9d12-45c5-9g15-2ysb44a3r2m4"
},
"copy": {
"name": "databasecopy",
"count": "[length(parameters('databaseNameList'))]"
}
}
]
I am refering to the cluster name deployed through template 1 in template 2 , specified at "dependsOn" but it fails with error The template resource 'adx-jtcjiot-dev-sea-adxdb001' at line '84' and column '9' is not valid: The template function 'reference' is not expected at this location.
Has anyone used reference functions for deployment like this, I want to keep cluster and database deployment separately as database creation might occur often at the same time i don't want to hardcode the clustername in the database template. Is there any other way to do it or to resolve this error.
Thanks in advance!
I'm not sure I understand why you want to keep those separate in the first place.
What about simply putting them together as in the example here: https://learn.microsoft.com/en-us/azure/data-explorer/automated-deploy-overview#step-3-create-an-arm-template-to-deploy-the-cluster?
Ultimately, dependsOn doesn't accept reference functions as appeared in the error message. My second thought was to find out resource name using resourceID function, but apparently that's not supported. So, instead I have defined the server name in variables and used it for database "name field"
Because you're depending on a resource being deployed in the same deployment, you don't need to define a resource id, or use a reference. You can just use the name of the resource deployment (as defined in the arm template), like this:
{
"type": "Microsoft.Resources/deployments",
"name": "linkedTemplate",
etc
},
{
"type": "Microsoft.Kusto/Clusters/Databases",
etc
"dependsOn": [
"linkedTemplate"
]
}
That will ensure that the deployment of the database will not start until the deployment of the Kusto cluster has been completed.
Is there option copy iteration check conditionally in ARM templates? Example if copy index is zero set another value?
My ARM Code:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"storageAccountName": {
"type": "string"
},
"mediaServicesAccountName": {
"type": "string"
}
},
"functions": [],
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-01-01",
"name": "[concat('storage', copyIndex(), uniqueString(resourceGroup().id))]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"copy": {
"name": "storagecopy",
"count": 3
}
},
{
"type": "Microsoft.Media/mediaservices",
"apiVersion": "2020-05-01",
"name": "[parameters('mediaServicesAccountName')]",
"location": "[parameters('location')]",
"properties": {
"storageAccounts": [
{
"type": "Primary", # Primary if copyIndex is zero otherwise Secondary
"id": "[resourceId('Microsoft.Storage/storageAccounts', concat('storage', copyIndex(), uniqueString(resourceGroup().id)))]"
}
]
},
"identity": {
"type": "SystemAssigned"
},
"dependsOn": ["storagecopy"]
}
],
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.3.126.58533",
"templateHash": "2006367938138350540"
}
}
}
In above code I am creating 3 storage accounts and after that I am creating azure media service, I need to map storage accounts to azure media service dynamically. Under properties, I need to use copy loop and set Primary if index is zero else Secondary for defined number of storages.
Below Block implementation is required for copy loop condition:
"storageAccounts": [
{
"type": "Primary", # Primary if copyIndex is zero otherwise Secondary
"id": "[resourceId('Microsoft.Storage/storageAccounts', concat('storage', copyIndex(), uniqueString(resourceGroup().id)))]"
}
]
You need to make a separate loop over storageAccounts property:
{
"type": "Microsoft.Media/mediaservices",
"apiVersion": "2020-05-01",
...
"properties": {
"copy": [
{
"name": "storageAccounts",
"count": "3",
"input": {
"type": "[if(equals(copyIndex('storageAccounts'), 0), 'Primary', 'Secondary']", # Primary if copyIndex is zero otherwise Secondary
"id": "[resourceId('Microsoft.Storage/storageAccounts', concat('storage', copyIndex('storageAccounts'), uniqueString(resourceGroup().id)))]"
}
}
]
}
}
See more information here: Property iteration in ARM templates
I am trying to use nested resources with copy option as mentioned in https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#serial-copy
I have defined "lbApiVersion" inside nested resource variable block but some how the nested variable block is not recognize.
below is the ARM template i am trying and this is just example in actual scenario i want to pass array to arm template and then create multiple group of resources in loop, so in that case i need the nested variable block.
ARM -
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"numberToDeploy": {
"type": "int",
"minValue": 2,
"defaultValue": 2
}
},
"resources": [
{
"apiVersion": "2015-01-01",
"type": "Microsoft.Resources/deployments",
"name": "[concat('loop-', copyIndex())]",
"copy": {
"name": "iterator",
"count": "[parameters('numberToDeploy')]",
"mode": "serial",
"batchSize": 1
},
"properties": {
"mode": "Incremental",
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {
"lbApiVersion": "2015-06-15"
},
"resources": [
{
"apiVersion": "[variables('lbApiVersion')]",
"type": "Microsoft.Network/loadBalancers",
"name": "[concat('LB','-', copyIndex())]",
"location": "[parameters('clusterLocation')]",
"dependsOn": [
],
"properties": {
"frontendIPConfigurations": [
],
"backendAddressPools": [
],
"loadBalancingRules": [
],
"probes": [
],
"inboundNatPools": [
]
},
"tags": {
"resourceType": "Service Fabric"
}
}
],
"outputs": {
}
}
}
}
],
"outputs": {
}
}
"message": "Unable to process template language expressions for resource '/subscriptions/*************/resourceGroups/cluv2/providers/Microsoft.Resources/deployments/loop-0' at line '14' and column '10'. 'The template
variable 'lbApiVersion' is not found.
Based on my experience, when you define nested templates inline (so in the code of your existing template) they take parameter and variable values from your parent template, so just move the variable definition to your parent template
Unfortunately, you are not able to use variables and parameters in nested templates as indicated by the documentation. You can use them in external templates.
If you are trying to deploy multiple resources in an inline template, declare a variable or parameter of type object in the main template like this:
"variables" : {
"loadBalancers": [
{
"version": "2015-06-15"
},
{
"version": "2015-06-15"
}
]
}
Your copy on the Microsoft.Resources/deployments resource will look like this:
"copy": {
"name": "loadBalancerLoop",
"count": "[length(variables('loadBalancers'))]"
}
Then, in your nested resource, use the copyIndex() to grab the version
"apiVersion": "[variables('loadBalancers')[copyIndex()].version]"