Using Copy command in ARM template with properties that repeat and some that do not - arm-template

I'm creating an ARM template that has rules for FTP traffic. I'd like to use the copy command within the probes property of the load balancer to create probes for a range of ports, but I also have other probes that do not need to be repeated (fall outside of the copy). When I try to add both, the ARM template says it's invalid because the probes property is already present. Is it possible to use the "copy" syntax in addition to other value or must the "copy" be the only ARM syntax that builds the entire list of array values?
{
"type": "Microsoft.Network/loadBalancers",
"sku": {
"name": "Standard",
"tier": "Regional"
},
"name": "[variables('lb-csl-private.name')]",
"apiVersion": "2018-10-01",
"location": "[resourceGroup().location]",
"scale": null,
"properties": {
"frontendIPConfigurations": [
{
"name": "LoadBalancerFrontEnd",
"properties": {
"privateIPAddress": "[variables('ip-lb-csl-private')]",
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnet-csl.name'), variables('vnet-csl.subnets.loadBalancerName'))]"
}
}
}
],
"copy": [
{
"name": "probes",
"count": 10,
"input": {
"name": "[concat('probe-CSL-PASV-', copyIndex('probes'))]",
"properties": {
"protocol": "Tcp",
"port": "[copyIndex('probes')]",
"inervalInSeconds": 5,
"numberOfProbes": 2
}
}
}
],
"probes": [
{
"name": "probe-CSL-FTP",
"properties": {
"protocol": "Tcp",
"port": 21,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
}
],
"inboundNatRules": [],
"outboundRules": [],
"inboundNatPools": []
}
}

it is possible to do that using variables:
"variables": {
"copy": [
{
"name": "probes",
"count": 10,
"input": {
"name": "[concat('probe-CSL-PASV-', copyIndex('probes'))]",
"properties": {
"protocol": "Tcp",
"port": "[copyIndex('probes')]",
"inervalInSeconds": 5,
"numberOfProbes": 2
}
}
}
],
"otherProbes": [
{
probe1
},
{
probe2
},
etc
]
}
and then, in your load balancer:
"probes": "[concat(variables('probes'), variables('otherProbes'))]"

Related

Azure FrontDoor: how to set up backendPool with multiple instance inside?

