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