remove and vendor hyperv (#10952)

This commit is contained in:
Sylvia Moss 2021-04-21 16:32:34 +02:00 committed by GitHub
parent 2db338e322
commit 469f033c36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 27 additions and 11566 deletions

View File

@ -13,9 +13,6 @@
/builder/digitalocean/ @andrewsomething
/website/pages/docs/builders/digitalocean* @andrewsomething
/builder/hyperv/ @taliesins
/website/pages/docs/builders/hyperv* @taliesins
/examples/jdcloud/ @XiaohanLiang @remrain
/builder/jdcloud/ @XiaohanLiang @remrain
/website/pages/docs/builders/jdcloud* @XiaohanLiang @remrain

View File

@ -1,73 +0,0 @@
package common
import (
"fmt"
"os"
"path/filepath"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
// This is the common builder ID to all of these artifacts.
const BuilderId = "MSOpenTech.hyperv"
// Artifact is the result of running the hyperv builder, namely a set
// of files associated with the resulting machine.
type artifact struct {
dir string
f []string
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
// NewArtifact returns a hyperv artifact containing the files
// in the given directory.
func NewArtifact(dir string, generatedData map[string]interface{}) (packersdk.Artifact, error) {
files := make([]string, 0, 5)
visit := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
files = append(files, path)
}
return nil
}
if err := filepath.Walk(dir, visit); err != nil {
return nil, err
}
return &artifact{
dir: dir,
f: files,
StateData: generatedData,
}, nil
}
func (*artifact) BuilderId() string {
return BuilderId
}
func (a *artifact) Files() []string {
return a.f
}
func (*artifact) Id() string {
return "VM"
}
func (a *artifact) String() string {
return fmt.Sprintf("VM files in directory: %s", a.dir)
}
func (a *artifact) State(name string) interface{} {
return a.StateData[name]
}
func (a *artifact) Destroy() error {
return os.RemoveAll(a.dir)
}

View File

@ -1,47 +0,0 @@
package common
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func TestArtifact_impl(t *testing.T) {
var _ packersdk.Artifact = new(artifact)
}
func TestNewArtifact(t *testing.T) {
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
err = ioutil.WriteFile(filepath.Join(td, "a"), []byte("foo"), 0644)
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Mkdir(filepath.Join(td, "b"), 0755); err != nil {
t.Fatalf("err: %s", err)
}
generatedData := map[string]interface{}{"generated_data": "data"}
a, err := NewArtifact(td, generatedData)
if err != nil {
t.Fatalf("err: %s", err)
}
if a.BuilderId() != BuilderId {
t.Fatalf("bad: %#v", a.BuilderId())
}
if len(a.Files()) != 1 {
t.Fatalf("should length 1: %d", len(a.Files()))
}
if a.State("generated_data") != "data" {
t.Fatalf("bad: should length have generated_data: %s", a.State("generated_data"))
}
}

View File

@ -1,434 +0,0 @@
//go:generate packer-sdc struct-markdown
package common
import (
"fmt"
"log"
"os"
"strings"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
powershell "github.com/hashicorp/packer/builder/hyperv/common/powershell"
"github.com/hashicorp/packer/builder/hyperv/common/powershell/hyperv"
)
const (
DefaultDiskSize = 40 * 1024 // ~40GB
MinDiskSize = 256 // 256MB
MaxDiskSize = 64 * 1024 * 1024 // 64TB
MaxVHDSize = 2040 * 1024 // 2040GB
DefaultDiskBlockSize = 32 // 32MB
MinDiskBlockSize = 1 // 1MB
MaxDiskBlockSize = 256 // 256MB
DefaultRamSize = 1 * 1024 // 1GB
MinRamSize = 32 // 32MB
MaxRamSize = 32 * 1024 // 32GB
MinNestedVirtualizationRamSize = 4 * 1024 // 4GB
LowRam = 256 // 256MB
DefaultUsername = ""
DefaultPassword = ""
)
type CommonConfig struct {
commonsteps.FloppyConfig `mapstructure:",squash"`
commonsteps.CDConfig `mapstructure:",squash"`
// The block size of the VHD to be created.
// Recommended disk block size for Linux hyper-v guests is 1 MiB. This
// defaults to "32" MiB.
DiskBlockSize uint `mapstructure:"disk_block_size" required:"false"`
// The amount, in megabytes, of RAM to assign to the
// VM. By default, this is 1 GB.
RamSize uint `mapstructure:"memory" required:"false"`
// A list of ISO paths to
// attach to a VM when it is booted. This is most useful for unattended
// Windows installs, which look for an Autounattend.xml file on removable
// media. By default, no secondary ISO will be attached.
SecondaryDvdImages []string `mapstructure:"secondary_iso_images" required:"false"`
// The size or sizes of any
// additional hard disks for the VM in megabytes. If this is not specified
// then the VM will only contain a primary hard disk. Additional drives
// will be attached to the SCSI interface only. The builder uses
// expandable rather than fixed-size virtual hard disks, so the actual
// file representing the disk will not use the full size unless it is
// full.
AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false"`
// If set to attach then attach and
// mount the ISO image specified in guest_additions_path. If set to
// none then guest additions are not attached and mounted; This is the
// default.
GuestAdditionsMode string `mapstructure:"guest_additions_mode" required:"false"`
// The path to the ISO image for guest
// additions.
GuestAdditionsPath string `mapstructure:"guest_additions_path" required:"false"`
// This is the name of the new virtual machine,
// 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
// 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 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"`
// This allows a specific MAC address to be used on
// the default 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"`
// 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"`
// The Hyper-V generation for the virtual machine. By
// default, this is 1. Generation 2 Hyper-V virtual machines do not support
// floppy drives. In this scenario use secondary_iso_images instead. Hard
// drives and DVD drives will also be SCSI and not IDE.
Generation uint `mapstructure:"generation" required:"false"`
// If true enable MAC address spoofing
// for the virtual machine. This defaults to false.
EnableMacSpoofing bool `mapstructure:"enable_mac_spoofing" required:"false"`
// If true enable dynamic memory for
// the virtual machine. This defaults to false.
EnableDynamicMemory bool `mapstructure:"enable_dynamic_memory" required:"false"`
// If true enable secure boot for the
// virtual machine. This defaults to false. See secure_boot_template
// below for additional settings.
EnableSecureBoot bool `mapstructure:"enable_secure_boot" required:"false"`
// The secure boot template to be
// configured. Valid values are "MicrosoftWindows" (Windows) or
// "MicrosoftUEFICertificateAuthority" (Linux). This only takes effect if
// enable_secure_boot is set to "true". This defaults to "MicrosoftWindows".
SecureBootTemplate string `mapstructure:"secure_boot_template" required:"false"`
// If true enable
// virtualization extensions for the virtual machine. This defaults to
// false. For nested virtualization you need to enable MAC spoofing,
// disable dynamic memory and have at least 4GB of RAM assigned to the
// virtual machine.
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions" required:"false"`
// The location under which Packer will create a directory to house all the
// VM files and folders during the build. By default `%TEMP%` is used
// which, for most systems, will evaluate to
// `%USERPROFILE%/AppData/Local/Temp`.
//
// The build directory housed under `temp_path` will have a name similar to
// `packerhv1234567`. The seven digit number at the end of the name is
// automatically generated by Packer to ensure the directory name is
// unique.
TempPath string `mapstructure:"temp_path" required:"false"`
// This allows you to set the vm version when calling New-VM to generate
// the vm.
Version string `mapstructure:"configuration_version" required:"false"`
// If "true", Packer will not delete the VM from
// The Hyper-V manager.
KeepRegistered bool `mapstructure:"keep_registered" required:"false"`
// If true skip compacting the hard disk for
// the virtual machine when exporting. This defaults to false.
SkipCompaction bool `mapstructure:"skip_compaction" required:"false"`
// If true Packer will skip the export of the VM.
// If you are interested only in the VHD/VHDX files, you can enable this
// option. The resulting VHD/VHDX file will be output to
// <output_directory>/Virtual Hard Disks. By default this option is false
// and Packer will export the VM to output_directory.
SkipExport bool `mapstructure:"skip_export" required:"false"`
// Packer defaults to building Hyper-V virtual
// machines by launching a GUI that shows the console of the machine being
// built. When this value is set to true, the machine will start without a
// console.
Headless bool `mapstructure:"headless" required:"false"`
// When configured, determines the device or device type that is given preferential
// treatment when choosing a boot device.
//
// For Generation 1:
// - `IDE`
// - `CD` *or* `DVD`
// - `Floppy`
// - `NET`
//
// For Generation 2:
// - `IDE:x:y`
// - `SCSI:x:y`
// - `CD` *or* `DVD`
// - `NET`
FirstBootDevice string `mapstructure:"first_boot_device" required:"false"`
// When configured, the boot order determines the order of the devices
// from which to boot.
//
// The device name must be in the form of `SCSI:x:y`, for example,
// to boot from the first scsi device use `SCSI:0:0`.
//
// **NB** You should also set `first_boot_device` (e.g. `DVD`).
//
// **NB** Although the VM will have this initial boot order, the OS can
// change it, for example, Ubuntu 18.04 will modify the boot order to
// include itself as the first boot option.
//
// **NB** This only works for Generation 2 machines.
BootOrder []string `mapstructure:"boot_order" required:"false"`
}
func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) ([]error, []string) {
// Accumulate any errors and warns
var errs []error
var warns []string
if c.VMName == "" {
c.VMName = fmt.Sprintf("packer-%s", pc.PackerBuildName)
log.Println(fmt.Sprintf("%s: %v", "VMName", c.VMName))
}
if c.SwitchName == "" {
c.SwitchName = c.detectSwitchName(pc.PackerBuildName)
log.Println(fmt.Sprintf("Using switch %s", c.SwitchName))
}
if c.Generation < 1 || c.Generation > 2 {
c.Generation = 1
}
if c.Generation == 2 {
if len(c.FloppyFiles) > 0 || len(c.FloppyDirectories) > 0 {
err := fmt.Errorf("Generation 2 vms don't support floppy drives. Use ISO image instead.")
errs = append(errs, err)
}
}
if len(c.AdditionalDiskSize) > 64 {
errs = append(errs, fmt.Errorf("VM's currently support a maximum of 64 additional SCSI attached disks."))
}
// Errors
errs = append(errs, c.FloppyConfig.Prepare(ctx)...)
errs = append(errs, c.CDConfig.Prepare(ctx)...)
if c.GuestAdditionsMode == "" {
if c.GuestAdditionsPath != "" {
c.GuestAdditionsMode = "attach"
} else {
c.GuestAdditionsPath = os.Getenv("WINDIR") + "\\system32\\vmguest.iso"
if _, err := os.Stat(c.GuestAdditionsPath); os.IsNotExist(err) {
if err != nil {
c.GuestAdditionsPath = ""
c.GuestAdditionsMode = "none"
} else {
c.GuestAdditionsMode = "attach"
}
}
}
}
if c.GuestAdditionsPath == "" && c.GuestAdditionsMode == "attach" {
c.GuestAdditionsPath = os.Getenv("WINDIR") + "\\system32\\vmguest.iso"
if _, err := os.Stat(c.GuestAdditionsPath); os.IsNotExist(err) {
if err != nil {
c.GuestAdditionsPath = ""
}
}
}
for _, isoPath := range c.SecondaryDvdImages {
if _, err := os.Stat(isoPath); os.IsNotExist(err) {
if err != nil {
errs = append(
errs, fmt.Errorf("Secondary Dvd image does not exist: %s", err))
}
}
}
numberOfIsos := len(c.SecondaryDvdImages)
if c.GuestAdditionsMode == "attach" {
if _, err := os.Stat(c.GuestAdditionsPath); os.IsNotExist(err) {
if err != nil {
errs = append(
errs, fmt.Errorf("Guest additions iso does not exist: %s", err))
}
}
numberOfIsos = numberOfIsos + 1
}
if c.Generation < 2 && numberOfIsos > 2 {
if c.GuestAdditionsMode == "attach" {
errs = append(errs, fmt.Errorf("There are only 2 ide controllers available, so "+
"we can't support guest additions and these secondary dvds: %s",
strings.Join(c.SecondaryDvdImages, ", ")))
} else {
errs = append(errs, fmt.Errorf("There are only 2 ide controllers available, so "+
"we can't support these secondary dvds: %s",
strings.Join(c.SecondaryDvdImages, ", ")))
}
} else if c.Generation > 1 && len(c.SecondaryDvdImages) > 16 {
if c.GuestAdditionsMode == "attach" {
errs = append(errs, fmt.Errorf("There are not enough drive letters available for "+
"scsi (limited to 16), so we can't support guest additions and these secondary dvds: %s",
strings.Join(c.SecondaryDvdImages, ", ")))
} else {
errs = append(errs, fmt.Errorf("There are not enough drive letters available for "+
"scsi (limited to 16), so we can't support these secondary dvds: %s",
strings.Join(c.SecondaryDvdImages, ", ")))
}
}
if c.EnableVirtualizationExtensions {
hasVirtualMachineVirtualizationExtensions, err := powershell.HasVirtualMachineVirtualizationExtensions()
if err != nil {
errs = append(errs, fmt.Errorf("Failed detecting virtual machine virtualization "+
"extensions support: %s", err))
} else {
if !hasVirtualMachineVirtualizationExtensions {
errs = append(errs, fmt.Errorf("This version of Hyper-V does not support "+
"virtual machine virtualization extension. Please use Windows 10 or Windows Server 2016 "+
"or newer."))
}
}
}
if c.FirstBootDevice != "" {
_, _, _, err := ParseBootDeviceIdentifier(c.FirstBootDevice, c.Generation)
if err != nil {
errs = append(errs, fmt.Errorf("first_boot_device: %s", err))
}
}
if c.EnableVirtualizationExtensions {
if c.EnableDynamicMemory {
warning := fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
"dynamic memory should not be allowed.")
warns = Appendwarns(warns, warning)
}
if !c.EnableMacSpoofing {
warning := fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
"mac spoofing should be allowed.")
warns = Appendwarns(warns, warning)
}
if c.RamSize < MinNestedVirtualizationRamSize {
warning := fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
"there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start " +
"any nested VMs.")
warns = Appendwarns(warns, warning)
}
}
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)
}
err = c.checkRamSize()
if err != nil {
errs = append(errs, err)
}
// warns
warning := c.checkHostAvailableMemory()
if warning != "" {
warns = Appendwarns(warns, warning)
}
if errs != nil && len(errs) > 0 {
return errs, warns
}
return nil, warns
}
func (c *CommonConfig) checkDiskBlockSize() error {
if c.DiskBlockSize == 0 {
c.DiskBlockSize = DefaultDiskBlockSize
}
log.Println(fmt.Sprintf("%s: %v", "DiskBlockSize", c.DiskBlockSize))
if c.DiskBlockSize < MinDiskBlockSize {
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size >= %v MB, but defined: %v",
MinDiskBlockSize, c.DiskBlockSize)
} else if c.DiskBlockSize > MaxDiskBlockSize {
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size <= %v MB, but defined: %v",
MaxDiskBlockSize, c.DiskBlockSize)
}
return nil
}
func (c *CommonConfig) checkHostAvailableMemory() string {
powershellAvailable, _, _ := powershell.IsPowershellAvailable()
if powershellAvailable {
freeMB := powershell.GetHostAvailableMemory()
if (freeMB - float64(c.RamSize)) < LowRam {
return fmt.Sprintf("Hyper-V might fail to create a VM if there is not enough free memory in the system.")
}
}
return ""
}
func (c *CommonConfig) checkRamSize() error {
if c.RamSize == 0 {
c.RamSize = DefaultRamSize
}
log.Println(fmt.Sprintf("%s: %v", "RamSize", c.RamSize))
if c.RamSize < MinRamSize {
return fmt.Errorf("memory: Virtual machine requires memory size >= %v MB, but defined: %v",
MinRamSize, c.RamSize)
} else if c.RamSize > MaxRamSize {
return fmt.Errorf("memory: Virtual machine requires memory size <= %v MB, but defined: %v",
MaxRamSize, c.RamSize)
}
return nil
}
func (c *CommonConfig) detectSwitchName(buildName string) string {
powershellAvailable, _, _ := powershell.IsPowershellAvailable()
if powershellAvailable {
// no switch name, try to get one attached to a online network adapter
onlineSwitchName, err := hyperv.GetExternalOnlineVirtualSwitch()
if onlineSwitchName != "" && err == nil {
return onlineSwitchName
}
}
return fmt.Sprintf("packer-%s", buildName)
}
func Appendwarns(slice []string, data ...string) []string {
m := len(slice)
n := m + len(data)
if n > cap(slice) { // if necessary, reallocate
// allocate double what's needed, for future growth.
newSlice := make([]string, (n+1)*2)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:n]
copy(slice[m:n], data)
return slice
}

View File

@ -1,133 +0,0 @@
package common
import (
"context"
)
// A driver is able to talk to HyperV and perform certain
// operations with it. Some of the operations on here may seem overly
// specific, but they were built specifically in mind to handle features
// of the HyperV builder for Packer, and to abstract differences in
// versions out of the builder steps, so sometimes the methods are
// extremely specific.
type Driver interface {
// Checks if the VM named is running.
IsRunning(string) (bool, error)
// Checks if the VM named is off.
IsOff(string) (bool, error)
//How long has VM been on
Uptime(vmName string) (uint64, error)
// Start starts a VM specified by the name given.
Start(string) error
// Stop stops a VM specified by the name given.
Stop(string) error
// Verify checks to make sure that this driver should function
// properly. If there is any indication the driver can't function,
// this will return an error.
Verify() error
// Finds the MAC address of the NIC nic0
Mac(string) (string, error)
// Finds the IP address of a VM connected that uses DHCP by its MAC address
IpAddress(string) (string, error)
// Finds the hostname for the ip address
GetHostName(string) (string, error)
// Finds the IP address of a host adapter connected to switch
GetHostAdapterIpAddressForSwitch(string) (string, error)
// Type scan codes to virtual keyboard of vm
TypeScanCodes(string, string) error
//Get the ip address for network adaptor
GetVirtualMachineNetworkAdapterAddress(string) (string, error)
//Set the vlan to use for switch
SetNetworkAdapterVlanId(string, string) error
//Set the vlan to use for machine
SetVirtualMachineVlanId(string, string) error
SetVmNetworkAdapterMacAddress(string, string) error
//Replace the network adapter with a (non-)legacy adapter
ReplaceVirtualMachineNetworkAdapter(string, bool) error
UntagVirtualMachineNetworkAdapterVlan(string, string) error
CreateExternalVirtualSwitch(string, string) error
GetVirtualMachineSwitchName(string) (string, error)
ConnectVirtualMachineNetworkAdapterToSwitch(string, string) error
CreateVirtualSwitch(string, string) (bool, error)
DeleteVirtualSwitch(string) error
CheckVMName(string) error
CreateVirtualMachine(string, string, string, int64, int64, int64, string, uint, bool, bool, string) error
AddVirtualMachineHardDrive(string, string, string, int64, int64, string) error
CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string, bool) error
DeleteVirtualMachine(string) error
GetVirtualMachineGeneration(string) (uint, error)
SetVirtualMachineCpuCount(string, uint) error
SetVirtualMachineMacSpoofing(string, bool) error
SetVirtualMachineDynamicMemory(string, bool) error
SetVirtualMachineSecureBoot(string, bool, string) error
SetVirtualMachineVirtualizationExtensions(string, bool) error
EnableVirtualMachineIntegrationService(string, string) error
ExportVirtualMachine(string, string) error
PreserveLegacyExportBehaviour(string, string) error
MoveCreatedVHDsToOutputDir(string, string) error
CompactDisks(string) (string, error)
RestartVirtualMachine(string) error
CreateDvdDrive(string, string, uint) (uint, uint, error)
MountDvdDrive(string, string, uint, uint) error
SetBootDvdDrive(string, uint, uint, uint) error
SetFirstBootDevice(string, string, uint, uint, uint) error
SetBootOrder(string, []string) error
UnmountDvdDrive(string, uint, uint) error
DeleteDvdDrive(string, uint, uint) error
MountFloppyDrive(string, string) error
UnmountFloppyDrive(string) error
// Connect connects to a VM specified by the name given.
Connect(string) (context.CancelFunc, error)
// Disconnect disconnects to a VM specified by the context cancel function.
Disconnect(context.CancelFunc)
}

View File

