欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[Azure]使用Powershell克隆ARM虚拟机(托管磁盘)

程序员文章站 2022-05-10 16:20:54
...

ARM模式下对多磁盘虚拟机捕获映像再批量创建需要对磁盘进行一般化操作,然后使用模板进行创建,整个过程比较复杂,因此为了简化操作,整理了一个用于克隆虚拟机的脚本。

特别针对多网卡虚拟机以及网卡多IP的情况进行了逻辑处理。但是对于诊断等设置没有进行克隆。

注意这个脚本针对托管磁盘虚拟机。


脚本如下:

param(
    [Parameter(Mandatory = $true)] 
    [string]$SubscriptionName, 
 
    [Parameter(Mandatory = $true)]
    [string]$ResourceGroupName,

    [Parameter(Mandatory = $true)]
    [string]$VMName,

    [Parameter(Mandatory = $true)]
    [string]$NewVmName,

    [Parameter(Mandatory = $true)]
    [string]$VmCount
)

Function GetResourceNameFromResourceId($resourceId)
{
    return $resourceId.Substring($resourceId.LastIndexOf('/') + 1);
}

Function GetResourcePropertyFromResourceId($resourceId, $propertyName)
{
    $propertyName = $propertyName + "/";
    $rgName = $resourceId.Substring($resourceId.IndexOf($propertyName) + $propertyName.Length);
    return $rgName.Substring(0, $rgName.IndexOf("/"));
}

Function CollectVMInformation($rgName, $vmName)
{
    $vmInfo = @{};
    $vmInfo.Add("ResourceGroup", $rgName);
    $vmInfo.Add("Name", $vmName);
    
    $vm = Get-AzureRmVM -ResourceGroupName $rgName -Name $vmName -ErrorAction Ignore -WarningAction Ignore;
    if ($vm -eq $null)
    {
        return $null;
    }

    $vmInfo.Add("Size", $vm.HardwareProfile.VmSize);
    $vmInfo.Add("Location", $vm.Location);
    if ($vm.AvailabilitySetReference -ne $null)
    {
        $vmInfo.Add("AvailabilitySet", $vm.AvailabilitySetReference.Id);
    }

    $vmInfo.Add("OSType", $vm.StorageProfile.OsDisk.OsType.ToString());

    #network properties
    $nicId = ($vm.NetworkProfile.NetworkInterfaces | where {$_.Primary -eq $true}).Id;
    if ($nicId -eq $null -and $vm.NetworkProfile.NetworkInterfaces.Count -eq 1)
    {
        $nicId = $vm.NetworkProfile.NetworkInterfaces[0].Id;
    }
    $vmInfo.Add("PrimaryNetworkInterfaceId", $nicId);
    $secondaryNics = @($vm.NetworkProfile.NetworkInterfaces | where {$_.Primary -eq $false});
    $vmInfo.Add("SecondaryNetworkInterfaces", $secondaryNics);

    #disk
    $vmInfo.Add("OSDisk", $vm.StorageProfile.OsDisk);
    $vmInfo.Add("DataDisks", @($vm.StorageProfile.DataDisks));

    Write-Host ("{0, -16}: {1}" -f "Name", $vmInfo["Name"]) -ForegroundColor Cyan;
    Write-Host ("{0, -16}: {1}" -f "Resource Group", $vmInfo["ResourceGroup"]) -ForegroundColor Cyan;
    Write-Host ("{0, -16}: {1}" -f "Size", $vmInfo["Size"]) -ForegroundColor Cyan;
    Write-Host ("{0, -16}: {1}" -f "Location", $vmInfo["Location"]) -ForegroundColor Cyan;
    Write-Host ("{0, -16}: {1}" -f "OS Type", $vmInfo["OSType"]) -ForegroundColor Cyan;
    if ($vmInfo.ContainsKey("AvailabilitySet"))
    {
        Write-Host ("{0, -16}: {1}" -f "Availability Set", $vmInfo["AvailabilitySet"]) -ForegroundColor Cyan;
    }
    Write-Host ("{0, -16}: {1}" -f "Primary NIC", $vmInfo["PrimaryNetworkInterfaceId"]) -ForegroundColor Cyan;
    foreach ($secondaryNic in $vmInfo["SecondaryNetworkInterfaces"])
    {
        Write-Host ("{0, -16}: {1}" -f "Secondary NIC", $secondaryNic.Id) -ForegroundColor Cyan;
    }
    Write-Host ("{0, -16}: {1}" -f "OS Disk", $vmInfo["OSDisk"].Name) -ForegroundColor Cyan;
    foreach ($dataDisk in $vmInfo["DataDisks"])
    {
        Write-Host ("{0, -16}: {1}" -f "Data Disk", $dataDisk.Name) -ForegroundColor Cyan;
    }

    return $vmInfo;
}

