diff --git a/builder/virtualbox/common/step_attach_isos.go b/builder/virtualbox/common/step_attach_isos.go index 897cdbd06..84241cb7b 100644 --- a/builder/virtualbox/common/step_attach_isos.go +++ b/builder/virtualbox/common/step_attach_isos.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + + "strconv" ) // This step attaches the boot ISO, cd_files iso, and guest additions to the @@ -69,49 +71,50 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult // We have three different potential isos we can attach, so let's // assign each one its own spot so they don't conflict. - var controllerName, device, port string + var controllerName string + var device, port int switch diskCategory { case "boot_iso": // figure out controller path controllerName = "IDE Controller" - port = "0" - device = "1" + port = 0 + device = 1 if s.ISOInterface == "sata" { controllerName = "SATA Controller" - port = "1" - device = "0" + port = 15 + device = 0 } else if s.ISOInterface == "virtio" { controllerName = "VirtIO Controller" - port = "1" - device = "0" + port = 15 + device = 0 } ui.Message("Mounting boot ISO...") case "guest_additions": controllerName = "IDE Controller" - port = "1" - device = "0" + port = 1 + device = 0 if s.GuestAdditionsInterface == "sata" { controllerName = "SATA Controller" - port = "2" - device = "0" + port = 14 + device = 0 } else if s.GuestAdditionsInterface == "virtio" { controllerName = "VirtIO Controller" - port = "2" - device = "0" + port = 14 + device = 0 } ui.Message("Mounting guest additions ISO...") case "cd_files": controllerName = "IDE Controller" - port = "1" - device = "1" + port = 1 + device = 1 if s.ISOInterface == "sata" { controllerName = "SATA Controller" - port = "3" - device = "0" + port = 13 + device = 0 } else if s.ISOInterface == "virtio" { controllerName = "VirtIO Controller" - port = "3" - device = "0" + port = 13 + device = 0 } ui.Message("Mounting cd_files ISO...") } @@ -120,8 +123,8 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult command := []string{ "storageattach", vmName, "--storagectl", controllerName, - "--port", port, - "--device", device, + "--port", strconv.Itoa(port), + "--device", strconv.Itoa(device), "--type", "dvddrive", "--medium", isoPath, } @@ -137,8 +140,8 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult unmountCommand := []string{ "storageattach", vmName, "--storagectl", controllerName, - "--port", port, - "--device", device, + "--port", strconv.Itoa(port), + "--device", strconv.Itoa(device), "--type", "dvddrive", "--medium", "none", } diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 0643370de..cb6660cd2 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -149,6 +149,12 @@ type Config struct { // When set to sata, the drive is attached to an AHCI SATA controller. // When set to virtio, the drive is attached to a VirtIO controller. ISOInterface string `mapstructure:"iso_interface" required:"false"` + // Additional disks to create. Uses `vm_name` as the disk name template and + // appends `-#` where `#` is the position in the array. `#` starts at 1 since 0 + // is the default disk. Each value represents the disk image size in MiB. + // Each additional disk uses the same disk parameters as the default disk. + // Unset by default. + AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false"` // Set this to true if you would like to keep the VM registered with // virtualbox. Defaults to false. KeepRegistered bool `mapstructure:"keep_registered" required:"false"` diff --git a/builder/virtualbox/iso/builder.hcl2spec.go b/builder/virtualbox/iso/builder.hcl2spec.go index f453a721d..ca02f022e 100644 --- a/builder/virtualbox/iso/builder.hcl2spec.go +++ b/builder/virtualbox/iso/builder.hcl2spec.go @@ -135,6 +135,7 @@ type FlatConfig struct { NVMePortCount *int `mapstructure:"nvme_port_count" required:"false" cty:"nvme_port_count" hcl:"nvme_port_count"` HardDriveNonrotational *bool `mapstructure:"hard_drive_nonrotational" required:"false" cty:"hard_drive_nonrotational" hcl:"hard_drive_nonrotational"` ISOInterface *string `mapstructure:"iso_interface" required:"false" cty:"iso_interface" hcl:"iso_interface"` + AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"` SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"` VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` @@ -277,6 +278,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "nvme_port_count": &hcldec.AttrSpec{Name: "nvme_port_count", Type: cty.Number, Required: false}, "hard_drive_nonrotational": &hcldec.AttrSpec{Name: "hard_drive_nonrotational", Type: cty.Bool, Required: false}, "iso_interface": &hcldec.AttrSpec{Name: "iso_interface", Type: cty.String, Required: false}, + "disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false}, "keep_registered": &hcldec.AttrSpec{Name: "keep_registered", Type: cty.Bool, Required: false}, "skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, diff --git a/builder/virtualbox/iso/step_create_disk.go b/builder/virtualbox/iso/step_create_disk.go index b23418d5f..fe2582c65 100644 --- a/builder/virtualbox/iso/step_create_disk.go +++ b/builder/virtualbox/iso/step_create_disk.go @@ -22,31 +22,50 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packersdk.Ui) vmName := state.Get("vmName").(string) - format := "VDI" - path := filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format))) - - command := []string{ - "createhd", - "--filename", path, - "--size", strconv.FormatUint(uint64(config.DiskSize), 10), - "--format", format, - "--variant", "Standard", + + // The main disk and additional disks + diskFullPaths := []string{} + diskSizes := []uint{config.DiskSize} + if len(config.AdditionalDiskSize) == 0 { + // If there are no additional disks, use disk naming as before + diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format)))) + } else { + // If there are additional disks, use consistent naming with numbers + diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, fmt.Sprintf("%s-0.%s", config.VMName, strings.ToLower(format)))) + + for i, diskSize := range config.AdditionalDiskSize { + path := filepath.Join(config.OutputDir, fmt.Sprintf("%s-%d.%s", config.VMName, i+1, strings.ToLower(format))) + diskFullPaths = append(diskFullPaths, path) + diskSizes = append(diskSizes, diskSize) + } } - ui.Say("Creating hard drive...") - err := driver.VBoxManage(command...) - if err != nil { - err := fmt.Errorf("Error creating hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + // Create all required disks + for i := range diskFullPaths { + ui.Say(fmt.Sprintf("Creating hard drive %s with size %d MiB...", diskFullPaths[i], diskSizes[i])) + + command := []string{ + "createhd", + "--filename", diskFullPaths[i], + "--size", strconv.FormatUint(uint64(diskSizes[i]), 10), + "--format", format, + "--variant", "Standard", + } + + err := driver.VBoxManage(command...) + if err != nil { + err := fmt.Errorf("Error creating hard drive: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } } // Add the IDE controller so we can later attach the disk. // When the hard disk controller is not IDE, this device is still used // by VirtualBox to deliver the guest extensions. - err = driver.VBoxManage("storagectl", vmName, "--name", "IDE Controller", "--add", "ide") + err := driver.VBoxManage("storagectl", vmName, "--name", "IDE Controller", "--add", "ide") if err != nil { err := fmt.Errorf("Error creating disk controller: %s", err) state.Put("error", err) @@ -116,21 +135,23 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult discard = "on" } - command = []string{ - "storageattach", vmName, - "--storagectl", controllerName, - "--port", "0", - "--device", "0", - "--type", "hdd", - "--medium", path, - "--nonrotational", nonrotational, - "--discard", discard, - } - if err := driver.VBoxManage(command...); err != nil { - err := fmt.Errorf("Error attaching hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + for i := range diskFullPaths { + command := []string{ + "storageattach", vmName, + "--storagectl", controllerName, + "--port", strconv.FormatUint(uint64(i), 10), + "--device", "0", + "--type", "hdd", + "--medium", diskFullPaths[i], + "--nonrotational", nonrotational, + "--discard", discard, + } + if err := driver.VBoxManage(command...); err != nil { + err := fmt.Errorf("Error attaching hard drive: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } } return multistep.ActionContinue diff --git a/website/content/partials/builder/virtualbox/iso/Config-not-required.mdx b/website/content/partials/builder/virtualbox/iso/Config-not-required.mdx index 95520e65d..e928a4e29 100644 --- a/website/content/partials/builder/virtualbox/iso/Config-not-required.mdx +++ b/website/content/partials/builder/virtualbox/iso/Config-not-required.mdx @@ -103,6 +103,12 @@ When set to sata, the drive is attached to an AHCI SATA controller. When set to virtio, the drive is attached to a VirtIO controller. +- `disk_additional_size` ([]uint) - Additional disks to create. Uses `vm_name` as the disk name template and + appends `-#` where `#` is the position in the array. `#` starts at 1 since 0 + is the default disk. Each value represents the disk image size in MiB. + Each additional disk uses the same disk parameters as the default disk. + Unset by default. + - `keep_registered` (bool) - Set this to true if you would like to keep the VM registered with virtualbox. Defaults to false.