github.com/atorr/packer@v1.2.2/common/powershell/hyperv/hyperv.go (about)

     1  package hyperv
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/packer/common/powershell"
     9  )
    10  
    11  func GetHostAdapterIpAddressForSwitch(switchName string) (string, error) {
    12  	var script = `
    13  param([string]$switchName, [int]$addressIndex)
    14  
    15  $HostVMAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $switchName
    16  if ($HostVMAdapter){
    17      $HostNetAdapter = Get-NetAdapter | ?{ $_.DeviceID -eq $HostVMAdapter.DeviceId }
    18      if ($HostNetAdapter){
    19          $HostNetAdapterConfiguration =  @(get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = 'TRUE' AND InterfaceIndex=$($HostNetAdapter.ifIndex)")
    20          if ($HostNetAdapterConfiguration){
    21              return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
    22          }
    23      }
    24  }
    25  return $false
    26  `
    27  
    28  	var ps powershell.PowerShellCmd
    29  	cmdOut, err := ps.Output(script, switchName, "0")
    30  
    31  	return cmdOut, err
    32  }
    33  
    34  func GetVirtualMachineNetworkAdapterAddress(vmName string) (string, error) {
    35  
    36  	var script = `
    37  param([string]$vmName, [int]$addressIndex)
    38  try {
    39    $adapter = Hyper-V\Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
    40    $ip = $adapter.IPAddresses[$addressIndex]
    41    if($ip -eq $null) {
    42      return $false
    43    }
    44  } catch {
    45    return $false
    46  }
    47  $ip
    48  `
    49  
    50  	var ps powershell.PowerShellCmd
    51  	cmdOut, err := ps.Output(script, vmName, "0")
    52  
    53  	return cmdOut, err
    54  }
    55  
    56  func CreateDvdDrive(vmName string, isoPath string, generation uint) (uint, uint, error) {
    57  	var ps powershell.PowerShellCmd
    58  	var script string
    59  
    60  	script = `
    61  param([string]$vmName, [string]$isoPath)
    62  $dvdController = Hyper-V\Add-VMDvdDrive -VMName $vmName -path $isoPath -Passthru
    63  $dvdController | Hyper-V\Set-VMDvdDrive -path $null
    64  $result = "$($dvdController.ControllerNumber),$($dvdController.ControllerLocation)"
    65  $result
    66  `
    67  
    68  	cmdOut, err := ps.Output(script, vmName, isoPath)
    69  	if err != nil {
    70  		return 0, 0, err
    71  	}
    72  
    73  	cmdOutArray := strings.Split(cmdOut, ",")
    74  	if len(cmdOutArray) != 2 {
    75  		return 0, 0, errors.New("Did not return controller number and controller location")
    76  	}
    77  
    78  	controllerNumberTemp, err := strconv.ParseUint(strings.TrimSpace(cmdOutArray[0]), 10, 64)
    79  	if err != nil {
    80  		return 0, 0, err
    81  	}
    82  	controllerNumber := uint(controllerNumberTemp)
    83  
    84  	controllerLocationTemp, err := strconv.ParseUint(strings.TrimSpace(cmdOutArray[1]), 10, 64)
    85  	if err != nil {
    86  		return controllerNumber, 0, err
    87  	}
    88  	controllerLocation := uint(controllerLocationTemp)
    89  
    90  	return controllerNumber, controllerLocation, err
    91  }
    92  
    93  func MountDvdDrive(vmName string, path string, controllerNumber uint, controllerLocation uint) error {
    94  
    95  	var script = `
    96  param([string]$vmName,[string]$path,[string]$controllerNumber,[string]$controllerLocation)
    97  $vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
    98  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
    99  Hyper-V\Set-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation -Path $path
   100  `
   101  
   102  	var ps powershell.PowerShellCmd
   103  	err := ps.Run(script, vmName, path, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
   104  	return err
   105  }
   106  
   107  func UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
   108  	var script = `
   109  param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
   110  $vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   111  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
   112  Hyper-V\Set-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation -Path $null
   113  `
   114  
   115  	var ps powershell.PowerShellCmd
   116  	err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
   117  	return err
   118  }
   119  
   120  func SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint, generation uint) error {
   121  
   122  	if generation < 2 {
   123  		script := `
   124  param([string]$vmName)
   125  Hyper-V\Set-VMBios -VMName $vmName -StartupOrder @("CD", "IDE","LegacyNetworkAdapter","Floppy")
   126  `
   127  		var ps powershell.PowerShellCmd
   128  		err := ps.Run(script, vmName)
   129  		return err
   130  	} else {
   131  		script := `
   132  param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
   133  $vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   134  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
   135  Hyper-V\Set-VMFirmware -VMName $vmName -FirstBootDevice $vmDvdDrive -ErrorAction SilentlyContinue
   136  `
   137  		var ps powershell.PowerShellCmd
   138  		err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
   139  		return err
   140  	}
   141  }
   142  
   143  func DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
   144  	var script = `
   145  param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
   146  $vmDvdDrive = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   147  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
   148  Hyper-V\Remove-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   149  `
   150  
   151  	var ps powershell.PowerShellCmd
   152  	err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
   153  	return err
   154  }
   155  
   156  func DeleteAllDvdDrives(vmName string) error {
   157  	var script = `
   158  param([string]$vmName)
   159  Hyper-V\Get-VMDvdDrive -VMName $vmName | Hyper-V\Remove-VMDvdDrive
   160  `
   161  
   162  	var ps powershell.PowerShellCmd
   163  	err := ps.Run(script, vmName)
   164  	return err
   165  }
   166  
   167  func MountFloppyDrive(vmName string, path string) error {
   168  	var script = `
   169  param([string]$vmName, [string]$path)
   170  Hyper-V\Set-VMFloppyDiskDrive -VMName $vmName -Path $path
   171  `
   172  
   173  	var ps powershell.PowerShellCmd
   174  	err := ps.Run(script, vmName, path)
   175  	return err
   176  }
   177  
   178  func UnmountFloppyDrive(vmName string) error {
   179  
   180  	var script = `
   181  param([string]$vmName)
   182  Hyper-V\Set-VMFloppyDiskDrive -VMName $vmName -Path $null
   183  `
   184  
   185  	var ps powershell.PowerShellCmd
   186  	err := ps.Run(script, vmName)
   187  	return err
   188  }
   189  
   190  func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, switchName string, generation uint, diffDisks bool) error {
   191  
   192  	if generation == 2 {
   193  		var script = `
   194  param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks)
   195  $vhdx = $vmName + '.vhdx'
   196  $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx
   197  if ($harddrivePath){
   198  	if($diffDisks -eq "true"){
   199  		New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing
   200  	} else {
   201  		Copy-Item -Path $harddrivePath -Destination $vhdPath
   202  	}
   203  	Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation
   204  } else {
   205  	Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation
   206  }
   207  `
   208  		var ps powershell.PowerShellCmd
   209  		if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10), strconv.FormatBool(diffDisks)); err != nil {
   210  			return err
   211  		}
   212  
   213  		return DisableAutomaticCheckpoints(vmName)
   214  	} else {
   215  		var script = `
   216  param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [string]$diffDisks)
   217  $vhdx = $vmName + '.vhdx'
   218  $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx
   219  if ($harddrivePath){
   220  	if($diffDisks -eq "true"){
   221  		New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing
   222  	}
   223  	else{
   224  		Copy-Item -Path $harddrivePath -Destination $vhdPath
   225  	}
   226  	Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName
   227  } else {
   228  	Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName
   229  }
   230  `
   231  		var ps powershell.PowerShellCmd
   232  		if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatBool(diffDisks)); err != nil {
   233  			return err
   234  		}
   235  
   236  		if err := DisableAutomaticCheckpoints(vmName); err != nil {
   237  			return err
   238  		}
   239  
   240  		return DeleteAllDvdDrives(vmName)
   241  	}
   242  }
   243  
   244  func DisableAutomaticCheckpoints(vmName string) error {
   245  	var script = `
   246  param([string]$vmName)
   247  if ((Get-Command Hyper-V\Set-Vm).Parameters["AutomaticCheckpointsEnabled"]) { 
   248  	Hyper-V\Set-Vm -Name $vmName -AutomaticCheckpointsEnabled $false }
   249  `
   250  	var ps powershell.PowerShellCmd
   251  	err := ps.Run(script, vmName)
   252  	return err
   253  }
   254  
   255  func ExportVmxcVirtualMachine(exportPath string, vmName string, snapshotName string, allSnapshots bool) error {
   256  	var script = `
   257  param([string]$exportPath, [string]$vmName, [string]$snapshotName, [string]$allSnapshotsString)
   258  
   259  $WorkingPath = Join-Path $exportPath $vmName 
   260  
   261  if (Test-Path $WorkingPath) {
   262  	throw "Export path working directory: $WorkingPath already exists!"
   263  }
   264  
   265  $allSnapshots = [System.Boolean]::Parse($allSnapshotsString)
   266  
   267  if ($snapshotName) {
   268      $snapshot = Hyper-V\Get-VMSnapshot -VMName $vmName -Name $snapshotName
   269      Hyper-V\Export-VMSnapshot -VMSnapshot $snapshot -Path $exportPath -ErrorAction Stop
   270  } else {
   271      if (!$allSnapshots) {
   272          #Use last snapshot if one was not specified
   273          $snapshot = Hyper-V\Get-VMSnapshot -VMName $vmName | Select -Last 1
   274      } else {
   275          $snapshot = $null
   276      }
   277      
   278      if (!$snapshot) {
   279          #No snapshot clone
   280          Hyper-V\Export-VM -Name $vmName -Path $exportPath -ErrorAction Stop
   281      } else {
   282          #Snapshot clone
   283          Hyper-V\Export-VMSnapshot -VMSnapshot $snapshot -Path $exportPath -ErrorAction Stop
   284      }
   285  }
   286  
   287  $result = Get-ChildItem -Path $WorkingPath | Move-Item -Destination $exportPath -Force
   288  $result = Remove-Item -Path $WorkingPath
   289  	`
   290  
   291  	allSnapshotsString := "False"
   292  	if allSnapshots {
   293  		allSnapshotsString = "True"
   294  	}
   295  
   296  	var ps powershell.PowerShellCmd
   297  	err := ps.Run(script, exportPath, vmName, snapshotName, allSnapshotsString)
   298  
   299  	return err
   300  }
   301  
   302  func CopyVmxcVirtualMachine(exportPath string, cloneFromVmxcPath string) error {
   303  	var script = `
   304  param([string]$exportPath, [string]$cloneFromVmxcPath)
   305  if (!(Test-Path $cloneFromVmxcPath)){
   306  	throw "Clone from vmxc directory: $cloneFromVmxcPath does not exist!"
   307  }
   308  	
   309  if (!(Test-Path $exportPath)){
   310  	New-Item -ItemType Directory -Force -Path $exportPath
   311  }
   312  $cloneFromVmxcPath = Join-Path $cloneFromVmxcPath '\*'
   313  Copy-Item $cloneFromVmxcPath $exportPath -Recurse -Force
   314  	`
   315  
   316  	var ps powershell.PowerShellCmd
   317  	err := ps.Run(script, exportPath, cloneFromVmxcPath)
   318  
   319  	return err
   320  }
   321  
   322  func SetVmNetworkAdapterMacAddress(vmName string, mac string) error {
   323  	var script = `
   324  param([string]$vmName, [string]$mac)
   325  Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac
   326  	`
   327  
   328  	var ps powershell.PowerShellCmd
   329  	err := ps.Run(script, vmName, mac)
   330  
   331  	return err
   332  }
   333  
   334  func ImportVmxcVirtualMachine(importPath string, vmName string, harddrivePath string, ram int64, switchName string) error {
   335  	var script = `
   336  param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName)
   337  
   338  $VirtualHarddisksPath = Join-Path -Path $importPath -ChildPath 'Virtual Hard Disks'
   339  if (!(Test-Path $VirtualHarddisksPath)) {
   340  	New-Item -ItemType Directory -Force -Path $VirtualHarddisksPath
   341  }
   342  
   343  $vhdPath = ""
   344  if ($harddrivePath){
   345  	$vhdx = $vmName + '.vhdx'
   346  	$vhdPath = Join-Path -Path $VirtualHarddisksPath -ChildPath $vhdx
   347  }
   348  
   349  $VirtualMachinesPath = Join-Path $importPath 'Virtual Machines'
   350  if (!(Test-Path $VirtualMachinesPath)) {
   351  	New-Item -ItemType Directory -Force -Path $VirtualMachinesPath
   352  }
   353  
   354  $VirtualMachinePath = Get-ChildItem -Path $VirtualMachinesPath -Filter *.vmcx -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
   355  if (!$VirtualMachinePath){
   356      $VirtualMachinePath = Get-ChildItem -Path $VirtualMachinesPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
   357  }
   358  if (!$VirtualMachinePath){
   359      $VirtualMachinePath = Get-ChildItem -Path $importPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
   360  }
   361  
   362  $compatibilityReport = Hyper-V\Compare-VM -Path $VirtualMachinePath -VirtualMachinePath $importPath -SmartPagingFilePath $importPath -SnapshotFilePath $importPath -VhdDestinationPath $VirtualHarddisksPath -GenerateNewId -Copy:$false
   363  if ($vhdPath){
   364  	Copy-Item -Path $harddrivePath -Destination $vhdPath
   365  	$existingFirstHarddrive = $compatibilityReport.VM.HardDrives | Select -First 1
   366  	if ($existingFirstHarddrive) {
   367  		$existingFirstHarddrive | Hyper-V\Set-VMHardDiskDrive -Path $vhdPath
   368  	} else {
   369  		Hyper-V\Add-VMHardDiskDrive -VM $compatibilityReport.VM -Path $vhdPath
   370  	}	
   371  }
   372  Hyper-V\Set-VMMemory -VM $compatibilityReport.VM -StartupBytes $memoryStartupBytes
   373  $networkAdaptor = $compatibilityReport.VM.NetworkAdapters | Select -First 1
   374  Hyper-V\Disconnect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor
   375  Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switchName 
   376  $vm = Hyper-V\Import-VM -CompatibilityReport $compatibilityReport
   377  
   378  if ($vm) {
   379      $result = Hyper-V\Rename-VM -VM $vm -NewName $VMName
   380  }
   381  	`
   382  
   383  	var ps powershell.PowerShellCmd
   384  	err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), switchName)
   385  
   386  	return err
   387  }
   388  
   389  func CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error {
   390  	if cloneFromVmName != "" {
   391  		if err := ExportVmxcVirtualMachine(path, cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots); err != nil {
   392  			return err
   393  		}
   394  	}
   395  
   396  	if cloneFromVmxcPath != "" {
   397  		if err := CopyVmxcVirtualMachine(path, cloneFromVmxcPath); err != nil {
   398  			return err
   399  		}
   400  	}
   401  
   402  	if err := ImportVmxcVirtualMachine(path, vmName, harddrivePath, ram, switchName); err != nil {
   403  		return err
   404  	}
   405  
   406  	return DeleteAllDvdDrives(vmName)
   407  }
   408  
   409  func GetVirtualMachineGeneration(vmName string) (uint, error) {
   410  	var script = `
   411  param([string]$vmName)
   412  $generation = Hyper-V\Get-Vm -Name $vmName | %{$_.Generation}
   413  if (!$generation){
   414      $generation = 1
   415  }
   416  return $generation
   417  `
   418  	var ps powershell.PowerShellCmd
   419  	cmdOut, err := ps.Output(script, vmName)
   420  
   421  	if err != nil {
   422  		return 0, err
   423  	}
   424  
   425  	generationUint32, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 32)
   426  
   427  	if err != nil {
   428  		return 0, err
   429  	}
   430  
   431  	generation := uint(generationUint32)
   432  
   433  	return generation, err
   434  }
   435  
   436  func SetVirtualMachineCpuCount(vmName string, cpu uint) error {
   437  
   438  	var script = `
   439  param([string]$vmName, [int]$cpu)
   440  Hyper-V\Set-VMProcessor -VMName $vmName -Count $cpu
   441  `
   442  	var ps powershell.PowerShellCmd
   443  	err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10))
   444  	return err
   445  }
   446  
   447  func SetVirtualMachineVirtualizationExtensions(vmName string, enableVirtualizationExtensions bool) error {
   448  
   449  	var script = `
   450  param([string]$vmName, [string]$exposeVirtualizationExtensionsString)
   451  $exposeVirtualizationExtensions = [System.Boolean]::Parse($exposeVirtualizationExtensionsString)
   452  Hyper-V\Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $exposeVirtualizationExtensions
   453  `
   454  	exposeVirtualizationExtensionsString := "False"
   455  	if enableVirtualizationExtensions {
   456  		exposeVirtualizationExtensionsString = "True"
   457  	}
   458  	var ps powershell.PowerShellCmd
   459  	err := ps.Run(script, vmName, exposeVirtualizationExtensionsString)
   460  	return err
   461  }
   462  
   463  func SetVirtualMachineDynamicMemory(vmName string, enableDynamicMemory bool) error {
   464  
   465  	var script = `
   466  param([string]$vmName, [string]$enableDynamicMemoryString)
   467  $enableDynamicMemory = [System.Boolean]::Parse($enableDynamicMemoryString)
   468  Hyper-V\Set-VMMemory -VMName $vmName -DynamicMemoryEnabled $enableDynamicMemory
   469  `
   470  	enableDynamicMemoryString := "False"
   471  	if enableDynamicMemory {
   472  		enableDynamicMemoryString = "True"
   473  	}
   474  	var ps powershell.PowerShellCmd
   475  	err := ps.Run(script, vmName, enableDynamicMemoryString)
   476  	return err
   477  }
   478  
   479  func SetVirtualMachineMacSpoofing(vmName string, enableMacSpoofing bool) error {
   480  	var script = `
   481  param([string]$vmName, $enableMacSpoofing)
   482  Hyper-V\Set-VMNetworkAdapter -VMName $vmName -MacAddressSpoofing $enableMacSpoofing
   483  `
   484  
   485  	var ps powershell.PowerShellCmd
   486  
   487  	enableMacSpoofingString := "Off"
   488  	if enableMacSpoofing {
   489  		enableMacSpoofingString = "On"
   490  	}
   491  
   492  	err := ps.Run(script, vmName, enableMacSpoofingString)
   493  	return err
   494  }
   495  
   496  func SetVirtualMachineSecureBoot(vmName string, enableSecureBoot bool) error {
   497  	var script = `
   498  param([string]$vmName, $enableSecureBoot)
   499  Hyper-V\Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBoot
   500  `
   501  
   502  	var ps powershell.PowerShellCmd
   503  
   504  	enableSecureBootString := "Off"
   505  	if enableSecureBoot {
   506  		enableSecureBootString = "On"
   507  	}
   508  
   509  	err := ps.Run(script, vmName, enableSecureBootString)
   510  	return err
   511  }
   512  
   513  func DeleteVirtualMachine(vmName string) error {
   514  
   515  	var script = `
   516  param([string]$vmName)
   517  
   518  $vm = Hyper-V\Get-VM -Name $vmName
   519  if (($vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::Off) -and ($vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::OffCritical)) {
   520      Hyper-V\Stop-VM -VM $vm -TurnOff -Force -Confirm:$false
   521  }
   522  
   523  Hyper-V\Remove-VM -Name $vmName -Force -Confirm:$false
   524  `
   525  
   526  	var ps powershell.PowerShellCmd
   527  	err := ps.Run(script, vmName)
   528  	return err
   529  }
   530  
   531  func ExportVirtualMachine(vmName string, path string) error {
   532  
   533  	var script = `
   534  param([string]$vmName, [string]$path)
   535  Hyper-V\Export-VM -Name $vmName -Path $path
   536  
   537  if (Test-Path -Path ([IO.Path]::Combine($path, $vmName, 'Virtual Machines', '*.VMCX')))
   538  {
   539    $vm = Hyper-V\Get-VM -Name $vmName
   540    $vm_adapter = Hyper-V\Get-VMNetworkAdapter -VM $vm | Select -First 1
   541  
   542    $config = [xml]@"
   543  <?xml version="1.0" ?>
   544  <configuration>
   545    <properties>
   546      <subtype type="integer">$($vm.Generation - 1)</subtype>
   547      <name type="string">$($vm.Name)</name>
   548    </properties>
   549    <settings>
   550      <processors>
   551        <count type="integer">$($vm.ProcessorCount)</count>
   552      </processors>
   553      <memory>
   554        <bank>
   555          <dynamic_memory_enabled type="bool">$($vm.DynamicMemoryEnabled)</dynamic_memory_enabled>
   556          <limit type="integer">$($vm.MemoryMaximum / 1MB)</limit>
   557          <reservation type="integer">$($vm.MemoryMinimum / 1MB)</reservation>
   558          <size type="integer">$($vm.MemoryStartup / 1MB)</size>
   559        </bank>
   560      </memory>
   561    </settings>
   562    <AltSwitchName type="string">$($vm_adapter.SwitchName)</AltSwitchName>
   563    <boot>
   564      <device0 type="string">Optical</device0>
   565    </boot>
   566    <secure_boot_enabled type="bool">False</secure_boot_enabled>
   567    <notes type="string">$($vm.Notes)</notes>
   568    <vm-controllers/>
   569  </configuration>
   570  "@
   571  
   572    if ($vm.Generation -eq 1)
   573    {
   574      $vm_controllers  = Hyper-V\Get-VMIdeController -VM $vm
   575      $controller_type = $config.SelectSingleNode('/configuration/vm-controllers')
   576      # IDE controllers are not stored in a special XML container
   577    }
   578    else
   579    {
   580      $vm_controllers  = Hyper-V\Get-VMScsiController -VM $vm
   581      $controller_type = $config.CreateElement('scsi')
   582      $controller_type.SetAttribute('ChannelInstanceGuid', 'x')
   583      # SCSI controllers are stored in the scsi XML container
   584      if ((Hyper-V\Get-VMFirmware -VM $vm).SecureBoot -eq [Microsoft.HyperV.PowerShell.OnOffState]::On)
   585      {
   586        $config.configuration.secure_boot_enabled.'#text' = 'True'
   587      }
   588      else
   589      {
   590        $config.configuration.secure_boot_enabled.'#text' = 'False'
   591      }
   592    }
   593  
   594    $vm_controllers | ForEach {
   595      $controller = $config.CreateElement('controller' + $_.ControllerNumber)
   596      $_.Drives | ForEach {
   597        $drive = $config.CreateElement('drive' + ($_.DiskNumber + 0))
   598        $drive_path = $config.CreateElement('pathname')
   599        $drive_path.SetAttribute('type', 'string')
   600        $drive_path.AppendChild($config.CreateTextNode($_.Path))
   601        $drive_type = $config.CreateElement('type')
   602        $drive_type.SetAttribute('type', 'string')
   603        if ($_ -is [Microsoft.HyperV.PowerShell.HardDiskDrive])
   604        {
   605          $drive_type.AppendChild($config.CreateTextNode('VHD'))
   606        }
   607        elseif ($_ -is [Microsoft.HyperV.PowerShell.DvdDrive])
   608        {
   609          $drive_type.AppendChild($config.CreateTextNode('ISO'))
   610        }
   611        else
   612        {
   613          $drive_type.AppendChild($config.CreateTextNode('NONE'))
   614        }
   615        $drive.AppendChild($drive_path)
   616        $drive.AppendChild($drive_type)
   617        $controller.AppendChild($drive)
   618      }
   619      $controller_type.AppendChild($controller)
   620    }
   621    if ($controller_type.Name -ne 'vm-controllers')
   622    {
   623      $config.SelectSingleNode('/configuration/vm-controllers').AppendChild($controller_type)
   624    }
   625  
   626    $config.Save([IO.Path]::Combine($path, $vm.Name, 'Virtual Machines', 'box.xml'))
   627  }
   628  `
   629  
   630  	var ps powershell.PowerShellCmd
   631  	err := ps.Run(script, vmName, path)
   632  	return err
   633  }
   634  
   635  func CompactDisks(expPath string, vhdDir string) error {
   636  	var script = `
   637  param([string]$srcPath, [string]$vhdDirName)
   638  Get-ChildItem "$srcPath/$vhdDirName" -Filter *.vhd* | %{
   639      Optimize-VHD -Path $_.FullName -Mode Full
   640  }
   641  `
   642  
   643  	var ps powershell.PowerShellCmd
   644  	err := ps.Run(script, expPath, vhdDir)
   645  	return err
   646  }
   647  
   648  func CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error {
   649  
   650  	var script = `
   651  param([string]$srcPath, [string]$dstPath, [string]$vhdDirName, [string]$vmDir)
   652  Move-Item -Path $srcPath/*.* -Destination $dstPath
   653  Move-Item -Path $srcPath/$vhdDirName -Destination $dstPath
   654  Move-Item -Path $srcPath/$vmDir -Destination $dstPath
   655  `
   656  
   657  	var ps powershell.PowerShellCmd
   658  	err := ps.Run(script, expPath, outputPath, vhdDir, vmDir)
   659  	return err
   660  }
   661  
   662  func CreateVirtualSwitch(switchName string, switchType string) (bool, error) {
   663  
   664  	var script = `
   665  param([string]$switchName,[string]$switchType)
   666  $switches = Hyper-V\Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
   667  if ($switches.Count -eq 0) {
   668    Hyper-V\New-VMSwitch -Name $switchName -SwitchType $switchType
   669    return $true
   670  }
   671  return $false
   672  `
   673  
   674  	var ps powershell.PowerShellCmd
   675  	cmdOut, err := ps.Output(script, switchName, switchType)
   676  	var created = strings.TrimSpace(cmdOut) == "True"
   677  	return created, err
   678  }
   679  
   680  func DeleteVirtualSwitch(switchName string) error {
   681  
   682  	var script = `
   683  param([string]$switchName)
   684  $switch = Hyper-V\Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
   685  if ($switch -ne $null) {
   686      $switch | Hyper-V\Remove-VMSwitch -Force -Confirm:$false
   687  }
   688  `
   689  
   690  	var ps powershell.PowerShellCmd
   691  	err := ps.Run(script, switchName)
   692  	return err
   693  }
   694  
   695  func StartVirtualMachine(vmName string) error {
   696  
   697  	var script = `
   698  param([string]$vmName)
   699  $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
   700  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off) {
   701    Hyper-V\Start-VM -Name $vmName -Confirm:$false
   702  }
   703  `
   704  
   705  	var ps powershell.PowerShellCmd
   706  	err := ps.Run(script, vmName)
   707  	return err
   708  }
   709  
   710  func RestartVirtualMachine(vmName string) error {
   711  
   712  	var script = `
   713  param([string]$vmName)
   714  Hyper-V\Restart-VM $vmName -Force -Confirm:$false
   715  `
   716  
   717  	var ps powershell.PowerShellCmd
   718  	err := ps.Run(script, vmName)
   719  	return err
   720  }
   721  
   722  func StopVirtualMachine(vmName string) error {
   723  
   724  	var script = `
   725  param([string]$vmName)
   726  $vm = Hyper-V\Get-VM -Name $vmName
   727  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
   728      Hyper-V\Stop-VM -VM $vm -Force -Confirm:$false
   729  }
   730  `
   731  
   732  	var ps powershell.PowerShellCmd
   733  	err := ps.Run(script, vmName)
   734  	return err
   735  }
   736  
   737  func EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error {
   738  
   739  	integrationServiceId := ""
   740  	switch integrationServiceName {
   741  	case "Time Synchronization":
   742  		integrationServiceId = "2497F4DE-E9FA-4204-80E4-4B75C46419C0"
   743  	case "Heartbeat":
   744  		integrationServiceId = "84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47"
   745  	case "Key-Value Pair Exchange":
   746  		integrationServiceId = "2A34B1C2-FD73-4043-8A5B-DD2159BC743F"
   747  	case "Shutdown":
   748  		integrationServiceId = "9F8233AC-BE49-4C79-8EE3-E7E1985B2077"
   749  	case "VSS":
   750  		integrationServiceId = "5CED1297-4598-4915-A5FC-AD21BB4D02A4"
   751  	case "Guest Service Interface":
   752  		integrationServiceId = "6C09BB55-D683-4DA0-8931-C9BF705F6480"
   753  	default:
   754  		panic("unrecognized Integration Service Name")
   755  	}
   756  
   757  	var script = `
   758  param([string]$vmName,[string]$integrationServiceId)
   759  Hyper-V\Get-VMIntegrationService -VmName $vmName | ?{$_.Id -match $integrationServiceId} | Hyper-V\Enable-VMIntegrationService
   760  `
   761  
   762  	var ps powershell.PowerShellCmd
   763  	err := ps.Run(script, vmName, integrationServiceId)
   764  	return err
   765  }
   766  
   767  func SetNetworkAdapterVlanId(switchName string, vlanId string) error {
   768  
   769  	var script = `
   770  param([string]$networkAdapterName,[string]$vlanId)
   771  Hyper-V\Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $networkAdapterName -Access -VlanId $vlanId
   772  `
   773  
   774  	var ps powershell.PowerShellCmd
   775  	err := ps.Run(script, switchName, vlanId)
   776  	return err
   777  }
   778  
   779  func SetVirtualMachineVlanId(vmName string, vlanId string) error {
   780  
   781  	var script = `
   782  param([string]$vmName,[string]$vlanId)
   783  Hyper-V\Set-VMNetworkAdapterVlan -VMName $vmName -Access -VlanId $vlanId
   784  `
   785  	var ps powershell.PowerShellCmd
   786  	err := ps.Run(script, vmName, vlanId)
   787  	return err
   788  }
   789  
   790  func GetExternalOnlineVirtualSwitch() (string, error) {
   791  
   792  	var script = `
   793  $adapters = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Up' } | Sort-Object -Descending -Property Speed
   794  foreach ($adapter in $adapters) {
   795    $switch = Hyper-V\Get-VMSwitch -SwitchType External | Where-Object { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
   796  
   797    if ($switch -ne $null) {
   798      $switch.Name
   799      break
   800    }
   801  }
   802  `
   803  
   804  	var ps powershell.PowerShellCmd
   805  	cmdOut, err := ps.Output(script)
   806  	if err != nil {
   807  		return "", err
   808  	}
   809  
   810  	var switchName = strings.TrimSpace(cmdOut)
   811  	return switchName, nil
   812  }
   813  
   814  func CreateExternalVirtualSwitch(vmName string, switchName string) error {
   815  
   816  	var script = `
   817  param([string]$vmName,[string]$switchName)
   818  $switch = $null
   819  $names = @('ethernet','wi-fi','lan')
   820  $adapters = foreach ($name in $names) {
   821    Get-NetAdapter -Physical -Name $name -ErrorAction SilentlyContinue | where status -eq 'up'
   822  }
   823  
   824  foreach ($adapter in $adapters) {
   825    $switch = Hyper-V\Get-VMSwitch -SwitchType External | where { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
   826  
   827    if ($switch -eq $null) {
   828      $switch = Hyper-V\New-VMSwitch -Name $switchName -NetAdapterName $adapter.Name -AllowManagementOS $true -Notes 'Parent OS, VMs, WiFi'
   829    }
   830  
   831    if ($switch -ne $null) {
   832      break
   833    }
   834  }
   835  
   836  if($switch -ne $null) {
   837    Hyper-V\Get-VMNetworkAdapter -VMName $vmName | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $switch
   838  } else {
   839    Write-Error 'No internet adapters found'
   840  }
   841  `
   842  	var ps powershell.PowerShellCmd
   843  	err := ps.Run(script, vmName, switchName)
   844  	return err
   845  }
   846  
   847  func GetVirtualMachineSwitchName(vmName string) (string, error) {
   848  
   849  	var script = `
   850  param([string]$vmName)
   851  (Hyper-V\Get-VMNetworkAdapter -VMName $vmName).SwitchName
   852  `
   853  
   854  	var ps powershell.PowerShellCmd
   855  	cmdOut, err := ps.Output(script, vmName)
   856  	if err != nil {
   857  		return "", err
   858  	}
   859  
   860  	return strings.TrimSpace(cmdOut), nil
   861  }
   862  
   863  func ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error {
   864  
   865  	var script = `
   866  param([string]$vmName,[string]$switchName)
   867  Hyper-V\Get-VMNetworkAdapter -VMName $vmName | Hyper-V\Connect-VMNetworkAdapter -SwitchName $switchName
   868  `
   869  
   870  	var ps powershell.PowerShellCmd
   871  	err := ps.Run(script, vmName, switchName)
   872  	return err
   873  }
   874  
   875  func AddVirtualMachineHardDiskDrive(vmName string, vhdRoot string, vhdName string, vhdSizeBytes int64, controllerType string) error {
   876  
   877  	var script = `
   878  param([string]$vmName,[string]$vhdRoot, [string]$vhdName, [string]$vhdSizeInBytes, [string]$controllerType)
   879  $vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdName
   880  New-VHD $vhdPath -SizeBytes $vhdSizeInBytes
   881  Hyper-V\Add-VMHardDiskDrive -VMName $vmName -path $vhdPath -controllerType $controllerType
   882  `
   883  	var ps powershell.PowerShellCmd
   884  	err := ps.Run(script, vmName, vhdRoot, vhdName, strconv.FormatInt(vhdSizeBytes, 10), controllerType)
   885  	return err
   886  }
   887  
   888  func UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error {
   889  
   890  	var script = `
   891  param([string]$vmName,[string]$switchName)
   892  Hyper-V\Set-VMNetworkAdapterVlan -VMName $vmName -Untagged
   893  Hyper-V\Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $switchName -Untagged
   894  `
   895  
   896  	var ps powershell.PowerShellCmd
   897  	err := ps.Run(script, vmName, switchName)
   898  	return err
   899  }
   900  
   901  func IsRunning(vmName string) (bool, error) {
   902  
   903  	var script = `
   904  param([string]$vmName)
   905  $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
   906  $vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
   907  `
   908  
   909  	var ps powershell.PowerShellCmd
   910  	cmdOut, err := ps.Output(script, vmName)
   911  
   912  	if err != nil {
   913  		return false, err
   914  	}
   915  
   916  	var isRunning = strings.TrimSpace(cmdOut) == "True"
   917  	return isRunning, err
   918  }
   919  
   920  func IsOff(vmName string) (bool, error) {
   921  
   922  	var script = `
   923  param([string]$vmName)
   924  $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
   925  $vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off
   926  `
   927  
   928  	var ps powershell.PowerShellCmd
   929  	cmdOut, err := ps.Output(script, vmName)
   930  
   931  	if err != nil {
   932  		return false, err
   933  	}
   934  
   935  	var isRunning = strings.TrimSpace(cmdOut) == "True"
   936  	return isRunning, err
   937  }
   938  
   939  func Uptime(vmName string) (uint64, error) {
   940  
   941  	var script = `
   942  param([string]$vmName)
   943  $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
   944  $vm.Uptime.TotalSeconds
   945  `
   946  	var ps powershell.PowerShellCmd
   947  	cmdOut, err := ps.Output(script, vmName)
   948  
   949  	if err != nil {
   950  		return 0, err
   951  	}
   952  
   953  	uptime, err := strconv.ParseUint(strings.TrimSpace(cmdOut), 10, 64)
   954  
   955  	return uptime, err
   956  }
   957  
   958  func Mac(vmName string) (string, error) {
   959  	var script = `
   960  param([string]$vmName, [int]$adapterIndex)
   961  try {
   962    $adapter = Hyper-V\Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
   963    $mac = $adapter[$adapterIndex].MacAddress
   964    if($mac -eq $null) {
   965      return ""
   966    }
   967  } catch {
   968    return ""
   969  }
   970  $mac
   971  `
   972  
   973  	var ps powershell.PowerShellCmd
   974  	cmdOut, err := ps.Output(script, vmName, "0")
   975  
   976  	return cmdOut, err
   977  }
   978  
   979  func IpAddress(mac string) (string, error) {
   980  	var script = `
   981  param([string]$mac, [int]$addressIndex)
   982  try {
   983    $ip = Hyper-V\Get-Vm | %{$_.NetworkAdapters} | ?{$_.MacAddress -eq $mac} | %{$_.IpAddresses[$addressIndex]}
   984  
   985    if($ip -eq $null) {
   986      return ""
   987    }
   988  } catch {
   989    return ""
   990  }
   991  $ip
   992  `
   993  
   994  	var ps powershell.PowerShellCmd
   995  	cmdOut, err := ps.Output(script, mac, "0")
   996  
   997  	return cmdOut, err
   998  }
   999  
  1000  func TurnOff(vmName string) error {
  1001  
  1002  	var script = `
  1003  param([string]$vmName)
  1004  $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
  1005  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
  1006    Hyper-V\Stop-VM -Name $vmName -TurnOff -Force -Confirm:$false
  1007  }
  1008  `
  1009  
  1010  	var ps powershell.PowerShellCmd
  1011  	err := ps.Run(script, vmName)
  1012  	return err
  1013  }
  1014  
  1015  func ShutDown(vmName string) error {
  1016  
  1017  	var script = `
  1018  param([string]$vmName)
  1019  $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
  1020  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
  1021    Hyper-V\Stop-VM -Name $vmName -Force -Confirm:$false
  1022  }
  1023  `
  1024  
  1025  	var ps powershell.PowerShellCmd
  1026  	err := ps.Run(script, vmName)
  1027  	return err
  1028  }
  1029  
  1030  func TypeScanCodes(vmName string, scanCodes string) error {
  1031  	if len(scanCodes) == 0 {
  1032  		return nil
  1033  	}
  1034  
  1035  	var script = `
  1036  param([string]$vmName, [string]$scanCodes)
  1037  	#Requires -Version 3
  1038  
  1039  	function Hyper-V\Get-VMConsole
  1040  	{
  1041  	    [CmdletBinding()]
  1042  	    param (
  1043  	        [Parameter(Mandatory)]
  1044  	        [string] $VMName
  1045  	    )
  1046  
  1047  	    $ErrorActionPreference = "Stop"
  1048  
  1049  	    $vm = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName Msvm_ComputerSystem -ErrorAction Ignore -Verbose:$false | where ElementName -eq $VMName | select -first 1
  1050  	    if ($vm -eq $null){
  1051  	        Write-Error ("VirtualMachine({0}) is not found!" -f $VMName)
  1052  	    }
  1053  
  1054  	    $vmKeyboard = $vm | Get-CimAssociatedInstance -ResultClassName "Msvm_Keyboard" -ErrorAction Ignore -Verbose:$false
  1055  
  1056  		if ($vmKeyboard -eq $null) {
  1057  			$vmKeyboard = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName Msvm_Keyboard -ErrorAction Ignore -Verbose:$false | where SystemName -eq $vm.Name | select -first 1
  1058  		}
  1059  
  1060  		if ($vmKeyboard -eq $null) {
  1061  			$vmKeyboard = Get-CimInstance -Namespace "root\virtualization" -ClassName Msvm_Keyboard -ErrorAction Ignore -Verbose:$false | where SystemName -eq $vm.Name | select -first 1
  1062  		}
  1063  
  1064  	    if ($vmKeyboard -eq $null){
  1065  	        Write-Error ("VirtualMachine({0}) keyboard class is not found!" -f $VMName)
  1066  	    }
  1067  
  1068  	    #TODO: It may be better using New-Module -AsCustomObject to return console object?
  1069  
  1070  	    #Console object to return
  1071  	    $console = [pscustomobject] @{
  1072  	        Msvm_ComputerSystem = $vm
  1073  	        Msvm_Keyboard = $vmKeyboard
  1074  	    }
  1075  
  1076  	    #Need to import assembly to use System.Windows.Input.Key
  1077  	    Add-Type -AssemblyName WindowsBase
  1078  
  1079  	    #region Add Console Members
  1080  	    $console | Add-Member -MemberType ScriptMethod -Name TypeText -Value {
  1081  	        [OutputType([bool])]
  1082  	        param (
  1083  	            [ValidateNotNullOrEmpty()]
  1084  	            [Parameter(Mandatory)]
  1085  	            [string] $AsciiText
  1086  	        )
  1087  	        $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeText" -Arguments @{ asciiText = $AsciiText }
  1088  	        return (0 -eq $result.ReturnValue)
  1089  	    }
  1090  
  1091  	    #Define method:TypeCtrlAltDel
  1092  	    $console | Add-Member -MemberType ScriptMethod -Name TypeCtrlAltDel -Value {
  1093  	        $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeCtrlAltDel"
  1094  	        return (0 -eq $result.ReturnValue)
  1095  	    }
  1096  
  1097  	    #Define method:TypeKey
  1098  	    $console | Add-Member -MemberType ScriptMethod -Name TypeKey -Value {
  1099  	        [OutputType([bool])]
  1100  	        param (
  1101  	            [Parameter(Mandatory)]
  1102  	            [Windows.Input.Key] $Key,
  1103  	            [Windows.Input.ModifierKeys] $ModifierKey = [Windows.Input.ModifierKeys]::None
  1104  	        )
  1105  
  1106  	        $keyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey($Key)
  1107  
  1108  	        switch ($ModifierKey)
  1109  	        {
  1110  	            ([Windows.Input.ModifierKeys]::Control){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftCtrl)}
  1111  	            ([Windows.Input.ModifierKeys]::Alt){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftAlt)}
  1112  	            ([Windows.Input.ModifierKeys]::Shift){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftShift)}
  1113  	            ([Windows.Input.ModifierKeys]::Windows){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LWin)}
  1114  	        }
  1115  
  1116  	        if ($ModifierKey -eq [Windows.Input.ModifierKeys]::None)
  1117  	        {
  1118  	            $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeKey" -Arguments @{ keyCode = $keyCode }
  1119  	        }
  1120  	        else
  1121  	        {
  1122  	            $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "PressKey" -Arguments @{ keyCode = $modifierKeyCode }
  1123  	            $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeKey" -Arguments @{ keyCode = $keyCode }
  1124  	            $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "ReleaseKey" -Arguments @{ keyCode = $modifierKeyCode }
  1125  	        }
  1126  	        $result = return (0 -eq $result.ReturnValue)
  1127  	    }
  1128  
  1129  	    #Define method:Scancodes
  1130  	    $console | Add-Member -MemberType ScriptMethod -Name TypeScancodes -Value {
  1131  	        [OutputType([bool])]
  1132  	        param (
  1133  	            [Parameter(Mandatory)]
  1134  	            [byte[]] $ScanCodes
  1135  	        )
  1136  	        $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeScancodes" -Arguments @{ ScanCodes = $ScanCodes }
  1137  	        return (0 -eq $result.ReturnValue)
  1138  	    }
  1139  
  1140  	    #Define method:ExecCommand
  1141  	    $console | Add-Member -MemberType ScriptMethod -Name ExecCommand -Value {
  1142  	        param (
  1143  	            [Parameter(Mandatory)]
  1144  	            [string] $Command
  1145  	        )
  1146  	        if ([String]::IsNullOrEmpty($Command)){
  1147  	            return
  1148  	        }
  1149  
  1150  	        $console.TypeText($Command) > $null
  1151  	        $console.TypeKey([Windows.Input.Key]::Enter) > $null
  1152  	        #sleep -Milliseconds 100
  1153  	    }
  1154  
  1155  	    #Define method:Dispose
  1156  	    $console | Add-Member -MemberType ScriptMethod -Name Dispose -Value {
  1157  	        $this.Msvm_ComputerSystem.Dispose()
  1158  	        $this.Msvm_Keyboard.Dispose()
  1159  	    }
  1160  
  1161  
  1162  	    #endregion
  1163  
  1164  	    return $console
  1165  	}
  1166  
  1167  	$vmConsole = Hyper-V\Get-VMConsole -VMName $vmName
  1168  	$scanCodesToSend = ''
  1169  	$scanCodes.Split(' ') | %{
  1170  		$scanCode = $_
  1171  
  1172  		if ($scanCode.StartsWith('wait')){
  1173  			$timeToWait = $scanCode.Substring(4)
  1174  			if (!$timeToWait){
  1175  				$timeToWait = "1"
  1176  			}
  1177  
  1178  			if ($scanCodesToSend){
  1179  				$scanCodesToSendByteArray = [byte[]]@($scanCodesToSend.Split(' ') | %{"0x$_"})
  1180  
  1181                  $scanCodesToSendByteArray | %{
  1182  				    $vmConsole.TypeScancodes($_)
  1183                  }
  1184  			}
  1185  
  1186  			write-host "Special code <wait> found, will sleep $timeToWait second(s) at this point."
  1187  			Start-Sleep -s $timeToWait
  1188  
  1189  			$scanCodesToSend = ''
  1190  		} else {
  1191  			if ($scanCodesToSend){
  1192  				write-host "Sending special code '$scanCodesToSend' '$scanCode'"
  1193  				$scanCodesToSend = "$scanCodesToSend $scanCode"
  1194  			} else {
  1195  				write-host "Sending char '$scanCode'"
  1196  				$scanCodesToSend = "$scanCode"
  1197  			}
  1198  		}
  1199  	}
  1200  	if ($scanCodesToSend){
  1201  		$scanCodesToSendByteArray = [byte[]]@($scanCodesToSend.Split(' ') | %{"0x$_"})
  1202  
  1203          $scanCodesToSendByteArray | %{
  1204  			$vmConsole.TypeScancodes($_)
  1205          }
  1206  	}
  1207  `
  1208  
  1209  	var ps powershell.PowerShellCmd
  1210  	err := ps.Run(script, vmName, scanCodes)
  1211  	return err
  1212  }