Search notes:

VirtualBox: Unattended OS installation with PowerShell

Variables

Before the unattended installation is started, a few variables are defined.
Note the two values for $userName and $fullUserName.
This script must be dot sourced so that the defined variables are set in the current scope as opposed to the child code.
#
# Add Virtual Box bin-path to PATH environment variable if necessary:
#
  if ( (get-command VBoxManage.exe -errorAction silentlyContinue) -eq $null) {
     $env:path="C:\Program Files\Oracle\VirtualBox;$env:path"
  }


# $vmName  = 'Win10Preview'
  $vmName  = 'Windows10'
# $vmName  = 'Ubuntu-desktop-22.04.1'
# $vmName  = 'Debian-11.6.0'

#
# Location of installation ISO file:
#
# $isoFile = "$home\VirtualBox\ISOs\Windows10_InsiderPreview_Client_x64_en-us_19035.iso"
  $isoFile = "$home\VirtualBox\ISOs\Windows-10.iso"
# $isoFile = "$home\VirtualBox\ISOs\ubuntu-22.04.1-desktop-amd64.iso"
# $isoFile = "$home\VirtualBox\ISOs\debian-11.6.0-amd64-netinst.iso"

  if (-not (test-path $isoFile)) {
    "$isoFile does not exist"
     return
  }

#
# VBoxManage.exe unattended detect --iso=$isoFile
#

#
# VBoxManage list ostypes
#
  $osType='Windows10_64'
# $osType='Ubuntu_64'
# $osType='Debian_64'

  $vmPath="$home\VirtualBox VMs\$vmName"

#
# Default user name is vboxuser
#
  $userName     = 'tq84'
  $fullUserName = 'Tee Queue Eighty-Four'

#
# Default password is changeme
#
  $password='theSecret'

#
# Some Memory sizes
#
  $hdSizeMb  = 65536
  $memSizeMb = 16384
  $vramMb    =   128 # Must be in range 0 … 256 (Mb) - GUI allows max of 128 only.

#
# Number of CPUs
#
  $nofCPUs  =     4

#
# Path of shared folder
#
  $sharedFolder = "$home\VirtualBox\sharedFolder"
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/variables.ps1

Remove last creation

I already might have created a virtual machine with the same name, especially when testing this scenario. So delete this
  VBoxManage controlvm    $vmName poweroff
  VBoxManage unregistervm $vmName --delete

  rmdir -recurse $vmPath
# rmdir -recurse $sharedFolder
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/remove-vm.ps1

Detect OS of given .iso

VBoxManage.exe unattended detect --iso=$isoFile
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/detect.ps1
The output of this command includes a line with the OS TypeId of the iso's operating system:
...
    OS TypeId    = WindowsNT_64
...
I'd think I should be using this value for the parameter --ostype when creating the virtual machine definition file, but it turns aout that Windows10_64 is better.
BTW, a list of supported OS types can be shown with
VBoxManage list ostypes

Create the virtual machine definition file

The following step creates a new XML virtual machine definition file with the file extension .vbox.
In this example, the file created is $vmPath\$vmName.vbox.
VBoxManage createvm --name $vmName --ostype $osType --register

if (! (test-path $vmPath\$vmName.vbox)) {
   echo "I expected a .vbox"
   return
}
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/createvm.ps1

Add a hard disk

A hard disk is needed. The --size parameter is in megabytes.
The value of this parameter is set in variables.ps
VBoxManage createmedium --filename $vmPath\hard-drive.vdi --size $hdSizeMb

if (! (test-path $vmPath\hard-drive.vdi)) {
   echo "I expected a .vdi"
   return
}
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/create-hd.ps1
This step creates the .vdi file $vmPath\hard-drive.vdi.

Add SATA controller and attach hard disk to it

The hard disk needs a SATA storage controller to which it can be attached.
VBoxManage storagectl    $vmName --name       'SATA Controller' --add sata --controller IntelAHCI
VBoxManage storageattach $vmName --storagectl 'SATA Controller' --port 0 --device 0 --type hdd --medium $vmPath/hard-drive.vdi
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/create-sata.ps1

Add IDE controller and virtual DVD drive

The .iso file with the OS to be installed needs to be mounted via an IDE controller.
VBoxManage storagectl    $vmName --name       'IDE Controller' --add ide
VBoxManage storageattach $vmName --storagectl 'IDE Controller' --port 0 --device 0 --type dvddrive --medium $isoFile
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/create-ide.ps1

Enable APIC

I/O APIC is enabled for the motherboard of the virtual machine.
VBoxManage modifyvm $vmName --ioapic on
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/enable-apic.ps1

Specify boot order of devices

Configure the boot device order for the VM.
A virtual machine has 4 slots from which it tries to boot from. The following command specifies their order:
VBoxManage modifyvm $vmName --boot1 dvd --boot2 disk --boot3 none --boot4 none
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/boot-device-order.ps1

Memory

Memory is needed by both, the RAM and video processor. Their sizes can be specified in one go with the --memory and --vram options of VBoxManage modifyvm:
VBoxManage modifyvm $vmName --memory $memSizeMb --vram $vramMb
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/allocate-memory.ps1

Specify (host) location of a shared folder

In order to be able to exchange files between the guest and the host, the (already existing) folder on the host is specified:
# $null = mkdir $sharedFolder
  VBoxManage sharedfolder add $vmName --name shr --hostpath $sharedFolder --automount
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/prepare-shared-folder.ps1
See also VBoxManage sharedfolder.

Enable clipboard content sharing

Allow clipboard content to be shared between host and guest in both directions.
VBoxManage modifyvm  $vmName --clipboard-mode bidirectional
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/clipboard-mode.ps1

VBoxSVGA

VBoxManage modifyvm  $vmName --graphicscontroller vboxsvga
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/vboxsvga.ps1

Number of CPUs

Set the number of CPUs:
VBoxManage modifyvm $vmName --cpus $nofCPUs
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/cpus.ps1
The current number of CPUs can be queried like so
VBoxManage VBoxManage showvminfo $vmName --machinereadable | findstr cpus=

Specify an unattended installation

I specify the desire for an unattended installation of the operating system.
The --post-install-command specifies a command to be executed by A:\VBOXPOST.CMD after it has executed E:\vboxadditions\VBoxWindowsAdditions.exe /S.
VBoxManage unattended install $vmName      `
  --iso=$isoFile                           `
  --user=$userName                         `
  --password=$password                     `
  --full-user-name=$fullUserName           `
  --install-additions                      `
  --time-zone=CET                          `
# --post-install-command='VBoxControl guestproperty set installation_finished y'
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/install.ps1
TODO: Among others, the command also emits the following interesting lines which leads to the assumption that the installation can be controlled with more XML-configuration or cmd files:
           scriptTemplatePath = C:\Program Files\Oracle\VirtualBox\UnattendedTemplates\win_nt6_unattended.xml
postInstallScriptTemplatePath = C:\Program Files\Oracle\VirtualBox\UnattendedTemplates\win_postinstall.cmd

List virtual machines

The new virtual machine is shown to be registered.
VBoxManage list vms
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/list-vms.ps1

Remove menus

I don't want any menus in the the window that shows the content of the virtual machine.
VBoxManage setextradata $vmName GUI/RestrictedRuntimeMenus ALL 
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/remove-menues.ps1

Start the virtual machine

The unattended installation is triggered by starting the unattended installation.
VBoxManage startvm $vmName
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/start-vm.ps1

Specify the size of the screen

VBoxManage controlvm $vmName setvideomodehint 1200 900  32
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/video-mode-hint.ps1
I also tried the following commands to specify the screen size, yet without any positive result:
VBoxManage setextradata $vmName CustomVideoMode             1600x900x32
VBoxManage controlvm    $vmName setvideomodehint            1920 1080 32
VBoxManage controlvm    $vmName setscreenlayout primary 0 0 1600 1200 24

Wait for installation to be finished

The following command waits until the specified property is set. It is supposed to be set after the installation of the VirtualBox is finished. However, in my environment, it turned out that the property was always set before Windows was installed. So, the idea is good, but the implementation is imperfect.
VBoxManage guestproperty wait $vmName installation_finished
Github repository about-VirtualBox, path: /command-line/PowerShell/unattended-os-installation/wait-for-finished-installation.ps1
The property that was set is also visible with
VBoxManage guestproperty enumerate $vmName 

TODO

Should ACPI be enabled by default (compare VBoxManage modifyvm --acpi [on|off])
Should nested VT-x/AMD-v be enabled?
Should 3D acceleration be turned on?

Next steps

After installing an OS, its environment might be set up.

See also

VBoxManage unattended

Index