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 = 'Windows11'
# $vmName = 'Ubuntu-desktop-22.04.1'
# $vmName = 'Debian-11.6.0'
#
# Location of installation ISO file:
#
# $isoFile = "$home\ISOs\Windows10_InsiderPreview_Client_x64_en-us_19035.iso"
# $isoFile = "$home\ISOs\Windows-10.iso"
$isoFile = "$home\ISOs\Win11_24H2_English_x64.iso"
# $isoFile = "$home\ISOs\ubuntu-22.04.1-desktop-amd64.iso"
# $isoFile = "$home\ISOs\debian-11.6.0-amd64-netinst.iso"
if (-not (test-path $isoFile)) {
"$isoFile does not exist"
return
}
$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 = (get-cimInstance win32_physicalMemory | measure-object -property capacity -sum).sum / 1mb / 2 # Half of the total memory
$vramMb = 128 # Must be in range 0 … 256 (Mb) - GUI allows max of 128 only.
#
# Number of CPUs:
#
$nofCPUs = $([System.Environment]::ProcessorCount / 2)
#
# Path of shared folder
#
$sharedFolder = "$home\VirtualBox\sharedFolder"
Remove last creation
I already might have created a
virtual machine with the same name, especially when testing this scenario. So delete this
$registeredVMs = & VBoxManage list vms
if (VBoxManage list vms | select-string \b${vmName}\b) {
write-host "$vmName is registered"
VBoxManage controlvm $vmName poweroff
VBoxManage unregistervm $vmName --delete
}
else {
write-host "$vmName is not registered"
}
if (test-path $vmPath) {
write-host "Removing vm files $vmPath"
rmdir -recurse $vmPath
}
if (test-path $sharedFolder) {
write-host "Removing shared folder $sharedFolder"
rmdir -recurse -force $sharedFolder
}
Detect OS of given .iso
Use VBoxManage detect
to detect the OS type and assign the detected value to the variable $osType
.
# VBoxManage unattended detect --iso=$isoFile
VBoxManage unattended detect --iso=$isoFile | select-string TypeId | foreach-object { $osType = $_ -replace '.* = (.*)', '$1'}
write-host "detected os type $osType"
Change 2024-12-21
Sometimes before 2024-12-21 or Virtual Box Version 7.1.4, the output of this command includes a line with the OS TypeId of the iso's operating system:
...
OS TypeId = WindowsNT_64
...
But this values seemed wrong as --ostype
, when creating the virtual machine definition file, should be Windows10_64
.
This seems to be fixed now.
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
.
If the directory C:\Users\username\VirtualBox VMs
and the subdirectory $vmName
does not exist, it is created.
VBoxManage createvm --name $vmName --ostype $osType --register
if (! (test-path $vmPath\$vmName.vbox)) {
echo "I expected a .vbox"
return
}
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
}
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
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
Enable APIC
VBoxManage modifyvm $vmName --ioapic on
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
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
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
Enable clipboard content sharing
Allow
clipboard content to be shared between host and guest in both directions.
VBoxManage modifyvm $vmName --clipboard-mode bidirectional
VBoxSVGA
Enable the
VBoxSVGA graphic controller . (This step is likely not required as VBoxSVGA should be the default for Windows guests anyway).
VBoxManage modifyvm $vmName --graphicscontroller vboxsvga
Number of CPUs
VBoxManage modifyvm $vmName --cpus $nofCPUs
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'
The last line of the commands output reads
VBoxManage.exe: info: VM ‥ is ready to be started (e.g. VBoxManage startvm) , see
Start he virtual machine below.
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
Remove menus
I don't want any menus in the window that shows the content of the virtual machine.
VBoxManage setextradata $vmName GUI/RestrictedRuntimeMenus ALL
Start the virtual machine
The unattended installation is triggered by starting the unattended installation.
VBoxManage startvm $vmName
Error: Windows cannot read the <ProductKey> setting from the unattend answer file
2024-12-21: With Virtual Box Version 7.1.4, I got the error message Windows cannot read the <ProductKey> setting from the unattend answer file .
As per
this thread , I unsuccessfully tried to insert the following fragment into the
Unattended*autounattend.xml
file under
$vmPath
.
Specify the size of the screen
VBoxManage controlvm $vmName setvideomodehint 1200 900 32
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
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?