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