'Azure ARM template with array as parameter

I am trying to user copy loop function in Azure ARM template following is the resource block I have

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupsName": {
  "type": "string"
},
"clusterName": {
  "type": "string"
},
"clusterLoginUserName": {
  "type": "string"
},
"clusterLoginPassword": {
  "type": "securestring"
},
"sshUserName": {
  "type": "string"
},
"sshPassword": {
  "type": "securestring"
},
"location": {
  "type": "string"
},
"clusterType": {
  "type": "string",
  "defaultValue": "spark"
},
"clusterVersion": {
  "type": "string"
},
"sparkVersion": {
  "type": "string"
},
"clusterWorkerNodeCount": {
  "type": "int",
  "defaultValue": 2
},
"virtualNetworkName": {
  "type": "string"
},
"subnetName": {
  "type": "string"
},
"vnetResourceGroupName": {
  "type": "string"
},
"clusterStorageAccountName": {
  "type": "string"
},
"dataStorageAccountName": {
  "type": "string"
},
"clusterStorageContainerName": {
  "type": "string"
},
"externalStorageAccounts": {
  "type": "array"
},
"storageAccountResourceGroupName": {
  "type": "string"
},
"headNodeSize": {
  "type": "string"
},
"workerNodeSize": {
  "type": "string"
},
"edgeNodeSize": {
  "type": "string"
},
"dbServerName": {
  "type": "string"
},
"hivedbName": {
  "type": "string",
  "metadata": { "description": "Name of the database where metadata will be stored" }
},
"ooziedbName": {
  "type": "string",
  "metadata": { "description": "Name of the database where metadata will be stored" }
},
"dbuser": {
  "type": "string",
  "metadata": { "description": "User Name of the database server where metadata will be stored" }
},
"dbpassword": {
  "type": "securestring",
  "metadata": { "description": "Password of the database where metadata will be stored" }
},
"collation": {
  "type": "string",
  "defaultValue": "SQL_Latin1_General_CP1_CI_AS",
  "metadata": {
    "description": "The database collation for governing the proper use of characters."
  }
},
"edition": {
  "type": "string",
  "defaultValue": "Standard",
  "allowedValues": [
    "Basic",
    "Standard",
    "Premium"
  ],
  "metadata": {
    "description": "The type of database to create."
  }
},
"maxSizeBytes": {
  "type": "string",
  "defaultValue": "1073741824",
  "metadata": {
    "description": "The maximum size, in bytes, for the database"
  }
},
"requestedServiceObjectiveName": {
  "type": "string",
  "defaultValue": "S1",
  "allowedValues": [
    "Basic",
    "S0",
    "S1",
    "S2",
    "P1",
    "P2",
    "P3"
  ],
  "metadata": {
    "description": "Describes the performance level for Edition"
  }
},
"omsWorkspace": {
  "type": "string",
  "metadata": {
    "description": "OMS Workspace ID"
  }
},
"omsResourceGroup": {
  "type": "string",
  "metadata": {
    "description": "OMS Workspace Key"
  }
},
"Environment": {
  "type": "string",
  "allowedValues": [
    "dev",
    "qa",
    "stage",
    "prod"
  ],
  "metadata": {
    "description": "The environment that the resources will be tagged with (dev, test, stage, prod)."
  }
},
"ProjectName": {
  "type": "string",
  "metadata": {
    "description": "A name for the project or company that this template is being provisioned for (used for tagging)."
  }
}
},
"variables": {
"dbServerName": "[concat(parameters('dbServerName'),'.database.windows.net')]",
"defaultApiVersion": "2015-05-01-preview",
"clusterApiVersion": "2015-03-01-preview",
"vnetID": "[concat(resourceId(parameters('vnetResourceGroupName'),'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName')))]",
"subnet1Ref": "[concat(variables('vnetID'),'/subnets/', parameters('subnetName'))]",
"applicationName": "[concat('edgenode')]"
},
"resources": [
  {
    "name": "[parameters('clusterName')]",
    "type": "Microsoft.HDInsight/clusters",
    "location": "[resourceGroup().location]",
    "apiVersion": "[variables('clusterApiVersion')]",
    "dependsOn": [],
    "tags": {
      "Environment": "[parameters('Environment')]",
      "Project": "[parameters('ProjectName')]"
    },
    "properties": {
      "clusterVersion": "[parameters('clusterVersion')]",
      "osType": "Linux",
      "tier": "standard",
      "clusterDefinition": {
        "kind": "[parameters('clusterType')]",
        "componentVersion": {
                "Spark": "[parameters('sparkVersion')]"
        },
        "configurations": {
          "gateway": {
            "restAuthCredential.isEnabled": true,
            "restAuthCredential.username": "[parameters('clusterLoginUserName')]",
            "restAuthCredential.password": "[parameters('clusterLoginPassword')]"
          },
          "core-site": {
          },
          "hive-site": {
            "javax.jdo.option.ConnectionDriverName": "com.microsoft.sqlserver.jdbc.SQLServerDriver",
            "javax.jdo.option.ConnectionURL": "[concat('jdbc:sqlserver://', variables('dbServerName'),';database=', parameters('hivedbName'),';encrypt=true;trustServerCertificate=true;create=false;loginTimeout=300')]",
            "javax.jdo.option.ConnectionUserName": "[parameters('dbuser')]",
            "javax.jdo.option.ConnectionPassword": "[parameters('dbpassword')]"
          },
          "hive-env": {
            "hive_database": "Existing MSSQL Server database with SQL authentication",
            "hive_database_name": "[parameters('hivedbName')]",
            "hive_database_type": "mssql",
            "hive_existing_mssql_server_database": "[parameters('hivedbName')]",
            "hive_existing_mssql_server_host": "[variables('dbServerName')]",
            "hive_hostname": "[variables('dbServerName')]"
          },
          "oozie-site": {
            "oozie.service.JPAService.jdbc.driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver",
            "oozie.service.JPAService.jdbc.url": "[concat('jdbc:sqlserver://', variables('dbServerName'),';database=', parameters('ooziedbName'),';encrypt=true;trustServerCertificate=true;create=false;loginTimeout=300')]",
            "oozie.service.JPAService.jdbc.username": "[parameters('dbuser')]",
            "oozie.service.JPAService.jdbc.password": "[parameters('dbpassword')]",
            "oozie.db.schema.name": "oozie"
          },
          "oozie-env": {
            "oozie_database": "Existing MSSQL Server database with SQL authentication",
            "oozie_database_name": "[parameters('ooziedbName')]",
            "oozie_database_type": "mssql",
            "oozie_existing_mssql_server_database": "[parameters('ooziedbName')]",
            "oozie_existing_mssql_server_host": "[variables('dbServerName')]",
            "oozie_hostname": "[variables('dbServerName')]"
          }
        }
      },
      "storageProfile": {
        "copy": [
            {
                "name": "storageaccounts",
                "count": "[length(parameters('externalStorageAccounts'))]",
                "input": {
                    "name": "[concat(parameters('externalStorageAccounts')[copyIndex('storageaccounts')].name,'.blob.core.windows.net')]",
                    "isDefault": "[parameters('externalStorageAccounts')[copyIndex('storageaccounts')].isDefault]",
                    "container": "[parameters('externalStorageAccounts')[copyIndex('storageaccounts')].container]",
                    "key": "[listKeys(resourceId(parameters('externalStorageAccounts')[copyIndex('storageaccounts')].resourceGroupsName,'Microsoft.Storage/storageAccounts', parameters('externalStorageAccounts')[copyIndex('storageaccounts')].name), variables('defaultApiVersion')).key1]",
                }
            }
        ]
      },
    "computeProfile": {
        "roles": [
          {
            "name": "headnode",
            "targetInstanceCount": "2",
            "hardwareProfile": {
              "vmSize": "[parameters('headNodeSize')]"
            },
            "osProfile": {
              "linuxOperatingSystemProfile": {
                "username": "[parameters('sshUserName')]",
                "password": "[parameters('sshPassword')]"
              }
            },
            "virtualNetworkProfile": {
              "id": "[variables('vnetID')]",
              "subnet": "[variables('subnet1Ref')]"
            },
            "scriptActions": []
          },
          {
            "name": "workernode",
            "targetInstanceCount": "[parameters('clusterWorkerNodeCount')]",
            "hardwareProfile": {
              "vmSize": "[parameters('workerNodeSize')]"
            },
            "osProfile": {
              "linuxOperatingSystemProfile": {
                "username": "[parameters('sshUserName')]",
                "password": "[parameters('sshPassword')]"
              }
            },
            "virtualNetworkProfile": {
              "id": "[variables('vnetID')]",
              "subnet": "[variables('subnet1Ref')]"
            },
            "scriptActions": []
          }
        ]
      }
    }
  }
],
"outputs": {
"cluster": {
  "type": "object",
  "value": "[reference(resourceId('Microsoft.HDInsight/clusters',parameters('clusterName')))]"
 }
}
}

With parameter file : https://gist.github.com/anonymous/fa27714ea74bbcecafbbfa1380b2308a

I am passing parameter externalStorageAccounts as array with following details

"externalStorageAccounts": {
  "value": [
    { "name": "sparkstg", "container": "sparkdata", "isDefault": "true","resourceGroupsName": "Spark" },
    { "name": "s1rg", "container": "blank", "isDefault": "false","resourceGroupsName": "s1rg" },
    { "name": "s2rg", "container": "blank", "isDefault": "false","resourceGroupsName": "s2rg" },
    { "name": "s3rg", "container": "blank", "isDefault": "false","resourceGroupsName": "s3rg" },
    { "name": "s4rg", "container": "blank", "isDefault": "false","resourceGroupsName": "s3rg" }
  ]
}

But getting invalid template error

Trying to understand what wrong I am doing here.



Solution 1:[1]

You need to remove the copy from the resource definition and create the following json:

"storageProfile": {
    "copy": [
        {
            "name": "storageaccounts",
            "count": "[length(parameters('externalStorageAccounts'))]",
            "input": {
                "name": "[concat(parameters('externalStorageAccounts')[copyIndex('storageaccounts')].name,'.blob.core.windows.net')]",
                "isDefault": "[parameters('externalStorageAccounts')[copyIndex('storageaccounts')].isDefault]",
                "container": "[parameters('externalStorageAccounts')[copyIndex('storageaccounts')].container]",
                "key": "[listKeys(resourceId(parameters('externalStorageAccounts')[copyIndex('storageaccounts')].resourceGroupsName,'Microsoft.Storage/storageAccounts', parameters('externalStorageAccounts')[copyIndex('storageaccounts')].name), variables('defaultApiVersion')).key1]"
            }
        }
    ]
}

