ARM template: issue with output in nested templates - azure

I'm currently facing an issue with nested template.
When I'm applying my template (detail below), I get this answer from Azure:
Azure Error: InvalidTemplate
Message: Deployment template validation failed: 'The template reference 'sandbox.test.portal' is not valid: could not find template resource or resource copy with this name. Please see https://aka.ms/arm-template-expressions/#reference for usage details.'.
However, I don't really understand why I get this issue, because for the content inside the nested template, I used what they provide in the documentation here: https://github.com/Azure/azure-quickstart-templates/blob/master/101-azure-dns-new-zone/azuredeploy.json
My ARM template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"newZoneName": {
"type": "string",
"defaultValue": "sandbox.test.portal",
"metadata": {
"description": "The name of the DNS zone to be created. Must have at least 2 segements, e.g. hostname.org"
}
},
"newRecordName": {
"type": "string",
"defaultValue": "www",
"metadata": {
"description": "The name of the DNS record to be created. The name is relative to the zone, not the FQDN."
}
}
},
"variables": {
"publicIPAddressName": "[concat(resourceGroup().name, '-pip')]",
},
"resources": [
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[parameters('location')]",
"properties": {
"publicIPAllocationMethod": "Dynamic",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsLabelPrefix')]"
}
}
},
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "my-rg",
"subscriptionId": "[subscription().subscriptionId]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
},
"resources": [
{
"type": "Microsoft.Network/dnszones",
"name": "[parameters('newZoneName')]",
"apiVersion": "2016-04-01",
"location": "global",
"properties": {
}
},
{
"type": "Microsoft.Network/dnszones/a",
"name": "[concat(parameters('newZoneName'), '/', parameters('newRecordName'))]",
"apiVersion": "2016-04-01",
"location": "global",
"dependsOn": [
"[parameters('newZoneName')]"
],
"properties": {
"TTL": 3600,
"ARecords": [
{
"ipv4Address": "1.2.3.4"
},
{
"ipv4Address": "1.2.3.5"
}
]
}
}
],
"outputs": {
"nameServers": {
"type": "array",
"value": "[reference(parameters('newZoneName')).nameServers]"
}
}
}
}
}
]
}

basically, you need to remove the outputs from the nested inline template, so remove this bit:
"outputs": {
"nameServers": {
"type": "array",
"value": "[reference(parameters('newZoneName')).nameServers]"
}
}
long story short, nested inline deployments are bad. dont use them.
alternatively move those to the parent template and do a real lookup:
reference(resourceId('Microsoft.Network/dnszones', parameters('newZoneName')))

You have several minor mistakes in your template:
Comma in variables '-pip')]",
Undefined parameter dnsLabelPrefix
The general mistake in nested outputs. When you use nested templates azure don't find it in your main template. Therefore you must use a reference function with identifier and API: "[reference(resourceId('Microsoft.Network/dnszones', parameters('newZoneName')), '2016-04-01').nameServers]".
I modified your template and validate in my subscription.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"newZoneName": {
"type": "string",
"defaultValue": "sandbox.test.portal",
"metadata": {
"description": "The name of the DNS zone to be created. Must have at least 2 segements, e.g. hostname.org"
}
},
"newRecordName": {
"type": "string",
"defaultValue": "www",
"metadata": {
"description": "The name of the DNS record to be created. The name is relative to the zone, not the FQDN."
}
},
"dnsLabelPrefix": {
"type": "string",
"defaultValue": "[concat('dns',uniqueString(resourceGroup().name))]"
},
"nestedResourceGroup": {
"type": "string",
"defaultValue": "my-rg",
"metadata": {
"description": "my-rg"
}
}
},
"variables": {
"publicIPAddressName": "[concat(resourceGroup().name, '-pip')]"
},
"resources": [
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "Dynamic",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsLabelPrefix')]"
}
}
},
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('nestedResourceGroup')]",
"subscriptionId": "[subscription().subscriptionId]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
},
"resources": [
{
"type": "Microsoft.Network/dnszones",
"name": "[parameters('newZoneName')]",
"apiVersion": "2016-04-01",
"location": "global",
"properties": {
}
},
{
"type": "Microsoft.Network/dnszones/a",
"name": "[concat(parameters('newZoneName'), '/', parameters('newRecordName'))]",
"apiVersion": "2016-04-01",
"location": "global",
"dependsOn": [
"[resourceId('Microsoft.Network/dnszones', parameters('newZoneName'))]"
],
"properties": {
"TTL": 3600,
"ARecords": [
{
"ipv4Address": "1.2.3.4"
},
{
"ipv4Address": "1.2.3.5"
}
]
}
}
],
"outputs": {
"nameServers": {
"type": "array",
"value": "[reference(resourceId('Microsoft.Network/dnszones', parameters('newZoneName')), '2016-04-01').nameServers]"
}
}
}
}
}
]
}
Have a nice day!

