又来分享一篇Azure相关的blog,同样是关于创建虚拟机的,这算是在Azure中最常见的任务了。
很多时候我们在从本地迁移到Azure或者是从一个Azure租户迁移到另外一个时,都会遇到如何迁移虚拟机或者物理机的问题,通常意义来说,迁移VHD是最简单的做法,Azure本身就是构建在Hyper-V之上,如果之前使用的是Azure或者是一代Hyper-V虚拟机,那迁移如果不考虑业务是否允许中断的情况,单纯从技术上其实并不是件太复杂的事
首先如果是本地的虚拟机或者物理机,经过转换得到VHD文件之后需要上传到Azure Storage Account中,以便用来创建虚拟机,上传的方法其实很多,一般比较推荐的是使用AZcopy,这个是微软自己的工具,可以支持很多文件拷贝的场景,包括从不同的storage account迁移数据,Azure File迁移数据等等,关于AZcopy更详细的介绍可以参考微软官方的文档https://docs.microsoft.com/zh-cn/azure/storage/common/storage-use-azcopy
文档是Azure global的, 但是这个工具本身也是支持Mooncake的,如果要下载AZcopy,可以直接通过这个短地址下载http://aka.ms/downloadazcopy
AZcopy的使用方法就不介绍了,并不复杂,微软的文档也写的很详细。下边来说下将VHD上传到Azure Storage Account之后,如何创建虚拟机,众所周知的是,ARM不像ASM一样可以直接从portal使用VHD创建虚拟机,这一操作在ARM下需要用PowerShell来完成,类似的脚本其实不少,但是一般来说做成比较灵活的参数化的比较少,都是需要自己修改很多变量的那种,今天来分享一个我自己用的脚本
先上代码,这个脚本其实和我之前分享的那些一样,可以支持MoonCake和Azure Global,使用时如果是MoonCake需要用switch AzureMoonCake控制,脚本本身需要指定的参数有很多,因为创建虚拟机的时候其实要用到很多参数,比如VNET,SUBNET,ResourceGroup等等,有一些参数是必须要指定的,比如VHD的URI,虚拟机的名字,subnet和Vnet名字等,要注意的是,这个脚本里如果ResourceGroup,Subnet或者Vnet不存在的话会自动进行创建,地址空间则是用VNetPrefix等来控制,这些是有默认值的,如果想用自己的,请在参数里指定出来,脚本里自动生成网卡这些function其实是参考的script center里的一个脚本,再加上自己根据需要进行了一定的修改,这里要特别感谢下
param ( [parameter(Mandatory = $false)] [switch]$AzureMoonCake, [parameter(Mandatory = $false)] [switch]$DoNotLogin, [Parameter(Mandatory = $true)] [string]$LocationName, [Parameter(Mandatory = $true)] [string]$ResourceGroupName, [parameter(Mandatory = $true)] [string]$VMName, [parameter(Mandatory = $true)] [string]$VhdUri, [parameter(Mandatory = $true)] [string]$VnetName, [parameter(Mandatory = $true)] [string]$SubnetName, [parameter(Mandatory = $false)] [switch]$HybridBenefit, [parameter(Mandatory = $false)] [string]$VMSizeName = "Standard_D2", [parameter(Mandatory = $false)] [string]$AvailabilitySetName, [parameter(Mandatory = $false)] [ValidateSet("Windows", "Linux")][string]$OS = 'Windows', [parameter(Mandatory = $false)] [string]$VnetPrefix = "10.140.0.0/16", [parameter(Mandatory = $false)] [string]$SubnetPrefix = "10.140.0.0/24", [parameter(Mandatory = $false)] [ValidateSet("Static", "Dynamic")][string]$PublicIPAllocationMethod = "Dynamic" ) #检查Location是否存在,并返回结果 function Check-AzureRmLocation() { param ( [string]$LocationName = $(throw "Parameter missing: -LocationName LocationName") ) Write-Host "$(Get-Date) * Checking location $LocationName" -ForegroundColor Green $Location = Get-AzureRmLocation | Where-Object { $_.Location -eq $LocationName } If (-not ($Location)) { Write-Host "$(Get-Date) * The location" $LocationName "does not exist." -ForegroundColor Red return $false } Else { Write-Host "$(Get-Date) * Location $LocationName exists" -ForegroundColor Green return $true } } #检查RG是否存在,不存在则创建新的RG function Check-AzureRmResourceGroup() { param ( [string]$ResourceGroupName = $(throw "Parameter missing: -ResourceGroupName ResourceGroupName"), [string]$LocationName = $(throw "Parameter missing: -LocationName LocationName") ) Write-Host "$(Get-Date) * Checking resource group $ResourceGroupName, if not, created it." -ForegroundColor Green Try { $ResourceGroup = Get-AzureRmResourceGroup -Name $ResourceGroupName -Location $LocationName -ErrorAction SilentlyContinue If (-not ($ResourceGroup)) { Write-Host "$(Get-Date) * Creating resource group" $ResourceGroupName "..." -ForegroundColor Green New-AzureRmResourceGroup -Name $ResourceGroupName -Location $LocationName -ErrorAction Stop return $true } Else { Write-Host "$(Get-Date) * Resource group $ResourceGroupName exists" -ForegroundColor Green return $true } } Catch { Write-Host -ForegroundColor Red "$(Get-Date) * Create resource group" $LocationName "failed." $_.Exception.Message return $false } } #随机生成新的NIC function AutoGenerate-AzureRmNetworkInterface() { param ( [string]$ResourceGroupName = $(throw "Parameter missing: -ResourceGroupName ResourceGroupName"), [string]$LocationName = $(throw "Parameter missing: -LocationName LocationName"), [string]$VMName = $(throw "Parameter missing: -VMName VMName"), [string]$SubnetName, [string]$VnetName, [string]$SubnetPrefix = "10.140.0.0/24", [string]$VnetPrefix = "10.140.0.0/16", [switch]$Create, [string]$PublicIPAllocationMethod = "Dynamic" ) Try { $RandomNum = Get-Random -minimum 100 -maximum 9999 $IpName = $VMName + "-ip" + $RandomNum $NicName = $VMName + "-ni" + $RandomNum Write-Host "$(Get-Date) * Auto generate network interface $NicName" -ForegroundColor Green $Pip = New-AzureRmPublicIpAddress -Name $IpName -ResourceGroupName $ResourceGroupName -Location $LocationName -AllocationMethod $PublicIPAllocationMethod -ErrorAction Stop if ($Create) { #Vnet does not exist $Subnet = New-AzureRmVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $SubnetPrefix -ErrorAction Stop $Vnet = New-AzureRmVirtualNetwork -Name $VnetName -ResourceGroupName $ResourceGroupName -Location $LocationName -AddressPrefix $VnetPrefix -Subnet $Subnet -ErrorAction Stop $Nic = New-AzureRmNetworkInterface -Name $NicName -ResourceGroupName $ResourceGroupName -Location $LocationName -SubnetId $Vnet.Subnets[0].Id -PublicIpAddressId $Pip.Id -ErrorAction Stop } else { #Vnet exist $Vnet = Get-AzureRmVirtualNetwork -ResourceGroupName $ResourceGroupName -Name $VnetName -ErrorAction stop $subnet = Get-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $Vnet -Name $SubnetName -ErrorAction SilentlyContinue if ($subnet -eq $null) { #subnet does not exist Write-Host "$(Get-Date) * Subnet $SubnetName does not exist,create it,subnet prefix $SubnetPrefix" -ForegroundColor Green $Subnet = New-AzureRmVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $SubnetPrefix -ErrorAction Stop $vnet.subnets += $Subnet #update Vnet Set-AzureRmVirtualNetwork -VirtualNetwork $Vnet -ErrorAction Stop #get vnet again $Vnet = Get-AzureRmVirtualNetwork -ResourceGroupName $ResourceGroupName -Name $VnetName -ErrorAction stop $Subnet = Get-AzureRmVirtualNetworkSubnetConfig -Name $SubnetName -VirtualNetwork $Vnet -ErrorAction stop $Nic = New-AzureRmNetworkInterface -Name $NicName -ResourceGroupName $ResourceGroupName -Location $LocationName -SubnetId $Subnet.Id -PublicIpAddressId $Pip.Id -ErrorAction Stop } else { #subnet exist Write-Host "$(Get-Date) * Subnet $SubnetName exist" -ForegroundColor Green $Nic = New-AzureRmNetworkInterface -Name $NicName -ResourceGroupName $ResourceGroupName -Location $LocationName -SubnetId $Subnet.Id -PublicIpAddressId $Pip.Id -ErrorAction Stop } } return $Nic.Id } Catch { Write-Host -ForegroundColor Red "$(Get-Date) * Auto generate network interface" $_.Exception.Message return $false } } #Login Azure with ARM mode Import-Module AzureRM.Profile $Error.Clear() if (!$DoNotLogin) { if ($AzureMoonCake) { Write-Warning "$(Get-Date) * Current environment is Azure China(Mooncake)" Login-AzureRmAccount -EnvironmentName AzureChinaCloud } else { Write-Warning "$(Get-Date) * Current environment is Azure Global" Login-AzureRmAccount } if ($? -eq $true) { Write-Host "$(Get-Date) * Login succeeded!" -ForegroundColor Green } else { Write-Host $Error[0].Exception.Message -ForegroundColor Red break } } else { $CurrentSubscription = Get-AzureRmSubscription if ($CurrentSubscription -eq $null) { Write-Warning "$(Get-Date) * Didn't find any subscription for now! Please login" break } } try { #check location $Error.clear() if (Check-AzureRmLocation -LocationName $LocationName) { #check RM resource group, if not exist, create one if (Check-AzureRmResourceGroup -ResourceGroupName $ResourceGroupName -LocationName $LocationName) { #Check VM Name If (Get-AzureRmVM -Name $VMName -ResourceGroupName $ResourceGroupName -ErrorAction Ignore) { Write-Host -ForegroundColor Red "$(Get-Date) * VM $VMName has already exist." } else { #Check VM Size Write-Host "$(Get-Date) * Checking VM Size $VMSizeName" -ForegroundColor Green If (Get-AzureRmVMSize -Location $LocationName | Where-Object { $_.Name -eq $VMSizeName }) { Write-Host "$(Get-Date) * VM Size $VMSizeName exist" -ForegroundColor Green If ($VhdUri) { #Create a network interface $Vnet = Get-AzureRmVirtualNetwork -ResourceGroupName $ResourceGroupName -Name $VnetName -ErrorAction SilentlyContinue if ($Vnet -eq $null) { Write-Host "$(Get-Date) * Virtual network $VnetName does not exist,create it,vnet prefix $VnetPrefix" -ForegroundColor Green $Nid = AutoGenerate-AzureRmNetworkInterface -Location $LocationName -ResourceGroupName $ResourceGroupName -VMName $VMName -VnetName $VnetName -VnetPrefix $VnetPrefix -SubnetName $SubnetName -SubnetPrefix $SubnetPrefix -PublicIPAllocationMethod $PublicIPAllocationMethod -Create } else { Write-Host "$(Get-Date) * Virtual network $VnetName exist" -ForegroundColor Green $Nid = AutoGenerate-AzureRmNetworkInterface -Location $LocationName -ResourceGroupName $ResourceGroupName -VMName $VMName -VnetName $VnetName -VnetPrefix $VnetPrefix -SubnetName $SubnetName -SubnetPrefix $SubnetPrefix -PublicIPAllocationMethod $PublicIPAllocationMethod } If ($Nid) { Write-Host "$(Get-Date) * Creating VM $VMName ..." -ForegroundColor Green if ($AvailabilitySetName) { Write-Host "$(Get-Date) * Verify availability set" -ForegroundColor Green $AvailabilitySet = Get-AzureRmAvailabilitySet -ResourceGroupName $ResourceGroupName -Name $AvailabilitySetName -ErrorAction SilentlyContinue if (!$AvailabilitySet) { write-host "$(Get-Date) * AvailabilitySet $AvailabilitySetName does not exist, create a new one" -ForegroundColor Green $AvailabilitySet = New-AzureRmAvailabilitySet -ResourceGroupName $ResourceGroupName -Name $AvailabilitySetName -Location $LocationName -ErrorAction Stop $VM = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSizeName -AvailabilitySetId $AvailabilitySet.Id -ErrorAction Stop } else { $VM = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSizeName -AvailabilitySetId $AvailabilitySet.Id -ErrorAction Stop } } else { $VM = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSizeName -ErrorAction Stop } } #Choose source image #$VM = Set-AzureRmVMSourceImage -VM $VM -PublisherName $PublisherName -Offer $OfferName -Skus $SkusName -Version "latest" -ErrorAction Stop #Add the network interface to the configuration. $VM = Add-AzureRmVMNetworkInterface -VM $VM -Id $Nid -ErrorAction Stop $DiskName = "vmosdisk" #$vmConfig = Set-AzureRmVMOSDisk -VM $vmConfig -Name $osDiskName -VhdUri $destinationVhd -CreateOption Attach -Linux #$VM = Set-AzureRmVMOSDisk -VM $VM -Name $DiskName -VhdUri $OSDiskUri -CreateOption fromImage -Caching $OSDiskCaching -ErrorAction Stop if ($OS -eq "Windows") { $VM = Set-AzureRmVMOSDisk -VM $VM -Name $DiskName -VhdUri $VhdUri -CreateOption attach -Caching None -Windows -ErrorAction Stop } else { $VM = Set-AzureRmVMOSDisk -VM $VM -Name $DiskName -VhdUri $VhdUri -CreateOption attach -Caching None -Linux -ErrorAction Stop } #Create a virtual machine if ($HybridBenefit) { Write-Host "$(Get-Date) * Hybrid Benefit enabled for VM $VMName !" -ForegroundColor Green New-AzureRmVM -ResourceGroupName $ResourceGroupName -Location $LocationName -VM $VM -ErrorAction Stop -LicenseType "Windows_Server" | Out-Null } else { New-AzureRmVM -ResourceGroupName $ResourceGroupName -Location $LocationName -VM $VM -ErrorAction Stop | Out-Null } Write-Host "$(Get-Date) * Create virtual machine $VMName successfully!" -ForegroundColor Green #Set private nic to static <# start-sleep 5 $NicName = $NID.split("/")[-1] $Nic = Get-AzureRmNetworkInterface -Name $NicName -ResourceGroupName $ResourceGroupName $Nic.IpConfigurations[0].PrivateIpAllocationMethod = "Static" Set-AzureRmNetworkInterface -NetworkInterface $Nic | out-null #> } } else { Write-Host -ForegroundColor Red "$(Get-Date) * VM Size $VMSizeName does not exist." } } } } } catch { Write-Host -ForegroundColor Red "$(Get-Date) * Create a virtual machine $VMName failed" $_.Exception.Message }
这个脚本本身偷懒没写太复杂的帮助信息,所以拿到后可能不太好入手到底如何运行,下边举个最基础的例子,在这之前先把脚本的限制说下
本身这个脚本可以支持创建ARM的虚拟机,包括高级存储或者是普通HDD的,但是在创建高级存储的虚拟机之前,请确保你的VHD文件也是放在Preimum Storage Account里,否则创建的时候肯定会报错的。另外这个脚本只支持非托管磁盘,托管磁盘暂时还没搞,准备以后搞出来后再把脚本更新下
VNETNAME这些参数基本就不再介绍了,都很好理解,HybridBenefit要特别说下,因为现在是可以直接使用本地的Windows Server 的license给Azure的虚拟机的(当然,这个是有前提条件的,不多谈),这种情况下创建虚拟机时就需要指定HybridBenefit了,脚本里加了一个HybridBenefit的switch来让用户指定是否创建混合权益的虚拟机
下边举个最简单的创建windows 虚拟机的例子,运行脚本时需要指定下边的参数,因为是中国版所以要加AzureMoonCake,因为之前已经登录过了所以要加-DoNotLogin,否则还会要你再登陆一次,后边的基本就不需要多说了,意思很清楚了
PS C:\Users\mxy> D:\Create-AzureRMVMFromImageV4.ps1 -AzureMoonCake -DoNotLogin -LocationName chinanorth -ResourceGroupName PS_ResourceMG -VMName ttt -VhdUri https://mxymigration.blob.core.chi
nacloudapi.cn/vhds/azuretest.vhd -VnetName mxyansible -SubnetName subnet1-HybridBenefit -VMSizeName Standard_D2 -OS Windows -PublicIPAllocationMethod Static
运行时截图是这样的
要注意的是,使用本地上传的VHD创建时,很有可能脚本是没办法执行完成的,会卡在最后Create VM那里,最后可能会得到这样一个报错
这个其实不用紧张,实际上虚拟机已经创建完成了,但是因为本地的VM默认情况下是不会安装VM Agent的,系统检测不到时就会出现这种问题,虚拟机其实是没问题的,如果想避免这种问题,可以提前将VM Agent在本地安装好, 后边的下载地址 VM Agent
基本上就是这样了,在不同的场景下,可以通过不同的参数来控制虚拟机的属性。