@ -1,647 +0,0 @@
package common
import (
"context"
)
type DriverMock struct {
IsRunning_Called bool
IsRunning_VmName string
IsRunning_Return bool
IsRunning_Err error
IsOff_Called bool
IsOff_VmName string
IsOff_Return bool
IsOff_Err error
Uptime_Called bool
Uptime_VmName string
Uptime_Return uint64
Uptime_Err error
Start_Called bool
Start_VmName string
Start_Err error
Stop_Called bool
Stop_VmName string
Stop_Err error
Verify_Called bool
Verify_Err error
Mac_Called bool
Mac_VmName string
Mac_Return string
Mac_Err error
IpAddress_Called bool
IpAddress_Mac string
IpAddress_Return string
IpAddress_Err error
GetHostName_Called bool
GetHostName_Ip string
GetHostName_Return string
GetHostName_Err error
GetVirtualMachineGeneration_Called bool
GetVirtualMachineGeneration_VmName string
GetVirtualMachineGeneration_Return uint
GetVirtualMachineGeneration_Err error
GetHostAdapterIpAddressForSwitch_Called bool
GetHostAdapterIpAddressForSwitch_SwitchName string
GetHostAdapterIpAddressForSwitch_Return string
GetHostAdapterIpAddressForSwitch_Err error
TypeScanCodes_Called bool
TypeScanCodes_VmName string
TypeScanCodes_ScanCodes string
TypeScanCodes_Err error
GetVirtualMachineNetworkAdapterAddress_Called bool
GetVirtualMachineNetworkAdapterAddress_VmName string
GetVirtualMachineNetworkAdapterAddress_Return string
GetVirtualMachineNetworkAdapterAddress_Err error
ReplaceVirtualMachineNetworkAdapter_Called bool
ReplaceVirtualMachineNetworkAdapter_VmName string
ReplaceVirtualMachineNetworkAdapter_Replace bool
ReplaceVirtualMachineNetworkAdapter_Err error
SetNetworkAdapterVlanId_Called bool
SetNetworkAdapterVlanId_SwitchName string
SetNetworkAdapterVlanId_VlanId string
SetNetworkAdapterVlanId_Err error
SetVmNetworkAdapterMacAddress_Called bool
SetVmNetworkAdapterMacAddress_VmName string
SetVmNetworkAdapterMacAddress_Mac string
SetVmNetworkAdapterMacAddress_Err error
SetVirtualMachineVlanId_Called bool
SetVirtualMachineVlanId_VmName string
SetVirtualMachineVlanId_VlanId string
SetVirtualMachineVlanId_Err error
UntagVirtualMachineNetworkAdapterVlan_Called bool
UntagVirtualMachineNetworkAdapterVlan_VmName string
UntagVirtualMachineNetworkAdapterVlan_SwitchName string
UntagVirtualMachineNetworkAdapterVlan_Err error
CreateExternalVirtualSwitch_Called bool
CreateExternalVirtualSwitch_VmName string
CreateExternalVirtualSwitch_SwitchName string
CreateExternalVirtualSwitch_Err error
GetVirtualMachineSwitchName_Called bool
GetVirtualMachineSwitchName_VmName string
GetVirtualMachineSwitchName_Return string
GetVirtualMachineSwitchName_Err error
ConnectVirtualMachineNetworkAdapterToSwitch_Called bool
ConnectVirtualMachineNetworkAdapterToSwitch_VmName string
ConnectVirtualMachineNetworkAdapterToSwitch_SwitchName string
ConnectVirtualMachineNetworkAdapterToSwitch_Err error
DeleteVirtualSwitch_Called bool
DeleteVirtualSwitch_SwitchName string
DeleteVirtualSwitch_Err error
CheckVMName_Called bool
CheckVMName_Err error
CreateVirtualSwitch_Called bool
CreateVirtualSwitch_SwitchName string
CreateVirtualSwitch_SwitchType string
CreateVirtualSwitch_Return bool
CreateVirtualSwitch_Err error
AddVirtualMachineHardDrive_Called bool
AddVirtualMachineHardDrive_VmName string
AddVirtualMachineHardDrive_VhdFile string
AddVirtualMachineHardDrive_VhdName string
AddVirtualMachineHardDrive_VhdSizeBytes int64
AddVirtualMachineHardDrive_VhdBlockSize int64
AddVirtualMachineHardDrive_ControllerType string
AddVirtualMachineHardDrive_Err error
CreateVirtualMachine_Called bool
CreateVirtualMachine_VmName string
CreateVirtualMachine_Path string
CreateVirtualMachine_HarddrivePath string
CreateVirtualMachine_Ram int64
CreateVirtualMachine_DiskSize int64
CreateVirtualMachine_DiskBlockSize int64
CreateVirtualMachine_SwitchName string
CreateVirtualMachine_Generation uint
CreateVirtualMachine_DifferentialDisk bool
CreateVirtualMachine_FixedVHD bool
CreateVirtualMachine_Version string
CreateVirtualMachine_Err error
CloneVirtualMachine_Called bool
CloneVirtualMachine_CloneFromVmcxPath string
CloneVirtualMachine_CloneFromVmName string
CloneVirtualMachine_CloneFromSnapshotName string
CloneVirtualMachine_CloneAllSnapshots bool
CloneVirtualMachine_VmName string
CloneVirtualMachine_Path string
CloneVirtualMachine_HarddrivePath string
CloneVirtualMachine_Ram int64
CloneVirtualMachine_SwitchName string
CloneVirtualMachine_Copy bool
CloneVirtualMachine_Err error
DeleteVirtualMachine_Called bool
DeleteVirtualMachine_VmName string
DeleteVirtualMachine_Err error
SetVirtualMachineCpuCount_Called bool
SetVirtualMachineCpuCount_VmName string
SetVirtualMachineCpuCount_Cpu uint
SetVirtualMachineCpuCount_Err error
SetVirtualMachineMacSpoofing_Called bool
SetVirtualMachineMacSpoofing_VmName string
SetVirtualMachineMacSpoofing_Enable bool
SetVirtualMachineMacSpoofing_Err error
SetVirtualMachineDynamicMemory_Called bool
SetVirtualMachineDynamicMemory_VmName string
SetVirtualMachineDynamicMemory_Enable bool
SetVirtualMachineDynamicMemory_Err error
SetVirtualMachineSecureBoot_Called bool
SetVirtualMachineSecureBoot_VmName string
SetVirtualMachineSecureBoot_TemplateName string
SetVirtualMachineSecureBoot_Enable bool
SetVirtualMachineSecureBoot_Err error
SetVirtualMachineVirtualizationExtensions_Called bool
SetVirtualMachineVirtualizationExtensions_VmName string
SetVirtualMachineVirtualizationExtensions_Enable bool
SetVirtualMachineVirtualizationExtensions_Err error
EnableVirtualMachineIntegrationService_Called bool
EnableVirtualMachineIntegrationService_VmName string
EnableVirtualMachineIntegrationService_IntegrationServiceName string
EnableVirtualMachineIntegrationService_Err error
ExportVirtualMachine_Called bool
ExportVirtualMachine_VmName string
ExportVirtualMachine_Path string
ExportVirtualMachine_Err error
PreserveLegacyExportBehaviour_Called bool
PreserveLegacyExportBehaviour_SrcPath string
PreserveLegacyExportBehaviour_DstPath string
PreserveLegacyExportBehaviour_Err error
MoveCreatedVHDsToOutputDir_Called bool
MoveCreatedVHDsToOutputDir_SrcPath string
MoveCreatedVHDsToOutputDir_DstPath string
MoveCreatedVHDsToOutputDir_Err error
CompactDisks_Called bool
CompactDisks_Path string
CompactDisks_Result string
CompactDisks_Err error
RestartVirtualMachine_Called bool
RestartVirtualMachine_VmName string
RestartVirtualMachine_Err error
CreateDvdDrive_Called bool
CreateDvdDrive_VmName string
CreateDvdDrive_IsoPath string
CreateDvdDrive_Generation uint
CreateDvdDrive_ControllerNumber uint
CreateDvdDrive_ControllerLocation uint
CreateDvdDrive_Err error
MountDvdDrive_Called bool
MountDvdDrive_VmName string
MountDvdDrive_Path string
MountDvdDrive_ControllerNumber uint
MountDvdDrive_ControllerLocation uint
MountDvdDrive_Err error
SetBootDvdDrive_Called bool
SetBootDvdDrive_VmName string
SetBootDvdDrive_ControllerNumber uint
SetBootDvdDrive_ControllerLocation uint
SetBootDvdDrive_Generation uint
SetBootDvdDrive_Err error
SetFirstBootDevice_Called bool
SetFirstBootDevice_VmName string
SetFirstBootDevice_ControllerType string
SetFirstBootDevice_ControllerNumber uint
SetFirstBootDevice_ControllerLocation uint
SetFirstBootDevice_Generation uint
SetFirstBootDevice_Err error
SetBootOrder_Called bool
SetBootOrder_VmName string
SetBootOrder_BootOrder []string
SetBootOrder_Err error
UnmountDvdDrive_Called bool
UnmountDvdDrive_VmName string
UnmountDvdDrive_ControllerNumber uint
UnmountDvdDrive_ControllerLocation uint
UnmountDvdDrive_Err error
DeleteDvdDrive_Called bool
DeleteDvdDrive_VmName string
DeleteDvdDrive_ControllerNumber uint
DeleteDvdDrive_ControllerLocation uint
DeleteDvdDrive_Err error
MountFloppyDrive_Called bool
MountFloppyDrive_VmName string
MountFloppyDrive_Path string
MountFloppyDrive_Err error
UnmountFloppyDrive_Called bool
UnmountFloppyDrive_VmName string
UnmountFloppyDrive_Err error
Connect_Called bool
Connect_VmName string
Connect_Cancel context.CancelFunc
Connect_Err error
Disconnect_Called bool
Disconnect_Cancel context.CancelFunc
}
func (d *DriverMock) IsRunning(vmName string) (bool, error) {
d.IsRunning_Called = true
d.IsRunning_VmName = vmName
return d.IsRunning_Return, d.IsRunning_Err
}
func (d *DriverMock) IsOff(vmName string) (bool, error) {
d.IsOff_Called = true
d.IsOff_VmName = vmName
return d.IsOff_Return, d.IsOff_Err
}
func (d *DriverMock) Uptime(vmName string) (uint64, error) {
d.Uptime_Called = true
d.Uptime_VmName = vmName
return d.Uptime_Return, d.Uptime_Err
}
func (d *DriverMock) Start(vmName string) error {
d.Start_Called = true
d.Start_VmName = vmName
return d.Start_Err
}
func (d *DriverMock) Stop(vmName string) error {
d.Stop_Called = true
d.Stop_VmName = vmName
return d.Stop_Err
}
func (d *DriverMock) Verify() error {
d.Verify_Called = true
return d.Verify_Err
}
func (d *DriverMock) Mac(vmName string) (string, error) {
d.Mac_Called = true
d.Mac_VmName = vmName
return d.Mac_Return, d.Mac_Err
}
func (d *DriverMock) IpAddress(mac string) (string, error) {
d.IpAddress_Called = true
d.IpAddress_Mac = mac
return d.IpAddress_Return, d.IpAddress_Err
}
func (d *DriverMock) GetHostName(ip string) (string, error) {
d.GetHostName_Called = true
d.GetHostName_Ip = ip
return d.GetHostName_Return, d.GetHostName_Err
}
func (d *DriverMock) GetVirtualMachineGeneration(vmName string) (uint, error) {
d.GetVirtualMachineGeneration_Called = true
d.GetVirtualMachineGeneration_VmName = vmName
return d.GetVirtualMachineGeneration_Return, d.GetVirtualMachineGeneration_Err
}
func (d *DriverMock) GetHostAdapterIpAddressForSwitch(switchName string) (string, error) {
d.GetHostAdapterIpAddressForSwitch_Called = true
d.GetHostAdapterIpAddressForSwitch_SwitchName = switchName
return d.GetHostAdapterIpAddressForSwitch_Return, d.GetHostAdapterIpAddressForSwitch_Err
}
func (d *DriverMock) TypeScanCodes(vmName string, scanCodes string) error {
d.TypeScanCodes_Called = true
d.TypeScanCodes_VmName = vmName
d.TypeScanCodes_ScanCodes = scanCodes
return d.TypeScanCodes_Err
}
func (d *DriverMock) GetVirtualMachineNetworkAdapterAddress(vmName string) (string, error) {
d.GetVirtualMachineNetworkAdapterAddress_Called = true
d.GetVirtualMachineNetworkAdapterAddress_VmName = vmName
return d.GetVirtualMachineNetworkAdapterAddress_Return, d.GetVirtualMachineNetworkAdapterAddress_Err
}
func (d *DriverMock) ReplaceVirtualMachineNetworkAdapter(vmName string, replace bool) error {
d.ReplaceVirtualMachineNetworkAdapter_Called = true
d.ReplaceVirtualMachineNetworkAdapter_VmName = vmName
d.ReplaceVirtualMachineNetworkAdapter_Replace = replace
return d.ReplaceVirtualMachineNetworkAdapter_Err
}
func (d *DriverMock) SetNetworkAdapterVlanId(switchName string, vlanId string) error {
d.SetNetworkAdapterVlanId_Called = true
d.SetNetworkAdapterVlanId_SwitchName = switchName
d.SetNetworkAdapterVlanId_VlanId = vlanId
return d.SetNetworkAdapterVlanId_Err
}
func (d *DriverMock) SetVmNetworkAdapterMacAddress(vmName string, mac string) error {
d.SetVmNetworkAdapterMacAddress_Called = true
d.SetVmNetworkAdapterMacAddress_VmName = vmName
d.SetVmNetworkAdapterMacAddress_Mac = mac
return d.SetVmNetworkAdapterMacAddress_Err
}
func (d *DriverMock) SetVirtualMachineVlanId(vmName string, vlanId string) error {
d.SetVirtualMachineVlanId_Called = true
d.SetVirtualMachineVlanId_VmName = vmName
d.SetVirtualMachineVlanId_VlanId = vlanId
return d.SetVirtualMachineVlanId_Err
}
func (d *DriverMock) UntagVirtualMachineNetworkAdapterVlan(vmName string, switchName string) error {
d.UntagVirtualMachineNetworkAdapterVlan_Called = true
d.UntagVirtualMachineNetworkAdapterVlan_VmName = vmName
d.UntagVirtualMachineNetworkAdapterVlan_SwitchName = switchName
return d.UntagVirtualMachineNetworkAdapterVlan_Err
}
func (d *DriverMock) CreateExternalVirtualSwitch(vmName string, switchName string) error {
d.CreateExternalVirtualSwitch_Called = true
d.CreateExternalVirtualSwitch_VmName = vmName
d.CreateExternalVirtualSwitch_SwitchName = switchName
return d.CreateExternalVirtualSwitch_Err
}
func (d *DriverMock) GetVirtualMachineSwitchName(vmName string) (string, error) {
d.GetVirtualMachineSwitchName_Called = true
d.GetVirtualMachineSwitchName_VmName = vmName
return d.GetVirtualMachineSwitchName_Return, d.GetVirtualMachineSwitchName_Err
}
func (d *DriverMock) ConnectVirtualMachineNetworkAdapterToSwitch(vmName string, switchName string) error {
d.ConnectVirtualMachineNetworkAdapterToSwitch_Called = true
d.ConnectVirtualMachineNetworkAdapterToSwitch_VmName = vmName
d.ConnectVirtualMachineNetworkAdapterToSwitch_SwitchName = switchName
return d.ConnectVirtualMachineNetworkAdapterToSwitch_Err
}
func (d *DriverMock) DeleteVirtualSwitch(switchName string) error {
d.DeleteVirtualSwitch_Called = true
d.DeleteVirtualSwitch_SwitchName = switchName
return d.DeleteVirtualSwitch_Err
}
func (d *DriverMock) CreateVirtualSwitch(switchName string, switchType string) (bool, error) {
d.CreateVirtualSwitch_Called = true
d.CreateVirtualSwitch_SwitchName = switchName
d.CreateVirtualSwitch_SwitchType = switchType
return d.CreateVirtualSwitch_Return, d.CreateVirtualSwitch_Err
}
func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string,
vhdSizeBytes int64, vhdDiskBlockSize int64, controllerType string) error {
d.AddVirtualMachineHardDrive_Called = true
d.AddVirtualMachineHardDrive_VmName = vmName
d.AddVirtualMachineHardDrive_VhdFile = vhdFile
d.AddVirtualMachineHardDrive_VhdName = vhdName
d.AddVirtualMachineHardDrive_VhdSizeBytes = vhdSizeBytes
d.AddVirtualMachineHardDrive_VhdSizeBytes = vhdDiskBlockSize
d.AddVirtualMachineHardDrive_ControllerType = controllerType
return d.AddVirtualMachineHardDrive_Err
}
func (d *DriverMock) CheckVMName(vmName string) error {
d.CheckVMName_Called = true
return d.CheckVMName_Err
}
func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string,
ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint,
diffDisks bool, fixedVHD bool, version string) error {
d.CreateVirtualMachine_Called = true
d.CreateVirtualMachine_VmName = vmName
d.CreateVirtualMachine_Path = path
d.CreateVirtualMachine_HarddrivePath = harddrivePath
d.CreateVirtualMachine_Ram = ram
d.CreateVirtualMachine_DiskSize = diskSize
d.CreateVirtualMachine_DiskBlockSize = diskBlockSize
d.CreateVirtualMachine_SwitchName = switchName
d.CreateVirtualMachine_Generation = generation
d.CreateVirtualMachine_DifferentialDisk = diffDisks
d.CreateVirtualMachine_Version = version
return d.CreateVirtualMachine_Err
}
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 {
d.CloneVirtualMachine_Called = true
d.CloneVirtualMachine_CloneFromVmcxPath = cloneFromVmcxPath
d.CloneVirtualMachine_CloneFromVmName = cloneFromVmName
d.CloneVirtualMachine_CloneFromSnapshotName = cloneFromSnapshotName
d.CloneVirtualMachine_CloneAllSnapshots = cloneAllSnapshots
d.CloneVirtualMachine_VmName = vmName
d.CloneVirtualMachine_Path = path
d.CloneVirtualMachine_HarddrivePath = harddrivePath
d.CloneVirtualMachine_Ram = ram
d.CloneVirtualMachine_SwitchName = switchName
d.CloneVirtualMachine_Copy = copyTF
return d.CloneVirtualMachine_Err
}
func (d *DriverMock) DeleteVirtualMachine(vmName string) error {
d.DeleteVirtualMachine_Called = true
d.DeleteVirtualMachine_VmName = vmName
return d.DeleteVirtualMachine_Err
}
func (d *DriverMock) SetVirtualMachineCpuCount(vmName string, cpu uint) error {
d.SetVirtualMachineCpuCount_Called = true
d.SetVirtualMachineCpuCount_VmName = vmName
d.SetVirtualMachineCpuCount_Cpu = cpu
return d.SetVirtualMachineCpuCount_Err
}
func (d *DriverMock) SetVirtualMachineMacSpoofing(vmName string, enable bool) error {
d.SetVirtualMachineMacSpoofing_Called = true
d.SetVirtualMachineMacSpoofing_VmName = vmName
d.SetVirtualMachineMacSpoofing_Enable = enable
return d.SetVirtualMachineMacSpoofing_Err
}
func (d *DriverMock) SetVirtualMachineDynamicMemory(vmName string, enable bool) error {
d.SetVirtualMachineDynamicMemory_Called = true
d.SetVirtualMachineDynamicMemory_VmName = vmName
d.SetVirtualMachineDynamicMemory_Enable = enable
return d.SetVirtualMachineDynamicMemory_Err
}
func (d *DriverMock) SetVirtualMachineSecureBoot(vmName string, enable bool, templateName string) error {
d.SetVirtualMachineSecureBoot_Called = true
d.SetVirtualMachineSecureBoot_VmName = vmName
d.SetVirtualMachineSecureBoot_Enable = enable
d.SetVirtualMachineSecureBoot_TemplateName = templateName
return d.SetVirtualMachineSecureBoot_Err
}
func (d *DriverMock) SetVirtualMachineVirtualizationExtensions(vmName string, enable bool) error {
d.SetVirtualMachineVirtualizationExtensions_Called = true
d.SetVirtualMachineVirtualizationExtensions_VmName = vmName
d.SetVirtualMachineVirtualizationExtensions_Enable = enable
return d.SetVirtualMachineVirtualizationExtensions_Err
}
func (d *DriverMock) EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error {
d.EnableVirtualMachineIntegrationService_Called = true
d.EnableVirtualMachineIntegrationService_VmName = vmName
d.EnableVirtualMachineIntegrationService_IntegrationServiceName = integrationServiceName
return d.EnableVirtualMachineIntegrationService_Err
}
func (d *DriverMock) ExportVirtualMachine(vmName string, path string) error {
d.ExportVirtualMachine_Called = true
d.ExportVirtualMachine_VmName = vmName
d.ExportVirtualMachine_Path = path
return d.ExportVirtualMachine_Err
}
func (d *DriverMock) PreserveLegacyExportBehaviour(srcPath string, dstPath string) error {
d.PreserveLegacyExportBehaviour_Called = true
d.PreserveLegacyExportBehaviour_SrcPath = srcPath
d.PreserveLegacyExportBehaviour_DstPath = dstPath
return d.PreserveLegacyExportBehaviour_Err
}
func (d *DriverMock) MoveCreatedVHDsToOutputDir(srcPath string, dstPath string) error {
d.MoveCreatedVHDsToOutputDir_Called = true
d.MoveCreatedVHDsToOutputDir_SrcPath = srcPath
d.MoveCreatedVHDsToOutputDir_DstPath = dstPath
return d.MoveCreatedVHDsToOutputDir_Err
}
func (d *DriverMock) CompactDisks(path string) (result string, err error) {
d.CompactDisks_Called = true
d.CompactDisks_Path = path
d.CompactDisks_Result = "Mock compact result msg: mockdisk.vhdx. Disk size reduced by 20%"
return d.CompactDisks_Result, d.CompactDisks_Err
}
func (d *DriverMock) RestartVirtualMachine(vmName string) error {
d.RestartVirtualMachine_Called = true
d.RestartVirtualMachine_VmName = vmName
return d.RestartVirtualMachine_Err
}
func (d *DriverMock) CreateDvdDrive(vmName string, isoPath string, generation uint) (uint, uint, error) {
d.CreateDvdDrive_Called = true
d.CreateDvdDrive_VmName = vmName
d.CreateDvdDrive_IsoPath = isoPath
d.CreateDvdDrive_Generation = generation
return d.CreateDvdDrive_ControllerNumber, d.CreateDvdDrive_ControllerLocation, d.CreateDvdDrive_Err
}
func (d *DriverMock) MountDvdDrive(vmName string, path string, controllerNumber uint,
controllerLocation uint) error {
d.MountDvdDrive_Called = true
d.MountDvdDrive_VmName = vmName
d.MountDvdDrive_Path = path
d.MountDvdDrive_ControllerNumber = controllerNumber
d.MountDvdDrive_ControllerLocation = controllerLocation
return d.MountDvdDrive_Err
}
func (d *DriverMock) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint,
generation uint) error {
d.SetBootDvdDrive_Called = true
d.SetBootDvdDrive_VmName = vmName
d.SetBootDvdDrive_ControllerNumber = controllerNumber
d.SetBootDvdDrive_ControllerLocation = controllerLocation
d.SetBootDvdDrive_Generation = generation
return d.SetBootDvdDrive_Err
}
func (d *DriverMock) SetFirstBootDevice(vmName string, controllerType string, controllerNumber uint,
controllerLocation uint, generation uint) error {
d.SetFirstBootDevice_Called = true
d.SetFirstBootDevice_VmName = vmName
d.SetFirstBootDevice_ControllerType = controllerType
d.SetFirstBootDevice_ControllerNumber = controllerNumber
d.SetFirstBootDevice_ControllerLocation = controllerLocation
d.SetFirstBootDevice_Generation = generation
return d.SetFirstBootDevice_Err
}
func (d *DriverMock) SetBootOrder(vmName string, bootOrder []string) error {
d.SetBootOrder_Called = true
d.SetBootOrder_VmName = vmName
d.SetBootOrder_BootOrder = bootOrder
return d.SetBootOrder_Err
}
func (d *DriverMock) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
d.UnmountDvdDrive_Called = true
d.UnmountDvdDrive_VmName = vmName
d.UnmountDvdDrive_ControllerNumber = controllerNumber
d.UnmountDvdDrive_ControllerLocation = controllerLocation
return d.UnmountDvdDrive_Err
}
func (d *DriverMock) DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
d.DeleteDvdDrive_Called = true
d.DeleteDvdDrive_VmName = vmName
d.DeleteDvdDrive_ControllerNumber = controllerNumber
d.DeleteDvdDrive_ControllerLocation = controllerLocation
return d.DeleteDvdDrive_Err
}
func (d *DriverMock) MountFloppyDrive(vmName string, path string) error {
d.MountFloppyDrive_Called = true
d.MountFloppyDrive_VmName = vmName
d.MountFloppyDrive_Path = path
return d.MountFloppyDrive_Err
}
func (d *DriverMock) UnmountFloppyDrive(vmName string) error {
d.UnmountFloppyDrive_Called = true
d.UnmountFloppyDrive_VmName = vmName
return d.UnmountFloppyDrive_Err
}
func (d *DriverMock) Connect(vmName string) (context.CancelFunc, error) {
d.Connect_Called = true
d.Connect_VmName = vmName
return d.Connect_Cancel, d.Connect_Err
}
func (d *DriverMock) Disconnect(cancel context.CancelFunc) {
d.Disconnect_Called = true
d.Disconnect_Cancel = cancel
}

View File

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

View File

@ -1,30 +0,0 @@
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type OutputConfig
package common
import (
"fmt"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type OutputConfig struct {
// This setting specifies the directory that
// artifacts from the build, such as the virtual machine files and disks,
// will be output to. The path to the directory may be relative or
// absolute. If relative, the path is relative to the working directory
// packer is executed from. This directory must not exist or, if
// created, must be empty prior to running the builder. By default this is
// "output-BUILDNAME" where "BUILDNAME" is the name of the build.
OutputDir string `mapstructure:"output_directory" required:"false"`
}
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
if c.OutputDir == "" {
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
}
return nil
}

View File

@ -1,31 +0,0 @@
// 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"
)
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatOutputConfig struct {
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
}
// FlatMapstructure returns a new FlatOutputConfig.
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*OutputConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatOutputConfig)
}
// HCL2Spec returns the hcl spec of a OutputConfig.
// This spec is used by HCL to read the fields of OutputConfig.
// The decoded values from this spec will then be applied to a FlatOutputConfig.
func (*FlatOutputConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,47 +0,0 @@
package common
import (
"io/ioutil"
"os"
"testing"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
func TestOutputConfigPrepare(t *testing.T) {
c := new(OutputConfig)
if c.OutputDir != "" {
t.Fatalf("what: %s", c.OutputDir)
}
pc := &common.PackerConfig{PackerBuildName: "foo"}
errs := c.Prepare(interpolate.NewContext(), pc)
if len(errs) > 0 {
t.Fatalf("err: %#v", errs)
}
if c.OutputDir == "" {
t.Fatal("should have output dir")
}
}
func TestOutputConfigPrepare_exists(t *testing.T) {
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
c := new(OutputConfig)
c.OutputDir = td
pc := &common.PackerConfig{
PackerBuildName: "foo",
PackerForce: false,
}
errs := c.Prepare(interpolate.NewContext(), pc)
if len(errs) != 0 {
t.Fatal("should not have errors")
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,131 +0,0 @@
package hyperv
import (
"strings"
"testing"
)
func Test_getCreateVMScript(t *testing.T) {
opts := scriptOptions{
Version: "5.0",
VMName: "myvm",
Path: "C://mypath",
HardDrivePath: "C://harddrivepath",
MemoryStartupBytes: int64(1024),
NewVHDSizeBytes: int64(8192),
VHDBlockSizeBytes: int64(10),
SwitchName: "hyperv-vmx-switch",
Generation: uint(1),
DiffDisks: true,
FixedVHD: true,
}
// Check Fixed VHD conditional set
scriptString, err := getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected := `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhd"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch" -Version 5.0`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
// We should never get here thanks to good template validation, but it's
// good to fail rather than trying to run the ps script and erroring.
opts.Generation = uint(2)
scriptString, err = getCreateVMScript(&opts)
if err == nil {
t.Fatalf("Should have Error: %s", err.Error())
}
// Check VHDX conditional set
opts.FixedVHD = false
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch" -Generation 2 -Version 5.0`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
// Check generation 1 no fixed VHD
opts.FixedVHD = false
opts.Generation = uint(1)
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch" -Version 5.0`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
// Check that we use generation one template even if generation is unset
opts.Generation = uint(0)
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
// same "expected" as above
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.Version = ""
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.DiffDisks = false
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Copy-Item -Path "C://harddrivepath" -Destination $vhdPath
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.HardDrivePath = ""
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -SizeBytes 8192 -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.FixedVHD = true
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhd"
Hyper-V\New-VHD -Path $vhdPath -Fixed -SizeBytes 8192
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
}

View File

@ -1,371 +0,0 @@
package powershell
import (
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"strconv"
"strings"
"github.com/hashicorp/packer-plugin-sdk/tmp"
)
const (
powerShellFalse = "False"
powerShellTrue = "True"
)
func IsTrue(s string) bool {
return strings.TrimSpace(s) == powerShellTrue
}
func IsFalse(s string) bool {
return strings.TrimSpace(s) == powerShellFalse
}
type PowerShellCmd struct {
Stdout io.Writer
Stderr io.Writer
}
func (ps *PowerShellCmd) Run(fileContents string, params ...string) error {
_, err := ps.Output(fileContents, params...)
return err
}
// Output runs the PowerShell command and returns its standard output.
func (ps *PowerShellCmd) Output(fileContents string, params ...string) (string, error) {
path, err := ps.getPowerShellPath()
if err != nil {
return "", fmt.Errorf("Cannot find PowerShell in the path")
}
filename, err := saveScript(fileContents)
if err != nil {
return "", err
}
debug := os.Getenv("PACKER_POWERSHELL_DEBUG") != ""
verbose := debug || os.Getenv("PACKER_POWERSHELL_VERBOSE") != ""
if !debug {
defer os.Remove(filename)
}
args := createArgs(filename, params...)
if verbose {
log.Printf("Run: %s %s", path, args)
}
var stdout, stderr bytes.Buffer
command := exec.Command(path, args...)
command.Stdout = &stdout
command.Stderr = &stderr
err = command.Run()
if ps.Stdout != nil {
stdout.WriteTo(ps.Stdout)
}
if ps.Stderr != nil {
stderr.WriteTo(ps.Stderr)
}
stderrString := strings.TrimSpace(stderr.String())
if _, ok := err.(*exec.ExitError); ok {
err = fmt.Errorf("PowerShell error: %s", stderrString)
}
if len(stderrString) > 0 {
err = fmt.Errorf("PowerShell error: %s", stderrString)
}
stdoutString := strings.TrimSpace(stdout.String())
if verbose && stdoutString != "" {
log.Printf("stdout: %s", stdoutString)
}
// only write the stderr string if verbose because
// the error string will already be in the err return value.
if verbose && stderrString != "" {
log.Printf("stderr: %s", stderrString)
}
return stdoutString, err
}
func IsPowershellAvailable() (bool, string, error) {
path, err := exec.LookPath("powershell")
if err != nil {
return false, "", err
} else {
return true, path, err
}
}
func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
powershellAvailable, path, err := IsPowershellAvailable()
if err != nil {
log.Fatalf("IsPowershellAvailable: %v", err)
}
if !powershellAvailable {
log.Fatal("Cannot find PowerShell in the path")
return "", err
}
return path, nil
}
func saveScript(fileContents string) (string, error) {
file, err := tmp.File("powershell")
if err != nil {
return "", err
}
_, err = file.Write([]byte(fileContents))
if err != nil {
return "", err
}
err = file.Close()
if err != nil {
return "", err
}
newFilename := file.Name() + ".ps1"
err = os.Rename(file.Name(), newFilename)
if err != nil {
return "", err
}
return newFilename, nil
}
func createArgs(filename string, params ...string) []string {
args := make([]string, len(params)+5)
args[0] = "-ExecutionPolicy"
args[1] = "Bypass"
args[2] = "-NoProfile"
args[3] = "-File"
args[4] = filename
for key, value := range params {
args[key+5] = value
}
return args
}
func GetHostAvailableMemory() float64 {
var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024"
var ps PowerShellCmd
output, _ := ps.Output(script)
freeMB, _ := strconv.ParseFloat(output, 64)
return freeMB
}
func GetHostName(ip string) (string, error) {
var script = `
param([string]$ip)
try {
$HostName = [System.Net.Dns]::GetHostEntry($ip).HostName
if ($HostName -ne $null) {
$HostName = $HostName.Split('.')[0]
}
$HostName
} catch { }
`
//
var ps PowerShellCmd
cmdOut, err := ps.Output(script, ip)
if err != nil {
return "", err
}
return cmdOut, nil
}
func IsCurrentUserAnAdministrator() (bool, error) {
var script = `
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = new-object System.Security.Principal.WindowsPrincipal($identity)
$administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
return $principal.IsInRole($administratorRole)
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script)
if err != nil {
return false, err
}
res := strings.TrimSpace(cmdOut)
return res == powerShellTrue, nil
}
func ModuleExists(moduleName string) (bool, error) {
var script = `
param([string]$moduleName)
(Get-Module -Name $moduleName) -ne $null
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script)
if err != nil {
return false, err
}
res := strings.TrimSpace(cmdOut)
if res == powerShellFalse {
err := fmt.Errorf("PowerShell %s module is not loaded. Make sure %s feature is on.", moduleName, moduleName)
return false, err
}
return true, nil
}
func HasVirtualMachineVirtualizationExtensions() (bool, error) {
var script = `
(GET-Command Hyper-V\Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions"
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script)
if err != nil {
return false, err
}
var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True"
return hasVirtualMachineVirtualizationExtensions, err
}
func DoesVirtualMachineExist(vmName string) (bool, error) {
var script = `
param([string]$vmName)
return (Hyper-V\Get-VM -Name $vmName | ?{$_.Name -eq $vmName}) -ne $null
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return false, err
}
var exists = strings.TrimSpace(cmdOut) == "True"
return exists, err
}
func DoesVirtualMachineSnapshotExist(vmName string, snapshotName string) (bool, error) {
var script = `
param([string]$vmName, [string]$snapshotName)
return (Hyper-V\Get-VMSnapshot -VMName $vmName | ?{$_.Name -eq $snapshotName}) -ne $null
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script, vmName, snapshotName)
if err != nil {
return false, err
}
var exists = strings.TrimSpace(cmdOut) == "True"
return exists, err
}
func IsVirtualMachineOn(vmName string) (bool, error) {
var script = `
param([string]$vmName)
$vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue
$vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return false, err
}
var isRunning = strings.TrimSpace(cmdOut) == "True"
return isRunning, err
}
func GetVirtualMachineGeneration(vmName string) (uint, error) {
var script = `
param([string]$vmName)
$generation = Hyper-V\Get-Vm -Name $vmName | %{$_.Generation}
if (!$generation){
$generation = 1
}
return $generation
`
var ps PowerShellCmd
cmdOut, err := ps.Output(script, vmName)
if err != nil {
return 0, err
}
generationUint32, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 32)
if err != nil {
return 0, err
}
generation := uint(generationUint32)
return generation, err
}
func SetUnattendedProductKey(path string, productKey string) error {
var script = `
param([string]$path,[string]$productKey)
$unattend = [xml](Get-Content -Path $path)
$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
$setupNode = $unattend |
Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
Select-Object -ExpandProperty Node
$productKeyNode = $setupNode |
Select-Xml -XPath '//un:ProductKey' -Namespace $ns |
Select-Object -ExpandProperty Node
if ($productKeyNode -eq $null) {
$productKeyNode = $unattend.CreateElement('ProductKey', $ns.un)
[Void]$setupNode.AppendChild($productKeyNode)
}
$productKeyNode.InnerText = $productKey
$unattend.Save($path)
`
var ps PowerShellCmd
err := ps.Run(script, path, productKey)
return err
}

