remove and vendor hyperv (#10952)
This commit is contained in:
parent
2db338e322
commit
469f033c36
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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"))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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) {}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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) {}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {}
|
|
@ -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) {
|
||||
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -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) {}
|
|
@ -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) {
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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) {
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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.
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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
9
go.mod
|
@ -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
15
go.sum
|
@ -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=
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
||||
```
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue