使用 AzureRM 在同一 vnet 中部署不同子网的嵌套模板

Nesting templates that deploy different subnets in the same vnet with AzureRM

我正在尝试通过模板实现一定程度的容器化。最后,我希望能够在一个模板中定义驻留在虚拟网络和子网(如数据库集群)内的组件(一台或多台服务器)。通过另一个模板,定义驻留在虚拟网络和子网内的组件,但可能是不同的。

模板A:

模板 B:

这样,将模板 B 嵌套在模板 A 中会让我得到:

模板 AB:

相反,我收到以下错误:

New-AzureRmResourceGroupDeployment : 4:56:27 PM - Resource 
 Microsoft.Network/virtualNetworks 'overlayTest-vnet' failed with message '
{
  "error": {
    "code": "InUseSubnetCannotBeDeleted",
    "message": "Subnet overlayTest-subnet2 is in use by  /subscriptions/beep-boop/resourceGroups/d/providers/Microsoft.Network/networkInterfaces/overlayTest-vnet-interface2/ipConfigurations/ipconfig1 and cannot be deleted.",
    "details": []
  }
}

生成的资源组是这样填充的(就像部署了嵌套模板并且嵌套后的资源退出一样)。

模板 AB:

而我的模板如下:

azuredeploy.json

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "solutionName": {
      "type": "string",
      "defaultValue": "testing"
    },
    "vmIP": {
      "type": "string",
      "defaultValue": "10.0.100.100"
    },
    "virtualNetworkName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'), '-vnet')]"
    },
    "vnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.0.0/16"
    },
    "subnetName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'),'-subnet1')]"
    },
    "subnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.100.0/24"
    },
    "subnetName2": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'),'-subnet2')]"
    },
    "subnetAddressPrefix2": {
      "type": "string",
      "defaultValue": "10.0.101.0/24"
    },
    "_artifactsLocation": {
      "type": "string"
    },
    "_artifactsLocationSasToken": {
      "type": "securestring"
    }
  },
  "variables": {
    "OverlaySubnetTemplateFolder": "nestedtemplates",
    "OverlaySubnetTemplateFileName": "OverlaySubnet.json",
    "OverlaySubnetTemplateParametersFileName": "OverlaySubnet.parameters.json",
    "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
    "subnetRef": "[concat(variables('vnetID'), '/subnets/', parameters('subnetName'))]"
  },
  "resources": [
    {
      "name": "OverlaySubnet",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('OverlaySubnetTemplateFolder'), '/', variables('OverlaySubnetTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "solutionName": {
            "value": "[parameters('solutionName')]"
          }
        }
      }
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/virtualNetworks",
      "name": "[parameters('virtualNetworkName')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "addressSpace": {
          "addressPrefixes": [
            "[parameters('vnetAddressPrefix')]"
          ]
        },
        "subnets": [
          {
            "name": "[parameters('subnetName')]",
            "properties": {
              "mode": "Incremental",
              "addressPrefix": "[parameters('subnetAddressPrefix')]"
            }
          }
        ],
        "virtualNetworkPeerings": []
      }
    },
    {
      "type": "Microsoft.Network/virtualNetworks/subnets",
      "name": "[concat(parameters('virtualNetworkName'), '/', parameters('subnetName'))]",
      "apiVersion": "2017-06-01",
      "properties": {
        "addressPrefix": "[parameters('subnetAddressPrefix')]",
        "privateAccessServices": []
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]"
      ]
    },
    {
      "type": "Microsoft.Network/virtualNetworks/subnets",
      "name": "[concat(parameters('virtualNetworkName'), '/', parameters('subnetName2'))]",
      "apiVersion": "2017-06-01",
      "properties": {
        "addressPrefix": "[parameters('subnetAddressPrefix2')]",
        "privateAccessServices": []
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]"
      ]
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[concat(parameters('virtualNetworkName'), '-interface1')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              /*
              "privateIPAllocationMethod": "Static",
              // reserved IP address range for /24 is 1-3, so start with [0 + 4] or more
              "privateIPAddress": "[parameters('vmIP')]",
              */
              "privateIPAllocationMethod": "Dynamic",
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
            }
          }
        ]
      }
    }
  ],
  "outputs": {}
}

nestedtemplates/OverlaySubnet.json

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "solutionName": {
      "type": "string",
      "defaultValue": "testing"
    },
    "virtualNetworkName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'), '-vnet')]"
    },
    "vnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.0.0/16"
    },
    "subnetName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'),'-subnet2')]"
    },
    "subnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.101.0/24"
    }
  },
  "variables": {
    "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]",
    "subnetRef": "[concat(variables('vnetID'),'/subnets/', parameters('subnetName'))]"
  },
  "resources": [
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/virtualNetworks",
      "name": "[parameters('virtualNetworkName')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [],
      "properties": {
        "addressSpace": {
          "addressPrefixes": [
            "[parameters('vnetAddressPrefix')]"
          ]
        },
        "subnets": [
          {
            "name": "[parameters('subnetName')]",
            "properties": {
              "mode": "Incremental",
              "addressPrefix": "[parameters('subnetAddressPrefix')]"
            }
          }
        ],
        "virtualNetworkPeerings": []
      }
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[concat(parameters('virtualNetworkName'), '-interface2')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              /*
              "privateIPAllocationMethod": "Static",
              // reserved IP address range for /24 is 1-3, so start with [0 + 4] or more
              "privateIPAddress": "[parameters('vmIP')]",
              */
              "privateIPAllocationMethod": "Dynamic",
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
            }
          }
        ]
      }
    }
  ],
  "outputs": {}
}