Related

Arm Template - Why am I getting an error saying resource is not defined in template?

I have a parent arm template that uses various linked component templates. The webApp I am creating requires a dependency on a service plan but after adding a dependency like the one in the dependencies section of the documentation I keep getting an error: 'The resource 'Microsoft.Resources/deployments/NovaArmTestDev' is not defined in the template.
The parent template: (top two deployments are the ones causing issue)
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourcegroupName": {
"type": "string",
"metadata": {
"description": "The name given to the group and all resources it contains by default"
}
},
"templateFolderUri": {
"type": "string",
"metadata": {
"description": "The URI of the template component folder"
}
}
},
"functions": [],
"variables": {},
"resources": [
{
"name": "[parameters('resourceGroupName')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('resourcegroupName')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('templateFolderUri'), '/servicePlanCreator.json')]",
"contentVersion": "1.0.0.0"
}
}
},
{
"name": "[concat(parameters('resourceGroupName'), 'App')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('resourcegroupName')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('templateFolderUri'), '/dualSlotWebApp.json')]",
"contentVersion": "1.0.0.0"
},
"parameters": {}
},
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', parameters('resourceGroupName'))]"
]
},
{
"name": "[concat(parameters('resourceGroupName'), 'Storage')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('resourcegroupName')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('templateFolderUri'), '/storageAccountTemplate.json')]",
"contentVersion": "1.0.0.0"
},
"parameters": {}
}
},
{
"name": "[concat(parameters('resourceGroupName'), 'Vault')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('resourcegroupName')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('templateFolderUri'), '/keyVaultCreator.json')]",
"contentVersion": "1.0.0.0"
},
"parameters": {}
}
}
],
"outputs": {}
}
servicePlanCreator:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"servicePlanName": {
"defaultValue": "[resourceGroup().name]",
"type": "string",
"metadata": {
"description": "The name of the newly created resource"
}
},
"operatingSystem": {
"type": "string",
"defaultValue": "windows",
"metadata": {
"description": "The Operating system the the newly created resource will use"
}
},
"sku": {
"type": "string",
"defaultValue": "S1",
"metadata": {
"description": "The sku (pricing tier) the resource group the service plan will use"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().Location]",
"metadata": {
"description": "(Optional) The location og the resource. Will default to the location of the resource group if not set."
}
}
},
"functions": [],
"variables": {},
"resources": [
{
"name": "[parameters('servicePlanName')]",
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2020-12-01",
"location": "[parameters('location')]",
"kind": "[parameters('operatingSystem')]",
"sku": {
"name": "[parameters('sku')]"
},
"tags": {},
"properties": {
"name": "[parameters('servicePlanName')]"
}
}
],
"outputs": {}
}
dualSlotWebApp template:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"webAppName": {
"type": "string",
"defaultValue": "[concat(resourceGroup().name)]",
"metadata": {
"description": "(Optional) Web App name. Defaults to '<ResourceGroupName>Plane' if not supplied"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "(Optional) Web App name. Defaults to Resource group location if not supplied"
}
},
"appServicePlan": {
"type": "string",
"defaultValue": "[resourceGroup().name]",
"metadata": {
"description": "name of the Service plan the app will be assigned to"
}
}
},
"functions": [],
"variables": {},
"resources": [
{
"name": "[parameters('webAppName')]",
"type": "Microsoft.Web/sites",
"apiVersion": "2020-12-01",
"location": "[parameters('location')]",
"properties": {
"name": "[parameters('webAppName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlan'))]"
},
"resources": [
{
"name": "[concat(parameters('webAppName'), '/Slot1')]",
"type": "Microsoft.Web/sites/slots",
"apiVersion": "2021-03-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('webAppName'))]"
],
"tags": {
"displayName": "Web Deploy for webApp1"
},
"properties": {
"packageUri": "[concat('artifactsLocation', '/WebPackages/webApp1.zip', 'artifactsLocationSasToken')]",
"dbType": "None",
"connectionString": "",
"setParameters": {
"IIS Web Application Name": "webApp1"
}
}
}
]
}
],
"outputs": {}
}
Referencing the templates from your earlier question, it appeared that the dependsOn was not configured correctly.
Originally, it was setup incorrectly with:
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]"
I updated in two places to use the same deployment name:
"[concat(parameters('resourceGroupName'), 'ServicePlan')]"
The two sections look like:
"resources": [
...
{
"name": "[concat(parameters('resourceGroupName'), 'ServicePlan')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('resourcegroupName')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('templateFolderUri'), '/servicePlanCreator.json')]",
"contentVersion": "1.0.0.0"
}
},
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('resourceGroupName'))]"
]
},
...
{
"name": "[concat(parameters('resourceGroupName'), 'App')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('resourcegroupName')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('templateFolderUri'), '/dualSlotWebApp.json')]",
"contentVersion": "1.0.0.0"
},
"parameters": {}
},
"dependsOn": [
"[concat(parameters('resourceGroupName'), 'ServicePlan')]"
]
}
],
Setting the dependency as I have ensures that the service plan completes deployment before the app begins deployment.

How to tag Current time as a Tag for an ARM Deployment

I am trying to create a Log Analytics Workspace using an ARM template and a parameter files. I am also thinking to tag currrent time as CreatedOn tag for the resource.
Below is my ARM template-
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"LAWName": {
"type": "string"
},
"LocationName": {
"type": "string"
},
"SKUName": {
"type": "string"
},
"Tags": {
"type": "object"
}
},
"resources": [
{
"apiVersion": "2017-03-15-preview",
"name": "[parameters('LAWName')]",
"location": "[parameters('LocationName')]",
"tags": "[parameters('Tags')]",
"type": "Microsoft.OperationalInsights/workspaces",
"properties": {
"sku": {
"name": "[parameters('SKUName')]"
}
}
}
]
}
and here is my param file-
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"LAWName": {
"value": "atifmtest1"
},
"LocationName": {
"value": "westeurope"
},
"SKUName": {
"value": "pergb2018"
}
"Tags": {
"value": {
"CreatedBy": "Atif",
"CreatedOn": "[utcNow()]",
"Purpose": "Monitoring"
}
}
}
}
I read here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-date#utcnow that there is utcNow() function for ARM template but that is being considered as a string here and the current time does not appear as a tag for the resource.
What is the other way using which this can be achieved ?
Thanks in advance !!
Here is a working example:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"utcShort": {
"type": "string",
"defaultValue": "[utcNow('d')]"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"resources": [
{
"apiVersion": "2019-04-01",
"type": "Microsoft.Storage/storageAccounts",
"name": "[concat('storage', uniqueString(resourceGroup().id))]",
"location": "[parameters('location')]",
"tags": {
"Dept": "Finance",
"Environment": "Production",
"LastDeployed": "[parameters('utcShort')]"
},
"sku": {
"name": "Standard_LRS"
},
"kind": "Storage",
"properties": {}
}
]
}
Source.
Please follow the below steps for better results.
Add the utcShort in parameters and give a default value "[utcNow()]", Its not work from the parameters file. add utcShort into variables to make an object type. Follow the below steps.
"utcShort ": {
"type": "string",
"defaultValue": "[utcNow()]"
},
"resourceTags": {
"type": "object"
}
},
"variables":{
"createdDate": {
"createdDate": "[parameters('utcShort ')]"
}
},
Use this variable in Tags like below..
"tags": "[union(parameters('resourceTags'), variables('createdDate'))]"

Azure Databricks with custom vnet arm template won't connect to the custom vnet

