Search code examples
azureinfrastructure-as-code

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
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}
]
}

Solution

  • 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