View File

@ -1,69 +0,0 @@
package powershell
import (
"bytes"
"testing"
)
func TestOutput(t *testing.T) {
var ps PowerShellCmd
powershellAvailable, _, _ := IsPowershellAvailable()
if !powershellAvailable {
t.Skipf("powershell not installed")
return
}
cmdOut, err := ps.Output("")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if cmdOut != "" {
t.Fatalf("output '%v' is not ''", cmdOut)
}
trueOutput, err := ps.Output("$True")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if trueOutput != "True" {
t.Fatalf("output '%v' is not 'True'", trueOutput)
}
falseOutput, err := ps.Output("$False")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if falseOutput != "False" {
t.Fatalf("output '%v' is not 'False'", falseOutput)
}
}
func TestRunFile(t *testing.T) {
var ps PowerShellCmd
powershellAvailable, _, _ := IsPowershellAvailable()
if !powershellAvailable {
t.Skipf("powershell not installed")
return
}
var blockBuffer bytes.Buffer
blockBuffer.WriteString(`param([string]$a, [string]$b, [int]$x, [int]$y) if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $n = $x + $y; Write-Output "$a $b $n";`)
cmdOut, err := ps.Output(blockBuffer.String(), "a", "b", "5", "10")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if cmdOut != "a b 15" {
t.Fatalf("output '%v' is not 'a b 15'", cmdOut)
}
}

View File

@ -1,29 +0,0 @@
package powershell
import (
"bytes"
)
type ScriptBuilder struct {
buffer bytes.Buffer
}
func (b *ScriptBuilder) WriteLine(s string) (n int, err error) {
n, err = b.buffer.WriteString(s)
b.buffer.WriteString("\n")
return n + 1, err
}
func (b *ScriptBuilder) WriteString(s string) (n int, err error) {
n, err = b.buffer.WriteString(s)
return n, err
}
func (b *ScriptBuilder) String() string {
return b.buffer.String()
}
func (b *ScriptBuilder) Reset() {
b.buffer.Reset()
}

View File

@ -1,33 +0,0 @@
package common
import (
"log"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func CommHost(host string) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
// Skip IP auto detection if the configuration has an ssh host configured.
if host != "" {
log.Printf("Using host value: %s", host)
return host, nil
}
vmName := state.Get("vmName").(string)
driver := state.Get("driver").(Driver)
mac, err := driver.Mac(vmName)
if err != nil {
return "", err
}
ip, err := driver.IpAddress(mac)
if err != nil {
return "", err
}
return ip, nil
}
}

View File

