add step customize and docs
This commit is contained in:
parent
943cb1275d
commit
c6e9bde97b
|
@ -54,6 +54,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if b.config.CustomizeConfig != nil {
|
||||||
|
steps = append(steps, &StepCustomize{
|
||||||
|
Config: b.config.CustomizeConfig,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if b.config.Comm.Type != "none" {
|
if b.config.Comm.Type != "none" {
|
||||||
steps = append(steps,
|
steps = append(steps,
|
||||||
&common.StepHTTPIPDiscover{
|
&common.StepHTTPIPDiscover{
|
||||||
|
|
|
@ -40,6 +40,8 @@ type Config struct {
|
||||||
// The VM template will not be imported if no [Content Library Import Configuration](#content-library-import-configuration) is specified.
|
// The VM template will not be imported if no [Content Library Import Configuration](#content-library-import-configuration) is specified.
|
||||||
// The import doesn't work if [convert_to_template](#convert_to_template) is set to true.
|
// The import doesn't work if [convert_to_template](#convert_to_template) is set to true.
|
||||||
ContentLibraryDestinationConfig *common.ContentLibraryDestinationConfig `mapstructure:"content_library_destination"`
|
ContentLibraryDestinationConfig *common.ContentLibraryDestinationConfig `mapstructure:"content_library_destination"`
|
||||||
|
// Customize the cloned VM to configure host, network, or licensing settings. See the [customization options](#customization).
|
||||||
|
CustomizeConfig *CustomizeConfig `mapstructure:"customize"`
|
||||||
|
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
@ -75,6 +77,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
if c.ContentLibraryDestinationConfig != nil {
|
if c.ContentLibraryDestinationConfig != nil {
|
||||||
errs = packer.MultiErrorAppend(errs, c.ContentLibraryDestinationConfig.Prepare(&c.LocationConfig)...)
|
errs = packer.MultiErrorAppend(errs, c.ContentLibraryDestinationConfig.Prepare(&c.LocationConfig)...)
|
||||||
}
|
}
|
||||||
|
if c.CustomizeConfig != nil {
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.CustomizeConfig.Prepare()...)
|
||||||
|
}
|
||||||
|
|
||||||
if len(errs.Errors) > 0 {
|
if len(errs.Errors) > 0 {
|
||||||
return nil, errs
|
return nil, errs
|
||||||
|
|
|
@ -118,6 +118,7 @@ type FlatConfig struct {
|
||||||
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template" hcl:"convert_to_template"`
|
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template" hcl:"convert_to_template"`
|
||||||
Export *common.FlatExportConfig `mapstructure:"export" cty:"export" hcl:"export"`
|
Export *common.FlatExportConfig `mapstructure:"export" cty:"export" hcl:"export"`
|
||||||
ContentLibraryDestinationConfig *common.FlatContentLibraryDestinationConfig `mapstructure:"content_library_destination" cty:"content_library_destination" hcl:"content_library_destination"`
|
ContentLibraryDestinationConfig *common.FlatContentLibraryDestinationConfig `mapstructure:"content_library_destination" cty:"content_library_destination" hcl:"content_library_destination"`
|
||||||
|
CustomizeConfig *FlatCustomizeConfig `mapstructure:"customize" cty:"customize" hcl:"customize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatConfig.
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
@ -240,6 +241,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
|
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
|
||||||
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
|
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
|
||||||
"content_library_destination": &hcldec.BlockSpec{TypeName: "content_library_destination", Nested: hcldec.ObjectSpec((*common.FlatContentLibraryDestinationConfig)(nil).HCL2Spec())},
|
"content_library_destination": &hcldec.BlockSpec{TypeName: "content_library_destination", Nested: hcldec.ObjectSpec((*common.FlatContentLibraryDestinationConfig)(nil).HCL2Spec())},
|
||||||
|
"customize": &hcldec.BlockSpec{TypeName: "customize", Nested: hcldec.ObjectSpec((*FlatCustomizeConfig)(nil).HCL2Spec())},
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type CustomizeConfig,LinuxOptions,NetworkInterfaces,NetworkInterface,GlobalDnsSettings,GlobalRoutingSettings
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/config"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A cloned virtual machine can be [customized](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-58E346FF-83AE-42B8-BE58-253641D257BC.html)
|
||||||
|
// to configure host, network, or licensing settings.
|
||||||
|
//
|
||||||
|
// To perform virtual machine customization as a part of the clone process, specify the customize block with the
|
||||||
|
// respective customization options. Windows guests are customized using Sysprep, which will result in the machine SID being reset.
|
||||||
|
// Before using customization, check that your source VM meets the [requirements](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-E63B6FAA-8D35-428D-B40C-744769845906.html)
|
||||||
|
// for guest OS customization on vSphere.
|
||||||
|
// See the [customization example](#customization-example) for a usage synopsis.
|
||||||
|
//
|
||||||
|
// The settings for customize are as follows:
|
||||||
|
type CustomizeConfig struct {
|
||||||
|
// Settings to Linux guest OS customization. See [Linux customization settings](#linux-customization-settings).
|
||||||
|
LinuxOptions *LinuxOptions `mapstructure:"linux_options"`
|
||||||
|
// Supply your own sysprep.xml file to allow full control of the customization process out-of-band of vSphere.
|
||||||
|
WindowsSysPrepFile string `mapstructure:"windows_sysprep_file"`
|
||||||
|
// Configure network interfaces on a per-interface basis that should matched up to the network adapters present in the VM.
|
||||||
|
// To use DHCP, declare an empty network_interface for each adapter being configured. This field is required.
|
||||||
|
// See [Network interface settings](#network-interface-settings).
|
||||||
|
NetworkInterfaces NetworkInterfaces `mapstructure:"network_interface"`
|
||||||
|
GlobalRoutingSettings `mapstructure:",squash"`
|
||||||
|
GlobalDnsSettings `mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinuxOptions struct {
|
||||||
|
// The domain name for this machine. This, along with [host_name](#host_name), make up the FQDN of this virtual machine.
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
// The host name for this machine. This, along with [domain](#domain), make up the FQDN of this virtual machine.
|
||||||
|
Hostname string `mapstructure:"host_name"`
|
||||||
|
// Tells the operating system that the hardware clock is set to UTC. Default: true.
|
||||||
|
HWClockUTC config.Trilean `mapstructure:"hw_clock_utc"`
|
||||||
|
// Sets the time zone. The default is UTC.
|
||||||
|
Timezone string `mapstructure:"time_zone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkInterface struct {
|
||||||
|
// Network interface-specific DNS server settings for Windows operating systems.
|
||||||
|
// Ignored on Linux and possibly other operating systems - for those systems, please see the [global DNS settings](#global-dns-settings) section.
|
||||||
|
DnsServerList []string `mapstructure:"dns_server_list"`
|
||||||
|
// Network interface-specific DNS search domain for Windows operating systems.
|
||||||
|
// Ignored on Linux and possibly other operating systems - for those systems, please see the [global DNS settings](#global-dns-settings) section.
|
||||||
|
DnsDomain string `mapstructure:"dns_domain"`
|
||||||
|
// The IPv4 address assigned to this network adapter. If left blank or not included, DHCP is used.
|
||||||
|
Ipv4Address string `mapstructure:"ipv4_address"`
|
||||||
|
// The IPv4 subnet mask, in bits (example: 24 for 255.255.255.0).
|
||||||
|
Ipv4NetMask int `mapstructure:"ipv4_netmask"`
|
||||||
|
// The IPv6 address assigned to this network adapter. If left blank or not included, auto-configuration is used.
|
||||||
|
Ipv6Address string `mapstructure:"ipv6_address"`
|
||||||
|
// The IPv6 subnet mask, in bits (example: 32).
|
||||||
|
Ipv6NetMask int `mapstructure:"ipv6_netmask"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkInterfaces []NetworkInterface
|
||||||
|
|
||||||
|
// The settings here must match the IP/mask of at least one network_interface supplied to customization.
|
||||||
|
type GlobalRoutingSettings struct {
|
||||||
|
// The IPv4 default gateway when using network_interface customization on the virtual machine.
|
||||||
|
Ipv4Gateway string `mapstructure:"ipv4_gateway"`
|
||||||
|
// The IPv6 default gateway when using network_interface customization on the virtual machine.
|
||||||
|
Ipv6Gateway string `mapstructure:"ipv6_gateway"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following settings configure DNS globally, generally for Linux systems. For Windows systems,
|
||||||
|
// this is done per-interface, see [network interface](#network_interface) settings.
|
||||||
|
type GlobalDnsSettings struct {
|
||||||
|
// The list of DNS servers to configure on a virtual machine.
|
||||||
|
DnsServerList []string `mapstructure:"dns_server_list"`
|
||||||
|
// A list of DNS search domains to add to the DNS configuration on the virtual machine.
|
||||||
|
DnsSuffixList []string `mapstructure:"dns_suffix_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepCustomize struct {
|
||||||
|
Config *CustomizeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CustomizeConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.LinuxOptions == nil && c.WindowsSysPrepFile == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("customize is empty"))
|
||||||
|
}
|
||||||
|
if c.LinuxOptions != nil && c.WindowsSysPrepFile != "" {
|
||||||
|
errs = append(errs, fmt.Errorf("`linux_options` and `windows_sysprep_text` both set - one must not be included if the other is specified"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.LinuxOptions != nil {
|
||||||
|
if c.LinuxOptions.Hostname == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("linux options `host_name` is empty"))
|
||||||
|
}
|
||||||
|
if c.LinuxOptions.Domain == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("linux options `domain` is empty"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.LinuxOptions.HWClockUTC == config.TriUnset {
|
||||||
|
c.LinuxOptions.HWClockUTC = config.TriTrue
|
||||||
|
}
|
||||||
|
if c.LinuxOptions.Timezone == "" {
|
||||||
|
c.LinuxOptions.Timezone = "UTC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.NetworkInterfaces) == 0 {
|
||||||
|
errs = append(errs, fmt.Errorf("one or more `network_interface` must be provided"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
identity, err := s.identitySettings()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
nicSettingsMap := s.nicSettingsMap()
|
||||||
|
globalIpSettings := s.globalIpSettings()
|
||||||
|
|
||||||
|
spec := types.CustomizationSpec{
|
||||||
|
Identity: identity,
|
||||||
|
NicSettingMap: nicSettingsMap,
|
||||||
|
GlobalIPSettings: globalIpSettings,
|
||||||
|
}
|
||||||
|
ui.Say("Customizing VM...")
|
||||||
|
err = vm.Customize(spec)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) identitySettings() (types.BaseCustomizationIdentitySettings, error) {
|
||||||
|
if s.Config.LinuxOptions != nil {
|
||||||
|
return &types.CustomizationLinuxPrep{
|
||||||
|
HostName: &types.CustomizationFixedName{
|
||||||
|
Name: s.Config.LinuxOptions.Hostname,
|
||||||
|
},
|
||||||
|
Domain: s.Config.LinuxOptions.Domain,
|
||||||
|
TimeZone: s.Config.LinuxOptions.Timezone,
|
||||||
|
HwClockUTC: s.Config.LinuxOptions.HWClockUTC.ToBoolPointer(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Config.WindowsSysPrepFile != "" {
|
||||||
|
sysPrep, err := ioutil.ReadFile(s.Config.WindowsSysPrepFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error on reading %s: %s", s.Config.WindowsSysPrepFile, err)
|
||||||
|
}
|
||||||
|
return &types.CustomizationSysprepText{
|
||||||
|
Value: string(sysPrep),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no customization identity found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) nicSettingsMap() []types.CustomizationAdapterMapping {
|
||||||
|
result := make([]types.CustomizationAdapterMapping, len(s.Config.NetworkInterfaces))
|
||||||
|
var ipv4gwFound, ipv6gwFound bool
|
||||||
|
for i := range s.Config.NetworkInterfaces {
|
||||||
|
var adapter types.CustomizationIPSettings
|
||||||
|
adapter, ipv4gwFound, ipv6gwFound = s.ipSettings(i, !ipv4gwFound, !ipv6gwFound)
|
||||||
|
obj := types.CustomizationAdapterMapping{
|
||||||
|
Adapter: adapter,
|
||||||
|
}
|
||||||
|
result[i] = obj
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) ipSettings(n int, ipv4gwAdd bool, ipv6gwAdd bool) (types.CustomizationIPSettings, bool, bool) {
|
||||||
|
var v4gwFound, v6gwFound bool
|
||||||
|
var obj types.CustomizationIPSettings
|
||||||
|
|
||||||
|
ipv4Address := s.Config.NetworkInterfaces[n].Ipv4Address
|
||||||
|
if ipv4Address != "" {
|
||||||
|
ipv4mask := s.Config.NetworkInterfaces[n].Ipv4NetMask
|
||||||
|
ipv4Gateway := s.Config.Ipv4Gateway
|
||||||
|
obj.Ip = &types.CustomizationFixedIp{
|
||||||
|
IpAddress: ipv4Address,
|
||||||
|
}
|
||||||
|
obj.SubnetMask = v4CIDRMaskToDotted(ipv4mask)
|
||||||
|
// Check for the gateway
|
||||||
|
if ipv4gwAdd && ipv4Gateway != "" && matchGateway(ipv4Address, ipv4mask, ipv4Gateway) {
|
||||||
|
obj.Gateway = []string{ipv4Gateway}
|
||||||
|
v4gwFound = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj.Ip = &types.CustomizationDhcpIpGenerator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.DnsServerList = s.Config.NetworkInterfaces[n].DnsServerList
|
||||||
|
obj.DnsDomain = s.Config.NetworkInterfaces[n].DnsDomain
|
||||||
|
obj.IpV6Spec, v6gwFound = s.IPSettingsIPV6Address(n, ipv6gwAdd)
|
||||||
|
|
||||||
|
return obj, v4gwFound, v6gwFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func v4CIDRMaskToDotted(mask int) string {
|
||||||
|
m := net.CIDRMask(mask, 32)
|
||||||
|
a := int(m[0])
|
||||||
|
b := int(m[1])
|
||||||
|
c := int(m[2])
|
||||||
|
d := int(m[3])
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", a, b, c, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) IPSettingsIPV6Address(n int, gwAdd bool) (*types.CustomizationIPSettingsIpV6AddressSpec, bool) {
|
||||||
|
addr := s.Config.NetworkInterfaces[n].Ipv6Address
|
||||||
|
var gwFound bool
|
||||||
|
if addr == "" {
|
||||||
|
return nil, gwFound
|
||||||
|
}
|
||||||
|
mask := s.Config.NetworkInterfaces[n].Ipv6NetMask
|
||||||
|
gw := s.Config.Ipv6Gateway
|
||||||
|
obj := &types.CustomizationIPSettingsIpV6AddressSpec{
|
||||||
|
Ip: []types.BaseCustomizationIpV6Generator{
|
||||||
|
&types.CustomizationFixedIpV6{
|
||||||
|
IpAddress: addr,
|
||||||
|
SubnetMask: int32(mask),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if gwAdd && gw != "" && matchGateway(addr, mask, gw) {
|
||||||
|
obj.Gateway = []string{gw}
|
||||||
|
gwFound = true
|
||||||
|
}
|
||||||
|
return obj, gwFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchGateway take an IP, mask, and gateway, and checks to see if the gateway
|
||||||
|
// is reachable from the IP address.
|
||||||
|
func matchGateway(a string, m int, g string) bool {
|
||||||
|
ip := net.ParseIP(a)
|
||||||
|
gw := net.ParseIP(g)
|
||||||
|
var mask net.IPMask
|
||||||
|
if ip.To4() != nil {
|
||||||
|
mask = net.CIDRMask(m, 32)
|
||||||
|
} else {
|
||||||
|
mask = net.CIDRMask(m, 128)
|
||||||
|
}
|
||||||
|
if ip.Mask(mask).Equal(gw.Mask(mask)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) globalIpSettings() types.CustomizationGlobalIPSettings {
|
||||||
|
return types.CustomizationGlobalIPSettings{
|
||||||
|
DnsServerList: s.Config.DnsServerList,
|
||||||
|
DnsSuffixList: s.Config.DnsSuffixList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCustomize) Cleanup(_ multistep.StateBag) {}
|
|
@ -0,0 +1,154 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type CustomizeConfig,LinuxOptions,NetworkInterfaces,NetworkInterface,GlobalDnsSettings,GlobalRoutingSettings"; DO NOT EDIT.
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatCustomizeConfig is an auto-generated flat version of CustomizeConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatCustomizeConfig struct {
|
||||||
|
LinuxOptions *FlatLinuxOptions `mapstructure:"linux_options" cty:"linux_options" hcl:"linux_options"`
|
||||||
|
WindowsSysPrepFile *string `mapstructure:"windows_sysprep_file" cty:"windows_sysprep_file" hcl:"windows_sysprep_file"`
|
||||||
|
NetworkInterfaces []FlatNetworkInterface `mapstructure:"network_interface" cty:"network_interface" hcl:"network_interface"`
|
||||||
|
Ipv4Gateway *string `mapstructure:"ipv4_gateway" cty:"ipv4_gateway" hcl:"ipv4_gateway"`
|
||||||
|
Ipv6Gateway *string `mapstructure:"ipv6_gateway" cty:"ipv6_gateway" hcl:"ipv6_gateway"`
|
||||||
|
DnsServerList []string `mapstructure:"dns_server_list" cty:"dns_server_list" hcl:"dns_server_list"`
|
||||||
|
DnsSuffixList []string `mapstructure:"dns_suffix_list" cty:"dns_suffix_list" hcl:"dns_suffix_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatCustomizeConfig.
|
||||||
|
// FlatCustomizeConfig is an auto-generated flat version of CustomizeConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*CustomizeConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatCustomizeConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a CustomizeConfig.
|
||||||
|
// This spec is used by HCL to read the fields of CustomizeConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatCustomizeConfig.
|
||||||
|
func (*FlatCustomizeConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"linux_options": &hcldec.BlockSpec{TypeName: "linux_options", Nested: hcldec.ObjectSpec((*FlatLinuxOptions)(nil).HCL2Spec())},
|
||||||
|
"windows_sysprep_file": &hcldec.AttrSpec{Name: "windows_sysprep_file", Type: cty.String, Required: false},
|
||||||
|
"network_interface": &hcldec.BlockListSpec{TypeName: "network_interface", Nested: hcldec.ObjectSpec((*FlatNetworkInterface)(nil).HCL2Spec())},
|
||||||
|
"ipv4_gateway": &hcldec.AttrSpec{Name: "ipv4_gateway", Type: cty.String, Required: false},
|
||||||
|
"ipv6_gateway": &hcldec.AttrSpec{Name: "ipv6_gateway", Type: cty.String, Required: false},
|
||||||
|
"dns_server_list": &hcldec.AttrSpec{Name: "dns_server_list", Type: cty.List(cty.String), Required: false},
|
||||||
|
"dns_suffix_list": &hcldec.AttrSpec{Name: "dns_suffix_list", Type: cty.List(cty.String), Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatGlobalDnsSettings is an auto-generated flat version of GlobalDnsSettings.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatGlobalDnsSettings struct {
|
||||||
|
DnsServerList []string `mapstructure:"dns_server_list" cty:"dns_server_list" hcl:"dns_server_list"`
|
||||||
|
DnsSuffixList []string `mapstructure:"dns_suffix_list" cty:"dns_suffix_list" hcl:"dns_suffix_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatGlobalDnsSettings.
|
||||||
|
// FlatGlobalDnsSettings is an auto-generated flat version of GlobalDnsSettings.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*GlobalDnsSettings) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatGlobalDnsSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a GlobalDnsSettings.
|
||||||
|
// This spec is used by HCL to read the fields of GlobalDnsSettings.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatGlobalDnsSettings.
|
||||||
|
func (*FlatGlobalDnsSettings) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"dns_server_list": &hcldec.AttrSpec{Name: "dns_server_list", Type: cty.List(cty.String), Required: false},
|
||||||
|
"dns_suffix_list": &hcldec.AttrSpec{Name: "dns_suffix_list", Type: cty.List(cty.String), Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatGlobalRoutingSettings is an auto-generated flat version of GlobalRoutingSettings.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatGlobalRoutingSettings struct {
|
||||||
|
Ipv4Gateway *string `mapstructure:"ipv4_gateway" cty:"ipv4_gateway" hcl:"ipv4_gateway"`
|
||||||
|
Ipv6Gateway *string `mapstructure:"ipv6_gateway" cty:"ipv6_gateway" hcl:"ipv6_gateway"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatGlobalRoutingSettings.
|
||||||
|
// FlatGlobalRoutingSettings is an auto-generated flat version of GlobalRoutingSettings.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*GlobalRoutingSettings) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatGlobalRoutingSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a GlobalRoutingSettings.
|
||||||
|
// This spec is used by HCL to read the fields of GlobalRoutingSettings.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatGlobalRoutingSettings.
|
||||||
|
func (*FlatGlobalRoutingSettings) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"ipv4_gateway": &hcldec.AttrSpec{Name: "ipv4_gateway", Type: cty.String, Required: false},
|
||||||
|
"ipv6_gateway": &hcldec.AttrSpec{Name: "ipv6_gateway", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatLinuxOptions is an auto-generated flat version of LinuxOptions.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatLinuxOptions struct {
|
||||||
|
Domain *string `mapstructure:"domain" cty:"domain" hcl:"domain"`
|
||||||
|
Hostname *string `mapstructure:"host_name" cty:"host_name" hcl:"host_name"`
|
||||||
|
HWClockUTC *bool `mapstructure:"hw_clock_utc" cty:"hw_clock_utc" hcl:"hw_clock_utc"`
|
||||||
|
Timezone *string `mapstructure:"time_zone" cty:"time_zone" hcl:"time_zone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatLinuxOptions.
|
||||||
|
// FlatLinuxOptions is an auto-generated flat version of LinuxOptions.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*LinuxOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatLinuxOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a LinuxOptions.
|
||||||
|
// This spec is used by HCL to read the fields of LinuxOptions.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatLinuxOptions.
|
||||||
|
func (*FlatLinuxOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"domain": &hcldec.AttrSpec{Name: "domain", Type: cty.String, Required: false},
|
||||||
|
"host_name": &hcldec.AttrSpec{Name: "host_name", Type: cty.String, Required: false},
|
||||||
|
"hw_clock_utc": &hcldec.AttrSpec{Name: "hw_clock_utc", Type: cty.Bool, Required: false},
|
||||||
|
"time_zone": &hcldec.AttrSpec{Name: "time_zone", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatNetworkInterface is an auto-generated flat version of NetworkInterface.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatNetworkInterface struct {
|
||||||
|
DnsServerList []string `mapstructure:"dns_server_list" cty:"dns_server_list" hcl:"dns_server_list"`
|
||||||
|
DnsDomain *string `mapstructure:"dns_domain" cty:"dns_domain" hcl:"dns_domain"`
|
||||||
|
Ipv4Address *string `mapstructure:"ipv4_address" cty:"ipv4_address" hcl:"ipv4_address"`
|
||||||
|
Ipv4NetMask *int `mapstructure:"ipv4_netmask" cty:"ipv4_netmask" hcl:"ipv4_netmask"`
|
||||||
|
Ipv6Address *string `mapstructure:"ipv6_address" cty:"ipv6_address" hcl:"ipv6_address"`
|
||||||
|
Ipv6NetMask *int `mapstructure:"ipv6_netmask" cty:"ipv6_netmask" hcl:"ipv6_netmask"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatNetworkInterface.
|
||||||
|
// FlatNetworkInterface is an auto-generated flat version of NetworkInterface.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*NetworkInterface) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatNetworkInterface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a NetworkInterface.
|
||||||
|
// This spec is used by HCL to read the fields of NetworkInterface.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatNetworkInterface.
|
||||||
|
func (*FlatNetworkInterface) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"dns_server_list": &hcldec.AttrSpec{Name: "dns_server_list", Type: cty.List(cty.String), Required: false},
|
||||||
|
"dns_domain": &hcldec.AttrSpec{Name: "dns_domain", Type: cty.String, Required: false},
|
||||||
|
"ipv4_address": &hcldec.AttrSpec{Name: "ipv4_address", Type: cty.String, Required: false},
|
||||||
|
"ipv4_netmask": &hcldec.AttrSpec{Name: "ipv4_netmask", Type: cty.Number, Required: false},
|
||||||
|
"ipv6_address": &hcldec.AttrSpec{Name: "ipv6_address", Type: cty.String, Required: false},
|
||||||
|
"ipv6_netmask": &hcldec.AttrSpec{Name: "ipv6_netmask", Type: cty.Number, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -524,6 +524,14 @@ func (vm *VirtualMachine) Configure(config *HardwareConfig) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) Customize(spec types.CustomizationSpec) error {
|
||||||
|
task, err := vm.vm.Customize(vm.driver.ctx, spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return task.Wait(vm.driver.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (vm *VirtualMachine) ResizeDisk(diskSize int64) error {
|
func (vm *VirtualMachine) ResizeDisk(diskSize int64) error {
|
||||||
var confSpec types.VirtualMachineConfigSpec
|
var confSpec types.VirtualMachineConfigSpec
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,70 @@ can be done via environment variable:
|
||||||
|
|
||||||
@include 'builder/vsphere/common/ConfigParamsConfig-not-required.mdx'
|
@include 'builder/vsphere/common/ConfigParamsConfig-not-required.mdx'
|
||||||
|
|
||||||
|
### Customization
|
||||||
|
|
||||||
|
@include '/builder/vsphere/clone/CustomizeConfig.mdx'
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/CustomizeConfig-not-required.mdx'
|
||||||
|
|
||||||
|
#### Network Interface Settings
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/NetworkInterface-not-required.mdx'
|
||||||
|
|
||||||
|
#### Global Routing Settings
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/GlobalRoutingSettings.mdx'
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/GlobalRoutingSettings-not-required.mdx'
|
||||||
|
|
||||||
|
#### Global DNS Settings
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/GlobalDnsSettings.mdx'
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/GlobalDnsSettings-not-required.mdx'
|
||||||
|
|
||||||
|
#### Linux Customization Settings
|
||||||
|
|
||||||
|
@include 'builder/vsphere/clone/LinuxOptions-not-required.mdx'
|
||||||
|
|
||||||
|
#### Customization Example
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="JSON">
|
||||||
|
|
||||||
|
```json
|
||||||
|
"customize": {
|
||||||
|
"linux_options": {
|
||||||
|
"host_name": "packer-test",
|
||||||
|
"domain": "test.internal"
|
||||||
|
},
|
||||||
|
"network_interface": {
|
||||||
|
"ipv4_address": "10.0.0.10",
|
||||||
|
"ipv4_netmask": "24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
<Tab heading="HCL2">
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
customize {
|
||||||
|
linux_options {
|
||||||
|
host_name = "packer-test"
|
||||||
|
domain = "test.internal"
|
||||||
|
}
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
ipv4_address = "10.0.0.10"
|
||||||
|
ipv4_netmask = "24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
### Boot configuration
|
### Boot configuration
|
||||||
|
|
||||||
@include 'common/bootcommand/BootConfig.mdx'
|
@include 'common/bootcommand/BootConfig.mdx'
|
||||||
|
|
|
@ -11,3 +11,5 @@
|
||||||
- `content_library_destination` (\*common.ContentLibraryDestinationConfig) - Configuration for importing the VM template to a Content Library.
|
- `content_library_destination` (\*common.ContentLibraryDestinationConfig) - Configuration for importing the VM template to a Content Library.
|
||||||
The VM template will not be imported if no [Content Library Import Configuration](#content-library-import-configuration) is specified.
|
The VM template will not be imported if no [Content Library Import Configuration](#content-library-import-configuration) is specified.
|
||||||
The import doesn't work if [convert_to_template](#convert_to_template) is set to true.
|
The import doesn't work if [convert_to_template](#convert_to_template) is set to true.
|
||||||
|
|
||||||
|
- `customize` (\*CustomizeConfig) - Customize the cloned VM to configure host, network, or licensing settings. See the [customization options](#customization).
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- Code generated from the comments of the CustomizeConfig struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
- `linux_options` (\*LinuxOptions) - Settings to Linux guest OS customization. See [Linux customization settings](#linux-customization-settings).
|
||||||
|
|
||||||
|
- `windows_sysprep_file` (string) - Supply your own sysprep.xml file to allow full control of the customization process out-of-band of vSphere.
|
||||||
|
|
||||||
|
- `network_interface` (NetworkInterfaces) - Configure network interfaces on a per-interface basis that should matched up to the network adapters present in the VM.
|
||||||
|
To use DHCP, declare an empty network_interface for each adapter being configured. This field is required.
|
||||||
|
See [Network interface settings](#network-interface-settings).
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!-- Code generated from the comments of the CustomizeConfig struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
A cloned virtual machine can be [customized](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-58E346FF-83AE-42B8-BE58-253641D257BC.html)
|
||||||
|
to configure host, network, or licensing settings.
|
||||||
|
|
||||||
|
To perform virtual machine customization as a part of the clone process, specify the customize block with the
|
||||||
|
respective customization options. Windows guests are customized using Sysprep, which will result in the machine SID being reset.
|
||||||
|
Before using customization, check that your source VM meets the [requirements](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-E63B6FAA-8D35-428D-B40C-744769845906.html)
|
||||||
|
for guest OS customization on vSphere.
|
||||||
|
See the [customization example](#customization-example) for a usage synopsis.
|
||||||
|
|
||||||
|
The settings for customize are as follows:
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Code generated from the comments of the GlobalDnsSettings struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
- `dns_server_list` ([]string) - The list of DNS servers to configure on a virtual machine.
|
||||||
|
|
||||||
|
- `dns_suffix_list` ([]string) - A list of DNS search domains to add to the DNS configuration on the virtual machine.
|
|
@ -0,0 +1,4 @@
|
||||||
|
<!-- Code generated from the comments of the GlobalDnsSettings struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
The following settings configure DNS globally, generally for Linux systems. For Windows systems,
|
||||||
|
this is done per-interface, see [network interface](#network_interface) settings.
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Code generated from the comments of the GlobalRoutingSettings struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
- `ipv4_gateway` (string) - The IPv4 default gateway when using network_interface customization on the virtual machine.
|
||||||
|
|
||||||
|
- `ipv6_gateway` (string) - The IPv6 default gateway when using network_interface customization on the virtual machine.
|
|
@ -0,0 +1,3 @@
|
||||||
|
<!-- Code generated from the comments of the GlobalRoutingSettings struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
The settings here must match the IP/mask of at least one network_interface supplied to customization.
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- Code generated from the comments of the LinuxOptions struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
- `domain` (string) - The domain name for this machine. This, along with [host_name](#host_name), make up the FQDN of this virtual machine.
|
||||||
|
|
||||||
|
- `host_name` (string) - The host name for this machine. This, along with [domain](#domain), make up the FQDN of this virtual machine.
|
||||||
|
|
||||||
|
- `hw_clock_utc` (boolean) - Tells the operating system that the hardware clock is set to UTC. Default: true.
|
||||||
|
|
||||||
|
- `time_zone` (string) - Sets the time zone. The default is UTC.
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!-- Code generated from the comments of the NetworkInterface struct in builder/vsphere/clone/step_customize.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
- `dns_server_list` ([]string) - Network interface-specific DNS server settings for Windows operating systems.
|
||||||
|
Ignored on Linux and possibly other operating systems - for those systems, please see the [global DNS settings](#global-dns-settings) section.
|
||||||
|
|
||||||
|
- `dns_domain` (string) - Network interface-specific DNS search domain for Windows operating systems.
|
||||||
|
Ignored on Linux and possibly other operating systems - for those systems, please see the [global DNS settings](#global-dns-settings) section.
|
||||||
|
|
||||||
|
- `ipv4_address` (string) - The IPv4 address assigned to this network adapter. If left blank or not included, DHCP is used.
|
||||||
|
|
||||||
|
- `ipv4_netmask` (int) - The IPv4 subnet mask, in bits (example: 24 for 255.255.255.0).
|
||||||
|
|
||||||
|
- `ipv6_address` (string) - The IPv6 address assigned to this network adapter. If left blank or not included, auto-configuration is used.
|
||||||
|
|
||||||
|
- `ipv6_netmask` (int) - The IPv6 subnet mask, in bits (example: 32).
|
Loading…
Reference in New Issue