Files
Packer-Cn/common/powershell/hyperv/hyperv.go
T
DanHam c2437ba592 Remove the option to place the VHD files in a separate directory
The export process now exports the VM directly from the build directory
into the output directory. There are no intermediate steps or copying of
files involved. This means that there is no longer any benefit in having
a separate directory to house the VHD files - see #5206 for the
reasoning behind the introduction of this feature.

If a user wishes to house the build files on a separate disk from the
output directory (perhaps for performance reasons or due to disk space
limitations) they can still do so through the use of `temp_path`.
2018-07-20 02:12:43 +01:00

1339 lines
42 KiB
Go

package hyperv
import (
"context"
"errors"
"os/exec"
"strconv"
"strings"
"github.com/hashicorp/packer/common/powershell"
)
func GetHostAdapterIpAddressForSwitch(switchName string) (string, error) {
var script = `
param([string]$switchName, [int]$addressIndex)
$HostVMAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $switchName
if ($HostVMAdapter){
$HostNetAdapter = Get-NetAdapter | ?{ $_.DeviceID -eq $HostVMAdapter.DeviceId }
if ($HostNetAdapter){
$HostNetAdapterConfiguration = @(get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = 'TRUE' AND InterfaceIndex=$($HostNetAdapter.ifIndex)")
if ($HostNetAdapterConfiguration){
return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
}
}
}
return $false
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, switchName, "0")
return cmdOut, err
}
func GetVirtualMachineNetworkAdapterAddress(vmName string) (string, error) {
var script = `
param([string]$vmName, [int]$addressIndex)
try {
$adapter = Hyper-V\Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
if ($adapter.IPAddresses) {
$ip = $adapter.IPAddresses[$addressIndex]
} else {
$vm = Get-CimInstance -ClassName Msvm_ComputerSystem -Namespace root\virtualization\v2 -Filter "ElementName='$vmName'"
$ip_details = (Get-CimAssociatedInstance -InputObject $vm -ResultClassName Msvm_KvpExchangeComponent).GuestIntrinsicExchangeItems | %{ [xml]$_ } | ?{ $_.SelectSingleNode("/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text()='NetworkAddressIPv4']") }
if ($null -eq $ip_details) {
return $false
}
$ip_addresses = $ip_details.SelectSingleNode("/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()").Value
$ip = ($ip_addresses -split ";")[0]
}
} catch {
return $false
}
$ip
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName, "0")
return cmdOut, err
}
func CreateDvdDrive(vmName string, isoPath string, generation uint) (uint, uint, error) {
var ps powershell.PowerShellCmd
var script string
script = `
param([string]$vmName, [string]$isoPath)
$dvdController = Hyper-V\Add-VMDvdDrive -VMName $vmName -path $isoPath -Passthru
$dvdController | Hyper-V\Set-VMDvdDrive -path $null
$result = "$($dvdController.ControllerNumber),$($dvdController.ControllerLocation)"
$result
`
cmdOut, err := ps.Output(script, vmName, isoPath)
if err != nil {
return 0, 0, err
}
cmdOutArray := strings.Split(cmdOut, ",")
if len(cmdOutArray) != 2 {
return 0, 0, errors.New("Did not return controller number and controller location")
}
controllerNumberTemp, err := strconv.ParseUint(strings.TrimSpace(cmdOutArray[0]), 10, 64)
if err != nil {
return 0, 0, err
}
controllerNumber := uint(controllerNumberTemp)
controllerLocationTemp, err := strconv.ParseUint(strings.TrimSpace(cmdOutArray[1]), 10, 64)
if err != nil {
return controllerNumber, 0, err
}
controllerLocation := uint(controllerLocationTemp)
return controllerNumber, controllerLocation, err
}
func MountDvdDrive(vmName string, path string, controllerNumber uint, controllerLocation uint) error {
var script = `
param([string]$vmName,[string]$path,[string]$controllerNumber,[string]$controllerLocation)
$vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
Hyper-V\Set-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation -Path $path
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, path, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
return err
}
func UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
var script = `
param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
$vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
Hyper-V\Set-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation -Path $null
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
return err
}
func SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint, generation uint) error {
if generation < 2 {
script := `
param([string]$vmName)
Hyper-V\Set-VMBios -VMName $vmName -StartupOrder @("CD", "IDE","LegacyNetworkAdapter","Floppy")
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
} else {
script := `
param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
$vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
Hyper-V\Set-VMFirmware -VMName $vmName -FirstBootDevice $vmDvdDrive -ErrorAction SilentlyContinue
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
return err
}
}
func DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
var script = `
param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
$vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
Hyper-V\Remove-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
return err
}
func DeleteAllDvdDrives(vmName string) error {
var script = `
param([string]$vmName)
Hyper-V\Get-VMDvdDrive -VMName $vmName | Hyper-V\Remove-VMDvdDrive
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func MountFloppyDrive(vmName string, path string) error {
var script = `
param([string]$vmName, [string]$path)
Hyper-V\Set-VMFloppyDiskDrive -VMName $vmName -Path $path
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, path)
return err
}
func UnmountFloppyDrive(vmName string) error {
var script = `
param([string]$vmName)
Hyper-V\Set-VMFloppyDiskDrive -VMName $vmName -Path $null
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, fixedVHD bool) error {
if generation == 2 {
var script = `
param([string]$vmName, [string]$path, [string]$harddrivePath, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks)
$vhdx = $vmName + '.vhdx'
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
if ($harddrivePath){
if($diffDisks -eq "true"){
New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes
} else {
Copy-Item -Path $harddrivePath -Destination $vhdPath
}
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation
} else {
Hyper-V\New-VHD -Path $vhdPath -SizeBytes $newVHDSizeBytes -BlockSizeBytes $vhdBlockSizeBytes
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation
}
`
var ps powershell.PowerShellCmd
if err := ps.Run(script, vmName, path, harddrivePath, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10), switchName, strconv.FormatInt(int64(generation), 10), strconv.FormatBool(diffDisks)); err != nil {
return err
}
return DisableAutomaticCheckpoints(vmName)
} else {
var script = `
param([string]$vmName, [string]$path, [string]$harddrivePath, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [string]$diffDisks, [string]$fixedVHD)
if($fixedVHD -eq "true"){
$vhdx = $vmName + '.vhd'
}
else{
$vhdx = $vmName + '.vhdx'
}
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
if ($harddrivePath){
if($diffDisks -eq "true"){
New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes
}
else{
Copy-Item -Path $harddrivePath -Destination $vhdPath
}
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName
} else {
if($fixedVHD -eq "true"){
Hyper-V\New-VHD -Path $vhdPath -Fixed -SizeBytes $newVHDSizeBytes
}
else {
Hyper-V\New-VHD -Path $vhdPath -SizeBytes $newVHDSizeBytes -BlockSizeBytes $vhdBlockSizeBytes
}
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName
}
`
var ps powershell.PowerShellCmd
if err := ps.Run(script, vmName, path, harddrivePath, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10), switchName, strconv.FormatBool(diffDisks), strconv.FormatBool(fixedVHD)); err != nil {
return err
}
if err := DisableAutomaticCheckpoints(vmName); err != nil {
return err
}
return DeleteAllDvdDrives(vmName)
}
}
func DisableAutomaticCheckpoints(vmName string) error {
var script = `
param([string]$vmName)
if ((Get-Command Hyper-V\Set-Vm).Parameters["AutomaticCheckpointsEnabled"]) {
Hyper-V\Set-Vm -Name $vmName -AutomaticCheckpointsEnabled $false }
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func ExportVmxcVirtualMachine(exportPath string, vmName string, snapshotName string, allSnapshots bool) error {
var script = `
param([string]$exportPath, [string]$vmName, [string]$snapshotName, [string]$allSnapshotsString)
$WorkingPath = Join-Path $exportPath $vmName
if (Test-Path $WorkingPath) {
throw "Export path working directory: $WorkingPath already exists!"
}
$allSnapshots = [System.Boolean]::Parse($allSnapshotsString)
if ($snapshotName) {
$snapshot = Hyper-V\Get-VMSnapshot -VMName $vmName -Name $snapshotName
Hyper-V\Export-VMSnapshot -VMSnapshot $snapshot -Path $exportPath -ErrorAction Stop
} else {
if (!$allSnapshots) {
#Use last snapshot if one was not specified
$snapshot = Hyper-V\Get-VMSnapshot -VMName $vmName | Select -Last 1
} else {
$snapshot = $null
}
if (!$snapshot) {
#No snapshot clone
Hyper-V\Export-VM -Name $vmName -Path $exportPath -ErrorAction Stop
} else {
#Snapshot clone
Hyper-V\Export-VMSnapshot -VMSnapshot $snapshot -Path $exportPath -ErrorAction Stop
}
}
$result = Get-ChildItem -Path $WorkingPath | Move-Item -Destination $exportPath -Force
$result = Remove-Item -Path $WorkingPath
`
allSnapshotsString := "False"
if allSnapshots {
allSnapshotsString = "True"
}
var ps powershell.PowerShellCmd
err := ps.Run(script, exportPath, vmName, snapshotName, allSnapshotsString)
return err
}
func CopyVmxcVirtualMachine(exportPath string, cloneFromVmxcPath string) error {
var script = `
param([string]$exportPath, [string]$cloneFromVmxcPath)
if (!(Test-Path $cloneFromVmxcPath)){
throw "Clone from vmxc directory: $cloneFromVmxcPath does not exist!"
}
if (!(Test-Path $exportPath)){
New-Item -ItemType Directory -Force -Path $exportPath
}
$cloneFromVmxcPath = Join-Path $cloneFromVmxcPath '\*'
Copy-Item $cloneFromVmxcPath $exportPath -Recurse -Force
`
var ps powershell.PowerShellCmd
err := ps.Run(script, exportPath, cloneFromVmxcPath)
return err
}
func SetVmNetworkAdapterMacAddress(vmName string, mac string) error {
var script = `
param([string]$vmName, [string]$mac)
Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, mac)
return err
}
func ImportVmxcVirtualMachine(importPath string, vmName string, harddrivePath string, ram int64, switchName string) error {
var script = `
param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName)
$VirtualHarddisksPath = Join-Path -Path $importPath -ChildPath 'Virtual Hard Disks'
if (!(Test-Path $VirtualHarddisksPath)) {
New-Item -ItemType Directory -Force -Path $VirtualHarddisksPath
}
$vhdPath = ""
if ($harddrivePath){
$vhdx = $vmName + '.vhdx'
$vhdPath = Join-Path -Path $VirtualHarddisksPath -ChildPath $vhdx
}
$VirtualMachinesPath = Join-Path $importPath 'Virtual Machines'
if (!(Test-Path $VirtualMachinesPath)) {
New-Item -ItemType Directory -Force -Path $VirtualMachinesPath
}
$VirtualMachinePath = Get-ChildItem -Path $VirtualMachinesPath -Filter *.vmcx -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
if (!$VirtualMachinePath){
$VirtualMachinePath = Get-ChildItem -Path $VirtualMachinesPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
}
if (!$VirtualMachinePath){
$VirtualMachinePath = Get-ChildItem -Path $importPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
}
$compatibilityReport = Hyper-V\Compare-VM -Path $VirtualMachinePath -VirtualMachinePath $importPath -SmartPagingFilePath $importPath -SnapshotFilePath $importPath -VhdDestinationPath $VirtualHarddisksPath -GenerateNewId -Copy:$false
if ($vhdPath){
Copy-Item -Path $harddrivePath -Destination $vhdPath
$existingFirstHarddrive = $compatibilityReport.VM.HardDrives | Select -First 1
if ($existingFirstHarddrive) {
$existingFirstHarddrive | Hyper-V\Set-VMHardDiskDrive -Path $vhdPath
} else {
Hyper-V\Add-VMHardDiskDrive -VM $compatibilityReport.VM -Path $vhdPath
}
}
Hyper-V\Set-VMMemory -VM $compatibilityReport.VM -StartupBytes $memoryStartupBytes
$networkAdaptor = $compatibilityReport.VM.NetworkAdapters | Select -First 1
Hyper-V\Disconnect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor
Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switchName
$vm = Hyper-V\Import-VM -CompatibilityReport $compatibilityReport
if ($vm) {
$result = Hyper-V\Rename-VM -VM $vm -NewName $VMName
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), switchName)
return err
}
func CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error {
if cloneFromVmName != "" {
if err := ExportVmxcVirtualMachine(path, cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots); err != nil {
return err
}
}
if cloneFromVmxcPath != "" {
if err := CopyVmxcVirtualMachine(path, cloneFromVmxcPath); err != nil {
return err
}
}
if err := ImportVmxcVirtualMachine(path, vmName, harddrivePath, ram, switchName); err != nil {
return err
}
return DeleteAllDvdDrives(vmName)
}
func GetVirtualMachineGeneration(vmName string) (uint, error) {
var script = `
param([string]$vmName)
$generation = Hyper-V\Get-Vm -Name $vmName | %{$_.Generation}
if (!$generation){
$generation = 1
}
return $generation
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return 0, err
}
generationUint32, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 32)
if err != nil {
return 0, err
}
generation := uint(generationUint32)
return generation, err
}
func SetVirtualMachineCpuCount(vmName string, cpu uint) error {
var script = `
param([string]$vmName, [int]$cpu)
Hyper-V\Set-VMProcessor -VMName $vmName -Count $cpu
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10))
return err
}
func SetVirtualMachineVirtualizationExtensions(vmName string, enableVirtualizationExtensions bool) error {
var script = `
param([string]$vmName, [string]$exposeVirtualizationExtensionsString)
$exposeVirtualizationExtensions = [System.Boolean]::Parse($exposeVirtualizationExtensionsString)
Hyper-V\Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $exposeVirtualizationExtensions
`
exposeVirtualizationExtensionsString := "False"
if enableVirtualizationExtensions {
exposeVirtualizationExtensionsString = "True"
}
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, exposeVirtualizationExtensionsString)
return err
}
func SetVirtualMachineDynamicMemory(vmName string, enableDynamicMemory bool) error {
var script = `
param([string]$vmName, [string]$enableDynamicMemoryString)
$enableDynamicMemory = [System.Boolean]::Parse($enableDynamicMemoryString)
Hyper-V\Set-VMMemory -VMName $vmName -DynamicMemoryEnabled $enableDynamicMemory
`
enableDynamicMemoryString := "False"
if enableDynamicMemory {
enableDynamicMemoryString = "True"
}
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, enableDynamicMemoryString)
return err
}
func SetVirtualMachineMacSpoofing(vmName string, enableMacSpoofing bool) error {
var script = `
param([string]$vmName, $enableMacSpoofing)
Hyper-V\Set-VMNetworkAdapter -VMName $vmName -MacAddressSpoofing $enableMacSpoofing
`
var ps powershell.PowerShellCmd
enableMacSpoofingString := "Off"
if enableMacSpoofing {
enableMacSpoofingString = "On"
}
err := ps.Run(script, vmName, enableMacSpoofingString)
return err
}
func SetVirtualMachineSecureBoot(vmName string, enableSecureBoot bool, templateName string) error {
var script = `
param([string]$vmName, [string]$enableSecureBootString, [string]$templateName)
$cmdlet = Get-Command Hyper-V\Set-VMFirmware
# The SecureBootTemplate parameter is only available in later versions
if ($cmdlet.Parameters.SecureBootTemplate) {
Hyper-V\Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBootString -SecureBootTemplate $templateName
} else {
Hyper-V\Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBootString
}
`
var ps powershell.PowerShellCmd
enableSecureBootString := "Off"
if enableSecureBoot {
enableSecureBootString = "On"
}
if templateName == "" {
templateName = "MicrosoftWindows"
}
err := ps.Run(script, vmName, enableSecureBootString, templateName)
return err
}
func DeleteVirtualMachine(vmName string) error {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName
if (($vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::Off) -and ($vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::OffCritical)) {
Hyper-V\Stop-VM -VM $vm -TurnOff -Force -Confirm:$false
}
Hyper-V\Remove-VM -Name $vmName -Force -Confirm:$false
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func ExportVirtualMachine(vmName string, path string) error {
var script = `
param([string]$vmName, [string]$path)
Hyper-V\Export-VM -Name $vmName -Path $path
if (Test-Path -Path ([IO.Path]::Combine($path, $vmName, 'Virtual Machines', '*.VMCX')))
{
$vm = Hyper-V\Get-VM -Name $vmName
$vm_adapter = Hyper-V\Get-VMNetworkAdapter -VM $vm | Select -First 1
$config = [xml]@"
<?xml version="1.0" ?>
<configuration>
<properties>
<subtype type="integer">$($vm.Generation - 1)</subtype>
<name type="string">$($vm.Name)</name>
</properties>
<settings>
<processors>
<count type="integer">$($vm.ProcessorCount)</count>
</processors>
<memory>
<bank>
<dynamic_memory_enabled type="bool">$($vm.DynamicMemoryEnabled)</dynamic_memory_enabled>
<limit type="integer">$($vm.MemoryMaximum / 1MB)</limit>
<reservation type="integer">$($vm.MemoryMinimum / 1MB)</reservation>
<size type="integer">$($vm.MemoryStartup / 1MB)</size>
</bank>
</memory>
</settings>
<AltSwitchName type="string">$($vm_adapter.SwitchName)</AltSwitchName>
<boot>
<device0 type="string">Optical</device0>
</boot>
<secure_boot_enabled type="bool">False</secure_boot_enabled>
<notes type="string">$($vm.Notes)</notes>
<vm-controllers/>
</configuration>
"@
if ($vm.Generation -eq 1)
{
$vm_controllers = Hyper-V\Get-VMIdeController -VM $vm
$controller_type = $config.SelectSingleNode('/configuration/vm-controllers')
# IDE controllers are not stored in a special XML container
}
else
{
$vm_controllers = Hyper-V\Get-VMScsiController -VM $vm
$controller_type = $config.CreateElement('scsi')
$controller_type.SetAttribute('ChannelInstanceGuid', 'x')
# SCSI controllers are stored in the scsi XML container
if ((Hyper-V\Get-VMFirmware -VM $vm).SecureBoot -eq [Microsoft.HyperV.PowerShell.OnOffState]::On)
{
$config.configuration.secure_boot_enabled.'#text' = 'True'
}
else
{
$config.configuration.secure_boot_enabled.'#text' = 'False'
}
}
$vm_controllers | ForEach {
$controller = $config.CreateElement('controller' + $_.ControllerNumber)
$_.Drives | ForEach {
$drive = $config.CreateElement('drive' + ($_.DiskNumber + 0))
$drive_path = $config.CreateElement('pathname')
$drive_path.SetAttribute('type', 'string')
$drive_path.AppendChild($config.CreateTextNode($_.Path))
$drive_type = $config.CreateElement('type')
$drive_type.SetAttribute('type', 'string')
if ($_ -is [Microsoft.HyperV.PowerShell.HardDiskDrive])
{
$drive_type.AppendChild($config.CreateTextNode('VHD'))
}
elseif ($_ -is [Microsoft.HyperV.PowerShell.DvdDrive])
{
$drive_type.AppendChild($config.CreateTextNode('ISO'))
}
else
{
$drive_type.AppendChild($config.CreateTextNode('NONE'))
}
$drive.AppendChild($drive_path)
$drive.AppendChild($drive_type)
$controller.AppendChild($drive)
}
$controller_type.AppendChild($controller)
}
if ($controller_type.Name -ne 'vm-controllers')
{
$config.SelectSingleNode('/configuration/vm-controllers').AppendChild($controller_type)
}
$config.Save([IO.Path]::Combine($path, $vm.Name, 'Virtual Machines', 'box.xml'))
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, path)
return err
}
func PreserveLegacyExportBehaviour(srcPath, dstPath string) error {
var script = `
param([string]$srcPath, [string]$dstPath)
# Validate the paths returning an error if they are empty or don't exist
$srcPath, $dstPath | % {
if ($_) {
if (! (Test-Path $_)) {
[System.Console]::Error.WriteLine("Path $_ does not exist")
exit
}
} else {
[System.Console]::Error.WriteLine("A supplied path is empty")
exit
}
}
# Export-VM should just create directories at the root of the export path
# but, just in case, move all files as well...
Move-Item -Path (Join-Path (Get-Item $srcPath).FullName "*.*") -Destination (Get-Item $dstPath).FullName
# Move directories with content; Delete empty directories
$dirObj = Get-ChildItem $srcPath -Directory | % {
New-Object PSObject -Property @{
FullName=$_.FullName;
HasContent=$(if ($_.GetFileSystemInfos().Count -gt 0) {$true} else {$false})
}
}
foreach ($directory in $dirObj) {
if ($directory.HasContent) {
Move-Item -Path $directory.FullName -Destination (Get-Item $dstPath).FullName
} else {
Remove-Item -Path $directory.FullName
}
}
# Only remove the source directory if it is now empty
if ( $((Get-Item $srcPath).GetFileSystemInfos().Count) -eq 0 ) {
Remove-Item -Path $srcPath
} else {
# 'Return' an error message to PowerShellCmd as the directory should
# always be empty at the end of the script. The check is here to stop
# the Remove-Item command from doing any damage if some unforeseen
# error has occured
[System.Console]::Error.WriteLine("Refusing to remove $srcPath as it is not empty")
exit
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, srcPath, dstPath)
return err
}
func CompactDisks(path string) (result string, err error) {
var script = `
param([string]$srcPath)
$disks = Get-ChildItem -Path $srcPath -Recurse -Filter *.vhd* -ErrorAction SilentlyContinue | % { $_.FullName }
# Failure to find any disks is treated as a 'soft' error. Simply print out
# a warning and exit
if ($disks.Length -eq 0) {
Write-Output "WARNING: No disks found under $srcPath"
exit
}
foreach ($disk in $disks) {
Write-Output "Compacting disk: $(Split-Path $disk -leaf)"
$sizeBefore = $disk.Length
Optimize-VHD -Path $disk -Mode Full
$sizeAfter = $disk.Length
# Calculate the percentage change in disk size
if ($sizeAfter -gt 0) { # Protect against division by zero
$percentChange = ( ( $sizeAfter / $sizeBefore ) * 100 ) - 100
switch($percentChange) {
{$_ -lt 0} {Write-Output "Disk size reduced by: $(([math]::Abs($_)).ToString("#.#"))%"}
{$_ -eq 0} {Write-Output "Disk size is unchanged"}
{$_ -gt 0} {Write-Output "WARNING: Disk size increased by: $($_.ToString("#.#"))%"}
}
}
}
`
var ps powershell.PowerShellCmd
result, err = ps.Output(script, path)
return
}
func CreateVirtualSwitch(switchName string, switchType string) (bool, error) {
var script = `
param([string]$switchName,[string]$switchType)
$switches = Hyper-V\Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
if ($switches.Count -eq 0) {
Hyper-V\New-VMSwitch -Name $switchName -SwitchType $switchType
return $true
}
return $false
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, switchName, switchType)
var created = strings.TrimSpace(cmdOut) == "True"
return created, err
}
func DeleteVirtualSwitch(switchName string) error {
var script = `
param([string]$switchName)
$switch = Hyper-V\Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
if ($switch -ne $null) {
$switch | Hyper-V\Remove-VMSwitch -Force -Confirm:$false
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, switchName)
return err
}
func StartVirtualMachine(vmName string) error {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off) {
Hyper-V\Start-VM -Name $vmName -Confirm:$false
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func RestartVirtualMachine(vmName string) error {
var script = `
param([string]$vmName)
Hyper-V\Restart-VM $vmName -Force -Confirm:$false
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func StopVirtualMachine(vmName string) error {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
Hyper-V\Stop-VM -VM $vm -Force -Confirm:$false
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error {
integrationServiceId := ""
switch integrationServiceName {
case "Time Synchronization":
integrationServiceId = "2497F4DE-E9FA-4204-80E4-4B75C46419C0"
case "Heartbeat":
integrationServiceId = "84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47"
case "Key-Value Pair Exchange":
integrationServiceId = "2A34B1C2-FD73-4043-8A5B-DD2159BC743F"
case "Shutdown":
integrationServiceId = "9F8233AC-BE49-4C79-8EE3-E7E1985B2077"
case "VSS":
integrationServiceId = "5CED1297-4598-4915-A5FC-AD21BB4D02A4"
case "Guest Service Interface":
integrationServiceId = "6C09BB55-D683-4DA0-8931-C9BF705F6480"
default:
panic("unrecognized Integration Service Name")
}
var script = `
param([string]$vmName,[string]$integrationServiceId)
Hyper-V\Get-VMIntegrationService -VmName $vmName | ?{$_.Id -match $integrationServiceId} | Hyper-V\Enable-VMIntegrationService
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, integrationServiceId)
return err
}
func SetNetworkAdapterVlanId(switchName string, vlanId string) error {
var script = `
param([string]$networkAdapterName,[string]$vlanId)
Hyper-V\Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $networkAdapterName -Access -VlanId $vlanId
`
var ps powershell.PowerShellCmd
err := ps.Run(script, switchName, vlanId)
return err
}
func SetVirtualMachineVlanId(vmName string, vlanId string) error {
var script = `
param([string]$vmName,[string]$vlanId)
Hyper-V\Set-VMNetworkAdapterVlan -VMName $vmName -Access -VlanId $vlanId
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, vlanId)
return err
}
func GetExternalOnlineVirtualSwitch() (string, error) {
var script = `
$adapters = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Up' } | Sort-Object -Descending -Property Speed
foreach ($adapter in $adapters) {
$switch = Hyper-V\Get-VMSwitch -SwitchType External | Where-Object { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
if ($switch -ne $null) {
$switch.Name
break
}
}
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script)
if err != nil {
return "", err
}
var switchName = strings.TrimSpace(cmdOut)
return switchName, nil
}
func CreateExternalVirtualSwitch(vmName string, switchName string) error {
var script = `
param([string]$vmName,[string]$switchName)
$switch = $null
$names = @('ethernet','wi-fi','lan')
$adapters = foreach ($name in $names) {
Get-NetAdapter -Physical -Name $name -ErrorAction SilentlyContinue | where status -eq 'up'
}
foreach ($adapter in $adapters) {
$switch = Hyper-V\Get-VMSwitch -SwitchType External | where { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
if ($switch -eq $null) {
$switch = Hyper-V\New-VMSwitch -Name $switchName -NetAdapterName $adapter.Name -AllowManagementOS $true -Notes 'Parent OS, VMs, WiFi'
}
if ($switch -ne $null) {
break
}
}
if($switch -ne $null) {
Hyper-V\Get-VMNetworkAdapter -VMName $vmName | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $switch
} else {
Write-Error 'No internet adapters found'
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, switchName)
return err
}
func GetVirtualMachineSwitchName(vmName string) (string, error) {
var script = `
param([string]$vmName)
(Hyper-V\Get-VMNetworkAdapter -VMName $vmName).SwitchName
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return "", err
}
return strings.TrimSpace(cmdOut), nil
}
func ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error {
var script = `
param([string]$vmName,[string]$switchName)
Hyper-V\Get-VMNetworkAdapter -VMName $vmName | Hyper-V\Connect-VMNetworkAdapter -SwitchName $switchName
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, switchName)
return err
}
func AddVirtualMachineHardDiskDrive(vmName string, vhdRoot string, vhdName string, vhdSizeBytes int64, vhdBlockSize int64, controllerType string) error {
var script = `
param([string]$vmName,[string]$vhdRoot, [string]$vhdName, [string]$vhdSizeInBytes, [string]$vhdBlockSizeInByte, [string]$controllerType)
$vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdName
Hyper-V\New-VHD -path $vhdPath -SizeBytes $vhdSizeInBytes -BlockSizeBytes $vhdBlockSizeInByte
Hyper-V\Add-VMHardDiskDrive -VMName $vmName -path $vhdPath -controllerType $controllerType
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, vhdRoot, vhdName, strconv.FormatInt(vhdSizeBytes, 10), strconv.FormatInt(vhdBlockSize, 10), controllerType)
return err
}
func UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error {
var script = `
param([string]$vmName,[string]$switchName)
Hyper-V\Set-VMNetworkAdapterVlan -VMName $vmName -Untagged
Hyper-V\Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $switchName -Untagged
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, switchName)
return err
}
func IsRunning(vmName string) (bool, error) {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
$vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return false, err
}
var isRunning = strings.TrimSpace(cmdOut) == "True"
return isRunning, err
}
func IsOff(vmName string) (bool, error) {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
$vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return false, err
}
var isRunning = strings.TrimSpace(cmdOut) == "True"
return isRunning, err
}
func Uptime(vmName string) (uint64, error) {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
$vm.Uptime.TotalSeconds
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return 0, err
}
uptime, err := strconv.ParseUint(strings.TrimSpace(cmdOut), 10, 64)
return uptime, err
}
func Mac(vmName string) (string, error) {
var script = `
param([string]$vmName, [int]$adapterIndex)
try {
$adapter = Hyper-V\Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
$mac = $adapter[$adapterIndex].MacAddress
if($mac -eq $null) {
return ""
}
} catch {
return ""
}
$mac
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, vmName, "0")
return cmdOut, err
}
func IpAddress(mac string) (string, error) {
var script = `
param([string]$mac, [int]$addressIndex)
try {
$vm = Hyper-V\Get-VM | ?{$_.NetworkAdapters.MacAddress -eq $mac}
if ($vm.NetworkAdapters.IpAddresses) {
$ipAddresses = $vm.NetworkAdapters.IPAddresses
if ($ipAddresses -isnot [array]) {
$ipAddresses = @($ipAddresses)
}
$ip = $ipAddresses[$addressIndex]
} else {
$vm_info = Get-CimInstance -ClassName Msvm_ComputerSystem -Namespace root\virtualization\v2 -Filter "ElementName='$($vm.Name)'"
$ip_details = (Get-CimAssociatedInstance -InputObject $vm_info -ResultClassName Msvm_KvpExchangeComponent).GuestIntrinsicExchangeItems | %{ [xml]$_ } | ?{ $_.SelectSingleNode("/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text()='NetworkAddressIPv4']") }
if ($null -eq $ip_details) {
return ""
}
$ip_addresses = $ip_details.SelectSingleNode("/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()").Value
$ip = ($ip_addresses -split ";")[0]
}
} catch {
return ""
}
$ip
`
var ps powershell.PowerShellCmd
cmdOut, err := ps.Output(script, mac, "0")
return cmdOut, err
}
func TurnOff(vmName string) error {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
Hyper-V\Stop-VM -Name $vmName -TurnOff -Force -Confirm:$false
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func ShutDown(vmName string) error {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
Hyper-V\Stop-VM -Name $vmName -Force -Confirm:$false
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
}
func TypeScanCodes(vmName string, scanCodes string) error {
if len(scanCodes) == 0 {
return nil
}
var script = `
param([string]$vmName, [string]$scanCodes)
#Requires -Version 3
function Hyper-V\Get-VMConsole
{
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string] $VMName
)
$ErrorActionPreference = "Stop"
$vm = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName Msvm_ComputerSystem -ErrorAction Ignore -Verbose:$false | where ElementName -eq $VMName | select -first 1
if ($vm -eq $null){
Write-Error ("VirtualMachine({0}) is not found!" -f $VMName)
}
$vmKeyboard = $vm | Get-CimAssociatedInstance -ResultClassName "Msvm_Keyboard" -ErrorAction Ignore -Verbose:$false
if ($vmKeyboard -eq $null) {
$vmKeyboard = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName Msvm_Keyboard -ErrorAction Ignore -Verbose:$false | where SystemName -eq $vm.Name | select -first 1
}
if ($vmKeyboard -eq $null) {
$vmKeyboard = Get-CimInstance -Namespace "root\virtualization" -ClassName Msvm_Keyboard -ErrorAction Ignore -Verbose:$false | where SystemName -eq $vm.Name | select -first 1
}
if ($vmKeyboard -eq $null){
Write-Error ("VirtualMachine({0}) keyboard class is not found!" -f $VMName)
}
#TODO: It may be better using New-Module -AsCustomObject to return console object?
#Console object to return
$console = [pscustomobject] @{
Msvm_ComputerSystem = $vm
Msvm_Keyboard = $vmKeyboard
}
#Need to import assembly to use System.Windows.Input.Key
Add-Type -AssemblyName WindowsBase
#region Add Console Members
$console | Add-Member -MemberType ScriptMethod -Name TypeText -Value {
[OutputType([bool])]
param (
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory)]
[string] $AsciiText
)
$result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeText" -Arguments @{ asciiText = $AsciiText }
return (0 -eq $result.ReturnValue)
}
#Define method:TypeCtrlAltDel
$console | Add-Member -MemberType ScriptMethod -Name TypeCtrlAltDel -Value {
$result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeCtrlAltDel"
return (0 -eq $result.ReturnValue)
}
#Define method:TypeKey
$console | Add-Member -MemberType ScriptMethod -Name TypeKey -Value {
[OutputType([bool])]
param (
[Parameter(Mandatory)]
[Windows.Input.Key] $Key,
[Windows.Input.ModifierKeys] $ModifierKey = [Windows.Input.ModifierKeys]::None
)
$keyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey($Key)
switch ($ModifierKey)
{
([Windows.Input.ModifierKeys]::Control){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftCtrl)}
([Windows.Input.ModifierKeys]::Alt){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftAlt)}
([Windows.Input.ModifierKeys]::Shift){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftShift)}
([Windows.Input.ModifierKeys]::Windows){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LWin)}
}
if ($ModifierKey -eq [Windows.Input.ModifierKeys]::None)
{
$result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeKey" -Arguments @{ keyCode = $keyCode }
}
else
{
$this.Msvm_Keyboard | Invoke-CimMethod -MethodName "PressKey" -Arguments @{ keyCode = $modifierKeyCode }
$result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeKey" -Arguments @{ keyCode = $keyCode }
$this.Msvm_Keyboard | Invoke-CimMethod -MethodName "ReleaseKey" -Arguments @{ keyCode = $modifierKeyCode }
}
$result = return (0 -eq $result.ReturnValue)
}
#Define method:Scancodes
$console | Add-Member -MemberType ScriptMethod -Name TypeScancodes -Value {
[OutputType([bool])]
param (
[Parameter(Mandatory)]
[byte[]] $ScanCodes
)
$result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeScancodes" -Arguments @{ ScanCodes = $ScanCodes }
return (0 -eq $result.ReturnValue)
}
#Define method:ExecCommand
$console | Add-Member -MemberType ScriptMethod -Name ExecCommand -Value {
param (
[Parameter(Mandatory)]
[string] $Command
)
if ([String]::IsNullOrEmpty($Command)){
return
}
$console.TypeText($Command) > $null
$console.TypeKey([Windows.Input.Key]::Enter) > $null
#sleep -Milliseconds 100
}
#Define method:Dispose
$console | Add-Member -MemberType ScriptMethod -Name Dispose -Value {
$this.Msvm_ComputerSystem.Dispose()
$this.Msvm_Keyboard.Dispose()
}
#endregion
return $console
}
$vmConsole = Hyper-V\Get-VMConsole -VMName $vmName
$scanCodesToSend = ''
$scanCodes.Split(' ') | %{
$scanCode = $_
if ($scanCode.StartsWith('wait')){
$timeToWait = $scanCode.Substring(4)
if (!$timeToWait){
$timeToWait = "1"
}
if ($scanCodesToSend){
$scanCodesToSendByteArray = [byte[]]@($scanCodesToSend.Split(' ') | %{"0x$_"})
$scanCodesToSendByteArray | %{
$vmConsole.TypeScancodes($_)
}
}
write-host "Special code <wait> found, will sleep $timeToWait second(s) at this point."
Start-Sleep -s $timeToWait
$scanCodesToSend = ''
} else {
if ($scanCodesToSend){
write-host "Sending special code '$scanCodesToSend' '$scanCode'"
$scanCodesToSend = "$scanCodesToSend $scanCode"
} else {
write-host "Sending char '$scanCode'"
$scanCodesToSend = "$scanCode"
}
}
}
if ($scanCodesToSend){
$scanCodesToSendByteArray = [byte[]]@($scanCodesToSend.Split(' ') | %{"0x$_"})
$scanCodesToSendByteArray | %{
$vmConsole.TypeScancodes($_)
}
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, scanCodes)
return err
}
func ConnectVirtualMachine(vmName string) (context.CancelFunc, error) {
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, "vmconnect.exe", "localhost", vmName)
err := cmd.Start()
if err != nil {
// Failed to start so cancel function not required
cancel = nil
}
return cancel, err
}
func DisconnectVirtualMachine(cancel context.CancelFunc) {
cancel()
}