This code is outdated. Refactor before reuse..
###############################################################################################################
# HyperV-MoveVM.ps1
# Assumption: Hyper-V has been setup with Microsoft Failover Cluster
###############################################################################################################
# Set variables
$windowsName="CONCU.intranet.kimconnect.com"
$sourceVmName="CONCU";
$interimDestination="C:\ClusterStorage\Volume1\VHD";
$finalDestination="C:\ClusterStorage\Volume1\VHD";
$targetClusterName="HYPERV-CLUSTER01"
$resizeDiskIndex=0;
$resizeVolumeLetter="C";
$resizeTo="160GB";
$backupSourceFolder="C:\ClusterStorage\Volume2\VHD\CONCU"
$backupDestinationFolder="C:\ClusterStorage\Volume2\VHD\CONCU-BACKUP"
# Function to Create Template
# Need to add feature to check whether target cluster node has access to the $destinationFolder
function exportVM{
param(
$sourceVmName="Windows 2016 Template",
$destinationFolder="C:\ClusterStorage\Volume1\VHD"
)
# Remove all snapshots prior to performing disks expansion
try {
Get-VMSnapshot -VMName $sourceVmName | %{Remove-VMSnapshot -VMName $sourceVmName -Name $_.Name}
}
catch{
write-host "$Error`r`nUnable to remove all snapshots. Aborting Remove-VMSnapshot commands."
break;
}
# Gather source volumes
$sourceVolumes=(get-vm $sourceVmName | select -expand HardDrives).Path
# Calculate volume sizes
$totalSize=0;
$sourceVolumes|%{$totalSize+=(get-item $_).Length}
$totalSizeInGB=$totalSize/1GB
# Perform export
try{
$exportDuration=Measure-Command {Export-VM -Name $sourceVmName -Path $destinationFolder}
$exportHours=[math]::round($exportDuration.TotalHours,2);
$gbPerHour=[math]::round($totalSizeInGB/$exportHours,2);
write-host "$sourceVmName has been exported to $destinationFolder at the speed of $gbPerHour GB/Hour ($totalSizeInGB GB in $exportHours hours)";
return $true
}catch{
return $false
}
}
# Helper functions
function addVmToCluster{
param($vmName="Windows 2016 Template")
if(get-cluster -ea SilentlyContinue){Add-ClusterVirtualMachineRole -VirtualMachine $vmName}else{write-host "No clusters found."}
}
function removeVmFromCluster{
param($vmName="Windows 2016 Template")
$clusterName=(get-cluster -ea SilentlyContinue).Name
if($clusterName){
try{
Remove-ClusterGroup -VMId (Get-VM -Name $vmName).VMId -RemoveResources -Force;
write-host "$vmName has been removed from the cluster $clusterName";
}catch{
write-host $Error
}
}else{write-host "No clusters found."}
}
function moveItemToRecycleBin{
param($item="C:\Temp\helloworld.py")
Add-Type -AssemblyName Microsoft.VisualBasic
$itemType=(get-item $item).Attributes
switch ($itemType){
"Directory" {[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory("$item",'OnlyErrorDialogs','SendToRecycleBin')}
"Archive" {[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile("$item",'OnlyErrorDialogs','SendToRecycleBin')}
}
<#
$shell = new-object -comobject "Shell.Application"
$item = $shell.Namespace(0).ParseName("$path")
$item.InvokeVerb("delete")
#>
}
# Function to gracefully shutdown remote computer
function gracefulShutdown{
param(
$windowsName,
$vmName
)
# Enable VMIntegration for Shutdown Commands
function ensureVMIntegrationForShutdown{
param($vmName)
# Ensure that the VM Integration Service for Shutdown method was enabled
$vmIntegrationShutdownEnabled=(Get-VMIntegrationService -VMName $vmName | ?{$_.Name -eq "Shutdown"}).Enabled
if (!($vmIntegrationShutdownEnabled)){Enable-VMIntegrationService -VMName $vmName -Name "Shutdown"}
}
# Ping computer, retry until wait-time expires
function pingHostUntilDown{
param($computername="localhost",$waitTime=300)
$t=0;
write-host "Pinging $ComputerName"
do {
$hostIsPingable=if(Test-Connection $ComputerName -Count 1 -Delay 1 -ErrorAction SilentlyContinue){$true}else{$false}
if($hostIsPingable){
write-host -nonewline ".";
$t+=5;
sleep 5;
}
}while ($hostIsPingable -or $t -gt $waitTime)
if($hostIsPingable){return $true}else{return $false}
}
function pingTest{
Param([string]$node)
try{
Return Test-Connection $node -Count 1 -Quiet -ea Stop;
}
catch{Return $False}
}
function enableRemoteWinRM{
Param([string]$computername)
Write-Host "checking $computername..."
if (pingTest $computername){
if (!(Test-WSMan $computername -ea SilentlyContinue)){
if(!(get-command psexec)){
if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
}
choco install sysinternals -y
}
psexec.exe \\$computername -s C:\Windows\system32\winrm.cmd qc -quiet
}else{Write-Host "WinRM has been already enabled. No changes to WinRM have been made."}
}
Else{Write-Host "Unable to determine if WinRM is enabled on $computername`.`n Ping test has failed. Check if this computer is online and whether there's a firewall blocking of ICMP";}
}
# function to connect to remote machine and execute commands
function connectWinRmToShutdown{
param($remoteComputer)
do{
$session = New-PSSession -ComputerName $RemoteComputer
"Connecting to remote computer $computer..."
sleep -seconds 5
if ($session){"Connected."}
} until ($session.state -match "Opened")
invoke-command -session $session -scriptblock{
# Gracefully stop SQL if it is present
Get-service *SQL* | Where-Object {$_.status -eq "Running"} | Stop-Service -Force
#force in this context will enable the process to shutdown dependent services prior to stopping the target service
# Gracefully stop Exchange if it is present
$exchangeServices=Get-Service | Where-Object {$_.DisplayName –like "Microsoft Exchange *"};
if($exchangeServices){
net stop msexchangeadtopology /y;
net stop msexchangefba /y;
net stop msftesql-exchange /y;
net stop msexchangeis /y;
net stop msexchangesa /y;
net stop iisadmin /y;
net stop w3svc /y;
$exchangeServices | Stop-Service -Force;
}
# Finally shutdown the OS via Windows native method
Stop-Computer $env:computername -Force
}
$pingResult=pingHostUntilDown -computername $RemoteComputer -waitTime 300
if ($pingResult){write-host "$RemoteComputer is still online.";break}else{write-host "$RemoteComputer is now offline."}
}
# Attempt to connect to remote machine via WinRM
try{
ensureVMIntegrationForShutdown -vmName $vmName
if (pingTest $windowsName){
enableRemoteWinRM -computername $windowsName
connectWinRmToShutdown -remoteComputer $windowsName
if((get-vm $vmName).Status -eq "Running"){stop-vm -Name $vmName -Force -ErrorAction SilentlyContinue;} #shutdown VM in case it is still online; #shutdown VM in case it is still online
}else{
write-host "$windowsName is currently unpingable. Considering it as already shut down.";
if((get-vm $vmName).Status -eq "Running"){stop-vm -Name $vmName -Force -ErrorAction SilentlyContinue;}
}
}catch{
write-host $Error;
break;
}
}
# Function to Clone from Template
# Future feature not currently available: Import to new destination with a new VM name and GUID
# importVM -vmName $sourceVmName -sourceDirectory $interimDestination -destinationDirectory $finalDestination;
function importVm{
param(
$vmName="Windows 2016 Template",
$sourceDirectory="C:\ClusterStorage\Volume1\VHD",
$destinationDirectory="C:\ClusterStorage\Volume1\VHD"
)
# Sanity checks
# Verify whether source VM already exists in Hyper-V
$vmExists=get-vm $vmName -ErrorAction SilentlyContinue
if ($vmExists){
write-host "Cannot import $vmName because it already exists.";
break;
}
# Verify whether Interim destination and Final Destination are the same, and whether the destination node has access to Final Destination
$sameSourceAndDestination=$sourceDirectory -eq $destinationDirectory
# Gather source volumes
$sourceVolumes=(get-item "$sourceDirectory\$vmName\Virtual Hard Disks\*.vhdx").FullName
# Calculate volume sizes
$totalSize=0;
$sourceVolumes|%{$totalSize+=(get-item $_).Length}
$totalSizeInGB=$totalSize/1GB
# Use this import sequence when Source Directory and Destination Directory are the same
$vmcxFile=(get-item "$sourceDirectory\$vmName\Virtual Machines\*.vmcx").FullName
if (!($vmcxFile)){
write-host "Cannot import $vmName because a VMCX file does not exist at '$destinationFolder\$vmName\Virtual Machines\'";
break;
}else{
$importDuration=Measure-Command {
if($sameSourceAndDestination){
# Use this import sequence when Source Directory and Destination Directory are the same
Import-VM -Path "$vmcxFile";
}else{
# Use this import sequence when Source Directory and Destination Directory are different
Import-VM -Path "$vmcxFile" -Copy -GenerateNewId;
}
addVMToCluster -vmName $vmName;
}
$importHours=[math]::round($importDuration.TotalHours,2)
$gbPerHour=[math]::round($totalSizeInGB/$importHours,2)
write-host "$vmName has been imported to $destinationDirectory\$vmName at the speed of $gbPerHour GB/Hour ($totalSizeInGB GB in $importHours hours)"
if(!($sameSourceAndDestination)){write-host "`r`nRecommendation: clean up $sourceDirectory\$vmName after validation"}
}
}
# Function to unregister guest VM from Cluster and Hyper-V
function deregisterVm{
param([string]$vmName="Windows 2016 Template")
try{
$vmState=(get-vm $vmName).State
if($vmState -eq "Running"){gracefulShutdown -RemoteComputer $vmName}
$vmStopped=(get-vm $vmName).State -eq "Off"
if($vmStopped){
removeVmFromCluster -vmName $vmName;
Remove-VM -Name $vmName -Force;
write-host "$vmName has been removed from Hyper-V."
}
}catch{
write-host $Error
write-host "Unable to deregister VM $vmName."
}
}
function getVmDisks{
param($vmName)
#write-host "Gathering disks information...";
$disks=Get-VM $vmName | Select-Object VMId | Get-VHD
return $disks
}
# Expand the targeted disk
# resizeVmDisk -vmName $vmName -diskIndex $diskIndex -newSizeGb $newSize;
function resizeVmDisk{
param($vmName,$diskIndex,$newSizeGb)
write-host "Removing all snapshots prior to performing disks expansion..."
try {
Get-VMSnapshot -VMName $vmName | %{Remove-VMSnapshot -VMName $vmName -Name $_.Name}
}
catch{
write-host "unable to remove all snapshots. Aborting Remove-VMSnapshot commands."
break;
}
write-host "Gathering disks information...";
$disks=getVmDisks -vmName $vmName
$before= $($disks | select VhdType,Path,@{label='currentSizeGb';expression={$_.FileSize/1gb -as [int]}},`
@{label='minimumSizeGbAllowed';expression={$_.MinimumSize/1gb -as [int]}},`
@{label='maxSizeGb';expression={$_.Size/1gb -as [int]}}`
| ft -AutoSize | Out-String)
write-host "Before:`r`n$before"
$diskX=$disks | Select -Index $diskIndex # VhdType: Fixed, Dynamic, Differencing
$diskMinimumAllowed=$diskX.MinimumSize;
$diskCurrentSize=$diskX.FileSize;
$diskMaxSize=$diskX.Size;
$vmVolumeFile=$diskX.Path
#$vmVolume=get-vm $vmName | select -expand HardDrives | select -Index $diskIndex
#$vmVolumeFile=$vmVolume.Path
# if $newSize is not specified, set disk value to double its original size
if(!($newSizeGb)){
$newSizeBytes=$diskMaxSize*2
}else{
$newSizeBytes=$newSizeGb/1;
}
if ($newSizeGb/1 -ge $diskMinimumAllowed){
try{
Resize-VHD -Path "$vmVolumeFile" -SizeBytes ($newSizeGb/1);
$disksAfter=getVmDisks -vmName $vmName
$after= $($disksAfter | select VhdType,Path,@{label='currentSizeGb';expression={$_.FileSize/1gb -as [int]}},`
@{label='minimumSizeGbAllowed';expression={$_.MinimumSize/1gb -as [int]}},`
@{label='maxSizeGb';expression={$_.Size/1gb -as [int]}}`
| ft -AutoSize | Out-String)
write-host "After:`r`n$after"
write-host "$vmVolumeFile has been resized to $newSizeGb successfully."
}catch{
write-host "Cannot resize $vmVolumeFile.";
}
}else{
write-host "$newSizeGb GB is less than the minimum allowed of $($diskMinimumAllowed/1GB). Cannot resize this disk.";
}
}
# Connect to Windows OS of Guest-VM & Resize volume to its available maximum
function expandWindowsVmVolume{
param($windowsName,$vmName,$driveLetter="C")
if((get-vm -name $vmName).State -ne "Running"){Start-VM -Name $vmName}
$localComputerName=$env:computername
$localIps=([System.Net.Dns]::GetHostAddresses($env:computername)|?{$_.AddressFamily -eq "InterNetwork"}|%{$_.IPAddressToString;}|out-string).Trim();
$isLocal=if($windowsName -match "(localhost|127.0.0.1|$localComputerName)" -or $localIps -match $windowsName){$true}else{$false}
# Ping computer, retry until wait-time expires
function pingHostUntilUp{
param($computername="localhost",$waitTime=300)
$t=0;
write-host "Pinging $ComputerName"
do {
$hostIsPingable=if(Test-Connection $ComputerName -Count 1 -Delay 1 -ErrorAction SilentlyContinue){$true}else{$false}
if(!($hostIsPingable)){
write-host -nonewline ".";
$t+=5;
sleep 5;
}
}while (!($hostIsPingable) -and $t -le $waitTime)
return $hostIsPingable
}
function expandVolumeMax{
param($driveLetter)
Update-HostStorageCache
# Before
$before = (gwmi -Class win32_volume -Filter "DriveType!=5" -ea stop| ?{$_.DriveLetter -eq "$driveLetter`:"}|`
Select-object @{Name="Letter";Expression={$_.DriveLetter}},`
@{Name="Label";Expression={$_.Label}},`
@{Name="Capacity";Expression={"{0:N2} GiB" -f ($_.Capacity/1073741824)}},`
@{Name = "Available"; Expression = {"{0:N2} GiB" -f ($_.FreeSpace/1073741824)}},`
@{Name = "Utilization"; Expression = {"{0:N2} %" -f ((($_.Capacity-$_.FreeSpace) / $_.Capacity)*100)}}`
| ft -autosize | Out-String).Trim()
write-host "Before:`r`n$before"
$max=(Get-PartitionSupportedSize -DriveLetter $driveLetter).SizeMax;
$maxGb=[math]::round($max/1GB,4);
try{
Resize-Partition -DriveLetter $driveLetter -Size $max;
Update-HostStorageCache;
$after = (gwmi -Class win32_volume -Filter "DriveType!=5" -ea stop| ?{$_.DriveLetter -eq "$driveLetter`:"}|`
Select-object @{Name="Letter";Expression={$_.DriveLetter}},`
@{Name="Label";Expression={$_.Label}},`
@{Name="Capacity";Expression={"{0:N2} GiB" -f ($_.Capacity/1073741824)}},`
@{Name = "Available"; Expression = {"{0:N2} GiB" -f ($_.FreeSpace/1073741824)}},`
@{Name = "Utilization"; Expression = {"{0:N2} %" -f ((($_.Capacity-$_.FreeSpace) / $_.Capacity)*100)}}`
| ft -autosize | Out-String).Trim()
write-host "After:`r`n$after"
Write-Host "`r`n$driveLetter has been resized to $maxGb GB successfully.";
}catch{
write-host $Error;
Write-Host "`r`n$driveLetter has NOT been resized due to an error.";
break;
}
}
function expandVolume{
param($computerName,$isLocal)
if ($isLocal){
expandVolumeMax -driveLetter $driveLetter;
}else{
invoke-command -computername $computerName -ScriptBlock{
param($expandVolumeMax,$driveLetter)
[ScriptBlock]::Create($expandVolumeMax).Invoke($driveLetter);
}-Args ${function:expandVolumeMax},$driveLetter
}
}
if(pingHostUntilUp -computername $windowsName){
expandVolume -computerName $windowsName -isLocal $isLocal
}else{
Start-VM -Name $vmName
$hostOnline=pingHostUntilUp -computername $windowsName
if($hostOnline){
expandVolume -computerName $windowsName -isLocal $isLocal
}else{
write-host "Cannot bring $windowsName online to perform disk expansion.";
}
}
}
function moveVM{
param(
[string]$windowsName="ISI-WSUS",
[string]$sourceVmName="Windows 2016 Template",
[string]$interimDestination="C:\ClusterStorage\Volume1\VHD",
[string]$finalDestination="C:\ClusterStorage\Volume1\VHD"
)
$timer=[System.Diagnostics.Stopwatch]::StartNew();
#gracefulShutdown -windowsName $windowsName -vmName $sourceVmName;
$exportSuccess=exportVM -sourceVmName $sourceVmName -destinationFolder $interimDestination;
if($exportSuccess){
deregisterVM -vmName $sourceVmName
importVM -vmName $sourceVmName -sourceDirectory $interimDestination -destinationDirectory $finalDestination;
$totalSeconds=$timer.Elapsed.TotalSeconds;
$hours=[math]::round($totalSeconds/3600,2);
write-host "The moveVM function has completed in $hours hours.";
write-host "Once validation succeeds, manually delete this folder $interimDestination\$sourceVmName";
$timer.stop();
}else{
write-host "The exportVM function has failed. Aborting moveVM function.";
break;
}
}
function expandVmDiskAndVolume{
param(
[string]$windowsName="Windows2016Template",
[string]$vmName="Windows 2016 Template",
[int]$diskIndex=0,
[string]$driveLetter="C",
[string]$newSize="160GB"
)
resizeVmDisk -vmName $vmName -diskIndex $diskIndex -newSizeGb $newSize;
expandWindowsVmVolume -windowsName $windowsName -vmName $vmName -driveLetter $driveLetter;
}
function copyOnlyFiles{
param(
[Parameter(Mandatory=$true)][string]$sourceFolder,
[Parameter(Mandatory=$true)][string]$destinationFolder
)
$totalSizeBytes=0;
# Get-ChildItem "C:\Temp"|?{!$_.PSIsContainer}|%{robocopy "C:\Temp" "C:\TempCopy" $_}
# Get-ChildItem $sourceFolder|?{!$_.PSIsContainer}|%{robocopy $sourceFolder $destinationFolder $_}
# New-Item -ItemType Directory -Force -Path $destinationFolder | Out-Null
#$duration=measure-command {Get-ChildItem $sourceFolder|?{!$_.PSIsContainer}|Copy-Item -Destination $destinationFolder}
$duration=measure-command {
$filesOnly=Get-ChildItem $sourceFolder|?{!$_.PSIsContainer}
$filesOnly|%{
$totalSizeBytes+=$_.Length;
write-host $_;
robocopy $sourceFolder $destinationFolder $_;
robocopy $sourceFolder $destinationFolder $_ | %{$data = $_.Split([char]9); if("$($data[4])" -ne "") { $file = "$($data[4])"} ;Write-Progress "Percentage $($data[0])" -Activity "Robocopy" -CurrentOperation "$($file)" -ErrorAction SilentlyContinue; }
}
}
$totalsizeGb=$totalSizeBytes/1GB
$totalMinutes=[math]::round($duration.TotalMinutes,2)
$gbPerHour=[math]::round($totalsizeGb/(($duration.TotalMinutes)/60),2)
write-host "-------------------------------------------------`r`n$([math]::round($totalsizeGb,2)) GB completed in $totalMinutes minutes. Speed was $gbPerHour GB/Hour."
}
function resizeVhdxChain{
param(
[Parameter(Mandatory=$true)][String]$vmName,
[Parameter(Mandatory=$false)][int]$diskIndex=0,
[Parameter(Mandatory=$false)][String]$newSizeGb="160GB"
)
function resizeVhdx{
param(
[string]$vhdxFile,
$newSizeGb
)
# Validation
if($newsizegb.gettype().Name -eq "String"){
$sizeBytes=$newSizeGb/1
}else{
write-host "There appears to be an error with the input size of $newSizeGb"
break;
}
try{
Resize-VHD -Path "$vhdxFile" -SizeBytes $sizeBytes;
}catch{
write-host $Error
}
}
$disks=getVmDisks -vmName $vmName
$diskX=$disks | Select -Index $diskIndex
$file=$diskX.Path
$parent=(Get-VHD -Path '$file' -ea SilentlyContinue).ParentPath
if ($parent){
$vhdxFiles=while($file = (Get-VHD -Path '$file').ParentPath){$file}
}else{
$vhdxFiles=$file
}
if($vhdxFiles.Count -gt 1){
$statements=$vhdxFiles|%{
"resizeVhdx -vhdxFile '$_' -newSizeGb '$newSizeGb'`r`n";
}
#$statements+="Merge-VHD -Path '$($vhdxFiles[0])' -DestinationPath '$($vhdxFiles[$vhdxFiles.length-1])'"
write-host "Statements to confirm`r`n$statements"
pause;
$vhdxFiles|%{resizeVhdx -vhdxFile '$_' -newSizeGb '$newSizeGb'}
#Merge-VHD -Path '$($vhdxFiles[0])' -DestinationPath '$($vhdxFiles[$vhdxFiles.length-1])'
}else{
write-host "There are not parent disks to resize."
}
}
function mergeVhdxChain{
param(
[Parameter(Mandatory=$true)][String]$vmName,
[Parameter(Mandatory=$false)][int]$diskIndex=0
)
$disks=getVmDisks -vmName $vmName
$diskX=$disks | Select -Index $diskIndex
$file=$diskX.Path
$parent=(Get-VHD -Path '$file' -ea SilentlyContinue).ParentPath
if ($parent){
$vhdxFiles=while($file = (Get-VHD -Path $file).ParentPath){$file}
}else{
$vhdxFiles=$file
}
if($vhdxFiles.Count -gt 1){
Merge-VHD -Path "$($vhdxFiles[0])" -DestinationPath "$($vhdxFiles[$vhdxFiles.length-1])"
}else{
write-host "There are not parent disks to merge."
}
}
write-host "Commands loaded - copy and paste these lines when ready"
write-host "gracefulShutdown -windowsName `$windowsName -vmName `$sourceVmName"
write-host "copyOnlyFiles -sourceFolder `$backupSourceFolder -destinationFolder `$backupDestinationFolder"
write-host "moveVM -windowsName `$windowsName -sourceVmName `$sourceVmName -interimDestination `$interimDestination -finalDestination `$finalDestination"
write-host "getVmDisks -vmName `$sourceVmName"
write-host "resizeVhdxChain -vmName `$sourceVmName -diskIndex `$resizeDiskIndex -newSizeGb `$resizeTo"
write-host "expandVmDiskAndVolume -windowsName `$windowsName -vmName `$sourceVmName -diskIndex `$resizeDiskIndex -driveLetter `$resizeVolumeLetter -newSize `$resizeTo"
<# Sample Output:
PS C:\Windows\system32> migrateVM -sourceVmName "CONCU" -destinationDirectory "C:\ClusterStorage\Volume2\VHD"
checking CONCU...
WinRM has been already enabled. No changes to WinRM have been made.
Connecting to remote computer ...
Connected.
WARNING: Waiting for service 'Sage Fixed Assets Service (SQLANYs_Sage_FAS_Fixed_Assets)' to stop...
WARNING: Waiting for service 'Sage Fixed Assets Service (SQLANYs_Sage_FAS_Fixed_Assets)' to stop...
Pinging CONCU
...CONCU is now offline.
CONCU has been exported to C:\ClusterStorage\Volume2\VHD at the speed of 254.61 GB/Hour (38.19140625 GB in 0.15 hours)
CONCU has been removed from the cluster.
CONCU has been imported to C:\ClusterStorage\Volume2\VHD\CONCU at the speed of 3819.14 GB/Hour (38.19140625 GB in 0.01 hours)
PS C:\Windows\system32> expandVmDiskAndVolume -vmName "CONCU" -diskIndex 0 -driveLetter "C" -newSize "250GB"
Pinging CONCU
.Before:
Letter Label Capacity Available Utilization
------ ----- -------- --------- -----------
C: 159.45 GiB 42.14 GiB 73.57 %
After:
Letter Label Capacity Available Utilization
------ ----- -------- --------- -----------
C: 249.45 GiB 132.14 GiB 47.03 %
C has been resized to 249.4472 GB successfully.
#>
<#
Sample VM Migation Plan (time window = 3 hours):
Pre-emptively resolve disks merging errors prior to migrating VM, then expand C:\ volume after migration
1. Gracefully shutdown VM (5 minutes)
2. Make a file-level copy backup of VM (5 to 60 minutes)
3. Export to Interim Storage and Import VM at New Storage Destination (5 to 60 minutes minutes)
4. Resize C:\ volume (5 minutes)
5. Perform Validation (20 minutes)
a. Success - no further actions
b. Failure - unregister VM > re-register VM from backup > re-validate (20 minutes)
#>
Categories: