JsonADDomainExtension 和复制循环出错

Error with JsonADDomainExtension and copy loop

我开发了 ARM 模板,可以并行构建多个 VM。每台机器都有一个 OS 磁盘和一个 vNIC。我可以使用 PowerShell 部署 ARM 模板,也可以上传 ARM 模板并使用 Azure 门户的 "Deploy Custom Template" 功能进行部署。

但是,我似乎无法通过复制循环将正确的语法添加到 JsonADDomainExtension 中,以便所有机器都添加到同一模板中的我的域中。

目前我所有的资源都在一个资源组中。我已经预先部署了一个带有子网和存储帐户的 vNET。

下面是可以同时部署多台机器的 ARM 模板。

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters":
    {
    "adminUsername":
        {
        "type": "string",
        "metadata": {"description": "Administrator username for the Virtual Machine."}
        },
    "adminPassword":
         {
        "type": "securestring",
        "metadata": {"description": "Password for the Virtual Machine.Should contain any 3 of: 1 Lower Case, 1 Upper Case, 1 Number and 1 Special character."}
        },
    "numberOfInstances":
        {
        "type": "int",
        "defaultValue": 3,
        "minValue": 2,
        "maxValue": 5,
        "metadata": {"description": "Number of VMs to deploy, limit 5 since this sample is using a single storage account."}
        },
    "StartOfCount":
        {
        "type": "int",
        "defaultValue": 1,
        "metadata": {"description": "Number that you want to start the machine name numbering from, EG to create 5 VMs with the name starting at 045 (W2016SRV-045-vm) you would enter 45 here, or to start at 1 just enter 1 here, this wil give you a machine name like 'W2016SRV-001-vm'."}
        },
    "vmNamePrefix": 
        {
            "type": "string",
            "defaultValue": "W2016SRV-",
            "maxLength": 9,
            "metadata": {"description": "The VM name prefix maximum 9 characters. This allows for three digits in the name and trailing '-vm'. EG: 'W2016SRV-045-vm'."}
            },
    "vmSize": 
        {
        "type": "string",
        "defaultValue": "Standard_B2ms",
        "metadata": 
            {
            "description": "The size(T-shirt) for the VM. (Standard_D8s_v3)",
            "SNC::Parameter::Metadata": {"referenceType": "Microsoft.Compute/virtualMachines/vmSize"}
            }
        },
    "vmLocation": 
        {
        "type": "string",
        "defaultValue": "AustraliaSoutheast",
        "metadata": 
            {
            "description": "Location or datacenter where the VM will be placed.",
            "SNC::Parameter::Metadata": {"referenceType": "Microsoft.Azure/region"}
            }
        },
    "domainToJoin": 
        {
        "type": "string",
        "metadata": {"description": "The FQDN of the AD domain"}
        },
    "domainUsername": 
        {
        "type": "string",
        "metadata": {"description": "Username of the account on the domain"}
        },
    "domainPassword": 
        {
        "type": "securestring",
        "metadata": {"description": "Password of the account on the domain"}
        },
    "ouPath": 
        {
        "type": "string",
        "metadata": {"description": "Specifies an organizational unit (OU) for the domain account. Enter the full distinguished name of the OU in quotation marks. Example: 'OU=testOU; DC=domain; DC=Domain; DC=com"}
        },
    "sizeOfDiskInGB": 
        {
        "type": "int",
        "defaultValue": 200,
        "metadata": {"description": "The disk size for the OS drive in the VM, in GBs. Default Value is 500"}
        },
    "existingBootDiagStorageResourceGroup": 
        {
        "type": "string",
        "metadata": {"description": "Storage account resource group name where boot diagnistics will be stored"}
        },
    "existingBootDiagStorageName": 
        {
        "type": "string",
        "metadata": 
            {
            "description": "Storage account name where boot diagnistics will be stored. It should be at the same location as the VM.",
            "SNC::Parameter::Metadata": {"referenceType": "Microsoft.Storage/storageAccounts"}
            }
        },
    "existingvNetResourceGroup": 
        {
        "type": "string",
        "metadata": 
            {
            "description": "Resource Group of the Existing Virtual Network.",
            "SNC::Parameter::Metadata": {"referenceType": "Microsoft.Resources/resourceGroups"}
            }
        },
    "existingvNetName": 
        {
        "type": "string",
        "metadata": 
            {
            "description": "Existing Virtual Network to connect to Network Interface to.It should be at the same location as the VM.",
            "SNC::Parameter::Metadata": {"referenceType": "Microsoft.Network/virtualNetworks"}
            }
        },
    "subnetName": 
        {
        "type": "string",
        "metadata": 
            {
            "description": "The Subnet for the VM.",
            "SNC::Parameter::Metadata": {"referenceType": "Microsoft.Network/subNets"}
            }
        }
    },
