2015年6月30日

使用Azure Resource Manager Template佈署虛擬機器

Azure Resource Manager是Azure下一代的管理機制,透過Azure Resource Manager我們可以將所需要的Infrastructure透過Template file定義,然後以Powershell將定義好的Infrastructure佈署到Azure上。

相關的資源可以參考以下的MSDN文件:

本系列文章會使用ARM Template佈署一組如下的虛擬機器、虛擬網路並設定HTTP Load Balance Set。

image

為了讓這個佈署更具彈性,因此我們會希望可以透過參數的方式指定要建立多少台機器,並為每一台機器設定一張網卡;同時,將所有的機器的80 port加入Load Balance中。

另外,我們會建立一台JumpBox,並為這台Jumpbox啟用RDP連線。

所有的動作,都會透過Visual Studie 2013與Azure SDK 2.6進行。

  • 首先開啟Visual Studio 2013,新增一個Azure Resource Manager專案

image

  • 打開DemploymentTemplateFile.json,可以看到一個基本的Template大致上分成這幾部分:parameters, variables, resources。其中
    • parameters定義了這個Template File可以接受的參數,在佈署時可以透過指定參數值來改變佈署環境,例如要開幾台機器,要開在哪個資料中心等等
    • variables定義在Template file中所需要的變數值,一般在這裡我們會將一些比較複雜的資訊透過variable的方式定義,在之後Template file中使用他;例如Resource ID。
    • resources則是我們定義實際infrastructure所需要使用到的resource的地方

