package common import ( "context" "fmt" "log" "runtime" "strconv" "strings" "github.com/hashicorp/packer/common/powershell" "github.com/hashicorp/packer/common/powershell/hyperv" ) type HypervPS4Driver struct { } func NewHypervPS4Driver() (Driver, error) { appliesTo := "Applies to Windows 8.1, Windows PowerShell 4.0, Windows Server 2012 R2 only" // Check this is Windows if runtime.GOOS != "windows" { err := fmt.Errorf("%s", appliesTo) return nil, err } ps4Driver := &HypervPS4Driver{} if err := ps4Driver.Verify(); err != nil { return nil, err } return ps4Driver, nil } func (d *HypervPS4Driver) IsRunning(vmName string) (bool, error) { return hyperv.IsRunning(vmName) } func (d *HypervPS4Driver) IsOff(vmName string) (bool, error) { return hyperv.IsOff(vmName) } func (d *HypervPS4Driver) Uptime(vmName string) (uint64, error) { return hyperv.Uptime(vmName) } // Start starts a VM specified by the name given. func (d *HypervPS4Driver) Start(vmName string) error { return hyperv.StartVirtualMachine(vmName) } // Stop stops a VM specified by the name given. func (d *HypervPS4Driver) Stop(vmName string) error { return hyperv.StopVirtualMachine(vmName) } func (d *HypervPS4Driver) Verify() error { if err := d.verifyPSVersion(); err != nil { return err } if err := d.verifyPSHypervModule(); err != nil { return err } if err := d.verifyHypervPermissions(); err != nil { return err } return nil } // Get mac address for VM. func (d *HypervPS4Driver) Mac(vmName string) (string, error) { res, err := hyperv.Mac(vmName) if err != nil { return res, err } if res == "" { err := fmt.Errorf("%s", "No mac address.") return res, err } return res, err } // Get ip address for mac address. func (d *HypervPS4Driver) IpAddress(mac string) (string, error) { res, err := hyperv.IpAddress(mac) if err != nil { return res, err } if res == "" { err := fmt.Errorf("%s", "No ip address.") return res, err } return res, err } // Get host name from ip address func (d *HypervPS4Driver) GetHostName(ip string) (string, error) { return powershell.GetHostName(ip) } func (d *HypervPS4Driver) GetVirtualMachineGeneration(vmName string) (uint, error) { return hyperv.GetVirtualMachineGeneration(vmName) } // Finds the IP address of a host adapter connected to switch func (d *HypervPS4Driver) GetHostAdapterIpAddressForSwitch(switchName string) (string, error) { res, err := hyperv.GetHostAdapterIpAddressForSwitch(switchName) if err != nil { return res, err } if res == "" { err := fmt.Errorf("%s", "No ip address.") return res, err } return res, err } // Type scan codes to virtual keyboard of vm func (d *HypervPS4Driver) TypeScanCodes(vmName string, scanCodes string) error { return hyperv.TypeScanCodes(vmName, scanCodes) } // Get network adapter address func (d *HypervPS4Driver) GetVirtualMachineNetworkAdapterAddress(vmName string) (string, error) { return hyperv.GetVirtualMachineNetworkAdapterAddress(vmName) } //Set the vlan to use for switch func (d *HypervPS4Driver) SetNetworkAdapterVlanId(switchName string, vlanId string) error { return hyperv.SetNetworkAdapterVlanId(switchName, vlanId) } //Set the vlan to use for machine func (d *HypervPS4Driver) SetVirtualMachineVlanId(vmName string, vlanId string) error { return hyperv.SetVirtualMachineVlanId(vmName, vlanId) } func (d *HypervPS4Driver) SetVmNetworkAdapterMacAddress(vmName string, mac string) error { return hyperv.SetVmNetworkAdapterMacAddress(vmName, mac) } func (d *HypervPS4Driver) UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error { return hyperv.UntagVirtualMachineNetworkAdapterVlan(vmName, switchName) } func (d *HypervPS4Driver) CreateExternalVirtualSwitch(vmName string, switchName string) error { return hyperv.CreateExternalVirtualSwitch(vmName, switchName) } func (d *HypervPS4Driver) GetVirtualMachineSwitchName(vmName string) (string, error) { return hyperv.GetVirtualMachineSwitchName(vmName) } func (d *HypervPS4Driver) ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error { return hyperv.ConnectVirtualMachineNetworkAdapterToSwitch(vmName, switchName) } func (d *HypervPS4Driver) DeleteVirtualSwitch(switchName string) error { return hyperv.DeleteVirtualSwitch(switchName) } func (d *HypervPS4Driver) CreateVirtualSwitch(switchName string, switchType string) (bool, error) { return hyperv.CreateVirtualSwitch(switchName, switchType) } func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, diskBlockSize int64, controllerType string) error { return hyperv.AddVirtualMachineHardDiskDrive(vmName, vhdFile, vhdName, vhdSizeBytes, diskBlockSize, controllerType) } func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, fixedVHD bool) error { return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, diskBlockSize, switchName, generation, diffDisks, fixedVHD) } func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error { return hyperv.CloneVirtualMachine(cloneFromVmxcPath, cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName) } func (d *HypervPS4Driver) DeleteVirtualMachine(vmName string) error { return hyperv.DeleteVirtualMachine(vmName) } func (d *HypervPS4Driver) SetVirtualMachineCpuCount(vmName string, cpu uint) error { return hyperv.SetVirtualMachineCpuCount(vmName, cpu) } func (d *HypervPS4Driver) SetVirtualMachineMacSpoofing(vmName string, enable bool) error { return hyperv.SetVirtualMachineMacSpoofing(vmName, enable) } func (d *HypervPS4Driver) SetVirtualMachineDynamicMemory(vmName string, enable bool) error { return hyperv.SetVirtualMachineDynamicMemory(vmName, enable) } func (d *HypervPS4Driver) SetVirtualMachineSecureBoot(vmName string, enable bool, templateName string) error { return hyperv.SetVirtualMachineSecureBoot(vmName, enable, templateName) } func (d *HypervPS4Driver) SetVirtualMachineVirtualizationExtensions(vmName string, enable bool) error { return hyperv.SetVirtualMachineVirtualizationExtensions(vmName, enable) } func (d *HypervPS4Driver) EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error { return hyperv.EnableVirtualMachineIntegrationService(vmName, integrationServiceName) } func (d *HypervPS4Driver) ExportVirtualMachine(vmName string, path string) error { return hyperv.ExportVirtualMachine(vmName, path) } func (d *HypervPS4Driver) CompactDisks(path string) error { return hyperv.CompactDisks(path) } func (d *HypervPS4Driver) RestartVirtualMachine(vmName string) error { return hyperv.RestartVirtualMachine(vmName) } func (d *HypervPS4Driver) CreateDvdDrive(vmName string, isoPath string, generation uint) (uint, uint, error) { return hyperv.CreateDvdDrive(vmName, isoPath, generation) } func (d *HypervPS4Driver) MountDvdDrive(vmName string, path string, controllerNumber uint, controllerLocation uint) error { return hyperv.MountDvdDrive(vmName, path, controllerNumber, controllerLocation) } func (d *HypervPS4Driver) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint, generation uint) error { return hyperv.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, generation) } func (d *HypervPS4Driver) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { return hyperv.UnmountDvdDrive(vmName, controllerNumber, controllerLocation) } func (d *HypervPS4Driver) DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error { return hyperv.DeleteDvdDrive(vmName, controllerNumber, controllerLocation) } func (d *HypervPS4Driver) MountFloppyDrive(vmName string, path string) error { return hyperv.MountFloppyDrive(vmName, path) } func (d *HypervPS4Driver) UnmountFloppyDrive(vmName string) error { return hyperv.UnmountFloppyDrive(vmName) } func (d *HypervPS4Driver) verifyPSVersion() error { log.Printf("Enter method: %s", "verifyPSVersion") // check PS is available and is of proper version versionCmd := "$host.version.Major" var ps powershell.PowerShellCmd cmdOut, err := ps.Output(versionCmd) if err != nil { return err } versionOutput := strings.TrimSpace(cmdOut) log.Printf("%s output: %s", versionCmd, versionOutput) ver, err := strconv.ParseInt(versionOutput, 10, 32) if err != nil { return err } if ver < 4 { err := fmt.Errorf("%s", "Windows PowerShell version 4.0 or higher is expected") return err } return nil } func (d *HypervPS4Driver) verifyPSHypervModule() error { log.Printf("Enter method: %s", "verifyPSHypervModule") versionCmd := "function foo(){try{ $commands = Get-Command -Module Hyper-V;if($commands.Length -eq 0){return $false} }catch{return $false}; return $true} foo" var ps powershell.PowerShellCmd cmdOut, err := ps.Output(versionCmd) if err != nil { return err } if powershell.IsFalse(cmdOut) { err := fmt.Errorf("%s", "PS Hyper-V module is not loaded. Make sure Hyper-V feature is on.") return err } return nil } func (d *HypervPS4Driver) isCurrentUserAHyperVAdministrator() (bool, error) { //SID:S-1-5-32-578 = 'BUILTIN\Hyper-V Administrators' //https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems var script = ` $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = new-object System.Security.Principal.WindowsPrincipal($identity) $hypervrole = [System.Security.Principal.SecurityIdentifier]"S-1-5-32-578" return $principal.IsInRole($hypervrole) ` var ps powershell.PowerShellCmd cmdOut, err := ps.Output(script) if err != nil { return false, err } return powershell.IsTrue(cmdOut), nil } func (d *HypervPS4Driver) verifyHypervPermissions() error { log.Printf("Enter method: %s", "verifyHypervPermissions") hyperVAdmin, err := d.isCurrentUserAHyperVAdministrator() if err != nil { log.Printf("Error discovering if current is is a Hyper-V Admin: %s", err) } if !hyperVAdmin { isAdmin, _ := powershell.IsCurrentUserAnAdministrator() if !isAdmin { err := fmt.Errorf("%s", "Current user is not a member of 'Hyper-V Administrators' or 'Administrators' group") return err } } return nil } // Connect connects to a VM specified by the name given. func (d *HypervPS4Driver) Connect(vmName string) (context.CancelFunc, error) { return hyperv.ConnectVirtualMachine(vmName) } // Disconnect disconnects to a VM specified by calling the context cancel function returned // from Connect. func (d *HypervPS4Driver) Disconnect(cancel context.CancelFunc) { hyperv.DisconnectVirtualMachine(cancel) }