Can I have an ARM template resource with a COPY ar

2020-03-01 07:24发布

I'm deploying an ARM template that uses a copy resource block to deploy 1 or more data disks to a VM. What I'd like to do is change this to 0 or more.

The parameter I use is

    "VirtualMachineDiskSizeArray": {
        "type": "array",
        "defaultValue": [ "100" ]
    },

Which is then called in a resource:

   "resources": [
    {
        "name": "[parameters('virtualMachineName')]",
        "type": "Microsoft.Compute/virtualMachines",
        "apiVersion": "2016-04-30-preview",
        "location": "[parameters('rgLocation')]",
        "dependsOn": [
            "[concat('Microsoft.Storage/storageAccounts/', parameters('rgStorageAccountName'))]"
        ],
        "properties": {
            "osProfile": { ... },
            "hardwareProfile": { ... },
            "storageProfile": {
                "imageReference": { ... },
                "osDisk": { ... },
                "copy": [
                    {
                        "name": "dataDisks",
                        "count": "[length(parameters('VirtualMachineDiskSizeArray'))]",
                        "input": {
                            "lun": "[copyIndex('dataDisks')]",
                            "name": "[concat(parameters('vmDataDiskNameStub'), add(copyIndex('dataDisks'),1), '.vhd')]",
                            "diskSizeGB": "[parameters('VirtualMachineDiskSizeArray')[copyIndex('dataDisks')]]",
                            "createOption": "Empty",
                            "vhd": {
                                "uri": "[concat(concat(reference(resourceId(parameters('rgName'), 'Microsoft.Storage/storageAccounts', parameters('rgStorageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), concat(parameters('vmDataDiskNameStub'),  add(copyIndex('dataDisks'),1), '.vhd') )]"
                            }
                        }
                    }
                ]
            }
        }
    },

However, when I pass in an array of data disks with 0 elements, I get this error, as expected:

Validation returned the following errors:
: Deployment template validation failed: 'The template 'copy' definition at line '0' and column '0' has an invalid copy count. The co
py count must be a postive integer value and cannot exceed '800'. Please see https://aka.ms/arm-copy for usage details.'.

Template is invalid.

I would like to try to work around this somehow - I tried adding a condition on the copy:

"condition": "[  greater(length(parameters('VirtualMachineDiskSizeArray')), 0)]",

But that returned the same error.

I'm researching nested templates, but that doesn't look good for a section of a resource.

7条回答
放我归山
2楼-- · 2020-03-01 07:56

Regarding the Disksize I can help you out. What I have actually done is reference to a JSON template file where the size, name, and caching is defined for disk creation.

These can easily be parsed to the deployment

"storageProfile": {
      "copy": [{
        "name": "dataDisks",
        "count": "[length(parameters('dataDiskArray'))]",
        "input": {
          "name": "[concat(parameters('vmName'), if(less(copyindex(1), 10), concat('0', copyindex(1)), concat(copyindex(1))), '-datadisk-', parameters('dataDiskArray')[copyIndex('dataDisks')].name)]",
          "diskSizeGB": "[parameters('dataDiskArray')[copyIndex('dataDisks')].size]",
          "caching": "[parameters('dataDiskArray')[copyIndex('dataDisks')].cache]",
          "lun": "[copyIndex('dataDisks')]",
          "createOption": "Empty"
        }
      }],

The issue remains that is part of the VM deployment, so there is no option to select a Null value if you want to deploy a VM without extra disks.

I have tried to move the disk creation to the variables section of the template, but than it is not possible to use the VMName as part of the name of the disks you want to create. This is because the copy loop for the VM is not yet initialized, so It will give the wrong name.

查看更多
干净又极端
3楼-- · 2020-03-01 08:03

I would like to share our solution based many of the answers already here. This is a simple example to have an input array of disks - values being their sizes - and create with those with your vm. works from 0-n. n needs to be smaller than supported disks of your VM size and other azure limits :) This should also help with this comment

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        [...]
        "dataDiskArray": {
            "type": "array",
            "defaultValue": [
                "100",
                "50"
            ]
        }
    },
    "variables": {
        "copy": [
            {
                "name": "dataDisks",
                "count": "[if(equals(length(parameters('dataDiskArray')),0),1, length(parameters('dataDiskArray')))]",
                "input": {
                    "lun": "[if(equals(length(parameters('dataDiskArray')),0),0, copyIndex('dataDisks'))]",
                    "createOption": "Empty",
                    "diskSizeGB": "[if(equals(length(parameters('dataDiskArray')),0),10,parameters('dataDiskArray')[copyIndex('dataDisks')])]"
                }
            }
        ]
    },
    "resources": [
        {
            "name": "[parameters('virtualMachineName')]",
            "type": "Microsoft.Compute/virtualMachines",
            [...]
            "properties": {
                [...]
                "storageProfile": {
                    [...]
                    "dataDisks": "[if(equals(length(parameters('dataDiskArray')),0),json('null'),variables('dataDisks'))]"
                },
                [...]
                }
            }
    ],
    "outputs": {
    }
}
查看更多
甜甜的少女心
4楼-- · 2020-03-01 08:03