@ -1,14 +0,0 @@
package common
import (
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type SSHConfig struct {
Comm communicator.Config `mapstructure:",squash"`
}
func (c *SSHConfig) Prepare(ctx *interpolate.Context) []error {
return c.Comm.Prepare(ctx)
}

View File

@ -1,183 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"path/filepath"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
// This step clones an existing virtual machine.
//
// Produces:
// VMName string - The name of the VM
type StepCloneVM struct {
CloneFromVMCXPath string
CloneFromVMName string
CloneFromSnapshotName string
CloneAllSnapshots bool
VMName string
SwitchName string
CompareCopy bool
RamSize uint
Cpu uint
EnableMacSpoofing bool
EnableDynamicMemory bool
EnableSecureBoot bool
SecureBootTemplate string
EnableVirtualizationExtensions bool
MacAddress string
KeepRegistered bool
AdditionalDiskSize []uint
DiskBlockSize uint
}
func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Cloning virtual machine...")
path := state.Get("build_dir").(string)
// Determine if we even have an existing virtual harddrive to attach
harddrivePath := ""
if harddrivePathRaw, ok := state.GetOk("iso_path"); ok {
extension := strings.ToLower(filepath.Ext(harddrivePathRaw.(string)))
if extension == ".vhd" || extension == ".vhdx" {
harddrivePath = harddrivePathRaw.(string)
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
// convert the MB to bytes
ramSize := int64(s.RamSize * 1024 * 1024)
err := driver.CloneVirtualMachine(s.CloneFromVMCXPath, s.CloneFromVMName,
s.CloneFromSnapshotName, s.CloneAllSnapshots, s.VMName, path,
harddrivePath, ramSize, s.SwitchName, s.CompareCopy)
if err != nil {
err := fmt.Errorf("Error cloning virtual machine: %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 creating setting virtual machine cpu: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if s.EnableDynamicMemory {
err = driver.SetVirtualMachineDynamicMemory(s.VMName, s.EnableDynamicMemory)
if err != nil {
err := fmt.Errorf("Error creating setting virtual machine dynamic memory: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if s.EnableMacSpoofing {
err = driver.SetVirtualMachineMacSpoofing(s.VMName, s.EnableMacSpoofing)
if err != nil {
err := fmt.Errorf("Error creating setting virtual machine mac spoofing: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
generation, err := driver.GetVirtualMachineGeneration(s.VMName)
if err != nil {
err := fmt.Errorf("Error detecting vm generation: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if generation == 2 {
err = driver.SetVirtualMachineSecureBoot(s.VMName, s.EnableSecureBoot, s.SecureBootTemplate)
if err != nil {
err := fmt.Errorf("Error setting secure boot: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if s.EnableVirtualizationExtensions {
//This is only supported on Windows 10 and Windows Server 2016 onwards
err = driver.SetVirtualMachineVirtualizationExtensions(s.VMName, s.EnableVirtualizationExtensions)
if err != nil {
err := fmt.Errorf("Error creating setting virtual machine virtualization extensions: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if len(s.AdditionalDiskSize) > 0 {
for index, size := range s.AdditionalDiskSize {
diskSize := int64(size * 1024 * 1024)
diskFile := fmt.Sprintf("%s-%d.vhdx", s.VMName, index)
diskBlockSize := int64(s.DiskBlockSize) * 1024 * 1024
err = driver.AddVirtualMachineHardDrive(s.VMName, path, diskFile, diskSize, diskBlockSize, "SCSI")
if err != nil {
err := fmt.Errorf("Error creating and attaching additional disk drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
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
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_id", s.VMName)
return multistep.ActionContinue
}
func (s *StepCloneVM) Cleanup(state multistep.StateBag) {
if s.VMName == "" {
return
}
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
if s.KeepRegistered {
ui.Say("keep_registered set. Skipping unregister/deletion of VM.")
return
}
ui.Say("Unregistering and deleting virtual machine...")
err := driver.DeleteVirtualMachine(s.VMName)
if err != nil {
ui.Error(fmt.Sprintf("Error deleting virtual machine: %s", err))
}
}

View File

@ -1,69 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepCollateArtifacts struct {
OutputDir string
SkipExport bool
}
// Runs the step required to collate all build artifacts under the
// specified output directory
func (s *StepCollateArtifacts) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Collating build artifacts...")
if s.SkipExport {
// Get the path to the main build directory from the statebag
var buildDir string
if v, ok := state.GetOk("build_dir"); ok {
buildDir = v.(string)
}
// If the user has chosen to skip a full export of the VM the only
// artifacts that they are interested in will be the VHDs. The
// called function searches for all disks under the given source
// directory and moves them to a 'Virtual Hard Disks' folder under
// the destination directory
err := driver.MoveCreatedVHDsToOutputDir(buildDir, s.OutputDir)
if err != nil {
err = fmt.Errorf("Error moving VHDs from build dir to output dir: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
// Get the full path to the export directory from the statebag
var exportPath string
if v, ok := state.GetOk("export_path"); ok {
exportPath = v.(string)
}
// The export process exports the VM into a folder named 'vm name'
// under the output directory. However, to maintain backwards
// compatibility, we now need to shuffle around the exported folders
// so the 'Snapshots', 'Virtual Hard Disks' and 'Virtual Machines'
// directories appear *directly* under <output directory>.
// The empty '<output directory>/<vm name>' directory is removed
// when complete.
// The 'Snapshots' folder will not be moved into the output
// directory if it is empty.
err := driver.PreserveLegacyExportBehaviour(exportPath, s.OutputDir)
if err != nil {
// No need to halt here; Just warn the user instead
err = fmt.Errorf("WARNING: Error restoring legacy export dir structure: %s", err)
ui.Error(err.Error())
}
}
return multistep.ActionContinue
}
// Cleanup does nothing
func (s *StepCollateArtifacts) Cleanup(state multistep.StateBag) {}

View File

@ -1,92 +0,0 @@
package common
import (
"context"
"path/filepath"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepCollateArtifacts_impl(t *testing.T) {
var _ multistep.Step = new(StepCollateArtifacts)
}
func TestStepCollateArtifacts_exportedArtifacts(t *testing.T) {
state := testState(t)
step := new(StepCollateArtifacts)
step.OutputDir = "foopath"
vmName := "foo"
// Uses export path from the state bag
exportPath := filepath.Join(step.OutputDir, vmName)
state.Put("export_path", exportPath)
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
// Test the driver
if !driver.PreserveLegacyExportBehaviour_Called {
t.Fatal("Should have called PreserveLegacyExportBehaviour")
}
if driver.PreserveLegacyExportBehaviour_SrcPath != exportPath {
t.Fatalf("Should call with correct srcPath. Got: %s Wanted: %s",
driver.PreserveLegacyExportBehaviour_SrcPath, exportPath)
}
if driver.PreserveLegacyExportBehaviour_DstPath != step.OutputDir {
t.Fatalf("Should call with correct dstPath. Got: %s Wanted: %s",
driver.PreserveLegacyExportBehaviour_DstPath, step.OutputDir)
}
// Should only be called when skip_export is true
if driver.MoveCreatedVHDsToOutputDir_Called {
t.Fatal("Should NOT have called MoveCreatedVHDsToOutputDir")
}
}
func TestStepCollateArtifacts_skipExportArtifacts(t *testing.T) {
state := testState(t)
step := new(StepCollateArtifacts)
// Needs the path to the main output directory and build directory
step.OutputDir = "foopath"
buildDir := "fooBuildPath"
state.Put("build_dir", buildDir)
// Export has been skipped
step.SkipExport = true
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
// Test the driver
if !driver.MoveCreatedVHDsToOutputDir_Called {
t.Fatal("Should have called MoveCreatedVHDsToOutputDir")
}
if driver.MoveCreatedVHDsToOutputDir_SrcPath != buildDir {
t.Fatalf("Should call with correct srcPath. Got: %s Wanted: %s",
driver.MoveCreatedVHDsToOutputDir_SrcPath, buildDir)
}
if driver.MoveCreatedVHDsToOutputDir_DstPath != step.OutputDir {
t.Fatalf("Should call with correct dstPath. Got: %s Wanted: %s",
driver.MoveCreatedVHDsToOutputDir_DstPath, step.OutputDir)
}
if driver.PreserveLegacyExportBehaviour_Called {
t.Fatal("Should NOT have called PreserveLegacyExportBehaviour")
}
}

View File

@ -1,50 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepCompactDisk struct {
SkipCompaction bool
}
// Run runs a compaction/optimisation process on attached VHD/VHDX disks
func (s *StepCompactDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
if s.SkipCompaction {
ui.Say("Skipping disk compaction...")
return multistep.ActionContinue
}
// Get the dir used to store the VMs files during the build process
var buildDir string
if v, ok := state.GetOk("build_dir"); ok {
buildDir = v.(string)
}
ui.Say("Compacting disks...")
// CompactDisks searches for all VHD/VHDX files under the supplied
// path and runs the compacting process on each of them. If no disks
// are found under the supplied path this is treated as a 'soft' error
// and a warning message is printed. All other errors halt the build.
result, err := driver.CompactDisks(buildDir)
if err != nil {
err := fmt.Errorf("Error compacting disks: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Report disk compaction results/warn if no disks were found
ui.Message(result)
return multistep.ActionContinue
}
// Cleanup does nothing
func (s *StepCompactDisk) Cleanup(state multistep.StateBag) {}

View File

@ -1,63 +0,0 @@
package common
import (
"context"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepCompactDisk_impl(t *testing.T) {
var _ multistep.Step = new(StepCompactDisk)
}
func TestStepCompactDisk(t *testing.T) {
state := testState(t)
step := new(StepCompactDisk)
// Set up the path to the build directory
buildDir := "foopath"
state.Put("build_dir", buildDir)
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
// Test the driver
if !driver.CompactDisks_Called {
t.Fatal("Should have called CompactDisks")
}
if driver.CompactDisks_Path != buildDir {
t.Fatalf("Should call with correct path. Got: %s Wanted: %s", driver.CompactDisks_Path, buildDir)
}
}
func TestStepCompactDisk_skip(t *testing.T) {
state := testState(t)
step := new(StepCompactDisk)
step.SkipCompaction = true
// Set up the path to the build directory
state.Put("build_dir", "foopath")
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatalf("Should NOT have error")
}
// Test the driver
if driver.CompactDisks_Called {
t.Fatal("Should NOT have called CompactDisks")
}
}

View File

@ -1,77 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepConfigureIp struct {
}
func (s *StepConfigureIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
errorMsg := "Error configuring ip address: %s"
vmName := state.Get("vmName").(string)
ui.Say("Configuring ip address...")
count := 60
var duration time.Duration = 1
sleepTime := time.Minute * duration
var ip string
for count != 0 {
cmdOut, err := driver.GetVirtualMachineNetworkAdapterAddress(vmName)
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ip = strings.TrimSpace(cmdOut)
if ip != "False" {
break
}
log.Println(fmt.Sprintf("Waiting for another %v minutes...", uint(duration)))
time.Sleep(sleepTime)
count--
}
if count == 0 {
err := fmt.Errorf(errorMsg, "IP address assigned to the adapter is empty")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("ip address is " + ip)
hostName, err := driver.GetHostName(ip)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("hostname is " + hostName)
state.Put("ip", ip)
state.Put("hostname", hostName)
return multistep.ActionContinue
}
func (s *StepConfigureIp) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,53 +0,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
}

View File

@ -1,68 +0,0 @@
package common
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/tmp"
)
type StepCreateBuildDir struct {
// User supplied directory under which we create the main build
// directory. The build directory is used to house the VM files and
// folders during the build. If unspecified the default temp directory
// for the OS is used
TempPath string
// The full path to the build directory. This is the concatenation of
// TempPath plus a directory uniquely named for the build
buildDir string
}
// Creates the main directory used to house the VMs files and folders
// during the build
func (s *StepCreateBuildDir) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Creating build directory...")
var err error
if s.TempPath == "" {
s.buildDir, err = tmp.Dir("hyperv")
} else {
s.buildDir, err = ioutil.TempDir(s.TempPath, "hyperv")
}
if err != nil {
err = fmt.Errorf("Error creating build directory: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Created build directory: %s", s.buildDir)
// Record the build directory location for later steps
state.Put("build_dir", s.buildDir)
return multistep.ActionContinue
}
// Cleanup removes the build directory
func (s *StepCreateBuildDir) Cleanup(state multistep.StateBag) {
if s.buildDir == "" {
return
}
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Deleting build directory...")
err := os.RemoveAll(s.buildDir)
if err != nil {
ui.Error(fmt.Sprintf("Error deleting build directory: %s", err))
}
}

View File

@ -1,113 +0,0 @@
package common
import (
"context"
"os"
"path/filepath"
"regexp"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepCreateBuildDir_imp(t *testing.T) {
var _ multistep.Step = new(StepCreateBuildDir)
}
func TestStepCreateBuildDir_Defaults(t *testing.T) {
state := testState(t)
step := new(StepCreateBuildDir)
// Default is for the user not to supply value for TempPath. When
// nothing is set the step should use the OS temp directory as the root
// for the build directory
step.TempPath = ""
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
if v, ok := state.GetOk("build_dir"); !ok {
t.Fatal("Should store path to build directory in statebag as 'build_dir'")
} else {
// On windows convert everything to forward slash separated paths
// This prevents the regexp interpreting backslashes as escape sequences
stateBuildDir := filepath.ToSlash(v.(string))
expectedBuildDirRe := regexp.MustCompile(
filepath.ToSlash(filepath.Join(os.TempDir(), "hyperv") + `[[:digit:]]{9}$`))
match := expectedBuildDirRe.MatchString(stateBuildDir)
if !match {
t.Fatalf("Got path that doesn't match expected format in 'build_dir': %s", stateBuildDir)
}
}
// Test Cleanup
step.Cleanup(state)
if _, err := os.Stat(step.buildDir); err == nil {
t.Fatalf("Build directory should NOT exist after Cleanup: %s", step.buildDir)
}
}
func TestStepCreateBuildDir_UserDefinedTempPath(t *testing.T) {
state := testState(t)
step := new(StepCreateBuildDir)
// Create a directory we'll use as the user supplied temp_path
step.TempPath = genTestDirPath("userTempDir")
err := os.Mkdir(step.TempPath, 0755) // The directory must exist
if err != nil {
t.Fatal("Error creating test directory")
}
defer os.RemoveAll(step.TempPath)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
if v, ok := state.GetOk("build_dir"); !ok {
t.Fatal("Should store path to build directory in statebag as 'build_dir'")
} else {
// On windows convert everything to forward slash separated paths
// This prevents the regexp interpreting backslashes as escape sequences
stateBuildDir := filepath.ToSlash(v.(string))
expectedBuildDirRe := regexp.MustCompile(
filepath.ToSlash(filepath.Join(step.TempPath, "hyperv") + `[[:digit:]]{9}$`))
match := expectedBuildDirRe.MatchString(stateBuildDir)
if !match {
t.Fatalf("Got path that doesn't match expected format in 'build_dir': %s", stateBuildDir)
}
}
// Test Cleanup
step.Cleanup(state)
if _, err := os.Stat(step.buildDir); err == nil {
t.Fatalf("Build directory should NOT exist after Cleanup: %s", step.buildDir)
}
if _, err := os.Stat(step.TempPath); err != nil {
t.Fatal("User supplied root for build directory should NOT be deleted by Cleanup")
}
}
func TestStepCreateBuildDir_BadTempPath(t *testing.T) {
state := testState(t)
step := new(StepCreateBuildDir)
// Bad
step.TempPath = genTestDirPath("iDontExist")
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatal("Should have error due to bad path")
}
}

View File

@ -1,107 +0,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))
}
}

View File

@ -1,80 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const (
SwitchTypeInternal = "Internal"
SwitchTypePrivate = "Private"
DefaultSwitchType = SwitchTypeInternal
)
// This step creates switch for VM.
//
// 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
createdSwitch bool
}
func (s *StepCreateSwitch) 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
}
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 {
return
}
driver := state.Get("driver").(Driver)
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))
}
}

View File

@ -1,197 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"path/filepath"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
// This step creates the actual virtual machine.
//
// Produces:
// VMName string - The name of the VM
type StepCreateVM struct {
VMName string
SwitchName string
HarddrivePath string
RamSize uint
DiskSize uint
DiskBlockSize uint
UseLegacyNetworkAdapter bool
Generation uint
Cpu uint
EnableMacSpoofing bool
EnableDynamicMemory bool
EnableSecureBoot bool
SecureBootTemplate string
EnableVirtualizationExtensions bool
AdditionalDiskSize []uint
DifferencingDisk bool
MacAddress string
FixedVHD bool
Version string
KeepRegistered bool
}
func (s *StepCreateVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Creating virtual machine...")
var path string
if v, ok := state.GetOk("build_dir"); ok {
path = v.(string)
}
err := driver.CheckVMName(s.VMName)
if err != nil {
s.KeepRegistered = true
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Determine if we even have an existing virtual harddrive to attach
harddrivePath := ""
if harddrivePathRaw, ok := state.GetOk("iso_path"); ok {
extension := strings.ToLower(filepath.Ext(harddrivePathRaw.(string)))
if extension == ".vhd" || extension == ".vhdx" {
harddrivePath = harddrivePathRaw.(string)
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
// convert the MB to bytes
ramSize := int64(s.RamSize) * 1024 * 1024
diskSize := int64(s.DiskSize) * 1024 * 1024
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)
if err != nil {
err := fmt.Errorf("Error creating virtual machine: %s", err)
state.Put("error", err)
ui.Error(err.Error())
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)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
err = driver.SetVirtualMachineDynamicMemory(s.VMName, s.EnableDynamicMemory)
if err != nil {
err := fmt.Errorf("Error setting virtual machine dynamic memory: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if s.EnableMacSpoofing {
err = driver.SetVirtualMachineMacSpoofing(s.VMName, s.EnableMacSpoofing)
if err != nil {
err := fmt.Errorf("Error setting virtual machine mac spoofing: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if s.Generation == 2 {
err = driver.SetVirtualMachineSecureBoot(s.VMName, s.EnableSecureBoot, s.SecureBootTemplate)
if err != nil {
err := fmt.Errorf("Error setting secure boot: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if s.EnableVirtualizationExtensions {
//This is only supported on Windows 10 and Windows Server 2016 onwards
err = driver.SetVirtualMachineVirtualizationExtensions(s.VMName, s.EnableVirtualizationExtensions)
if err != nil {
err := fmt.Errorf("Error setting virtual machine virtualization extensions: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if len(s.AdditionalDiskSize) > 0 {
for index, size := range s.AdditionalDiskSize {
diskSize := int64(size * 1024 * 1024)
diskFile := fmt.Sprintf("%s-%d.vhdx", s.VMName, index)
err = driver.AddVirtualMachineHardDrive(s.VMName, path, diskFile, diskSize, diskBlockSize, "SCSI")
if err != nil {
err := fmt.Errorf("Error creating and attaching additional disk drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
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
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_id", s.VMName)
return multistep.ActionContinue
}
func (s *StepCreateVM) Cleanup(state multistep.StateBag) {
if s.VMName == "" {
return
}
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
if s.KeepRegistered {
ui.Say("keep_registered set. Skipping unregister/deletion of VM.")
return
}
ui.Say("Unregistering and deleting virtual machine...")
err := driver.DeleteVirtualMachine(s.VMName)
if err != nil {
ui.Error(fmt.Sprintf("Error deleting virtual machine: %s", err))
}
// TODO: Clean up created VHDX
}

View File

@ -1,58 +0,0 @@
package common
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepCreateVM_impl(t *testing.T) {
var _ multistep.Step = new(StepCreateVM)
}
func TestStepCreateVM(t *testing.T) {
state := testState(t)
step := new(StepCreateVM)
step.VMName = "test-VM-Name"
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
// Test the driver
if !driver.CheckVMName_Called {
t.Fatal("Should have called CheckVMName")
}
}
func TestStepCreateVM_CheckVMNameErr(t *testing.T) {
state := testState(t)
step := new(StepCreateVM)
step.VMName = "test-VM-Name"
driver := state.Get("driver").(*DriverMock)
driver.CheckVMName_Err = fmt.Errorf("A virtual machine with the name is already" +
" defined in Hyper-V. To avoid a name collision, please set your " +
"vm_name to a unique value")
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatal("Should have error")
}
// Test the driver
if !driver.CheckVMName_Called {
t.Fatal("Should have called CheckVMName")
}
}

View File

@ -1,37 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepDisableVlan struct {
}
func (s *StepDisableVlan) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
errorMsg := "Error disabling vlan: %s"
vmName := state.Get("vmName").(string)
switchName := state.Get("SwitchName").(string)
ui.Say("Disabling vlan...")
err := driver.UntagVirtualMachineNetworkAdapterVlan(vmName, switchName)
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepDisableVlan) Cleanup(state multistep.StateBag) {
//do nothing
}

View File

@ -1,37 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepEnableIntegrationService struct {
name string
}
func (s *StepEnableIntegrationService) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Enabling Integration Service...")
vmName := state.Get("vmName").(string)
s.name = "Guest Service Interface"
err := driver.EnableVirtualMachineIntegrationService(vmName, s.name)
if err != nil {
err := fmt.Errorf("Error enabling Integration Service: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepEnableIntegrationService) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,54 +0,0 @@
package common
import (
"context"
"fmt"
"path/filepath"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepExportVm struct {
OutputDir string
SkipExport bool
}
func (s *StepExportVm) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
if s.SkipExport {
ui.Say("Skipping export of virtual machine...")
return multistep.ActionContinue
}
ui.Say("Exporting virtual machine...")
// The VM name is needed for the export command
var vmName string
if v, ok := state.GetOk("vmName"); ok {
vmName = v.(string)
}
// The export process exports the VM to a folder named 'vmName' under
// the output directory. This contains the usual 'Snapshots', 'Virtual
// Hard Disks' and 'Virtual Machines' directories.
err := driver.ExportVirtualMachine(vmName, s.OutputDir)
if err != nil {
err = fmt.Errorf("Error exporting vm: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Store the path to the export directory for later steps
exportPath := filepath.Join(s.OutputDir, vmName)
state.Put("export_path", exportPath)
return multistep.ActionContinue
}
func (s *StepExportVm) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,87 +0,0 @@
package common
import (
"context"
"path/filepath"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepExportVm_impl(t *testing.T) {
var _ multistep.Step = new(StepExportVm)
}
func TestStepExportVm(t *testing.T) {
state := testState(t)
step := new(StepExportVm)
// ExportVirtualMachine needs the VM name and a path to export to
vmName := "foo"
state.Put("vmName", vmName)
outputDir := "foopath"
step.OutputDir = outputDir
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
// Test the driver
if !driver.ExportVirtualMachine_Called {
t.Fatal("Should have called ExportVirtualMachine")
}
if driver.ExportVirtualMachine_Path != outputDir {
t.Fatalf("Should call with correct path. Got: %s Wanted: %s",
driver.ExportVirtualMachine_Path, outputDir)
}
if driver.ExportVirtualMachine_VmName != vmName {
t.Fatalf("Should call with correct vm name. Got: %s Wanted: %s",
driver.ExportVirtualMachine_VmName, vmName)
}
// Test we stored the export path in the statebag and it is correct
expectedPath := filepath.Join(outputDir, vmName)
if exportPath, ok := state.GetOk("export_path"); !ok {
t.Fatal("Should set export_path")
} else if exportPath != expectedPath {
t.Fatalf("Bad path stored for export_path. Got: %#v Wanted: %#v", exportPath, expectedPath)
}
}
func TestStepExportVm_skip(t *testing.T) {
state := testState(t)
step := new(StepExportVm)
step.SkipExport = true
// ExportVirtualMachine needs the VM name and a path to export to
vmName := "foo"
state.Put("vmName", vmName)
outputDir := "foopath"
step.OutputDir = outputDir
driver := state.Get("driver").(*DriverMock)
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatalf("Should NOT have error")
}
// Test the driver
if driver.ExportVirtualMachine_Called {
t.Fatal("Should NOT have called ExportVirtualMachine")
}
// Should not store the export path in the statebag
if _, ok := state.GetOk("export_path"); ok {
t.Fatal("Should NOT have stored export_path in the statebag")
}
}

View File

@ -1,121 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"path/filepath"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepMountDvdDrive struct {
Generation uint
FirstBootDevice string
}
func (s *StepMountDvdDrive) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
errorMsg := "Error mounting dvd drive: %s"
vmName := state.Get("vmName").(string)
// Determine if we even have a dvd disk to attach
var isoPath string
if isoPathRaw, ok := state.GetOk("iso_path"); ok {
isoPath = isoPathRaw.(string)
} else {
log.Println("No dvd disk, not attaching.")
return multistep.ActionContinue
}
// Determine if its a virtual hdd to mount
if strings.ToLower(filepath.Ext(isoPath)) == ".vhd" || strings.ToLower(filepath.Ext(isoPath)) == ".vhdx" {
log.Println("Its a hard disk, not attaching.")
return multistep.ActionContinue
}
// should be able to mount up to 60 additional iso images using SCSI
// but Windows would only allow a max of 22 due to available drive letters
// Will Windows assign DVD drives to A: and B: ?
// For IDE, there are only 2 controllers (0,1) with 2 locations each (0,1)
var dvdControllerProperties DvdControllerProperties
controllerNumber, controllerLocation, err := driver.CreateDvdDrive(vmName, isoPath, s.Generation)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
dvdControllerProperties.ControllerNumber = controllerNumber
dvdControllerProperties.ControllerLocation = controllerLocation
dvdControllerProperties.Existing = false
state.Put("os.dvd.properties", dvdControllerProperties)
// the "first_boot_device" setting has precedence over the legacy boot order
// configuration, but only if its been assigned a value.
if s.FirstBootDevice == "" {
if s.Generation > 1 {
// only print this message for Gen2, it's not a true statement for Gen1 VMs
ui.Say(fmt.Sprintf("Setting boot drive to os dvd drive %s ...", isoPath))
}
err = driver.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, s.Generation)
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
ui.Say(fmt.Sprintf("Mounting os dvd drive %s ...", isoPath))
err = driver.MountDvdDrive(vmName, isoPath, controllerNumber, controllerLocation)
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepMountDvdDrive) Cleanup(state multistep.StateBag) {
dvdControllerState := state.Get("os.dvd.properties")
if dvdControllerState == nil {
return
}
dvdController := dvdControllerState.(DvdControllerProperties)
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
ui := state.Get("ui").(packersdk.Ui)
errorMsg := "Error unmounting os dvd drive: %s"
ui.Say("Clean up os dvd drive...")
if dvdController.Existing {
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error unmounting dvd drive: %s", err)
log.Print(fmt.Sprintf(errorMsg, err))
}
} else {
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error deleting dvd drive: %s", err)
log.Print(fmt.Sprintf(errorMsg, err))
}
}
}

View File

@ -1,120 +0,0 @@
package common
import (
"context"
"fmt"
"io"
"log"
"os"
"path/filepath"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/tmp"
)
const (
FloppyFileName = "assets.vfd"
)
type StepMountFloppydrive struct {
Generation uint
floppyPath string
}
func (s *StepMountFloppydrive) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if s.Generation > 1 {
return multistep.ActionContinue
}
driver := state.Get("driver").(Driver)
// Determine if we even have a floppy disk to attach
var floppyPath string
if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
floppyPath = floppyPathRaw.(string)
} else {
log.Println("No floppy disk, not attaching.")
return multistep.ActionContinue
}
// Hyper-V is really dumb and can't figure out the format of the file
// without an extension, so we need to add the "vfd" extension to the
// floppy.
floppyPath, err := s.copyFloppy(floppyPath)
if err != nil {
state.Put("error", fmt.Errorf("Error preparing floppy: %s", err))
return multistep.ActionHalt
}
ui := state.Get("ui").(packersdk.Ui)
vmName := state.Get("vmName").(string)
ui.Say("Mounting floppy drive...")
err = driver.MountFloppyDrive(vmName, floppyPath)
if err != nil {
state.Put("error", fmt.Errorf("Error mounting floppy drive: %s", err))
return multistep.ActionHalt
}
// Track the path so that we can unregister it from Hyper-V later
s.floppyPath = floppyPath
return multistep.ActionContinue
}
func (s *StepMountFloppydrive) Cleanup(state multistep.StateBag) {
if s.Generation > 1 {
return
}
driver := state.Get("driver").(Driver)
if s.floppyPath == "" {
return
}
errorMsg := "Error unmounting floppy drive: %s"
vmName := state.Get("vmName").(string)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Cleanup floppy drive...")
err := driver.UnmountFloppyDrive(vmName)
if err != nil {
log.Print(fmt.Sprintf(errorMsg, err))
}
err = os.Remove(s.floppyPath)
if err != nil {
log.Print(fmt.Sprintf(errorMsg, err))
}
}
func (s *StepMountFloppydrive) copyFloppy(path string) (string, error) {
tempdir, err := tmp.Dir("hyperv")
if err != nil {
return "", err
}
floppyPath := filepath.Join(tempdir, "floppy.vfd")
f, err := os.Create(floppyPath)
if err != nil {
return "", err
}
defer f.Close()
sourceF, err := os.Open(path)
if err != nil {
return "", err
}
defer sourceF.Close()
log.Printf("Copying floppy to temp location: %s", floppyPath)
if _, err := io.Copy(f, sourceF); err != nil {
return "", err
}
return floppyPath, nil
}

View File

@ -1,95 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepMountGuestAdditions struct {
GuestAdditionsMode string
GuestAdditionsPath string
Generation uint
}
func (s *StepMountGuestAdditions) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
if s.GuestAdditionsMode != "attach" {
ui.Say("Skipping mounting Integration Services Setup Disk...")
return multistep.ActionContinue
}
driver := state.Get("driver").(Driver)
ui.Say("Mounting Integration Services Setup Disk...")
vmName := state.Get("vmName").(string)
// should be able to mount up to 60 additional iso images using SCSI
// but Windows would only allow a max of 22 due to available drive letters
// Will Windows assign DVD drives to A: and B: ?
// For IDE, there are only 2 controllers (0,1) with 2 locations each (0,1)
var dvdControllerProperties DvdControllerProperties
controllerNumber, controllerLocation, err := driver.CreateDvdDrive(vmName, s.GuestAdditionsPath, s.Generation)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
dvdControllerProperties.ControllerNumber = controllerNumber
dvdControllerProperties.ControllerLocation = controllerLocation
dvdControllerProperties.Existing = false
state.Put("guest.dvd.properties", dvdControllerProperties)
ui.Say(fmt.Sprintf("Mounting Integration Services dvd drive %s ...", s.GuestAdditionsPath))
err = driver.MountDvdDrive(vmName, s.GuestAdditionsPath, controllerNumber, controllerLocation)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", s.GuestAdditionsPath,
controllerNumber, controllerLocation))
return multistep.ActionContinue
}
func (s *StepMountGuestAdditions) Cleanup(state multistep.StateBag) {
if s.GuestAdditionsMode != "attach" {
return
}
dvdControllerState := state.Get("guest.dvd.properties")
if dvdControllerState == nil {
return
}
dvdController := dvdControllerState.(DvdControllerProperties)
ui := state.Get("ui").(packersdk.Ui)
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
errorMsg := "Error unmounting Integration Services dvd drive: %s"
ui.Say("Cleanup Integration Services dvd drive...")
if dvdController.Existing {
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
log.Print(fmt.Sprintf(errorMsg, err))
}
} else {
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
log.Print(fmt.Sprintf(errorMsg, err))
}
}
}

View File

@ -1,107 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepMountSecondaryDvdImages struct {
IsoPaths []string
Generation uint
}
type DvdControllerProperties struct {
ControllerNumber uint
ControllerLocation uint
Existing bool
}
func (s *StepMountSecondaryDvdImages) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Mounting secondary DVD images...")
vmName := state.Get("vmName").(string)
// should be able to mount up to 60 additional iso images using SCSI
// but Windows would only allow a max of 22 due to available drive letters
// Will Windows assign DVD drives to A: and B: ?
// For IDE, there are only 2 controllers (0,1) with 2 locations each (0,1)
var dvdProperties []DvdControllerProperties
isoPaths := s.IsoPaths
// Add our custom CD, if it exists
cd_path, ok := state.Get("cd_path").(string)
if ok {
if cd_path != "" {
isoPaths = append(isoPaths, cd_path)
}
}
for _, isoPath := range isoPaths {
var properties DvdControllerProperties
controllerNumber, controllerLocation, err := driver.CreateDvdDrive(vmName, isoPath, s.Generation)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
properties.ControllerNumber = controllerNumber
properties.ControllerLocation = controllerLocation
properties.Existing = false
dvdProperties = append(dvdProperties, properties)
state.Put("secondary.dvd.properties", dvdProperties)
ui.Say(fmt.Sprintf("Mounting secondary dvd drive %s ...", isoPath))
err = driver.MountDvdDrive(vmName, isoPath, controllerNumber, controllerLocation)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", isoPath, controllerNumber,
controllerLocation))
}
return multistep.ActionContinue
}
func (s *StepMountSecondaryDvdImages) Cleanup(state multistep.StateBag) {
dvdControllersState := state.Get("secondary.dvd.properties")
if dvdControllersState == nil {
return
}
dvdControllers := dvdControllersState.([]DvdControllerProperties)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
vmName := state.Get("vmName").(string)
errorMsg := "Error unmounting secondary dvd drive: %s"
ui.Say("Clean up secondary dvd drives...")
for _, dvdController := range dvdControllers {
if dvdController.Existing {
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
log.Print(fmt.Sprintf(errorMsg, err))
}
} else {
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
log.Print(fmt.Sprintf(errorMsg, err))
}
}
}
}

View File

@ -1,80 +0,0 @@
package common
import (
"bytes"
"context"
"fmt"
"log"
"os/exec"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const port string = "13000"
type StepPollingInstallation struct {
}
func (s *StepPollingInstallation) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
errorMsg := "Error polling VM: %s"
vmIp := state.Get("ip").(string)
ui.Say("Start polling VM to check the installation is complete...")
host := "'" + vmIp + "'," + port
var blockBuffer bytes.Buffer
blockBuffer.WriteString("Invoke-Command -scriptblock {function foo(){try{$client=New-Object System.Net.Sockets.TcpClient(")
blockBuffer.WriteString(host)
blockBuffer.WriteString(") -ErrorAction SilentlyContinue;if($client -eq $null){return $false}}catch{return $false}return $true} foo}")
count := 60
var duration time.Duration = 20
sleepTime := time.Second * duration
var res string
for count > 0 {
log.Println(fmt.Sprintf("Connecting vm (%s)...", host))
cmd := exec.Command("powershell", blockBuffer.String())
cmdOut, err := cmd.Output()
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
res = strings.TrimSpace(string(cmdOut))
if res != "False" {
ui.Say("Signal was received from the VM")
// Sleep before starting provision
time.Sleep(time.Second * 30)
break
}
log.Println(fmt.Sprintf("Slipping for more %v seconds...", uint(duration)))
time.Sleep(sleepTime)
count--
}
if count == 0 {
err := fmt.Errorf(errorMsg, "a signal from vm was not received in a given time period ")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("The installation complete")
return multistep.ActionContinue
}
func (s *StepPollingInstallation) Cleanup(state multistep.StateBag) {
}

View File

@ -1,42 +0,0 @@
package common
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepRebootVm struct {
}
func (s *StepRebootVm) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
errorMsg := "Error rebooting vm: %s"
vmName := state.Get("vmName").(string)
ui.Say("Rebooting vm...")
err := driver.RestartVirtualMachine(vmName)
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Waiting the VM to complete rebooting (2 minutes)...")
sleepTime := time.Minute * 2
time.Sleep(sleepTime)
return multistep.ActionContinue
}
func (s *StepRebootVm) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,77 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepRun struct {
GuiCancelFunc context.CancelFunc
Headless bool
SwitchName string
vmName string
}
func (s *StepRun) 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)
ui.Say("Determine Host IP for HyperV machine...")
hostIp, err := driver.GetHostAdapterIpAddressForSwitch(s.SwitchName)
if err != nil {
err := fmt.Errorf("Error getting host adapter ip address: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Host IP for the HyperV machine: %s", hostIp))
state.Put("http_ip", hostIp)
if !s.Headless {
ui.Say("Attempting to connect with vmconnect...")
s.GuiCancelFunc, err = driver.Connect(vmName)
if err != nil {
log.Printf(fmt.Sprintf("Non-fatal error starting vmconnect: %s. continuing...", err))
}
}
ui.Say("Starting the virtual machine...")
err = driver.Start(vmName)
if err != nil {
err := fmt.Errorf("Error starting vm: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.vmName = vmName
return multistep.ActionContinue
}
func (s *StepRun) Cleanup(state multistep.StateBag) {
if s.vmName == "" {
return
}
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
if !s.Headless && s.GuiCancelFunc != nil {
ui.Say("Disconnecting from vmconnect...")
s.GuiCancelFunc()
}
if running, _ := driver.IsRunning(s.vmName); running {
if err := driver.Stop(s.vmName); err != nil {
ui.Error(fmt.Sprintf("Error shutting down VM: %s", err))
}
}
}

View File

@ -1,37 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepSetBootOrder struct {
BootOrder []string
}
func (s *StepSetBootOrder) 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)
if s.BootOrder != nil {
ui.Say(fmt.Sprintf("Setting boot order to %q", s.BootOrder))
err := driver.SetBootOrder(vmName, s.BootOrder)
if err != nil {
err := fmt.Errorf("Error setting the boot order: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepSetBootOrder) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,48 +0,0 @@
package common
import (
"context"
"reflect"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
type bootOrderTest struct {
bootOrder []string
}
var bootOrderTests = [...]bootOrderTest{
{[]string{"SCSI:0:0"}},
}
func TestStepSetBootOrder(t *testing.T) {
step := new(StepSetBootOrder)
for _, d := range bootOrderTests {
state := testState(t)
driver := state.Get("driver").(*DriverMock)
vmName := "test"
state.Put("vmName", vmName)
step.BootOrder = d.bootOrder
action := step.Run(context.Background(), state)
if multistep.ActionContinue != action {
t.Fatalf("Should have returned action %v but got %v", multistep.ActionContinue, action)
}
if vmName != driver.SetBootOrder_VmName {
t.Fatalf("Should have set VmName to %v but got %v", vmName, driver.SetBootOrder_VmName)
}
if !driver.SetBootOrder_Called {
t.Fatalf("Should have called SetBootOrder")
}
if !reflect.DeepEqual(d.bootOrder, driver.SetBootOrder_BootOrder) {
t.Fatalf("Should have set BootOrder to %v but got %v", d.bootOrder, driver.SetBootOrder_BootOrder)
}
}
}

View File

@ -1,160 +0,0 @@
package common
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepSetFirstBootDevice struct {
Generation uint
FirstBootDevice string
}
func ParseBootDeviceIdentifier(deviceIdentifier string, generation uint) (string, uint, uint, error) {
// all input strings are forced to upperCase for comparison, I believe this is
// safe as all of our values are 7bit ASCII clean.
lookupDeviceIdentifier := strings.ToUpper(deviceIdentifier)
if generation == 1 {
// Gen1 values are a simple set of if/then/else values, which we coalesce into a map
// here for simplicity
lookupTable := map[string]string{
"FLOPPY": "FLOPPY",
"IDE": "IDE",
"NET": "NET",
"CD": "CD",
"DVD": "CD",
}
controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
if !isDefined {
return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device group identifier.", deviceIdentifier)
}
// success
return controllerType, 0, 0, nil
}
// everything else is treated as generation 2... the first set of lookups covers
// the simple options..
lookupTable := map[string]string{
"CD": "CD",
"DVD": "CD",
"NET": "NET",
}
controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
if isDefined {
// these types do not require controllerNumber or controllerLocation
return controllerType, 0, 0, nil
}
// not a simple option, check for a controllerType:controllerNumber:controllerLocation formatted
// device..
r, err := regexp.Compile(`^(IDE|SCSI):(\d+):(\d+)$`)
if err != nil {
return "", 0, 0, err
}
controllerMatch := r.FindStringSubmatch(lookupDeviceIdentifier)
if controllerMatch != nil {
var controllerLocation int64
var controllerNumber int64
// NOTE: controllerNumber and controllerLocation cannot be negative, the regex expression
// would not have matched if either number was signed
controllerNumber, err = strconv.ParseInt(controllerMatch[2], 10, 8)
if err == nil {
controllerLocation, err = strconv.ParseInt(controllerMatch[3], 10, 8)
if err == nil {
return controllerMatch[1], uint(controllerNumber), uint(controllerLocation), nil
}
}
return "", 0, 0, err
}
return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device identifier.", deviceIdentifier)
}
func (s *StepSetFirstBootDevice) 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)
if s.FirstBootDevice != "" {
controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(s.FirstBootDevice, s.Generation)
if err == nil {
switch {
case controllerType == "CD":
{
// the "DVD" controller is special, we only apply the setting if we actually mounted
// an ISO and only if that was mounted as the "IsoUrl" not a secondary ISO.
dvdControllerState := state.Get("os.dvd.properties")
if dvdControllerState == nil {
ui.Say("First Boot Device is DVD, but no primary ISO mounted. Ignoring.")
return multistep.ActionContinue
}
ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
dvdController := dvdControllerState.(DvdControllerProperties)
err = driver.SetFirstBootDevice(vmName, controllerType, dvdController.ControllerNumber, dvdController.ControllerLocation, s.Generation)
}
default:
{
// anything else, we just pass as is..
ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
err = driver.SetFirstBootDevice(vmName, controllerType, controllerNumber, controllerLocation, s.Generation)
}
}
}
if err != nil {
err := fmt.Errorf("Error setting first boot device: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepSetFirstBootDevice) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,170 +0,0 @@
package common
import (
"context"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
type parseBootDeviceIdentifierTest struct {
generation uint
deviceIdentifier string
controllerType string
controllerNumber uint
controllerLocation uint
failInParse bool // true if ParseBootDeviceIdentifier should return an error
haltStep bool // true if Step.Run should return Halt action
shouldCallSet bool // true if driver.SetFirstBootDevice should have been called
setDvdProps bool // true to set DvdDeviceProperties state
}
var parseIdentifierTests = [...]parseBootDeviceIdentifierTest{
{1, "IDE", "IDE", 0, 0, false, false, true, false},
{1, "idE", "IDE", 0, 0, false, false, true, false},
{1, "CD", "CD", 0, 0, false, false, false, false},
{1, "CD", "CD", 0, 0, false, false, true, true},
{1, "cD", "CD", 0, 0, false, false, false, false},
{1, "DVD", "CD", 0, 0, false, false, false, false},
{1, "DVD", "CD", 0, 0, false, false, true, true},
{1, "Dvd", "CD", 0, 0, false, false, false, false},
{1, "FLOPPY", "FLOPPY", 0, 0, false, false, true, false},
{1, "FloppY", "FLOPPY", 0, 0, false, false, true, false},
{1, "NET", "NET", 0, 0, false, false, true, false},
{1, "net", "NET", 0, 0, false, false, true, false},
{1, "", "", 0, 0, true, false, false, false},
{1, "bad", "", 0, 0, true, true, false, false},
{1, "IDE:0:0", "", 0, 0, true, true, true, false},
{1, "SCSI:0:0", "", 0, 0, true, true, true, false},
{2, "IDE", "", 0, 0, true, true, true, false},
{2, "idE", "", 0, 0, true, true, true, false},
{2, "CD", "CD", 0, 0, false, false, false, false},
{2, "CD", "CD", 0, 0, false, false, true, true},
{2, "cD", "CD", 0, 0, false, false, false, false},
{2, "DVD", "CD", 0, 0, false, false, false, false},
{2, "DVD", "CD", 0, 0, false, false, true, true},
{2, "Dvd", "CD", 0, 0, false, false, false, false},
{2, "FLOPPY", "", 0, 0, true, true, true, false},
{2, "FloppY", "", 0, 0, true, true, true, false},
{2, "NET", "NET", 0, 0, false, false, true, false},
{2, "net", "NET", 0, 0, false, false, true, false},
{2, "", "", 0, 0, true, false, false, false},
{2, "bad", "", 0, 0, true, true, false, false},
{2, "IDE:0:0", "IDE", 0, 0, false, false, true, false},
{2, "SCSI:0:0", "SCSI", 0, 0, false, false, true, false},
{2, "Ide:0:0", "IDE", 0, 0, false, false, true, false},
{2, "sCsI:0:0", "SCSI", 0, 0, false, false, true, false},
{2, "IDEscsi:0:0", "", 0, 0, true, true, false, false},
{2, "SCSIide:0:0", "", 0, 0, true, true, false, false},
{2, "IDE:0", "", 0, 0, true, true, false, false},
{2, "SCSI:0", "", 0, 0, true, true, false, false},
{2, "IDE:0:a", "", 0, 0, true, true, false, false},
{2, "SCSI:0:a", "", 0, 0, true, true, false, false},
{2, "IDE:0:653", "", 0, 0, true, true, false, false},
{2, "SCSI:-10:0", "", 0, 0, true, true, false, false},
}
func TestStepSetFirstBootDevice_impl(t *testing.T) {
var _ multistep.Step = new(StepSetFirstBootDevice)
}
func TestStepSetFirstBootDevice_ParseIdentifier(t *testing.T) {
for _, identifierTest := range parseIdentifierTests {
controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(
identifierTest.deviceIdentifier,
identifierTest.generation)
if (err != nil) != identifierTest.failInParse {
t.Fatalf("Test %q (gen %v): failInParse: %v but err: %v", identifierTest.deviceIdentifier,
identifierTest.generation, identifierTest.failInParse, err)
}
switch {
case controllerType != identifierTest.controllerType:
t.Fatalf("Test %q (gen %v): controllerType: %q != %q", identifierTest.deviceIdentifier, identifierTest.generation,
identifierTest.controllerType, controllerType)
case controllerNumber != identifierTest.controllerNumber:
t.Fatalf("Test %q (gen %v): controllerNumber: %v != %v", identifierTest.deviceIdentifier, identifierTest.generation,
identifierTest.controllerNumber, controllerNumber)
case controllerLocation != identifierTest.controllerLocation:
t.Fatalf("Test %q (gen %v): controllerLocation: %v != %v", identifierTest.deviceIdentifier, identifierTest.generation,
identifierTest.controllerLocation, controllerLocation)
}
}
}
func TestStepSetFirstBootDevice(t *testing.T) {
step := new(StepSetFirstBootDevice)
for _, identifierTest := range parseIdentifierTests {
state := testState(t)
driver := state.Get("driver").(*DriverMock)
// requires the vmName state value
vmName := "foo"
state.Put("vmName", vmName)
// pretend that we mounted a DVD somewhere (CD:0:0)
if identifierTest.setDvdProps {
var dvdControllerProperties DvdControllerProperties
dvdControllerProperties.ControllerNumber = 0
dvdControllerProperties.ControllerLocation = 0
dvdControllerProperties.Existing = false
state.Put("os.dvd.properties", dvdControllerProperties)
}
step.Generation = identifierTest.generation
step.FirstBootDevice = identifierTest.deviceIdentifier
action := step.Run(context.Background(), state)
if (action != multistep.ActionContinue) != identifierTest.haltStep {
t.Fatalf("Test %q (gen %v): Bad action: %v", identifierTest.deviceIdentifier, identifierTest.generation, action)
}
if identifierTest.haltStep {
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("Test %q (gen %v): Should have error", identifierTest.deviceIdentifier, identifierTest.generation)
}
// don't perform the remaining checks..
continue
} else {
if _, ok := state.GetOk("error"); ok {
t.Fatalf("Test %q (gen %v): Should NOT have error", identifierTest.deviceIdentifier, identifierTest.generation)
}
}
if driver.SetFirstBootDevice_Called != identifierTest.shouldCallSet {
if identifierTest.shouldCallSet {
t.Fatalf("Test %q (gen %v): Should have called SetFirstBootDevice", identifierTest.deviceIdentifier, identifierTest.generation)
}
t.Fatalf("Test %q (gen %v): Should NOT have called SetFirstBootDevice", identifierTest.deviceIdentifier, identifierTest.generation)
}
if (driver.SetFirstBootDevice_Called) &&
((driver.SetFirstBootDevice_VmName != vmName) ||
(driver.SetFirstBootDevice_ControllerType != identifierTest.controllerType) ||
(driver.SetFirstBootDevice_ControllerNumber != identifierTest.controllerNumber) ||
(driver.SetFirstBootDevice_ControllerLocation != identifierTest.controllerLocation) ||
(driver.SetFirstBootDevice_Generation != identifierTest.generation)) {
t.Fatalf("Test %q (gen %v): Called SetFirstBootDevice with unexpected arguments.", identifierTest.deviceIdentifier, identifierTest.generation)
}
}
}

View File

@ -1,90 +0,0 @@
package common
import (
"bytes"
"context"
"errors"
"fmt"
"log"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
// This step shuts down the machine. It first attempts to do so gracefully,
// but ultimately forcefully shuts it down if that fails.
//
// Uses:
// communicator packersdk.Communicator
// driver Driver
// ui packersdk.Ui
// vmName string
//
// Produces:
// <nothing>
type StepShutdown struct {
Command string
Timeout time.Duration
}
func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packersdk.Communicator)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
vmName := state.Get("vmName").(string)
if s.Command != "" {
ui.Say("Gracefully halting virtual machine...")
log.Printf("Executing shutdown command: %s", s.Command)
var stdout, stderr bytes.Buffer
cmd := &packersdk.RemoteCmd{
Command: s.Command,
Stdout: &stdout,
Stderr: &stderr,
}
if err := comm.Start(ctx, cmd); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Wait for the machine to actually shut down
log.Printf("Waiting max %s for shutdown to complete", s.Timeout)
shutdownTimer := time.After(s.Timeout)
for {
running, _ := driver.IsRunning(vmName)
if !running {
break
}
select {
case <-shutdownTimer:
log.Printf("Shutdown stdout: %s", stdout.String())
log.Printf("Shutdown stderr: %s", stderr.String())
err := errors.New("Timeout while waiting for machine to shut down.")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
default:
time.Sleep(500 * time.Millisecond)
}
}
} else {
ui.Say("Forcibly halting virtual machine...")
if err := driver.Stop(vmName); err != nil {
err := fmt.Errorf("Error stopping VM: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
log.Println("VM shut down.")
return multistep.ActionContinue
}
func (s *StepShutdown) Cleanup(state multistep.StateBag) {}

View File

@ -1,31 +0,0 @@
package common
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepSleep struct {
Minutes time.Duration
ActionName string
}
func (s *StepSleep) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
if len(s.ActionName) > 0 {
ui.Say(s.ActionName + "! Waiting for " + fmt.Sprintf("%v", uint(s.Minutes)) +
" minutes to let the action to complete...")
}
time.Sleep(time.Minute * s.Minutes)
return multistep.ActionContinue
}
func (s *StepSleep) Cleanup(state multistep.StateBag) {
}

View File

@ -1,28 +0,0 @@
package common
import (
"bytes"
"os"
"path/filepath"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/uuid"
)
func testState(t *testing.T) multistep.StateBag {
state := new(multistep.BasicStateBag)
state.Put("driver", new(DriverMock))
state.Put("ui", &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
return state
}
// Generates an absolute path to a directory under OS temp with a name
// beginning with prefix and a UUID appended to the end
func genTestDirPath(prefix string) string {
return filepath.Join(os.TempDir(), prefix+"-"+uuid.TimeOrderedUUID())
}

View File

@ -1,88 +0,0 @@
package common
import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type bootCommandTemplateData struct {
HTTPIP string
HTTPPort int
Name string
}
// This step "types" the boot command into the VM via the Hyper-V virtual keyboard
type StepTypeBootCommand struct {
BootCommand string
BootWait time.Duration
SwitchName string
Ctx interpolate.Context
GroupInterval time.Duration
}
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packersdk.Ui)
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
hostIp := state.Get("http_ip").(string)
// Wait the for the vm to boot.
if int64(s.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
select {
case <-time.After(s.BootWait):
break
case <-ctx.Done():
return multistep.ActionHalt
}
}
s.Ctx.Data = &bootCommandTemplateData{
hostIp,
httpPort,
vmName,
}
sendCodes := func(codes []string) error {
scanCodesToSendString := strings.Join(codes, " ")
return driver.TypeScanCodes(vmName, scanCodesToSendString)
}
d := bootcommand.NewPCXTDriver(sendCodes, 32, s.GroupInterval)
ui.Say("Typing the boot command...")
command, err := interpolate.Render(s.BootCommand, &s.Ctx)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
seq, err := bootcommand.GenerateExpressionSequence(command)
if err != nil {
err := fmt.Errorf("Error generating boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err := seq.Do(ctx, d); err != nil {
err := fmt.Errorf("Error running boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}

View File

@ -1,57 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepUnmountDvdDrive struct {
}
func (s *StepUnmountDvdDrive) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Unmount/delete os dvd drive...")
dvdControllerState := state.Get("os.dvd.properties")
if dvdControllerState == nil {
return multistep.ActionContinue
}
dvdController := dvdControllerState.(DvdControllerProperties)
if dvdController.Existing {
ui.Say(fmt.Sprintf("Unmounting os dvd drives controller %d location %d ...",
dvdController.ControllerNumber, dvdController.ControllerLocation))
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error unmounting os dvd drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
ui.Say(fmt.Sprintf("Delete os dvd drives controller %d location %d ...",
dvdController.ControllerNumber, dvdController.ControllerLocation))
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error deleting os dvd drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
state.Put("os.dvd.properties", nil)
return multistep.ActionContinue
}
func (s *StepUnmountDvdDrive) Cleanup(state multistep.StateBag) {
}

View File

@ -1,40 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepUnmountFloppyDrive struct {
Generation uint
}
func (s *StepUnmountFloppyDrive) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
if s.Generation > 1 {
return multistep.ActionContinue
}
vmName := state.Get("vmName").(string)
ui.Say("Unmount/delete floppy drive (Run)...")
errorMsg := "Error Unmounting floppy drive: %s"
err := driver.UnmountFloppyDrive(vmName)
if err != nil {
err := fmt.Errorf(errorMsg, err)
state.Put("error", err)
ui.Error(err.Error())
}
return multistep.ActionContinue
}
func (s *StepUnmountFloppyDrive) Cleanup(state multistep.StateBag) {
// do nothing
}

View File

@ -1,57 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepUnmountGuestAdditions struct {
}
func (s *StepUnmountGuestAdditions) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Unmount/delete Integration Services dvd drive...")
dvdControllerState := state.Get("guest.dvd.properties")
if dvdControllerState == nil {
return multistep.ActionContinue
}
dvdController := dvdControllerState.(DvdControllerProperties)
if dvdController.Existing {
ui.Say(fmt.Sprintf("Unmounting Integration Services dvd drives controller %d location %d ...",
dvdController.ControllerNumber, dvdController.ControllerLocation))
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error unmounting Integration Services dvd drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
ui.Say(fmt.Sprintf("Delete Integration Services dvd drives controller %d location %d ...",
dvdController.ControllerNumber, dvdController.ControllerLocation))
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error deleting Integration Services dvd drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
state.Put("guest.dvd.properties", nil)
return multistep.ActionContinue
}
func (s *StepUnmountGuestAdditions) Cleanup(state multistep.StateBag) {
}

View File

@ -1,59 +0,0 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepUnmountSecondaryDvdImages struct {
}
func (s *StepUnmountSecondaryDvdImages) 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)
ui.Say("Unmount/delete secondary dvd drives...")
dvdControllersState := state.Get("secondary.dvd.properties")
if dvdControllersState == nil {
return multistep.ActionContinue
}
dvdControllers := dvdControllersState.([]DvdControllerProperties)
for _, dvdController := range dvdControllers {
if dvdController.Existing {
ui.Say(fmt.Sprintf("Unmounting secondary dvd drives controller %d location %d ...",
dvdController.ControllerNumber, dvdController.ControllerLocation))
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error unmounting secondary dvd drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
ui.Say(fmt.Sprintf("Delete secondary dvd drives controller %d location %d ...",
dvdController.ControllerNumber, dvdController.ControllerLocation))
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
if err != nil {
err := fmt.Errorf("Error deleting secondary dvd drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
state.Put("secondary.dvd.properties", nil)
return multistep.ActionContinue
}
func (s *StepUnmountSecondaryDvdImages) Cleanup(state multistep.StateBag) {
}

View File

@ -1,92 +0,0 @@
package common
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const (
SleepSeconds = 10
)
type StepWaitForPowerOff struct {
}
func (s *StepWaitForPowerOff) 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)
ui.Say("Waiting for vm to be powered down...")
for {
isOff, err := driver.IsOff(vmName)
if err != nil {
err := fmt.Errorf("Error checking if vm is off: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if isOff {
break
} else {
time.Sleep(time.Second * SleepSeconds)
}
}
return multistep.ActionContinue
}
func (s *StepWaitForPowerOff) Cleanup(state multistep.StateBag) {
}
type StepWaitForInstallToComplete struct {
ExpectedRebootCount uint
ActionName string
}
func (s *StepWaitForInstallToComplete) 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)
if len(s.ActionName) > 0 {
ui.Say(fmt.Sprintf("%v ! Waiting for VM to reboot %v times...", s.ActionName, s.ExpectedRebootCount))
}
var rebootCount uint
var lastUptime uint64
for rebootCount < s.ExpectedRebootCount {
uptime, err := driver.Uptime(vmName)
if err != nil {
err := fmt.Errorf("Error checking uptime: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if uptime < lastUptime {
rebootCount++
ui.Say(fmt.Sprintf("%v -> Detected reboot %v after %v seconds...", s.ActionName, rebootCount, lastUptime))
}
lastUptime = uptime
if rebootCount < s.ExpectedRebootCount {
time.Sleep(time.Second * SleepSeconds)
}
}
return multistep.ActionContinue
}
func (s *StepWaitForInstallToComplete) Cleanup(state multistep.StateBag) {
}

View File

@ -1,377 +0,0 @@
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package iso
import (
"context"
"errors"
"fmt"
"log"
"path/filepath"
"strings"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/shutdowncommand"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
)
const (
DefaultDiskSize = 40 * 1024 // ~40GB
MinDiskSize = 256 // 256MB
MaxDiskSize = 64 * 1024 * 1024 // 64TB
MaxVHDSize = 2040 * 1024 // 2040GB
DefaultDiskBlockSize = 32 // 32MB
MinDiskBlockSize = 1 // 1MB
MaxDiskBlockSize = 256 // 256MB
DefaultRamSize = 1 * 1024 // 1GB
MinRamSize = 32 // 32MB
MaxRamSize = 32 * 1024 // 32GB
MinNestedVirtualizationRamSize = 4 * 1024 // 4GB
LowRam = 256 // 256MB
DefaultUsername = ""
DefaultPassword = ""
)
// Builder implements packersdk.Builder and builds the actual Hyperv
// images.
type Builder struct {
config Config
runner multistep.Runner
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
commonsteps.HTTPConfig `mapstructure:",squash"`
commonsteps.ISOConfig `mapstructure:",squash"`
bootcommand.BootConfig `mapstructure:",squash"`
hypervcommon.OutputConfig `mapstructure:",squash"`
hypervcommon.SSHConfig `mapstructure:",squash"`
hypervcommon.CommonConfig `mapstructure:",squash"`
shutdowncommand.ShutdownConfig `mapstructure:",squash"`
// The size, in megabytes, of the hard disk to create
// for the VM. By default, this is 40 GB.
DiskSize uint `mapstructure:"disk_size" required:"false"`
// If true use a legacy network adapter as the NIC.
// This defaults to false. A legacy network adapter is fully emulated NIC, and is thus
// supported by various exotic operating systems, but this emulation requires
// additional overhead and should only be used if absolutely necessary.
UseLegacyNetworkAdapter bool `mapstructure:"use_legacy_network_adapter" required:"false"`
// If true enables differencing disks. Only
// the changes will be written to the new disk. This is especially useful if
// your source is a VHD/VHDX. This defaults to false.
DifferencingDisk bool `mapstructure:"differencing_disk" required:"false"`
// If true, creates the boot disk on the
// virtual machine as a fixed VHD format disk. The default is false, which
// creates a dynamic VHDX format disk. This option requires setting
// generation to 1, skip_compaction to true, and
// differencing_disk to false. Additionally, any value entered for
// disk_block_size will be ignored. The most likely use case for this
// option is outputing a disk that is in the format required for upload to
// Azure.
FixedVHD bool `mapstructure:"use_fixed_vhd_format" required:"false"`
ctx interpolate.Context
}
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
PluginType: hypervcommon.BuilderId,
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"boot_command",
},
},
}, raws...)
if err != nil {
return nil, nil, err
}
// Accumulate any errors and warnings
var errs *packersdk.MultiError
warnings := make([]string, 0)
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
warnings = append(warnings, isoWarnings...)
errs = packersdk.MultiErrorAppend(errs, isoErrs...)
errs = packersdk.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
errs = packersdk.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
commonErrs, commonWarns := b.config.CommonConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)
errs = packersdk.MultiErrorAppend(errs, commonErrs...)
warnings = append(warnings, commonWarns...)
if len(b.config.ISOConfig.ISOUrls) < 1 ||
(strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhd" &&
strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhdx") {
//We only create a new hard drive if an existing one to copy from does not exist
err = b.checkDiskSize()
if err != nil {
errs = packersdk.MultiErrorAppend(errs, err)
}
}
if b.config.Cpu < 1 {
b.config.Cpu = 1
}
if b.config.Generation == 2 {
if b.config.UseLegacyNetworkAdapter {
err = errors.New("Generation 2 vms don't support legacy network adapters.")
errs = packersdk.MultiErrorAppend(errs, err)
}
}
// Errors
if b.config.Generation > 1 && b.config.FixedVHD {
err = errors.New("Fixed VHD disks are only supported on Generation 1 virtual machines.")
errs = packersdk.MultiErrorAppend(errs, err)
}
if !b.config.SkipCompaction && b.config.FixedVHD {
err = errors.New("Fixed VHD disks do not support compaction.")
errs = packersdk.MultiErrorAppend(errs, err)
}
if b.config.DifferencingDisk && b.config.FixedVHD {
err = errors.New("Fixed VHD disks are not supported with differencing disks.")
errs = packersdk.MultiErrorAppend(errs, err)
}
// Warnings
if b.config.ShutdownCommand == "" {
warnings = append(warnings,
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
"will forcibly halt the virtual machine, which may result in data loss.")
}
if errs != nil && len(errs.Errors) > 0 {
return nil, warnings, errs
}
return nil, warnings, nil
}
// Run executes a Packer build and returns a packersdk.Artifact representing
// a Hyperv appliance.
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
// Create the driver that we'll use to communicate with Hyperv
driver, err := hypervcommon.NewHypervPS4Driver()
if err != nil {
return nil, fmt.Errorf("Failed creating Hyper-V driver: %s", err)
}
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
steps := []multistep.Step{
&hypervcommon.StepCreateBuildDir{
TempPath: b.config.TempPath,
},
&commonsteps.StepOutputDir{
Force: b.config.PackerForce,
Path: b.config.OutputDir,
},
&commonsteps.StepDownload{
Checksum: b.config.ISOChecksum,
Description: "ISO",
ResultKey: "iso_path",
Url: b.config.ISOUrls,
Extension: b.config.TargetExtension,
TargetPath: b.config.TargetPath,
},
&commonsteps.StepCreateFloppy{
Files: b.config.FloppyConfig.FloppyFiles,
Directories: b.config.FloppyConfig.FloppyDirectories,
Label: b.config.FloppyConfig.FloppyLabel,
},
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
&hypervcommon.StepCreateSwitch{
SwitchName: b.config.SwitchName,
},
&hypervcommon.StepCreateVM{
VMName: b.config.VMName,
SwitchName: b.config.SwitchName,
RamSize: b.config.RamSize,
DiskSize: b.config.DiskSize,
DiskBlockSize: b.config.DiskBlockSize,
Generation: b.config.Generation,
Cpu: b.config.Cpu,
EnableMacSpoofing: b.config.EnableMacSpoofing,
EnableDynamicMemory: b.config.EnableDynamicMemory,
EnableSecureBoot: b.config.EnableSecureBoot,
SecureBootTemplate: b.config.SecureBootTemplate,
EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions,
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.StepMountDvdDrive{
Generation: b.config.Generation,
FirstBootDevice: b.config.FirstBootDevice,
},
&hypervcommon.StepMountFloppydrive{
Generation: b.config.Generation,
},
&hypervcommon.StepMountGuestAdditions{
GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsPath: b.config.GuestAdditionsPath,
Generation: b.config.Generation,
},
&commonsteps.StepCreateCD{
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&hypervcommon.StepMountSecondaryDvdImages{
IsoPaths: b.config.SecondaryDvdImages,
Generation: b.config.Generation,
},
&hypervcommon.StepConfigureVlan{
VlanId: b.config.VlanId,
SwitchVlanId: b.config.SwitchVlanId,
},
&hypervcommon.StepSetBootOrder{
BootOrder: b.config.BootOrder,
},
&hypervcommon.StepSetFirstBootDevice{
Generation: b.config.Generation,
FirstBootDevice: b.config.FirstBootDevice,
},
&hypervcommon.StepRun{
Headless: b.config.Headless,
SwitchName: b.config.SwitchName,
},
&hypervcommon.StepTypeBootCommand{
BootCommand: b.config.FlatBootCommand(),
BootWait: b.config.BootWait,
SwitchName: b.config.SwitchName,
Ctx: b.config.ctx,
GroupInterval: b.config.BootConfig.BootGroupInterval,
},
// configure the communicator ssh, winrm
&communicator.StepConnect{
Config: &b.config.SSHConfig.Comm,
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()),
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
},
// provision requires communicator to be setup
&commonsteps.StepProvision{},
// Remove ephemeral key from authorized_hosts if using SSH communicator
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.SSHConfig.Comm,
},
&hypervcommon.StepShutdown{
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
},
// wait for the vm to be powered off
&hypervcommon.StepWaitForPowerOff{},
// remove the secondary dvd images
// after we power down
&hypervcommon.StepUnmountSecondaryDvdImages{},
&hypervcommon.StepUnmountGuestAdditions{},
&hypervcommon.StepUnmountDvdDrive{},
&hypervcommon.StepUnmountFloppyDrive{
Generation: b.config.Generation,
},
&hypervcommon.StepCompactDisk{
SkipCompaction: b.config.SkipCompaction,
},
&hypervcommon.StepExportVm{
OutputDir: b.config.OutputDir,
SkipExport: b.config.SkipExport,
},
&hypervcommon.StepCollateArtifacts{
OutputDir: b.config.OutputDir,
SkipExport: b.config.SkipExport,
},
// the clean up actions for each step will be executed reverse order
}
// Run the steps.
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)
// Report any errors.
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If we were interrupted or cancelled, then just exit.
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("Build was cancelled.")
}
if _, ok := state.GetOk(multistep.StateHalted); ok {
return nil, errors.New("Build was halted.")
}
generatedData := map[string]interface{}{"generated_data": state.Get("generated_data")}
return hypervcommon.NewArtifact(b.config.OutputDir, generatedData)
}
// Cancel.
func (b *Builder) checkDiskSize() error {
if b.config.DiskSize == 0 {
b.config.DiskSize = DefaultDiskSize
}
log.Println(fmt.Sprintf("%s: %v", "DiskSize", b.config.DiskSize))
if b.config.DiskSize < MinDiskSize {
return fmt.Errorf("disk_size: Virtual machine requires disk space >= %v GB, but defined: %v",
MinDiskSize, b.config.DiskSize/1024)
} else if b.config.DiskSize > MaxDiskSize && !b.config.FixedVHD {
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v",
MaxDiskSize, b.config.DiskSize/1024)
} else if b.config.DiskSize > MaxVHDSize && b.config.FixedVHD {
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v",
MaxVHDSize/1024, b.config.DiskSize/1024)
}
return nil
}

View File

@ -1,247 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package iso
import (
"github.com/hashicorp/hcl/v2/hcldec"
"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"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
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"`
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"`
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.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false},
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
"disk_block_size": &hcldec.AttrSpec{Name: "disk_block_size", Type: cty.Number, Required: false},
"memory": &hcldec.AttrSpec{Name: "memory", Type: cty.Number, Required: false},
"secondary_iso_images": &hcldec.AttrSpec{Name: "secondary_iso_images", Type: cty.List(cty.String), Required: false},
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
"guest_additions_mode": &hcldec.AttrSpec{Name: "guest_additions_mode", Type: cty.String, Required: false},
"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_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},
"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},
"enable_dynamic_memory": &hcldec.AttrSpec{Name: "enable_dynamic_memory", Type: cty.Bool, Required: false},
"enable_secure_boot": &hcldec.AttrSpec{Name: "enable_secure_boot", Type: cty.Bool, Required: false},
"secure_boot_template": &hcldec.AttrSpec{Name: "secure_boot_template", Type: cty.String, Required: false},
"enable_virtualization_extensions": &hcldec.AttrSpec{Name: "enable_virtualization_extensions", Type: cty.Bool, Required: false},
"temp_path": &hcldec.AttrSpec{Name: "temp_path", Type: cty.String, Required: false},
"configuration_version": &hcldec.AttrSpec{Name: "configuration_version", Type: cty.String, Required: false},
"keep_registered": &hcldec.AttrSpec{Name: "keep_registered", Type: cty.Bool, Required: false},
"skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false},
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
"first_boot_device": &hcldec.AttrSpec{Name: "first_boot_device", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.List(cty.String), Required: false},
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"use_legacy_network_adapter": &hcldec.AttrSpec{Name: "use_legacy_network_adapter", Type: cty.Bool, Required: false},
"differencing_disk": &hcldec.AttrSpec{Name: "differencing_disk", Type: cty.Bool, Required: false},
"use_fixed_vhd_format": &hcldec.AttrSpec{Name: "use_fixed_vhd_format", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -1,658 +0,0 @@
// +build !windows
package iso
import (
"context"
"fmt"
"reflect"
"strconv"
"testing"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
)
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",
"memory": 64,
"disk_size": 256,
"disk_block_size": 1,
"guest_additions_mode": "none",
"disk_additional_size": "50000,40000,30000",
common.BuildNameConfigKey: "foo",
}
}
func TestBuilder_ImplementsBuilder(t *testing.T) {
var raw interface{}
raw = &Builder{}
if _, ok := raw.(packersdk.Builder); !ok {
t.Error("Builder must implement builder.")
}
}
func TestBuilderPrepare_Defaults(t *testing.T) {
var b Builder
config := testConfig()
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.VMName != "packer-foo" {
t.Errorf("bad vm name: %s", b.config.VMName)
}
}
func TestBuilderPrepare_DiskSize(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "disk_size")
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
if b.config.DiskSize != 40*1024 {
t.Fatalf("bad size: %d", b.config.DiskSize)
}
config["disk_size"] = 256
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.DiskSize != 256 {
t.Fatalf("bad size: %d", b.config.DiskSize)
}
}
func TestBuilderPrepare_DiskBlockSize(t *testing.T) {
var b Builder
config := testConfig()
expected_default_block_size := uint(32)
expected_min_block_size := uint(0)
expected_max_block_size := uint(256)
// Test default with empty disk_block_size
delete(config, "disk_block_size")
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
if b.config.DiskBlockSize != expected_default_block_size {
t.Fatalf("bad default block size with empty config: %d. Expected %d", b.config.DiskBlockSize,
expected_default_block_size)
}
test_sizes := []uint{0, 1, 32, 256, 512, 1 * 1024, 32 * 1024}
for _, test_size := range test_sizes {
config["disk_block_size"] = test_size
b = Builder{}
_, warns, err = b.Prepare(config)
if test_size > expected_max_block_size || test_size < expected_min_block_size {
if len(warns) > 0 {
t.Fatalf("bad, should have no warns: %#v", warns)
}
if err == nil {
t.Fatalf("bad, should have error. disk_block_size=%d outside expected valid range [%d,%d]",
test_size, expected_min_block_size, expected_max_block_size)
}
} else {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad, should not have error: %s", err)
}
if test_size == 0 {
if b.config.DiskBlockSize != expected_default_block_size {
t.Fatalf("bad default block size with 0 value config: %d. Expected: %d",
b.config.DiskBlockSize, expected_default_block_size)
}
} else {
if b.config.DiskBlockSize != test_size {
t.Fatalf("bad block size with 0 value config: %d. Expected: %d", b.config.DiskBlockSize,
expected_default_block_size)
}
}
}
}
}
func TestBuilderPrepare_FixedVHDFormat(t *testing.T) {
var b Builder
config := testConfig()
config["use_fixed_vhd_format"] = true
config["generation"] = 1
config["skip_compaction"] = true
config["differencing_disk"] = false
// use_fixed_vhd_format should work with generation = 1, skip_compaction
// = true, and differencing_disk = false
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
//use_fixed_vhd_format should not work with differencing_disk = true
config["differencing_disk"] = true
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
config["differencing_disk"] = false
//use_fixed_vhd_format should not work with skip_compaction = false
config["skip_compaction"] = false
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
config["skip_compaction"] = true
//use_fixed_vhd_format should not work with generation = 2
config["generation"] = 2
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_FloppyFiles(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "floppy_files")
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
if len(b.config.FloppyFiles) != 0 {
t.Fatalf("bad: %#v", b.config.FloppyFiles)
}
floppiesPath := "../../test-fixtures/floppies"
config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppiesPath), fmt.Sprintf("%s/foo.ps1", floppiesPath)}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
expected := []string{fmt.Sprintf("%s/bar.bat", floppiesPath), fmt.Sprintf("%s/foo.ps1", floppiesPath)}
if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
t.Fatalf("bad: %#v", b.config.FloppyFiles)
}
}
func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
var b Builder
config := testConfig()
config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"}
b = Builder{}
_, _, errs := b.Prepare(config)
if errs == nil {
t.Fatalf("Nonexistent floppies should trigger multierror")
}
if len(errs.(*packersdk.MultiError).Errors) != 2 {
t.Fatalf("Multierror should work and report 2 errors")
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_ISOChecksum(t *testing.T) {
var b Builder
config := testConfig()
// Test bad
config["iso_checksum"] = ""
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test invalid checksum
config["iso_checksum"] = "FOo"
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test good
config["iso_checksum"] = "md5:0B0F137F17AC10944716020B018F8126"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
var b Builder
config := testConfig()
config["iso_checksum"] = "0B0F137F17AC10944716020B018F8126"
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test good
config["iso_checksum"] = "mD5:0B0F137F17AC10944716020B018F8126"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test unknown
config["iso_checksum"] = "fake:foo"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Log("should error in prepare but go-getter doesn't let us validate yet. This will fail before dl.")
}
// Test none
config["iso_checksum"] = "none"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) == 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ISOUrl(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "iso_url")
delete(config, "iso_urls")
// Test both empty
config["iso_url"] = ""
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test iso_url set
config["iso_url"] = "http://www.packer.io"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
expected := []string{"http://www.packer.io"}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
// Test both set
config["iso_url"] = "http://www.packer.io"
config["iso_urls"] = []string{"http://www.packer.io"}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test just iso_urls set
delete(config, "iso_url")
config["iso_urls"] = []string{
"http://www.packer.io",
"http://www.hashicorp.com",
}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
expected = []string{
"http://www.packer.io",
"http://www.hashicorp.com",
}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
}
func TestBuilderPrepare_SizeNotRequiredWhenUsingExistingHarddrive(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "iso_url")
delete(config, "iso_urls")
delete(config, "disk_size")
config["disk_size"] = 1
// Test just iso_urls set but with vhdx
delete(config, "iso_url")
config["iso_urls"] = []string{
"http://www.packer.io/hdd.vhdx",
"http://www.hashicorp.com/dvd.iso",
}
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
expected := []string{
"http://www.packer.io/hdd.vhdx",
"http://www.hashicorp.com/dvd.iso",
}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
// Test just iso_urls set but with vhd
delete(config, "iso_url")
config["iso_urls"] = []string{
"http://www.packer.io/hdd.vhd",
"http://www.hashicorp.com/dvd.iso",
}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
expected = []string{
"http://www.packer.io/hdd.vhd",
"http://www.hashicorp.com/dvd.iso",
}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
}
func TestBuilderPrepare_SizeIsRequiredWhenNotUsingExistingHarddrive(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "iso_url")
delete(config, "iso_urls")
delete(config, "disk_size")
config["disk_size"] = 1
// Test just iso_urls set but with vhdx
delete(config, "iso_url")
config["iso_urls"] = []string{
"http://www.packer.io/os.iso",
"http://www.hashicorp.com/dvd.iso",
}
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Errorf("should have error")
}
expected := []string{
"http://www.packer.io/os.iso",
"http://www.hashicorp.com/dvd.iso",
}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
}
func TestBuilderPrepare_MaximumOfSixtyFourAdditionalDisks(t *testing.T) {
var b Builder
config := testConfig()
disks := make([]string, 65)
for i := range disks {
disks[i] = strconv.Itoa(i)
}
config["disk_additional_size"] = disks
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Errorf("should have error")
}
}
func TestBuilderPrepare_CommConfig(t *testing.T) {
// Test Winrm
{
config := testConfig()
config["communicator"] = "winrm"
config["winrm_username"] = "username"
config["winrm_password"] = "password"
config["winrm_host"] = "1.2.3.4"
var b Builder
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Comm.WinRMUser != "username" {
t.Errorf("bad winrm_username: %s", b.config.Comm.WinRMUser)
}
if b.config.Comm.WinRMPassword != "password" {
t.Errorf("bad winrm_password: %s", b.config.Comm.WinRMPassword)
}
if host := b.config.Comm.Host(); host != "1.2.3.4" {
t.Errorf("bad host: %s", host)
}
}
// Test SSH
{
config := testConfig()
config["communicator"] = "ssh"
config["ssh_username"] = "username"
config["ssh_password"] = "password"
config["ssh_host"] = "1.2.3.4"
var b Builder
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Comm.SSHUsername != "username" {
t.Errorf("bad ssh_username: %s", b.config.Comm.SSHUsername)
}
if b.config.Comm.SSHPassword != "password" {
t.Errorf("bad ssh_password: %s", b.config.Comm.SSHPassword)
}
if host := b.config.Comm.Host(); host != "1.2.3.4" {
t.Errorf("bad host: %s", host)
}
}
}
func TestUserVariablesInBootCommand(t *testing.T) {
var b Builder
config := testConfig()
config[common.UserVariablesConfigKey] = map[string]string{"test-variable": "test"}
config["boot_command"] = []string{"blah {{user `test-variable`}} blah"}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
ui := packersdk.TestUi(t)
hook := &packersdk.MockHook{}
driver := &hypervcommon.DriverMock{}
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("http_port", 0)
state.Put("http_ip", "0.0.0.0")
state.Put("ui", ui)
state.Put("vmName", "packer-foo")
step := &hypervcommon.StepTypeBootCommand{
BootCommand: b.config.FlatBootCommand(),
SwitchName: b.config.SwitchName,
Ctx: b.config.ctx,
}
ret := step.Run(context.Background(), state)
if ret != multistep.ActionContinue {
t.Fatalf("should not have error: %#v", ret)
}
}
func TestBuilderPrepare_UseLegacyNetworkAdapter(t *testing.T) {
var b Builder
config := testConfig()
// should be allowed for default config
config["use_legacy_network_adapter"] = true
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
// should not be allowed for gen 2
config["generation"] = 2
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var HypervPluginVersion *version.PluginVersion
func init() {
HypervPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -1,397 +0,0 @@
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package vmcx
import (
"context"
"errors"
"fmt"
"os"
"strings"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/shutdowncommand"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
powershell "github.com/hashicorp/packer/builder/hyperv/common/powershell"
)
const (
DefaultRamSize = 1 * 1024 // 1GB
MinRamSize = 32 // 32MB
MaxRamSize = 1024 * 1024 // 1TB
MinNestedVirtualizationRamSize = 4 * 1024 // 4GB
LowRam = 256 // 256MB
DefaultUsername = ""
DefaultPassword = ""
)
// Builder implements packersdk.Builder and builds the actual Hyperv
// images.
type Builder struct {
config Config
runner multistep.Runner
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
commonsteps.HTTPConfig `mapstructure:",squash"`
commonsteps.ISOConfig `mapstructure:",squash"`
bootcommand.BootConfig `mapstructure:",squash"`
hypervcommon.OutputConfig `mapstructure:",squash"`
hypervcommon.SSHConfig `mapstructure:",squash"`
hypervcommon.CommonConfig `mapstructure:",squash"`
shutdowncommand.ShutdownConfig `mapstructure:",squash"`
// This is the path to a directory containing an exported virtual machine.
CloneFromVMCXPath string `mapstructure:"clone_from_vmcx_path"`
// This is the name of the virtual machine to clone from.
CloneFromVMName string `mapstructure:"clone_from_vm_name"`
// The name of a snapshot in the
// source machine to use as a starting point for the clone. If the value
// given is an empty string, the last snapshot present in the source will
// be chosen as the starting point for the new VM.
CloneFromSnapshotName string `mapstructure:"clone_from_snapshot_name" required:"false"`
// If set to true all snapshots
// present in the source machine will be copied when the machine is
// cloned. The final result of the build will be an exported virtual
// machine that contains all the snapshots of the parent.
CloneAllSnapshots bool `mapstructure:"clone_all_snapshots" required:"false"`
// If true enables differencing disks. Only
// the changes will be written to the new disk. This is especially useful if
// your source is a VHD/VHDX. This defaults to false.
DifferencingDisk bool `mapstructure:"differencing_disk" required:"false"`
// When cloning a vm to build from, we run a powershell
// Compare-VM command, which, depending on your version of Windows, may need
// the "Copy" flag to be set to true or false. Defaults to "false". Command:
CompareCopy bool `mapstructure:"copy_in_compare" required:"false"`
ctx interpolate.Context
}
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
PluginType: hypervcommon.BuilderId,
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"boot_command",
},
},
}, raws...)
if err != nil {
return nil, nil, err
}
// Accumulate any errors and warnings
var errs *packersdk.MultiError
warnings := make([]string, 0)
if b.config.RawSingleISOUrl != "" || len(b.config.ISOUrls) > 0 {
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
warnings = append(warnings, isoWarnings...)
errs = packersdk.MultiErrorAppend(errs, isoErrs...)
}
errs = packersdk.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
errs = packersdk.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
commonErrs, commonWarns := b.config.CommonConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)
errs = packersdk.MultiErrorAppend(errs, commonErrs...)
warnings = append(warnings, commonWarns...)
if b.config.Cpu < 1 {
b.config.Cpu = 1
}
if b.config.CloneFromVMName == "" {
if b.config.CloneFromVMCXPath == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vm_name must be specified if "+
"clone_from_vmcx_path is not specified."))
}
} else {
virtualMachineExists, err := powershell.DoesVirtualMachineExist(b.config.CloneFromVMName)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine to clone "+
"from exists: %s", err))
} else {
if !virtualMachineExists {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Virtual machine '%s' to clone from does not "+
"exist.", b.config.CloneFromVMName))
} else {
b.config.Generation, err = powershell.GetVirtualMachineGeneration(b.config.CloneFromVMName)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine to clone "+
"from generation: %s", err))
}
if b.config.CloneFromSnapshotName != "" {
virtualMachineSnapshotExists, err := powershell.DoesVirtualMachineSnapshotExist(
b.config.CloneFromVMName, b.config.CloneFromSnapshotName)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine "+
"snapshot to clone from exists: %s", err))
} else {
if !virtualMachineSnapshotExists {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Virtual machine snapshot '%s' on "+
"virtual machine '%s' to clone from does not exist.",
b.config.CloneFromSnapshotName, b.config.CloneFromVMName))
}
}
}
virtualMachineOn, err := powershell.IsVirtualMachineOn(b.config.CloneFromVMName)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine to "+
"clone is running: %s", err))
} else {
if virtualMachineOn {
warning := fmt.Sprintf("Cloning from a virtual machine that is running.")
warnings = hypervcommon.Appendwarns(warnings, warning)
}
}
}
}
}
if b.config.CloneFromVMCXPath == "" {
if b.config.CloneFromVMName == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vmcx_path be specified if "+
"clone_from_vm_name must is not specified."))
}
} else {
if _, err := os.Stat(b.config.CloneFromVMCXPath); os.IsNotExist(err) {
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("CloneFromVMCXPath does not exist: %s", err))
}
}
if strings.HasSuffix(strings.ToLower(b.config.CloneFromVMCXPath), ".vmcx") {
// User has provided the vmcx file itself rather than the containing
// folder.
if strings.Contains(b.config.CloneFromVMCXPath, "Virtual Machines") {
keep := strings.Split(b.config.CloneFromVMCXPath, "Virtual Machines")
b.config.CloneFromVMCXPath = keep[0]
} else {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Unable to "+
"parse the clone_from_vmcx_path to find the vm directory. "+
"Please provide the path to the folder containing the "+
"vmcx file, not the file itself. Example: instead of "+
"C:\\path\\to\\output-hyperv-iso\\Virtual Machines\\filename.vmcx"+
", provide C:\\path\\to\\output-hyperv-iso\\."))
}
}
}
// Warnings
if b.config.ShutdownCommand == "" {
warnings = hypervcommon.Appendwarns(warnings,
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
"will forcibly halt the virtual machine, which may result in data loss.")
}
if errs != nil && len(errs.Errors) > 0 {
return nil, warnings, errs
}
return nil, warnings, nil
}
// Run executes a Packer build and returns a packersdk.Artifact representing
// a Hyperv appliance.
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
// Create the driver that we'll use to communicate with Hyperv
driver, err := hypervcommon.NewHypervPS4Driver()
if err != nil {
return nil, fmt.Errorf("Failed creating Hyper-V driver: %s", err)
}
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
steps := []multistep.Step{
&hypervcommon.StepCreateBuildDir{
TempPath: b.config.TempPath,
},
&commonsteps.StepOutputDir{
Force: b.config.PackerForce,
Path: b.config.OutputDir,
},
&commonsteps.StepDownload{
Checksum: b.config.ISOChecksum,
Description: "ISO",
ResultKey: "iso_path",
Url: b.config.ISOUrls,
Extension: b.config.TargetExtension,
TargetPath: b.config.TargetPath,
},
&commonsteps.StepCreateFloppy{
Files: b.config.FloppyFiles,
Directories: b.config.FloppyConfig.FloppyDirectories,
Label: b.config.FloppyConfig.FloppyLabel,
},
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
&hypervcommon.StepCreateSwitch{
SwitchName: b.config.SwitchName,
},
&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,
EnableMacSpoofing: b.config.EnableMacSpoofing,
EnableDynamicMemory: b.config.EnableDynamicMemory,
EnableSecureBoot: b.config.EnableSecureBoot,
SecureBootTemplate: b.config.SecureBootTemplate,
EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions,
MacAddress: b.config.MacAddress,
KeepRegistered: b.config.KeepRegistered,
AdditionalDiskSize: b.config.AdditionalDiskSize,
DiskBlockSize: b.config.DiskBlockSize,
},
&hypervcommon.StepEnableIntegrationService{},
&hypervcommon.StepMountDvdDrive{
Generation: b.config.Generation,
FirstBootDevice: b.config.FirstBootDevice,
},
&hypervcommon.StepMountFloppydrive{
Generation: b.config.Generation,
},
&hypervcommon.StepMountGuestAdditions{
GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsPath: b.config.GuestAdditionsPath,
Generation: b.config.Generation,
},
&commonsteps.StepCreateCD{
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&hypervcommon.StepMountSecondaryDvdImages{
IsoPaths: b.config.SecondaryDvdImages,
Generation: b.config.Generation,
},
&hypervcommon.StepConfigureVlan{
VlanId: b.config.VlanId,
SwitchVlanId: b.config.SwitchVlanId,
},
&hypervcommon.StepSetBootOrder{
BootOrder: b.config.BootOrder,
},
&hypervcommon.StepSetFirstBootDevice{
Generation: b.config.Generation,
FirstBootDevice: b.config.FirstBootDevice,
},
&hypervcommon.StepRun{
Headless: b.config.Headless,
SwitchName: b.config.SwitchName,
},
&hypervcommon.StepTypeBootCommand{
BootCommand: b.config.FlatBootCommand(),
BootWait: b.config.BootWait,
SwitchName: b.config.SwitchName,
Ctx: b.config.ctx,
GroupInterval: b.config.BootConfig.BootGroupInterval,
},
// configure the communicator ssh, winrm
&communicator.StepConnect{
Config: &b.config.SSHConfig.Comm,
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()),
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
},
// provision requires communicator to be setup
&commonsteps.StepProvision{},
// Remove ephemeral SSH keys, if using
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.SSHConfig.Comm,
},
&hypervcommon.StepShutdown{
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
},
// wait for the vm to be powered off
&hypervcommon.StepWaitForPowerOff{},
// remove the secondary dvd images
// after we power down
&hypervcommon.StepUnmountSecondaryDvdImages{},
&hypervcommon.StepUnmountGuestAdditions{},
&hypervcommon.StepUnmountDvdDrive{},
&hypervcommon.StepUnmountFloppyDrive{
Generation: b.config.Generation,
},
&hypervcommon.StepCompactDisk{
SkipCompaction: b.config.SkipCompaction,
},
&hypervcommon.StepExportVm{
OutputDir: b.config.OutputDir,
SkipExport: b.config.SkipExport,
},
&hypervcommon.StepCollateArtifacts{
OutputDir: b.config.OutputDir,
SkipExport: b.config.SkipExport,
},
}
// the clean up actions for each step will be executed reverse order
// Run the steps.
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)
// Report any errors.
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If we were interrupted or cancelled, then just exit.
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("Build was cancelled.")
}
if _, ok := state.GetOk(multistep.StateHalted); ok {
return nil, errors.New("Build was halted.")
}
generatedData := map[string]interface{}{"generated_data": state.Get("generated_data")}
return hypervcommon.NewArtifact(b.config.OutputDir, generatedData)
}
// Cancel.