instead of you existing storageProfile

basically what you were doing - trying to create same resource X times and what you need to do is copy single property X times

Okay, after tinkering with this a bit, I can assure you this is not possible as listkeys is a runtime function and property copy is a compilation time function. so this cannot possibly work (this didn't cross my mind at first somehow).

Your workaround could be pulling keys beforehand and adding them directly to the array, so your array would look like this:

"externalStorageAccounts": {
  "value": [
    { "name": "salsbx01sparkstg", "container": "dlid01spk21", "isDefault": "true","key": "xxx" },
    ...
    { "name": "s4rg", "container": "blank", "isDefault": "false","key": "xxx" }
  ]
},

Solution 2:[2]

I think this may be related to a fix we're rolling out across the data centers - can you try deploying into westus (if possible, I know you have some prereqs) and see if that works? I'm able to validate the template there (and couldn't last week).

Solution 3:[3]

This solution works well for me

{
  "type": "Microsoft.Compute/disks",
  "name": "[concat(parameters('VmName'), copyIndex(), '-dataDisk')]",
  "apiVersion": "2017-03-30",
  "location": "[resourceGroup().location]",
  "sku": {
    "name": "Standard_LRS"
  },
  "copy": {
    "name": "VMDataDisksLoop",
    "count": "[parameters('numberOfNodes')]"
  },
  "properties": {
    "creationData": {
      "createOption": "Empty"
    },
    "diskSizeGB": 128
  }
}

it creates specified number of disks, one for each node

Solution 4:[4]

Try using:

npm install --save @ramonak/react-excel --legacy-peer-deps

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 bmoore-msft
Solution 3 Stas S
Solution 4 Dada