I started Infrastructure as Code with ARM Template and previously all my deployment was made with Powershell. Hope you can help me to fix this issue.
I would like to deploy {2 app services + Azure FrontDoor]. In FrontDoor-Backendpool I want to define the 2 appservices. Below my code:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "array",
"metadata": {
"description": "array of region"
},
"defaultValue": [
"centralus",
"eastus"
]
},
"Stage": {
"type": "string",
"metadata": {
"description": "Stage dev, prod"
},
"allowedValues": [
"Dev",
"Prod"
],
"defaultValue": "Dev"
}
},
"functions": [],
"variables": {
"appServicePlanName": "[concat('AppServicePlan-', parameters('Stage'),'-')]",
"appServiceName": "[concat('AppService-', parameters('Stage'), '-')]",
"frontDoorName": "[concat('FrontDoor-', parameters('Stage'), uniqueString(resourceGroup().id))]"
},
"resources": [
{ // App Service Plan
"type": "Microsoft.Web/serverfarms",
"name": "[concat(variables('appServicePlanName'),parameters('location')[copyIndex()])]",
"apiVersion": "2018-02-01",
"copy": {
"count": "[length(parameters('location'))]",
"name": "copy multiple"
},
"location": "[parameters('location')[copyIndex()]]",
"sku": {
"name": "F1",
"capacity": 1
},
"tags": {
"cost": "[parameters('Stage')]"
},
"properties": {
"name": "[concat(variables('appServicePlanName'),parameters('location')[copyIndex()])]"
}
},
{ // App Services
"type": "Microsoft.Web/sites",
"name": "[concat(variables('appServiceName'), parameters('location')[copyIndex()])]",
"apiVersion": "2018-11-01",
"copy": {
"name": "Copy website",
"count": "[length(parameters('location'))]"
},
"location": "[parameters('location')[copyIndex()]]",
"tags": {
"cost": "[parameters('Stage')]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', concat(variables('appServicePlanName'),parameters('location')[copyIndex()]))]"
],
"properties": {
"name": "[concat(variables('appServiceName'), parameters('location')[copyIndex()])]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', concat(variables('appServicePlanName'),parameters('location')[copyIndex()]))]"
}
},
{ // Front Door
"type": "Microsoft.Network/frontDoors",
"apiVersion": "2020-05-01",
"name": "[variables('frontDoorName')]",
"location": "global",
"properties": {
"routingRules": [
{
"name": "routingRule1",
"properties": {
"frontendEndpoints": [
{
"id": "[resourceId('Microsoft.Network/frontDoors/frontendEndpoints', variables('frontDoorName'), 'frontendEndpoint1')]"
}
],
"acceptedProtocols": [
"Http",
"Https"
],
"patternsToMatch": [
"/*"
],
"routeConfiguration": {
"#odata.type": "#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration",
"forwardingProtocol": "MatchRequest",
"backendPool": {
"id": "[resourceId('Microsoft.Network/frontDoors/backendPools', variables('frontDoorName'), 'backendPool1')]"
}
},
"enabledState": "Enabled"
}
}
],
"healthProbeSettings": [
{
"name": "healthProbeSettings1",
"properties": {
"path": "/",
"protocol": "Http",
"intervalInSeconds": 120
}
}
],
"loadBalancingSettings": [
{
"name": "loadBalancingSettings1",
"properties": {
"sampleSize": 4,
"successfulSamplesRequired": 2
}
}
],
"backendPools": [
{
"id": "backendPool1",
"name": "backendPool1",
"properties": {
"copy": [
{
"name": "backends",
"count": "[length(parameters('location'))]",
"input": {
"address": "[concat(variables('appServiceName'), parameters('location')[copyIndex()], '.azurewebsites.net') ]",
"httpPort": 80,
"httpsPort": 443,
"weight": 50,
"priority": 1,
"enabledState": "Enabled"
}
}
],
"loadBalancingSettings": {
"id": "[resourceId('Microsoft.Network/frontDoors/loadBalancingSettings', variables('frontDoorName'), 'loadBalancingSettings1')]"
},
"healthProbeSettings": {
"id": "[resourceId('Microsoft.Network/frontDoors/healthProbeSettings', variables('frontDoorName'), 'healthProbeSettings1')]"
}
}
}
],
"frontendEndpoints": [
{
"name": "frontendEndpoint1",
"properties": {
"hostName": "[concat(variables('frontDoorName'), '.azurefd.net')]",
"sessionAffinityEnabledState": "Enabled"
}
}
],
"enabledState": "Enabled"
}
}
],
"outputs": {}
}
As you can see i iterate on paramater location to create my AppService Plan and AppService and it worked well. So I thought to do same for BackEndpool.
Here part of code which break my head
address": "[concat(variables('appServiceName'), parameters('location')[copyIndex()], '.azurewebsites.net') ]",
Something is wrong inside but I have no idea why.
Error retuned is:
Error: Code=InvalidTemplate; Message=Deployment template language expression evaluation
failed: 'The template language function 'copyIndex' has an invalid argument. The provided copy name '' doesn't exist in the resource.
Please see https://aka.ms/arm-copy for usage details.'. Please see https://aka.ms/arm-template-expressions for usage details.
I take my inspiration from official MS documentation link from MS
Any idea on how I can fix it ?
Thx
You need to include the copy name property in the call to copyIndex in the backendPools part. That is why is says "The provided copy name '' doesn't exist". The property copy is treated a little differently than the resource copy.
"The loopName property enables you to specify whether copyIndex is referring to a resource iteration or property iteration. If no value is provided for loopName, the current resource type iteration is used. Provide a value for loopName when iterating on a property."
Source: https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-numeric#copyindex
parameters('location')[copyIndex('backends')]

Azure Application Gateway DNS returning 307 to backend pool

