diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 8cfe67582..35df38cb3 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -72,9 +72,15 @@ type Driver interface { DeleteVirtualMachine(string) error - SetVirtualMachineCpu(string, uint, bool) error + SetVirtualMachineCpuCount(string, uint) error - SetSecureBoot(string, bool) error + SetVirtualMachineMacSpoofing(string, bool) error + + SetVirtualMachineDynamicMemory(string, bool) error + + SetVirtualMachineSecureBoot(string, bool) error + + SetVirtualMachineVirtualizationExtensions(string, bool) error EnableVirtualMachineIntegrationService(string, string) error diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index df1355c59..1fa522034 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -6,12 +6,13 @@ package common import ( "fmt" - "github.com/mitchellh/packer/powershell" - "github.com/mitchellh/packer/powershell/hyperv" "log" "runtime" "strconv" "strings" + + "github.com/mitchellh/packer/powershell" + "github.com/mitchellh/packer/powershell/hyperv" ) type HypervPS4Driver struct { @@ -177,12 +178,24 @@ func (d *HypervPS4Driver) DeleteVirtualMachine(vmName string) error { return hyperv.DeleteVirtualMachine(vmName) } -func (d *HypervPS4Driver) SetVirtualMachineCpu(vmName string, cpu uint, exposeVirtualizationExtensions bool) error { - return hyperv.SetVirtualMachineCpu(vmName, cpu, exposeVirtualizationExtensions) +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) error { + return hyperv.SetVirtualMachineSecureBoot(vmName, enable) } -func (d *HypervPS4Driver) SetSecureBoot(vmName string, enable bool) error { - return hyperv.SetSecureBoot(vmName, enable) +func (d *HypervPS4Driver) SetVirtualMachineVirtualizationExtensions(vmName string, enable bool) error { + return hyperv.SetVirtualMachineVirtualizationExtensions(vmName, enable) } func (d *HypervPS4Driver) EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error { diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index c03636f30..54a5a7103 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -6,6 +6,7 @@ package common import ( "fmt" + "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -15,13 +16,15 @@ import ( // Produces: // VMName string - The name of the VM type StepCreateVM struct { - VMName string - SwitchName string - RamSizeMB uint - DiskSize uint - Generation uint - Cpu uint - EnableSecureBoot bool + VMName string + SwitchName string + RamSizeMB uint + DiskSize uint + Generation uint + Cpu uint + EnableMacSpoofing bool + EnableDynamicMemory bool + EnableSecureBoot bool EnableVirtualizationExtensions bool } @@ -36,10 +39,7 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { ram := int64(s.RamSizeMB * 1024 * 1024) diskSize := int64(s.DiskSize * 1024 * 1024) - switchName := s.SwitchName - enableSecureBoot := s.EnableSecureBoot - - err := driver.CreateVirtualMachine(s.VMName, path, ram, diskSize, switchName, s.Generation) + err := driver.CreateVirtualMachine(s.VMName, path, ram, diskSize, s.SwitchName, s.Generation) if err != nil { err := fmt.Errorf("Error creating virtual machine: %s", err) state.Put("error", err) @@ -47,7 +47,7 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - err = driver.SetVirtualMachineCpu(s.VMName, s.Cpu, s.EnableVirtualizationExtensions) + err = driver.SetVirtualMachineCpuCount(s.VMName, s.Cpu) if err != nil { err := fmt.Errorf("Error creating setting virtual machine cpu: %s", err) state.Put("error", err) @@ -55,8 +55,28 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + if s.EnableDynamicMemory { + err = driver.SetVirtualMachineDynamicMemory(s.VMName, s.EnableDynamicMemory) + if err != nil { + err := fmt.Errorf("Error creating setting virtual machine dynamic memory: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + if s.EnableMacSpoofing { + err = driver.SetVirtualMachineMacSpoofing(s.VMName, s.EnableMacSpoofing) + if err != nil { + err := fmt.Errorf("Error creating setting virtual machine mac spoofing: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + if s.Generation == 2 { - err = driver.SetSecureBoot(s.VMName, enableSecureBoot) + err = driver.SetVirtualMachineSecureBoot(s.VMName, s.EnableSecureBoot) if err != nil { err := fmt.Errorf("Error setting secure boot: %s", err) state.Put("error", err) @@ -65,6 +85,17 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { } } + if s.EnableVirtualizationExtensions { + //This is only supported on Windows 10 and Windows Server 2016 onwards + err = driver.SetVirtualMachineVirtualizationExtensions(s.VMName, s.EnableVirtualizationExtensions) + if err != nil { + err := fmt.Errorf("Error creating setting virtual machine virtualization extensions: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + // Set the final name in the state bag so others can use it state.Put("vmName", s.VMName) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 274dca2ea..0772e24ba 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -28,9 +28,10 @@ const ( MinDiskSize = 256 // 256MB MaxDiskSize = 64 * 1024 * 1024 // 64TB - DefaultRamSize = 1 * 1024 // 1GB - MinRamSize = 32 // 32MB - MaxRamSize = 32 * 1024 // 32GB + DefaultRamSize = 1 * 1024 // 1GB + MinRamSize = 32 // 32MB + MaxRamSize = 32 * 1024 // 32GB + MinNestedVirtualizationRamSize = 4 * 1024 // 4GB LowRam = 256 // 256MB @@ -84,12 +85,14 @@ type Config struct { // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. VMName string `mapstructure:"vm_name"` - BootCommand []string `mapstructure:"boot_command"` - SwitchName string `mapstructure:"switch_name"` - Cpu uint `mapstructure:"cpu"` - Generation uint `mapstructure:"generation"` - EnableSecureBoot bool `mapstructure:"enable_secure_boot"` - EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"` + BootCommand []string `mapstructure:"boot_command"` + SwitchName string `mapstructure:"switch_name"` + Cpu uint `mapstructure:"cpu"` + Generation uint `mapstructure:"generation"` + EnableMacSpoofing bool `mapstructure:"enable_mac_spoofing"` + EnableDynamicMemory bool `mapstructure:"enable_dynamic_memory"` + EnableSecureBoot bool `mapstructure:"enable_secure_boot"` + EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"` Communicator string `mapstructure:"communicator"` @@ -227,6 +230,17 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } + if b.config.EnableVirtualizationExtensions { + hasVirtualMachineVirtualizationExtensions, err := powershell.HasVirtualMachineVirtualizationExtensions() + if err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine virtualization extensions support: %s", err)) + } else { + if !hasVirtualMachineVirtualizationExtensions { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("This version of Hyper-V does not support virtual machine virtualization extension. Please use Windows 10 or Windows Server 2016 or newer.")) + } + } + } + // Warnings if b.config.ShutdownCommand == "" { @@ -240,6 +254,23 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { warnings = appendWarnings(warnings, warning) } + if b.config.EnableVirtualizationExtensions { + if b.config.EnableDynamicMemory { + warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, dynamic memory should not be allowed.") + warnings = appendWarnings(warnings, warning) + } + + if !b.config.EnableMacSpoofing { + warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, mac spoofing should be allowed.") + warnings = appendWarnings(warnings, warning) + } + + if b.config.RamSizeMB < MinNestedVirtualizationRamSize { + warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start any nested VMs.") + warnings = appendWarnings(warnings, warning) + } + } + if errs != nil && len(errs.Errors) > 0 { return warnings, errs } @@ -292,13 +323,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchName: b.config.SwitchName, }, &hypervcommon.StepCreateVM{ - VMName: b.config.VMName, - SwitchName: b.config.SwitchName, - RamSizeMB: b.config.RamSizeMB, - DiskSize: b.config.DiskSize, - Generation: b.config.Generation, - Cpu: b.config.Cpu, - EnableSecureBoot: b.config.EnableSecureBoot, + VMName: b.config.VMName, + SwitchName: b.config.SwitchName, + RamSizeMB: b.config.RamSizeMB, + DiskSize: b.config.DiskSize, + Generation: b.config.Generation, + Cpu: b.config.Cpu, + EnableMacSpoofing: b.config.EnableMacSpoofing, + EnableDynamicMemory: b.config.EnableDynamicMemory, + EnableSecureBoot: b.config.EnableSecureBoot, EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions, }, &hypervcommon.StepEnableIntegrationService{}, diff --git a/powershell/hyperv/hyperv.go b/powershell/hyperv/hyperv.go index f242dfa3c..67e26216a 100644 --- a/powershell/hyperv/hyperv.go +++ b/powershell/hyperv/hyperv.go @@ -217,23 +217,67 @@ New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHD } } -func SetVirtualMachineCpu(vmName string, cpu uint, enableVirtualizationExtensions bool) error { +func SetVirtualMachineCpuCount(vmName string, cpu uint) error { var script = ` -param([string]$vmName, [int]$cpu, [string]$exposeVirtualizationExtensions) -$nested = [System.Boolean]::Parse($exposeVirtualizationExtensions) -Set-VMProcessor -VMName $vmName -Count $cpu -exposeVirtualizationExtensions $nested +param([string]$vmName, [int]$cpu) +Set-VMProcessor -VMName $vmName -Count $cpu +` + var ps powershell.PowerShellCmd + err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10)) + return err +} + +func SetVirtualMachineVirtualizationExtensions(vmName string, enableVirtualizationExtensions bool) error { + + var script = ` +param([string]$vmName, [string]$exposeVirtualizationExtensionsString) +$exposeVirtualizationExtensions = [System.Boolean]::Parse($exposeVirtualizationExtensionsString) +Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $exposeVirtualizationExtensions ` exposeVirtualizationExtensionsString := "False" if enableVirtualizationExtensions { exposeVirtualizationExtensionsString = "True" - } + } + var ps powershell.PowerShellCmd + err := ps.Run(script, vmName, exposeVirtualizationExtensionsString) + return err +} + +func SetVirtualMachineDynamicMemory(vmName string, enableDynamicMemory bool) error { + + var script = ` +param([string]$vmName, [string]$enableDynamicMemoryString) +$enableDynamicMemory = [System.Boolean]::Parse($enableDynamicMemoryString) +Set-VMMemory -VMName $vmName -DynamicMemoryEnabled $enableDynamicMemory +` + enableDynamicMemoryString := "False" + if enableDynamicMemory { + enableDynamicMemoryString = "True" + } var ps powershell.PowerShellCmd - err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10), exposeVirtualizationExtensionsString) + err := ps.Run(script, vmName, enableDynamicMemoryString) + return err +} + +func SetVirtualMachineMacSpoofing(vmName string, enableMacSpoofing bool) error { + var script = ` +param([string]$vmName, $enableMacSpoofing) +Set-VMNetworkAdapter -VMName $vmName -MacAddressSpoofing $enableMacSpoofing +` + + var ps powershell.PowerShellCmd + + enableMacSpoofingString := "Off" + if enableMacSpoofing { + enableMacSpoofingString = "On" + } + + err := ps.Run(script, vmName, enableMacSpoofingString) return err } -func SetSecureBoot(vmName string, enable bool) error { +func SetVirtualMachineSecureBoot(vmName string, enableSecureBoot bool) error { var script = ` param([string]$vmName, $enableSecureBoot) Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBoot @@ -241,12 +285,12 @@ Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBoot var ps powershell.PowerShellCmd - enableSecureBoot := "Off" - if enable { - enableSecureBoot = "On" + enableSecureBootString := "Off" + if enableSecureBoot { + enableSecureBootString = "On" } - err := ps.Run(script, vmName, enableSecureBoot) + err := ps.Run(script, vmName, enableSecureBootString) return err } diff --git a/powershell/powershell.go b/powershell/powershell.go index 044ec6384..93ba91150 100644 --- a/powershell/powershell.go +++ b/powershell/powershell.go @@ -231,6 +231,23 @@ param([string]$moduleName) return true, nil } +func HasVirtualMachineVirtualizationExtensions() (bool, error) { + + var script = ` +(GET-Command Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions" +` + + var ps PowerShellCmd + cmdOut, err := ps.Output(script) + + if err != nil { + return false, err + } + + var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True" + return hasVirtualMachineVirtualizationExtensions, err +} + func SetUnattendedProductKey(path string, productKey string) error { var script = `