How to: - ARM template conditional array with copy - azure

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

Related

Use of ARM template function "reference" in "dependsOn" fails with error: The template function 'reference' is not expected at this location

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.

Parameter with nested array - is iteration possible?

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.

Azure availability zone parameter syntax

I'm trying to parameterise a VM deployment that uses availability zones. However, I keep receiving this error on deployment:
'The provided value for the template parameter 'availabilityZoneParameter' at line '1' and column '5118' is not valid.'
or:
"Deployment template parse failed: 'Error converting value \"[ '1' ]\" to type 'System.String[]'. Path ''.'."
The parameter file syntax is currently:
"availabilityZoneParameter": {
"value": "[ '1' ]"
}
I am then porting it in as a parameter and turning it into a variable, before exporting it to other linked templates as well as using it in the initial build template.
Parameter in deploy file syntax:
"availabilityZoneParameter": {
"type": "string"
}
Variable in original deploy file syntax:
"availabilityZone": "[parameters('availabilityZoneParameter')]"
Disk creation syntax in original deploy file:
{
"name": "[variables('diskName')]",
"type": "Microsoft.Compute/disks",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"zones": [ "[variables('availabilityZone')]" ],
"sku": {
"name": "Standard_LRS"
},
"properties": {
"creationData": {
"createOption": "Empty"
},
"diskSizeGB": 1023
}
},
VM parameter in original deploy template, which feeds into linked template:
"name": "PAN-VM",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-05-01",
"dependsOn": [
"[concat('Microsoft.Compute/disks/', variables('diskName'))]",
"Microsoft.Resources/deployments/SettingUpVirtualNetwork",
"Microsoft.Resources/deployments/SettingUpPublicIP",
"Microsoft.Resources/deployments/SetupNetworkInterfaces"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(variables('virtualMachineTemplate'), parameters('artifactsLocationSasToken'))]",
"contentVersion": "1.0.0.5"
},
"parameters": {
"avZone": {
"value": "[variables('availabilityZone')]"
VM template parameter:
"avZone": {
"type": "string"
VM template variable:
"variables": {
"apiVersion": "2018-04-01",
"availabilityZone": "[parameters('avZone')]"
VM template resource (calling parameter):
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"zones": "[variables('availabilityZone')]",
"plan": {
"name": "[parameters('imageSku')]",
"product": "[parameters('imageOffer')]",
"publisher": "[parameters('imagePublisher')]"
},
"properties":
For context - there are several files at play here. An initial azureparameters file, an azuredeploy file, and then at least two linked templates which also rely on the availability zone value.
Any advice on the correct syntax?
According to the example I've found online, it should be like this:
"availabilityZoneParameter": {
"value": [ "1" ]
}
also, it should be array:
"availabilityZoneParameter": {
"type": "array"
}
As it excepts an array, not a string that looks like an array:
https://github.com/Azure/azure-quickstart-templates/blob/master/101-vm-simple-zones/azuredeploy.json#L176
Should the parameter just be ?
"availabilityZoneParameter": {
"value": "1"
}
Final syntax, for those coming to this board seeking the same answer:
Note that the value is an array and not a string, as pointed out by contributor 4c74356b41 in this thread.
In original azureparameter file:
},
"availabilityZone": {
"value": [ "3" ]
}
In azuredeploy file:
},
"availabilityZone": {
"type": "array"
}
To call the availability zone parameter in nested template (example using storage disk resource):
"name": "[variables('diskName')]",
"type": "Microsoft.Compute/disks",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"zones": "[parameters('availabilityZone')]",
"sku": {
If using a linked template, when expressing the linked template parameters, I used this syntax:
"avZone": {
"value": "[parameters('availabilityZone')]"
Importing the parameter in the linked template:
},
"avZone": {
"type": "array"
}
And then in the resources within the linked template, I called the parameter in the same way as the azuredeploy template:
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"zones": "[parameters('avZone')]",
As you can see, I decided not to turn it into a variable as this was unnecessary in my case.

Using copyindex() for subnet name creates error as a duplicate resource

I've tried using copyIndex() to create subnets with different names but I get the error
"message": "Resource
/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/ has two child
resources with the same name
([parameters('subnets').subnetProperties[copyIndex('subnets')].name)).
But I followed documation to use copy and this is what I've been using, so I'm not sure why is wouldn't move to the next name property:
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2016-03-30",
"name": "[parameters('virtualNetworkName')]",
"location": "[parameters('location')]",
"tags": "[parameters('virtualNetworkTags')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('vNetAddressSpaces')]"
]
},
"copy": [
{
"name": "subnets",
"count": "[parameters('numberOfSubnets')]",
"input": {
"name": "[parameters('subnets').subnetProperties[copyIndex('subnets')].name)",
"properties": {
"addressPrefix": "[parameters('subnets').subnetProperties[copyIndex('subnets')].addressPrefix]"
}
}
}
]
}
},
Param file:
"subnets":{
"value":{
"subnetProperties":[
{
"name":"firstSubnet",
"addressPrefix":"10.0.0.0/24"
},
{
"name":"secondSubnet",
"addressPrefix":"10.0.1.0/24"
}
]
}
},
I've also tried using copyIndex(), but that throws
template language expression evaluation failed: 'The template language
function 'copyIndex' has an invalid argument. The provided copy name '' doesn't exist in the
resource.
I think you messed up with the brackets in this line:
"name": "[parameters('subnets').subnetProperties[copyIndex('subnets')].name)",
It should look like:
"name": "[parameters('subnets').subnetProperties[copyIndex('subnets')].name]",
The last bracket is wrong. If the brackets does not match the complete expression will not processed. This will result in the same name on the second loop.
Greetings,
KirK

