日本語は機械翻訳による参考訳です。内容に矛盾や不一致があった場合には、英語の内容が優先されます。
『Deploying Microsoft Hyper-V on NetApp Storage: Migration Script』
共同作成者
変更を提案
ここでは、FlexCloneを使用した移行に使用できるPowerShellスクリプトについて説明します。
PowerShellスクリプト
param (
[Parameter(Mandatory=$True, HelpMessage="VCenter DNS name or IP Address")]
[String]$VCENTER,
[Parameter(Mandatory=$True, HelpMessage="NetApp ONTAP NFS Datastore name")]
[String]$DATASTORE,
[Parameter(Mandatory=$True, HelpMessage="VCenter credentials")]
[System.Management.Automation.PSCredential]$VCENTER_CREDS,
[Parameter(Mandatory=$True, HelpMessage="The IP Address of the ONTAP Cluster")]
[String]$ONTAP_CLUSTER,
[Parameter(Mandatory=$True, HelpMessage="NetApp ONTAP VServer/SVM name")]
[String]$VSERVER,
[Parameter(Mandatory=$True, HelpMessage="NetApp ONTAP NSF,SMB Volume name")]
[String]$ONTAP_VOLUME_NAME,
[Parameter(Mandatory=$True, HelpMessage="ONTAP NFS/CIFS Volume mount Drive on Hyper-V host")]
[String]$ONTAP_NETWORK_SHARE_ADDRESS,
[Parameter(Mandatory=$True, HelpMessage="NetApp ONTAP Volume QTree folder name")]
[String]$VHDX_QTREE_NAME,
[Parameter(Mandatory=$True, HelpMessage="The Credential to connect to the ONTAP Cluster")]
[System.Management.Automation.PSCredential]$ONTAP_CREDS,
[Parameter(Mandatory=$True, HelpMessage="Hyper-V VM switch name")]
[String]$HYPERV_VM_SWITCH
)
function main {
ConnectVCenter
ConnectONTAP
GetVMList
GetVMInfo
#PowerOffVMs
CreateOntapVolumeSnapshot
Shift
ConfigureVMsOnHyperV
}
function ConnectVCenter {
Write-Host "------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Connecting to vCenter $VCENTER" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
[string]$vmwareModuleName = "VMware.VimAutomation.Core"
Write-Host "Importing VMware $vmwareModuleName Powershell module"
if ((Get-Module|Select-Object -ExpandProperty Name) -notcontains $vmwareModuleName) {
Try {
Import-Module $vmwareModuleName -ErrorAction Stop
Write-Host "$vmwareModuleName imported successfully" -ForegroundColor Green
} Catch {
Write-Error "Error: $vmwareMdouleName PowerShell module not found"
break;
}
}
else {
Write-Host "$vmwareModuleName Powershell module already imported" -ForegroundColor Green
}
Write-Host "`nConnecting to vCenter $VCENTER"
Try {
$connect = Connect-VIServer -Server $VCENTER -Protocol https -Credential $VCENTER_CREDS -ErrorAction Stop
Write-Host "Connected to vCenter $VCENTER" -ForegroundColor Green
} Catch {
Write-Error "Failed to connect to vCenter $VCENTER. Error : $($_.Exception.Message)"
break;
}
}
function ConnectONTAP {
Write-Host "`n------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Connecting to VSerevr $VSERVER at ONTAP Cluster $ONTAP_CLUSTER" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
[string]$ontapModuleName = "NetApp.ONTAP"
Write-Host "Importing NetApp ONTAP $ontapModuleName Powershell module"
if ((Get-Module|Select-Object -ExpandProperty Name) -notcontains $ontapModuleName) {
Try {
Import-Module $ontapModuleName -ErrorAction Stop
Write-Host "$ontapModuleName imported successfully" -ForegroundColor Green
} Catch {
Write-Error "Error: $vmwareMdouleName PowerShell module not found"
break;
}
}
else {
Write-Host "$ontapModuleName Powershell module already imported" -ForegroundColor Green
}
Write-Host "`nConnecting to ONTAP Cluster $ONTAP_CLUSTER"
Try {
$connect = Connect-NcController -Name $ONTAP_CLUSTER -Credential $ONTAP_CREDS -Vserver $VSERVER
Write-Host "Connected to ONTAP Cluster $ONTAP_CLUSTER" -ForegroundColor Green
} Catch {
Write-Error "Failed to connect to ONTAP Cluster $ONTAP_CLUSTER. Error : $($_.Exception.Message)"
break;
}
}
function GetVMList {
Write-Host "`n------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Fetching powered on VMs list with Datastore $DATASTORE" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
try {
$vmList = VMware.VimAutomation.Core\Get-VM -Datastore $DATASTORE -ErrorAction Stop| Where-Object {$_.PowerState -eq "PoweredOn"} | OUT-GridView -OutputMode Multiple
#$vmList = Get-VM -Datastore $DATASTORE -ErrorAction Stop| Where-Object {$_.PowerState -eq "PoweredOn"}
if($vmList) {
Write-Host "Selected VMs for Shift" -ForegroundColor Green
$vmList | Format-Table -Property Name
$Script:VMList = $vmList
}
else {
Throw "No VMs selected"
}
}
catch {
Write-Error "Failed to get VM List. Error : $($_.Exception.Message)"
Break;
}
}
function GetVMInfo {
Write-Host "------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "VM Information" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------" -ForegroundColor Cyan
$vmObjArray = New-Object System.Collections.ArrayList
if($VMList) {
foreach($vm in $VMList) {
$vmObj = New-Object -TypeName System.Object
$vmObj | Add-Member -MemberType NoteProperty -Name ID -Value $vm.Id
$vmObj | Add-Member -MemberType NoteProperty -Name Name -Value $vm.Name
$vmObj | Add-Member -MemberType NoteProperty -Name NumCpu -Value $vm.NumCpu
$vmObj | Add-Member -MemberType NoteProperty -Name MemoryGB -Value $vm.MemoryGB
$vmObj | Add-Member -MemberType NoteProperty -Name Firmware -Value $vm.ExtensionData.Config.Firmware
$vmDiskInfo = $vm | VMware.VimAutomation.Core\Get-HardDisk
$vmDiskArray = New-Object System.Collections.ArrayList
foreach($disk in $vmDiskInfo) {
$diskObj = New-Object -TypeName System.Object
$diskObj | Add-Member -MemberType NoteProperty -Name Name -Value $disk.Name
$fileName = $disk.Filename
if ($fileName -match '\[(.*?)\]') {
$dataStoreName = $Matches[1]
}
$parts = $fileName -split " "
$pathParts = $parts[1] -split "/"
$folderName = $pathParts[0]
$fileName = $pathParts[1]
$diskObj | Add-Member -MemberType NoteProperty -Name DataStore -Value $dataStoreName
$diskObj | Add-Member -MemberType NoteProperty -Name Folder -Value $folderName
$diskObj | Add-Member -MemberType NoteProperty -Name Filename -Value $fileName
$diskObj | Add-Member -MemberType NoteProperty -Name CapacityGB -Value $disk.CapacityGB
$null = $vmDiskArray.Add($diskObj)
}
$vmObj | Add-Member -MemberType NoteProperty -Name PrimaryHardDisk -Value "[$($vmDiskArray[0].DataStore)] $($vmDiskArray[0].Folder)/$($vmDiskArray[0].Filename)"
$vmObj | Add-Member -MemberType NoteProperty -Name HardDisks -Value $vmDiskArray
$null = $vmObjArray.Add($vmObj)
$vmNetworkArray = New-Object System.Collections.ArrayList
$vm |
ForEach-Object {
$VM = $_
$VM | VMware.VimAutomation.Core\Get-VMGuest | Select-Object -ExpandProperty Nics |
ForEach-Object {
$Nic = $_
foreach ($IP in $Nic.IPAddress)
{
if ($IP.Contains('.'))
{
$networkObj = New-Object -TypeName System.Object
$vlanId = VMware.VimAutomation.Core\Get-VirtualPortGroup | Where-Object {$_.Key -eq $Nic.NetworkName}
$networkObj | Add-Member -MemberType NoteProperty -Name VLanID -Value $vlanId
$networkObj | Add-Member -MemberType NoteProperty -Name IPv4Address -Value $IP
$null = $vmNetworkArray.Add($networkObj)
}
}
}
}
$vmObj | Add-Member -MemberType NoteProperty -Name PrimaryIPv4 -Value $vmNetworkArray[0].IPv4Address
$vmObj | Add-Member -MemberType NoteProperty -Name PrimaryVLanID -Value $vmNetworkArray.VLanID
$vmObj | Add-Member -MemberType NoteProperty -Name Networks -Value $vmNetworkArray
$guest = $vm.Guest
$parts = $guest -split ":"
$afterColon = $parts[1]
$osFullName = $afterColon
$vmObj | Add-Member -MemberType NoteProperty -Name OSFullName -Value $osFullName
$vmObj | Add-Member -MemberType NoteProperty -Name GuestID -Value $vm.GuestId
}
}
$vmObjArray | Format-Table -Property ID, Name, NumCpu, MemoryGB, PrimaryHardDisk, PrimaryIPv4, PrimaryVLanID, GuestID, OSFullName, Firmware
$Script:VMObjList = $vmObjArray
}
function PowerOffVMs {
Write-Host "`n------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Power Off VMs" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
foreach($vm in $VMObjList) {
try {
Write-Host "Powering Off VM $($vm.Name) in vCenter $($VCENTER)"
$null = VMware.VimAutomation.Core\Stop-VM -VM $vm.Name -Confirm:$false -ErrorAction Stop
Write-Host "Powered Off VM $($vm.Name)" -ForegroundColor Green
}
catch {
Write-Error "Failed to Power Off VM $($vm.Name). Error : $._Exception.Message"
Break;
}
Write-Host "`n"
}
}
function CreateOntapVolumeSnapshot {
Write-Host "`n------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Taking ONTAP Snapshot for Volume $ONTAP_VOLUME_NAME" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
Try {
Write-Host "Taking snapshot for Volume $ONTAP_VOLUME_NAME"
$timestamp = Get-Date -Format "yyyy-MM-dd_HHmmss"
$snapshot = New-NcSnapshot -VserverContext $VSERVER -Volume $ONTAP_VOLUME_NAME -Snapshot "snap.script-$timestamp"
if($snapshot) {
Write-Host "Snapshot ""$($snapshot.Name)"" created for Volume $ONTAP_VOLUME_NAME" -ForegroundColor Green
$Script:OntapVolumeSnapshot = $snapshot
}
} Catch {
Write-Error "Failed to create snapshot for Volume $ONTAP_VOLUME_NAME. Error : $_.Exception.Message"
Break;
}
}
function Shift {
Write-Host "------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "VM Shift" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
$Script:HypervVMList = New-Object System.Collections.ArrayList
foreach($vmObj in $VMObjList) {
Write-Host "***********************************************"
Write-Host "Performing VM conversion for $($vmObj.Name)" -ForegroundColor Blue
Write-Host "***********************************************"
$hypervVMObj = New-Object -TypeName System.Object
$directoryName = "/vol/$($ONTAP_VOLUME_NAME)/$($VHDX_QTREE_NAME)/$($vmObj.HardDisks[0].Folder)"
try {
Write-Host "Creating Folder ""$directoryName"" for VM $($vmObj.Name)"
$dir = New-NcDirectory -VserverContext $VSERVER -Path $directoryName -Permission 0777 -Type directory -ErrorAction Stop
if($dir) {
Write-Host "Created folder ""$directoryName"" for VM $($vmObj.Name)`n" -ForegroundColor Green
}
}
catch {
if($_.Exception.Message -eq "[500]: File exists") {
Write-Warning "Folder ""$directoryName"" already exists!`n"
}
Else {
Write-Error "Failed to create folder ""$directoryName"" for VM $($vmObj.Name). Error : $($_.Exception.Message)"
Break;
}
}
$vmDiskArray = New-Object System.Collections.ArrayList
foreach($disk in $vmObj.HardDisks) {
$vmDiskObj = New-Object -TypeName System.Object
try {
Write-Host "`nConverting $($disk.Name)"
Write-Host "--------------------------------"
$vmdkPath = "/vol/$($ONTAP_VOLUME_NAME)/$($disk.Folder)/$($disk.Filename)"
$fileName = $disk.Filename -replace '\.vmdk$', ''
$vhdxPath = "$($directoryName)/$($fileName).vhdx"
Write-Host "Converting ""$($disk.Name)"" VMDK path ""$($vmdkPath)"" to VHDX at Path ""$($vhdxPath)"" for VM $($vmObj.Name)"
$convert = ConvertTo-NcVhdx -SourceVmdk $vmdkPath -DestinationVhdx $vhdxPath -SnapshotName $OntapVolumeSnapshot -ErrorAction Stop -WarningAction SilentlyContinue
if($convert) {
Write-Host "Successfully converted VM ""$($vmObj.Name)"" VMDK path ""$($vmdkPath)"" to VHDX at Path ""$($vhdxPath)""" -ForegroundColor Green
$vmDiskObj | Add-Member -MemberType NoteProperty -Name Name -Value $disk.Name
$vmDiskObj | Add-Member -MemberType NoteProperty -Name VHDXPath -Value $vhdxPath
$null = $vmDiskArray.Add($vmDiskObj)
}
}
catch {
Write-Error "Failed to convert ""$($disk.Name)"" VMDK to VHDX for VM $($vmObj.Name). Error : $($_.Exception.Message)"
Break;
}
}
$hypervVMObj | Add-Member -MemberType NoteProperty -Name Name -Value $vmObj.Name
$hypervVMObj | Add-Member -MemberType NoteProperty -Name HardDisks -Value $vmDiskArray
$hypervVMObj | Add-Member -MemberType NoteProperty -Name MemoryGB -Value $vmObj.MemoryGB
$hypervVMObj | Add-Member -MemberType NoteProperty -Name Firmware -Value $vmObj.Firmware
$hypervVMObj | Add-Member -MemberType NoteProperty -Name GuestID -Value $vmObj.GuestID
$null = $HypervVMList.Add($hypervVMObj)
Write-Host "`n"
}
}
function ConfigureVMsOnHyperV {
Write-Host "------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Configuring VMs on Hyper-V" -ForegroundColor Magenta
Write-Host "------------------------------------------------------------------------------`n" -ForegroundColor Cyan
foreach($vm in $HypervVMList) {
try {
# Define the original path
$originalPath = $vm.HardDisks[0].VHDXPath
# Replace forward slashes with backslashes
$windowsPath = $originalPath -replace "/", "\"
# Replace the initial part of the path with the Windows drive letter
$windowsPath = $windowsPath -replace "^\\vol\\", "\\$($ONTAP_NETWORK_SHARE_ADDRESS)\"
$vmGeneration = if ($vm.Firmware -eq "bios") {1} else {2};
Write-Host "***********************************************"
Write-Host "Creating VM $($vm.Name)" -ForegroundColor Blue
Write-Host "***********************************************"
Write-Host "Creating VM $($vm.Name) with Memory $($vm.MemoryGB)GB, vSwitch $($HYPERV_VM_SWITCH), $($vm.HardDisks[0].Name) ""$($windowsPath)"", Generation $($vmGeneration) on Hyper-V"
$createVM = Hyper-V\New-VM -Name $vm.Name -VHDPath $windowsPath -SwitchName $HYPERV_VM_SWITCH -MemoryStartupBytes (Invoke-Expression "$($vm.MemoryGB)GB") -Generation $vmGeneration -ErrorAction Stop
if($createVM) {
Write-Host "VM $($createVM.Name) created on Hyper-V host`n" -ForegroundColor Green
$index = 0
foreach($vmDisk in $vm.HardDisks) {
$index++
if ($index -eq 1) {
continue
}
Write-Host "`nAttaching $($vmDisk.Name) for VM $($vm.Name)"
Write-Host "---------------------------------------------"
$originalPath = $vmDisk.VHDXPath
# Replace forward slashes with backslashes
$windowsPath = $originalPath -replace "/", "\"
# Replace the initial part of the path with the Windows drive letter
$windowsPath = $windowsPath -replace "^\\vol\\", "\\$($ONTAP_NETWORK_SHARE_ADDRESS)\"
try {
$attachDisk = Hyper-v\Add-VMHardDiskDrive -VMName $vm.Name -Path $windowsPath -ErrorAction Stop
Write-Host "Attached $($vmDisk.Name) ""$($windowsPath)"" to VM $($vm.Name)" -ForegroundColor Green
}
catch {
Write-Error "Failed to attach $($vmDisk.Name) $($windowsPath) to VM $($vm.Name): Error : $($_.Exception.Message)"
Break;
}
}
if($vmGeneration -eq 2 -and $vm.GuestID -like "*rhel*") {
try {
Write-Host "`nDisabling secure boot"
Hyper-V\Set-VMFirmware -VMName $createVM.Name -EnableSecureBoot Off -ErrorAction Stop
Write-Host "Secure boot disabled" -ForegroundColor Green
}
catch {
Write-Error "Failed to disable secure boot for VM $($createVM.Name). Error : $($_.Exception.Message)"
}
}
try {
Write-Host "`nStarting VM $($createVM.Name)"
Hyper-v\Start-VM -Name $createVM.Name -ErrorAction Stop
Write-Host "Started VM $($createVM.Name)`n" -ForegroundColor Green
}
catch {
Write-Error "Failed to start VM $($createVM.Name). Error : $($_.Exception.Message)"
Break;
}
}
}
catch {
Write-Error "Failed to create VM $($vm.Name) on Hyper-V. Error : $($_.Exception.Message)"
Break;
}
}
}
main