Reputation: 43
We're trying to create templates using objects for parameters so there's the option of having multiple values in different resources, i.e. deploy an Event Hub namespace that could have multiple authorization rules and eventhubs, but another object in the parameters for a second Event Hub namespace that may only have one of each.
The template is like below:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"eventhubs": {
"type": "object",
"metadata": {
"description": "JSON object that describes the deployment. see example parameters file"
}
}
},
"variables": {
"resourceNamePrefix": "[substring(resourceGroup().name, 0, 8)]",
"datacenterCode": "[substring(resourceGroup().name, 0, 3)]",
"productCode": "[substring(resourceGroup().name, 3, 3)]",
"environmentLevel": "[substring(resourceGroup().name, 6, 2)]"
},
"resources": [
{
"type": "Microsoft.EventHub/namespaces",
"name": "[concat(variables('resourceNamePrefix'), parameters('eventhubs').instances[copyIndex()].name)]",
"apiVersion": "2015-08-01",
"location": "[resourceGroup().location]",
"sku": {
"name": "[concat(variables('resourceNamePrefix'), parameters('eventhubs').instances[copyIndex()].sku.name)]",
"tier": "[parameters('eventhubs').instances[copyIndex()].sku.tier]",
"capacity": "[parameters('eventhubs').instances[copyIndex()].sku.capacity]"
},
"copy": {
"name": "eventHubCopy",
"count": "[length(parameters('eventhubs').instances)]"
},
"properties": {
"serviceBusEndpoint": "[concat('https://',variables('resourceNamePrefix'), parameters('eventhubs').instances[copyIndex()].name,'.servicebus.windows.net:443/')]",
"enabled": "[parameters('eventhubs').instances[copyIndex()].properties.enabled]"
},
"resources": [
*** PARAMETER OBJECT ***
]
"dependsOn": []
}
],
"outputs": {}
}
And the parameters file:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"eventhubs": {
"value": {
"instances": [
{
"name": "EVT001",
"sku": {
"name": "Standard",
"tier": "Standard",
"capacity": 4
},
"scale": null,
"properties": {
"enabled": "true"
},
"resources": [
{
"type": "AuthorizationRules",
"name": "SendKey",
"apiVersion": "2015-08-01",
"properties": {
"rights": [
"Send"
]
}
},
{
"type": "AuthorizationRules",
"name": "ListenKey",
"apiVersion": "2015-08-01",
"properties": {
"rights": [
"Listen"
]
}
},
{
"type": "EventHub",
"name": "TestHub",
"apiVersion": "2015-08-01",
"properties": {
"messageRetentionInDays": 7,
"status": "Active",
"partitionCount": 4
}
}
]
},
{
"name": "EVT002",
"sku": {
"name": "Standard",
"tier": "Standard",
"capacity": 4
},
"scale": null,
"properties": {
"enabled": "true"
},
"resources": [
{
"type": "AuthorizationRules",
"name": "SendKey",
"apiVersion": "2015-08-01",
"properties": {
"rights": [
"Send"
]
}
},
{
"type": "EventHub",
"name": "TestHub",
"apiVersion": "2015-08-01",
"properties": {
"messageRetentionInDays": 7,
"status": "Active",
"partitionCount": 4
}
},
{
"type": "EventHub",
"name": "SecondHub",
"apiVersion": "2015-08-01",
"properties": {
"messageRetentionInDays": 7,
"status": "Active",
"partitionCount": 4
}
}
]
}
]
}
}
}
}
What I'm attempting to do is move the content of the resources array in the parameters file into the nested resources array in the template file. This is possible when moving an array into an object, but I'm facing the following problems with an array into an array:
"resources": "[parameters('eventhubs').instances[copyIndex()].properties]", <--- value must be of type array
"resources": [ { "[parameters('eventhubs').instances[copyIndex()].properties]" } ], <--- expecting a name and value as it's in an object
"resources": [ "[parameters('eventhubs').instances[copyIndex()].properties]" ], <--- value must be of the following types: object
Adding another set of square brackets around the object in the array in the parameters file doesn't help either.
Same errors when using the createArray function.
The workaround I have is to do
"resources": [
{
"type": "AuthorizationRules",
"name": "[parameters('eventhubs').instances[copyIndex()].resources[0].name]",
"apiversion": "[parameters('eventhubs').instances[copyIndex()].resources[0].apiversion]",
"properties": "[parameters('eventhubs').instances[copyIndex()].resources[0].properties]",
"dependsOn": [ "[concat(variables('resourceNamePrefix'), parameters('eventhubs').instances[copyIndex()].name)]" ]
}
],
But the type property cannot be an expression so won't work for the way our templates will be consumed and used.
Is it possible to do what I'm attempting?
Upvotes: 0
Views: 7964
Reputation: 72151
this won't really fit into an answer (and i, sadly, dont have time to type out all the explanations), but you are basically looking to separate configuration and implementation (which is a wise thing to do), but ARM templates are not as straight forward as you would think. I will try to create a mcve. It will be really abstract, but you only need the idea.
So the premise: you need to deploy X amount of resource with different properties.
you config data looks like this:
"eventhubs": [
{
"name": "EVT001",
"sku": skuobject,
"scale": null,
"properties": propertiesobject,
"resources": [
nestedobject,
nestedobject,
nestedobject
]
},
parentobject,
]
This object is nested object inside array inside parent object inside array (i suggest you drop the outermost object, as it is useless and does nothing except for adding complexity). Your course of action, iterate outermost (parent object) array in a copy loop and attempt to iterate innermost (nested object) array in the properties of the resource. which doesnt work for ARM templates.
With arm templates you can only iterate one level deep (with a loop, with hardcode you can go as deep as you want) so what you need to do is split your configuration object into objects that are 1 level deep. your object is 2 levels deep (array of parent object and array of nested objects). you can do that with a nested deployment.
What you need to do is something like this:
{
"name": "[concat('nested-', copyIndex())]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2016-06-01",
"copy": {
"name": "eventHubLoop",
"count": "[length(parameters('eventhubs'))]" (this should be an array)
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "xxx",
"contentVersion": "1.0.0.0"
},
"parameters": {
"currentIteration": {
"value": "[parameters('eventhubs')[copyIndex()]]"
}
}
}
}
and your nested template should be parent resource (no copy, as it is always a single resource, we took care of that with the loop above) and properties\resources loop. unfortunately i dont have an example handy (nda), but i hope you get the idea. you need to create 2 distinct loops, that way you can handle your level of nestedness, if you have 3 levels of nestedness, you need 3 distinct loops (you can achieve that the same way)
you can of course make this much more complex and parametrize almost anything (location, different resource groups, different additional properties), but i feel like this level of complexity doesnt add anything to the table but rather makes you create a properties file exactly as a template (and this is the point where it gets useless if you ask me).
Upvotes: 1