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