With the following ARM template, I deploy an Azure Databricks with a custom managed Resource Group Name and add the workers to a custom VNET. In the portal this works fine. But When I try to do this inside an ARM template the managed resource groups keep deploying a workers vnet for the workers. I am thinking that I am on the right track but missing one setting. But can't figure it out. Is there anyone who can see what I am missing ?
Source ARM: https://github.com/Azure/azure-quickstart-templates/tree/master/101-databricks-workspace-with-vnet-injection
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"databricksName": {
"type": "string",
"metadata": {
"description": "The name of the databricks workspace"
}
},
"pricingTier": {
"type": "string",
"allowedValues": [
"trial",
"standard",
"premium"
],
"metadata": {
"description": "The pricing tier of workspace."
}
},
"managedResourceGroupName": {
"type": "string",
"metadata": {
"description": "The name of the managed resource group that databricks will create"
}
},
"Location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The Location of the deployment"
}
},
"vnetName": {
"type": "string",
"metadata": {
"description": "The Name of the virtual network where the Workers would be connected to"
}
},
"privateSubnetName": {
"defaultValue": "public-subnet",
"type": "string",
"metadata": {
"description": "The name of the private subnet to create."
}
},
"publicSubnetName": {
"defaultValue": "private-subnet",
"type": "string",
"metadata": {
"description": "The name of the public subnet to create."
}
}
},
"variables": {
"ManagedResourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('managedResourceGroupName'))]",
"vnetId": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
},
"resources": [
{
"name": "[parameters('databricksName')]",
"type": "Microsoft.Databricks/workspaces",
"apiVersion": "2018-04-01",
"tags": {
"description": "MIG6 databricks workspace",
"costCenter": "WPIPM12SG552"
},
"location": "[parameters('Location')]",
"properties": {
"managedResourceGroupId": "[variables('managedResourceGroupId')]",
"parameters": {
"customVirtualNetworkId": {
"value": "[variables('vnetId')]"
},
"customPublicSubnetName": {
"value": "[parameters('publicSubnetName')]"
},
"customPrivateSubnetName": {
"value": "[parameters('privateSubnetName')]"
}
}
},
"sku": {
"name": "[parameters('pricingTier')]"
}
}
]
}
You need to nest the vnet in the template, this works for me:
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vnetName": {
"type": "string"
},
"vnetRG": {
"type": "string"
},
"publicSubnetName": {
"type": "string"
},
"publicSubnetCIDR": {
"type": "string"
},
"privateSubnetName": {
"type": "string"
},
"privateSubnetCIDR": {
"type": "string"
},
"workspaceName": {
"type": "string"
},
"tier": {
"type": "string"
},
"location": {
"type": "string"
},
"nsgName": {
"defaultValue": "databricks-nsg",
"type": "string"
},
"environment": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('vnetRG')]",
"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-04-01",
"type": "Microsoft.Network/virtualNetworks/subnets",
"name": "[concat(parameters('vnetName'), '/', parameters('publicSubnetName'))]",
"location": "[parameters('location')]",
"properties": {
"addressPrefix": "[parameters('publicSubnetCIDR')]",
"networkSecurityGroup": {
"id": "[variables('nsgId')]"
}
}
},
{
"apiVersion": "2018-04-01",
"type": "Microsoft.Network/virtualNetworks/subnets",
"name": "[concat(parameters('vnetName'), '/', parameters('privateSubnetName'))]",
"location": "[parameters('location')]",
"dependsOn": [
"[concat('Microsoft.Network/virtualNetworks/', parameters('vnetName'), '/subnets/', parameters('publicSubnetName'))]"
],
"properties": {
"addressPrefix": "[parameters('privateSubnetCIDR')]",
"networkSecurityGroup": {
"id": "[variables('nsgId')]"
}
}
}
]
},
"parameters": {}
}
},
{
"apiVersion": "2018-04-01",
"type": "Microsoft.Databricks/workspaces",
"location": "[parameters('location')]",
"name": "[parameters('workspaceName')]",
"dependsOn": [
"['Microsoft.Resources/deployments/nestedTemplate']"
],
"sku": {
"name": "[parameters('tier')]"
},
"comments": "Please do not use an existing resource group for ManagedResourceGroupId.",
"properties": {
"ManagedResourceGroupId": "[variables('managedResourceGroupId')]",
"parameters": {
"customVirtualNetworkId": {
"value": "[variables('vnetId')]"
},
"customPublicSubnetName": {
"value": "[parameters('publicSubnetName')]"
},
"customPrivateSubnetName": {
"value": "[parameters('privateSubnetName')]"
}
}
}
}
],
"variables": {
"managedResourceGroupId": "[concat(subscription().id, '/resourceGroups/', variables('managedResourceGroupName'))]",
"managedResourceGroupName": "[concat(resourceGroup().name,'-DATABRICKS-MANAGED')]",
"vnetId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('vnetRG'), '/providers/Microsoft.Network/virtualNetworks/', parameters('vnetName'))]",
"nsgId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('vnetRG'), '/providers/Microsoft.Network/networkSecurityGroups/', parameters('nsgName'))]"
},
"outputs": {}
}

Azure ARM deployment - copyIndex error with multiple NICs

