Azure ARM copy Index not working in Json - json

I am trying to deploy multiple Azure VNETs using the code below,it
it gives error below
{
"error": {
"code": "InvalidTemplate",
"details": null,
"message": "Deployment template validation failed: 'The template resource '[concat(variables('namePrefix'), parameters('VNetSettings').vnets.name, [copyIndex(1)])]' at line '1' and column '923' is not valid: The language expression property 'name' has an invalid array index.. Please see https://aka.ms/arm-template-expressions for usage details.'.",
"target": null
},
"properties": null
}
This is my Code below. I want to use coyIndex to loop through multiple instances of azure virtual networks. Based on number of vnet names provided i want it get number of instances to be created.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"VNetSettings": {
"type": "object"
},
"namingSettings": {
"type": "object"
}
},
"variables": {
"namePrefix": "[concat(parameters('namingSettings').name.org,'-',parameters('namingSettings').name.accountEnv,'-',parameters('namingSettings').name.sdlcEnv,'-',parameters('namingSettings').name.region,'-',parameters('namingSettings').name.appname,'-')]"
},
"resources": [
{
"apiVersion": "2018-04-01",
"type": "Microsoft.Network/virtualNetworks",
"name": "[concat(variables('namePrefix'), parameters('VNetSettings').vnets.name, [copyIndex(1)])]",
"location": "[resourceGroup().location]",
"copy": {
"name": "vnetcopy",
"count": "[length(parameters('VNetSettings').vnets.name)]"
},
"scale": null,
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('VNetSettings').vnets.cidr]"
]
}
}
}
]
}
Update
Parameter fileenter code here
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters":{
"namingSettings": {
"value": {
"name": {
"org": "it",
"accountEnv": "nonprod",
"sdlcEnv": "it-test",
"region": "eastus2",
"tier": "",
"object": "",
"networkbuild": "network",
"auditbuild": "audit",
"automationbuild": "automation",
"dnsbuild": "dns",
"appname": "network"
}
}
},
"VNetSettings": {
"value": {
"vnets": [
{
"name": "vnet0",
"cidr": "10.10.10.0/24",
}
],
}
}
}
}

Your concat() input is wrong. copyIndex() should not be inside []. Try this:
"[concat(variables('namePrefix'), parameters('VNetSettings').vnets.name, copyIndex(1))]"
copyIndex() is just another function, you only wrap the string with [] once no matter how many functions are inside
you need to adjust your template to reflect the fact you are iterating over an array (right now you iterate over the name, and vnets.name is not a valid construct in the ARM Templates).
you need to do something like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"VNetSettings": {
"type": "object"
},
"namingSettings": {
"type": "object"
}
},
"variables": {
"namePrefix": "[concat(parameters('namingSettings').name.org,'-',parameters('namingSettings').name.accountEnv,'-',parameters('namingSettings').name.sdlcEnv,'-',parameters('namingSettings').name.region,'-',parameters('namingSettings').name.appname,'-')]"
},
"resources": [
{
"apiVersion": "2018-04-01",
"type": "Microsoft.Network/virtualNetworks",
"name": "[concat(variables('namePrefix'), parameters('VNetSettings').vnets[copyIndex()].name)]",
"location": "[resourceGroup().location]",
"copy": {
"name": "vnetcopy",
"count": "[length(parameters('VNetSettings').vnets)]"
},
"scale": null,
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('VNetSettings').vnets[copyIndex()].cidr]"
]
}
}
}
]
}
notice you need to use copyIndex() to get to the current vnet in your array and you need to use the .vnet to determine length

Related

Loop for nested template and dynamically get keyvault secret name

