github.com/benphegan/packer@v1.0.0-rc1/common/powershell/hyperv/hyperv.go (about)

     1  package hyperv
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/mitchellh/packer/common/powershell"
     9  )
    10  
    11  func GetHostAdapterIpAddressForSwitch(switchName string) (string, error) {
    12  	var script = `
    13  param([string]$switchName, [int]$addressIndex)
    14  
    15  $HostVMAdapter = 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 = 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 = Add-VMDvdDrive -VMName $vmName -path $isoPath -Passthru
    63  $dvdController | 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 = Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
    98  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
    99  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 = Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   111  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
   112  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  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 = Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   134  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
   135  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 = Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation
   147  if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
   148  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  Get-VMDvdDrive -VMName $vmName | 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  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  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, ram int64, diskSize int64, switchName string, generation uint) error {
   191  
   192  	if generation == 2 {
   193  		var script = `
   194  param([string]$vmName, [string]$path, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation)
   195  $vhdx = $vmName + '.vhdx'
   196  $vhdPath = Join-Path -Path $path -ChildPath $vhdx
   197  New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation
   198  `
   199  		var ps powershell.PowerShellCmd
   200  		err := ps.Run(script, vmName, path, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10))
   201  		return err
   202  	} else {
   203  		var script = `
   204  param([string]$vmName, [string]$path, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName)
   205  $vhdx = $vmName + '.vhdx'
   206  $vhdPath = Join-Path -Path $path -ChildPath $vhdx
   207  New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName
   208  `
   209  		var ps powershell.PowerShellCmd
   210  		err := ps.Run(script, vmName, path, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName)
   211  
   212  		if err != nil {
   213  			return err
   214  		}
   215  
   216  		return DeleteAllDvdDrives(vmName)
   217  	}
   218  }
   219  
   220  func SetVirtualMachineCpuCount(vmName string, cpu uint) error {
   221  
   222  	var script = `
   223  param([string]$vmName, [int]$cpu)
   224  Set-VMProcessor -VMName $vmName -Count $cpu
   225  `
   226  	var ps powershell.PowerShellCmd
   227  	err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10))
   228  	return err
   229  }
   230  
   231  func SetVirtualMachineVirtualizationExtensions(vmName string, enableVirtualizationExtensions bool) error {
   232  
   233  	var script = `
   234  param([string]$vmName, [string]$exposeVirtualizationExtensionsString)
   235  $exposeVirtualizationExtensions = [System.Boolean]::Parse($exposeVirtualizationExtensionsString)
   236  Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $exposeVirtualizationExtensions
   237  `
   238  	exposeVirtualizationExtensionsString := "False"
   239  	if enableVirtualizationExtensions {
   240  		exposeVirtualizationExtensionsString = "True"
   241  	}
   242  	var ps powershell.PowerShellCmd
   243  	err := ps.Run(script, vmName, exposeVirtualizationExtensionsString)
   244  	return err
   245  }
   246  
   247  func SetVirtualMachineDynamicMemory(vmName string, enableDynamicMemory bool) error {
   248  
   249  	var script = `
   250  param([string]$vmName, [string]$enableDynamicMemoryString)
   251  $enableDynamicMemory = [System.Boolean]::Parse($enableDynamicMemoryString)
   252  Set-VMMemory -VMName $vmName -DynamicMemoryEnabled $enableDynamicMemory
   253  `
   254  	enableDynamicMemoryString := "False"
   255  	if enableDynamicMemory {
   256  		enableDynamicMemoryString = "True"
   257  	}
   258  	var ps powershell.PowerShellCmd
   259  	err := ps.Run(script, vmName, enableDynamicMemoryString)
   260  	return err
   261  }
   262  
   263  func SetVirtualMachineMacSpoofing(vmName string, enableMacSpoofing bool) error {
   264  	var script = `
   265  param([string]$vmName, $enableMacSpoofing)
   266  Set-VMNetworkAdapter -VMName $vmName -MacAddressSpoofing $enableMacSpoofing
   267  `
   268  
   269  	var ps powershell.PowerShellCmd
   270  
   271  	enableMacSpoofingString := "Off"
   272  	if enableMacSpoofing {
   273  		enableMacSpoofingString = "On"
   274  	}
   275  
   276  	err := ps.Run(script, vmName, enableMacSpoofingString)
   277  	return err
   278  }
   279  
   280  func SetVirtualMachineSecureBoot(vmName string, enableSecureBoot bool) error {
   281  	var script = `
   282  param([string]$vmName, $enableSecureBoot)
   283  Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBoot
   284  `
   285  
   286  	var ps powershell.PowerShellCmd
   287  
   288  	enableSecureBootString := "Off"
   289  	if enableSecureBoot {
   290  		enableSecureBootString = "On"
   291  	}
   292  
   293  	err := ps.Run(script, vmName, enableSecureBootString)
   294  	return err
   295  }
   296  
   297  func DeleteVirtualMachine(vmName string) error {
   298  
   299  	var script = `
   300  param([string]$vmName)
   301  
   302  $vm = Get-VM -Name $vmName
   303  if (($vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::Off) -and ($vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::OffCritical)) {
   304      Stop-VM -VM $vm -TurnOff -Force -Confirm:$false
   305  }
   306  
   307  Remove-VM -Name $vmName -Force -Confirm:$false
   308  `
   309  
   310  	var ps powershell.PowerShellCmd
   311  	err := ps.Run(script, vmName)
   312  	return err
   313  }
   314  
   315  func ExportVirtualMachine(vmName string, path string) error {
   316  
   317  	var script = `
   318  param([string]$vmName, [string]$path)
   319  Export-VM -Name $vmName -Path $path
   320  
   321  if (Test-Path -Path ([IO.Path]::Combine($path, $vmName, 'Virtual Machines', '*.VMCX')))
   322  {
   323    $vm = Get-VM -Name $vmName
   324    $vm_adapter = Get-VMNetworkAdapter -VM $vm | Select -First 1
   325  
   326    $config = [xml]@"
   327  <?xml version="1.0" ?>
   328  <configuration>
   329    <properties>
   330      <subtype type="integer">$($vm.Generation - 1)</subtype>
   331      <name type="string">$($vm.Name)</name>
   332    </properties>
   333    <settings>
   334      <processors>
   335        <count type="integer">$($vm.ProcessorCount)</count>
   336      </processors>
   337      <memory>
   338        <bank>
   339          <dynamic_memory_enabled type="bool">$($vm.DynamicMemoryEnabled)</dynamic_memory_enabled>
   340          <limit type="integer">$($vm.MemoryMaximum / 1MB)</limit>
   341          <reservation type="integer">$($vm.MemoryMinimum / 1MB)</reservation>
   342          <size type="integer">$($vm.MemoryStartup / 1MB)</size>
   343        </bank>
   344      </memory>
   345    </settings>
   346    <AltSwitchName type="string">$($vm_adapter.SwitchName)</AltSwitchName>
   347    <boot>
   348      <device0 type="string">Optical</device0>
   349    </boot>
   350    <secure_boot_enabled type="bool">False</secure_boot_enabled>
   351    <notes type="string">$($vm.Notes)</notes>
   352    <vm-controllers/>
   353  </configuration>
   354  "@
   355  
   356    if ($vm.Generation -eq 1)
   357    {
   358      $vm_controllers  = Get-VMIdeController -VM $vm
   359      $controller_type = $config.SelectSingleNode('/configuration/vm-controllers')
   360      # IDE controllers are not stored in a special XML container
   361    }
   362    else
   363    {
   364      $vm_controllers  = Get-VMScsiController -VM $vm
   365      $controller_type = $config.CreateElement('scsi')
   366      $controller_type.SetAttribute('ChannelInstanceGuid', 'x')
   367      # SCSI controllers are stored in the scsi XML container
   368      if ((Get-VMFirmware -VM $vm).SecureBoot -eq [Microsoft.HyperV.PowerShell.OnOffState]::On)
   369      {
   370        $config.configuration.secure_boot_enabled.'#text' = 'True'
   371      }
   372      else
   373      {
   374        $config.configuration.secure_boot_enabled.'#text' = 'False'
   375      }
   376    }
   377  
   378    $vm_controllers | ForEach {
   379      $controller = $config.CreateElement('controller' + $_.ControllerNumber)
   380      $_.Drives | ForEach {
   381        $drive = $config.CreateElement('drive' + ($_.DiskNumber + 0))
   382        $drive_path = $config.CreateElement('pathname')
   383        $drive_path.SetAttribute('type', 'string')
   384        $drive_path.AppendChild($config.CreateTextNode($_.Path))
   385        $drive_type = $config.CreateElement('type')
   386        $drive_type.SetAttribute('type', 'string')
   387        if ($_ -is [Microsoft.HyperV.PowerShell.HardDiskDrive])
   388        {
   389          $drive_type.AppendChild($config.CreateTextNode('VHD'))
   390        }
   391        elseif ($_ -is [Microsoft.HyperV.PowerShell.DvdDrive])
   392        {
   393          $drive_type.AppendChild($config.CreateTextNode('ISO'))
   394        }
   395        else
   396        {
   397          $drive_type.AppendChild($config.CreateTextNode('NONE'))
   398        }
   399        $drive.AppendChild($drive_path)
   400        $drive.AppendChild($drive_type)
   401        $controller.AppendChild($drive)
   402      }
   403      $controller_type.AppendChild($controller)
   404    }
   405    if ($controller_type.Name -ne 'vm-controllers')
   406    {
   407      $config.SelectSingleNode('/configuration/vm-controllers').AppendChild($controller_type)
   408    }
   409  
   410    $config.Save([IO.Path]::Combine($path, $vm.Name, 'Virtual Machines', 'box.xml'))
   411  }
   412  `
   413  
   414  	var ps powershell.PowerShellCmd
   415  	err := ps.Run(script, vmName, path)
   416  	return err
   417  }
   418  
   419  func CompactDisks(expPath string, vhdDir string) error {
   420  	var script = `
   421  param([string]$srcPath, [string]$vhdDirName)
   422  Get-ChildItem "$srcPath/$vhdDirName" -Filter *.vhd* | %{
   423      Optimize-VHD -Path $_.FullName -Mode Full
   424  }
   425  `
   426  
   427  	var ps powershell.PowerShellCmd
   428  	err := ps.Run(script, expPath, vhdDir)
   429  	return err
   430  }
   431  
   432  func CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error {
   433  
   434  	var script = `
   435  param([string]$srcPath, [string]$dstPath, [string]$vhdDirName, [string]$vmDir)
   436  Move-Item -Path $srcPath/*.* -Destination $dstPath
   437  Move-Item -Path $srcPath/$vhdDirName -Destination $dstPath
   438  Move-Item -Path $srcPath/$vmDir -Destination $dstPath
   439  `
   440  
   441  	var ps powershell.PowerShellCmd
   442  	err := ps.Run(script, expPath, outputPath, vhdDir, vmDir)
   443  	return err
   444  }
   445  
   446  func CreateVirtualSwitch(switchName string, switchType string) (bool, error) {
   447  
   448  	var script = `
   449  param([string]$switchName,[string]$switchType)
   450  $switches = Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
   451  if ($switches.Count -eq 0) {
   452    New-VMSwitch -Name $switchName -SwitchType $switchType
   453    return $true
   454  }
   455  return $false
   456  `
   457  
   458  	var ps powershell.PowerShellCmd
   459  	cmdOut, err := ps.Output(script, switchName, switchType)
   460  	var created = strings.TrimSpace(cmdOut) == "True"
   461  	return created, err
   462  }
   463  
   464  func DeleteVirtualSwitch(switchName string) error {
   465  
   466  	var script = `
   467  param([string]$switchName)
   468  $switch = Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue
   469  if ($switch -ne $null) {
   470      $switch | Remove-VMSwitch -Force -Confirm:$false
   471  }
   472  `
   473  
   474  	var ps powershell.PowerShellCmd
   475  	err := ps.Run(script, switchName)
   476  	return err
   477  }
   478  
   479  func StartVirtualMachine(vmName string) error {
   480  
   481  	var script = `
   482  param([string]$vmName)
   483  $vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
   484  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off) {
   485    Start-VM -Name $vmName -Confirm:$false
   486  }
   487  `
   488  
   489  	var ps powershell.PowerShellCmd
   490  	err := ps.Run(script, vmName)
   491  	return err
   492  }
   493  
   494  func RestartVirtualMachine(vmName string) error {
   495  
   496  	var script = `
   497  param([string]$vmName)
   498  Restart-VM $vmName -Force -Confirm:$false
   499  `
   500  
   501  	var ps powershell.PowerShellCmd
   502  	err := ps.Run(script, vmName)
   503  	return err
   504  }
   505  
   506  func StopVirtualMachine(vmName string) error {
   507  
   508  	var script = `
   509  param([string]$vmName)
   510  $vm = Get-VM -Name $vmName
   511  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
   512      Stop-VM -VM $vm -Force -Confirm:$false
   513  }
   514  `
   515  
   516  	var ps powershell.PowerShellCmd
   517  	err := ps.Run(script, vmName)
   518  	return err
   519  }
   520  
   521  func EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error {
   522  
   523  	integrationServiceId := ""
   524  	switch integrationServiceName {
   525  	case "Time Synchronization":
   526  		integrationServiceId = "2497F4DE-E9FA-4204-80E4-4B75C46419C0"
   527  	case "Heartbeat":
   528  		integrationServiceId = "84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47"
   529  	case "Key-Value Pair Exchange":
   530  		integrationServiceId = "2A34B1C2-FD73-4043-8A5B-DD2159BC743F"
   531  	case "Shutdown":
   532  		integrationServiceId = "9F8233AC-BE49-4C79-8EE3-E7E1985B2077"
   533  	case "VSS":
   534  		integrationServiceId = "5CED1297-4598-4915-A5FC-AD21BB4D02A4"
   535  	case "Guest Service Interface":
   536  		integrationServiceId = "6C09BB55-D683-4DA0-8931-C9BF705F6480"
   537  	default:
   538  		panic("unrecognized Integration Service Name")
   539  	}
   540  
   541  	var script = `
   542  param([string]$vmName,[string]$integrationServiceId)
   543  Get-VMIntegrationService -VmName $vmName | ?{$_.Id -match $integrationServiceId} | Enable-VMIntegrationService
   544  `
   545  
   546  	var ps powershell.PowerShellCmd
   547  	err := ps.Run(script, vmName, integrationServiceId)
   548  	return err
   549  }
   550  
   551  func SetNetworkAdapterVlanId(switchName string, vlanId string) error {
   552  
   553  	var script = `
   554  param([string]$networkAdapterName,[string]$vlanId)
   555  Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $networkAdapterName -Access -VlanId $vlanId
   556  `
   557  
   558  	var ps powershell.PowerShellCmd
   559  	err := ps.Run(script, switchName, vlanId)
   560  	return err
   561  }
   562  
   563  func SetVirtualMachineVlanId(vmName string, vlanId string) error {
   564  
   565  	var script = `
   566  param([string]$vmName,[string]$vlanId)
   567  Set-VMNetworkAdapterVlan -VMName $vmName -Access -VlanId $vlanId
   568  `
   569  	var ps powershell.PowerShellCmd
   570  	err := ps.Run(script, vmName, vlanId)
   571  	return err
   572  }
   573  
   574  func GetExternalOnlineVirtualSwitch() (string, error) {
   575  
   576  	var script = `
   577  $adapters = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Up' } | Sort-Object -Descending -Property Speed
   578  foreach ($adapter in $adapters) { 
   579    $switch = Get-VMSwitch -SwitchType External | Where-Object { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
   580  
   581    if ($switch -ne $null) {
   582      $switch.Name
   583      break
   584    }
   585  }
   586  `
   587  
   588  	var ps powershell.PowerShellCmd
   589  	cmdOut, err := ps.Output(script)
   590  	if err != nil {
   591  		return "", err
   592  	}
   593  
   594  	var switchName = strings.TrimSpace(cmdOut)
   595  	return switchName, nil
   596  }
   597  
   598  func CreateExternalVirtualSwitch(vmName string, switchName string) error {
   599  
   600  	var script = `
   601  param([string]$vmName,[string]$switchName)
   602  $switch = $null
   603  $names = @('ethernet','wi-fi','lan')
   604  $adapters = foreach ($name in $names) {
   605    Get-NetAdapter -Physical -Name $name -ErrorAction SilentlyContinue | where status -eq 'up'
   606  }
   607  
   608  foreach ($adapter in $adapters) { 
   609    $switch = Get-VMSwitch -SwitchType External | where { $_.NetAdapterInterfaceDescription -eq $adapter.InterfaceDescription }
   610  
   611    if ($switch -eq $null) { 
   612      $switch = New-VMSwitch -Name $switchName -NetAdapterName $adapter.Name -AllowManagementOS $true -Notes 'Parent OS, VMs, WiFi'
   613    }
   614  
   615    if ($switch -ne $null) {
   616      break
   617    }
   618  }
   619  
   620  if($switch -ne $null) { 
   621    Get-VMNetworkAdapter -VMName $vmName | Connect-VMNetworkAdapter -VMSwitch $switch 
   622  } else { 
   623    Write-Error 'No internet adapters found'
   624  }
   625  `
   626  	var ps powershell.PowerShellCmd
   627  	err := ps.Run(script, vmName, switchName)
   628  	return err
   629  }
   630  
   631  func GetVirtualMachineSwitchName(vmName string) (string, error) {
   632  
   633  	var script = `
   634  param([string]$vmName)
   635  (Get-VMNetworkAdapter -VMName $vmName).SwitchName
   636  `
   637  
   638  	var ps powershell.PowerShellCmd
   639  	cmdOut, err := ps.Output(script, vmName)
   640  	if err != nil {
   641  		return "", err
   642  	}
   643  
   644  	return strings.TrimSpace(cmdOut), nil
   645  }
   646  
   647  func ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error {
   648  
   649  	var script = `
   650  param([string]$vmName,[string]$switchName)
   651  Get-VMNetworkAdapter -VMName $vmName | Connect-VMNetworkAdapter -SwitchName $switchName
   652  `
   653  
   654  	var ps powershell.PowerShellCmd
   655  	err := ps.Run(script, vmName, switchName)
   656  	return err
   657  }
   658  
   659  func UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error {
   660  
   661  	var script = `
   662  param([string]$vmName,[string]$switchName)
   663  Set-VMNetworkAdapterVlan -VMName $vmName -Untagged
   664  Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $switchName -Untagged
   665  `
   666  
   667  	var ps powershell.PowerShellCmd
   668  	err := ps.Run(script, vmName, switchName)
   669  	return err
   670  }
   671  
   672  func IsRunning(vmName string) (bool, error) {
   673  
   674  	var script = `
   675  param([string]$vmName)
   676  $vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
   677  $vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
   678  `
   679  
   680  	var ps powershell.PowerShellCmd
   681  	cmdOut, err := ps.Output(script, vmName)
   682  
   683  	if err != nil {
   684  		return false, err
   685  	}
   686  
   687  	var isRunning = strings.TrimSpace(cmdOut) == "True"
   688  	return isRunning, err
   689  }
   690  
   691  func IsOff(vmName string) (bool, error) {
   692  
   693  	var script = `
   694  param([string]$vmName)
   695  $vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
   696  $vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off
   697  `
   698  
   699  	var ps powershell.PowerShellCmd
   700  	cmdOut, err := ps.Output(script, vmName)
   701  
   702  	if err != nil {
   703  		return false, err
   704  	}
   705  
   706  	var isRunning = strings.TrimSpace(cmdOut) == "True"
   707  	return isRunning, err
   708  }
   709  
   710  func Uptime(vmName string) (uint64, error) {
   711  
   712  	var script = `
   713  param([string]$vmName)
   714  $vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
   715  $vm.Uptime.TotalSeconds
   716  `
   717  	var ps powershell.PowerShellCmd
   718  	cmdOut, err := ps.Output(script, vmName)
   719  
   720  	if err != nil {
   721  		return 0, err
   722  	}
   723  
   724  	uptime, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 64)
   725  
   726  	return uptime, err
   727  }
   728  
   729  func Mac(vmName string) (string, error) {
   730  	var script = `
   731  param([string]$vmName, [int]$adapterIndex)
   732  try {
   733    $adapter = Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue
   734    $mac = $adapter[$adapterIndex].MacAddress
   735    if($mac -eq $null) {
   736      return ""
   737    }
   738  } catch {
   739    return ""
   740  }
   741  $mac
   742  `
   743  
   744  	var ps powershell.PowerShellCmd
   745  	cmdOut, err := ps.Output(script, vmName, "0")
   746  
   747  	return cmdOut, err
   748  }
   749  
   750  func IpAddress(mac string) (string, error) {
   751  	var script = `
   752  param([string]$mac, [int]$addressIndex)
   753  try {
   754    $ip = Get-Vm | %{$_.NetworkAdapters} | ?{$_.MacAddress -eq $mac} | %{$_.IpAddresses[$addressIndex]}
   755  	
   756    if($ip -eq $null) {
   757      return ""
   758    }
   759  } catch {
   760    return ""
   761  }
   762  $ip
   763  `
   764  
   765  	var ps powershell.PowerShellCmd
   766  	cmdOut, err := ps.Output(script, mac, "0")
   767  
   768  	return cmdOut, err
   769  }
   770  
   771  func TurnOff(vmName string) error {
   772  
   773  	var script = `
   774  param([string]$vmName)
   775  $vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
   776  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
   777    Stop-VM -Name $vmName -TurnOff -Force -Confirm:$false
   778  }
   779  `
   780  
   781  	var ps powershell.PowerShellCmd
   782  	err := ps.Run(script, vmName)
   783  	return err
   784  }
   785  
   786  func ShutDown(vmName string) error {
   787  
   788  	var script = `
   789  param([string]$vmName)
   790  $vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
   791  if ($vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running) {
   792    Stop-VM -Name $vmName -Force -Confirm:$false
   793  }
   794  `
   795  
   796  	var ps powershell.PowerShellCmd
   797  	err := ps.Run(script, vmName)
   798  	return err
   799  }
   800  
   801  func TypeScanCodes(vmName string, scanCodes string) error {
   802  	if len(scanCodes) == 0 {
   803  		return nil
   804  	}
   805  
   806  	var script = `
   807  param([string]$vmName, [string]$scanCodes)
   808  	#Requires -Version 3
   809  	#Requires -RunAsAdministrator
   810  	
   811  	function Get-VMConsole
   812  	{
   813  	    [CmdletBinding()]
   814  	    param (
   815  	        [Parameter(Mandatory)]
   816  	        [string] $VMName
   817  	    )
   818  	
   819  	    $ErrorActionPreference = "Stop"
   820  	    
   821  	    $vm = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName Msvm_ComputerSystem -ErrorAction Ignore -Verbose:$false | where ElementName -eq $VMName | select -first 1
   822  	    if ($vm -eq $null){
   823  	        Write-Error ("VirtualMachine({0}) is not found!" -f $VMName)
   824  	    }
   825  		
   826  	    $vmKeyboard = $vm | Get-CimAssociatedInstance -ResultClassName "Msvm_Keyboard" -ErrorAction Ignore -Verbose:$false
   827  		
   828  		if ($vmKeyboard -eq $null) {
   829  			$vmKeyboard = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName Msvm_Keyboard -ErrorAction Ignore -Verbose:$false | where SystemName -eq $vm.Name | select -first 1
   830  		}
   831  
   832  		if ($vmKeyboard -eq $null) {
   833  			$vmKeyboard = Get-CimInstance -Namespace "root\virtualization" -ClassName Msvm_Keyboard -ErrorAction Ignore -Verbose:$false | where SystemName -eq $vm.Name | select -first 1
   834  		}
   835  		
   836  	    if ($vmKeyboard -eq $null){
   837  	        Write-Error ("VirtualMachine({0}) keyboard class is not found!" -f $VMName)
   838  	    }
   839  	
   840  	    #TODO: It may be better using New-Module -AsCustomObject to return console object?
   841  	
   842  	    #Console object to return
   843  	    $console = [pscustomobject] @{
   844  	        Msvm_ComputerSystem = $vm
   845  	        Msvm_Keyboard = $vmKeyboard
   846  	    }
   847  	
   848  	    #Need to import assembly to use System.Windows.Input.Key
   849  	    Add-Type -AssemblyName WindowsBase
   850  	
   851  	    #region Add Console Members
   852  	    $console | Add-Member -MemberType ScriptMethod -Name TypeText -Value {
   853  	        [OutputType([bool])]
   854  	        param (
   855  	            [ValidateNotNullOrEmpty()]
   856  	            [Parameter(Mandatory)]
   857  	            [string] $AsciiText
   858  	        )
   859  	        $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeText" -Arguments @{ asciiText = $AsciiText }
   860  	        return (0 -eq $result.ReturnValue)
   861  	    }
   862  	
   863  	    #Define method:TypeCtrlAltDel
   864  	    $console | Add-Member -MemberType ScriptMethod -Name TypeCtrlAltDel -Value {
   865  	        $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeCtrlAltDel"
   866  	        return (0 -eq $result.ReturnValue)
   867  	    }
   868  	
   869  	    #Define method:TypeKey
   870  	    $console | Add-Member -MemberType ScriptMethod -Name TypeKey -Value {
   871  	        [OutputType([bool])]
   872  	        param (
   873  	            [Parameter(Mandatory)]
   874  	            [Windows.Input.Key] $Key,
   875  	            [Windows.Input.ModifierKeys] $ModifierKey = [Windows.Input.ModifierKeys]::None
   876  	        )
   877  	
   878  	        $keyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey($Key)
   879  	        
   880  	        switch ($ModifierKey)
   881  	        {
   882  	            ([Windows.Input.ModifierKeys]::Control){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftCtrl)}
   883  	            ([Windows.Input.ModifierKeys]::Alt){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftAlt)}
   884  	            ([Windows.Input.ModifierKeys]::Shift){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LeftShift)}
   885  	            ([Windows.Input.ModifierKeys]::Windows){ $modifierKeyCode = [Windows.Input.KeyInterop]::VirtualKeyFromKey([Windows.Input.Key]::LWin)}
   886  	        }
   887  	
   888  	        if ($ModifierKey -eq [Windows.Input.ModifierKeys]::None)
   889  	        {
   890  	            $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeKey" -Arguments @{ keyCode = $keyCode }
   891  	        }
   892  	        else
   893  	        {
   894  	            $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "PressKey" -Arguments @{ keyCode = $modifierKeyCode }
   895  	            $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeKey" -Arguments @{ keyCode = $keyCode }
   896  	            $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "ReleaseKey" -Arguments @{ keyCode = $modifierKeyCode }
   897  	        }
   898  	        $result = return (0 -eq $result.ReturnValue)
   899  	    }
   900  	
   901  	    #Define method:Scancodes
   902  	    $console | Add-Member -MemberType ScriptMethod -Name TypeScancodes -Value {
   903  	        [OutputType([bool])]
   904  	        param (
   905  	            [Parameter(Mandatory)]
   906  	            [byte[]] $ScanCodes
   907  	        )
   908  	        $result = $this.Msvm_Keyboard | Invoke-CimMethod -MethodName "TypeScancodes" -Arguments @{ ScanCodes = $ScanCodes }
   909  	        return (0 -eq $result.ReturnValue)
   910  	    }
   911  	
   912  	    #Define method:ExecCommand
   913  	    $console | Add-Member -MemberType ScriptMethod -Name ExecCommand -Value {
   914  	        param (
   915  	            [Parameter(Mandatory)]
   916  	            [string] $Command
   917  	        )
   918  	        if ([String]::IsNullOrEmpty($Command)){
   919  	            return
   920  	        }
   921  	
   922  	        $console.TypeText($Command) > $null
   923  	        $console.TypeKey([Windows.Input.Key]::Enter) > $null
   924  	        #sleep -Milliseconds 100
   925  	    }
   926  	
   927  	    #Define method:Dispose
   928  	    $console | Add-Member -MemberType ScriptMethod -Name Dispose -Value {
   929  	        $this.Msvm_ComputerSystem.Dispose()
   930  	        $this.Msvm_Keyboard.Dispose()
   931  	    }
   932  	    
   933  	
   934  	    #endregion
   935  	
   936  	    return $console
   937  	}
   938  	
   939  	$vmConsole = Get-VMConsole -VMName $vmName
   940  	$scanCodesToSend = ''
   941  	$scanCodes.Split(' ') | %{
   942  		$scanCode = $_
   943  			
   944  		if ($scanCode.StartsWith('wait')){
   945  			$timeToWait = $scanCode.Substring(4)
   946  			if (!$timeToWait){
   947  				$timeToWait = "1"
   948  			}
   949  						
   950  			if ($scanCodesToSend){
   951  				$scanCodesToSendByteArray = [byte[]]@($scanCodesToSend.Split(' ') | %{"0x$_"})
   952  				
   953                  $scanCodesToSendByteArray | %{
   954  				    $vmConsole.TypeScancodes($_)
   955                  }
   956  			}
   957  			
   958  			write-host "Special code <wait> found, will sleep $timeToWait second(s) at this point."
   959  			Start-Sleep -s $timeToWait
   960  			
   961  			$scanCodesToSend = ''
   962  		} else {
   963  			if ($scanCodesToSend){
   964  				write-host "Sending special code '$scanCodesToSend' '$scanCode'"
   965  				$scanCodesToSend = "$scanCodesToSend $scanCode"
   966  			} else {
   967  				write-host "Sending char '$scanCode'"
   968  				$scanCodesToSend = "$scanCode"
   969  			}
   970  		}
   971  	}
   972  	if ($scanCodesToSend){
   973  		$scanCodesToSendByteArray = [byte[]]@($scanCodesToSend.Split(' ') | %{"0x$_"})
   974  		
   975          $scanCodesToSendByteArray | %{
   976  			$vmConsole.TypeScancodes($_)
   977          }
   978  	}
   979  `
   980  
   981  	var ps powershell.PowerShellCmd
   982  	err := ps.Run(script, vmName, scanCodes)
   983  	return err
   984  }