Function CopyManagedDisk($managedDiskId, $newDiskName)
{
    $rgName = GetResourcePropertyFromResourceId $managedDiskId "resourceGroups";
    $diskName = GetResourceNameFromResourceId $managedDiskId;
    $disk = Get-AzureRmDisk -ResourceGroupName $rgName -DiskName $diskName;
    $diskConfig = New-AzureRmDiskConfig -SourceResourceId $disk.Id -Location $disk.Location -CreateOption Copy -AccountType $disk.AccountType -OsType $disk.OsType -DiskSizeGB $disk.DiskSizeGB -Tag $disk.Tags;
    $newDisk = New-AzureRmDisk -ResourceGroupName $disk.ResourceGroupName -DiskName $newDiskName -Disk $diskConfig;
    return $newDisk.Id;
}

Function CopyNic($nicId, $newVmName, $nicIndex)
{
    $nicName = GetResourceNameFromResourceId $nicId;
    $rgName = GetResourcePropertyFromResourceId $nicId "resourceGroups";
    $nic = Get-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $rgName;
    $primaryIPCfg = $nic.IpConfigurations | where {$_.Primary -eq $true};
    $subnetId = $primaryIPCfg.Subnet.Id;
    $subnetName = GetResourceNameFromResourceId $subnetId;
    $vnetName = GetResourcePropertyFromResourceId $subnetId "virtualNetworks";
    $newNic = $null;
    $newNicName = ("{0}NIC{1}" -f $newVmName, $nicIndex);
    if ($primaryIPCfg.PublicIpAddress -eq $null)
    {
        $newNic = New-AzureRmNetworkInterface -Name $newNicName -ResourceGroupName $nic.ResourceGroupName -Location $nic.Location -SubnetId $subnetId -WarningAction Ignore;
    }
    else
    {
        $newPrimaryPipName = ("{0}PIP{1}_0" -f $newVmName, $nicIndex);
        $primaryPipId = (CopyPip $primaryIPCfg.PublicIpAddress.Id $newPrimaryPipName);
        $newNic = New-AzureRmNetworkInterface -Name $newNicName -ResourceGroupName $nic.ResourceGroupName -Location $nic.Location -SubnetId $subnetId -PublicIpAddressId $primaryPipId -WarningAction Ignore;
    }
    if ($nic.NetworkSecurityGroup -ne $null)
    {
        $newNic.NetworkSecurityGroup = $nic.NetworkSecurityGroup;
        [void]($newNic | Set-AzureRmNetworkInterface);
    }

    $secondaryIPCfgs = $nic.IpConfigurations | where {$_.Primary -eq $false};
    for ($i = 0; $i -lt $secondaryIPCfgs.Count; $i++)
    {
        $ipCfgName = ("{0}PIP{1}_{2}" -f $newVmName, $nicIndex, ($i+1));
        $ipCfg = $null;
        if ($secondaryIPCfgs[$i-1].PublicIpAddress -eq $null)
        {
            $ipCfg = New-AzureRmNetworkInterfaceIpConfig -Name $secondaryIPCfgs[$i-1].Name -SubnetId $subnetId -WarningAction Ignore;
        }
        else 
        {
            $newPipId = (CopyPip $secondaryIPCfgs[$i-1].PublicIpAddress.Id $ipCfgName);
            $ipCfg = New-AzureRmNetworkInterfaceIpConfig -Name $secondaryIPCfgs[$i-1].Name -SubnetId $subnetId -PublicIpAddressId $newPipId -WarningAction Ignore;
        }
        [void]($newNic.IpConfigurations.Add($ipCfg));
    }

    [void]($newNic | Set-AzureRmNetworkInterface);

    return $newNic.Id;
}

Function CopyPip($pipId, $newPipName)
{
    $rgName = GetResourcePropertyFromResourceId $pipId "resourceGroups";
    $ipName = GetResourceNameFromResourceId $pipId;
    $oldPip = Get-AzureRmPublicIpAddress -Name $ipName -ResourceGroupName $rgName;
    $pip = New-AzureRmPublicIpAddress -Name $newPipName -ResourceGroupName $rgName -Location $oldPip.Location -AllocationMethod $oldPip.PublicIpAllocationMethod -WarningAction Ignore;
    return $pip.Id;
}