Based on information from Microsoft site, it is possible get key vault secrets during the implementation. I would like to have similar solution but for 5 VMs and reuse templates in Loop to create 5 VMs with different password from key vault. The key vault already exists with the secrets name. The secrets name are like: Secrets0...Secrets4.
Does anyone has any idea of how to do this? Below my solution with Loop but doesn't works for me.
I appreciate for your support.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The location where the resources will be deployed."
}
},
"vaultName": {
"type": "string",
"metadata": {
"description": "The name of the keyvault that contains the secret."
}
},
"secretName": {
"type": "string",
"metadata": {
"description": "The name of the secret."
}
},
"vaultResourceGroupName": {
"type": "string",
"metadata": {
"description": "The name of the resource group that contains the keyvault."
}
},
"vaultSubscription": {
"type": "string",
"defaultValue": "[subscription().subscriptionId]",
"metadata": {
"description": "The name of the subscription that contains the keyvault."
}
}
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "[concat('DynamicSecret,copyIndex('VMsLoop'))]"
"copy": {
"name": "VMsLoop",
"count": 5,
"mode": "Serial",
"batchSize": 1
},
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adminLogin": {
"type": "string"
},
"adminPassword": {
"type": "securestring"
},
"location": {
"type": "string"
}
},
"variables": {
"sqlServerName": "[concat('sql-', uniqueString(resourceGroup().id, 'sql'))]"
},
"resources": [
{
"type": "Microsoft.Sql/servers",
"apiVersion": "2018-06-01-preview",
"name": "[variables('sqlServerName')]",
"location": "[parameters('location')]",
"properties": {
"administratorLogin": "[parameters('adminLogin')]",
"administratorLoginPassword": "[parameters('adminPassword')]"
}
}
],
"outputs": {
"sqlFQDN": {
"type": "string",
"value": "[reference(variables('sqlServerName')).fullyQualifiedDomainName]"
}
}
},
"parameters": {
"location": {
"value": "[parameters('location')]"
},
"adminLogin": {
"value": "ghuser"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "[resourceId(parameters('vaultSubscription'), parameters('vaultResourceGroupName'), 'Microsoft.KeyVault/vaults', parameters('vaultName'))]"
},
"secretName": "[concat(parameters('secretName'),copyIndex('VMsLoop'))]"
}
}
}
}
}
],
"outputs": {
}
}
The template you have provided actually works (except for one tiny typo - missing closing quote after DynamicSecret at "[concat('DynamicSecret,copyIndex('VMsLoop'))]" ).
However, since the uniquestring function that is used to generate the sql server name is deterministic (i.e. generated using the passed parameters of resoure group name and 'sql' string - uniqueString(resourceGroup().id, 'sql'). So the loop is hitting the same server instance just changing the password to the next secret in the list.
You can either promote the sqlServerName to parameter and suffix it with the loop index, or keep it as a variable and expand the uniqueString function with the deployment (i.e. uniqueString(resourceGroup().id, deployment().name, 'sql') ).

Multiple IP address variable in Azure ARM Template

If I am using an ARM Template to create an IP Group within Azure and want to add Multiple IP Addresses as a Parameter rather than putting them in the body of the resource is this possible?
The template is as follows
{
"type": "Microsoft.Network/ipGroups",
"apiVersion": "2021-05-01",
"name": "string",
"location": "string",
"tags": {
"tagName1": "tagValue1",
"tagName2": "tagValue2"
},
"properties": {
"ipAddresses": [ "10.10.10.10",
"10.10.10.11" ]
}
}
If I create a Parameter like the following
"ipgipaddress": {
"type": "string
"Value":
"10.10.10.10",
"10.10.10.11"
}
And update the code to
{
"type": "Microsoft.Network/ipGroups",
"apiVersion": "2021-05-01",
"name": "string",
"location": "string",
"tags": {
"tagName1": "tagValue1",
"tagName2": "tagValue2"
},
"properties": {
"ipAddresses": "[parameters('ipgroupsettings')]"
}
}
want to add Multiple IP Addresses as a Parameter rather than putting
them in the body of the resource is this possible?
Yes it is possible, you can add Multiple IP Addresses as a Parameter. As per the documentation, the property ipAddresses is of type string array.
To reproduce this ,In our ARM Template we have initialized those ipaddresses to a parameter of type array & further passed those values using "[paramerter(ipaddresses)]".
We have tested this, it is working fine.
Here is the ARM Template :
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"ipaddress":{
"type": "array",
"defaultValue": ["10.0.1.0/27","10.0.2.0/27"]
}
},
"functions": [],
"variables": {},
"resources": [{
"type": "Microsoft.Network/ipGroups",
"apiVersion": "2020-11-01",
"name": "testipgroups",
"location":"[resourceGroup().location]",
"properties":{
"ipAddresses": "[parameters('ipaddress')]"
}
}
],
"outputs": {}
}
Here is the sample Output for reference:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"ipaddress":{
"type": "string",
"defaultValue": "10.0.1.0/27,10.0.2.0/27"
}
},
"functions": [],
"variables": {
"iparray": "[split(parameters('ipaddress'),',')]"
},
"resources": [{
"type": "Microsoft.Network/ipGroups",
"apiVersion": "2020-11-01",
"name": "testipgroups",
"location":"[resourceGroup().location]",
"properties":{
"ipAddresses": "[variables('iparray')]"
}
}
],
"outputs": {}
}
If you want to save your time in providing input as String without much double quotes(" ") ,use split function with removing delimiter (comma in above example) which will create an array of IP's.

JSON to encrypt vms in loop of ARM template