在嵌套模板中将 VNet 资源更改为子网资源,它将起作用。你试图做的是创建一个新的 vnet 而不是现有的 vnet,因此它会尝试更新现有的 vnet 以匹配你的定义(因此删除所有子网并添加覆盖中定义的 1)。

但老实说,整个方法都是错误的,你应该使用属性复制循环并一次性完成所有事情。

最后,我实现了创建嵌套模板的目标,该模板在父模板的虚拟网络中定义了自己的子网,同时保持了在没有父模板的情况下部署嵌套模板的能力。关键是使用有条件的虚拟网络资源,并确保嵌套模板在嵌套时不会破坏父模板的虚拟网络。感谢@4c74356b41 将我推向正确的方向

azuredeploy.json

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "solutionName": {
      "type": "string",
      "defaultValue": "testing"
    },
    "vmIP": {
      "type": "string",
      "defaultValue": "10.0.100.100"
    },
    "virtualNetworkName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'), '-vnet')]"
    },
    "vnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.0.0/16"
    },
    "subnetName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'),'-subnet1')]"
    },
    "subnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.100.0/24"
    },
    "subnetName2": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'),'-subnet2')]"
    },
    "subnetAddressPrefix2": {
      "type": "string",
      "defaultValue": "10.0.101.0/24"
    },
    "_artifactsLocation": {
      "type": "string"
    },
    "_artifactsLocationSasToken": {
      "type": "securestring"
    }
  },
  "variables": {
    "OverlaySubnetTemplateFolder": "nestedtemplates",
    "OverlaySubnetTemplateFileName": "OverlaySubnet.json",
    "OverlaySubnetTemplateParametersFileName": "OverlaySubnet.parameters.json",
    "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
    "subnetRef": "[concat(variables('vnetID'), '/subnets/', parameters('subnetName'))]"
  },
  "resources": [
    {
      "name": "OverlaySubnet",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('OverlaySubnetTemplateFolder'), '/', variables('OverlaySubnetTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "solutionName": {
            "value": "[parameters('solutionName')]"
          },
          "createVirtualNetwork": {
            "value": "no"
          }
        }
      }
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/virtualNetworks",
      "name": "[parameters('virtualNetworkName')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "addressSpace": {
          "addressPrefixes": [
            "[parameters('vnetAddressPrefix')]"
          ]
        },
        "subnets": [
          {
            "name": "[parameters('subnetName')]",
            "properties": {
              "mode": "Incremental",
              "addressPrefix": "[parameters('subnetAddressPrefix')]"
            }
          }
        ],
        "virtualNetworkPeerings": []
      }
    },
    {
      "type": "Microsoft.Network/virtualNetworks/subnets",
      "name": "[concat(parameters('virtualNetworkName'), '/', parameters('subnetName'))]",
      "apiVersion": "2017-06-01",
      "properties": {
        "addressPrefix": "[parameters('subnetAddressPrefix')]",
        "privateAccessServices": []
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]"
      ]
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[concat(parameters('virtualNetworkName'), '-interface1')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              /*
              "privateIPAllocationMethod": "Static",
              // reserved IP address range for /24 is 1-3, so start with [0 + 4] or more
              "privateIPAddress": "[parameters('vmIP')]",
              */
              "privateIPAllocationMethod": "Dynamic",
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
            }
          }
        ]
      }
    }
  ],
  "outputs": {}
}

nestedtemplates/OverlaySubnet.json

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "solutionName": {
      "type": "string",
      "defaultValue": "testing"
    },
    "createVirtualNetwork": {
      "type": "string",
      "defaultValue": "yes",
      "allowedValues": [
        "yes",
        "no"
      ]
    },
    "virtualNetworkName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'), '-vnet')]"
    },
    "vnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.0.0/16"
    },
    "subnetName": {
      "type": "string",
      "defaultValue": "[concat(parameters('solutionName'),'-subnet2')]"
    },
    "subnetAddressPrefix": {
      "type": "string",
      "defaultValue": "10.0.101.0/24"
    }
  },
  "variables": {
    "createVirtualNetwork": "[equals(parameters('createVirtualNetwork'), 'yes')]",
    "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]",
    "subnetRef": "[concat(variables('vnetID'),'/subnets/', parameters('subnetName'))]"
  },
  "resources": [
    {
      "condition": "[variables('createVirtualNetwork')]",
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/virtualNetworks",
      "name": "[parameters('virtualNetworkName')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [],
      "properties": {
        "addressSpace": {
          "addressPrefixes": [
            "[parameters('vnetAddressPrefix')]"
          ]
        },
        "subnets": [
          {
            "name": "[parameters('subnetName')]",
            "properties": {
              "mode": "Incremental",
              "addressPrefix": "[parameters('subnetAddressPrefix')]"
            }
          }
        ],
        "virtualNetworkPeerings": []
      }
    },
    {
      "type": "Microsoft.Network/virtualNetworks/subnets",
      "name": "[concat(parameters('virtualNetworkName'), '/', parameters('subnetName'))]",
      "apiVersion": "2017-06-01",
      "properties": {
        "addressPrefix": "[parameters('subnetAddressPrefix')]",
        "privateAccessServices": []
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]"
      ]
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[concat(parameters('virtualNetworkName'), '-interface2')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
        "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'), '/subnets/', parameters('subnetName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              /*
              "privateIPAllocationMethod": "Static",
              // reserved IP address range for /24 is 1-3, so start with [0 + 4] or more
              "privateIPAddress": "[parameters('vmIP')]",
              */
              "privateIPAllocationMethod": "Dynamic",
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
            }
          }
        ]
      }
    }
  ],
  "outputs": {}
}