Working sample https://github.com/mariuszdotnet/azure-resource-manager-tutorial/blob/master/azuredeploy.json

Here is the summary:

  • Move the Disks into a variable and use variable iteration.
  • If numDisks = 0 then diskLoop = 1 (this prevents an error in variable copy)
  • Create variable copy object with count = diskLoop
"copy": [
  {
      "name": "dataDisks",
      "count": "[if(equals(parameters('numberOfDataDisks'),0),1, parameters('numberOfDataDisks'))]",
      "input": {
        "lun": "[copyIndex('dataDisks')]",
        "createOption": "Empty",
        "diskSizeGB": "1023"
      }
  }   ]
  • On the VM Resource for data disks.
  • On the VM resource, if numDisk=0 then datadisks = json(‘null’) else datadisks = variable
"dataDisks": 
                "[if(equals(parameters('numberOfDataDisks'),0),json('null'),variables('dataDisks'))]"

            },
查看更多
孤傲高冷的网名
5楼-- · 2020-03-01 08:05

So in the Interest of Time, I have changed my approach to this, but don't really like it...

I now have two deploy json files, VMDeploy.json and VMDeploy-NoDataDisks.json.

They are identical other than the storageProfile section of the VM resource:

VMDeploy.json:

"storageProfile": {
  "imageReference": { ... },
  "osDisk": { ... },
  "copy": [
    {
    "name": "dataDisks",
    "count": "[length(parameters('VirtualMachineDiskSizeArray'))]",
    "input": {
      "lun": "[copyIndex('dataDisks')]",
      "name": "[concat(parameters('vmDataDiskNameStub'), add(copyIndex('dataDisks'),1), '.vhd')]",
      "diskSizeGB": "[parameters('VirtualMachineDiskSizeArray')[copyIndex('dataDisks')]]",
      "createOption": "Empty",
      "vhd": {
        "uri": "[concat(concat(reference(resourceId(parameters('rgName'), 'Microsoft.Storage/storageAccounts', parameters('rgStorageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), concat(parameters('vmDataDiskNameStub'),  add(copyIndex('dataDisks'),1), '.vhd') )]"
        }
      }
    }
  ]
}

VMDeploy-NoDataDisks.json:

"storageProfile": {
  "imageReference": { ... },
  "osDisk": { ... },
  "dataDisks": []
}

And I have a Powershell block switches between the two json files:

if ($DriveArray.Count -eq 0) {
    $TemplateFile = $TemplateFile.Replace('.json','-NoDataDisks.json')
}
查看更多
倾城 Initia
6楼-- · 2020-03-01 08:07

I'm going to use this answer to use a reference on my research into nested templates.

Looking at here I can see an approach that would have two nested templates, one like this:

"resources": [
{
    "name": "MultiDataDisk",
    "type": "Microsoft.Compute/virtualMachines",
    "apiVersion": "2016-04-30-preview",
    "location": "[parameters('rgLocation')]",
    "dependsOn": [
        "[concat('Microsoft.Storage/storageAccounts/', parameters('rgStorageAccountName'))]"
    ],
    "properties": {
        "osProfile": { ... },
        "hardwareProfile": { ... },
        "storageProfile": {
            "imageReference": { ... },
            "osDisk": { ... },
            "copy": [
                {
                    "name": "dataDisks",
                    "count": "[length(parameters('VirtualMachineDiskSizeArray'))]",
                    "input": {
                        "lun": "[copyIndex('dataDisks')]",
                        "name": "[concat(parameters('vmDataDiskNameStub'), add(copyIndex('dataDisks'),1), '.vhd')]",
                        "diskSizeGB": "[parameters('VirtualMachineDiskSizeArray')[copyIndex('dataDisks')]]",
                        "createOption": "Empty",
                        "vhd": {
                            "uri": "[concat(concat(reference(resourceId(parameters('rgName'), 'Microsoft.Storage/storageAccounts', parameters('rgStorageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), concat(parameters('vmDataDiskNameStub'),  add(copyIndex('dataDisks'),1), '.vhd') )]"
                        }
                    }
                }
            ]
        }
    }
}
]

and one like this:

"resources": [
{
    "name": "ZeroDataDisk",
    "type": "Microsoft.Compute/virtualMachines",
    "apiVersion": "2016-04-30-preview",
    "location": "[parameters('rgLocation')]",
    "dependsOn": [
        "[concat('Microsoft.Storage/storageAccounts/', parameters('rgStorageAccountName'))]"
    ],
    "properties": {
        "osProfile": { ... },
        "hardwareProfile": { ... },
        "storageProfile": {
            "imageReference": { ... },
            "osDisk": { ... },
             "dataDisks": []
        }
    }
}
] 

And reference them from a parent template:

"parameters": {
 "nestedType": {
  "type": "string",
  "defaultValue": "ZeroDataDisk",
  "allowedValues": [
   "ZeroDataDisk",
   "MultiDataDisk"
  ],
 }
},
"resources": [
 {
 "name": "[concat("nested-",parameters('virtualMachineName')]",
 "type": "Microsoft.Resources/deployments",
 "apiVersion": "2015-01-01",
 "properties": {
  "mode": "Incremental",
  "templateLink": {
   "uri": "[concat('https://someplace.on.the.internet/nested/',parameter('nestedType'),".json")],
   "contentVersion": "1.0.0.0"
   },
   "parameters": {
    "rgStorageAccountName": {
     "value": "[parameters(rgStorageAccountName)]"
    },
    "OtherParms": {
     "value": "[parameters('otherParms')]"
    }
     . . .
  }
 }
]
}

However, I don't believe this is better/easier than what I did in my "Interest of Time" answer because

  1. The section that I'm interested in (dataDisks) is surrounded by a bunch of other json that doesn't change leaving me to the same crappy issue of having to manually sync code between two files.
  2. I now have to not only maintain two json files for the nested resources, but I have to publish them via a publicly accessible URL.

So basically, unless I can have a copy of 0-N of a section (rather than 1-N) or nest just a section, my two file hack with the powershell switch seems to be the least overhead.

查看更多
神经病院院长
7楼-- · 2020-03-01 08:07

The workaround I've used for myself doesn't need linked templates or complicated logic; it's based on this:

If the array is empty, create another array with only one item in it with the value of "EMPTY_ARRAY_INDICATOR".

Also, in my copy loop, I have added this condition: "condition": "[not(equals(variables('input')[0]), "EMPTY_ARRAY_INDICATOR")]" so that it doesn't deploy when the first element of the array is "EMPTY_ARRAY_INDICATOR".

Having a look at your template, I guess I can rewrite it this way:

   "parameters": [
      "VirtualMachineDiskSizeArray": {
        "type": "array",
        "defaultValue": []
      }
   ],
   "variables": [
      "VirtualMachineDiskSizeArray": "[if(empty(parameters('VirtualMachineDiskSizeArray')), array('EMPTY_ARRAY_INDICATOR'), parameters('VirtualMachineDiskSizeArray'))]"
   ],
   "resources": [
    {
        "name": "[parameters('virtualMachineName')]",
        "type": "Microsoft.Compute/virtualMachines",
        "apiVersion": "2016-04-30-preview",
        "location": "[parameters('rgLocation')]",
        "dependsOn": [
            "[concat('Microsoft.Storage/storageAccounts/', parameters('rgStorageAccountName'))]"
        ],
        "properties": {
            "osProfile": { ... },
            "hardwareProfile": { ... },
            "storageProfile": {
                "imageReference": { ... },
                "osDisk": { ... },
                "copy": [
                    {
                        "name": "dataDisks",
                        "condition": "[not(equals(variables('VirtualMachineDiskSizeArray')[0], 'EMPTY_ARRAY_INDICATOR'))]"
                        "count": "[length(variables('VirtualMachineDiskSizeArray'))]",
                        "input": {
                            "lun": "[copyIndex('dataDisks')]",
                            "name": "[concat(parameters('vmDataDiskNameStub'), add(copyIndex('dataDisks'),1), '.vhd')]",
                            "diskSizeGB": "[variables('VirtualMachineDiskSizeArray')[copyIndex('dataDisks')]]",
                            "createOption": "Empty",
                            "vhd": {
                                "uri": "[concat(concat(reference(resourceId(parameters('rgName'), 'Microsoft.Storage/storageAccounts', parameters('rgStorageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), concat(parameters('vmDataDiskNameStub'),  add(copyIndex('dataDisks'),1), '.vhd') )]"
                            }
                        }
                    }
                ]
            }
        }
    },

Please note the condition in the copy loop, and the usage of variables('VirtualMachineDiskSizeArray') instead of parameters(VirtualMachineDiskSizeArray).

I have tested it for my own template, but haven't run your template. Please let me know if it works or not.

查看更多
登录 后发表回答