2

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.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
roy
  • 6,344
  • 24
  • 92
  • 174
  • You're syntax is good - I think you're running into a problem using listkeys() within the property copy, not sure why - lemme dig a bit and see if I can find out... – bmoore-msft Jun 15 '17 at 17:10

3 Answers3

0

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" }
  ]
},
4c74356b41
  • 69,186
  • 6
  • 100
  • 141
  • No, I want to deploy resource just once, but want to attached array of storage account to the cluster in `storageProfile` block. – roy Jun 14 '17 at 18:12
  • check updated answer, also, i didn't validate your properties loop, so that might be wrong (i copy\pasted your existing properties). also notice the `copyIndex()` is different from your original @roy – 4c74356b41 Jun 14 '17 at 18:17
  • I tried this template https://gist.github.com/anonymous/5a3968223af0bb2daf726cbb591cd5ff which gave me invalid tamplate for `properties` block. – roy Jun 14 '17 at 18:53
  • update the question with the template you have and the exact error. i cant troubleshoot this as i cant deploy this, unless you give me parameters file. but like i said, i cannot verify the validity of your storageaccount array and i cannot deploy this to verify so @roy – 4c74356b41 Jun 14 '17 at 19:13
  • Template : https://gist.github.com/anonymous/5a3968223af0bb2daf726cbb591cd5ff Parameters : https://gist.github.com/anonymous/fa27714ea74bbcecafbbfa1380b2308a – roy Jun 14 '17 at 19:32
  • I am getting `Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template resource 'dlid01spk21' at line '184' 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.` – roy Jun 15 '17 at 16:06
  • 1
    try re-reading, you cant use copy properties with runtime functions @roy – 4c74356b41 Jun 15 '17 at 18:30
0

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).

bmoore-msft
  • 8,376
  • 20
  • 22
0

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

Stas S
  • 101
  • 1
  • 3