您的位置:首页 > 运维架构 > Shell

如何使用PowerShell从VHD创建虚拟机

2018-02-11 15:40 651 查看
又来分享一篇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

基本上就是这样了,在不同的场景下,可以通过不同的参数来控制虚拟机的属性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  VM 虚拟机 Azure