"variables": 
    {
    "storageAccountType": "Standard_LRS",
    "vnetID": "[resourceId(parameters('existingvNetResourceGroup'), 'Microsoft.Network/virtualNetworks', parameters('existingvNetName'))]",
    "subnetRef": "[concat(variables('vnetID'),'/subnets/', parameters('subnetName'))]",
    "StartOfCountString": "[String(Parameters('StartOfCount'))]"
    },
"resources": 
    [
        {
        "apiVersion": "[providers('Microsoft.Network','networkInterfaces').apiVersions[0]]",
        "type": "Microsoft.Network/networkInterfaces",
        "name": "[concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm-vNic')]",
        "location": "[parameters('vmLocation')]",
        "copy": {
            "name": "nicLoop",
            "count": "[parameters('numberOfInstances')]"
            },
        "properties": 
            {
            "ipConfigurations": 
                [
                    {
                    "name": "Prod",
                    "properties": 
                        {
                        "privateIPAllocationMethod": "Dynamic",
                        "subnet": {"id": "[variables('subnetRef')]"}
                        }
                    }
                ]
            }
        },
        {
        "apiVersion": "[providers('Microsoft.Compute','virtualMachines').apiVersions[0]]",
        "type": "Microsoft.Compute/virtualMachines",
        "name": "[concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm')]",
        "location": "[parameters('vmLocation')]",
        "copy": {
            "name": "vmLoop",
            "count": "[parameters('numberOfInstances')]"
            },
        "dependsOn": ["nicLoop"],
        "tags": 
            {
            "Project": "***"
            },
        "properties": 
            {
            "hardwareProfile": {"vmSize": "[parameters('vmSize')]"},
            "osProfile": 
                {
                "computerName": "[concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm')]",
                "adminUsername": "[parameters('adminUsername')]",
                "adminPassword": "[parameters('adminPassword')]",
                "windowsConfiguration": {"enableAutomaticUpdates": false}
                },
            "storageProfile": 
                {
                "imageReference": {
                    "id": "/subscriptions/***/resourceGroups/***/providers/Microsoft.Compute/images/***"
                },
                "osDisk": 
                    {
                    "name": "[concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm-osDisk')]",
                    "createOption": "FromImage",
                    "diskSizeGB": "[parameters('sizeOfDiskInGB')]",
                    "caching": "ReadWrite",
                    "managedDisk": {"storageAccountType": "[variables('storageAccountType')]"}
                    }
                },
            "networkProfile": {"networkInterfaces": [{"id": "[resourceId('Microsoft.Network/networkInterfaces',concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm-vNic'))]"}]},
            "diagnosticsProfile": 
                {
                "bootDiagnostics": 
                    {
                    "enabled": true,
                    "storageUri": "[reference(resourceId(parameters('existingBootDiagStorageResourceGroup'), 'Microsoft.Storage/storageAccounts', parameters('existingBootDiagStorageName')), '2015-06-15').primaryEndpoints['blob']]"
                    }
                }
            }
        }
    ]
}

但是,如果我将其添加到底部的资源位中,则它不起作用。

{
    "apiVersion": "2016-03-30",
    "type": "Microsoft.Compute/virtualMachines/extensions",
    "name": "[concat(parameters('vmNamePrefix'), padLeft(copyIndex(variables('StartOfCountString')),3, '0'), '/JoinDomain')]",
    "location": "[resourceGroup().location]",
    "copy": {
        "name": "DomainJoinLoop",
        "count": "[parameters('numberOfInstances')]"
    },
    "dependsOn": "[concat('Microsoft.Compute/virtualMachines/', concat(parameters('vmNamePrefix'), padLeft(copyIndex(variables('StartOfCountString')),3, '0')))]",
    "properties": {
        "publisher": "Microsoft.Compute",
        "type": "JsonADDomainExtension",
        "typeHandlerVersion": "1.3",
        "autoUpgradeMinorVersion": true,
        "settings": {
            "Name": "[parameters('domainToJoin')]",
            "User": "[concat(parameters('domainToJoin'), '\', parameters('domainUsername'))]",
            "OUPath": "[parameters('ouPath')]",
            "Restart": "true",
            "Options": "3"
        },
        "protectedsettings": {
            "Password": "[parameters('domainPassword')]"
        }
    }
}