View File

@ -1,251 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package vmcx
import (
"github.com/hashicorp/hcl/v2/hcldec"
"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"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
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"`
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"`
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.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false},
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
"disk_block_size": &hcldec.AttrSpec{Name: "disk_block_size", Type: cty.Number, Required: false},
"memory": &hcldec.AttrSpec{Name: "memory", Type: cty.Number, Required: false},
"secondary_iso_images": &hcldec.AttrSpec{Name: "secondary_iso_images", Type: cty.List(cty.String), Required: false},
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
"guest_additions_mode": &hcldec.AttrSpec{Name: "guest_additions_mode", Type: cty.String, Required: false},
"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_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},
"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},
"enable_dynamic_memory": &hcldec.AttrSpec{Name: "enable_dynamic_memory", Type: cty.Bool, Required: false},
"enable_secure_boot": &hcldec.AttrSpec{Name: "enable_secure_boot", Type: cty.Bool, Required: false},
"secure_boot_template": &hcldec.AttrSpec{Name: "secure_boot_template", Type: cty.String, Required: false},
"enable_virtualization_extensions": &hcldec.AttrSpec{Name: "enable_virtualization_extensions", Type: cty.Bool, Required: false},
"temp_path": &hcldec.AttrSpec{Name: "temp_path", Type: cty.String, Required: false},
"configuration_version": &hcldec.AttrSpec{Name: "configuration_version", Type: cty.String, Required: false},
"keep_registered": &hcldec.AttrSpec{Name: "keep_registered", Type: cty.Bool, Required: false},
"skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false},
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
"first_boot_device": &hcldec.AttrSpec{Name: "first_boot_device", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.List(cty.String), Required: false},
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"clone_from_vmcx_path": &hcldec.AttrSpec{Name: "clone_from_vmcx_path", Type: cty.String, Required: false},
"clone_from_vm_name": &hcldec.AttrSpec{Name: "clone_from_vm_name", Type: cty.String, Required: false},
"clone_from_snapshot_name": &hcldec.AttrSpec{Name: "clone_from_snapshot_name", Type: cty.String, Required: false},
"clone_all_snapshots": &hcldec.AttrSpec{Name: "clone_all_snapshots", Type: cty.Bool, Required: false},
"differencing_disk": &hcldec.AttrSpec{Name: "differencing_disk", Type: cty.Bool, Required: false},
"copy_in_compare": &hcldec.AttrSpec{Name: "copy_in_compare", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -1,521 +0,0 @@
package vmcx
import (
"context"
"fmt"
"reflect"
"testing"
"io/ioutil"
"os"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
)
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_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{}
raw = &Builder{}
if _, ok := raw.(packersdk.Builder); !ok {
t.Error("Builder must implement builder.")
}
}
func TestBuilderPrepare_Defaults(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.VMName != "packer-foo" {
t.Errorf("bad vm name: %s", b.config.VMName)
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
// Add a random key
config["i_should_not_be_valid"] = true
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_CloneFromExistingMachineOrImportFromExportedMachineSettingsRequired(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "clone_from_vmcx_path")
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_ExportedMachinePathDoesNotExist(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
//Delete the folder immediately
os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_ExportedMachinePathExists(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
//Only delete afterwards
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func disabled_TestBuilderPrepare_CloneFromVmSettingUsedSoNoCloneFromVmcxPathRequired(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "clone_from_vmcx_path")
config["clone_from_vm_name"] = "test_machine_name_that_does_not_exist"
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
} else {
errorMessage := err.Error()
if errorMessage != "1 error(s) occurred:\n\n* Virtual machine 'test_machine_name_that_does_not_exist' "+
"to clone from does not exist." {
t.Fatalf("should not have error: %s", err)
}
}
}
func TestBuilderPrepare_ISOChecksum(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
// Test bad
config["iso_checksum"] = ""
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test good
config["iso_checksum"] = "0B0F137F17AC10944716020B018F8126"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
config["iso_checksum"] = "0B0F137F17AC10944716020B018F8126"
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test good
config["iso_checksum"] = "mD5:0B0F137F17AC10944716020B018F8126"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test none
config["iso_checksum"] = "none"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) == 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ISOUrl(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
delete(config, "iso_url")
delete(config, "iso_urls")
// Test both empty (should be allowed, as we cloning a vm so we probably don't need an ISO file)
config["iso_url"] = ""
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatal("should not have an error")
}
// Test iso_url set
config["iso_url"] = "http://www.packer.io"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
expected := []string{"http://www.packer.io"}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
// Test both set
config["iso_url"] = "http://www.packer.io"
config["iso_urls"] = []string{"http://www.packer.io"}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test just iso_urls set
delete(config, "iso_url")
config["iso_urls"] = []string{
"http://www.packer.io",
"http://www.hashicorp.com",
}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Errorf("should not have error: %s", err)
}
expected = []string{
"http://www.packer.io",
"http://www.hashicorp.com",
}
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
t.Fatalf("bad: %#v", b.config.ISOUrls)
}
}
func TestBuilderPrepare_FloppyFiles(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
delete(config, "floppy_files")
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
if len(b.config.FloppyFiles) != 0 {
t.Fatalf("bad: %#v", b.config.FloppyFiles)
}
floppies_path := "../../test-fixtures/floppies"
config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
expected := []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
t.Fatalf("bad: %#v", b.config.FloppyFiles)
}
}
func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"}
b = Builder{}
_, _, errs := b.Prepare(config)
if errs == nil {
t.Fatalf("Nonexistent floppies should trigger multierror")
}
if len(errs.(*packersdk.MultiError).Errors) != 2 {
t.Fatalf("Multierror should work and report 2 errors")
}
}
func TestBuilderPrepare_CommConfig(t *testing.T) {
// Test Winrm
{
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
config["communicator"] = "winrm"
config["winrm_username"] = "username"
config["winrm_password"] = "password"
config["winrm_host"] = "1.2.3.4"
var b Builder
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Comm.WinRMUser != "username" {
t.Errorf("bad winrm_username: %s", b.config.Comm.WinRMUser)
}
if b.config.Comm.WinRMPassword != "password" {
t.Errorf("bad winrm_password: %s", b.config.Comm.WinRMPassword)
}
if host := b.config.Comm.Host(); host != "1.2.3.4" {
t.Errorf("bad host: %s", host)
}
}
// Test SSH
{
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
config["communicator"] = "ssh"
config["ssh_username"] = "username"
config["ssh_password"] = "password"
config["ssh_host"] = "1.2.3.4"
var b Builder
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Comm.SSHUsername != "username" {
t.Errorf("bad ssh_username: %s", b.config.Comm.SSHUsername)
}
if b.config.Comm.SSHPassword != "password" {
t.Errorf("bad ssh_password: %s", b.config.Comm.SSHPassword)
}
if host := b.config.Comm.Host(); host != "1.2.3.4" {
t.Errorf("bad host: %s", host)
}
}
}
func TestUserVariablesInBootCommand(t *testing.T) {
var b Builder
config := testConfig()
//Create vmcx folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmcx_path"] = td
config[common.UserVariablesConfigKey] = map[string]string{"test-variable": "test"}
config["boot_command"] = []string{"blah {{user `test-variable`}} blah"}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
ui := packersdk.TestUi(t)
hook := &packersdk.MockHook{}
driver := &hypervcommon.DriverMock{}
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("http_port", 0)
state.Put("http_ip", "0.0.0.0")
state.Put("ui", ui)
state.Put("vmName", "packer-foo")
step := &hypervcommon.StepTypeBootCommand{
BootCommand: b.config.FlatBootCommand(),
SwitchName: b.config.SwitchName,
Ctx: b.config.ctx,
}
ret := step.Run(context.Background(), state)
if ret != multistep.ActionContinue {
t.Fatalf("should not have error: %#v", ret)
}
}