I have a JSON ARM template which creates multiple vms through a loop, I thought I could add encryptvm at the end of the loop and it would encrypt all the disks.
However it keeps failing with a strange error
The full json is here:
https://pastebin.com/embed_iframe/Lxmb7Y42
I've used both these parameters.
, parameters('VMNames'), copyIndex(1))]",
and
, parameters('VMNames'))]"
but neither seems to work.
All I'm doing essentially is adding the following resource section in:
"resources": [
{
"name": "[concat(parameters('VMNames'),'UpdateEncryptionSettings)']",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('VMNames'))]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('_artifactsLocation'),'/nestedtemplates/encryptVm.json',parameters('_artifactsLocationSasToken'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"vmName": {
"value": "[parameters('VMNames')]"
},
"aadClientID": {
"value": "[parameters('aadClientID')]"
},
"aadClientSecret": {
"value": "[parameters('aadClientSecret')]"
},
"keyVaultName": {
"value": "[parameters('keyVaultName')]"
},
"keyVaultResourceGroup": {
"value": "[parameters('keyVaultResourceGroup')]"
},
"useExistingKek": {
"value": "[parameters('useExistingKek')]"
},
"keyEncryptionKeyURL": {
"value": "[parameters('keyEncryptionKeyURL')]"
},
"_artifactsLocation": {
"value": "[parameters('_artifactsLocation')]"
},
"_artifactsLocationSasToken": {
"value": "[parameters('_artifactsLocationSasToken')]"
}
}
}
},
{
"apiVersion": "2017-05-10",
"name": "[concat(parameters('VMNames'),'recoveryServicesVault')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('recoveryServicesVaultResourceGroup')]",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('VMNames'))]"
// "[resourceId('Microsoft.Resources/deployments/', concat(parameters('VMNames'), copyIndex(1),'UpdateEncryptionSettings'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"name": "[concat(parameters('recoveryServicesVaultName'), '/', 'Azure', '/', variables('rsvV2vm'), resourceGroup().name, ';', parameters('VMNames'))]",
"apiVersion": "2017-07-01",
"type": "Microsoft.RecoveryServices/vaults/backupFabrics/backupProtectionIntent",
"properties": {
"friendlyName": "[concat(parameters('VMNames'), copyIndex(1), 'BackupIntent')]",
"protectionIntentItemType": "AzureResourceItem",
"policyId": "[resourceId(parameters('recoveryServicesVaultResourceGroup'), 'Microsoft.RecoveryServices/vaults/backupPolicies', parameters('recoveryServicesVaultName'), parameters('recoveryServicesVaultBackupPolicyName'))]",
"sourceResourceId": "[resourceId(resourceGroup().name, 'Microsoft.Compute/virtualMachines', parameters('VMNames'))]"
}
}
]
}
}
}
]
Can't see where I'm going wrong. Maybe I've added it in the wrong place, but checking on JSON validator tools online and everything seems fine.
this template is due to the fact you are defining your deployment once for each vm, but you are giving each deployment the same name:
"name": "[concat(parameters('VMNames'),'UpdateEncryptionSettings')]",
"type": "Microsoft.Resources/deployments",
you need to add copyIndex() function to this name

Azure ARM Template different Resource Group Dependency Issue

I have a template that deploys a new template in a dedicated resource group for VNETs. I want to deploy a network interface in a subnet of the VNET, but put the network interface in its own separate resource group. When I run the template I receive the following error. I am sure it is a dependency issue, but the dependsOn parameter is not working for me... any ideas?
Recieved Error
"error": {
"code": "InvalidResourceReference",
"message": "Resource /subscriptions/{removed-subscription-id}/resourceGroups/vnet-resource-group/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/subnet-1 referenced by resource /subscriptions/{removed-subscription-id}/resourceGroups/test-rg-1/providers/Microsoft.Network/networkInterfaces/my-first-nic was not found. Please make sure that the referenced resource exists, and that both resources are in the same region.",
"details": []
}
}
Template
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vnetName": {
"type": "string",
"defaultValue": "my-vnet"
},
"vnetResourceGroup": {
"type": "string",
"defaultValue": "vnet-resource-group"
},
"nicResourceGroup": {
"type": "string",
"defaultValue": "nic-resource-group"
}
},
"variables": {
"vnetID": "[resourceId(parameters('vnetResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]",
"subnetID": "[concat(variables('vnetID'),'/subnets/','subnet-1')]"
},
"resources": [
{
"apiVersion": "2017-05-10",
"name": "vnetNestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('vnetResourceGroup')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"apiVersion": "2018-06-01",
"name": "my-vnet",
"type": "Microsoft.Network/virtualNetworks",
"location": "[resourceGroup().location]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.0.0.0/16"
]
},
"subnets": [
{
"name": "subnet-1",
"properties": {
"addressPrefix": "10.0.0.0/24"
}
}
]
}
}
]
}
}
},
{
"apiVersion": "2017-05-10",
"name": "nestedNicTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('nicResourceGroup')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/networkInterfaces",
"name": "my-first-nic",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId(parameters('vnetResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Static",
"subnet": {
"id": "[variables('subnetID')]"
},
"privateIpAddress": "10.0.0.5"
}
}
]
}
}
]
}
}
}
]
}
You need to add:
"dependsOn": [
"vnetNestedTemplate"
]
to the second deployment. in the template you can only depend on sources that are in the same template. since you are invoking a nested deployment, its not in the same template (but in the nested template).
ps. maybe you are just showing an over simplified example, but there is no point in using nested deployment (ESPECIALLY INLINE) for this.
pps. i'd suggest against using INLINE nested deployments, they really have their weird mechanics

