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 cce32d8e..712fa655 100644 --- a/.web-docs/components/builder/iso/README.md +++ b/.web-docs/components/builder/iso/README.md @@ -253,24 +253,35 @@ 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. + +- `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 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 + 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". - `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 36f80bc5..631f15e8 100644 --- a/.web-docs/components/builder/vmcx/README.md +++ b/.web-docs/components/builder/vmcx/README.md @@ -280,24 +280,35 @@ 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. + +- `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 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 + 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". - `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 707e24dd..bc7446da 100644 --- a/builder/hyperv/common/config.go +++ b/builder/hyperv/common/config.go @@ -74,24 +74,35 @@ 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 type 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. + 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 + // 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 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"` @@ -197,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 { @@ -333,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) @@ -388,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 12d2de3a..bed36228 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -54,26 +54,23 @@ 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 diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index a7d2cb78..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,7 +145,7 @@ type DriverMock struct { CreateVirtualMachine_Ram int64 CreateVirtualMachine_DiskSize int64 CreateVirtualMachine_DiskBlockSize int64 - CreateVirtualMachine_SwitchName string + CreateVirtualMachine_MainSwitch string CreateVirtualMachine_Generation uint CreateVirtualMachine_DifferentialDisk bool CreateVirtualMachine_FixedVHD bool @@ -154,7 +161,7 @@ type DriverMock struct { CloneVirtualMachine_Path string CloneVirtualMachine_HarddrivePath string CloneVirtualMachine_Ram int64 - CloneVirtualMachine_SwitchName string + CloneVirtualMachine_MainSwitch string CloneVirtualMachine_Copy bool CloneVirtualMachine_Err error @@ -398,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 @@ -405,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) { @@ -465,7 +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_MainSwitch = switchName d.CreateVirtualMachine_Generation = generation d.CreateVirtualMachine_DifferentialDisk = diffDisks d.CreateVirtualMachine_Version = version @@ -474,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, copyTF bool) error { + harddrivePath string, ram int64, mainSwitch string, copyTF bool) error { d.CloneVirtualMachine_Called = true d.CloneVirtualMachine_CloneFromVmcxPath = cloneFromVmcxPath d.CloneVirtualMachine_CloneFromVmName = cloneFromVmName @@ -484,7 +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_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 ffef9220..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, 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, + 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, copyTF bool) error { + ram int64, mainSwitch string, copyTF bool) error { return hyperv.CloneVirtualMachine(cloneFromVmcxPath, cloneFromVmName, cloneFromSnapshotName, - cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName, 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 d95fe0d6..c9560cd4 100644 --- a/builder/hyperv/common/powershell/hyperv/hyperv.go +++ b/builder/hyperv/common/powershell/hyperv/hyperv.go @@ -26,7 +26,7 @@ type scriptOptions struct { MemoryStartupBytes int64 NewVHDSizeBytes int64 VHDBlockSizeBytes int64 - SwitchName string + MainSwitch string Generation uint DiffDisks bool FixedVHD bool @@ -355,7 +355,7 @@ $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 -}} `)) @@ -396,7 +396,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, mainSwitch string, generation uint, diffDisks bool, fixedVHD bool, version string) error { opts := scriptOptions{ Version: version, @@ -406,7 +406,7 @@ func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram MemoryStartupBytes: ram, NewVHDSizeBytes: diskSize, VHDBlockSizeBytes: diskBlockSize, - SwitchName: switchName, + MainSwitch: mainSwitch, Generation: generation, DiffDisks: diffDisks, FixedVHD: fixedVHD, @@ -509,20 +509,20 @@ 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 +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, 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) @@ -578,14 +578,14 @@ if ($vm) { } ` 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), 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, copyTF bool) error { + path string, harddrivePath string, ram int64, mainSwitch string, copyTF bool) error { if cloneFromVmName != "" { if err := ExportVmcxVirtualMachine(path, cloneFromVmName, @@ -600,7 +600,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, mainSwitch, copyTF); err != nil { return err } @@ -1035,7 +1035,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 @@ -1157,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 = ` @@ -1199,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) { @@ -1221,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 98421378..328991a6 100644 --- a/builder/hyperv/common/step_clone_vm.go +++ b/builder/hyperv/common/step_clone_vm.go @@ -25,7 +25,6 @@ type StepCloneVM struct { CloneFromSnapshotName string CloneAllSnapshots bool VMName string - SwitchName string CompareCopy bool RamSize uint Cpu uint @@ -35,7 +34,6 @@ type StepCloneVM struct { SecureBootTemplate string EnableVirtualizationExtensions bool EnableTPM bool - MacAddress string KeepRegistered bool AdditionalDiskSize []uint DiskBlockSize uint @@ -44,6 +42,7 @@ type StepCloneVM struct { 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) @@ -66,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.CompareCopy) + harddrivePath, ramSize, mainSwitch, s.CompareCopy) if err != nil { err := fmt.Errorf("Error cloning virtual machine: %s", err) state.Put("error", err) @@ -157,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 e8587781..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,55 +22,51 @@ const ( // // Produces: // -// SwitchName string - The name of the Switch -type StepCreateSwitch struct { - // Specifies the name of the switch to be created. - SwitchName 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 +// SwitchConfigs []SwitchConfigs - The new swichConfigs +type StepCreateSwitches struct { + // Specifies the switches to be created. + SwitchConfigs []SwitchConfig - 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) - if len(s.SwitchType) == 0 { - s.SwitchType = DefaultSwitchType + 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 { + 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...", sw.SwitchName)) + } } - ui.Say(fmt.Sprintf("Creating switch '%v' if required...", s.SwitchName)) - - 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 - } - - s.createdSwitch = createdSwitch - - if !s.createdSwitch { - ui.Say(fmt.Sprintf(" switch '%v' already exists. Will not delete on cleanup...", s.SwitchName)) - } - - // Set the final name in the state bag so others can use it - state.Put("SwitchName", s.SwitchName) - 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 len(s.SwitchConfigs) == 0 { return } @@ -77,8 +74,12 @@ 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)) + for index, sw := range s.SwitchConfigs { + if s.createdSwitch[index] { + 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 631ff43f..ddcfb14c 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -21,7 +21,6 @@ import ( // VMName string - The name of the VM type StepCreateVM struct { VMName string - SwitchName string HarddrivePath string RamSize uint DiskSize uint @@ -37,7 +36,6 @@ type StepCreateVM struct { EnableTPM bool AdditionalDiskSize []uint DifferencingDisk bool - MacAddress string FixedVHD bool Version string KeepRegistered bool @@ -47,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 { @@ -80,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.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) @@ -88,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) @@ -168,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/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) { diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index d3d39ba2..8ef9b8e1 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -223,12 +223,11 @@ 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{ + SwitchConfigs: b.config.SwitchConfigs, }, &hypervcommon.StepCreateVM{ VMName: b.config.VMName, - SwitchName: b.config.SwitchName, RamSize: b.config.RamSize, DiskSize: b.config.DiskSize, DiskBlockSize: b.config.DiskBlockSize, @@ -243,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, @@ -273,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, }, @@ -287,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 94473be3..a97b809c 100644 --- a/builder/hyperv/iso/builder.hcl2spec.go +++ b/builder/hyperv/iso/builder.hcl2spec.go @@ -4,125 +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"` - 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"` - 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. @@ -224,9 +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}, + "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}, "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 ef7b2529..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,16 +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.StepCreateSwitch{ - SwitchName: b.config.SwitchName, - }, + + // 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, CompareCopy: b.config.CompareCopy, RamSize: b.config.RamSize, Cpu: b.config.Cpu, @@ -283,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, @@ -295,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, @@ -318,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, }, @@ -332,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 30336bbf..b9080167 100644 --- a/builder/hyperv/vmcx/builder.hcl2spec.go +++ b/builder/hyperv/vmcx/builder.hcl2spec.go @@ -4,128 +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"` - 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"` - 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. @@ -227,9 +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}, + "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}, "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 170ed91f..57029b61 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" @@ -17,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", @@ -30,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{} @@ -41,10 +55,10 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { func TestBuilderPrepare_Defaults(t *testing.T) { var b Builder - config := testConfig() + config := deprecatedTestConfig() //Create vmcx folder - td, err := ioutil.TempDir("", "packer") + td, err := os.MkdirTemp("", "packer") if err != nil { t.Fatalf("err: %s", err) } @@ -52,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) } @@ -69,7 +88,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 +125,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 +149,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 +197,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 +232,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 +277,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 +356,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 +398,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 +423,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 +460,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 +498,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) } @@ -513,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 816be863..df17d553 100644 --- a/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx +++ b/docs-partials/builder/hyperv/common/CommonConfig-not-required.mdx @@ -32,24 +32,35 @@ 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. + +- `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 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 + 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". - `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. + + 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() {