From 5afaff7249a04574d5045b7b2c88c0eaec9f33ea Mon Sep 17 00:00:00 2001 From: leowijnhoven Date: Tue, 8 Sep 2026 08:38:09 +0200 Subject: [PATCH 1/3] add support for storage pool configuration in full clone operations --- builder/proxmox/clone/builder.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/builder/proxmox/clone/builder.go b/builder/proxmox/clone/builder.go index f0d5c466..fe58d471 100644 --- a/builder/proxmox/clone/builder.go +++ b/builder/proxmox/clone/builder.go @@ -191,6 +191,16 @@ func (*cloneVMCreator) Create(vmRef *proxmoxapi.VmRef, vmConfig proxmoxapi.Confi } } + if fullClone == 1 && len(c.Disks) > 0 && c.Disks[0].StoragePool != "" { + if vmConfig.QemuDisks == nil { + vmConfig.QemuDisks = proxmoxapi.QemuDevices{} + } + if vmConfig.QemuDisks[0] == nil { + vmConfig.QemuDisks[0] = make(map[string]interface{}) + } + vmConfig.QemuDisks[0]["storage"] = c.Disks[0].StoragePool + } + err := vmConfig.CloneVm(sourceVmr, vmRef, client) if err != nil { return err From 77e20b9454f79ffa60b66d0df1c7be8e1f91de40 Mon Sep 17 00:00:00 2001 From: leowijnhoven Date: Tue, 8 Sep 2026 14:10:50 +0200 Subject: [PATCH 2/3] add support for clone_storage_pool configuration option for full clones --- builder/proxmox/clone/builder.go | 9 +++++++-- builder/proxmox/clone/config.go | 3 +++ builder/proxmox/clone/config.hcl2spec.go | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/builder/proxmox/clone/builder.go b/builder/proxmox/clone/builder.go index fe58d471..dc8568ed 100644 --- a/builder/proxmox/clone/builder.go +++ b/builder/proxmox/clone/builder.go @@ -191,14 +191,19 @@ func (*cloneVMCreator) Create(vmRef *proxmoxapi.VmRef, vmConfig proxmoxapi.Confi } } - if fullClone == 1 && len(c.Disks) > 0 && c.Disks[0].StoragePool != "" { + storagePool := c.CloneStoragePool + if storagePool == "" && len(c.Disks) > 0 { + storagePool = c.Disks[0].StoragePool + } + if fullClone == 1 && storagePool != "" { if vmConfig.QemuDisks == nil { vmConfig.QemuDisks = proxmoxapi.QemuDevices{} } if vmConfig.QemuDisks[0] == nil { vmConfig.QemuDisks[0] = make(map[string]interface{}) } - vmConfig.QemuDisks[0]["storage"] = c.Disks[0].StoragePool + vmConfig.QemuDisks[0]["storage"] = storagePool + ui.Say(fmt.Sprintf("Using clone storage pool: %s", storagePool)) } err := vmConfig.CloneVm(sourceVmr, vmRef, client) diff --git a/builder/proxmox/clone/config.go b/builder/proxmox/clone/config.go index 021c6890..0b270b3a 100644 --- a/builder/proxmox/clone/config.go +++ b/builder/proxmox/clone/config.go @@ -30,6 +30,9 @@ type Config struct { CloneVMID int `mapstructure:"clone_vm_id" required:"true"` // Whether to run a full or shallow clone from the base clone_vm. Defaults to `true`. FullClone config.Trilean `mapstructure:"full_clone" required:"false"` + // Storage pool to place the cloned source disk on. Only used for full clones. + // Defaults to the storage_pool of the first defined disk if not set. + CloneStoragePool string `mapstructure:"clone_storage_pool" required:"false"` // Set nameserver IP address(es) via Cloud-Init. // If not given, the same setting as on the host is used. diff --git a/builder/proxmox/clone/config.hcl2spec.go b/builder/proxmox/clone/config.hcl2spec.go index 526a7869..a96a1ca9 100644 --- a/builder/proxmox/clone/config.hcl2spec.go +++ b/builder/proxmox/clone/config.hcl2spec.go @@ -126,6 +126,7 @@ type FlatConfig struct { CloneVM *string `mapstructure:"clone_vm" required:"true" cty:"clone_vm" hcl:"clone_vm"` CloneVMID *int `mapstructure:"clone_vm_id" required:"true" cty:"clone_vm_id" hcl:"clone_vm_id"` FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"` + CloneStoragePool *string `mapstructure:"clone_storage_pool" required:"false" cty:"clone_storage_pool" hcl:"clone_storage_pool"` Nameserver *string `mapstructure:"nameserver" required:"false" cty:"nameserver" hcl:"nameserver"` Searchdomain *string `mapstructure:"searchdomain" required:"false" cty:"searchdomain" hcl:"searchdomain"` Ipconfigs []FlatcloudInitIpconfig `mapstructure:"ipconfig" required:"false" cty:"ipconfig" hcl:"ipconfig"` @@ -258,6 +259,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false}, "clone_vm_id": &hcldec.AttrSpec{Name: "clone_vm_id", Type: cty.Number, Required: false}, "full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false}, + "clone_storage_pool": &hcldec.AttrSpec{Name: "clone_storage_pool", Type: cty.String, Required: false}, "nameserver": &hcldec.AttrSpec{Name: "nameserver", Type: cty.String, Required: false}, "searchdomain": &hcldec.AttrSpec{Name: "searchdomain", Type: cty.String, Required: false}, "ipconfig": &hcldec.BlockListSpec{TypeName: "ipconfig", Nested: hcldec.ObjectSpec((*FlatcloudInitIpconfig)(nil).HCL2Spec())}, From 4190d097b6f17da71ed17355a7f4c7b0c9873606 Mon Sep 17 00:00:00 2001 From: leowijnhoven Date: Tue, 8 Sep 2026 15:09:54 +0200 Subject: [PATCH 3/3] add support for target_storage_pool --- builder/proxmox/clone/builder.go | 11 +++++- builder/proxmox/clone/config.go | 7 ++++ builder/proxmox/clone/config.hcl2spec.go | 2 ++ builder/proxmox/clone/config_test.go | 34 +++++++++++++++++++ .../proxmox/clone/Config-not-required.mdx | 4 +++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/builder/proxmox/clone/builder.go b/builder/proxmox/clone/builder.go index f0d5c466..da25d41d 100644 --- a/builder/proxmox/clone/builder.go +++ b/builder/proxmox/clone/builder.go @@ -65,7 +65,16 @@ func (*cloneVMCreator) Create(vmRef *proxmoxapi.VmRef, vmConfig proxmoxapi.Confi fullClone = 0 } vmConfig.FullClone = &fullClone - + if c.TargetStoragePool != "" { + if config.QemuDisks == nil { + config.QemuDisks = make(proxmoxapi.QemuDevices) + } + if config.QemuDisks[0] == nil { + config.QemuDisks[0] = make(proxmoxapi.QemuDevice) + } + config.QemuDisks[0]["storage"] = c.TargetStoragePool + } + // cloud-init options var nameServers []netip.Addr diff --git a/builder/proxmox/clone/config.go b/builder/proxmox/clone/config.go index 021c6890..e972c6a8 100644 --- a/builder/proxmox/clone/config.go +++ b/builder/proxmox/clone/config.go @@ -30,6 +30,10 @@ type Config struct { CloneVMID int `mapstructure:"clone_vm_id" required:"true"` // Whether to run a full or shallow clone from the base clone_vm. Defaults to `true`. FullClone config.Trilean `mapstructure:"full_clone" required:"false"` + // Name of the Proxmox storage pool to store the cloned VM disks on. + // If not given, the source template's storage is used. + // This setting only applies to full clones. + TargetStoragePool string `mapstructure:"target_storage_pool" required:"false"` // Set nameserver IP address(es) via Cloud-Init. // If not given, the same setting as on the host is used. @@ -89,6 +93,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) { if c.CloneVMID != 0 && (c.CloneVMID < 100 || c.CloneVMID > 999999999) { errs = packersdk.MultiErrorAppend(errs, errors.New("clone_vm_id must be in range 100-999999999")) } + if c.FullClone.False() && c.TargetStoragePool != "" { + warnings = append(warnings, "target_storage_pool is only used for full clones and will be ignored when full_clone is false") + } // Check validity of given IP addresses if c.Nameserver != "" { diff --git a/builder/proxmox/clone/config.hcl2spec.go b/builder/proxmox/clone/config.hcl2spec.go index 526a7869..b94be3b4 100644 --- a/builder/proxmox/clone/config.hcl2spec.go +++ b/builder/proxmox/clone/config.hcl2spec.go @@ -126,6 +126,7 @@ type FlatConfig struct { CloneVM *string `mapstructure:"clone_vm" required:"true" cty:"clone_vm" hcl:"clone_vm"` CloneVMID *int `mapstructure:"clone_vm_id" required:"true" cty:"clone_vm_id" hcl:"clone_vm_id"` FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"` + TargetStoragePool *string `mapstructure:"target_storage_pool" required:"false" cty:"target_storage_pool" hcl:"target_storage_pool"` Nameserver *string `mapstructure:"nameserver" required:"false" cty:"nameserver" hcl:"nameserver"` Searchdomain *string `mapstructure:"searchdomain" required:"false" cty:"searchdomain" hcl:"searchdomain"` Ipconfigs []FlatcloudInitIpconfig `mapstructure:"ipconfig" required:"false" cty:"ipconfig" hcl:"ipconfig"` @@ -258,6 +259,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false}, "clone_vm_id": &hcldec.AttrSpec{Name: "clone_vm_id", Type: cty.Number, Required: false}, "full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false}, + "target_storage_pool": &hcldec.AttrSpec{Name: "target_storage_pool", Type: cty.String, Required: false}, "nameserver": &hcldec.AttrSpec{Name: "nameserver", Type: cty.String, Required: false}, "searchdomain": &hcldec.AttrSpec{Name: "searchdomain", Type: cty.String, Required: false}, "ipconfig": &hcldec.BlockListSpec{TypeName: "ipconfig", Nested: hcldec.ObjectSpec((*FlatcloudInitIpconfig)(nil).HCL2Spec())}, diff --git a/builder/proxmox/clone/config_test.go b/builder/proxmox/clone/config_test.go index adc62e41..5af672e1 100644 --- a/builder/proxmox/clone/config_test.go +++ b/builder/proxmox/clone/config_test.go @@ -272,3 +272,37 @@ func TestIpconfig(t *testing.T) { }) } } + +func TestTargetStoragePool(t *testing.T) { + t.Run("target_storage_pool with full clone", func(t *testing.T) { + cfg := mandatoryConfig(t) + cfg["target_storage_pool"] = "local-lvm" + + var c Config + _, warnings, err := c.Prepare(&c, cfg) + if err != nil { + t.Fatalf("unexpected failure: %s", err) + } + if len(warnings) != 0 { + t.Fatalf("unexpected warnings: %v", warnings) + } + }) + + t.Run("target_storage_pool with linked clone warning", func(t *testing.T) { + cfg := mandatoryConfig(t) + cfg["target_storage_pool"] = "local-lvm" + cfg["full_clone"] = false + + var c Config + _, warnings, err := c.Prepare(&c, cfg) + if err != nil { + t.Fatalf("unexpected failure: %s", err) + } + if len(warnings) != 1 { + t.Fatalf("expected one warning, got %d (%v)", len(warnings), warnings) + } + if !strings.Contains(warnings[0], "target_storage_pool") { + t.Fatalf("expected target_storage_pool warning, got %q", warnings[0]) + } + }) +} diff --git a/docs-partials/builder/proxmox/clone/Config-not-required.mdx b/docs-partials/builder/proxmox/clone/Config-not-required.mdx index e16dd563..fec28abf 100644 --- a/docs-partials/builder/proxmox/clone/Config-not-required.mdx +++ b/docs-partials/builder/proxmox/clone/Config-not-required.mdx @@ -2,6 +2,10 @@ - `full_clone` (boolean) - Whether to run a full or shallow clone from the base clone_vm. Defaults to `true`. +- `target_storage_pool` (string) - Name of the Proxmox storage pool to store the cloned VM disks on. + If not given, the source template's storage is used. + This setting only applies to full clones. + - `nameserver` (string) - Set nameserver IP address(es) via Cloud-Init. If not given, the same setting as on the host is used.