From 1441bd4b8e06d55c1315329314630a7e31f2661a Mon Sep 17 00:00:00 2001 From: remi-espie Date: Wed, 13 Sep 2023 16:47:50 +0200 Subject: [PATCH 1/5] feat(nics): first draft at creating multiple switch for hyperv This is the aggregation of all of remi-espie's work aggregated into one commit. The direction is different but was the starting point. --- .web-docs/components/builder/iso/README.md | 14 +++-- .web-docs/components/builder/vmcx/README.md | 14 +++-- builder/hyperv/common/config.go | 14 +++-- builder/hyperv/common/driver.go | 4 +- builder/hyperv/common/driver_mock.go | 12 ++++- builder/hyperv/common/driver_ps_4.go | 8 +-- .../hyperv/common/powershell/hyperv/hyperv.go | 31 ++++++++--- .../hyperv/common/powershell/powershell.go | 4 +- builder/hyperv/common/step_clone_vm.go | 4 +- builder/hyperv/common/step_create_switch.go | 54 +++++++++++-------- builder/hyperv/common/step_create_vm.go | 4 +- builder/hyperv/iso/builder.go | 7 ++- builder/hyperv/iso/builder.hcl2spec.go | 4 ++ builder/hyperv/vmcx/builder.go | 7 ++- builder/hyperv/vmcx/builder.hcl2spec.go | 4 ++ .../common/CommonConfig-not-required.mdx | 14 +++-- main.go | 3 +- 17 files changed, 143 insertions(+), 59 deletions(-) diff --git a/.web-docs/components/builder/iso/README.md b/.web-docs/components/builder/iso/README.md index cce32d8e..c7185a0c 100644 --- a/.web-docs/components/builder/iso/README.md +++ b/.web-docs/components/builder/iso/README.md @@ -253,18 +253,26 @@ created, must be empty prior to running the builder. By default this is without the file extension. By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. -- `switch_name` (string) - The name of the switch to connect the virtual +- `switch_name` (string) - The name of the main switch to connect the virtual machine to. By default, leaving this value unset will cause Packer to try and determine the switch to use by looking for an external switch that is up and running. +- `switches_names` ([]string) - The name of the switches to connect the virtual + machine to. By default, leaving this value unset will cause Packer to + not use any additional switches. + - `switch_vlan_id` (string) - This is the VLAN of the virtual switch's - network card. By default none is set. If none is set then a VLAN is not + network card. By default, none is set. If none is set then a VLAN is not set on the switch's network card. If this value is set it should match the VLAN specified in by vlan_id. - `mac_address` (string) - This allows a specific MAC address to be used on - the default virtual network card. The MAC address must be a string with + the default main virtual network card. The MAC address must be a string with + no delimiters, for example "0000deadbeef". + +- `mac_addresses` ([]string) - This allows a specific MAC addresses to be used on + the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with no delimiters, for example "0000deadbeef". - `vlan_id` (string) - This is the VLAN of the virtual machine's network diff --git a/.web-docs/components/builder/vmcx/README.md b/.web-docs/components/builder/vmcx/README.md index 36f80bc5..2f1534a9 100644 --- a/.web-docs/components/builder/vmcx/README.md +++ b/.web-docs/components/builder/vmcx/README.md @@ -280,18 +280,26 @@ In HCL2: without the file extension. By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. -- `switch_name` (string) - The name of the switch to connect the virtual +- `switch_name` (string) - The name of the main switch to connect the virtual machine to. By default, leaving this value unset will cause Packer to try and determine the switch to use by looking for an external switch that is up and running. +- `switches_names` ([]string) - The name of the switches to connect the virtual + machine to. By default, leaving this value unset will cause Packer to + not use any additional switches. + - `switch_vlan_id` (string) - This is the VLAN of the virtual switch's - network card. By default none is set. If none is set then a VLAN is not + network card. By default, none is set. If none is set then a VLAN is not set on the switch's network card. If this value is set it should match the VLAN specified in by vlan_id. - `mac_address` (string) - This allows a specific MAC address to be used on - the default virtual network card. The MAC address must be a string with + the default main virtual network card. The MAC address must be a string with + no delimiters, for example "0000deadbeef". + +- `mac_addresses` ([]string) - This allows a specific MAC addresses to be used on + the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with no delimiters, for example "0000deadbeef". - `vlan_id` (string) - This is the VLAN of the virtual machine's network diff --git a/builder/hyperv/common/config.go b/builder/hyperv/common/config.go index 707e24dd..d09b05fd 100644 --- a/builder/hyperv/common/config.go +++ b/builder/hyperv/common/config.go @@ -74,20 +74,28 @@ type CommonConfig struct { // without the file extension. By default this is "packer-BUILDNAME", // where "BUILDNAME" is the name of the build. VMName string `mapstructure:"vm_name" required:"false"` - // The name of the switch to connect the virtual + // The name of the main switch to connect the virtual // machine to. By default, leaving this value unset will cause Packer to // try and determine the switch to use by looking for an external switch // that is up and running. SwitchName string `mapstructure:"switch_name" required:"false"` + // The name of the switches to connect the virtual + // machine to. By default, leaving this value unset will cause Packer to + // not use any additional switches. + SwitchesNames []string `mapstructure:"switches_names" required:"false"` // This is the VLAN of the virtual switch's - // network card. By default none is set. If none is set then a VLAN is not + // network card. By default, none is set. If none is set then a VLAN is not // set on the switch's network card. If this value is set it should match // the VLAN specified in by vlan_id. SwitchVlanId string `mapstructure:"switch_vlan_id" required:"false"` // This allows a specific MAC address to be used on - // the default virtual network card. The MAC address must be a string with + // the default main virtual network card. The MAC address must be a string with // no delimiters, for example "0000deadbeef". MacAddress string `mapstructure:"mac_address" required:"false"` + // This allows a specific MAC addresses to be used on + // the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with + // no delimiters, for example "0000deadbeef". + MacAddresses []string `mapstructure:"mac_addresses" required:"false"` // This is the VLAN of the virtual machine's network // card for the new virtual machine. By default none is set. If none is set // then VLANs are not set on the virtual machine's network card. diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 12d2de3a..44e7b6ec 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -78,11 +78,11 @@ type Driver interface { CheckVMName(string) error - CreateVirtualMachine(string, string, string, int64, int64, int64, string, uint, bool, bool, string) error + CreateVirtualMachine(string, string, string, int64, int64, int64, string, []string, []string, uint, bool, bool, string) error AddVirtualMachineHardDrive(string, string, string, int64, int64, string) error - CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string, bool) error + CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string, []string, []string, bool) error ResizeVirtualMachineVhd(string, uint64) error diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index a7d2cb78..b48126cf 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -139,6 +139,8 @@ type DriverMock struct { CreateVirtualMachine_DiskSize int64 CreateVirtualMachine_DiskBlockSize int64 CreateVirtualMachine_SwitchName string + CreateVirtualMachine_SwitchesNames []string + CreateVirtualMachine_MacAddresses []string CreateVirtualMachine_Generation uint CreateVirtualMachine_DifferentialDisk bool CreateVirtualMachine_FixedVHD bool @@ -155,6 +157,8 @@ type DriverMock struct { CloneVirtualMachine_HarddrivePath string CloneVirtualMachine_Ram int64 CloneVirtualMachine_SwitchName string + CloneVirtualMachine_SwitchesNames []string + CloneVirtualMachine_MacAddresses []string CloneVirtualMachine_Copy bool CloneVirtualMachine_Err error @@ -456,7 +460,7 @@ func (d *DriverMock) CheckVMName(vmName string) error { } func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, - ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, + ram int64, diskSize int64, diskBlockSize int64, switchName string, switchesNames []string, macAddresses []string, generation uint, diffDisks bool, fixedVHD bool, version string) error { d.CreateVirtualMachine_Called = true d.CreateVirtualMachine_VmName = vmName @@ -466,6 +470,8 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP d.CreateVirtualMachine_DiskSize = diskSize d.CreateVirtualMachine_DiskBlockSize = diskBlockSize d.CreateVirtualMachine_SwitchName = switchName + d.CreateVirtualMachine_SwitchesNames = switchesNames + d.CreateVirtualMachine_MacAddresses = macAddresses d.CreateVirtualMachine_Generation = generation d.CreateVirtualMachine_DifferentialDisk = diffDisks d.CreateVirtualMachine_Version = version @@ -474,7 +480,7 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP func (d *DriverMock) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, - harddrivePath string, ram int64, switchName string, copyTF bool) error { + harddrivePath string, ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { d.CloneVirtualMachine_Called = true d.CloneVirtualMachine_CloneFromVmcxPath = cloneFromVmcxPath d.CloneVirtualMachine_CloneFromVmName = cloneFromVmName @@ -485,6 +491,8 @@ func (d *DriverMock) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmNa d.CloneVirtualMachine_HarddrivePath = harddrivePath d.CloneVirtualMachine_Ram = ram d.CloneVirtualMachine_SwitchName = switchName + d.CloneVirtualMachine_SwitchesNames = switchesNames + d.CloneVirtualMachine_MacAddresses = macAddresses d.CloneVirtualMachine_Copy = copyTF return d.CloneVirtualMachine_Err diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index ffef9220..f8778c1f 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -193,17 +193,17 @@ func (d *HypervPS4Driver) CheckVMName(vmName string) error { } func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64, - diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, + diskSize int64, diskBlockSize int64, switchName string, switchesNames []string, macAddresses []string, generation uint, diffDisks bool, fixedVHD bool, version string) error { - return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, ram, diskSize, diskBlockSize, switchName, + return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, ram, diskSize, diskBlockSize, switchName, switchesNames, macAddresses, generation, diffDisks, fixedVHD, version) } func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, - ram int64, switchName string, copyTF bool) error { + ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { return hyperv.CloneVirtualMachine(cloneFromVmcxPath, cloneFromVmName, cloneFromSnapshotName, - cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName, copyTF) + cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName, switchesNames, macAddresses, copyTF) } func (d *HypervPS4Driver) ResizeVirtualMachineVhd(vmName string, newSizeInBytes uint64) error { diff --git a/builder/hyperv/common/powershell/hyperv/hyperv.go b/builder/hyperv/common/powershell/hyperv/hyperv.go index d95fe0d6..ab3473c4 100644 --- a/builder/hyperv/common/powershell/hyperv/hyperv.go +++ b/builder/hyperv/common/powershell/hyperv/hyperv.go @@ -27,6 +27,8 @@ type scriptOptions struct { NewVHDSizeBytes int64 VHDBlockSizeBytes int64 SwitchName string + SwitchesNames []string + MacAddresses []string Generation uint DiffDisks bool FixedVHD bool @@ -358,6 +360,9 @@ $vhdPath = Join-Path -Path "{{ .Path }}" -ChildPath "{{ .VHDX }}" Hyper-V\New-VM -Name "{{ .VMName }}" -Path "{{ .Path }}" -MemoryStartupBytes {{ .MemoryStartupBytes }} -VHDPath $vhdPath -SwitchName "{{ .SwitchName }}" {{- if eq .Generation 2}} -Generation {{ .Generation }} {{- end -}} {{- if ne .Version ""}} -Version {{ .Version }} {{- end -}} +{{ range $i, $switchName := .SwitchesNames }} +Hyper-V\Add-VMNetworkAdapter -VMName "{{ $.VMName }}" -SwitchName "{{ $switchName }}"{{ if gt (len $.MacAddresses) $i }} -StaticMacAddress "{{ index $.MacAddresses $i }}".Replace("-","") {{ end }} +{{- end -}} `)) var b bytes.Buffer @@ -396,7 +401,7 @@ func CheckVMName(vmName string) error { } func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64, - diskSize int64, diskBlockSize int64, switchName string, generation uint, + diskSize int64, diskBlockSize int64, switchName string, switchesNames []string, macAddresses []string, generation uint, diffDisks bool, fixedVHD bool, version string) error { opts := scriptOptions{ Version: version, @@ -407,6 +412,8 @@ func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram NewVHDSizeBytes: diskSize, VHDBlockSizeBytes: diskBlockSize, SwitchName: switchName, + SwitchesNames: switchesNames, + MacAddresses: macAddresses, Generation: generation, DiffDisks: diffDisks, FixedVHD: fixedVHD, @@ -512,7 +519,7 @@ Copy-Item $cloneFromVmcxPath $exportPath -Recurse -Force func SetVmNetworkAdapterMacAddress(vmName string, mac string) error { var script = ` param([string]$vmName, [string]$mac) -Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac +Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac.Replace("-","") ` var ps powershell.PowerShellCmd @@ -522,10 +529,10 @@ Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac } func ImportVmcxVirtualMachine(importPath string, vmName string, harddrivePath string, - ram int64, switchName string, copyTF bool) error { + ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { var script = ` -param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName, [string]$copy) +param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName, [string]$copy, [string[]]$switchesNames, [string[]]$macAddresses) $VirtualHarddisksPath = Join-Path -Path $importPath -ChildPath 'Virtual Hard Disks' if (!(Test-Path $VirtualHarddisksPath)) { @@ -571,21 +578,29 @@ Hyper-V\Set-VMMemory -VM $compatibilityReport.VM -StartupBytes $memoryStartupByt $networkAdaptor = $compatibilityReport.VM.NetworkAdapters | Select -First 1 Hyper-V\Disconnect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switchName +foreach ($switch in $switchesNames) { +if ($macAddresses[$switchesNames.IndexOf($switch)] -ne $null) { + Hyper-V\Add-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switch -StaticMacAddress $macAddresses[$switchesNames.IndexOf($switch)].Replace("-","") +} else { + Hyper-V\Add-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switch +} $vm = Hyper-V\Import-VM -CompatibilityReport $compatibilityReport if ($vm) { $result = Hyper-V\Rename-VM -VM $vm -NewName $VMName } ` + switchesArray := strings.Join(switchesNames, ",") + macAddressesArray := strings.Join(macAddresses, ",") var ps powershell.PowerShellCmd - err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), switchName, strconv.FormatBool(copyTF)) + err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), switchName, strconv.FormatBool(copyTF), switchesArray, macAddressesArray) return err } func CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, - path string, harddrivePath string, ram int64, switchName string, copyTF bool) error { + path string, harddrivePath string, ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { if cloneFromVmName != "" { if err := ExportVmcxVirtualMachine(path, cloneFromVmName, @@ -600,7 +615,7 @@ func CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, } } - if err := ImportVmcxVirtualMachine(path, vmName, harddrivePath, ram, switchName, copyTF); err != nil { + if err := ImportVmcxVirtualMachine(path, vmName, harddrivePath, ram, switchName, switchesNames, macAddresses, copyTF); err != nil { return err } @@ -1035,7 +1050,7 @@ func CreateVirtualSwitch(switchName string, switchType string) (bool, error) { param([string]$switchName,[string]$switchType) $switches = Hyper-V\Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue if ($switches.Count -eq 0) { - Hyper-V\New-VMSwitch -Name $switchName -SwitchType $switchType + $ignoreMe = Hyper-V\New-VMSwitch -Name $switchName -SwitchType $switchType return $true } return $false diff --git a/builder/hyperv/common/powershell/powershell.go b/builder/hyperv/common/powershell/powershell.go index 22fb2bf1..af53ea2f 100644 --- a/builder/hyperv/common/powershell/powershell.go +++ b/builder/hyperv/common/powershell/powershell.go @@ -196,13 +196,15 @@ func createArgs(filename string, params ...string) []string { func GetHostAvailableMemory() float64 { - var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024" + var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory" var ps PowerShellCmd output, _ := ps.Output(script) freeMB, _ := strconv.ParseFloat(output, 64) + freeMB = freeMB / 1024.0 + return freeMB } diff --git a/builder/hyperv/common/step_clone_vm.go b/builder/hyperv/common/step_clone_vm.go index 98421378..f99cd4b8 100644 --- a/builder/hyperv/common/step_clone_vm.go +++ b/builder/hyperv/common/step_clone_vm.go @@ -26,6 +26,7 @@ type StepCloneVM struct { CloneAllSnapshots bool VMName string SwitchName string + SwitchesNames []string CompareCopy bool RamSize uint Cpu uint @@ -39,6 +40,7 @@ type StepCloneVM struct { KeepRegistered bool AdditionalDiskSize []uint DiskBlockSize uint + MacAddresses []string } func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -66,7 +68,7 @@ func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multist err := driver.CloneVirtualMachine(s.CloneFromVMCXPath, s.CloneFromVMName, s.CloneFromSnapshotName, s.CloneAllSnapshots, s.VMName, path, - harddrivePath, ramSize, s.SwitchName, s.CompareCopy) + harddrivePath, ramSize, s.SwitchName, s.SwitchesNames, s.MacAddresses, s.CompareCopy) if err != nil { err := fmt.Errorf("Error cloning virtual machine: %s", err) state.Put("error", err) diff --git a/builder/hyperv/common/step_create_switch.go b/builder/hyperv/common/step_create_switch.go index e8587781..0fb79bd0 100644 --- a/builder/hyperv/common/step_create_switch.go +++ b/builder/hyperv/common/step_create_switch.go @@ -22,9 +22,10 @@ const ( // Produces: // // SwitchName string - The name of the Switch -type StepCreateSwitch struct { +type StepCreateSwitches struct { // Specifies the name of the switch to be created. - SwitchName string + MainSwitchName string + SwitchesNames []string // Specifies the type of the switch to be created. Allowed values are Internal and Private. To create an External // virtual switch, specify either the NetAdapterInterfaceDescription or the NetAdapterName parameter, which // implicitly set the type of the virtual switch to External. @@ -34,10 +35,10 @@ type StepCreateSwitch struct { // Specifies the interface description of the network adapter to be bound to the switch to be created. NetAdapterInterfaceDescription string - createdSwitch bool + createdSwitch []bool } -func (s *StepCreateSwitch) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { +func (s *StepCreateSwitches) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) @@ -45,31 +46,34 @@ func (s *StepCreateSwitch) Run(ctx context.Context, state multistep.StateBag) mu s.SwitchType = DefaultSwitchType } - ui.Say(fmt.Sprintf("Creating switch '%v' if required...", s.SwitchName)) + switches := append([]string{s.MainSwitchName}, s.SwitchesNames...) - createdSwitch, err := driver.CreateVirtualSwitch(s.SwitchName, s.SwitchType) - if err != nil { - err := fmt.Errorf("Error creating switch: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - s.SwitchName = "" - return multistep.ActionHalt - } + for index, switchName := range switches { + ui.Say(fmt.Sprintf("Creating switch '%v' if required...", switchName)) + createdSwitch, err := driver.CreateVirtualSwitch(switchName, s.SwitchType) + if err != nil { + err := fmt.Errorf("Error creating switch: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } - s.createdSwitch = createdSwitch + s.createdSwitch = append(s.createdSwitch, createdSwitch) - if !s.createdSwitch { - ui.Say(fmt.Sprintf(" switch '%v' already exists. Will not delete on cleanup...", s.SwitchName)) + if !s.createdSwitch[index] { + ui.Say(fmt.Sprintf(" switch '%v' already exists. Will not delete on cleanup...", switchName)) + } } // Set the final name in the state bag so others can use it - state.Put("SwitchName", s.SwitchName) + state.Put("SwitchName", s.MainSwitchName) + state.Put("SwitchesNames", s.SwitchesNames) return multistep.ActionContinue } -func (s *StepCreateSwitch) Cleanup(state multistep.StateBag) { - if len(s.SwitchName) == 0 || !s.createdSwitch { +func (s *StepCreateSwitches) Cleanup(state multistep.StateBag) { + if s.MainSwitchName == "" && len(s.SwitchesNames) == 0 { return } @@ -77,8 +81,14 @@ func (s *StepCreateSwitch) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packersdk.Ui) ui.Say("Unregistering and deleting switch...") - err := driver.DeleteVirtualSwitch(s.SwitchName) - if err != nil { - ui.Error(fmt.Sprintf("Error deleting switch: %s", err)) + switches := append([]string{s.MainSwitchName}, s.SwitchesNames...) + + for index, switchName := range switches { + if s.createdSwitch[index] { + err := driver.DeleteVirtualSwitch(switchName) + if err != nil { + ui.Error(fmt.Sprintf("Error deleting switch: %s", err)) + } + } } } diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index 631ff43f..36062965 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -22,6 +22,7 @@ import ( type StepCreateVM struct { VMName string SwitchName string + SwitchesNames []string HarddrivePath string RamSize uint DiskSize uint @@ -38,6 +39,7 @@ type StepCreateVM struct { AdditionalDiskSize []uint DifferencingDisk bool MacAddress string + MacAddresses []string FixedVHD bool Version string KeepRegistered bool @@ -80,7 +82,7 @@ func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis diskBlockSize := int64(s.DiskBlockSize) * 1024 * 1024 err = driver.CreateVirtualMachine(s.VMName, path, harddrivePath, ramSize, diskSize, diskBlockSize, - s.SwitchName, s.Generation, s.DifferencingDisk, s.FixedVHD, s.Version) + s.SwitchName, s.SwitchesNames, s.MacAddresses, s.Generation, s.DifferencingDisk, s.FixedVHD, s.Version) if err != nil { err := fmt.Errorf("Error creating virtual machine: %s", err) state.Put("error", err) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index d3d39ba2..b22976a8 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -223,12 +223,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Label: b.config.FloppyConfig.FloppyLabel, }, commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), - &hypervcommon.StepCreateSwitch{ - SwitchName: b.config.SwitchName, + &hypervcommon.StepCreateSwitches{ + MainSwitchName: b.config.SwitchName, + SwitchesNames: b.config.SwitchesNames, }, &hypervcommon.StepCreateVM{ VMName: b.config.VMName, SwitchName: b.config.SwitchName, + SwitchesNames: b.config.SwitchesNames, + MacAddresses: b.config.MacAddresses, RamSize: b.config.RamSize, DiskSize: b.config.DiskSize, DiskBlockSize: b.config.DiskBlockSize, diff --git a/builder/hyperv/iso/builder.hcl2spec.go b/builder/hyperv/iso/builder.hcl2spec.go index 94473be3..29121f55 100644 --- a/builder/hyperv/iso/builder.hcl2spec.go +++ b/builder/hyperv/iso/builder.hcl2spec.go @@ -97,8 +97,10 @@ type FlatConfig struct { GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"` VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` + SwitchesNames []string `mapstructure:"switches_names" required:"false" cty:"switches_names" hcl:"switches_names"` SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` + MacAddresses []string `mapstructure:"mac_addresses" required:"false" cty:"mac_addresses" hcl:"mac_addresses"` VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` Cpu *uint `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` Generation *uint `mapstructure:"generation" required:"false" cty:"generation" hcl:"generation"` @@ -224,8 +226,10 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "switch_name": &hcldec.AttrSpec{Name: "switch_name", Type: cty.String, Required: false}, + "switches_names": &hcldec.AttrSpec{Name: "switches_names", Type: cty.List(cty.String), Required: false}, "switch_vlan_id": &hcldec.AttrSpec{Name: "switch_vlan_id", Type: cty.String, Required: false}, "mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false}, + "mac_addresses": &hcldec.AttrSpec{Name: "mac_addresses", Type: cty.List(cty.String), Required: false}, "vlan_id": &hcldec.AttrSpec{Name: "vlan_id", Type: cty.String, Required: false}, "cpus": &hcldec.AttrSpec{Name: "cpus", Type: cty.Number, Required: false}, "generation": &hcldec.AttrSpec{Name: "generation", Type: cty.Number, Required: false}, diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index ef7b2529..187d830d 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -264,8 +264,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Label: b.config.FloppyConfig.FloppyLabel, }, commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), - &hypervcommon.StepCreateSwitch{ - SwitchName: b.config.SwitchName, + &hypervcommon.StepCreateSwitches{ + MainSwitchName: b.config.SwitchName, + SwitchesNames: b.config.SwitchesNames, }, &hypervcommon.StepCloneVM{ CloneFromVMCXPath: b.config.CloneFromVMCXPath, @@ -274,6 +275,8 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) CloneAllSnapshots: b.config.CloneAllSnapshots, VMName: b.config.VMName, SwitchName: b.config.SwitchName, + SwitchesNames: b.config.SwitchesNames, + MacAddresses: b.config.MacAddresses, CompareCopy: b.config.CompareCopy, RamSize: b.config.RamSize, Cpu: b.config.Cpu, diff --git a/builder/hyperv/vmcx/builder.hcl2spec.go b/builder/hyperv/vmcx/builder.hcl2spec.go index 30336bbf..5c659ce6 100644 --- a/builder/hyperv/vmcx/builder.hcl2spec.go +++ b/builder/hyperv/vmcx/builder.hcl2spec.go @@ -97,8 +97,10 @@ type FlatConfig struct { GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"` VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` + SwitchesNames []string `mapstructure:"switches_names" required:"false" cty:"switches_names" hcl:"switches_names"` SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` + MacAddresses []string `mapstructure:"mac_addresses" required:"false" cty:"mac_addresses" hcl:"mac_addresses"` VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` Cpu *uint `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` Generation *uint `mapstructure:"generation" required:"false" cty:"generation" hcl:"generation"` @@ -227,8 +229,10 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "switch_name": &hcldec.AttrSpec{Name: "switch_name", Type: cty.String, Required: false}, + "switches_names": &hcldec.AttrSpec{Name: "switches_names", Type: cty.List(cty.String), Required: false}, "switch_vlan_id": &hcldec.AttrSpec{Name: "switch_vlan_id", Type: cty.String, Required: false}, "mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false}, + "mac_addresses": &hcldec.AttrSpec{Name: "mac_addresses", Type: cty.List(cty.String), Required: false}, "vlan_id": &hcldec.AttrSpec{Name: "vlan_id", Type: cty.String, Required: false}, "cpus": &hcldec.AttrSpec{Name: "cpus", Type: cty.Number, Required: false}, "generation": &hcldec.AttrSpec{Name: "generation", Type: cty.Number, Required: false}, diff --git a/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx b/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx index 816be863..79984add 100644 --- a/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx +++ b/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx @@ -32,18 +32,26 @@ without the file extension. By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. -- `switch_name` (string) - The name of the switch to connect the virtual +- `switch_name` (string) - The name of the main switch to connect the virtual machine to. By default, leaving this value unset will cause Packer to try and determine the switch to use by looking for an external switch that is up and running. +- `switches_names` ([]string) - The name of the switches to connect the virtual + machine to. By default, leaving this value unset will cause Packer to + not use any additional switches. + - `switch_vlan_id` (string) - This is the VLAN of the virtual switch's - network card. By default none is set. If none is set then a VLAN is not + network card. By default, none is set. If none is set then a VLAN is not set on the switch's network card. If this value is set it should match the VLAN specified in by vlan_id. - `mac_address` (string) - This allows a specific MAC address to be used on - the default virtual network card. The MAC address must be a string with + the default main virtual network card. The MAC address must be a string with + no delimiters, for example "0000deadbeef". + +- `mac_addresses` ([]string) - This allows a specific MAC addresses to be used on + the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with no delimiters, for example "0000deadbeef". - `vlan_id` (string) - This is the VLAN of the virtual machine's network diff --git a/main.go b/main.go index e5955161..d6b2302d 100644 --- a/main.go +++ b/main.go @@ -7,11 +7,10 @@ import ( "fmt" "os" - "github.com/hashicorp/packer-plugin-sdk/plugin" - "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/iso" "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/vmcx" "github.com/hashicorp/packer-plugin-hyperv/version" + "github.com/hashicorp/packer-plugin-sdk/plugin" ) func main() { From 16fd77e2acb60b15d9870f5e6e37496006462c35 Mon Sep 17 00:00:00 2001 From: Greg Althaus Date: Tue, 19 Mar 2024 10:52:18 -0500 Subject: [PATCH 2/5] fix(wsl): Allow cmd.exe to output stderr and return a value. Signed-off-by: Greg Althaus --- builder/hyperv/common/wsl/wsl.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builder/hyperv/common/wsl/wsl.go b/builder/hyperv/common/wsl/wsl.go index 66972735..d48e81e1 100644 --- a/builder/hyperv/common/wsl/wsl.go +++ b/builder/hyperv/common/wsl/wsl.go @@ -42,18 +42,19 @@ func GetWSlTemp() (string, error) { err := command.Run() stderrString := strings.TrimSpace(stderr.String()) + stdoutString := strings.TrimSpace(stdout.String()) if _, ok := err.(*exec.ExitError); ok { - err = fmt.Errorf("Error getting wsl TEMP dir: %s", stderrString) + err = fmt.Errorf("Error getting wsl TEMP dir (exec): %s", stderrString) return "", err } - if len(stderrString) > 0 { - err = fmt.Errorf("Error getting wsl TEMP dir: %s", stderrString) + if len(stdoutString) == 0 && len(stderrString) > 0 { + err = fmt.Errorf("Error getting wsl TEMP dir (stderr): \"%s\", %s", stdoutString, stderrString) return "", err } - return strings.TrimSpace(stdout.String()), err + return stdoutString, err } func ConvertWindowsPathToWSlPath(winPath string) (string, error) { From c62a0f56e9a78f6fa99618629b4f2afb0100f052 Mon Sep 17 00:00:00 2001 From: Greg Althaus Date: Tue, 19 Mar 2024 11:03:00 -0500 Subject: [PATCH 3/5] fix: Remove depracted ioutil calls Signed-off-by: Greg Althaus --- builder/hyperv/vmcx/builder_test.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/builder/hyperv/vmcx/builder_test.go b/builder/hyperv/vmcx/builder_test.go index 170ed91f..903bf651 100644 --- a/builder/hyperv/vmcx/builder_test.go +++ b/builder/hyperv/vmcx/builder_test.go @@ -6,7 +6,6 @@ package vmcx import ( "context" "fmt" - "io/ioutil" "os" "reflect" "testing" @@ -44,7 +43,7 @@ func TestBuilderPrepare_Defaults(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -69,7 +68,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -106,7 +105,7 @@ func TestBuilderPrepare_ExportedMachinePathDoesNotExist(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -130,7 +129,7 @@ func TestBuilderPrepare_ExportedMachinePathExists(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -178,7 +177,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -213,7 +212,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -258,7 +257,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -337,7 +336,7 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -379,7 +378,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -404,7 +403,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -441,7 +440,7 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -479,7 +478,7 @@ func TestUserVariablesInBootCommand(t *testing.T) { config := testConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } From 90f3b30d121276da8837600dd366a1d2a96870cb Mon Sep 17 00:00:00 2001 From: Greg Althaus Date: Tue, 19 Mar 2024 10:56:01 -0500 Subject: [PATCH 4/5] fix(powershell): Undo the math calc that works Signed-off-by: Greg Althaus --- builder/hyperv/common/powershell/powershell.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/builder/hyperv/common/powershell/powershell.go b/builder/hyperv/common/powershell/powershell.go index af53ea2f..22fb2bf1 100644 --- a/builder/hyperv/common/powershell/powershell.go +++ b/builder/hyperv/common/powershell/powershell.go @@ -196,15 +196,13 @@ func createArgs(filename string, params ...string) []string { func GetHostAvailableMemory() float64 { - var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory" + var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024" var ps PowerShellCmd output, _ := ps.Output(script) freeMB, _ := strconv.ParseFloat(output, 64) - freeMB = freeMB / 1024.0 - return freeMB } From 57a2790aa7e6c023dd272cf9707822f404911b3b Mon Sep 17 00:00:00 2001 From: Greg Althaus Date: Fri, 15 Mar 2024 11:12:40 -0500 Subject: [PATCH 5/5] feat: expand switch / adapter configuration. Signed-off-by: Greg Althaus --- .gitignore | 1 + .web-docs/components/builder/iso/README.md | 15 +- .web-docs/components/builder/vmcx/README.md | 15 +- builder/hyperv/common/adapter_config.go | 35 +++ .../hyperv/common/adapter_config.hcl2spec.go | 37 +++ builder/hyperv/common/config.go | 134 ++++++++-- builder/hyperv/common/driver.go | 21 +- builder/hyperv/common/driver_mock.go | 43 ++-- builder/hyperv/common/driver_ps_4.go | 20 +- .../hyperv/common/powershell/hyperv/hyperv.go | 77 +++--- .../common/powershell/hyperv/hyperv_test.go | 2 +- builder/hyperv/common/step_clone_vm.go | 17 +- .../hyperv/common/step_configure_adapters.go | 85 ++++++ builder/hyperv/common/step_configure_vlan.go | 56 ---- .../common/step_create_external_switch.go | 111 -------- builder/hyperv/common/step_create_switch.go | 59 ++--- builder/hyperv/common/step_create_vm.go | 27 +- builder/hyperv/common/step_create_vm_test.go | 2 + builder/hyperv/common/step_disable_vlan.go | 2 + builder/hyperv/common/step_run.go | 11 +- .../hyperv/common/step_type_boot_command.go | 1 - builder/hyperv/common/switch_config.go | 32 +++ .../hyperv/common/switch_config.hcl2spec.go | 35 +++ builder/hyperv/iso/builder.go | 21 +- builder/hyperv/iso/builder.hcl2spec.go | 237 ++++++++--------- builder/hyperv/iso/builder_test.go | 1 - builder/hyperv/vmcx/builder.go | 25 +- builder/hyperv/vmcx/builder.hcl2spec.go | 243 +++++++++--------- builder/hyperv/vmcx/builder_test.go | 27 +- .../common/AdapterConfig-not-required.mdx | 19 ++ .../common/CommonConfig-not-required.mdx | 15 +- .../common/SwitchConfig-not-required.mdx | 16 ++ 32 files changed, 815 insertions(+), 627 deletions(-) create mode 100644 builder/hyperv/common/adapter_config.go create mode 100644 builder/hyperv/common/adapter_config.hcl2spec.go create mode 100644 builder/hyperv/common/step_configure_adapters.go delete mode 100644 builder/hyperv/common/step_configure_vlan.go delete mode 100644 builder/hyperv/common/step_create_external_switch.go create mode 100644 builder/hyperv/common/switch_config.go create mode 100644 builder/hyperv/common/switch_config.hcl2spec.go create mode 100644 docs-partials/builder/hyperv/common/AdapterConfig-not-required.mdx create mode 100644 docs-partials/builder/hyperv/common/SwitchConfig-not-required.mdx diff --git a/.gitignore b/.gitignore index 158e8a6c..4de540f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ main dist/* packer-plugin-scaffolding +packer-plugin-hyperv .docs diff --git a/.web-docs/components/builder/iso/README.md b/.web-docs/components/builder/iso/README.md index c7185a0c..712fa655 100644 --- a/.web-docs/components/builder/iso/README.md +++ b/.web-docs/components/builder/iso/README.md @@ -258,9 +258,10 @@ created, must be empty prior to running the builder. By default this is try and determine the switch to use by looking for an external switch that is up and running. -- `switches_names` ([]string) - The name of the switches to connect the virtual +- `switch_type` (string) - The type of the main switch to connect the virtual machine to. By default, leaving this value unset will cause Packer to - not use any additional switches. + try and determine the switch to use by looking for an external switch + that is up and running. - `switch_vlan_id` (string) - This is the VLAN of the virtual switch's network card. By default, none is set. If none is set then a VLAN is not @@ -271,14 +272,16 @@ created, must be empty prior to running the builder. By default this is the default main virtual network card. The MAC address must be a string with no delimiters, for example "0000deadbeef". -- `mac_addresses` ([]string) - This allows a specific MAC addresses to be used on - the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with - no delimiters, for example "0000deadbeef". - - `vlan_id` (string) - This is the VLAN of the virtual machine's network card for the new virtual machine. By default none is set. If none is set then VLANs are not set on the virtual machine's network card. +- `switch_config` ([]SwitchConfig) - This allows for multiple switches to be configured. + This should be used exclusively with SwitchName, SwitchVlanId, and MacAddress. + +- `adapter_config` ([]AdapterConfig) - This allows for multiple switches to be configured. + This should be used exclusively with MacAddress and VlanId. + - `cpus` (uint) - The number of CPUs the virtual machine should use. If this isn't specified, the default is 1 CPU. diff --git a/.web-docs/components/builder/vmcx/README.md b/.web-docs/components/builder/vmcx/README.md index 2f1534a9..631f15e8 100644 --- a/.web-docs/components/builder/vmcx/README.md +++ b/.web-docs/components/builder/vmcx/README.md @@ -285,9 +285,10 @@ In HCL2: try and determine the switch to use by looking for an external switch that is up and running. -- `switches_names` ([]string) - The name of the switches to connect the virtual +- `switch_type` (string) - The type of the main switch to connect the virtual machine to. By default, leaving this value unset will cause Packer to - not use any additional switches. + try and determine the switch to use by looking for an external switch + that is up and running. - `switch_vlan_id` (string) - This is the VLAN of the virtual switch's network card. By default, none is set. If none is set then a VLAN is not @@ -298,14 +299,16 @@ In HCL2: the default main virtual network card. The MAC address must be a string with no delimiters, for example "0000deadbeef". -- `mac_addresses` ([]string) - This allows a specific MAC addresses to be used on - the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with - no delimiters, for example "0000deadbeef". - - `vlan_id` (string) - This is the VLAN of the virtual machine's network card for the new virtual machine. By default none is set. If none is set then VLANs are not set on the virtual machine's network card. +- `switch_config` ([]SwitchConfig) - This allows for multiple switches to be configured. + This should be used exclusively with SwitchName, SwitchVlanId, and MacAddress. + +- `adapter_config` ([]AdapterConfig) - This allows for multiple switches to be configured. + This should be used exclusively with MacAddress and VlanId. + - `cpus` (uint) - The number of CPUs the virtual machine should use. If this isn't specified, the default is 1 CPU. diff --git a/builder/hyperv/common/adapter_config.go b/builder/hyperv/common/adapter_config.go new file mode 100644 index 00000000..1b4e1ff5 --- /dev/null +++ b/builder/hyperv/common/adapter_config.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate packer-sdc struct-markdown +//go:generate packer-sdc mapstructure-to-hcl2 -type AdapterConfig + +package common + +import ( + "github.com/hashicorp/packer-plugin-sdk/common" + "github.com/hashicorp/packer-plugin-sdk/template/interpolate" +) + +type AdapterConfig struct { + // The name of the network adapter. + // By default, leaving this value unset will cause Packer to + // try and determine the switch to use by looking for an external switch + // that is up and running. + Name string `mapstructure:"adapter_name" required:"false"` + // The name of the switch for this adapter + SwitchName string `mapstructure:"switch_name" required:"false"` + // This is the VLAN of the virtual switch's + // network card. By default, none is set. If none is set then a VLAN is not + // set on the switch's network card. If this value is set it should match + // the VLAN specified in by vlan_id. + VlanId string `mapstructure:"vlan_id" required:"false"` + // This allows a specific MAC address to be used on + // the default main virtual network card. The MAC address must be a string with + // no delimiters, for example "037777777777deadbeef". + MacAddress string `mapstructure:"mac_address" required:"false"` +} + +func (ac AdapterConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) ([]error, []string) { + return nil, nil +} diff --git a/builder/hyperv/common/adapter_config.hcl2spec.go b/builder/hyperv/common/adapter_config.hcl2spec.go new file mode 100644 index 00000000..330d578d --- /dev/null +++ b/builder/hyperv/common/adapter_config.hcl2spec.go @@ -0,0 +1,37 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package common + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatAdapterConfig is an auto-generated flat version of AdapterConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatAdapterConfig struct { + Name *string `mapstructure:"adapter_name" required:"false" cty:"adapter_name" hcl:"adapter_name"` + SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` + VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` + MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` +} + +// FlatMapstructure returns a new FlatAdapterConfig. +// FlatAdapterConfig is an auto-generated flat version of AdapterConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*AdapterConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatAdapterConfig) +} + +// HCL2Spec returns the hcl spec of a AdapterConfig. +// This spec is used by HCL to read the fields of AdapterConfig. +// The decoded values from this spec will then be applied to a FlatAdapterConfig. +func (*FlatAdapterConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "adapter_name": &hcldec.AttrSpec{Name: "adapter_name", Type: cty.String, Required: false}, + "switch_name": &hcldec.AttrSpec{Name: "switch_name", Type: cty.String, Required: false}, + "vlan_id": &hcldec.AttrSpec{Name: "vlan_id", Type: cty.String, Required: false}, + "mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/hyperv/common/config.go b/builder/hyperv/common/config.go index d09b05fd..bc7446da 100644 --- a/builder/hyperv/common/config.go +++ b/builder/hyperv/common/config.go @@ -79,10 +79,11 @@ type CommonConfig struct { // try and determine the switch to use by looking for an external switch // that is up and running. SwitchName string `mapstructure:"switch_name" required:"false"` - // The name of the switches to connect the virtual + // The type of the main switch to connect the virtual // machine to. By default, leaving this value unset will cause Packer to - // not use any additional switches. - SwitchesNames []string `mapstructure:"switches_names" required:"false"` + // try and determine the switch to use by looking for an external switch + // that is up and running. + SwitchType string `mapstructure:"switch_type" required:"false"` // This is the VLAN of the virtual switch's // network card. By default, none is set. If none is set then a VLAN is not // set on the switch's network card. If this value is set it should match @@ -92,14 +93,16 @@ type CommonConfig struct { // the default main virtual network card. The MAC address must be a string with // no delimiters, for example "0000deadbeef". MacAddress string `mapstructure:"mac_address" required:"false"` - // This allows a specific MAC addresses to be used on - // the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with - // no delimiters, for example "0000deadbeef". - MacAddresses []string `mapstructure:"mac_addresses" required:"false"` // This is the VLAN of the virtual machine's network // card for the new virtual machine. By default none is set. If none is set // then VLANs are not set on the virtual machine's network card. VlanId string `mapstructure:"vlan_id" required:"false"` + // This allows for multiple switches to be configured. + // This should be used exclusively with SwitchName, SwitchVlanId, and MacAddress. + SwitchConfigs []SwitchConfig `mapstructure:"switch_config" required:"false"` + // This allows for multiple switches to be configured. + // This should be used exclusively with MacAddress and VlanId. + AdapterConfigs []AdapterConfig `mapstructure:"adapter_config" required:"false"` // The number of CPUs the virtual machine should use. If // this isn't specified, the default is 1 CPU. Cpu uint `mapstructure:"cpus" required:"false"` @@ -205,9 +208,110 @@ func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig log.Printf("%s: %v", "VMName", c.VMName) } - if c.SwitchName == "" { - c.SwitchName = c.detectSwitchName(pc.PackerBuildName) - log.Printf("Using switch %s", c.SwitchName) + // Validation of switch config and switch parameters + // The end result of this section is the unification of all Switch Parameters into SwitchConfigs. + if (c.SwitchName != "" || c.SwitchType != "" || c.SwitchVlanId != "" || c.MacAddress != "" || c.VlanId != "") && + (len(c.AdapterConfigs) > 0 || len(c.SwitchConfigs) > 0) { + err := fmt.Errorf("SwitchName/SwitchType/SwitchVlanId/MacAddress/VlanId and SwitchConfig/AdapterConfig not allowed.") + errs = append(errs, err) + } else { + if c.SwitchName != "" || c.SwitchType != "" || c.SwitchVlanId != "" || c.MacAddress != "" || c.VlanId != "" { + if c.SwitchType != "" { + warns = append(warns, "SwitchType is deprecated and should be converted to SwitchConfigs") + } + if c.SwitchName != "" { + warns = append(warns, "SwitchName is deprecated and should be converted to SwitchConfigs") + } else { + c.SwitchName = c.detectSwitchName(pc.PackerBuildName) + if c.SwitchType == "" { + c.SwitchType = SwitchTypeExternal + } + log.Printf("Using switch %s", c.SwitchName) + } + if c.SwitchVlanId != "" { + warns = append(warns, "SwitchVlanId is deprecated and should be converted to SwitchConfigs") + } + if c.MacAddress != "" { + warns = append(warns, "MacAddress is deprecated and should be converted to SwitchConfigs") + } + if c.VlanId != "" { + warns = append(warns, "VlanId is deprecated and should be converted to SwitchConfigs") + } + if c.SwitchVlanId != "" { + if c.SwitchVlanId != c.VlanId { + warning := fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor " + + "vlan. The switch will not be able to see traffic from the VM.") + warns = Appendwarns(warns, warning) + } + } + + c.SwitchConfigs = []SwitchConfig{{ + SwitchName: c.SwitchName, + SwitchType: c.SwitchType, + SwitchVlanId: c.SwitchVlanId, + }} + c.AdapterConfigs = []AdapterConfig{{ + Name: c.VMName, + VlanId: c.VlanId, + MacAddress: c.MacAddress, + SwitchName: c.SwitchName, + }} + + // Make sure we don't get confused.... + c.VlanId = "" + c.MacAddress = "" + c.SwitchName = "" + c.SwitchType = "" + c.SwitchVlanId = "" + } else { + // If switchConfigs and adapterConfigs are not 0, assume the user knows what they are doing somewhat. + // Make sure the structs are valid, but don't create things for them. + if len(c.SwitchConfigs) == 0 { + swname := c.detectSwitchName(pc.PackerBuildName) + log.Printf("Using switch %s", swname) + c.SwitchConfigs = []SwitchConfig{{SwitchName: swname}} + } + for ii, sw := range c.SwitchConfigs { + if sw.SwitchName == "" { + err := fmt.Errorf("SwitchName for Switch(%d) requires a name", ii) + errs = append(errs, err) + } + } + if c.SwitchConfigs[0].SwitchType == "" { + c.SwitchConfigs[0].SwitchType = SwitchTypeExternal + } + if len(c.AdapterConfigs) == 0 { + c.AdapterConfigs = []AdapterConfig{} + for ii, sw := range c.SwitchConfigs { + name := c.VMName + if ii != 0 { + name = fmt.Sprintf("%s-%d", c.VMName, ii) + } + c.AdapterConfigs = append(c.AdapterConfigs, AdapterConfig{Name: name, SwitchName: sw.SwitchName}) + } + } else { + for ii, adp := range c.AdapterConfigs { + found := false + for _, sw := range c.SwitchConfigs { + if sw.SwitchName == adp.SwitchName { + found = true + break + } + } + if !found { + err := fmt.Errorf("Network Adapter %d (%s) requires a switch that is not defined: %s", ii, adp.Name, adp.SwitchName) + errs = append(errs, err) + } + if adp.Name == "" { + name := c.VMName + if ii != 0 { + name = fmt.Sprintf("%s-%d", c.VMName, ii) + } + adp.Name = name + } + } + } + } } if c.Generation < 1 || c.Generation > 2 { @@ -341,14 +445,6 @@ func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig } } - if c.SwitchVlanId != "" { - if c.SwitchVlanId != c.VlanId { - warning := fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor " + - "vlan. The switch will not be able to see traffic from the VM.") - warns = Appendwarns(warns, warning) - } - } - err := c.checkDiskBlockSize() if err != nil { errs = append(errs, err) @@ -396,7 +492,7 @@ func (c *CommonConfig) checkHostAvailableMemory() string { freeMB := powershell.GetHostAvailableMemory() if (freeMB - float64(c.RamSize)) < LowRam { - return "Hyper-V might fail to create a VM if there is not enough free memory in the system." + return fmt.Sprintf("Hyper-V might fail to create a VM if there is not enough free memory in the system. (%v, %v, %v)", freeMB, float64(c.RamSize), LowRam) } } diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 44e7b6ec..bed36228 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -54,35 +54,32 @@ type Driver interface { GetVirtualMachineNetworkAdapterAddress(string) (string, error) //Set the vlan to use for switch - SetNetworkAdapterVlanId(string, string) error - - //Set the vlan to use for machine + CreateVirtualMachineNetworkAdapter(string, string, string, bool) error SetVirtualMachineVlanId(string, string) error - + //Set the vlan to use for machine + SetNetworkAdapterVlanId(string, string) error SetVmNetworkAdapterMacAddress(string, string) error //Replace the network adapter with a (non-)legacy adapter ReplaceVirtualMachineNetworkAdapter(string, bool) error + // Not used currently and likely broken. UntagVirtualMachineNetworkAdapterVlan(string, string) error - - CreateExternalVirtualSwitch(string, string) error - - GetVirtualMachineSwitchName(string) (string, error) - ConnectVirtualMachineNetworkAdapterToSwitch(string, string) error + GetVirtualMachineSwitchName(string) (string, error) + // Switch Management Functions CreateVirtualSwitch(string, string) (bool, error) - + CreateExternalVirtualSwitch(string) (bool, error) DeleteVirtualSwitch(string) error CheckVMName(string) error - CreateVirtualMachine(string, string, string, int64, int64, int64, string, []string, []string, uint, bool, bool, string) error + CreateVirtualMachine(string, string, string, int64, int64, int64, string, uint, bool, bool, string) error AddVirtualMachineHardDrive(string, string, string, int64, int64, string) error - CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string, []string, []string, bool) error + CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string, bool) error ResizeVirtualMachineVhd(string, uint64) error diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index b48126cf..7aac64d4 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -89,14 +89,21 @@ type DriverMock struct { SetVirtualMachineVlanId_VlanId string SetVirtualMachineVlanId_Err error + CreateVirtualMachineNetworkAdapter_Called bool + CreateVirtualMachineNetworkAdapter_VmName string + CreateVirtualMachineNetworkAdapter_AdpName string + CreateVirtualMachineNetworkAdapter_SwitchName string + CreateVirtualMachineNetworkAdapter_Legacy bool + CreateVirtualMachineNetworkAdapter_Err error + UntagVirtualMachineNetworkAdapterVlan_Called bool UntagVirtualMachineNetworkAdapterVlan_VmName string UntagVirtualMachineNetworkAdapterVlan_SwitchName string UntagVirtualMachineNetworkAdapterVlan_Err error CreateExternalVirtualSwitch_Called bool - CreateExternalVirtualSwitch_VmName string CreateExternalVirtualSwitch_SwitchName string + CreateExternalVirtualSwitch_Return bool CreateExternalVirtualSwitch_Err error GetVirtualMachineSwitchName_Called bool @@ -138,9 +145,7 @@ type DriverMock struct { CreateVirtualMachine_Ram int64 CreateVirtualMachine_DiskSize int64 CreateVirtualMachine_DiskBlockSize int64 - CreateVirtualMachine_SwitchName string - CreateVirtualMachine_SwitchesNames []string - CreateVirtualMachine_MacAddresses []string + CreateVirtualMachine_MainSwitch string CreateVirtualMachine_Generation uint CreateVirtualMachine_DifferentialDisk bool CreateVirtualMachine_FixedVHD bool @@ -156,9 +161,7 @@ type DriverMock struct { CloneVirtualMachine_Path string CloneVirtualMachine_HarddrivePath string CloneVirtualMachine_Ram int64 - CloneVirtualMachine_SwitchName string - CloneVirtualMachine_SwitchesNames []string - CloneVirtualMachine_MacAddresses []string + CloneVirtualMachine_MainSwitch string CloneVirtualMachine_Copy bool CloneVirtualMachine_Err error @@ -402,6 +405,15 @@ func (d *DriverMock) SetVirtualMachineVlanId(vmName string, vlanId string) error return d.SetVirtualMachineVlanId_Err } +func (d *DriverMock) CreateVirtualMachineNetworkAdapter(vmName string, adpName string, switchName string, legacy bool) error { + d.CreateVirtualMachineNetworkAdapter_Called = true + d.CreateVirtualMachineNetworkAdapter_VmName = vmName + d.CreateVirtualMachineNetworkAdapter_AdpName = adpName + d.CreateVirtualMachineNetworkAdapter_SwitchName = switchName + d.CreateVirtualMachineNetworkAdapter_Legacy = legacy + return d.CreateVirtualMachineNetworkAdapter_Err +} + func (d *DriverMock) UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error { d.UntagVirtualMachineNetworkAdapterVlan_Called = true d.UntagVirtualMachineNetworkAdapterVlan_VmName = vmName @@ -409,11 +421,10 @@ func (d *DriverMock) UntagVirtualMachineNetworkAdapterVlan(vmName string, switch return d.UntagVirtualMachineNetworkAdapterVlan_Err } -func (d *DriverMock) CreateExternalVirtualSwitch(vmName string, switchName string) error { +func (d *DriverMock) CreateExternalVirtualSwitch(switchName string) (bool, error) { d.CreateExternalVirtualSwitch_Called = true - d.CreateExternalVirtualSwitch_VmName = vmName d.CreateExternalVirtualSwitch_SwitchName = switchName - return d.CreateExternalVirtualSwitch_Err + return d.CreateExternalVirtualSwitch_Return, d.CreateExternalVirtualSwitch_Err } func (d *DriverMock) GetVirtualMachineSwitchName(vmName string) (string, error) { @@ -460,7 +471,7 @@ func (d *DriverMock) CheckVMName(vmName string) error { } func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, - ram int64, diskSize int64, diskBlockSize int64, switchName string, switchesNames []string, macAddresses []string, generation uint, + ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, fixedVHD bool, version string) error { d.CreateVirtualMachine_Called = true d.CreateVirtualMachine_VmName = vmName @@ -469,9 +480,7 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP d.CreateVirtualMachine_Ram = ram d.CreateVirtualMachine_DiskSize = diskSize d.CreateVirtualMachine_DiskBlockSize = diskBlockSize - d.CreateVirtualMachine_SwitchName = switchName - d.CreateVirtualMachine_SwitchesNames = switchesNames - d.CreateVirtualMachine_MacAddresses = macAddresses + d.CreateVirtualMachine_MainSwitch = switchName d.CreateVirtualMachine_Generation = generation d.CreateVirtualMachine_DifferentialDisk = diffDisks d.CreateVirtualMachine_Version = version @@ -480,7 +489,7 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP func (d *DriverMock) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, - harddrivePath string, ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { + harddrivePath string, ram int64, mainSwitch string, copyTF bool) error { d.CloneVirtualMachine_Called = true d.CloneVirtualMachine_CloneFromVmcxPath = cloneFromVmcxPath d.CloneVirtualMachine_CloneFromVmName = cloneFromVmName @@ -490,9 +499,7 @@ func (d *DriverMock) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmNa d.CloneVirtualMachine_Path = path d.CloneVirtualMachine_HarddrivePath = harddrivePath d.CloneVirtualMachine_Ram = ram - d.CloneVirtualMachine_SwitchName = switchName - d.CloneVirtualMachine_SwitchesNames = switchesNames - d.CloneVirtualMachine_MacAddresses = macAddresses + d.CloneVirtualMachine_MainSwitch = mainSwitch d.CloneVirtualMachine_Copy = copyTF return d.CloneVirtualMachine_Err diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index f8778c1f..c6f9e9ba 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -149,8 +149,8 @@ func (d *HypervPS4Driver) SetVirtualMachineVlanId(vmName string, vlanId string) return hyperv.SetVirtualMachineVlanId(vmName, vlanId) } -func (d *HypervPS4Driver) SetVmNetworkAdapterMacAddress(vmName string, mac string) error { - return hyperv.SetVmNetworkAdapterMacAddress(vmName, mac) +func (d *HypervPS4Driver) SetVmNetworkAdapterMacAddress(adpName string, mac string) error { + return hyperv.SetVmNetworkAdapterMacAddress(adpName, mac) } // Replace the network adapter with a (non-)legacy adapter @@ -162,8 +162,12 @@ func (d *HypervPS4Driver) UntagVirtualMachineNetworkAdapterVlan(vmName string, s return hyperv.UntagVirtualMachineNetworkAdapterVlan(vmName, switchName) } -func (d *HypervPS4Driver) CreateExternalVirtualSwitch(vmName string, switchName string) error { - return hyperv.CreateExternalVirtualSwitch(vmName, switchName) +func (d *HypervPS4Driver) CreateVirtualMachineNetworkAdapter(vmName string, adpName string, switchName string, legacy bool) error { + return hyperv.CreateVirtualMachineNetworkAdapter(vmName, adpName, switchName, legacy) +} + +func (d *HypervPS4Driver) CreateExternalVirtualSwitch(switchName string) (bool, error) { + return hyperv.CreateExternalVirtualSwitch(switchName) } func (d *HypervPS4Driver) GetVirtualMachineSwitchName(vmName string) (string, error) { @@ -193,17 +197,17 @@ func (d *HypervPS4Driver) CheckVMName(vmName string) error { } func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64, - diskSize int64, diskBlockSize int64, switchName string, switchesNames []string, macAddresses []string, generation uint, diffDisks bool, + diskSize int64, diskBlockSize int64, mainSwitch string, generation uint, diffDisks bool, fixedVHD bool, version string) error { - return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, ram, diskSize, diskBlockSize, switchName, switchesNames, macAddresses, + return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, ram, diskSize, diskBlockSize, mainSwitch, generation, diffDisks, fixedVHD, version) } func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, - ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { + ram int64, mainSwitch string, copyTF bool) error { return hyperv.CloneVirtualMachine(cloneFromVmcxPath, cloneFromVmName, cloneFromSnapshotName, - cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName, switchesNames, macAddresses, copyTF) + cloneAllSnapshots, vmName, path, harddrivePath, ram, mainSwitch, copyTF) } func (d *HypervPS4Driver) ResizeVirtualMachineVhd(vmName string, newSizeInBytes uint64) error { diff --git a/builder/hyperv/common/powershell/hyperv/hyperv.go b/builder/hyperv/common/powershell/hyperv/hyperv.go index ab3473c4..c9560cd4 100644 --- a/builder/hyperv/common/powershell/hyperv/hyperv.go +++ b/builder/hyperv/common/powershell/hyperv/hyperv.go @@ -26,9 +26,7 @@ type scriptOptions struct { MemoryStartupBytes int64 NewVHDSizeBytes int64 VHDBlockSizeBytes int64 - SwitchName string - SwitchesNames []string - MacAddresses []string + MainSwitch string Generation uint DiffDisks bool FixedVHD bool @@ -357,12 +355,9 @@ $vhdPath = Join-Path -Path "{{ .Path }}" -ChildPath "{{ .VHDX }}" {{- end -}} {{- end }} -Hyper-V\New-VM -Name "{{ .VMName }}" -Path "{{ .Path }}" -MemoryStartupBytes {{ .MemoryStartupBytes }} -VHDPath $vhdPath -SwitchName "{{ .SwitchName }}" +Hyper-V\New-VM -Name "{{ .VMName }}" -Path "{{ .Path }}" -MemoryStartupBytes {{ .MemoryStartupBytes }} -VHDPath $vhdPath -SwitchName "{{ .MainSwitch }}" {{- if eq .Generation 2}} -Generation {{ .Generation }} {{- end -}} {{- if ne .Version ""}} -Version {{ .Version }} {{- end -}} -{{ range $i, $switchName := .SwitchesNames }} -Hyper-V\Add-VMNetworkAdapter -VMName "{{ $.VMName }}" -SwitchName "{{ $switchName }}"{{ if gt (len $.MacAddresses) $i }} -StaticMacAddress "{{ index $.MacAddresses $i }}".Replace("-","") {{ end }} -{{- end -}} `)) var b bytes.Buffer @@ -401,7 +396,7 @@ func CheckVMName(vmName string) error { } func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64, - diskSize int64, diskBlockSize int64, switchName string, switchesNames []string, macAddresses []string, generation uint, + diskSize int64, diskBlockSize int64, mainSwitch string, generation uint, diffDisks bool, fixedVHD bool, version string) error { opts := scriptOptions{ Version: version, @@ -411,9 +406,7 @@ func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram MemoryStartupBytes: ram, NewVHDSizeBytes: diskSize, VHDBlockSizeBytes: diskBlockSize, - SwitchName: switchName, - SwitchesNames: switchesNames, - MacAddresses: macAddresses, + MainSwitch: mainSwitch, Generation: generation, DiffDisks: diffDisks, FixedVHD: fixedVHD, @@ -516,23 +509,23 @@ Copy-Item $cloneFromVmcxPath $exportPath -Recurse -Force return err } -func SetVmNetworkAdapterMacAddress(vmName string, mac string) error { +func SetVmNetworkAdapterMacAddress(adpName string, mac string) error { var script = ` -param([string]$vmName, [string]$mac) -Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac.Replace("-","") +param([string]$adpName, [string]$mac) +Hyper-V\Set-VMNetworkAdapter $adpName -staticmacaddress $mac.Replace("-","") ` var ps powershell.PowerShellCmd - err := ps.Run(script, vmName, mac) + err := ps.Run(script, adpName, mac) return err } func ImportVmcxVirtualMachine(importPath string, vmName string, harddrivePath string, - ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { + ram int64, mainSwitch string, copyTF bool) error { var script = ` -param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName, [string]$copy, [string[]]$switchesNames, [string[]]$macAddresses) +param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName, [string]$copy) $VirtualHarddisksPath = Join-Path -Path $importPath -ChildPath 'Virtual Hard Disks' if (!(Test-Path $VirtualHarddisksPath)) { @@ -578,29 +571,21 @@ Hyper-V\Set-VMMemory -VM $compatibilityReport.VM -StartupBytes $memoryStartupByt $networkAdaptor = $compatibilityReport.VM.NetworkAdapters | Select -First 1 Hyper-V\Disconnect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switchName -foreach ($switch in $switchesNames) { -if ($macAddresses[$switchesNames.IndexOf($switch)] -ne $null) { - Hyper-V\Add-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switch -StaticMacAddress $macAddresses[$switchesNames.IndexOf($switch)].Replace("-","") -} else { - Hyper-V\Add-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor -SwitchName $switch -} $vm = Hyper-V\Import-VM -CompatibilityReport $compatibilityReport if ($vm) { $result = Hyper-V\Rename-VM -VM $vm -NewName $VMName } ` - switchesArray := strings.Join(switchesNames, ",") - macAddressesArray := strings.Join(macAddresses, ",") var ps powershell.PowerShellCmd - err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), switchName, strconv.FormatBool(copyTF), switchesArray, macAddressesArray) + err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), mainSwitch, strconv.FormatBool(copyTF)) return err } func CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, - path string, harddrivePath string, ram int64, switchName string, switchesNames []string, macAddresses []string, copyTF bool) error { + path string, harddrivePath string, ram int64, mainSwitch string, copyTF bool) error { if cloneFromVmName != "" { if err := ExportVmcxVirtualMachine(path, cloneFromVmName, @@ -615,7 +600,7 @@ func CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string, } } - if err := ImportVmcxVirtualMachine(path, vmName, harddrivePath, ram, switchName, switchesNames, macAddresses, copyTF); err != nil { + if err := ImportVmcxVirtualMachine(path, vmName, harddrivePath, ram, mainSwitch, copyTF); err != nil { return err } @@ -1172,6 +1157,21 @@ Hyper-V\Set-VMNetworkAdapterVlan -VMName $vmName -Access -VlanId $vlanId return err } +func CreateVirtualMachineNetworkAdapter(vmName, adpName, switchName string, legacy bool) error { + var script = ` +param([string]$vmName,[string]$adpName,[string]$switchName,[string]$legacyString) +$legacy = [System.Boolean]::Parse($legacyString) +Add-VMNetworkAdapter -VMName $vmName -SwitchName $switchName -Name $adpName -IsLegacy $legacy +` + legacyString := "False" + if legacy { + legacyString = "True" + } + var ps powershell.PowerShellCmd + err := ps.Run(script, vmName, adpName, switchName, legacyString) + return err +} + func ReplaceVirtualMachineNetworkAdapter(vmName string, legacy bool) error { var script = ` @@ -1214,10 +1214,14 @@ foreach ($adapter in $adapters) { return switchName, nil } -func CreateExternalVirtualSwitch(vmName string, switchName string) error { +func CreateExternalVirtualSwitch(switchName string) (bool, error) { var script = ` -param([string]$vmName,[string]$switchName) +param([string]$switchName) +$switches = Hyper-V\Get-VMSwitch -Name $switchName -ErrorAction SilentlyContinue +if ($switches.Count -ne 0) { + return $false +} $switch = $null $names = @('ethernet','wi-fi','lan') $adapters = foreach ($name in $names) { @@ -1236,15 +1240,18 @@ foreach ($adapter in $adapters) { } } -if($switch -ne $null) { - Hyper-V\Get-VMNetworkAdapter -VMName $vmName | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $switch -} else { +if($switch -eq $null) { Write-Error 'No internet adapters found' + return $false } + +return $true ` + var ps powershell.PowerShellCmd - err := ps.Run(script, vmName, switchName) - return err + cmdOut, err := ps.Output(script, switchName) + var created = strings.TrimSpace(cmdOut) == "True" + return created, err } func GetVirtualMachineSwitchName(vmName string) (string, error) { diff --git a/builder/hyperv/common/powershell/hyperv/hyperv_test.go b/builder/hyperv/common/powershell/hyperv/hyperv_test.go index 953a4d2c..9382a480 100644 --- a/builder/hyperv/common/powershell/hyperv/hyperv_test.go +++ b/builder/hyperv/common/powershell/hyperv/hyperv_test.go @@ -17,7 +17,7 @@ func Test_getCreateVMScript(t *testing.T) { MemoryStartupBytes: int64(1024), NewVHDSizeBytes: int64(8192), VHDBlockSizeBytes: int64(10), - SwitchName: "hyperv-vmx-switch", + MainSwitch: "hyperv-vmx-switch", Generation: uint(1), DiffDisks: true, FixedVHD: true, diff --git a/builder/hyperv/common/step_clone_vm.go b/builder/hyperv/common/step_clone_vm.go index f99cd4b8..328991a6 100644 --- a/builder/hyperv/common/step_clone_vm.go +++ b/builder/hyperv/common/step_clone_vm.go @@ -25,8 +25,6 @@ type StepCloneVM struct { CloneFromSnapshotName string CloneAllSnapshots bool VMName string - SwitchName string - SwitchesNames []string CompareCopy bool RamSize uint Cpu uint @@ -36,16 +34,15 @@ type StepCloneVM struct { SecureBootTemplate string EnableVirtualizationExtensions bool EnableTPM bool - MacAddress string KeepRegistered bool AdditionalDiskSize []uint DiskBlockSize uint - MacAddresses []string } func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) + mainSwitch := state.Get("swName").(string) ui.Say("Cloning virtual machine...") path := state.Get("build_dir").(string) @@ -68,7 +65,7 @@ func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multist err := driver.CloneVirtualMachine(s.CloneFromVMCXPath, s.CloneFromVMName, s.CloneFromSnapshotName, s.CloneAllSnapshots, s.VMName, path, - harddrivePath, ramSize, s.SwitchName, s.SwitchesNames, s.MacAddresses, s.CompareCopy) + harddrivePath, ramSize, mainSwitch, s.CompareCopy) if err != nil { err := fmt.Errorf("Error cloning virtual machine: %s", err) state.Put("error", err) @@ -159,16 +156,6 @@ func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multist } } - if s.MacAddress != "" { - err = driver.SetVmNetworkAdapterMacAddress(s.VMName, s.MacAddress) - if err != nil { - err := fmt.Errorf("Error setting MAC address: %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) // instance_id is the generic term used so that users can have access to the diff --git a/builder/hyperv/common/step_configure_adapters.go b/builder/hyperv/common/step_configure_adapters.go new file mode 100644 index 00000000..40cdf182 --- /dev/null +++ b/builder/hyperv/common/step_configure_adapters.go @@ -0,0 +1,85 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + packersdk "github.com/hashicorp/packer-plugin-sdk/packer" +) + +type StepConfigureAdapters struct { + UseLegacyNetworkAdapter bool + AdapterConfigs []AdapterConfig +} + +func (s *StepConfigureAdapters) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packersdk.Ui) + ui.Say("Configuring network adapters...") + + driver := state.Get("driver").(Driver) + vmName := state.Get("vmName").(string) + + errorMsg := "Error configuring adapter: %s(%d): %v" + + for ii, adp := range s.AdapterConfigs { + ui.Say(fmt.Sprintf("Building network adapter %s(%d)...", adp.Name, ii)) + + if ii == 0 && s.UseLegacyNetworkAdapter { + err := driver.ReplaceVirtualMachineNetworkAdapter(adp.Name, true) + if err != nil { + err := fmt.Errorf("Error creating legacy network adapter: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } else if ii != 0 { + err := driver.CreateVirtualMachineNetworkAdapter(vmName, adp.Name, adp.SwitchName, s.UseLegacyNetworkAdapter) + if err != nil { + err := fmt.Errorf("Error creating network adapter: %s(%d): %v", adp.Name, ii, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + if adp.MacAddress != "" { + aerr := driver.SetVmNetworkAdapterMacAddress(adp.Name, adp.MacAddress) + if aerr != nil { + err := fmt.Errorf("Error setting MAC address: %s(%d): %v", adp.Name, ii, aerr) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + if adp.VlanId != "" { + err := driver.SetNetworkAdapterVlanId(adp.Name, adp.VlanId) + if err != nil { + err := fmt.Errorf(errorMsg, adp.Name, ii, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + if ii == 0 && adp.VlanId != "" { + err := driver.SetVirtualMachineVlanId(vmName, adp.VlanId) + if err != nil { + err := fmt.Errorf(errorMsg, adp.Name, ii, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + } + + return multistep.ActionContinue +} + +func (s *StepConfigureAdapters) Cleanup(state multistep.StateBag) { + //do nothing +} diff --git a/builder/hyperv/common/step_configure_vlan.go b/builder/hyperv/common/step_configure_vlan.go deleted file mode 100644 index 7f97ea45..00000000 --- a/builder/hyperv/common/step_configure_vlan.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type StepConfigureVlan struct { - VlanId string - SwitchVlanId string -} - -func (s *StepConfigureVlan) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - - errorMsg := "Error configuring vlan: %s" - vmName := state.Get("vmName").(string) - switchName := state.Get("SwitchName").(string) - vlanId := s.VlanId - switchVlanId := s.SwitchVlanId - - ui.Say("Configuring vlan...") - - if switchVlanId != "" { - err := driver.SetNetworkAdapterVlanId(switchName, vlanId) - if err != nil { - err := fmt.Errorf(errorMsg, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - if vlanId != "" { - err := driver.SetVirtualMachineVlanId(vmName, vlanId) - if err != nil { - err := fmt.Errorf(errorMsg, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - return multistep.ActionContinue -} - -func (s *StepConfigureVlan) Cleanup(state multistep.StateBag) { - //do nothing -} diff --git a/builder/hyperv/common/step_create_external_switch.go b/builder/hyperv/common/step_create_external_switch.go deleted file mode 100644 index 74e43f61..00000000 --- a/builder/hyperv/common/step_create_external_switch.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package common - -import ( - "context" - "fmt" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/uuid" -) - -// This step creates an external switch for the VM. -// -// Produces: -// -// SwitchName string - The name of the Switch -type StepCreateExternalSwitch struct { - SwitchName string - oldSwitchName string -} - -// Run runs the step required to create an external switch. Depending on -// the connectivity of the host machine, the external switch will allow the -// build VM to connect to the outside world. -func (s *StepCreateExternalSwitch) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - - vmName := state.Get("vmName").(string) - errorMsg := "Error creating external switch: %s" - var err error - - ui.Say("Creating external switch...") - - packerExternalSwitchName := "paes_" + uuid.TimeOrderedUUID() - - // CreateExternalVirtualSwitch checks for an existing external switch, - // creating one if required, and connects the VM to it - err = driver.CreateExternalVirtualSwitch(vmName, packerExternalSwitchName) - if err != nil { - err := fmt.Errorf(errorMsg, err) - state.Put("error", err) - ui.Error(err.Error()) - s.SwitchName = "" - return multistep.ActionHalt - } - - switchName, err := driver.GetVirtualMachineSwitchName(vmName) - if err != nil { - err := fmt.Errorf(errorMsg, err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if len(switchName) == 0 { - err := fmt.Errorf(errorMsg, err) - state.Put("error", "Can't get the VM switch name") - ui.Error(err.Error()) - return multistep.ActionHalt - } - - ui.Say("External switch name is: '" + switchName + "'") - - if switchName != packerExternalSwitchName { - s.SwitchName = "" - } else { - s.SwitchName = packerExternalSwitchName - s.oldSwitchName = state.Get("SwitchName").(string) - } - - // Set the final name in the state bag so others can use it - state.Put("SwitchName", switchName) - - return multistep.ActionContinue -} - -func (s *StepCreateExternalSwitch) Cleanup(state multistep.StateBag) { - if s.SwitchName == "" { - return - } - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - vmName := state.Get("vmName").(string) - - ui.Say("Unregistering and deleting external switch...") - - errMsg := "Error deleting external switch: %s" - - // connect the vm to the old switch - if s.oldSwitchName == "" { - ui.Error(fmt.Sprintf(errMsg, "the old switch name is empty")) - return - } - - err := driver.ConnectVirtualMachineNetworkAdapterToSwitch(vmName, s.oldSwitchName) - if err != nil { - ui.Error(fmt.Sprintf(errMsg, err)) - return - } - - state.Put("SwitchName", s.oldSwitchName) - - err = driver.DeleteVirtualSwitch(s.SwitchName) - if err != nil { - ui.Error(fmt.Sprintf(errMsg, err)) - } -} diff --git a/builder/hyperv/common/step_create_switch.go b/builder/hyperv/common/step_create_switch.go index 0fb79bd0..915ec53a 100644 --- a/builder/hyperv/common/step_create_switch.go +++ b/builder/hyperv/common/step_create_switch.go @@ -12,6 +12,7 @@ import ( ) const ( + SwitchTypeExternal = "External" SwitchTypeInternal = "Internal" SwitchTypePrivate = "Private" DefaultSwitchType = SwitchTypeInternal @@ -21,19 +22,10 @@ const ( // // Produces: // -// SwitchName string - The name of the Switch +// SwitchConfigs []SwitchConfigs - The new swichConfigs type StepCreateSwitches struct { - // Specifies the name of the switch to be created. - MainSwitchName string - SwitchesNames []string - // Specifies the type of the switch to be created. Allowed values are Internal and Private. To create an External - // virtual switch, specify either the NetAdapterInterfaceDescription or the NetAdapterName parameter, which - // implicitly set the type of the virtual switch to External. - SwitchType string - // Specifies the name of the network adapter to be bound to the switch to be created. - NetAdapterName string - // Specifies the interface description of the network adapter to be bound to the switch to be created. - NetAdapterInterfaceDescription string + // Specifies the switches to be created. + SwitchConfigs []SwitchConfig createdSwitch []bool } @@ -42,38 +34,39 @@ func (s *StepCreateSwitches) Run(ctx context.Context, state multistep.StateBag) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) - if len(s.SwitchType) == 0 { - s.SwitchType = DefaultSwitchType - } - - switches := append([]string{s.MainSwitchName}, s.SwitchesNames...) - - for index, switchName := range switches { - ui.Say(fmt.Sprintf("Creating switch '%v' if required...", switchName)) - createdSwitch, err := driver.CreateVirtualSwitch(switchName, s.SwitchType) + for index, sw := range s.SwitchConfigs { + ui.Say(fmt.Sprintf("Creating switch '%v' if required...", sw.SwitchName)) + if len(sw.SwitchType) == 0 { + sw.SwitchType = DefaultSwitchType + } + var createdSwitch bool + var err error + if sw.SwitchType == SwitchTypeExternal { + createdSwitch, err = driver.CreateExternalVirtualSwitch(sw.SwitchName) + } else { + createdSwitch, err = driver.CreateVirtualSwitch(sw.SwitchName, sw.SwitchType) + } if err != nil { - err := fmt.Errorf("Error creating switch: %s", err) - state.Put("error", err) + verr := fmt.Errorf("Error creating switch: %s: %v", sw.SwitchName, err) + state.Put("error", verr) ui.Error(err.Error()) return multistep.ActionHalt } - + if index == 0 { + state.Put("swName", sw.SwitchName) + } s.createdSwitch = append(s.createdSwitch, createdSwitch) if !s.createdSwitch[index] { - ui.Say(fmt.Sprintf(" switch '%v' already exists. Will not delete on cleanup...", switchName)) + ui.Say(fmt.Sprintf(" switch '%v' already exists. Will not delete on cleanup...", sw.SwitchName)) } } - // Set the final name in the state bag so others can use it - state.Put("SwitchName", s.MainSwitchName) - state.Put("SwitchesNames", s.SwitchesNames) - return multistep.ActionContinue } func (s *StepCreateSwitches) Cleanup(state multistep.StateBag) { - if s.MainSwitchName == "" && len(s.SwitchesNames) == 0 { + if len(s.SwitchConfigs) == 0 { return } @@ -81,11 +74,9 @@ func (s *StepCreateSwitches) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packersdk.Ui) ui.Say("Unregistering and deleting switch...") - switches := append([]string{s.MainSwitchName}, s.SwitchesNames...) - - for index, switchName := range switches { + for index, sw := range s.SwitchConfigs { if s.createdSwitch[index] { - err := driver.DeleteVirtualSwitch(switchName) + err := driver.DeleteVirtualSwitch(sw.SwitchName) if err != nil { ui.Error(fmt.Sprintf("Error deleting switch: %s", err)) } diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index 36062965..ddcfb14c 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -21,8 +21,6 @@ import ( // VMName string - The name of the VM type StepCreateVM struct { VMName string - SwitchName string - SwitchesNames []string HarddrivePath string RamSize uint DiskSize uint @@ -38,8 +36,6 @@ type StepCreateVM struct { EnableTPM bool AdditionalDiskSize []uint DifferencingDisk bool - MacAddress string - MacAddresses []string FixedVHD bool Version string KeepRegistered bool @@ -49,6 +45,7 @@ func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) ui.Say("Creating virtual machine...") + swName := state.Get("swName").(string) var path string if v, ok := state.GetOk("build_dir"); ok { @@ -82,7 +79,7 @@ func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis diskBlockSize := int64(s.DiskBlockSize) * 1024 * 1024 err = driver.CreateVirtualMachine(s.VMName, path, harddrivePath, ramSize, diskSize, diskBlockSize, - s.SwitchName, s.SwitchesNames, s.MacAddresses, s.Generation, s.DifferencingDisk, s.FixedVHD, s.Version) + swName, s.Generation, s.DifferencingDisk, s.FixedVHD, s.Version) if err != nil { err := fmt.Errorf("Error creating virtual machine: %s", err) state.Put("error", err) @@ -90,16 +87,6 @@ func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis return multistep.ActionHalt } - if s.UseLegacyNetworkAdapter { - err := driver.ReplaceVirtualMachineNetworkAdapter(s.VMName, true) - if err != nil { - err := fmt.Errorf("Error creating legacy network adapter: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - err = driver.SetVirtualMachineCpuCount(s.VMName, s.Cpu) if err != nil { err := fmt.Errorf("Error setting virtual machine cpu count: %s", err) @@ -170,16 +157,6 @@ func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis } } - if s.MacAddress != "" { - err = driver.SetVmNetworkAdapterMacAddress(s.VMName, s.MacAddress) - if err != nil { - err := fmt.Errorf("Error setting MAC address: %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) // instance_id is the generic term used so that users can have access to the diff --git a/builder/hyperv/common/step_create_vm_test.go b/builder/hyperv/common/step_create_vm_test.go index 4e3eb7a1..accc16c3 100644 --- a/builder/hyperv/common/step_create_vm_test.go +++ b/builder/hyperv/common/step_create_vm_test.go @@ -23,6 +23,7 @@ func TestStepCreateVM(t *testing.T) { driver := state.Get("driver").(*DriverMock) // Test the run + state.Put("swName", "mainSwitch") if action := step.Run(context.Background(), state); action != multistep.ActionContinue { t.Fatalf("Bad action: %v", action) } @@ -47,6 +48,7 @@ func TestStepCreateVM_CheckVMNameErr(t *testing.T) { "vm_name to a unique value") // Test the run + state.Put("swName", "mainSwitch") if action := step.Run(context.Background(), state); action != multistep.ActionHalt { t.Fatalf("Bad action: %v", action) } diff --git a/builder/hyperv/common/step_disable_vlan.go b/builder/hyperv/common/step_disable_vlan.go index 81574a71..7125590c 100644 --- a/builder/hyperv/common/step_disable_vlan.go +++ b/builder/hyperv/common/step_disable_vlan.go @@ -14,6 +14,8 @@ import ( type StepDisableVlan struct { } +// XXX: This is not used either currently. + func (s *StepDisableVlan) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) diff --git a/builder/hyperv/common/step_run.go b/builder/hyperv/common/step_run.go index d0fb718a..1b034996 100644 --- a/builder/hyperv/common/step_run.go +++ b/builder/hyperv/common/step_run.go @@ -17,7 +17,6 @@ import ( type StepRun struct { GuiCancelFunc context.CancelFunc Headless bool - SwitchName string vmName string } @@ -25,9 +24,17 @@ func (s *StepRun) Run(ctx context.Context, state multistep.StateBag) multistep.S driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) vmName := state.Get("vmName").(string) + swName := state.Get("swName").(string) // This is set by the create_switches for create or the builder for clone + + if swName == "" { + err := fmt.Errorf("Error getting main switch name") + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } ui.Say("Determine Host IP for HyperV machine...") - hostIp, err := driver.GetHostAdapterIpAddressForSwitch(s.SwitchName) + hostIp, err := driver.GetHostAdapterIpAddressForSwitch(swName) if err != nil { err := fmt.Errorf("Error getting host adapter ip address: %s", err) state.Put("error", err) diff --git a/builder/hyperv/common/step_type_boot_command.go b/builder/hyperv/common/step_type_boot_command.go index e0b23e5b..03a82759 100644 --- a/builder/hyperv/common/step_type_boot_command.go +++ b/builder/hyperv/common/step_type_boot_command.go @@ -25,7 +25,6 @@ type bootCommandTemplateData struct { type StepTypeBootCommand struct { BootCommand string BootWait time.Duration - SwitchName string Ctx interpolate.Context GroupInterval time.Duration } diff --git a/builder/hyperv/common/switch_config.go b/builder/hyperv/common/switch_config.go new file mode 100644 index 00000000..67b2b54e --- /dev/null +++ b/builder/hyperv/common/switch_config.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate packer-sdc struct-markdown +//go:generate packer-sdc mapstructure-to-hcl2 -type SwitchConfig + +package common + +import ( + "github.com/hashicorp/packer-plugin-sdk/common" + "github.com/hashicorp/packer-plugin-sdk/template/interpolate" +) + +type SwitchConfig struct { + // The name of the main switch to connect the virtual + // machine to. By default, leaving this value unset will cause Packer to + // try and determine the switch to use by looking for an external switch + // that is up and running. + SwitchName string `mapstructure:"switch_name" required:"false"` + // This allows the specification of the switch type. + // the default will be Internal if unspecificed + SwitchType string `mapstructure:"switch_type" required:"false"` + // This is the VLAN of the virtual switch's + // network card. By default, none is set. If none is set then a VLAN is not + // set on the switch's network card. If this value is set it should match + // the VLAN specified in by vlan_id. + SwitchVlanId string `mapstructure:"switch_vlan_id" required:"false"` +} + +func (sc SwitchConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) ([]error, []string) { + return nil, nil +} diff --git a/builder/hyperv/common/switch_config.hcl2spec.go b/builder/hyperv/common/switch_config.hcl2spec.go new file mode 100644 index 00000000..959fc3db --- /dev/null +++ b/builder/hyperv/common/switch_config.hcl2spec.go @@ -0,0 +1,35 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package common + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatSwitchConfig is an auto-generated flat version of SwitchConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatSwitchConfig struct { + SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` + SwitchType *string `mapstructure:"switch_type" required:"false" cty:"switch_type" hcl:"switch_type"` + SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` +} + +// FlatMapstructure returns a new FlatSwitchConfig. +// FlatSwitchConfig is an auto-generated flat version of SwitchConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*SwitchConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSwitchConfig) +} + +// HCL2Spec returns the hcl spec of a SwitchConfig. +// This spec is used by HCL to read the fields of SwitchConfig. +// The decoded values from this spec will then be applied to a FlatSwitchConfig. +func (*FlatSwitchConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "switch_name": &hcldec.AttrSpec{Name: "switch_name", Type: cty.String, Required: false}, + "switch_type": &hcldec.AttrSpec{Name: "switch_type", Type: cty.String, Required: false}, + "switch_vlan_id": &hcldec.AttrSpec{Name: "switch_vlan_id", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index b22976a8..8ef9b8e1 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -224,14 +224,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) }, commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), &hypervcommon.StepCreateSwitches{ - MainSwitchName: b.config.SwitchName, - SwitchesNames: b.config.SwitchesNames, + SwitchConfigs: b.config.SwitchConfigs, }, &hypervcommon.StepCreateVM{ VMName: b.config.VMName, - SwitchName: b.config.SwitchName, - SwitchesNames: b.config.SwitchesNames, - MacAddresses: b.config.MacAddresses, RamSize: b.config.RamSize, DiskSize: b.config.DiskSize, DiskBlockSize: b.config.DiskBlockSize, @@ -246,13 +242,17 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) UseLegacyNetworkAdapter: b.config.UseLegacyNetworkAdapter, AdditionalDiskSize: b.config.AdditionalDiskSize, DifferencingDisk: b.config.DifferencingDisk, - MacAddress: b.config.MacAddress, FixedVHD: b.config.FixedVHD, Version: b.config.Version, KeepRegistered: b.config.KeepRegistered, }, &hypervcommon.StepEnableIntegrationService{}, + &hypervcommon.StepConfigureAdapters{ + UseLegacyNetworkAdapter: b.config.UseLegacyNetworkAdapter, + AdapterConfigs: b.config.AdapterConfigs, + }, + &hypervcommon.StepMountDvdDrive{ Generation: b.config.Generation, FirstBootDevice: b.config.FirstBootDevice, @@ -276,11 +276,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Generation: b.config.Generation, }, - &hypervcommon.StepConfigureVlan{ - VlanId: b.config.VlanId, - SwitchVlanId: b.config.SwitchVlanId, - }, - &hypervcommon.StepSetBootOrder{ BootOrder: b.config.BootOrder, }, @@ -290,14 +285,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) }, &hypervcommon.StepRun{ - Headless: b.config.Headless, - SwitchName: b.config.SwitchName, + Headless: b.config.Headless, }, &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.FlatBootCommand(), BootWait: b.config.BootWait, - SwitchName: b.config.SwitchName, Ctx: b.config.ctx, GroupInterval: b.config.BootConfig.BootGroupInterval, }, diff --git a/builder/hyperv/iso/builder.hcl2spec.go b/builder/hyperv/iso/builder.hcl2spec.go index 29121f55..a97b809c 100644 --- a/builder/hyperv/iso/builder.hcl2spec.go +++ b/builder/hyperv/iso/builder.hcl2spec.go @@ -4,127 +4,129 @@ package iso import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/common" "github.com/zclconf/go-cty/cty" ) // FlatConfig is an auto-generated flat version of Config. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` - HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` - HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` - HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` - HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` - HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` - ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"` - RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"` - ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"` - TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"` - TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"` - BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` - BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` - BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` - OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"` - Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` - PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` - SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` - SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` - SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` - SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` - SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` - SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` - SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` - SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` - SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` - SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` - SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` - SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` - SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` - SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` - SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` - SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` - SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` - SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` - SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` - SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` - SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` - SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` - SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` - SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` - SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` - SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` - SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` - SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` - SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` - SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` - SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` - SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` - SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` - SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` - SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` - WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` - WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` - WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` - WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` - WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` - WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` - WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` - WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` - WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` - FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` - FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` - FloppyContent map[string]string `mapstructure:"floppy_content" cty:"floppy_content" hcl:"floppy_content"` - FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` - CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` - CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` - CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` - DiskBlockSize *uint `mapstructure:"disk_block_size" required:"false" cty:"disk_block_size" hcl:"disk_block_size"` - RamSize *uint `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"` - SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false" cty:"secondary_iso_images" hcl:"secondary_iso_images"` - AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` - GuestAdditionsMode *string `mapstructure:"guest_additions_mode" required:"false" cty:"guest_additions_mode" hcl:"guest_additions_mode"` - GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"` - VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` - SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` - SwitchesNames []string `mapstructure:"switches_names" required:"false" cty:"switches_names" hcl:"switches_names"` - SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` - MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` - MacAddresses []string `mapstructure:"mac_addresses" required:"false" cty:"mac_addresses" hcl:"mac_addresses"` - VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` - Cpu *uint `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` - Generation *uint `mapstructure:"generation" required:"false" cty:"generation" hcl:"generation"` - EnableMacSpoofing *bool `mapstructure:"enable_mac_spoofing" required:"false" cty:"enable_mac_spoofing" hcl:"enable_mac_spoofing"` - EnableDynamicMemory *bool `mapstructure:"enable_dynamic_memory" required:"false" cty:"enable_dynamic_memory" hcl:"enable_dynamic_memory"` - EnableSecureBoot *bool `mapstructure:"enable_secure_boot" required:"false" cty:"enable_secure_boot" hcl:"enable_secure_boot"` - SecureBootTemplate *string `mapstructure:"secure_boot_template" required:"false" cty:"secure_boot_template" hcl:"secure_boot_template"` - EnableVirtualizationExtensions *bool `mapstructure:"enable_virtualization_extensions" required:"false" cty:"enable_virtualization_extensions" hcl:"enable_virtualization_extensions"` - EnableTPM *bool `mapstructure:"enable_tpm" required:"false" cty:"enable_tpm" hcl:"enable_tpm"` - TempPath *string `mapstructure:"temp_path" required:"false" cty:"temp_path" hcl:"temp_path"` - Version *string `mapstructure:"configuration_version" required:"false" cty:"configuration_version" hcl:"configuration_version"` - KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"` - SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction" hcl:"skip_compaction"` - SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"` - Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"` - FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device" hcl:"first_boot_device"` - BootOrder []string `mapstructure:"boot_order" required:"false" cty:"boot_order" hcl:"boot_order"` - ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"` - ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"` - DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown" hcl:"disable_shutdown"` - DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"` - UseLegacyNetworkAdapter *bool `mapstructure:"use_legacy_network_adapter" required:"false" cty:"use_legacy_network_adapter" hcl:"use_legacy_network_adapter"` - DifferencingDisk *bool `mapstructure:"differencing_disk" required:"false" cty:"differencing_disk" hcl:"differencing_disk"` - FixedVHD *bool `mapstructure:"use_fixed_vhd_format" required:"false" cty:"use_fixed_vhd_format" hcl:"use_fixed_vhd_format"` + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` + HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` + HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` + HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` + HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` + HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` + ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"` + RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"` + ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"` + TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"` + TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"` + BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` + BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` + BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` + OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"` + Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` + SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` + SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` + SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` + SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` + SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` + SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` + SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` + SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` + WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` + FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` + FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` + FloppyContent map[string]string `mapstructure:"floppy_content" cty:"floppy_content" hcl:"floppy_content"` + FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` + CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` + CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` + CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` + DiskBlockSize *uint `mapstructure:"disk_block_size" required:"false" cty:"disk_block_size" hcl:"disk_block_size"` + RamSize *uint `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"` + SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false" cty:"secondary_iso_images" hcl:"secondary_iso_images"` + AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` + GuestAdditionsMode *string `mapstructure:"guest_additions_mode" required:"false" cty:"guest_additions_mode" hcl:"guest_additions_mode"` + GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"` + VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` + SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` + SwitchType *string `mapstructure:"switch_type" required:"false" cty:"switch_type" hcl:"switch_type"` + SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` + MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` + VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` + SwitchConfigs []common.FlatSwitchConfig `mapstructure:"switch_config" required:"false" cty:"switch_config" hcl:"switch_config"` + AdapterConfigs []common.FlatAdapterConfig `mapstructure:"adapter_config" required:"false" cty:"adapter_config" hcl:"adapter_config"` + Cpu *uint `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` + Generation *uint `mapstructure:"generation" required:"false" cty:"generation" hcl:"generation"` + EnableMacSpoofing *bool `mapstructure:"enable_mac_spoofing" required:"false" cty:"enable_mac_spoofing" hcl:"enable_mac_spoofing"` + EnableDynamicMemory *bool `mapstructure:"enable_dynamic_memory" required:"false" cty:"enable_dynamic_memory" hcl:"enable_dynamic_memory"` + EnableSecureBoot *bool `mapstructure:"enable_secure_boot" required:"false" cty:"enable_secure_boot" hcl:"enable_secure_boot"` + SecureBootTemplate *string `mapstructure:"secure_boot_template" required:"false" cty:"secure_boot_template" hcl:"secure_boot_template"` + EnableVirtualizationExtensions *bool `mapstructure:"enable_virtualization_extensions" required:"false" cty:"enable_virtualization_extensions" hcl:"enable_virtualization_extensions"` + EnableTPM *bool `mapstructure:"enable_tpm" required:"false" cty:"enable_tpm" hcl:"enable_tpm"` + TempPath *string `mapstructure:"temp_path" required:"false" cty:"temp_path" hcl:"temp_path"` + Version *string `mapstructure:"configuration_version" required:"false" cty:"configuration_version" hcl:"configuration_version"` + KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"` + SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction" hcl:"skip_compaction"` + SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"` + Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"` + FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device" hcl:"first_boot_device"` + BootOrder []string `mapstructure:"boot_order" required:"false" cty:"boot_order" hcl:"boot_order"` + ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"` + ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"` + DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown" hcl:"disable_shutdown"` + DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"` + UseLegacyNetworkAdapter *bool `mapstructure:"use_legacy_network_adapter" required:"false" cty:"use_legacy_network_adapter" hcl:"use_legacy_network_adapter"` + DifferencingDisk *bool `mapstructure:"differencing_disk" required:"false" cty:"differencing_disk" hcl:"differencing_disk"` + FixedVHD *bool `mapstructure:"use_fixed_vhd_format" required:"false" cty:"use_fixed_vhd_format" hcl:"use_fixed_vhd_format"` } // FlatMapstructure returns a new FlatConfig. @@ -226,11 +228,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "switch_name": &hcldec.AttrSpec{Name: "switch_name", Type: cty.String, Required: false}, - "switches_names": &hcldec.AttrSpec{Name: "switches_names", Type: cty.List(cty.String), Required: false}, + "switch_type": &hcldec.AttrSpec{Name: "switch_type", Type: cty.String, Required: false}, "switch_vlan_id": &hcldec.AttrSpec{Name: "switch_vlan_id", Type: cty.String, Required: false}, "mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false}, - "mac_addresses": &hcldec.AttrSpec{Name: "mac_addresses", Type: cty.List(cty.String), Required: false}, "vlan_id": &hcldec.AttrSpec{Name: "vlan_id", Type: cty.String, Required: false}, + "switch_config": &hcldec.BlockListSpec{TypeName: "switch_config", Nested: hcldec.ObjectSpec((*common.FlatSwitchConfig)(nil).HCL2Spec())}, + "adapter_config": &hcldec.BlockListSpec{TypeName: "adapter_config", Nested: hcldec.ObjectSpec((*common.FlatAdapterConfig)(nil).HCL2Spec())}, "cpus": &hcldec.AttrSpec{Name: "cpus", Type: cty.Number, Required: false}, "generation": &hcldec.AttrSpec{Name: "generation", Type: cty.Number, Required: false}, "enable_mac_spoofing": &hcldec.AttrSpec{Name: "enable_mac_spoofing", Type: cty.Bool, Required: false}, diff --git a/builder/hyperv/iso/builder_test.go b/builder/hyperv/iso/builder_test.go index e8141ad0..99eb968d 100644 --- a/builder/hyperv/iso/builder_test.go +++ b/builder/hyperv/iso/builder_test.go @@ -622,7 +622,6 @@ func TestUserVariablesInBootCommand(t *testing.T) { step := &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.FlatBootCommand(), - SwitchName: b.config.SwitchName, Ctx: b.config.ctx, } diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 187d830d..cfeb5036 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -241,6 +241,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) state.Put("hook", hook) state.Put("ui", ui) + // Clone will use the discovered switch for outbound access. All the rest will be inherited from the existing VM. + state.Put("swName", b.config.SwitchConfigs[0].SwitchName) + steps := []multistep.Step{ &hypervcommon.StepCreateBuildDir{ TempPath: b.config.TempPath, @@ -264,19 +267,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Label: b.config.FloppyConfig.FloppyLabel, }, commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), - &hypervcommon.StepCreateSwitches{ - MainSwitchName: b.config.SwitchName, - SwitchesNames: b.config.SwitchesNames, - }, + + // Don't create switches, the clone will use the existing ones. + &hypervcommon.StepCloneVM{ CloneFromVMCXPath: b.config.CloneFromVMCXPath, CloneFromVMName: b.config.CloneFromVMName, CloneFromSnapshotName: b.config.CloneFromSnapshotName, CloneAllSnapshots: b.config.CloneAllSnapshots, VMName: b.config.VMName, - SwitchName: b.config.SwitchName, - SwitchesNames: b.config.SwitchesNames, - MacAddresses: b.config.MacAddresses, CompareCopy: b.config.CompareCopy, RamSize: b.config.RamSize, Cpu: b.config.Cpu, @@ -286,7 +285,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) SecureBootTemplate: b.config.SecureBootTemplate, EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions, EnableTPM: b.config.EnableTPM, - MacAddress: b.config.MacAddress, KeepRegistered: b.config.KeepRegistered, AdditionalDiskSize: b.config.AdditionalDiskSize, DiskBlockSize: b.config.DiskBlockSize, @@ -298,6 +296,8 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) &hypervcommon.StepEnableIntegrationService{}, + // Don't use the network adapters specified, we are cloning. The clone should have created them. + &hypervcommon.StepMountDvdDrive{ Generation: b.config.Generation, FirstBootDevice: b.config.FirstBootDevice, @@ -321,11 +321,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Generation: b.config.Generation, }, - &hypervcommon.StepConfigureVlan{ - VlanId: b.config.VlanId, - SwitchVlanId: b.config.SwitchVlanId, - }, - &hypervcommon.StepSetBootOrder{ BootOrder: b.config.BootOrder, }, @@ -335,14 +330,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) }, &hypervcommon.StepRun{ - Headless: b.config.Headless, - SwitchName: b.config.SwitchName, + Headless: b.config.Headless, }, &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.FlatBootCommand(), BootWait: b.config.BootWait, - SwitchName: b.config.SwitchName, Ctx: b.config.ctx, GroupInterval: b.config.BootConfig.BootGroupInterval, }, diff --git a/builder/hyperv/vmcx/builder.hcl2spec.go b/builder/hyperv/vmcx/builder.hcl2spec.go index 5c659ce6..b9080167 100644 --- a/builder/hyperv/vmcx/builder.hcl2spec.go +++ b/builder/hyperv/vmcx/builder.hcl2spec.go @@ -4,130 +4,132 @@ package vmcx import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/common" "github.com/zclconf/go-cty/cty" ) // FlatConfig is an auto-generated flat version of Config. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` - HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` - HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` - HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` - HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` - HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` - ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"` - RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"` - ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"` - TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"` - TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"` - BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` - BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` - BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` - OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"` - Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` - PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` - SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` - SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` - SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` - SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` - SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` - SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` - SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` - SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` - SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` - SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` - SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` - SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` - SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` - SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` - SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` - SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` - SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` - SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` - SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` - SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` - SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` - SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` - SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` - SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` - SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` - SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` - SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` - SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` - SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` - SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` - SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` - SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` - SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` - SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` - SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` - WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` - WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` - WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` - WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` - WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` - WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` - WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` - WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` - WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` - FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` - FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` - FloppyContent map[string]string `mapstructure:"floppy_content" cty:"floppy_content" hcl:"floppy_content"` - FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` - CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` - CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` - CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` - DiskBlockSize *uint `mapstructure:"disk_block_size" required:"false" cty:"disk_block_size" hcl:"disk_block_size"` - RamSize *uint `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"` - SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false" cty:"secondary_iso_images" hcl:"secondary_iso_images"` - AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` - GuestAdditionsMode *string `mapstructure:"guest_additions_mode" required:"false" cty:"guest_additions_mode" hcl:"guest_additions_mode"` - GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"` - VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` - SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` - SwitchesNames []string `mapstructure:"switches_names" required:"false" cty:"switches_names" hcl:"switches_names"` - SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` - MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` - MacAddresses []string `mapstructure:"mac_addresses" required:"false" cty:"mac_addresses" hcl:"mac_addresses"` - VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` - Cpu *uint `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` - Generation *uint `mapstructure:"generation" required:"false" cty:"generation" hcl:"generation"` - EnableMacSpoofing *bool `mapstructure:"enable_mac_spoofing" required:"false" cty:"enable_mac_spoofing" hcl:"enable_mac_spoofing"` - EnableDynamicMemory *bool `mapstructure:"enable_dynamic_memory" required:"false" cty:"enable_dynamic_memory" hcl:"enable_dynamic_memory"` - EnableSecureBoot *bool `mapstructure:"enable_secure_boot" required:"false" cty:"enable_secure_boot" hcl:"enable_secure_boot"` - SecureBootTemplate *string `mapstructure:"secure_boot_template" required:"false" cty:"secure_boot_template" hcl:"secure_boot_template"` - EnableVirtualizationExtensions *bool `mapstructure:"enable_virtualization_extensions" required:"false" cty:"enable_virtualization_extensions" hcl:"enable_virtualization_extensions"` - EnableTPM *bool `mapstructure:"enable_tpm" required:"false" cty:"enable_tpm" hcl:"enable_tpm"` - TempPath *string `mapstructure:"temp_path" required:"false" cty:"temp_path" hcl:"temp_path"` - Version *string `mapstructure:"configuration_version" required:"false" cty:"configuration_version" hcl:"configuration_version"` - KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"` - SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction" hcl:"skip_compaction"` - SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"` - Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"` - FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device" hcl:"first_boot_device"` - BootOrder []string `mapstructure:"boot_order" required:"false" cty:"boot_order" hcl:"boot_order"` - ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"` - ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"` - DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown" hcl:"disable_shutdown"` - DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"` - CloneFromVMCXPath *string `mapstructure:"clone_from_vmcx_path" cty:"clone_from_vmcx_path" hcl:"clone_from_vmcx_path"` - CloneFromVMName *string `mapstructure:"clone_from_vm_name" cty:"clone_from_vm_name" hcl:"clone_from_vm_name"` - CloneFromSnapshotName *string `mapstructure:"clone_from_snapshot_name" required:"false" cty:"clone_from_snapshot_name" hcl:"clone_from_snapshot_name"` - CloneAllSnapshots *bool `mapstructure:"clone_all_snapshots" required:"false" cty:"clone_all_snapshots" hcl:"clone_all_snapshots"` - DifferencingDisk *bool `mapstructure:"differencing_disk" required:"false" cty:"differencing_disk" hcl:"differencing_disk"` - CompareCopy *bool `mapstructure:"copy_in_compare" required:"false" cty:"copy_in_compare" hcl:"copy_in_compare"` + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` + HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` + HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` + HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` + HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` + HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` + ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"` + RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"` + ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"` + TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"` + TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"` + BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` + BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` + BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` + OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"` + Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` + SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` + SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` + SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` + SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` + SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` + SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` + SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` + SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` + WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` + FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` + FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` + FloppyContent map[string]string `mapstructure:"floppy_content" cty:"floppy_content" hcl:"floppy_content"` + FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` + CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` + CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` + CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` + DiskBlockSize *uint `mapstructure:"disk_block_size" required:"false" cty:"disk_block_size" hcl:"disk_block_size"` + RamSize *uint `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"` + SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false" cty:"secondary_iso_images" hcl:"secondary_iso_images"` + AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` + GuestAdditionsMode *string `mapstructure:"guest_additions_mode" required:"false" cty:"guest_additions_mode" hcl:"guest_additions_mode"` + GuestAdditionsPath *string `mapstructure:"guest_additions_path" required:"false" cty:"guest_additions_path" hcl:"guest_additions_path"` + VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` + SwitchName *string `mapstructure:"switch_name" required:"false" cty:"switch_name" hcl:"switch_name"` + SwitchType *string `mapstructure:"switch_type" required:"false" cty:"switch_type" hcl:"switch_type"` + SwitchVlanId *string `mapstructure:"switch_vlan_id" required:"false" cty:"switch_vlan_id" hcl:"switch_vlan_id"` + MacAddress *string `mapstructure:"mac_address" required:"false" cty:"mac_address" hcl:"mac_address"` + VlanId *string `mapstructure:"vlan_id" required:"false" cty:"vlan_id" hcl:"vlan_id"` + SwitchConfigs []common.FlatSwitchConfig `mapstructure:"switch_config" required:"false" cty:"switch_config" hcl:"switch_config"` + AdapterConfigs []common.FlatAdapterConfig `mapstructure:"adapter_config" required:"false" cty:"adapter_config" hcl:"adapter_config"` + Cpu *uint `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` + Generation *uint `mapstructure:"generation" required:"false" cty:"generation" hcl:"generation"` + EnableMacSpoofing *bool `mapstructure:"enable_mac_spoofing" required:"false" cty:"enable_mac_spoofing" hcl:"enable_mac_spoofing"` + EnableDynamicMemory *bool `mapstructure:"enable_dynamic_memory" required:"false" cty:"enable_dynamic_memory" hcl:"enable_dynamic_memory"` + EnableSecureBoot *bool `mapstructure:"enable_secure_boot" required:"false" cty:"enable_secure_boot" hcl:"enable_secure_boot"` + SecureBootTemplate *string `mapstructure:"secure_boot_template" required:"false" cty:"secure_boot_template" hcl:"secure_boot_template"` + EnableVirtualizationExtensions *bool `mapstructure:"enable_virtualization_extensions" required:"false" cty:"enable_virtualization_extensions" hcl:"enable_virtualization_extensions"` + EnableTPM *bool `mapstructure:"enable_tpm" required:"false" cty:"enable_tpm" hcl:"enable_tpm"` + TempPath *string `mapstructure:"temp_path" required:"false" cty:"temp_path" hcl:"temp_path"` + Version *string `mapstructure:"configuration_version" required:"false" cty:"configuration_version" hcl:"configuration_version"` + KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"` + SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction" hcl:"skip_compaction"` + SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"` + Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"` + FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device" hcl:"first_boot_device"` + BootOrder []string `mapstructure:"boot_order" required:"false" cty:"boot_order" hcl:"boot_order"` + ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"` + ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"` + DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown" hcl:"disable_shutdown"` + DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"` + CloneFromVMCXPath *string `mapstructure:"clone_from_vmcx_path" cty:"clone_from_vmcx_path" hcl:"clone_from_vmcx_path"` + CloneFromVMName *string `mapstructure:"clone_from_vm_name" cty:"clone_from_vm_name" hcl:"clone_from_vm_name"` + CloneFromSnapshotName *string `mapstructure:"clone_from_snapshot_name" required:"false" cty:"clone_from_snapshot_name" hcl:"clone_from_snapshot_name"` + CloneAllSnapshots *bool `mapstructure:"clone_all_snapshots" required:"false" cty:"clone_all_snapshots" hcl:"clone_all_snapshots"` + DifferencingDisk *bool `mapstructure:"differencing_disk" required:"false" cty:"differencing_disk" hcl:"differencing_disk"` + CompareCopy *bool `mapstructure:"copy_in_compare" required:"false" cty:"copy_in_compare" hcl:"copy_in_compare"` } // FlatMapstructure returns a new FlatConfig. @@ -229,11 +231,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "switch_name": &hcldec.AttrSpec{Name: "switch_name", Type: cty.String, Required: false}, - "switches_names": &hcldec.AttrSpec{Name: "switches_names", Type: cty.List(cty.String), Required: false}, + "switch_type": &hcldec.AttrSpec{Name: "switch_type", Type: cty.String, Required: false}, "switch_vlan_id": &hcldec.AttrSpec{Name: "switch_vlan_id", Type: cty.String, Required: false}, "mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false}, - "mac_addresses": &hcldec.AttrSpec{Name: "mac_addresses", Type: cty.List(cty.String), Required: false}, "vlan_id": &hcldec.AttrSpec{Name: "vlan_id", Type: cty.String, Required: false}, + "switch_config": &hcldec.BlockListSpec{TypeName: "switch_config", Nested: hcldec.ObjectSpec((*common.FlatSwitchConfig)(nil).HCL2Spec())}, + "adapter_config": &hcldec.BlockListSpec{TypeName: "adapter_config", Nested: hcldec.ObjectSpec((*common.FlatAdapterConfig)(nil).HCL2Spec())}, "cpus": &hcldec.AttrSpec{Name: "cpus", Type: cty.Number, Required: false}, "generation": &hcldec.AttrSpec{Name: "generation", Type: cty.Number, Required: false}, "enable_mac_spoofing": &hcldec.AttrSpec{Name: "enable_mac_spoofing", Type: cty.Bool, Required: false}, diff --git a/builder/hyperv/vmcx/builder_test.go b/builder/hyperv/vmcx/builder_test.go index 903bf651..57029b61 100644 --- a/builder/hyperv/vmcx/builder_test.go +++ b/builder/hyperv/vmcx/builder_test.go @@ -16,7 +16,7 @@ import ( packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) -func testConfig() map[string]interface{} { +func deprecatedTestConfig() map[string]interface{} { return map[string]interface{}{ "iso_checksum": "md5:0B0F137F17AC10944716020B018F8126", "iso_url": "http://www.packer.io", @@ -29,6 +29,21 @@ func testConfig() map[string]interface{} { common.BuildNameConfigKey: "foo", } } +func testConfig() map[string]interface{} { + return map[string]interface{}{ + "iso_checksum": "md5:0B0F137F17AC10944716020B018F8126", + "iso_url": "http://www.packer.io", + "shutdown_command": "yes", + "ssh_username": "foo", + "switch_config": map[string]interface{}{ + "switch_name": "switch", // to avoid using builder.detectSwitchName which can lock down in travis-ci + }, + "memory": 64, + "guest_additions_mode": "none", + "clone_from_vmcx_path": "generated", + common.BuildNameConfigKey: "foo", + } +} func TestBuilder_ImplementsBuilder(t *testing.T) { var raw interface{} @@ -40,7 +55,7 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder - config := testConfig() + config := deprecatedTestConfig() //Create vmcx folder td, err := os.MkdirTemp("", "packer") @@ -51,9 +66,14 @@ func TestBuilderPrepare_Defaults(t *testing.T) { config["clone_from_vmcx_path"] = td _, warns, err := b.Prepare(config) - if len(warns) > 0 { + if len(warns) != 1 { t.Fatalf("bad: %#v", warns) } + + if warns[0] != "SwitchName is deprecated and should be converted to SwitchConfigs" { + t.Fatalf("bad: %#v", warns) + } + if err != nil { t.Fatalf("should not have error: %s", err) } @@ -512,7 +532,6 @@ func TestUserVariablesInBootCommand(t *testing.T) { step := &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.FlatBootCommand(), - SwitchName: b.config.SwitchName, Ctx: b.config.ctx, } diff --git a/docs-partials/builder/hyperv/common/AdapterConfig-not-required.mdx b/docs-partials/builder/hyperv/common/AdapterConfig-not-required.mdx new file mode 100644 index 00000000..98c879a4 --- /dev/null +++ b/docs-partials/builder/hyperv/common/AdapterConfig-not-required.mdx @@ -0,0 +1,19 @@ + + +- `adapter_name` (string) - The name of the network adapter. + By default, leaving this value unset will cause Packer to + try and determine the switch to use by looking for an external switch + that is up and running. + +- `switch_name` (string) - The name of the switch for this adapter + +- `vlan_id` (string) - This is the VLAN of the virtual switch's + network card. By default, none is set. If none is set then a VLAN is not + set on the switch's network card. If this value is set it should match + the VLAN specified in by vlan_id. + +- `mac_address` (string) - This allows a specific MAC address to be used on + the default main virtual network card. The MAC address must be a string with + no delimiters, for example "037777777777deadbeef". + + diff --git a/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx b/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx index 79984add..df17d553 100644 --- a/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx +++ b/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx @@ -37,9 +37,10 @@ try and determine the switch to use by looking for an external switch that is up and running. -- `switches_names` ([]string) - The name of the switches to connect the virtual +- `switch_type` (string) - The type of the main switch to connect the virtual machine to. By default, leaving this value unset will cause Packer to - not use any additional switches. + try and determine the switch to use by looking for an external switch + that is up and running. - `switch_vlan_id` (string) - This is the VLAN of the virtual switch's network card. By default, none is set. If none is set then a VLAN is not @@ -50,14 +51,16 @@ the default main virtual network card. The MAC address must be a string with no delimiters, for example "0000deadbeef". -- `mac_addresses` ([]string) - This allows a specific MAC addresses to be used on - the optional virtual network cards set in `switches_names` array. The MAC addresses must be strings with - no delimiters, for example "0000deadbeef". - - `vlan_id` (string) - This is the VLAN of the virtual machine's network card for the new virtual machine. By default none is set. If none is set then VLANs are not set on the virtual machine's network card. +- `switch_config` ([]SwitchConfig) - This allows for multiple switches to be configured. + This should be used exclusively with SwitchName, SwitchVlanId, and MacAddress. + +- `adapter_config` ([]AdapterConfig) - This allows for multiple switches to be configured. + This should be used exclusively with MacAddress and VlanId. + - `cpus` (uint) - The number of CPUs the virtual machine should use. If this isn't specified, the default is 1 CPU. diff --git a/docs-partials/builder/hyperv/common/SwitchConfig-not-required.mdx b/docs-partials/builder/hyperv/common/SwitchConfig-not-required.mdx new file mode 100644 index 00000000..a8433cb8 --- /dev/null +++ b/docs-partials/builder/hyperv/common/SwitchConfig-not-required.mdx @@ -0,0 +1,16 @@ + + +- `switch_name` (string) - The name of the main switch to connect the virtual + machine to. By default, leaving this value unset will cause Packer to + try and determine the switch to use by looking for an external switch + that is up and running. + +- `switch_type` (string) - This allows the specification of the switch type. + the default will be Internal if unspecificed + +- `switch_vlan_id` (string) - This is the VLAN of the virtual switch's + network card. By default, none is set. If none is set then a VLAN is not + set on the switch's network card. If this value is set it should match + the VLAN specified in by vlan_id. + +