View File

@ -19,8 +19,6 @@ import (
digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean"
filebuilder "github.com/hashicorp/packer/builder/file"
hcloudbuilder "github.com/hashicorp/packer/builder/hcloud"
hypervisobuilder "github.com/hashicorp/packer/builder/hyperv/iso"
hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx"
jdcloudbuilder "github.com/hashicorp/packer/builder/jdcloud"
linodebuilder "github.com/hashicorp/packer/builder/linode"
lxcbuilder "github.com/hashicorp/packer/builder/lxc"
@ -71,8 +69,6 @@ var Builders = map[string]packersdk.Builder{
"digitalocean": new(digitaloceanbuilder.Builder),
"file": new(filebuilder.Builder),
"hcloud": new(hcloudbuilder.Builder),
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"jdcloud": new(jdcloudbuilder.Builder),
"linode": new(linodebuilder.Builder),
"lxc": new(lxcbuilder.Builder),

View File

@ -31,6 +31,8 @@ import (
googlecomputeexportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-export"
googlecomputeimportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-import"
hyperonebuilder "github.com/hashicorp/packer-plugin-hyperone/builder/hyperone"
hypervisobuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/iso"
hypervvmcxbuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/vmcx"
ncloudbuilder "github.com/hashicorp/packer-plugin-ncloud/builder/ncloud"
openstackbuilder "github.com/hashicorp/packer-plugin-openstack/builder/openstack"
oscbsubuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsu"
@ -75,6 +77,9 @@ var VendoredBuilders = map[string]packersdk.Builder{
"cloudstack": new(cloudstackbuilder.Builder),
"docker": new(dockerbuilder.Builder),
"googlecompute": new(googlecomputebuilder.Builder),
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
"ncloud": new(ncloudbuilder.Builder),
"openstack": new(openstackbuilder.Builder),
"proxmox": new(proxmoxiso.Builder),
@ -95,7 +100,6 @@ var VendoredBuilders = map[string]packersdk.Builder{
"osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder),
"osc-bsuvolume": new(oscbsuvolumebuilder.Builder),
"osc-chroot": new(oscchrootbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
}
// VendoredProvisioners are provisioner components that were once bundled with the

9
go.mod
View File

@ -24,7 +24,7 @@ require (
github.com/gobwas/glob v0.2.3
github.com/google/go-cmp v0.5.5
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69
github.com/google/uuid v1.1.2
github.com/google/uuid v1.2.0
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
github.com/hashicorp/errwrap v1.0.0
@ -36,7 +36,7 @@ require (
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79
github.com/hashicorp/go-uuid v1.0.2
github.com/hashicorp/go-version v1.3.0
github.com/hashicorp/hcl/v2 v2.9.1
github.com/hashicorp/hcl/v2 v2.10.0
github.com/hashicorp/packer-plugin-alicloud v0.0.2
github.com/hashicorp/packer-plugin-amazon v0.0.1
github.com/hashicorp/packer-plugin-ansible v0.0.2
@ -45,8 +45,9 @@ require (
github.com/hashicorp/packer-plugin-docker v0.0.7
github.com/hashicorp/packer-plugin-googlecompute v0.0.1
github.com/hashicorp/packer-plugin-hyperone v0.0.1
github.com/hashicorp/packer-plugin-hyperv v0.0.1
github.com/hashicorp/packer-plugin-ncloud v0.0.2
github.com/hashicorp/packer-plugin-openstack v0.0.1
github.com/hashicorp/packer-plugin-openstack v0.0.2
github.com/hashicorp/packer-plugin-outscale v0.0.1
github.com/hashicorp/packer-plugin-parallels v0.0.1
github.com/hashicorp/packer-plugin-proxmox v0.0.2
@ -83,7 +84,7 @@ require (
github.com/ulikunitz/xz v0.5.6
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c
github.com/zclconf/go-cty v1.8.1
github.com/zclconf/go-cty v1.8.2
github.com/zclconf/go-cty-yaml v1.0.1
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc
golang.org/x/mod v0.4.1

15
go.sum
View File

@ -353,8 +353,9 @@ github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20170306145142-6a5e28554805/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
@ -443,8 +444,9 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
github.com/hashicorp/hcl/v2 v2.8.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
github.com/hashicorp/hcl/v2 v2.9.1 h1:eOy4gREY0/ZQHNItlfuEZqtcQbXIxzojlP301hDpnac=
github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg=
github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
@ -478,10 +480,12 @@ github.com/hashicorp/packer-plugin-googlecompute v0.0.1 h1:Shjio88MraB+ocj0VI5+M
github.com/hashicorp/packer-plugin-googlecompute v0.0.1/go.mod h1:MfV898IrEMpKH6wVnvOI5Tkhxm2snf3QxwVqV4k3bNI=
github.com/hashicorp/packer-plugin-hyperone v0.0.1 h1:Owp1B5cI0VgFgR3pCyeeQdyKPTWls36mVedv+WxZMOM=
github.com/hashicorp/packer-plugin-hyperone v0.0.1/go.mod h1:9DglrxEBIig85Hr8r11YE+uMn3G0u+pt0AZHVP+wnAY=
github.com/hashicorp/packer-plugin-hyperv v0.0.1 h1:ZdsJw4X+4zSgRYPzVQbJrx8Az73AkneSWLnmfpojl0k=
github.com/hashicorp/packer-plugin-hyperv v0.0.1/go.mod h1:sB9mEZCfaXVjTD6pS+Tt0xMtUD1Ocnl5mZ3i/PG6eB0=
github.com/hashicorp/packer-plugin-ncloud v0.0.2 h1:MGvGkOVfzeosqOSs5dteghLwv9VRcRxTuLoLX1ssUag=
github.com/hashicorp/packer-plugin-ncloud v0.0.2/go.mod h1:Hud2R1pkky96TQy3TPTTrr9Kej4b/4dqC/v+uEE0VDY=
github.com/hashicorp/packer-plugin-openstack v0.0.1 h1:FUaNjKguAipPZZXQ4UiJK6c5+2nS89CRxJHjAsfVyIQ=
github.com/hashicorp/packer-plugin-openstack v0.0.1/go.mod h1:L1OTbN24H+izce3v5IyQLdjDdrUigxPWgAQOK920h9A=
github.com/hashicorp/packer-plugin-openstack v0.0.2 h1:wGNE8es3Bn9auuIoX+gqT9chXzYY9GlM55eSpM4uwtU=
github.com/hashicorp/packer-plugin-openstack v0.0.2/go.mod h1:rHAdd4+JmI+1z98Zx+lVOehgzLZT1Rjo2YgtS0NNvwM=
github.com/hashicorp/packer-plugin-outscale v0.0.1 h1:BrL8hKypNYrvP3NR+d+xX03SZKB08yTgXPRnH9piUI8=
github.com/hashicorp/packer-plugin-outscale v0.0.1/go.mod h1:6jEWfJO7TgAbaL3e+St1bN5PoIC/MmDIsYqNUzAHF1w=
github.com/hashicorp/packer-plugin-parallels v0.0.1 h1:fcaaiGWdU1+X4IGadXdUhJ2si1ZA3apXS9tMNJXln2A=
@ -788,8 +792,9 @@ github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
github.com/zclconf/go-cty v1.7.0/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.8.1 h1:SI0LqNeNxAgv2WWqWJMlG2/Ad/6aYJ7IVYYMigmfkuI=
github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.8.2 h1:u+xZfBKgpycDnTNjPhGiTEYZS5qS/Sb5MqSfm7vzcjg=
github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=

View File

@ -1,22 +0,0 @@
---
description: |
The Hyper-V Packer builder is able to create Hyper-V virtual machines and
export them.
page_title: Hyper-V - Builders
---
# HyperV Builder
The HyperV Packer builder is able to create
[Hyper-V](https://www.microsoft.com/en-us/server-cloud/solutions/virtualization.aspx)
virtual machines and export them.
- [hyperv-iso](/docs/builders/hyperv-iso) - Starts from an ISO file,
creates a brand new Hyper-V VM, installs an OS, provisions software within
the OS, then exports that machine to create an image. This is best for
people who want to start from scratch.
- [hyperv-vmcx](/docs/builders/hyperv-vmcx) - Clones an an existing
virtual machine, provisions software within the OS, then exports that
machine to create an image. This is best for people who have existing base
images and want to customize them.

View File

@ -1,860 +0,0 @@
---
modeline: |
vim: set ft=pandoc:
description: |
The Hyper-V Packer builder is able to create Hyper-V virtual machines and
export them.
page_title: Hyper-V ISO - Builders
---
# Hyper-V Builder (from an ISO)
Type: `hyperv-iso`
Artifact BuilderId: `MSOpenTech.hyperv`
The Hyper-V Packer builder is able to create
[Hyper-V](https://www.microsoft.com/en-us/server-cloud/solutions/virtualization.aspx)
virtual machines and export them, starting from an ISO image.
The builder builds a virtual machine by creating a new virtual machine from
scratch. Typically, the VM is booted, an OS is installed, and software is
provisioned within the OS. Finally the VM is shut down. The result of the
Hyper-V builder is a directory containing all the files necessary to run
the virtual machine portably.
## Basic Example
Here is a basic example. This example is not functional. It will start the OS
installer but then fail because we don't provide the preseed file for Ubuntu
to self-install. Still, the example serves to show the basic configuration:
```json
{
"type": "hyperv-iso",
"iso_url": "http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso",
"iso_checksum": "md5:769474248a3897f4865817446f9a4a53",
"ssh_username": "packer",
"ssh_password": "packer",
"shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
}
```
By default Packer will perform a hard power off of a virtual machine.
However, when a machine is powered off this way, it is possible that
changes made to the VMs file system may not be fully synced, possibly
leading to corruption of files or lost changes. As such, it is important to
add a `shutdown_command`. This tells Packer how to safely shutdown and
power off the VM.
## ISO Configuration Reference
@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig.mdx'
### Required:
@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig-required.mdx'
### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig-not-required.mdx'
## Configuration Reference
There are many configuration options available for the Hyper-V builder. They
are organized below into two categories: required and optional. Within each
category, the available options are alphabetized and described.
In addition to the options listed here, a
[communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this
builder.
### Optional:
- `output_directory` (string) - This setting specifies the directory that
artifacts from the build, such as the virtual machine files and disks,
will be output to. The path to the directory may be relative or
absolute. If relative, the path is relative to the working directory
`packer` is executed from. This directory must not exist or, if
created, must be empty prior to running the builder. By default this is
"output-BUILDNAME" where "BUILDNAME" is the name of the build.
@include 'builder/hyperv/iso/Config-not-required.mdx'
@include 'builder/hyperv/common/CommonConfig-not-required.mdx'
## Http directory configuration reference
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig.mdx'
### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig-not-required.mdx'
## Shutdown configuration reference
### Optional:
@include 'packer-plugin-sdk/shutdowncommand/ShutdownConfig-not-required.mdx'
## Floppy configuration reference
@include 'packer-plugin-sdk/multistep/commonsteps/FloppyConfig.mdx'
### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/FloppyConfig-not-required.mdx'
### CD configuration reference
@include 'packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx'
## Communicator configuration reference
### Optional common fields:
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
### Optional SSH fields:
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
### Optional WinRM fields:
@include 'packer-plugin-sdk/communicator/WinRM-not-required.mdx'
## Boot Configuration Reference
@include 'packer-plugin-sdk/bootcommand/BootConfig.mdx'
### Optional:
@include 'packer-plugin-sdk/bootcommand/BootConfig-not-required.mdx'
## Integration Services
Packer will automatically attach the integration services ISO as a DVD drive
for the version of Hyper-V that is running.
## Generation 1 vs Generation 2
Floppy drives are no longer supported by generation 2 machines. This requires
you to take another approach when dealing with preseed or answer files. Two
possible options are using your own virtual DVD drives, the cd_files option,
or using Packer's built in web server.
When dealing with Windows you need to enable UEFI drives for generation 2
virtual machines.
## Creating an ISO From a Directory
Programs like mkisofs can be used to create an ISO from a directory. There is
a [windows version of
mkisofs](http://opensourcepack.blogspot.co.uk/p/cdrtools.html) available.
Below is a working PowerShell script that can be used to create a Windows
answer ISO:
```powershell
$isoFolder = "answer-iso"
if (test-path $isoFolder){
remove-item $isoFolder -Force -Recurse
}
if (test-path windows\windows-2012R2-serverdatacenter-amd64\answer.iso){
remove-item windows\windows-2012R2-serverdatacenter-amd64\answer.iso -Force
}
mkdir $isoFolder
copy windows\windows-2012R2-serverdatacenter-amd64\Autounattend.xml $isoFolder\
copy windows\windows-2012R2-serverdatacenter-amd64\sysprep-unattend.xml $isoFolder\
copy windows\common\set-power-config.ps1 $isoFolder\
copy windows\common\microsoft-updates.ps1 $isoFolder\
copy windows\common\win-updates.ps1 $isoFolder\
copy windows\common\run-sysprep.ps1 $isoFolder\
copy windows\common\run-sysprep.cmd $isoFolder\
$textFile = "$isoFolder\Autounattend.xml"
$c = Get-Content -Encoding UTF8 $textFile
# Enable UEFI and disable Non EUFI
$c | % { $_ -replace '<!-- Start Non UEFI -->','<!-- Start Non UEFI' } | % { $_ -replace '<!-- Finish Non UEFI -->','Finish Non UEFI -->' } | % { $_ -replace '<!-- Start UEFI compatible','<!-- Start UEFI compatible -->' } | % { $_ -replace 'Finish UEFI compatible -->','<!-- Finish UEFI compatible -->' } | sc -Path $textFile
& .\mkisofs.exe -r -iso-level 4 -UDF -o windows\windows-2012R2-serverdatacenter-amd64\answer.iso $isoFolder
if (test-path $isoFolder){
remove-item $isoFolder -Force -Recurse
}
```
## Example For Windows Server 2012 R2 Generation 2
Packer config:
```json
{
"builders": [
{
"vm_name": "windows2012r2",
"type": "hyperv-iso",
"disk_size": 61440,
"floppy_files": [],
"secondary_iso_images": [
"./windows/windows-2012R2-serverdatacenter-amd64/answer.iso"
],
"http_directory": "./windows/common/http/",
"boot_wait": "0s",
"boot_command": ["a<wait>a<wait>a"],
"iso_url": "http://download.microsoft.com/download/6/2/A/62A76ABB-9990-4EFC-A4FE-C7D698DAEB96/9600.16384.WINBLUE_RTM.130821-1623_X64FRE_SERVER_EVAL_EN-US-IRM_SSS_X64FREE_EN-US_DV5.ISO",
"iso_checksum": "md5:458ff91f8abc21b75cb544744bf92e6a",
"communicator": "winrm",
"winrm_username": "vagrant",
"winrm_password": "vagrant",
"winrm_timeout": "4h",
"shutdown_command": "f:\\run-sysprep.cmd",
"memory": 4096,
"cpus": 4,
"generation": 2,
"switch_name": "LAN",
"enable_secure_boot": true
}
],
"provisioners": [
{
"type": "powershell",
"elevated_user": "vagrant",
"elevated_password": "vagrant",
"scripts": [
"./windows/common/install-7zip.ps1",
"./windows/common/install-chef.ps1",
"./windows/common/compile-dotnet-assemblies.ps1",
"./windows/common/cleanup.ps1",
"./windows/common/ultradefrag.ps1",
"./windows/common/sdelete.ps1"
]
}
],
"post-processors": [
{
"type": "vagrant",
"keep_input_artifact": false,
"output": "{{.Provider}}_windows-2012r2_chef.box"
}
]
}
```
autounattend.xml:
```xml
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Start Non UEFI -->
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Type>Primary</Type>
<Order>1</Order>
<Size>350</Size>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>2</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Active>true</Active>
<Format>NTFS</Format>
<Label>boot</Label>
<Order>1</Order>
<PartitionID>1</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Format>NTFS</Format>
<Label>Windows 2012 R2</Label>
<Letter>C</Letter>
<Order>2</Order>
<PartitionID>2</PartitionID>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME </Key>
<Value>Windows Server 2012 R2 SERVERSTANDARD</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>2</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
<!-- Finish Non UEFI -->
<!-- Start UEFI compatible
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Order>1</Order>
<Size>300</Size>
<Type>Primary</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>2</Order>
<Size>100</Size>
<Type>EFI</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>3</Order>
<Size>128</Size>
<Type>MSR</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>4</Order>
<Extend>true</Extend>
<Type>Primary</Type>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>WINRE</Label>
<Format>NTFS</Format>
<TypeID>de94bba4-06d1-4d40-a16a-bfd50179d6ac</TypeID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>2</PartitionID>
<Label>System</Label>
<Format>FAT32</Format>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>3</Order>
<PartitionID>3</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>4</Order>
<PartitionID>4</PartitionID>
<Label>Windows</Label>
<Format>NTFS</Format>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
<WillShowUI>OnError</WillShowUI>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME </Key>
<Value>Windows Server 2012 R2 SERVERSTANDARD</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>4</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
Finish UEFI compatible -->
<UserData>
<!-- Product Key from http://technet.microsoft.com/en-us/library/jj612867.aspx -->
<ProductKey>
<!-- Do not uncomment the Key element if you are using trial ISOs -->
<!-- You must uncomment the Key element (and optionally insert your own key) if you are using retail or volume license ISOs -->
<!--<Key>D2N9P-3P6X9-2R39C-7RTCD-MDVJX</Key>-->
<WillShowUI>OnError</WillShowUI>
</ProductKey>
<AcceptEula>true</AcceptEula>
<FullName>Vagrant</FullName>
<Organization>Vagrant</Organization>
</UserData>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OEMInformation>
<HelpCustomized>false</HelpCustomized>
</OEMInformation>
<ComputerName>vagrant-2012r2</ComputerName>
<TimeZone>Coordinated Universal Time</TimeZone>
<RegisteredOwner />
</component>
<component name="Microsoft-Windows-ServerManager-SvrMgrNc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon>
</component>
<component name="Microsoft-Windows-IE-ESC" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<IEHardenAdmin>false</IEHardenAdmin>
<IEHardenUser>false</IEHardenUser>
</component>
<component name="Microsoft-Windows-OutOfBoxExperience" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenInitialConfigurationTasksAtLogon>true</DoNotOpenInitialConfigurationTasksAtLogon>
</component>
<component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipAutoActivation>true</SkipAutoActivation>
</component>
</settings>
<settings pass="oobeSystem">
<!-- Start Setup cache proxy during installation
<component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<POLICYProxySettingsPerUser>0</POLICYProxySettingsPerUser>
<HKLMProxyEnable>true</HKLMProxyEnable>
<HKLMProxyServer>cache-proxy:3142</HKLMProxyServer>
</component>
Finish Setup cache proxy during installation -->
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Password>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</Password>
<Enabled>true</Enabled>
<Username>vagrant</Username>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
<Description>Set Execution Policy 64 Bit</Description>
<Order>1</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
<Description>Set Execution Policy 32 Bit</Description>
<Order>2</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm quickconfig -q</CommandLine>
<Description>winrm quickconfig -q</Description>
<Order>3</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm quickconfig -transport:http</CommandLine>
<Description>winrm quickconfig -transport:http</Description>
<Order>4</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"}</CommandLine>
<Description>Win RM MaxTimeoutms</Description>
<Order>5</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB="300"}</CommandLine>
<Description>Win RM MaxMemoryPerShellMB</Description>
<Order>6</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/service @{AllowUnencrypted="true"}</CommandLine>
<Description>Win RM AllowUnencrypted</Description>
<Order>7</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/service/auth @{Basic="true"}</CommandLine>
<Description>Win RM auth Basic</Description>
<Order>8</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/client/auth @{Basic="true"}</CommandLine>
<Description>Win RM client auth Basic</Description>
<Order>9</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"} </CommandLine>
<Description>Win RM listener Address/Port</Description>
<Order>10</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes </CommandLine>
<Description>Win RM adv firewall enable</Description>
<Order>11</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall add rule name="WinRM 5985" protocol=TCP dir=in localport=5985 action=allow</CommandLine>
<Description>Win RM port open</Description>
<Order>12</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow</CommandLine>
<Description>Win RM port open</Description>
<Order>13</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c net stop winrm </CommandLine>
<Description>Stop Win RM Service </Description>
<Order>14</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c sc config winrm start= disabled</CommandLine>
<Description>Win RM Autostart</Description>
<Order>15</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v HideFileExt /t REG_DWORD /d 0 /f</CommandLine>
<Order>16</Order>
<Description>Show file extensions in Explorer</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f</CommandLine>
<Order>17</Order>
<Description>Enable QuickEdit mode</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v Start_ShowRun /t REG_DWORD /d 1 /f</CommandLine>
<Order>18</Order>
<Description>Show Run command in Start Menu</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v StartMenuAdminTools /t REG_DWORD /d 1 /f</CommandLine>
<Order>19</Order>
<Description>Show Administrative Tools in Start Menu</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateFileSizePercent /t REG_DWORD /d 0 /f</CommandLine>
<Order>20</Order>
<Description>Zero Hibernation File</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateEnabled /t REG_DWORD /d 0 /f</CommandLine>
<Order>21</Order>
<Description>Disable Hibernation Mode</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c wmic useraccount where "name='vagrant'" set PasswordExpires=FALSE</CommandLine>
<Order>22</Order>
<Description>Disable password expiration for vagrant user</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/winrs @{MaxShellsPerUser="30"}</CommandLine>
<Description>Win RM MaxShellsPerUser</Description>
<Order>23</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/winrs @{MaxProcessesPerShell="25"}</CommandLine>
<Description>Win RM MaxProcessesPerShell</Description>
<Order>24</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD "HKLM\System\CurrentControlSet\Services\Netlogon\Parameters" /v DisablePasswordChange /t REG_DWORD /d 1 /f</CommandLine>
<Description>Turn off computer password</Description>
<Order>25</Order>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow</CommandLine>
<Description>ICMP open for ping</Description>
<Order>26</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<!-- WITH WINDOWS UPDATES -->
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c IF EXIST a:\set-power-config.ps1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\set-power-config.ps1) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File f:\set-power-config.ps1)</CommandLine>
<Order>97</Order>
<Description>Turn off all power saving and timeouts</Description>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c IF EXIST a:\microsoft-updates.ps1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\microsoft-updates.ps1) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File f:\microsoft-updates.ps1)</CommandLine>
<Order>98</Order>
<Description>Enable Microsoft Updates</Description>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c IF EXIST a:\win-updates.ps1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\win-updates.ps1) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File f:\win-updates.ps1)</CommandLine>
<Description>Install Windows Updates</Description>
<Order>100</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<!-- END WITH WINDOWS UPDATES -->
</FirstLogonCommands>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>1</ProtectYourPC>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</Password>
<Group>administrators</Group>
<DisplayName>Vagrant</DisplayName>
<Name>vagrant</Name>
<Description>Vagrant User</Description>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<RegisteredOwner />
<TimeZone>Coordinated Universal Time</TimeZone>
</component>
</settings>
<settings pass="offlineServicing">
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<EnableLUA>false</EnableLUA>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/projects/baseboxes/9600.16384.winblue_rtm.130821-1623_x64fre_server_eval_en-us-irm_sss_x64free_en-us_dv5_slipstream/sources/install.wim#Windows Server 2012 R2 SERVERDATACENTER" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
```
sysprep-unattend.xml:
```xml
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="generalize">
<component language="neutral" name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipRearm>1</SkipRearm>
</component>
</settings>
<settings pass="oobeSystem">
<!-- Setup proxy after sysprep
<component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<POLICYProxySettingsPerUser>1</POLICYProxySettingsPerUser>
<HKLMProxyEnable>false</HKLMProxyEnable>
<HKLMProxyServer>cache-proxy:3142</HKLMProxyServer>
</component>
Finish proxy after sysprep -->
<component language="neutral" name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>0809:00000809</InputLocale>
<SystemLocale>en-GB</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-GB</UserLocale>
</component>
<component language="neutral" name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>1</ProtectYourPC>
<SkipUserOOBE>true</SkipUserOOBE>
<SkipMachineOOBE>true</SkipMachineOOBE>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</Password>
<Group>administrators</Group>
<DisplayName>Vagrant</DisplayName>
<Name>vagrant</Name>
<Description>Vagrant User</Description>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<DisableAutoDaylightTimeSet>true</DisableAutoDaylightTimeSet>
<TimeZone>Coordinated Universal Time</TimeZone>
<VisualEffects>
<SystemDefaultBackgroundColor>2</SystemDefaultBackgroundColor>
</VisualEffects>
</component>
</settings>
</unattend>
```
-> **Warning:** Please note that if you're setting up WinRM for provisioning, you'll probably want to turn it off or restrict its permissions as part of a shutdown script at the end of Packer's provisioning process. For more details on the why/how, check out this useful blog post and the associated code:
https://cloudywindows.io/post/winrm-for-provisioning-close-the-door-on-the-way-out-eh/
## Example For Ubuntu Vivid Generation 2
If you are running Windows under virtualization, you may need to create a
virtual switch with an `External` connection type.
### Packer config:
```json
{
"variables": {
"vm_name": "ubuntu-xenial",
"cpus": "2",
"memory": "1024",
"disk_size": "21440",
"iso_url": "http://releases.ubuntu.com/16.04/ubuntu-16.04.6-server-amd64.iso",
"iso_checksum": "sha1:056b7c15efc15bbbf40bf1a9ff1a3531fcbf70a2"
},
"builders": [
{
"vm_name": "{{user `vm_name`}}",
"type": "hyperv-iso",
"disk_size": "{{user `disk_size`}}",
"guest_additions_mode": "disable",
"iso_url": "{{user `iso_url`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"communicator": "ssh",
"ssh_username": "packer",
"ssh_password": "packer",
"ssh_timeout": "4h",
"http_directory": "./",
"boot_wait": "5s",
"boot_command": [
"<esc><wait10><esc><esc><enter><wait>",
"set gfxpayload=1024x768<enter>",
"linux /install/vmlinuz ",
"preseed/url=http://{{.HTTPIP}}:{{.HTTPPort}}/hyperv-taliesins.cfg ",
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
"hostname={{.Name}} ",
"fb=false debconf/frontend=noninteractive ",
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
"keyboard-configuration/variant=USA console-setup/ask_detect=false <enter>",
"initrd /install/initrd.gz<enter>",
"boot<enter>"
],
"shutdown_command": "echo 'packer' | sudo -S -E shutdown -P now",
"memory": "{{user `memory`}}",
"cpus": "{{user `cpus`}}",
"generation": 2,
"enable_secure_boot": false
}
]
}
```
### preseed.cfg:
```text
## Options to set on the command line
d-i debian-installer/locale string en_US.utf8
d-i console-setup/ask_detect boolean false
d-i console-setup/layout string us
d-i netcfg/get_hostname string nl-ams-basebox3
d-i netcfg/get_domain string unassigned-domain
d-i time/zone string UTC
d-i clock-setup/utc-auto boolean true
d-i clock-setup/utc boolean true
d-i kbd-chooser/method select American English
d-i netcfg/wireless_wep string
d-i base-installer/kernel/override-image string linux-server
d-i debconf debconf/frontend select Noninteractive
d-i pkgsel/install-language-support boolean false
tasksel tasksel/first multiselect standard, ubuntu-server
## Partitioning
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
# Write the changes to disks and configure LVM?
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-partitioning/no_bootable_gpt_biosgrub boolean false
d-i partman-partitioning/no_bootable_gpt_efi boolean false
d-i partman-efi/non_efi_system boolean true
# Default user
d-i passwd/user-fullname string packer
d-i passwd/username string packer
d-i passwd/user-password password packer
d-i passwd/user-password-again password packer
d-i user-setup/encrypt-home boolean false
d-i user-setup/allow-password-weak boolean true
# Minimum packages
d-i pkgsel/include string openssh-server ntp linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) linux-cloud-tools-common
# Upgrade packages after debootstrap? (none, safe-upgrade, full-upgrade)
# (note: set to none for speed)
d-i pkgsel/upgrade select none
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i finish-install/reboot_in_progress note
d-i pkgsel/update-policy select none
choose-mirror-bin mirror/http/proxy string
```
-> **Note for \*nix guests:** Please note that Packer requires the VM to be
running a hyper-v KVP daemon in order to detect the IP address of the guest VM.
On RHEL based machines this may require installing the package `hyperv-daemons`
and ensuring the `hypervkvpd` service is started at boot. On Debian based
machines, you may need `linux-cloud-tools-common` for `hv_kvp_daemon`. Failure
to do this may cause packer to wait at `Waiting for SSH to become available...`
before eventually timing out.
Also note that while the operating system is still being installed by a preseed
file, it is normal to see `Waiting for SSH/WinRM to be available` and
`Error getting SSH/WinRM host: No ip address` error messages until the system
is actually installed and ready to be connected to.
For more information about the hyper-v daemons and supported distributions, see
the Microsoft docs at
https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-linux-and-freebsd-virtual-machines-for-hyper-v-on-windows

View File

@ -1,881 +0,0 @@
---
modeline: |
vim: set ft=pandoc:
description: >-
The Hyper-V Packer builder is able to clone an existing Hyper-V virtual
machine and export them.
page_title: Hyper-V Builder (from a vmcx)
---
# Hyper-V Builder (from a vmcx)
Type: `hyperv-vmcx`
Artifact BuilderId: `MSOpenTech.hyperv`
The Hyper-V Packer builder is able to use exported virtual machines or clone
existing
[Hyper-V](https://www.microsoft.com/en-us/server-cloud/solutions/virtualization.aspx)
virtual machines.
Typically, the builder imports or clones an existing virtual machine,
boots it, provisions software within the OS, and then shuts it down. The
result of the Hyper-V builder is a directory containing all the files
necessary to run the virtual machine portably.
## Basic Examples
Here are some basic examples. Neither example would really do anything more
than producing a copy of the source virtual machine. However, the examples
could be used as a starting point for more advanced templates.
Import from folder:
```json
{
"type": "hyperv-vmcx",
"clone_from_vmcx_path": "c:/path/to/ubuntu-12.04.5-server-amd64",
"ssh_username": "packer",
"ssh_password": "packer",
"shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
}
```
Clone from existing virtual machine:
```json
{
"clone_from_vm_name": "ubuntu-12.04.5-server-amd64",
"shutdown_command": "echo 'packer' | sudo -S shutdown -P now",
"ssh_password": "packer",
"ssh_username": "packer",
"type": "hyperv-vmcx"
}
```
By default Packer will perform a hard power off of a virtual machine.
However, when a machine is powered off this way, it is possible that
changes made to the VMs file system may not be fully synced, possibly
leading to corruption of files or lost changes. As such, it is important to
add a `shutdown_command`. This tells Packer how to safely shutdown and
power off the VM.
## Configuration Reference
There are many configuration options available for the Hyper-V builder. They
are organized below into two categories: required and optional. Within each
category, the available options are alphabetized and described.
In addition to the options listed here, a
[communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this
builder.
## ISO Configuration Reference
@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig.mdx'
### Required:
@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig-required.mdx'
### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig-not-required.mdx'
### Required for virtual machine import:
- `clone_from_vmcx_path` (string) - The path to a directory containing a
previously exported virtual machine. The exported machine will be used
as the source for new VM.
note: You should provide the named directory that contains the
"Virtual Machines", "Snapshots", and/or "Virtual Hard Disks" subdirectories,
not the .vmcx file itself.
### Required for virtual machine clone:
- `clone_from_vm_name` (string) - The name of the VM to clone from. Ideally
the machine to clone from should be shutdown.
### Optional:
@include 'builder/hyperv/vmcx/Config-not-required.mdx'
@include 'builder/hyperv/common/CommonConfig-not-required.mdx'
## Communicator configuration reference
### Optional common fields:
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
### Optional SSH fields:
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
### Optional WinRM fields:
@include 'packer-plugin-sdk/communicator/WinRM-not-required.mdx'
### CD configuration
@include 'packer-plugin-sdk/multistep/commonsteps/CDConfig.mdx'
#### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx'
## Boot Command
The `boot_command` configuration is very important: it specifies the keys to
type when the virtual machine is first booted in order to start the OS
installer. This command is typed after `boot_wait`, which gives the virtual
machine some time to actually load the ISO.
As documented above, the `boot_command` is an array of strings. The strings
are all typed in sequence. It is an array only to improve readability within
the template.
The boot command is "typed" character for character over the virtual keyboard
to the machine, simulating a human actually typing the keyboard.
@include 'builders/boot-command.mdx'
The example shown below is a working boot command used to start an Ubuntu
12.04 installer:
```json
[
"<esc><esc><enter><wait>",
"/install/vmlinuz noapic ",
"preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
"hostname={{ .Name }} ",
"fb=false debconf/frontend=noninteractive ",
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
"keyboard-configuration/variant=USA console-setup/ask_detect=false ",
"initrd=/install/initrd.gz -- <enter>"
]
```
For more examples of various boot commands, see the sample projects from our
[community templates page](/community-tools#templates).
## Http directory configuration
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig.mdx'
### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig-not-required.mdx'
## Integration Services
Packer will automatically attach the integration services ISO as a DVD drive
for the version of Hyper-V that is running.
## Generation 1 vs Generation 2
Floppy drives are no longer supported by generation 2 machines. This requires
you to take another approach when dealing with preseed or answer files. Two
possible options are using the `cd_files` option or using Packer's built in web
server.
When dealing with Windows you need to enable UEFI drives for generation 2
virtual machines.
## Creating an ISO From a Directory
Programs like mkisofs can be used to create an ISO from a directory. There is
a [windows version of
mkisofs](http://opensourcepack.blogspot.co.uk/p/cdrtools.html) available.
Below is a working PowerShell script that can be used to create a Windows
answer ISO:
```powershell
$isoFolder = "answer-iso"
if (test-path $isoFolder){
remove-item $isoFolder -Force -Recurse
}
if (test-path windows\windows-2012R2-serverdatacenter-amd64\answer.iso){
remove-item windows\windows-2012R2-serverdatacenter-amd64\answer.iso -Force
}
mkdir $isoFolder
copy windows\windows-2012R2-serverdatacenter-amd64\Autounattend.xml $isoFolder\
copy windows\windows-2012R2-serverdatacenter-amd64\sysprep-unattend.xml $isoFolder\
copy windows\common\set-power-config.ps1 $isoFolder\
copy windows\common\microsoft-updates.ps1 $isoFolder\
copy windows\common\win-updates.ps1 $isoFolder\
copy windows\common\run-sysprep.ps1 $isoFolder\
copy windows\common\run-sysprep.cmd $isoFolder\
$textFile = "$isoFolder\Autounattend.xml"
$c = Get-Content -Encoding UTF8 $textFile
# Enable UEFI and disable Non EUFI
$c | % { $_ -replace '<!-- Start Non UEFI -->','<!-- Start Non UEFI' } | % { $_ -replace '<!-- Finish Non UEFI -->','Finish Non UEFI -->' } | % { $_ -replace '<!-- Start UEFI compatible','<!-- Start UEFI compatible -->' } | % { $_ -replace 'Finish UEFI compatible -->','<!-- Finish UEFI compatible -->' } | sc -Path $textFile
& .\mkisofs.exe -r -iso-level 4 -UDF -o windows\windows-2012R2-serverdatacenter-amd64\answer.iso $isoFolder
if (test-path $isoFolder){
remove-item $isoFolder -Force -Recurse
}
```
## Example For Windows Server 2012 R2 Generation 2
Packer config:
```json
{
"builders": [
{
"vm_name": "windows2012r2",
"type": "hyperv-iso",
"disk_size": 61440,
"floppy_files": [],
"secondary_iso_images": [
"./windows/windows-2012R2-serverdatacenter-amd64/answer.iso"
],
"http_directory": "./windows/common/http/",
"boot_wait": "0s",
"boot_command": ["a<wait>a<wait>a"],
"iso_url": "http://download.microsoft.com/download/6/2/A/62A76ABB-9990-4EFC-A4FE-C7D698DAEB96/9600.16384.WINBLUE_RTM.130821-1623_X64FRE_SERVER_EVAL_EN-US-IRM_SSS_X64FREE_EN-US_DV5.ISO",
"iso_checksum": "md5:458ff91f8abc21b75cb544744bf92e6a",
"communicator": "winrm",
"winrm_username": "vagrant",
"winrm_password": "vagrant",
"winrm_timeout": "4h",
"shutdown_command": "f:\\run-sysprep.cmd",
"memory": 4096,
"cpus": 4,
"generation": 2,
"switch_name": "LAN",
"enable_secure_boot": true
}
],
"provisioners": [
{
"type": "powershell",
"elevated_user": "vagrant",
"elevated_password": "vagrant",
"scripts": [
"./windows/common/install-7zip.ps1",
"./windows/common/install-chef.ps1",
"./windows/common/compile-dotnet-assemblies.ps1",
"./windows/common/cleanup.ps1",
"./windows/common/ultradefrag.ps1",
"./windows/common/sdelete.ps1"
]
}
],
"post-processors": [
{
"type": "vagrant",
"keep_input_artifact": false,
"output": "{{.Provider}}_windows-2012r2_chef.box"
}
]
}
```
autounattend.xml:
```xml
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Start Non UEFI -->
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Type>Primary</Type>
<Order>1</Order>
<Size>350</Size>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>2</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Active>true</Active>
<Format>NTFS</Format>
<Label>boot</Label>
<Order>1</Order>
<PartitionID>1</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Format>NTFS</Format>
<Label>Windows 2012 R2</Label>
<Letter>C</Letter>
<Order>2</Order>
<PartitionID>2</PartitionID>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME </Key>
<Value>Windows Server 2012 R2 SERVERSTANDARD</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>2</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
<!-- Finish Non UEFI -->
<!-- Start UEFI compatible
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Order>1</Order>
<Size>300</Size>
<Type>Primary</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>2</Order>
<Size>100</Size>
<Type>EFI</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>3</Order>
<Size>128</Size>
<Type>MSR</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>4</Order>
<Extend>true</Extend>
<Type>Primary</Type>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>WINRE</Label>
<Format>NTFS</Format>
<TypeID>de94bba4-06d1-4d40-a16a-bfd50179d6ac</TypeID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>2</PartitionID>
<Label>System</Label>
<Format>FAT32</Format>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>3</Order>
<PartitionID>3</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>4</Order>
<PartitionID>4</PartitionID>
<Label>Windows</Label>
<Format>NTFS</Format>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
<WillShowUI>OnError</WillShowUI>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME </Key>
<Value>Windows Server 2012 R2 SERVERSTANDARD</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>4</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
Finish UEFI compatible -->
<UserData>
<!-- Product Key from http://technet.microsoft.com/en-us/library/jj612867.aspx -->
<ProductKey>
<!-- Do not uncomment the Key element if you are using trial ISOs -->
<!-- You must uncomment the Key element (and optionally insert your own key) if you are using retail or volume license ISOs -->
<!--<Key>D2N9P-3P6X9-2R39C-7RTCD-MDVJX</Key>-->
<WillShowUI>OnError</WillShowUI>
</ProductKey>
<AcceptEula>true</AcceptEula>
<FullName>Vagrant</FullName>
<Organization>Vagrant</Organization>
</UserData>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OEMInformation>
<HelpCustomized>false</HelpCustomized>
</OEMInformation>
<ComputerName>vagrant-2012r2</ComputerName>
<TimeZone>Coordinated Universal Time</TimeZone>
<RegisteredOwner />
</component>
<component name="Microsoft-Windows-ServerManager-SvrMgrNc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon>
</component>
<component name="Microsoft-Windows-IE-ESC" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<IEHardenAdmin>false</IEHardenAdmin>
<IEHardenUser>false</IEHardenUser>
</component>
<component name="Microsoft-Windows-OutOfBoxExperience" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenInitialConfigurationTasksAtLogon>true</DoNotOpenInitialConfigurationTasksAtLogon>
</component>
<component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipAutoActivation>true</SkipAutoActivation>
</component>
</settings>
<settings pass="oobeSystem">
<!-- Start Setup cache proxy during installation
<component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<POLICYProxySettingsPerUser>0</POLICYProxySettingsPerUser>
<HKLMProxyEnable>true</HKLMProxyEnable>
<HKLMProxyServer>cache-proxy:3142</HKLMProxyServer>
</component>
Finish Setup cache proxy during installation -->
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Password>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</Password>
<Enabled>true</Enabled>
<Username>vagrant</Username>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
<Description>Set Execution Policy 64 Bit</Description>
<Order>1</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
<Description>Set Execution Policy 32 Bit</Description>
<Order>2</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm quickconfig -q</CommandLine>
<Description>winrm quickconfig -q</Description>
<Order>3</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm quickconfig -transport:http</CommandLine>
<Description>winrm quickconfig -transport:http</Description>
<Order>4</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"}</CommandLine>
<Description>Win RM MaxTimeoutms</Description>
<Order>5</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB="300"}</CommandLine>
<Description>Win RM MaxMemoryPerShellMB</Description>
<Order>6</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/service @{AllowUnencrypted="true"}</CommandLine>
<Description>Win RM AllowUnencrypted</Description>
<Order>7</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/service/auth @{Basic="true"}</CommandLine>
<Description>Win RM auth Basic</Description>
<Order>8</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/client/auth @{Basic="true"}</CommandLine>
<Description>Win RM client auth Basic</Description>
<Order>9</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"} </CommandLine>
<Description>Win RM listener Address/Port</Description>
<Order>10</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes </CommandLine>
<Description>Win RM adv firewall enable</Description>
<Order>11</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall add rule name="WinRM 5985" protocol=TCP dir=in localport=5985 action=allow</CommandLine>
<Description>Win RM port open</Description>
<Order>12</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow</CommandLine>
<Description>Win RM port open</Description>
<Order>13</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c net stop winrm </CommandLine>
<Description>Stop Win RM Service </Description>
<Order>14</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c sc config winrm start= disabled</CommandLine>
<Description>Win RM Autostart</Description>
<Order>15</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v HideFileExt /t REG_DWORD /d 0 /f</CommandLine>
<Order>16</Order>
<Description>Show file extensions in Explorer</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f</CommandLine>
<Order>17</Order>
<Description>Enable QuickEdit mode</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v Start_ShowRun /t REG_DWORD /d 1 /f</CommandLine>
<Order>18</Order>
<Description>Show Run command in Start Menu</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v StartMenuAdminTools /t REG_DWORD /d 1 /f</CommandLine>
<Order>19</Order>
<Description>Show Administrative Tools in Start Menu</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateFileSizePercent /t REG_DWORD /d 0 /f</CommandLine>
<Order>20</Order>
<Description>Zero Hibernation File</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateEnabled /t REG_DWORD /d 0 /f</CommandLine>
<Order>21</Order>
<Description>Disable Hibernation Mode</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c wmic useraccount where "name='vagrant'" set PasswordExpires=FALSE</CommandLine>
<Order>22</Order>
<Description>Disable password expiration for vagrant user</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/winrs @{MaxShellsPerUser="30"}</CommandLine>
<Description>Win RM MaxShellsPerUser</Description>
<Order>23</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c winrm set winrm/config/winrs @{MaxProcessesPerShell="25"}</CommandLine>
<Description>Win RM MaxProcessesPerShell</Description>
<Order>24</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>%SystemRoot%\System32\reg.exe ADD "HKLM\System\CurrentControlSet\Services\Netlogon\Parameters" /v DisablePasswordChange /t REG_DWORD /d 1 /f</CommandLine>
<Description>Turn off computer password</Description>
<Order>25</Order>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow</CommandLine>
<Description>ICMP open for ping</Description>
<Order>26</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<!-- WITH WINDOWS UPDATES -->
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c IF EXIST a:\set-power-config.ps1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\set-power-config.ps1) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File f:\set-power-config.ps1)</CommandLine>
<Order>97</Order>
<Description>Turn off all power saving and timeouts</Description>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c IF EXIST a:\microsoft-updates.ps1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\microsoft-updates.ps1) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File f:\microsoft-updates.ps1)</CommandLine>
<Order>98</Order>
<Description>Enable Microsoft Updates</Description>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c IF EXIST a:\win-updates.ps1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\win-updates.ps1) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File f:\win-updates.ps1)</CommandLine>
<Description>Install Windows Updates</Description>
<Order>100</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<!-- END WITH WINDOWS UPDATES -->
</FirstLogonCommands>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>1</ProtectYourPC>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</Password>
<Group>administrators</Group>
<DisplayName>Vagrant</DisplayName>
<Name>vagrant</Name>
<Description>Vagrant User</Description>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<RegisteredOwner />
<TimeZone>Coordinated Universal Time</TimeZone>
</component>
</settings>
<settings pass="offlineServicing">
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<EnableLUA>false</EnableLUA>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/projects/baseboxes/9600.16384.winblue_rtm.130821-1623_x64fre_server_eval_en-us-irm_sss_x64free_en-us_dv5_slipstream/sources/install.wim#Windows Server 2012 R2 SERVERDATACENTER" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
```
sysprep-unattend.xml:
```xml
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="generalize">
<component language="neutral" name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipRearm>1</SkipRearm>
</component>
</settings>
<settings pass="oobeSystem">
<!-- Setup proxy after sysprep
<component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<POLICYProxySettingsPerUser>1</POLICYProxySettingsPerUser>
<HKLMProxyEnable>false</HKLMProxyEnable>
<HKLMProxyServer>cache-proxy:3142</HKLMProxyServer>
</component>
Finish proxy after sysprep -->
<component language="neutral" name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>0809:00000809</InputLocale>
<SystemLocale>en-GB</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-GB</UserLocale>
</component>
<component language="neutral" name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>1</ProtectYourPC>
<SkipUserOOBE>true</SkipUserOOBE>
<SkipMachineOOBE>true</SkipMachineOOBE>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>vagrant</Value>
<PlainText>true</PlainText>
</Password>
<Group>administrators</Group>
<DisplayName>Vagrant</DisplayName>
<Name>vagrant</Name>
<Description>Vagrant User</Description>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<DisableAutoDaylightTimeSet>true</DisableAutoDaylightTimeSet>
<TimeZone>Coordinated Universal Time</TimeZone>
<VisualEffects>
<SystemDefaultBackgroundColor>2</SystemDefaultBackgroundColor>
</VisualEffects>
</component>
</settings>
</unattend>
```
-> **Warning:** Please note that if you're setting up WinRM for provisioning, you'll probably want to turn it off or restrict its permissions as part of a shutdown script at the end of Packer's provisioning process. For more details on the why/how, check out this useful blog post and the associated code:
https://cloudywindows.io/post/winrm-for-provisioning-close-the-door-on-the-way-out-eh/
## Example For Ubuntu Vivid Generation 2
If you are running Windows under virtualization, you may need to create a
virtual switch with an `External` connection type.
### Packer config:
```json
{
"variables": {
"vm_name": "ubuntu-xenial",
"cpu": "2",
"memory": "1024",
"disk_size": "21440",
"iso_url": "http://releases.ubuntu.com/16.04/ubuntu-16.04.1-server-amd64.iso",
"iso_checksum": "sha1:DE5EE8665048F009577763EFBF4A6F0558833E59"
},
"builders": [
{
"vm_name": "{{user `vm_name`}}",
"type": "hyperv-iso",
"disk_size": "{{user `disk_size`}}",
"guest_additions_mode": "disable",
"iso_url": "{{user `iso_url`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"communicator": "ssh",
"ssh_username": "packer",
"ssh_password": "packer",
"ssh_timeout": "4h",
"http_directory": "./",
"boot_wait": "5s",
"boot_command": [
"<esc><wait10><esc><esc><enter><wait>",
"set gfxpayload=1024x768<enter>",
"linux /install/vmlinuz ",
"preseed/url=http://{{.HTTPIP}}:{{.HTTPPort}}/hyperv-taliesins.cfg ",
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
"hostname={{.Name}} ",
"fb=false debconf/frontend=noninteractive ",
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
"keyboard-configuration/variant=USA console-setup/ask_detect=false <enter>",
"initrd /install/initrd.gz<enter>",
"boot<enter>"
],
"shutdown_command": "echo 'packer' | sudo -S -E shutdown -P now",
"memory": "{{user `memory`}}",
"cpu": "{{user `cpu`}}",
"generation": 2,
"enable_secure_boot": false
}
]
}
```
### preseed.cfg:
```text
## Options to set on the command line
d-i debian-installer/locale string en_US.utf8
d-i console-setup/ask_detect boolean false
d-i console-setup/layout string us
d-i netcfg/get_hostname string nl-ams-basebox3
d-i netcfg/get_domain string unassigned-domain
d-i time/zone string UTC
d-i clock-setup/utc-auto boolean true
d-i clock-setup/utc boolean true
d-i kbd-chooser/method select American English
d-i netcfg/wireless_wep string
d-i base-installer/kernel/override-image string linux-server
d-i debconf debconf/frontend select Noninteractive
d-i pkgsel/install-language-support boolean false
tasksel tasksel/first multiselect standard, ubuntu-server
## Partitioning
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
# Write the changes to disks and configure LVM?
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-partitioning/no_bootable_gpt_biosgrub boolean false
d-i partman-partitioning/no_bootable_gpt_efi boolean false
d-i partman-efi/non_efi_system boolean true
# Default user
d-i passwd/user-fullname string packer
d-i passwd/username string packer
d-i passwd/user-password password packer
d-i passwd/user-password-again password packer
d-i user-setup/encrypt-home boolean false
d-i user-setup/allow-password-weak boolean true
# Minimum packages
d-i pkgsel/include string openssh-server ntp linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) linux-cloud-tools-common
# Upgrade packages after debootstrap? (none, safe-upgrade, full-upgrade)
# (note: set to none for speed)
d-i pkgsel/upgrade select none
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i finish-install/reboot_in_progress note
d-i pkgsel/update-policy select none
choose-mirror-bin mirror/http/proxy string
```

View File

@ -1,141 +0,0 @@
<!-- Code generated from the comments of the CommonConfig struct in builder/hyperv/common/config.go; DO NOT EDIT MANUALLY -->
- `disk_block_size` (uint) - The block size of the VHD to be created.
Recommended disk block size for Linux hyper-v guests is 1 MiB. This
defaults to "32" MiB.
- `memory` (uint) - The amount, in megabytes, of RAM to assign to the
VM. By default, this is 1 GB.
- `secondary_iso_images` ([]string) - A list of ISO paths to
attach to a VM when it is booted. This is most useful for unattended
Windows installs, which look for an Autounattend.xml file on removable
media. By default, no secondary ISO will be attached.
- `disk_additional_size` ([]uint) - The size or sizes of any
additional hard disks for the VM in megabytes. If this is not specified
then the VM will only contain a primary hard disk. Additional drives
will be attached to the SCSI interface only. The builder uses
expandable rather than fixed-size virtual hard disks, so the actual
file representing the disk will not use the full size unless it is
full.
- `guest_additions_mode` (string) - If set to attach then attach and
mount the ISO image specified in guest_additions_path. If set to
none then guest additions are not attached and mounted; This is the
default.
- `guest_additions_path` (string) - The path to the ISO image for guest
additions.
- `vm_name` (string) - This is the name of the new virtual machine,
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
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
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
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.
- `cpus` (uint) - The number of CPUs the virtual machine should use. If
this isn't specified, the default is 1 CPU.
- `generation` (uint) - The Hyper-V generation for the virtual machine. By
default, this is 1. Generation 2 Hyper-V virtual machines do not support
floppy drives. In this scenario use secondary_iso_images instead. Hard
drives and DVD drives will also be SCSI and not IDE.
- `enable_mac_spoofing` (bool) - If true enable MAC address spoofing
for the virtual machine. This defaults to false.
- `enable_dynamic_memory` (bool) - If true enable dynamic memory for
the virtual machine. This defaults to false.
- `enable_secure_boot` (bool) - If true enable secure boot for the
virtual machine. This defaults to false. See secure_boot_template
below for additional settings.
- `secure_boot_template` (string) - The secure boot template to be
configured. Valid values are "MicrosoftWindows" (Windows) or
"MicrosoftUEFICertificateAuthority" (Linux). This only takes effect if
enable_secure_boot is set to "true". This defaults to "MicrosoftWindows".
- `enable_virtualization_extensions` (bool) - If true enable
virtualization extensions for the virtual machine. This defaults to
false. For nested virtualization you need to enable MAC spoofing,
disable dynamic memory and have at least 4GB of RAM assigned to the
virtual machine.
- `temp_path` (string) - The location under which Packer will create a directory to house all the
VM files and folders during the build. By default `%TEMP%` is used
which, for most systems, will evaluate to
`%USERPROFILE%/AppData/Local/Temp`.
The build directory housed under `temp_path` will have a name similar to
`packerhv1234567`. The seven digit number at the end of the name is
automatically generated by Packer to ensure the directory name is
unique.
- `configuration_version` (string) - This allows you to set the vm version when calling New-VM to generate
the vm.
- `keep_registered` (bool) - If "true", Packer will not delete the VM from
The Hyper-V manager.
- `skip_compaction` (bool) - If true skip compacting the hard disk for
the virtual machine when exporting. This defaults to false.
- `skip_export` (bool) - If true Packer will skip the export of the VM.
If you are interested only in the VHD/VHDX files, you can enable this
option. The resulting VHD/VHDX file will be output to
<output_directory>/Virtual Hard Disks. By default this option is false
and Packer will export the VM to output_directory.
- `headless` (bool) - Packer defaults to building Hyper-V virtual
machines by launching a GUI that shows the console of the machine being
built. When this value is set to true, the machine will start without a
console.
- `first_boot_device` (string) - When configured, determines the device or device type that is given preferential
treatment when choosing a boot device.
For Generation 1:
- `IDE`
- `CD` *or* `DVD`
- `Floppy`
- `NET`
For Generation 2:
- `IDE:x:y`
- `SCSI:x:y`
- `CD` *or* `DVD`
- `NET`
- `boot_order` ([]string) - When configured, the boot order determines the order of the devices
from which to boot.
The device name must be in the form of `SCSI:x:y`, for example,
to boot from the first scsi device use `SCSI:0:0`.
**NB** You should also set `first_boot_device` (e.g. `DVD`).
**NB** Although the VM will have this initial boot order, the OS can
change it, for example, Ubuntu 18.04 will modify the boot order to
include itself as the first boot option.
**NB** This only works for Generation 2 machines.
<!-- End of code generated from the comments of the CommonConfig struct in builder/hyperv/common/config.go; -->

View File

@ -1,11 +0,0 @@
<!-- Code generated from the comments of the OutputConfig struct in builder/hyperv/common/output_config.go; DO NOT EDIT MANUALLY -->
- `output_directory` (string) - This setting specifies the directory that
artifacts from the build, such as the virtual machine files and disks,
will be output to. The path to the directory may be relative or
absolute. If relative, the path is relative to the working directory
packer is executed from. This directory must not exist or, if
created, must be empty prior to running the builder. By default this is
"output-BUILDNAME" where "BUILDNAME" is the name of the build.
<!-- End of code generated from the comments of the OutputConfig struct in builder/hyperv/common/output_config.go; -->

View File

@ -1,6 +0,0 @@
<!-- Code generated from the comments of the Builder struct in builder/hyperv/iso/builder.go; DO NOT EDIT MANUALLY -->
Builder implements packersdk.Builder and builds the actual Hyperv
images.
<!-- End of code generated from the comments of the Builder struct in builder/hyperv/iso/builder.go; -->

View File

@ -1,24 +0,0 @@
<!-- Code generated from the comments of the Config struct in builder/hyperv/iso/builder.go; DO NOT EDIT MANUALLY -->
- `disk_size` (uint) - The size, in megabytes, of the hard disk to create
for the VM. By default, this is 40 GB.
- `use_legacy_network_adapter` (bool) - If true use a legacy network adapter as the NIC.
This defaults to false. A legacy network adapter is fully emulated NIC, and is thus
supported by various exotic operating systems, but this emulation requires
additional overhead and should only be used if absolutely necessary.
- `differencing_disk` (bool) - If true enables differencing disks. Only
the changes will be written to the new disk. This is especially useful if
your source is a VHD/VHDX. This defaults to false.
- `use_fixed_vhd_format` (bool) - If true, creates the boot disk on the
virtual machine as a fixed VHD format disk. The default is false, which
creates a dynamic VHDX format disk. This option requires setting
generation to 1, skip_compaction to true, and
differencing_disk to false. Additionally, any value entered for
disk_block_size will be ignored. The most likely use case for this
option is outputing a disk that is in the format required for upload to
Azure.
<!-- End of code generated from the comments of the Config struct in builder/hyperv/iso/builder.go; -->

View File

@ -1,6 +0,0 @@
<!-- Code generated from the comments of the Builder struct in builder/hyperv/vmcx/builder.go; DO NOT EDIT MANUALLY -->
Builder implements packersdk.Builder and builds the actual Hyperv
images.
<!-- End of code generated from the comments of the Builder struct in builder/hyperv/vmcx/builder.go; -->

View File

@ -1,25 +0,0 @@
<!-- Code generated from the comments of the Config struct in builder/hyperv/vmcx/builder.go; DO NOT EDIT MANUALLY -->
- `clone_from_vmcx_path` (string) - This is the path to a directory containing an exported virtual machine.
- `clone_from_vm_name` (string) - This is the name of the virtual machine to clone from.
- `clone_from_snapshot_name` (string) - The name of a snapshot in the
source machine to use as a starting point for the clone. If the value
given is an empty string, the last snapshot present in the source will
be chosen as the starting point for the new VM.
- `clone_all_snapshots` (bool) - If set to true all snapshots
present in the source machine will be copied when the machine is
cloned. The final result of the build will be an exported virtual
machine that contains all the snapshots of the parent.
- `differencing_disk` (bool) - If true enables differencing disks. Only
the changes will be written to the new disk. This is especially useful if
your source is a VHD/VHDX. This defaults to false.
- `copy_in_compare` (bool) - When cloning a vm to build from, we run a powershell
Compare-VM command, which, depending on your version of Windows, may need
the "Copy" flag to be set to true or false. Defaults to "false". Command:
<!-- End of code generated from the comments of the Config struct in builder/hyperv/vmcx/builder.go; -->

View File

@ -708,23 +708,6 @@
"title": "Hetzner Cloud",
"path": "builders/hetzner-cloud"
},
{
"title": "Hyper-V",
"routes": [
{
"title": "Overview",
"path": "builders/hyperv"
},
{
"title": "ISO",
"path": "builders/hyperv/iso"
},
{
"title": "VMCX",
"path": "builders/hyperv/vmcx"
}
]
},
{
"title": "Linode",
"path": "builders/linode"

View File

@ -52,6 +52,13 @@
"version": "latest",
"pluginTier": "community"
},
{
"title": "Hyper-V",
"path": "hyperv",
"repo": "hashicorp/packer-plugin-hyperv",
"version": "latest",
"pluginTier": "community"
},
{
"title": "Naver Cloud",
"path": "ncloud",