image

  • 在這個範例中,我希望透過這個Template在佈署時,我可以指定將上述的整個系統佈署到哪個資料中心、要有幾台機器、機器所使用的Storage名稱、VM Size以及Public DNS名稱等等;我們也可以透過"defaultValue”來指定預設值。因此,在Parameters這裡,我需告以下的參數。
   1: parameters": {
   2:     "vmCount": {
   3:         "type": "int",
   4:         "metadata": {
   5:             "description": "Number of VMs"
   6:         }
   7:     },
   8:     "storageAccountName": {
   9:         "type": "string",
  10:         "metadata": {
  11:             "description": "Name of storage account"
  12:         }
  13:     },
  14:     "adminUsername": {
  15:         "type": "string",
  16:         "metadata": {
  17:             "description": "Admin username"
  18:         }
  19:     },
  20:     "adminPassword": {
  21:         "type": "securestring",
  22:         "metadata": {
  23:             "description": "Admin password"
  24:         }
  25:     },
  26:     "dnsNameforLBIP": {
  27:         "type": "string",
  28:         "metadata": {
  29:             "description": "DNS for Load Balancer IP"
  30:         }
  31:     },
  32:     "avsetName": {
  33:         "type": "string",
  34:         "defaultValue": "myAvSet"
  35:     },
  36:     "vmSize": {
  37:         "type": "string",
  38:         "defaultValue": "Standard_D2",
  39:         "metadata": {
  40:             "description": "Size ofthe VM"
  41:         }
  42:     }
  43: }


  • 接著,先宣告以下的variables


   1: "variables": {
   2:     "storageAccountType": "Standard_LRS",
   3:     "addressPrefix": "10.0.0.0/16",
   4:     "subnetName": "Subnet-1",
   5:     "subnetPrefix": "10.0.0.0/24",
   6:     "publicIPAddressType": "Dynamic",
   7:     "vmNicNamePrefix": "nicVMs",
   8:     "imagePublisher": "MicrosoftWindowsServer",
   9:     "imageOffer": "WindowsServer",
  10:     "imageSKU": "2012-R2-Datacenter",
  11:     "vnetName": "myVNET",
  12:     "publicIPAddressName": "myPublicIP",
  13:     "lbName": "myLB",
  14:     "vmNamePrefix": "myVM",
  15:     "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('vnetName'))]",
  16:     "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
  17:     "publicIPAddressID": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
  18:     "lbID": "[resourceId('Microsoft.Network/loadBalancers',variables('lbName'))]",
  19:     "frontEndIPConfigID": "[concat(variables('lbID'),'/frontendIPConfigurations/LoadBalancerFrontEnd')]",
  20:     "lbPoolID": "[concat(variables('lbID'),'/backendAddressPools/BackendPool')]",
  21:     "lbProbeID": "[concat(variables('lbID'),'/probes/tcpProbe')]",
  22:     "jumpBoxNICID": "[resourceId('Microsoft.Network/networkInterfaces','nicVMs0')]",
  23:     "jumpBoxBEIPID": "[concat(variables('jumpBoxNICID'), '/ipConfigurations/ipconfig1')]"
  24: },



    • Azure Resource Manager透過新的方式來取得機器的Image Source:

      • Image Publisher
      • Image Offer
      • Image SKU

    • 可以透過Get-AzureVMImageOffer、Get-AzureVMImageSKU與Get-AzureVMImagePublisher來取得可使用的Publisher、Offer與SKU。
    • vnetID、subnetRef、publicIPAddressID、lbID、lbPoolID、lbProbeID這幾個變數紀載了這些Resource的Unique ID,後續我們設定Resource時會需要用到。Resource ID的格式基本上如:[ResourceProviderName/ResourceName/{Name}];例如[resourceId(“Microsoft.Network/loadBalancers/myLB”)]指的是Microsoft.Network Provider所提供的loadBalancers這種Resource,其中名稱為myLB的Load Balancer物件。

  • 定義完parameters與variables之後,接著就可以進入真正的工作了。
  • 在Azure Resource Manager的世界裡,所有組成服務的基本元件都是一個Resource;以一台虛擬機器來說,需要網卡,網卡需要綁IP,虛擬機器本身等等都是一個Resource。為了要建置一台機器,我們需要先宣告所需要的Resource:

    • Storage Account:用來放VHD
    • Public IP:指定給機器的Load Balancer
    • Load Balancer

      • 需要設定InbountNatRule來Load Balance HTTP 80 port

    • Network Interface:每台機器至少需要一張網卡
    • Virtual Network:所有的機器都必須位於Virtual Network中

  • 首先是宣告Storage Account;注意其中accountType為我們要建立的Stroage類型,例如Locally Redundant、Geo-Redundant等等。而Storage Name也是透過[parameters(‘storageAccountName’)]取得輸入的storageAccountName這個參數值。


   1: {
   2:     "type": "Microsoft.Storage/storageAccounts",//Storage Account的Resource Provier Type
   3:     "name": "[parameters('storageAccountName')]",//Storage Account的名稱,
   4:                                                  //這裡是傳入的參數值,在我們執行powershell可以指定
   5:     "apiVersion": "2015-05-01-preview",          // API Version,固定值
   6:     "location": "[resourceGroup().location]",    // 資源要建立在哪個資料中心,
   7:                                                  //傳入參數,透過resourceGroup().location取得輸入值
   8:     "properties": {
   9:         "accountType": "[variables('storageAccountType')]"
  10:     
  11: },


  • 宣告一個availibility set資源


   1: {
   2:     "name": "[parameters('avsetName')]",
   3:     "type": "Microsoft.Compute/availabilitySets",
   4:     "location": "[resourceGroup().location]",
   5:     "apiVersion": "2015-05-01-preview",
   6:     "tags": {
   7:         "displayName": "avset"
   8:     }
   9: },


  • 宣告一個public IP address資源。public IP address資源可以附加到Load Balancer或是Network Interface上。設定中的domainNameLabel則是我們所給予這個IP位址的DNS名稱。這邊如果我們指定abc,則最後的DNS名稱為abc.[datacenter].cloudapp.azure.com


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "type": "Microsoft.Network/publicIPAddresses",
   4:     "name": "[variables('publicIPAddressName')]",
   5:     "location": "[resourceGroup().location]",
   6:     "properties": {
   7:         "publicIPAllocationMethod": "[variables('publicIPAddressType')]",
   8:         "dnsSettings": {
   9:             "domainNameLabel": "[parameters('dnsNameforLBIP')]"
  10:         }
  11:     }
  12: },


  • 宣告虛擬網路;目前為止宣告的內容都很直觀,只要了解格式就很容易。


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "type": "Microsoft.Network/virtualNetworks",
   4:     "name": "[variables('vnetName')]",
   5:     "location": "[resourceGroup().location]",
   6:     "properties": {
   7:         "addressSpace": {
   8:             "addressPrefixes": [
   9:                 "[variables('addressPrefix')]"
  10:             ]
  11:         },
  12:         "subnets": [
  13:             {
  14:                 "name": "[variables('subnetName')]",
  15:                 "properties": {
  16:                     "addressPrefix": "[variables('subnetPrefix')]"
  17:                 }
  18:             }
  19:         ]
  20:     }
  21: },


  • 接下來宣告一個Public Load Balancer;我們會把剛剛宣告的Public IP位址賦予此Load Balancer;並且,由於這個Demo是建立幾台web server並透過Load Balancer做流量分配,因此,我們還會建立一個針對HTTP 80 port的Load Balancer Rule。
  • 為了方便起見,我將loadBalancer的設定拆為幾個部分來看,首先是基本設定;這裡的設定就如同先前一樣,只是給予基本的資料,同時指定這個load balancer資源必須要依賴public IP address資源。


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "name": "[variables('lbName')]",
   4:     "type": "Microsoft.Network/loadBalancers",
   5:     "location": "[resourceGroup().location]",
   6:     "dependsOn": [
   7:         "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
   8:     ],
   9:     //omitted
  10: }


  • 接著在properties中指定這個load balancer的Public IP為我們先前宣告的Public IP Address資源


   1: "properties": {
   2:     "frontendIPConfigurations": [
   3:         {
   4:             "name": "LoadBalancerFrontEnd",
   5:             "properties": {
   6:                 "publicIPAddress": {
   7:                     "id": "[variables('publicIPAddressID')]"
   8:                 }
   9:             }
  10:         }
  11:     ],
  12:     //omitted
  13: }


  • 這個load balancer他的後端IP範圍會被指定在BackendPool這個IP Range中


   1: "backendAddressPools": [
   2:     {
   3:         "name": "BackendPool"
   4:     }
   5: ],


  • 宣告一個Probe規則,針對後端的HTTP 80 port做health check;每五秒一次,兩次無法透過TCP連線到80 port,便認為該台機器無法服務80 port


   1: "probes": [
   2:     {
   3:         "name": "tcpProbe",
   4:         "properties": {
   5:             "protocol": "tcp",
   6:             "port": 80,
   7:             "intervalInSeconds": "5",
   8:             "numberOfProbes": "2"
   9:         }
  10:     }
  11: ]


  • 宣告一個HTTP Load Balancer規則;將由frontendIPConfiguration中指定的Public IP資源流入的流量,分散到backendAddressPool這個資源所指定的IP Range,其Portocol為tcp,Public/Private Port均為80;Probe為先前建立的probe。如果已經熟悉原本的VM建立步驟的話,這一個動作就是在設定Load Balance Set


   1: "loadBalancingRules": [
   2:     {
   3:         "name": "LBRule",
   4:         "properties": {
   5:             "frontendIPConfiguration": {
   6:                 "id": "[variables('frontEndIPConfigID')]"
   7:             },
   8:             "backendAddressPool": {
   9:                 "id": "[variables('lbPoolID')]"
  10:             },
  11:             "protocol": "tcp",
  12:             "frontendPort": 80,
  13:             "backendPort": 80,
  14:             "enableFloatingIP": false,
  15:             "idleTimeoutInMinutes": 5,
  16:             "probe": {
  17:                 "id": "[variables('lbProbeID')]"
  18:             }
  19:         }
  20:     }
  21: ], 


  • 最後,建立一個Inbount NAT Rule,指定將Public 50001 port導向3389 port


   1: "inboundNatRules": [
   2:     {
   3:         "name": "RDP-JumpBox",
   4:  
   5:         "properties": {
   6:             "frontendIPConfiguration": {
   7:                 "id": "[variables('frontEndIPConfigID')]"
   8:             },
   9:             /*
  10:             "backendIPConfiguration": {
  11:                 "id": "[variables('jumpBoxBEIPID')]"
  12:             },
  13:             */
  14:             "protocol": "tcp",
  15:             "frontendPort": 50001,
  16:             "backendPort": 3389,
  17:             "enableFloatingIP": false
  18:         }
  19:     }
  20: ],


  • 完整的宣告如下:


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "name": "[variables('lbName')]",
   4:     "type": "Microsoft.Network/loadBalancers",
   5:     "location": "[resourceGroup().location]",
   6:     "dependsOn": [
   7:         "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
   8:     ],
   9:     "properties": {
  10:         "frontendIPConfigurations": [
  11:             {
  12:                 "name": "LoadBalancerFrontEnd",
  13:                 "properties": {
  14:                     "publicIPAddress": {
  15:                         "id": "[variables('publicIPAddressID')]"
  16:                     }
  17:                 }
  18:             }
  19:         ],
  20:         "backendAddressPools": [
  21:             {
  22:                 "name": "BackendPool"
  23:             }
  24:         ],
  25:         "loadBalancingRules": [
  26:             {
  27:                 "name": "LBRule",
  28:                 "properties": {
  29:                     "frontendIPConfiguration": {
  30:                         "id": "[variables('frontEndIPConfigID')]"
  31:                     },
  32:                     "backendAddressPool": {
  33:                         "id": "[variables('lbPoolID')]"
  34:                     },
  35:                     "protocol": "tcp",
  36:                     "frontendPort": 80,
  37:                     "backendPort": 80,
  38:                     "enableFloatingIP": false,
  39:                     "idleTimeoutInMinutes": 5,
  40:                     "probe": {
  41:                         "id": "[variables('lbProbeID')]"
  42:                     }
  43:                 }
  44:             }
  45:         ], 
  46:         "inboundNatRules": [
  47:             {
  48:                 "name": "[concat('RDP-JumpBox']",
  49:  
  50:                 "properties": {
  51:                     "frontendIPConfiguration": {
  52:                         "id": "[variables('frontEndIPConfigID')]"
  53:                     },
  54:                     /*
  55:                     "backendIPConfiguration": {
  56:                         "id": "[variables('jumpBoxBEIPID')]"
  57:                     },
  58:                     */
  59:                     "protocol": "tcp",
  60:                     "frontendPort": 50001,
  61:                     "backendPort": 3389,
  62:                     "enableFloatingIP": false
  63:                 }
  64:             }
  65:         ],
  66:         "probes": [
  67:             {
  68:                 "name": "tcpProbe",
  69:                 "properties": {
  70:                     "protocol": "tcp",
  71:                     "port": 80,
  72:                     "intervalInSeconds": "5",
  73:                     "numberOfProbes": "2"
  74:                 }
  75:             }
  76:         ]
  77:     }
  78: },


  • 宣告給所有web server使用的Network Interface資源;由於目前我們並不知道會建立幾來web server,這個數量會在稍後透過powersehll佈署資源時傳入;因此這在裡,我們透過copyIndex()與copy來指定在這裡我們會根據傳入參數vmCount的值,跑一個迴圈。
  • 所建立出來的interface名稱如:myNic0、myNic1…etc


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "type": "Microsoft.Network/networkInterfaces",
   4:     "name": "[concat(variables('vmNicNamePrefix'), copyindex())]",
   5:     "location": "[resourceGroup().location]",
   6:     "copy": {
   7:         "name": "mainloop",
   8:         "count": "[parameters('vmCount')]"
   9:     },
  10:     "dependsOn": [
  11:         "[concat('Microsoft.Network/virtualNetworks/', variables('vnetName'))]",
  12:         "[concat('Microsoft.Network/loadBalancers/', variables('lbName'))]"
  13:     ],
  14:     "properties": {
  15:         "ipConfigurations": [
  16:             {
  17:                 "name": "ipconfig1",
  18:                 "properties": {
  19:                     "privateIPAllocationMethod": "Dynamic",
  20:                     "subnet": {
  21:                         "id": "[variables('subnetRef')]"
  22:                     },
  23:                     "loadBalancerBackendAddressPools": [
  24:                         {
  25:                             "id": "[concat(variables('lbID'), '/backendAddressPools/BackendPool')]"
  26:                         }
  27:                     ]
  28:                 }
  29:             }
  30:         ]
  31:     }
  32: },


  • 宣告JumpBox使用的NIC,與web server NIC的差別在於多了loadBalancerInboundNatRules宣告這張網卡會使用到load balancer上宣告的inbound NAT規則。


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "type": "Microsoft.Network/networkInterfaces",
   4:     "name": "nicJumpBox",
   5:     "location": "[resourceGroup().location]",
   6:     "dependsOn": [
   7:         "[concat('Microsoft.Network/virtualNetworks/', variables('vnetName'))]",
   8:         "[concat('Microsoft.Network/loadBalancers/', variables('lbName'))]"
   9:     ],
  10:     "properties": {
  11:         "ipConfigurations": [
  12:             {
  13:                 "name": "ipconfig1",
  14:                 "properties": {
  15:                     "privateIPAllocationMethod": "Dynamic",
  16:                     "subnet": {
  17:                         "id": "[variables('subnetRef')]"
  18:                     },
  19:                     "loadBalancerBackendAddressPools": [
  20:                         {
  21:                             "id": "[concat(variables('lbID'), '/backendAddressPools/BackendPool')]"
  22:                         }
  23:                     ],
  24:                     "loadBalancerInboundNatRules": [
  25:                         {
  26:                             "id": "[concat(variables('lbID'),'/inboundNatRules/RDP-JumpBox')]"
  27:                             
  28:                         }
  29:                     ]
  30:                 }
  31:             }
  32:         ]
  33:     }
  34: }


  • 以上都完成後,接著要宣告所有的web Server VM;看起來很長,但大多只是標準格式。

    • VM的名稱為以[vmPrefix][0,1,2,3…etc]的格式命命
    • VHD的檔名也會透過copyindex()與copy動態產生


   1: {
   2:     "apiVersion": "2015-05-01-preview",
   3:     "type": "Microsoft.Compute/virtualMachines",
   4:     "name": "[concat(variables('vmNamePrefix'),copyindex())]",
   5:     "location": "[resourceGroup().location]",
   6:     "dependsOn": [
   7:         "[concat('Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]",
   8:         "[concat('Microsoft.Network/networkInterfaces/', concat(variables('vmNicNamePrefix'), copyindex()))]"
   9:     ],
  10:     "copy": {
  11:         "name": "mainloop",
  12:         "count": "[parameters('vmCount')]"
  13:     },
  14:     "properties": {
  15:         "hardwareProfile": {
  16:             "vmSize": "[parameters('vmSize')]"
  17:         },
  18:         "availabilitySet": {
  19:             "id": "[resourceId('Microsoft.Compute/availabilitySets',parameters('avsetName'))]"
  20:         },
  21:         "osProfile": {
  22:             "computername": "[concat(variables('vmNamePrefix'),copyindex())]",
  23:             "adminUsername": "[parameters('adminUsername')]",
  24:             "adminPassword": "[parameters('adminPassword')]"
  25:         },
  26:         "storageProfile": {
  27:             "imageReference": {
  28:                 "publisher": "[variables('imagePublisher')]",
  29:                 "offer": "[variables('imageOffer')]",
  30:                 "sku": "[variables('imageSKU')]",
  31:                 "version": "latest"
  32:             },
  33:             "osDisk": {
  34:                 "name": "osdisk",
  35:                 "vhd": {
  36:                     "uri": "[concat('http://',parameters('storageAccountName'),'.blob.core.windows.net/vhds/','osdisk-vm-', copyindex(), '.vhd')]"
  37:                 },
  38:                 "caching": "ReadWrite",
  39:                 "createOption": "FromImage"
  40:             }
  41:         },
  42:         "networkProfile": {
  43:             "networkInterfaces": [
  44:                 {
  45:                     "properties": {
  46:                         "primary": false
  47:                     },
  48:                     "id": "[resourceId('Microsoft.Network/networkInterfaces',concat(variables('vmNicNamePrefix'), copyindex()))]",
  49:                     "copy": {
  50:                         "name": "mainloop",
  51:                         "count": "[parameters('vmCount')]"
  52:                     }
  53:                 }
  54:             ]
  55:         }
  56:     }
  57: }


  • 宣告Jumpbox VM


   1: {
   2: Version": "2015-05-01-preview",
   3: e": "Microsoft.Compute/virtualMachines",
   4: e": "jumpbox",
   5: ation": "[resourceGroup().location]",
   6: endsOn": [
   7: "[concat('Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]",
   8: "[concat('Microsoft.Network/networkInterfaces/', 'nicJumpBox')]"
   9:             ],
  10: perties": {
  11: "hardwareProfile": {
  12:     "vmSize": "[parameters('vmSize')]"
  13: },
  14: "availabilitySet": {
  15:     "id": "[resourceId('Microsoft.Compute/availabilitySets',parameters('avsetName'))]"
  16: },
  17: "osProfile": {
  18:     "computername": "[variables('vmNamePrefix')]",
  19:     "adminUsername": "[parameters('adminUsername')]",
  20:     "adminPassword": "[parameters('adminPassword')]"
  21: },
  22: "storageProfile": {
  23:     "imageReference": {
  24:         "publisher": "[variables('imagePublisher')]",
  25:         "offer": "[variables('imageOffer')]",
  26:         "sku": "[variables('imageSKU')]",
  27:         "version": "latest"
  28:     },
  29:     "osDisk": {
  30:         "name": "osdisk",
  31:         "vhd": {
  32:             "uri": "[concat('http://',parameters('storageAccountName'),'.blob.core.windows.net/vhds/','osdisk-vm-jumpbox.vhd')]"
  33:         },
  34:         "caching": "ReadWrite",
  35:         "createOption": "FromImage"
  36:     }
  37: },
  38: "networkProfile": {
  39:     "networkInterfaces": [
  40:         {
  41:             "properties": {
  42:                 "primary": false
  43:             },
  44:             "id": "[resourceId('Microsoft.Network/networkInterfaces','nicJumpBox')]"
  45:         }
  46:     ]
  47: }
  48:             }
  49:         }


  • 接著,宣告DSC Extension,我們要透過DSC設定IIS Server;所使用的powershell script已經壓縮為zip檔案並上傳到public accessiable的Azure Storage了


   1: {
   2:     "type": "Microsoft.Compute/virtualMachines/extensions",
   3:     "name": "[concat(variables('vmNamePrefix'),copyindex(),'/DSC-', copyindex())]",
   4:     "copy": {
   5:         "name": "mainloop",
   6:         "count": "[parameters('vmCount')]"
   7:     },
   8:     "apiVersion": "2015-05-01-preview",
   9:     "location": "[resourceGroup().location]",
  10:     "dependsOn": [
  11:         "[concat('Microsoft.Compute/virtualMachines/', concat(variables('vmNamePrefix'),copyindex()))]"
  12:     ],
  13:     "properties": {
  14:         "publisher": "Microsoft.Powershell",
  15:         "type": "DSC",
  16:         "typeHandlerVersion": "1.9",
  17:         "settings": {
  18:             "ModulesUrl": "http://tesskyrim.blob.core.windows.net/test/setup.zip",
  19:             "SasToken": "",
  20:             "ConfigurationFunction": "setup.ps1\\SetupIIS",
  21:             "Properties": {
  22:                 "MachineName": "[concat(variables('vmNamePrefix'),copyindex())]"
  23:             }
  24:         },
  25:         "protectedSettings": null
  26:     }
  27: }


  • 完整的Powershell如下


   1: Configuration SetupIIS
   2: {
   3:   param ($MachineName)
   4:  
   5:   Node $MachineName
   6:   {
   7:     #Install the IIS Role
   8:     WindowsFeature IIS
   9:     {
  10:       Ensure = “Present”
  11:       Name = “Web-Server”
  12:     }
  13:  
  14:     #Install ASP.NET 4.5
  15:     WindowsFeature ASP
  16:     {
  17:       Ensure = “Present"
  18:       Name = "Web-Asp-Net45"
  19:     }
  20:  
  21:      WindowsFeature WebServerManagementConsole
  22:     {
  23:         Name = "Web-Mgmt-Console"
  24:         Ensure = "Present"
  25:     }
  26:   }
  27: } 


  • 完整的sample在此
  • 打開powershell,執行以下的指令佈署整個環境到Azure



   1: New-AzureResourceGroup -Name michi-arm-new2-RG -Location "East Asia" -TemplateFile DeploymentTemplateFile1.json -storageAccountNameFromTemplate michiarmstg2 -adminUsername azureuser -dnsNameforLBIP michiarm201506292 -vmSize Standard_A0 -vmCount 3

About Me