How to create multiple Service Bus topics using ARM template?

I've looked at the https://github.com/Azure/azure-quickstart-templates/tree/master/101-servicebus-topic example. Now I'm trying to figure out how to create several topics when running the deploy script using nested resource looping (https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-multiple/#looping-on-a-nested-resource).
It would be nice to just define an array in the value for serviceBusTopicName and then somehow creating topics by looping through it, instead of doing it manually.
I am getting the following error when trying to deploy.
Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template resource '[parameters('serviceBusTopicName')[copyIndex()]]' at line '72' column '14' is not valid.
Copying nested resources is not supported. Please see https://aka.ms/arm-copy/#looping-on-a-nested-resource for usage details.'.
This is the template.json that I have tried to get to work.
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"serviceBusTopicName": {
"type": "array",
"metadata": {
"description": "Name of the Topic"
}
},
"serviceBusApiVersion": {
"type": "string",
"defaultValue": "2015-08-01",
"metadata": {
"description": "Service Bus ApiVersion used by the template"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusNamespaceName')]",
"type": "Microsoft.ServiceBus/Namespaces",
"location": "[variables('location')]",
"kind": "Messaging",
"sku": {
"name": "StandardSku",
"tier": "Standard"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusTopicName')]",
"type": "Topics",
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
],
"properties": {
"path": "[parameters('serviceBusTopicName')]"
},
"copy": {
"name": "datasetcopy",
"count": "[length(parameters('serviceBusTopicName'))]"
}
}
]
},
parameters.json
{"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"value": "ServiceBus"
},
"serviceBusTopicName": {
"value": [
"Person",
"Stadium",
"Team"
]
},
"serviceBusApiVersion": {
"value": "2015-08-01"
}
}
As #Lain said, copying nested resource isn't supported as of now Check Here. For deploying multiple resource you have to move it to root. I just finished my POC for deploying multiple topics (Just topics assuming Namespace already exists) here is the code. I am passing topic names as a comma separated string:
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"serviceBusTopicName": {
"type": "string",
"metadata": {
"description": "Comma seperated Topic Names"
}
},
"serviceBusApiVersion": {
"type": "string",
"defaultValue": "2015-08-01",
"metadata": {
"description": "Service Bus ApiVersion used by the template"
}
}
},
"variables": {
"location": "[resourceGroup().location]",
"sbVersion": "[parameters('serviceBusApiVersion')]",
"TopicNames": "[split(parameters('serviceBusTopicName'), ',')]"
},
"resources": [{
"apiVersion": "[variables('sbVersion')]",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', variables('TopicNames')[copyIndex()])]",
"type": "Microsoft.ServiceBus/Namespaces/Topics",
"copy": {
"name": "TopicNameCopy",
"count": "[length(variables('TopicNames'))]"
},
"properties": {
"path": "[variables('TopicNames')[copyIndex()]]"
}
}]
}
The link in the error message goes through this pretty well, but you can't have loops in nested resources, you need to push the resource up to the top level, and then link the resources together using names. This template will do what you seem to be trying to do:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus Namespace"
}
},
"serviceBusTopicNames": {
"type": "array",
"metadata": {
"description": "Name of the Service Bus Topic"
}
}
},
"variables": {
"sbVersion": "2015-08-01"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusNamespaceName')]",
"type": "Microsoft.ServiceBus/namespaces",
"location": "[resourceGroup().location]",
"properties": {
}
},
{
"apiVersion": "[variables('sbVersion')]",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('serviceBusTopicNames')[copyIndex()])]",
"type": "Microsoft.ServiceBus/namespaces/Topics",
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
],
"copy": {
"name": "datasetcopy",
"count": "[length(parameters('serviceBusTopicNames'))]"
},
"properties": {
"path": "[parameters('serviceBusTopicNames')[copyIndex()]]"
},
"resources": [
]
}
]
}