在代码的 copyIndex 部分,我尝试了变量和参数来设置 copyIndex 的偏移量。我也尝试过对其进行硬编码。我一直收到这样的错误。

{"telemetryId":"649e0a7f-2c22-41f9-bede-c13618f5053a", "bladeInstanceId":"Blade_b45c7efadff74340820345aa2e9d76c1_26_0", "galleryItemId":"Microsoft.Template","createBlade": "DeployToAzure", "code":"InvalidRequestContent", "message":"The request content was invalid and could not be deserialized: 'Error converting value \"[concat('Microsoft.Compute/virtualMachines/', concat(parameters('vmNamePrefix'), padLeft(copyIndex(variables('StartOfCountString')),3, '0')))]\" to type 'System.String[]'. Path 'properties.template.resources[1].resources[0].dependsOn', line 259, position 171.'."}

我想为复制索引设置偏移量的原因是这样我可以构建十台机器,如 W2016SRV-001、002、... 010,然后再构建更多从 011 开始的机器, 012、013 等

还有其他解决方法,但我真的希望能够在 ARM 模板中执行此操作,在我进行了大量谷歌搜索之后,我还不知道为什么它不可行。

................................................ ...................................

好的,按照@4c74356b41 的建议编辑模板后,当我尝试通过门户部署模板时,我现在遇到了一个全新的不同错误。

{"telemetryId":"649e0a7f-2c22-41f9-bede-c13618f5053a",
"bladeInstanceId":"Blade_b45c7efadff74340820345aa2e9d76c1_26_0",
"galleryItemId":"Microsoft.Template","createBlade":"DeployToAzure",
"code":"InvalidTemplate",
"message":"Deployment template validation failed: 'The template resource 
'[concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'),
'-vm/JoinDomain')]' 
at line '250' column '13' is not valid. Copying nested resources is not supported. 
Please see https://aka.ms/arm-copy/#looping-on-a-nested-resource for usage details.'."}

所以按照错误消息中的 link 阅读更多内容...我需要做的是将 Domain Join Extension 作为 VM 的子项删除,并将其作为顶级资源.呜呜呜它起作用了。

所以现在我的 ARM 模板的结尾看起来像这样。

            *** VM bits are here ***
                }
            }
        }, <-- end of the VM bits.
        {
        "apiVersion": "2016-03-30",
        "type": "Microsoft.Compute/virtualMachines/extensions",
        "name": "[concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm/JoinDomain')]",
        "location": "[resourceGroup().location]",
        "copy": {
            "name": "DomainJoinLoop",
            "count": "[parameters('numberOfInstances')]"
            },
        "dependsOn": [ "vmLoop" ],
        "properties": 
            {
            "publisher": "Microsoft.Compute",
            "type": "JsonADDomainExtension",
            "typeHandlerVersion": "1.3",
            "autoUpgradeMinorVersion": true,
            "settings":
                {
                "Name": "[parameters('domainToJoin')]",
                "User": "[concat(parameters('domainToJoin'), '\', parameters('domainUsername'))]",
                "OUPath": "[parameters('ouPath')]",
                "Restart": "true",
                "Options": "3"
                },
            "protectedsettings": {"Password": "[parameters('domainPassword')]"}
            }
        }

    ],
"outputs":
    {}
}

多个 VM 并行部署并且全部加入到域中没有问题。

我吸取的教训就是这些,我希望它们在未来对其他人有所帮助。

  1. 不能对子资源使用 copyIndex。
  2. 确保正确命名 DomainJoin 扩展。

比较您的虚拟机名称和扩展名称:

concat(parameters('vmNamePrefix'), padLeft(copyIndex(parameters('StartOfCount')),3, '0'), '-vm')
concat(parameters('vmNamePrefix'), padLeft(copyIndex(variables('StartOfCountString')),3, '0'), '/JoinDomain')

除了您正在使用不需要的变量这一事实之外,您还缺少 -vm。扩展名称必须采用 parent_name/extension_name 格式,否则它不知道该扩展属于哪个父资源(它必须始终属于某个虚拟机)。

你的dependsOn也是错误的,你可以简化为:

"dependsOn": [ "vmLoop" ]