I am trying to deploy the below Palo Alto deployment to an Azure environment. We are using it as an internal firewall for the various features it provides that NSGs don't, so we don't want a public IP. We're using a virtual gateway and sticking the firewall behind it in an active-active pair (the load balancer, NSGs, etc. are being done in a separate template).
I've been trying to get this template to work for a while now, but I'm stuck on deploying the two VMs with multiple NICs. I keep getting this error:
ERROR: Azure Error: InvalidTemplate
Message: Deployment template validation failed: 'The template variable 'nicName' is not valid: The template function 'copyIndex' is not expected at this location. The function can only be used in a resource with copy specified. Please see https://aka.ms/arm-copy for usage details.. Please see https://aka.ms/arm-template-expressions for usage details.'.
I've tried a bunch of fixes - changing the variable syntax, changing the syntax of the resource, but none of them are working. I've checked the Azure documentation on using the copyIndex feature, but I can't see where I'm going wrong. I was hoping someone with a bit more experience could point out where my syntax is wrong and provide suggestions on how to correct it?
Many thanks, template is below:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"LoadBalancerSku": {
"type": "string",
"allowedValues": [
"Standard",
"Basic"
],
"metadata": {
"description": "Std LB load balances across all the ports where as Basic LB load balances on a port-to-port basis"
},
"defaultValue": "Standard"
},
"storageName": {
"type": "string",
"metadata": {
"description": "Name of the storage account created to store the VM's disks. Storage account name must be globally unique."
},
"defaultValue": "Enter a globally unique name"
},
"mgmtPublicIPDns": {
"type": "string",
"metadata": {
"description": "DNS Name prefix of public IP resource for Management interface of VM-Series firewall. Name must be globally unique."
},
"defaultValue": "Enter a globally unique name"
},
"networkSecurityGroupName": {
"type": "string",
"defaultValue": "nsg",
"metadata": {
"description": "Network Security Group Name"
}
},
"networkSecurityGroupInboundIP": {
"type": "string",
"metadata": {
"description": "Your source public IP address. Added to the inbound NSG on eth0 (MGMT), to restrict access to the deployment."
},
"defaultValue": "1.1.1.1/32"
},
"avSetName": {
"type": "string",
"metadata": {
"description": "Name of the availability set for outbound firewall"
},
"defaultValue": "outbound-avset"
},
"storageType": {
"type": "string",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Premium_LRS",
"Standard_RAGRS"
],
"metadata": {
"description": "Type of the storage account created"
},
"defaultValue": "Standard_LRS"
},
"virtualNetworkName": {
"type": "string",
"defaultValue": "firewall-test",
"metadata": {
"description": "Virtual Network Name"
}
},
"virtualNetworkAddressPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/16",
"metadata": {
"description": "CIDR for Virtual Network"
}
},
"mgmtSubnetName": {
"type": "string",
"defaultValue": "Mgmt",
"metadata": {
"description": "Subnet for Management Network"
}
},
"mgmtSubnetPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/24",
"metadata": {
"description": "CIDR for Management Network"
}
},
"untrustSubnetName": {
"type": "string",
"defaultValue": "Untrust",
"metadata": {
"description": "Subnet for Untrusted Network"
}
},
"untrustSubnetPrefix": {
"type": "string",
"defaultValue": "10.0.1.0/24",
"metadata": {
"description": "CIDR for Untrusted Network"
}
},
"trustSubnetName": {
"type": "string",
"defaultValue": "Trust",
"metadata": {
"description": "Subnet for Trusted Network"
}
},
"trustSubnetPrefix": {
"type": "string",
"defaultValue": "10.0.2.0/24",
"metadata": {
"description": "CIDR for Trusted Network"
}
},
"mgmtPublicIPName": {
"type": "string",
"metadata": {
"description": "Name prefix of public IP resource for Management interface of VM-Series firewall."
},
"defaultValue": "mgmt-pip"
},
"loadBalancerName": {
"type": "string",
"metadata": {
"description": "Name for the outbound load balancer resource."
},
"defaultValue": "outbound-lb"
},
"loadBalancerIP": {
"type": "string",
"metadata": {
"description": "IP Address for the outbound load balancer resource in the Trust network."
},
"defaultValue": "10.0.2.4"
},
"imageSku": {
"type": "string",
"defaultValue": "bundle1",
"allowedValues": [
"byol",
"bundle1",
"bundle2"
],
"metadata": {
"description": "byol = Bring Your Own License; bundle1 = Bundle 1 PAYG (Hourly); bundle2 = Bundle 2 PAYG (Hourly)"
}
},
"virtualMachineName": {
"type": "string",
"metadata": {
"description": "Name prefix of VM-Series VM in the Azure portal"
},
"defaultValue": "outbound-vm-series"
},
"vmSize": {
"type": "string",
"allowedValues": [
"Standard_D3",
"Standard_D4",
"Standard_D3_v2",
"Standard_D4_v2",
"Standard_D5_v2",
"Standard_D14_v2",
"Standard_A4"
],
"metadata": {
"description": "Azure VM size for VM-Series"
},
"defaultValue": "Standard_D3_v2"
},
"authenticationType": {
"type": "string",
"metadata": {
"description": "Type of administrator user authentication "
},
"allowedValues": [
"sshPublicKey",
"password"
],
"defaultValue": "password"
},
"adminUsername": {
"type": "string",
"defaultValue": "pandemo",
"metadata": {
"description": "Username of the administrator account of VM instances"
}
},
"adminPassword": {
"type": "securestring",
"defaultValue": "Dem0pa$$w0rd",
"metadata": {
"description": "Password for the administrator account of all VM instances. This must be specified if Authentication Type is 'password'."
}
},
"sshKey": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "SSH RSA public key file as a string. Must be specified if Authentication Type is 'sshPublicKey'."
}
},
"vmCount": {
"type": "int",
"defaultValue": 2,
"metadata": {
"description": "Number of VM-Series firewall"
}
}
},
"variables": {
"baseUrl": "http://git.lr.net/Azure/management/firewall/tree/master/azure-pan-hub",
"deployStorageURL": "[concat(variables('baseUrl'),'/deployStorage.json')]",
"deployVnetURL": "[concat(variables('baseUrl'),'/deployVnet.json')]",
"deployFirewallURL": "[concat(variables('baseUrl'),'/deployFirewall.json')]",
"location": "[resourceGroup().location]",
"rgname": "[resourceGroup().name]",
"nicName": "[concat(parameters('virtualMachineName'), copyindex())]",
"imagePublisher": "paloaltonetworks",
"imageOffer": "vmseries1",
"version": "latest",
"vnetname": "[parameters('virtualNetworkName')]",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('vnetname'))]",
"mgmtSubnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('mgmtSubnetName'))]",
"untrustSubnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('untrustSubnetName'))]",
"trustSubnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('trustSubnetName'))]",
"subnets": [
{
"name": "[parameters('mgmtSubnetName')]",
"properties": {
"addressPrefix": "[parameters('mgmtSubnetPrefix')]"
}
},
{
"name": "[parameters('untrustSubnetName')]",
"properties": {
"addressPrefix": "[parameters('untrustSubnetPrefix')]"
}
},
{
"name": "[parameters('trustSubnetName')]",
"properties": {
"addressPrefix": "[parameters('trustSubnetPrefix')]"
}
}
]
},
"resources": [
{
"name": "deployStorage",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[parameters('storageName')]",
"apiVersion": "2015-06-15",
"location": "[variables('location')]",
"properties": {
"accountType": "[parameters('storageType')]"
}
}
]
}
}
},
{
"name": "deployVnet",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2015-06-15",
"location": "[variables('location')]",
"name": "[variables('vnetname')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('virtualNetworkAddressPrefix')]"
]
},
"subnets": "[variables('subnets')]"
}
}
]
}
}
},
{
"name": "deployAvailabilitySet",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"dependsOn": [
"Microsoft.Resources/deployments/deployStorage",
"Microsoft.Resources/deployments/deployVNet"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"apiVersion": "2015-05-01-preview",
"type": "Microsoft.Compute/availabilitySets",
"name": "[parameters('avSetName')]",
"location": "[variables('location')]"
}
]
}
}
},
{
"name": "deployMgmtNetworkInterface",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-08-01",
"dependsOn": [
"Microsoft.Resources/deployments/deployStorage",
"Microsoft.Resources/deployments/deployVNet"
],
"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('virtualMachineName'), copyindex(), '-nic0')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[variables('location')]",
"apiVersion": "2015-06-15",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', parameters('mgmtPublicIPName'), copyindex())]"
],
"copy": {
"name": "nicLoop",
"count": "[parameters('vmCount')]"
},
"properties": {
"ipConfigurations": [
{
"name": "ipconfig-mgmt",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[concat(variables('vnetId'),'/subnets/', parameters ('mgmtSubnetName'))]"
}
}
}
]
}
}
]
}
}
},
{
"name": "deployUntrustNetworkInterface",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-08-01",
"dependsOn": [
"Microsoft.Resources/deployments/deployStorage",
"Microsoft.Resources/deployments/deployVNet"
],
"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('virtualMachineName'), copyindex(), '-nic1-std')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[variables('location')]",
"apiVersion": "2015-06-15",
"copy": {
"name": "nicLoop",
"count": "[parameters('vmCount')]"
},
"properties": {
"enableIPForwarding": true,
"ipConfigurations": [
{
"name": "ipconfig-untrust",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[concat(variables('vnetId'),'/subnets/', parameters ('untrustSubnetName'))]"
}
}
}
]
}
}
]
}
}
},
{
"name": "deploytrustNetworkInterface",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-08-01",
"dependsOn": [
"Microsoft.Resources/deployments/deployStorage",
"Microsoft.Resources/deployments/deployVNet"
],
"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('virtualMachineName'), copyindex(), '-nic2')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[variables('location')]",
"apiVersion": "2015-06-15",
"copy": {
"name": "nicLoop",
"count": "[parameters('vmCount')]"
},
"properties": {
"enableIPForwarding": true,
"ipConfigurations": [
{
"name": "ipconfig-trust",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[concat(variables('vnetId'),'/subnets/', parameters ('trustSubnetName'))]"
}
}
}
]
}
}
]
}
}
},
{
"name": "[concat(parameters('virtualMachineName'), '-std-', copyindex())]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-08-01",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', parameters('virtualMachineName'), copyindex(), '-nic0')]",
"[concat('Microsoft.Network/networkInterfaces/', parameters('virtualMachineName'), copyindex(), '-nic1-std')]",
"[concat('Microsoft.Network/networkInterfaces/', parameters('virtualMachineName'), copyindex(), '-nic2')]"
],
"copy": {
"name": "vmLoop",
"count": "[parameters('vmCount')]"
},
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"name": "[concat(parameters('virtualMachineName'), '-std')]",
"location": "[variables('location')]",
"apiVersion": "2015-05-01-preview",
"plan": {
"name": "[parameters('imageSku')]",
"product": "[variables('imageOffer')]",
"publisher": "[variables('imagePublisher')]"
},
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[parameters('virtualMachineName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "[variables('imagePublisher')]",
"offer": "[variables('imageOffer')]",
"sku": "[parameters('imageSku')]",
"version": "latest"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat('http://', parameters('storageName'), '.blob.core.windows.net/vhds/', parameters('virtualMachineName'), '-', variables('imageOffer'), '-', parameters('imageSku'), '.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicName'),'-nic0'))]",
"properties": {
"primary": true
}
},
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicName'),'-nic1-std'))]",
"properties": {
"primary": false
}
},
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicName'),'-nic2'))]",
"properties": {
"primary": false
}
}
]
}
}
}
]
}
}
}
]
}
so in general with loops, you can only use copyIndex() function inside loops (and you are trying to use it outside of loop). with variables you can use this (same method applies to property loops):
"variables": {
"copy": [
{
"name": "real_var_name_goes_here",
"count": "how_many_items_with_var",
"input": {
"key": "value" << have to use copyIndex('real_var_name_goes_here')
}
}
]
}
and you'd use normal way for regular loops
Reading:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple

ARM - Add multiple VM to Recovery Services Vault (copyIndex)

I'm trying to use the Recovery Services where I can automatically add a VM to Azure Backup via ARM template. I have successfully done this on a single machine deploy, but I'm trying to import it for when multiple VMs are deployed.
Here is where I had help from:
https://www.francoisdelport.com/2017/03/automating-azure-vm-backups-using-arm-templates/
and
Azure ARM JSON template - Add VM to Recovery Services Vault in different Resource Group
Here is a snippet from a single deploy I had working
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "Env1",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
],
"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": "2016-06-01",
"name": "[concat( parameters('recoveryVault'), '/Azure/', 'iaasvmcontainer;iaasvmcontainerv2;', parameters('vmRsg') , ';', parameters('vmPrefix'), '/vm;iaasvmcontainerv2;', parameters('vmRsg'),';', parameters('vmPrefix'))]",
"location": "[resourceGroup().location]",
"type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
"properties": {
"protectedItemType": "Microsoft.Compute/virtualMachines",
"policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', parameters('recoveryVault'), parameters('recoveryPolicy'))]",
"sourceResourceId": "[resourceId(subscription().subscriptionId, parameters('vmRsg'), 'Microsoft.Compute/virtualMachines', parameters('vmPrefix'))]"
}
}
]
}
}
}
Now I'm trying to use that in a copyIndex form for VM deploy, and here is the code I've been testing with:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUsername": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Username for the Virtual Machine."
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Password for the Virtual Machine."
}
},
"dnsNameForPublicIP": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Globally unique DNS Name for the Public IP used to access the Virtual Machine."
}
},
"windowsOSVersion": {
"type": "string",
"defaultValue": "2012-R2-Datacenter",
"allowedValues": [
"2008-R2-SP1",
"2012-Datacenter",
"2012-R2-Datacenter"
],
"metadata": {
"description": "The Windows version for the VM. This will pick a fully patched image of this given Windows version. Allowed values: 2008-R2-SP1, 2012-Datacenter, 2012-R2-Datacenter."
}
},
"vmCount": {
"type": "int",
"defaultValue": 1
},
"virtualNetworkName": {
"type": "string"
},
"dataDiskCount": {
"type": "int",
"defaultValue": 1
},
"recoveryVault": {
"type": "string",
"metadata": {
"description": "Backup vault name"
}
},
"recoveryPolicy": {
"type": "string",
"metadata": {
"description": "Backcup policy name"
}
},
"vmPrefix": {
"type": "string",
"metadata": {
"description": "Prefix for VM names, used with vmCount to build the VM names"
}
},
"vmRsg": {
"type": "string",
"metadata": {
"description": "Resource group where VMs reside"
}
}
},
"variables": {
"imagePublisher": "MicrosoftWindowsServer",
"imageOffer": "WindowsServer",
"OSDiskName": "osdiskforwindowssimple",
"nicName": "myVMNic",
"subnetName": "Subnet",
"vhdStorageType": "Standard_LRS",
"publicIPAddressName": "myPublicIP",
"publicIPAddressType": "Dynamic",
"vhdStorageContainerName": "vhds",
"vmName": "MWindowsVM",
"vmSize": "Standard_A2",
"virtualNetworkName": "MyVNET",
"vnetId": "[resourceId(resourceGroup().name, 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
"subnetRef": "[concat(variables('vnetId'), '/subnets/', variables('subnetName'))]"
},
"resources": [
{
"apiVersion": "2016-03-30",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[concat(variables('publicIPAddressName'), copyIndex(1))]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "PublicIPAddress"
},
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[concat(parameters('dnsNameForPublicIP'), copyIndex(1))]"
}
},
"copy": {
"name": "publicIpCopy",
"count": "[parameters('vmCount')]"
}
},
{
"apiVersion": "2016-03-30",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('nicName'), copyIndex(1))]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "NetworkInterface"
},
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', concat(variables('publicIPAddressName'), copyIndex(1)))]"
],
"properties": {
"ipConfigurations": [
{
"name": "[concat('ipconfig', copyIndex(1))]",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', concat(variables('publicIPAddressName'), copyIndex(1)))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
},
"copy": {
"name": "nicCopy",
"count": "[parameters('vmCount')]"
}
},
{
"apiVersion": "2017-03-30",
"copy": {
"name": "nodeCopy",
"count": "[parameters('vmCount')]"
},
"type": "Microsoft.Compute/virtualMachines",
"name": "[concat(variables('vmName'), copyIndex(1))]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "VirtualMachine"
},
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces/', concat(variables('nicName'), copyIndex(1)))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[variables('vmSize')]"
},
"osProfile": {
"computerName": "[concat(variables('vmName'), copyIndex(1))]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "[variables('imagePublisher')]",
"offer": "[variables('imageOffer')]",
"sku": "[parameters('windowsOSVersion')]",
"version": "latest"
},
"osDisk": {
"createOption": "FromImage"
},
"copy": [
{
"name": "dataDisks",
"count": "[parameters('dataDiskCount')]",
"input": {
"diskSizeGB": 1023,
"lun": "[copyIndex('dataDisks')]",
"createOption": "Empty"
}
}
]
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('nicName'), copyIndex(1)))]"
}
]
}
}
},
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "Env1",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', concat(variables('vmName'), copyIndex(1)))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"copy": {
"name": "protectedItemsCopy",
"count": "[parameters('vmCount')]"
},
"apiVersion": "2017-03-30",
"name": "[concat( parameters('recoveryVault'), '/Azure/', 'iaasvmcontainer;iaasvmcontainerv2;', parameters('vmRsg') , ';', parameters('vmPrefix'), copyIndex(1), '/vm;iaasvmcontainerv2;', parameters('vmRsg'),';', parameters('vmPrefix'), copyIndex(1))]",
"location": "[resourceGroup().location]",
"type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
"properties": {
"protectedItemType": "Microsoft.Compute/virtualMachines",
"policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', parameters('recoveryVault'), parameters('recoveryPolicy'))]",
"sourceResourceId": "[resourceId(subscription().subscriptionId ,parameters('vmRsg'),'Microsoft.Compute/virtualMachines', concat(parameters('vmPrefix'), copyIndex(1)) )]"
}
}
]
}
}
}
]
}
Sadly it reports an error when trying to deploy, which I can't figure out why because it seems to be correct.
Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template resource 'nestedTemplate' at line '198' and column '10' is not valid: The template function 'copyIndex' is not expected at this location. The function can only be used in a resource with copy specified. Please see https://aka.ms/arm-copy for usage details.. Please see https://aka.ms/arm-template-expressions for usage details.'.
The deployment validation failed
FYI, line 198 is "name": "nestedTemplate",
Any ideas, please?
To expand upon #4c74356b41 answer I was missing the all important "index":{ "value": "[copyIndex()]" within "Microsoft.Resources/deployments" on the parent template.
For those wanting to know more, have a look at: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple
Ensure you have the ('index') parameter next to those items which need to be duplicated, such as "[concat(parameters('WHATEVER'), parameters('index'))]"
I also ended up having a nested source within my linked template for the overall design I was looking for.
So my parent template had a linked (child) template (to another file) with:
name": "[concat('nestings', copyIndex(1))]",
"type": "Microsoft.Resources/deployments", ...
My child template had all the usual buildings of a VM with the parameters ('index') to ensure the items which are duplicated are named correctly.
And finally at the bottom of the child template I had a nested template source so I could back the VM up to another resource group (had to be nested, otherwise you can't do multiple resource groups), which looked like this:
{
"apiVersion": "2017-05-10",
"name": "[concat('nestedTemplate', parameters('index'))]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "Env1",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', concat(variables('vmName'), parameters('index')))]"
],
"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": "2016-06-01",
"name": "[concat( parameters('recoveryVault'), '/Azure/', 'iaasvmcontainer;iaasvmcontainerv2;', parameters('vmRsg') , ';', concat(parameters('vmPrefix'), parameters('index')), '/vm;iaasvmcontainerv2;', parameters('vmRsg'),';', concat(parameters('vmPrefix'), parameters('index')))]",
"location": "[resourceGroup().location]",
"type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
"properties": {
"protectedItemType": "Microsoft.Compute/virtualMachines",
"policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', parameters('recoveryVault'), parameters('recoveryPolicy'))]",
"sourceResourceId": "[resourceId(subscription().subscriptionId, parameters('vmRsg'), 'Microsoft.Compute/virtualMachines', concat(parameters('vmPrefix'), parameters('index')))]"
}
}
]
}
}
}
So what its telling you that you are not supposed to use copyIndex() function in that place. Now why exactly this is happening I don't know, but I do know that inline templates are a mess (for instance they use parent template paremeters, not nested template), I'm pretty sure if you convert that template to a real nested template (so a linked template, completely separate file) the above syntax will work.
Also, I'm handling this in a separate manner. I'm using 1 single nested deployment for each VM I have, so I'm using copy on the deployment resource, not backup resource.

Resources