ARM Template concat resources

I have in ARM template:
"parameters": {
"applications": {
"value": "app1|app2|...|app(n)"
}
},
"variables": {
"applications": "[split(parameters('applications'), '|')]"
},
{
"name": "[concat('notificationhub', copyIndex())]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2016-09-01",
"dependsOn": [
"[concat('Microsoft.NotificationHubs/namespaces/',variables('notificationHubNamespace'))]"
],
"copy": {
"name": "notificationhubCopy",
"count": "[length(variables('applications'))]"
},
"parameters": {
"notificationHubNamespace": { "value" : "variables('notificationHubNamespace')]" },
"notificationHubName": { "value": "[concat('notificationhub-', variables('applications')[copyIndex()])]" },
...
}
}
},
How to concat created notificationhub1 and notificationhub2 into one value in app settings like
"notificationhub1.connection|notificationhub2.connection|...|notificationhub(n).connection"
or is there an option to dynamically create in app settings based on count properties with respective values?
{
"name": "[variables('webappName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"resources": [
{
"name": "appsettings",
"type": "config",
"properties": {
"MobileApps": "[parameters('applications')]",
"NotificationHubs": "???",
-- OR --
"App1NotificationHub": "notificationhub1.connection"
"App2NotificationHub": "notificationhub2.connection"
"App(n)NotificationHub": "notificationhubn(n).connection"
}
}
},
I can't be 100% sure on this, but looking at what you have here something like this should work:
[concat(listKeys(resourceId('Microsoft.EventHub/namespaces/authoriz‌​ationRules', 'eventHubNamespaceName', 'keyName'),'2015-08-01').primaryConnectionString, '|', listKeys(resourceId('Microsoft.EventHub/namespaces/authoriz‌​ationRules', 'eventHubNamespaceName', 'keyName'),'2015-08-01').primaryConnectionString)]
I can't verify if listkeys for eventhubs do work like this, but when you figure out how listkeys works, you should be able to paste that into the example above.
Also '|' might need escaping. I suppose escaping is done with \.
edit: Again, I'm not sure about this, I've never tried it, but you would want to use this link and try to reproduce it with notification hubs instead of storage accounts.

Resources