Function CreateNewVm($vmInfo, $vmName)
{
    #basic information
    $rgName = $vmInfo["ResourceGroup"];
    $vmSize = $vmInfo["Size"];
    $location = $vmInfo["Location"];
    $osType = $vmInfo["OSType"];

    #network
    $primaryNicId = $vmInfo["PrimaryNetworkInterfaceId"];
    $secondaryNics = $vmInfo["SecondaryNetworkInterfaces"];
    
    #disk
    $osDisk = $vmInfo["OSDisk"];
    $dataDisks = $vmInfo["DataDisks"];

    $vmconfig = $null;
    if ($vmInfo.ContainsKey("AvailabilitySet"))
    {
        $avaSetId = $vmInfo["AvailabilitySet"];
        $vmconfig = New-AzureRmVMConfig -VMName $vmName -VMSize $vmSize -AvailabilitySetId $avaSetId;
    }
    else
    {
        $vmconfig = New-AzureRmVMConfig -VMName $vmName -VMSize $vmSize;
    }
    $vmconfig = $vmconfig | Set-AzureRmVMBootDiagnostics -Disable;

    Write-Host "Cloning disks..." -ForegroundColor Yellow;
    $newOSDiskName = "{0}_OSDisk" -f $vmName;
    $newOSDiskId = (CopyManagedDisk $osDisk.ManagedDisk.Id $newOSDiskName);
    if ($osType -eq "Windows")
    {
        $vmconfig = $vmconfig | Set-AzureRmVMOSDisk -ManagedDiskId $newOSDiskId -Caching $osDisk.Caching -CreateOption attach -Windows;
    } else {
        $vmconfig = $vmconfig | Set-AzureRmVMOSDisk -ManagedDiskId $newOSDiskId -Caching $osDisk.Caching -CreateOption attach -Linux;
    }
    #下面这个步骤是必须要有而且非常重要的,实测下来发现上面的命令有个小bug,就是托管磁盘添加后,磁盘名默认会被设置成虚拟机名称
    #导致创建的时候报错“New-AzureRmVM : Changing property 'osDisk.name' is not allowed”
    $vmconfig.StorageProfile.OsDisk.Name = $newOSDiskName;
    $vmconfig.StorageProfile.ImageReference = $null;

    $index = 0;
    foreach ($dataDisk in $dataDisks)
    {
        $newDataDiskName = "{0}_DataDisk_{1}" -f $vmName, $index;
        $newDataDiskId = (CopyManagedDisk $dataDisk.ManagedDisk.Id $newDataDiskName);
        $vmconfig = $vmconfig | Add-AzureRmVMDataDisk -ManagedDiskId $newDataDiskId -Lun $dataDisk.Lun -Caching $dataDisk.Caching -CreateOption attach;
        #下面这个步骤是必须要有而且非常重要的,实测下来发现上面的命令有个小bug,就是托管磁盘添加后,磁盘名默认会被设置成虚拟机名称
        #导致创建的时候报错“New-AzureRmVM : Changing property 'dataDisk.name' is not allowed”
        $vmconfig.StorageProfile.DataDisks[$index].Name = $newDataDiskName;
        $index += 1;
    }

    Write-Host "Cloning network interfaces..." -ForegroundColor Yellow;
    $newPrimaryNicId = (CopyNic $primaryNicId $vmName 0);
    $vmconfig = $vmconfig | Add-AzureRmVMNetworkInterface -Id $newPrimaryNicId -Primary;
    $secondaryNicCount = $secondaryNics.Count;
    for ($i = 0; $i -lt $secondaryNicCount; $i++)
    {
        $secondaryNicId = $secondaryNics[$i].Id;
        $newSecondaryNicId = (CopyNic $secondaryNicId $vmName ($i+1));
        $vmconfig = $vmconfig | Add-AzureRmVMNetworkInterface -Id $newSecondaryNicId;
    }

    Write-Host "Creating new virtual machine..." -ForegroundColor Yellow;
    [void](New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $vmconfig);
}

[void](Select-AzureRmSubscription -SubscriptionName $SubscriptionName);

Write-Host "Virtual Machine information:" -ForegroundColor Green;
$vmInfo = (CollectVMInformation $ResourceGroupName $vmName);
if ($vmInfo -eq $null)
{
    Write-Host "Failed to collect vm information." -ForegroundColor Red;
    return;
}

for ($i = 0; $i -lt $VmCount; $i++)
{
    $newName = $NewVmName + ($i+1);
    Write-Host ("Cloning virtual machine {0}..." -f $newName) -ForegroundColor Yellow;
    CreateNewVm $vmInfo $newName;
    Write-Host ("Finished cloning virtual machine {0}." -f $newName) -ForegroundColor Green;
}

Write-Host "Finished" -ForegroundColor Green;


运行结果:

[Azure]使用Powershell克隆ARM虚拟机(托管磁盘)