I am trying to configure Azure Application Gateway with Basic Rule. For my Frontend IP, I have created set DNS name to whatever.canadacentral.cloudapp.azure.com and uploaded a self-signed certificate. When I hit https:// everything works correctly however when I go to https://whatever.canadacentral.cloudapp.azure.com it returns 307 redirecting me to my backend pool https://whatever.azurewebsites.net/
Is this something to do with canadacentral.cloudapp.azure.com and I need to provide custom DNS?
Here's my template for Application Gateway:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationGateways_ExampleDev_name": {
"defaultValue": "ExampleDev",
"type": "String"
},
"virtualNetworks_Ex_DEV_externalid": {
"defaultValue": "/subscriptions/xxx/resourceGroups/Example-Ex-DEV/providers/Microsoft.Network/virtualNetworks/Ex-DEV",
"type": "String"
},
"publicIPAddresses_ExampleDevIP_externalid": {
"defaultValue": "/subscriptions/xxx/resourceGroups/Example-Ex-DEV/providers/Microsoft.Network/publicIPAddresses/ExampleDevIP",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Network/applicationGateways",
"apiVersion": "2019-09-01",
"name": "[parameters('applicationGateways_ExampleDev_name')]",
"location": "canadacentral",
"properties": {
"sku": {
"name": "WAF_v2",
"tier": "WAF_v2"
},
"gatewayIPConfigurations": [
{
"name": "appGatewayIpConfig",
"properties": {
"subnet": {
"id": "[concat(parameters('virtualNetworks_Ex_DEV_externalid'), '/subnets/default')]"
}
}
}
],
"sslCertificates": [
{
"name": "ApplicationGateway",
"properties": {}
}
],
"trustedRootCertificates": [],
"frontendIPConfigurations": [
{
"name": "appGwPublicFrontendIp",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[parameters('publicIPAddresses_ExampleDevIP_externalid')]"
}
}
}
],
"frontendPorts": [
{
"name": "port_80",
"properties": {
"port": 80
}
},
{
"name": "port_443",
"properties": {
"port": 443
}
}
],
"backendAddressPools": [
{
"name": "ExampleApiDev",
"properties": {
"backendAddresses": [
{
"fqdn": "Exampleapi-dev.azurewebsites.net"
}
]
}
},
{
"name": "ExampleAuthDev",
"properties": {
"backendAddresses": [
{
"fqdn": "Exampleauth-dev.azurewebsites.net"
}
]
}
},
{
"name": "ExampleAppDev",
"properties": {
"backendAddresses": [
{
"fqdn": "Exampleapp-dev.azurewebsites.net"
}
]
}
}
],
"backendHttpSettingsCollection": [
{
"name": "default",
"properties": {
"port": 80,
"protocol": "Http",
"cookieBasedAffinity": "Disabled",
"pickHostNameFromBackendAddress": true,
"affinityCookieName": "ApplicationGatewayAffinity",
"requestTimeout": 20,
"probe": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/probes/defaultxxx')]"
}
}
}
],
"httpListeners": [
{
"name": "public-https",
"properties": {
"frontendIPConfiguration": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/frontendIPConfigurations/appGwPublicFrontendIp')]"
},
"frontendPort": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/frontendPorts/port_443')]"
},
"protocol": "Https",
"sslCertificate": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/sslCertificates/ApplicationGateway')]"
},
"hostNames": [],
"requireServerNameIndication": false
}
}
],
"urlPathMaps": [],
"requestRoutingRules": [
{
"name": "basic",
"properties": {
"ruleType": "Basic",
"httpListener": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/httpListeners/public-https')]"
},
"backendAddressPool": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/backendAddressPools/ExampleApiDev')]"
},
"backendHttpSettings": {
"id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGateways_ExampleDev_name')), '/backendHttpSettingsCollection/default')]"
}
}
}
],
"probes": [
{
"name": "default07a3e3ac-3c07-40f6-ad80-837f4cdd1009",
"properties": {
"protocol": "Http",
"path": "/swagger/index.html",
"interval": 30,
"timeout": 30,
"unhealthyThreshold": 3,
"pickHostNameFromBackendHttpSettings": true,
"minServers": 0,
"match": {
"statusCodes": [
"200-399"
]
}
}
}
],
"rewriteRuleSets": [],
"redirectConfigurations": [],
"webApplicationFirewallConfiguration": {
"enabled": true,
"firewallMode": "Prevention",
"ruleSetType": "OWASP",
"ruleSetVersion": "3.0",
"disabledRuleGroups": [],
"exclusions": [],
"requestBodyCheck": true,
"maxRequestBodySizeInKb": 128,
"fileUploadLimitInMb": 50
},
"enableHttp2": false,
"autoscaleConfiguration": {
"minCapacity": 0,
"maxCapacity": 2
}
}
}
]
}
In this case, for application gateway V2, you have two solutions from this document.
Rewrite the location header
Set the host name in the location header to the application gateway's
domain name. To do this, create a rewrite rule with a condition that
evaluates if the location header in the response contains
azurewebsites.net. It must also perform an action to rewrite the
location header to have the application gateway's host name.
Use a custom domain name
In this way, you must own a custom domain and add custom domain in app servvice, see Map an existing custom DNS name to Azure App Service. You could follow this process:

How to get Private IP of HDI cluster using ARM template

I have created template1 which will deploy HDI cluster and template2 which will deploy Azure VM seperately.
Now I want to get the Head-node Private IP from cluster and pass it to Azure VM template for processing using ARM template.
How can I do so?
Considering this is the object you are getting from HDcluster:
{
"id": "xxx",
"name": "xxx",
"type": "Microsoft.HDInsight/clusters",
"location": "East US",
"etag": "xxx",
"tags": null,
"properties": {
"clusterVersion": "3.5.1000.0",
"osType": "Linux",
"clusterDefinition": {
"blueprint": "https://blueprints.azurehdinsight.net/spark-3.5.1000.0.9865375.json",
"kind": "SPARK",
"componentVersion": {
"Spark": "1.6"
}
},
"computeProfile": {
"roles": [
{
"name": "headnode",
"targetInstanceCount": 2,
"hardwareProfile": {
"vmSize": "ExtraLarge"
},
"osProfile": {
"linuxOperatingSystemProfile": {
"username": "sshuser"
}
}
},
{
"name": "workernode",
"targetInstanceCount": 1,
"hardwareProfile": {
"vmSize": "Large"
},
"osProfile": {
"linuxOperatingSystemProfile": {
"username": "sshuser"
}
}
},
{
"name": "zookeepernode",
"targetInstanceCount": 3,
"hardwareProfile": {
"vmSize": "Medium"
},
"osProfile": {
"linuxOperatingSystemProfile": {
"username": "sshuser"
}
}
}
]
},
"provisioningState": "Succeeded",
"clusterState": "Running",
"createdDate": "2017-04-11T09:07:44.68",
"quotaInfo": {
"coresUsed": 20
},
"connectivityEndpoints": [
{
"name": "SSH",
"protocol": "TCP",
"location": "xxx.azurehdinsight.net",
"port": 22
},
{
"name": "HTTPS",
"protocol": "TCP",
"location": "xxx.azurehdinsight.net",
"port": 443
}
],
"tier": "standard"
}
}
I'm guessing this is the best output you can get, so you can use something like:
"outputs": {
"test": {
"type": "Object",
"value": "[reference(parameters('clusterName'),'2015-03-01-preview').connectivityEndpoints[0].location]"
}
}
This will get you an output of xxx.azurehdinsight.net
And you can either create a new deployment with this data or (just like I said) add RHEL VM to the same template and make it dependOn on the HDCluster deployment and reference the same thing as an input to VMextension.

Can I loop over properties in ARM templates?

I have an ARM template where I set up a load balancer and I want to add a number of port openings by adding rules and probes to the LB.
This is the template I have so far:
{
"type": "Microsoft.Network/loadBalancers",
"name": "LB-front",
"apiVersion": "2016-03-30",
"location": "westeurope",
"tags": { },
"properties": {
"frontendIPConfigurations": [
{
"name": "LoadBalancerIPConfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_lbipdev_0_name'))]"
}
}
}
],
"backendAddressPools": [
{
"name": "LoadBalancerBEAddressPool"
}
],
"loadBalancingRules": [
{
"name": "AppPortLBRule1",
"properties": {
"frontendIPConfiguration": {
"id": "[parameters('loadBalancers_LB_dev_id_6')]"
},
"frontendPort": 80,
"backendPort": 80,
"enableFloatingIP": false,
"idleTimeoutInMinutes": 5,
"protocol": "Tcp",
"loadDistribution": "Default",
"backendAddressPool": {
"id": "[parameters('loadBalancers_LB_dev_id_7')]"
},
"probe": {
"id": "[parameters('loadBalancers_LB_dev_id_8')]"
}
}
},
{
"name": "AppPortLBRule2",
"properties": {
"frontendIPConfiguration": {
"id": "[parameters('loadBalancers_LB_dev_id_9')]"
},
"frontendPort": 81,
"backendPort": 81,
"enableFloatingIP": false,
"idleTimeoutInMinutes": 5,
"protocol": "Tcp",
"loadDistribution": "Default",
"backendAddressPool": {
"id": "[parameters('loadBalancers_LB_dev_id_10')]"
},
"probe": {
"id": "[parameters('loadBalancers_LB_dev_id_11')]"
}
}
},
{
"name": "AppPortLBRule3",
"properties": {
"frontendIPConfiguration": {
"id": "[parameters('loadBalancers_LB_dev_id_12')]"
},
"frontendPort": 82,
"backendPort": 82,
"enableFloatingIP": false,
"idleTimeoutInMinutes": 5,
"protocol": "Tcp",
"loadDistribution": "Default",
"backendAddressPool": {
"id": "[parameters('loadBalancers_LB_dev_id_13')]"
},
"probe": {
"id": "[parameters('loadBalancers_LB_dev_id_14')]"
}
}
}
],
"probes": [
{
"name": "AppPortProbe1",
"properties": {
"protocol": "Tcp",
"port": 80,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
},
{
"name": "AppPortProbe2",
"properties": {
"protocol": "Tcp",
"port": 81,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
},
{
"name": "AppPortProbe3",
"properties": {
"protocol": "Tcp",
"port": 82,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
}
],
"inboundNatRules": [],
"outboundNatRules": [],
"inboundNatPools": []
},
"resources": [],
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_lbipdev_1_name'))]"
]
},
(some details omitted)
What I would like to do is to have an array of the port numbers I want to create rules and probes for and loop over those instead of explicitly having to write each rule and probe as a property for the resource.
Basically I would like a parameter or variable in my template like this:
"ports": [ 80, 81, 82, ...]
and that I could loop over this similar to this: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple.
Indeed you can! Copy does work with properties!
Create a parameter or variable like this (this example will use parameter array):
"lbRules": {
"type": "array",
"defaultValue": [
{
"name": "httpPort",
"frontendPort": "80",
"backendPort": "80",
"protocol": "tcp"
},
{
"name": "customAppPort",
"frontendPort": "8080",
"backendPort": "8888",
"protocol": "tcp"
},
{
"name": "httpsPort",
"frontendPort": "443",
"backendPort": "443",
"protocol": "tcp"
}
]
}
Use this parameter in the Loadbalancer resource using copy like this that will create that many probes and rules that you defined in your parameter array:
{
"apiVersion": "[variables('lbApiVersion')]",
"type": "Microsoft.Network/loadBalancers",
"name": "[parameters('myLoadBalancer')]",
"location": "[parameters('computeLocation')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/',concat(parameters('lbIPName'),'-','0'))]"
],
"properties": {
"frontendIPConfigurations": [
{
"name": "LoadBalancerIPConfig",
"properties": {
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',concat(parameters('lbIPName'),'-','0'))]"
}
}
}
],
"backendAddressPools": [
{
"name": "LoadBalancerBEAddressPool",
"properties": {}
}
],
"copy": [
{
"name": "probes",
"count": "[length(parameters('lbRules'))]",
"input": {
"name": "[concat(parameters('lbRules')[copyIndex('probes')].name,'Probe')]",
"properties": {
"intervalInSeconds": 5,
"numberOfProbes": 2,
"port": "[parameters('lbRules')[copyIndex('probes')].backendPort]",
"protocol": "[parameters('lbRules')[copyIndex('probes')].protocol]"
}
}
},
{
"name": "loadBalancingRules",
"count": "[length(parameters('lbRules'))]",
"input": {
"name": "[parameters('lbRules')[copyIndex('loadBalancingRules')].name]",
"properties": {
"frontendIPConfiguration": {
"id": "[concat(resourceId('Microsoft.Network/loadBalancers', parameters('myLoadBalancer')),'/frontendIPConfigurations/LoadBalancerIPConfig')]"
},
"frontendport": "[parameters('lbRules')[copyIndex('loadBalancingRules')].frontendport]",
"backendport": "[parameters('lbRules')[copyIndex('loadBalancingRules')].backendport]",
"enableFloatingIP": false,
"idleTimeoutInMinutes": "5",
"protocol": "[parameters('lbRules')[copyIndex('loadBalancingRules')].protocol]",
"backendAddressPool": {
"id": "[concat(resourceId('Microsoft.Network/loadBalancers', parameters('myLoadBalancer')),'/backendAddressPools/LoadBalancerBEAddressPool')]"
},
"probe": {
"id": "[concat(variables('lbID0'),'/probes/', parameters('lbRules')[copyIndex('loadBalancingRules')].name,'Probe')]"
}
}
}
}
],
"inboundNatPools": []
},
}
}
More info can be found here:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#property-iteration
https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/copy-properties
You can only apply the copy object to a top-level resource.
You cannot apply it to a property on a resource type, or to a child resource.
"resources": [
{
"type": "{provider-namespace-and-type}",
"name": "parentResource",
"copy": {
/* yes, copy can be applied here */
},
"properties": {
"exampleProperty": {
/* no, copy cannot be applied here */
}
},
"resources": [
{
"type": "{provider-type}",
"name": "childResource",
/* copy can be applied if resource is promoted to top level */
}
]
}
]
Source of Quotation: Deploy multiple instances of resources in Azure Resource Manager templates
You can loop over properties in ARM Template ONLY IF the copy object is applied to a top-level resource, which is in your case the "Microsoft.Network/loadBalancers", but that will also create multiple copy of the said resource.
If this is not what you want to achieve, I would recommend you to keep your existing way until ARM Template support copy object to property on a resource type in the future.
It is now possible to loop on properties or on child resources as stated in https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#property-iteration or in
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#create-multiple-instances-of-a-child-resource
You can a child resource extension (e.g. WebSite/Extension) as a top-level resource by following the format for the type:
{resource-provider-namespace}/{parent-resource-type}/{child-resource-type}.
For instance
Microsoft.Web/sites/siteextensions
You have also to reference the parent resource in the child resource by a concat. For instance:
"name": "[concat('mywebsite', '/', 'myextension', copyIndex())]"
What you want to achieve is possible with the take function.
You linked the proper documentation site yourself. Go to the link you posted and check out the section "Create multiple instances when copy won't work".
in your case this would look like this:
"variables": {
"probeArray": [
{
"name": "AppPortProbe1",
"properties": {
"protocol": "Tcp",
"port": 80,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
},
{
"name": "AppPortProbe2",
"properties": {
"protocol": "Tcp",
"port": 81,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
},
{
"name": "AppPortProbe3",
"properties": {
"protocol": "Tcp",
"port": 82,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
}
],
You then create an parameter specifying how many probes you want
"parameters": {
...
"numProbes": {
"type": "int",
"maxValue": 3,
"metadata": {
"description": "This parameter allows you to select the number of probes you want"
}
}
Finally you use take inside the resource:
"resources": [
...
{
"type": "Microsoft.Network/loadBalancers",
"properties": {
...
"probes": "[take(variables('probeArray'),parameters('numProbes'))]"
},
...
}
...
}
]
If you continue the through the documentation you will see that you can get even more crazy and combine copy and take with linked templates.

Azure - Specifying Load Balancer Rules in ARM Template

I am trying to modify an ARM template that I have which deploys some VMs and defines some autoscale rules (you can see the full template at https://gist.github.com/jinky32/d80e0ab2137236ff262484193f93c946, it is based on the template at https://github.com/gbowerman/azure-myriad/tree/master/vmss-ubuntu-scale).
I am trying to add in some load balancer rules so that traffic is spread across the new VMs as they are generated in reponse to the autoscale rules that are defined.
When I run this template via Azure CLI I get no errors in terminal but the deployment fails. Digging into the error events I see two:
statusCode:BadRequest serviceRequestId:ef42ec66-600e-4fb9-b4e2-dc2c06dda79c statusMessage:{"error":{"code":"InvalidRequestFormat","message":"Cannot parse the request.","details":[{"code":"InvalidJsonReferenceFormat","message":"Reference Id cc2bepool is not formatted correctly. The Id is expected to reference resources of type loadBalancers/backendAddressPools. Path properties.loadBalancingRules[0].properties.backendAddressPool."}]}} responseBody:{"error":{"code":"InvalidRequestFormat","message":"Cannot parse the request.","details":[{"code":"InvalidJsonReferenceFormat","message":"Reference Id cc2bepool is not formatted correctly. The Id is expected to reference resources of type loadBalancers/backendAddressPools. Path properties.loadBalancingRules[0].properties.backendAddressPool."}]}}
and
statusCode:BadRequest statusMessage:{"error":{"code":"InvalidRequestFormat","message":"Cannot parse the request.","details":[{"code":"InvalidJsonReferenceFormat","message":"Reference Id cc2bepool is not formatted correctly. The Id is expected to reference resources of type loadBalancers/backendAddressPools. Path properties.loadBalancingRules[0].properties.backendAddressPool."}]}}
I've put some of the relevant variables below and have also included my loadbalancer object but I believe that the issue is related to how I am referencing backendAddressPool:
"loadBalancingRules": [
{
"name": "LBRule",
"properties": {
"frontendIPConfiguration": {
"id": "[variables('frontEndIPConfigID')]"
},
"backendAddressPool": {
"id": "[variables('bePoolName')]"
},
but I'm confused because I refer to it the same way elsewhere. Any advice on how to do this correctly much appreciated.
"variables": {
....
"loadBalancerName": "[concat(parameters('vmssName'), 'lb')]",
"lbProbeID": "[concat(variables('lbID'),'/probes/tcpProbe')]",
"publicIPAddressID": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
"lbID": "[resourceId('Microsoft.Network/loadBalancers',variables('loadBalancerName'))]",
"natPoolName": "[concat(parameters('vmssName'), 'natpool')]",
"bePoolName": "[concat(parameters('vmssName'), 'bepool')]",
....
....
}
.....
.....
{
"type": "Microsoft.Network/loadBalancers",
"name": "[variables('loadBalancerName')]",
"location": "[variables('location')]",
"apiVersion": "[variables('networkApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
],
"properties": {
"frontendIPConfigurations": [
....
],
"backendAddressPools": [
{
"name": "[variables('bePoolName')]"
}
],
"inboundNatPools": [
{
"name": "[variables('natPoolName')]",
...
},
{
"name": "natpooltileserver",
....
},
{
"name": "natpool2",
....
],
"loadBalancingRules": [
{
"name": "LBRule",
"properties": {
"frontendIPConfiguration": {
"id": "[variables('frontEndIPConfigID')]"
},
"backendAddressPool": {
"id": "[variables('bePoolName')]"
},
"protocol": "tcp",
"frontendPort": 80,
"backendPort": 80,
"enableFloatingIP": false,
"idleTimeoutInMinutes": 5,
"probe": {
"id": "[variables('lbProbeID')]"
}
}
}
],
"probes": [
{
"name": "tcpProbe",
"properties": {
"protocol": "tcp",
"port": 80,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
}
]
}
},
please go to the portal and open a support request to see what's wrong with your template edits.

Resources