Slipstream VirtIO Drivers into Windows ISO to use on Nutanix AHV

This powershell script will use the given Windows Installation ISO and VirtIO ISO to create an Windows ISO with the drivers inserted. You can use this ISO to install a virtual machine on the Nutanix AHV platform.

Here is a video showing how to run this:

And hereby the powershell script:

<#
    This script will generate an ISO file of the given ISO file with the given VirtIO drivers in it. 
    So installing Windows from this ISO will have already the correct drivers in it. 

    The VirtIO drivers can be found here: https://portal.nutanix.com/page/downloads?product=ahv
    
    Version    : 1.0
    Date       : 27 February 2024
    Created by : Jeroen Tielen - Tielen Consultancy B.V.
    Email      : jeroen@tielenconsultancy.nl
  
    History :
         1.0   : 27 February 2024 - Initial setup script.
#>

# Please change these variables to match your files.
$VirtIO_ISO     = "C:\TEMP\Originals\Nutanix-VirtIO-1.2.3.iso"
$Windows_ISO    = "C:\TEMP\Originals\SW_DVD9_Win_Server_STD_CORE_2022__64Bit_English_DC_STD_MLF_X22-74290.ISO"
$DriveLetter    = "Y:" # Temporary drive letter to mount ISOs, please make sure this is an unused driveletter. 

# Checking is this script is running with admin privileges.
If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")) {
    Write-Host "Running with admin privileges, will continue" -ForegroundColor Green
} else {
    Write-Host "Running without admin privileges, please restart script as admin." -ForegroundColor Red
    Exit
}

# Checking if ADK is installed. This is needed to create the bootable iso. 
$OSCDIMG = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
If (Test-Path $OSCDIMG) {
    Write-Host "OSCDIMG is available, will continue..." -ForegroundColor Green
} else {
    Write-Host "Windows Assesment and Deployment KIT is not installed. Installer will run in a couple of seconds."
    Write-Host "This can take a couple of minutes, depending on internet and system performance."
    Invoke-Webrequest -uri https://go.microsoft.com/fwlink/?linkid=2243390 -OutFile "$env:TEMP\adksetup.exe"
    & $ENV:TEMP\adksetup.exe /features OptionId.DeploymentTools /ceip off /norestart /quiet
    While (!(Test-Path $OSCDIMG)) {
        Write-Host "Please wait, install Deployment Tools..."
        Sleep 2
    }
}

# Preparing Folders and cleanup leftovers from previous runs.
Write-Host "Cleaning-up system and leftovers (from previous runs) in the temp directory" -ForegroundColor Green
$CleanUp = DISM /Cleanup-WIM 
$CleanUp = DISM /Get-MountedWIMInfo
If (Test-Path "$ENV:TEMP\Slipstream") { Remove-Item -Path "$ENV:TEMP\Slipstream" -Recurse }

MKDIR "$ENV:TEMP\Slipstream\VirtIO"      # Used to place extracted VirtIO Drivers.
MKDIR "$ENV:TEMP\Slipstream\Windows"     # Used to place extracted Windows ISO.
MKDIR "$ENV:TEMP\Slipstream\Mount_Point" # Used to mount install.wim and boot.wim
MKDIR "$ENV:TEMP\Slipstream\Output"      # Used to place newly created ISO file. 

# Extracting VirtIO drivers to temporary folder.
If (Test-Path $DriveLetter) { 
    Write-Host "An used Drive Letter is choosen, please change the variables at the top of this scripts..." -ForegroundColor Red
    Exit
}

Write-Host "Mounting the VirtIO driver ISO and copying the files to temporary location. (This can open an explorer Window, you can ingnore/close this)" -ForegroundColor Green
$Mount = Mount-DiskImage -ImagePath $VirtIO_ISO -NoDriveLetter
$VolumeInfo = $Mount | Get-Volume
Sleep 1
MountVol $DriveLetter $VolumeInfo.UniqueId
Sleep 5
Copy-Item -Path $DriveLetter\* -Destination "$ENV:TEMP\Slipstream\VirtIO" -Recurse
Sleep 5
Dismount-DiskImage -ImagePath $VirtIO_ISO

# Extracting Windows ISO to temporary folder.
Write-Host "Mounting the Windows ISO and copying the files to temporary location. (This can open an explorer Window, you can ingnore/close this)" -ForegroundColor Green
$Mount = Mount-DiskImage -ImagePath $Windows_ISO -NoDriveLetter
$VolumeInfo = $Mount | Get-Volume
Sleep 1
MountVol $DriveLetter $VolumeInfo.UniqueId
Sleep 5
Copy-Item -Path $DriveLetter\* -Destination "$ENV:TEMP\Slipstream\Windows" -Recurse
Sleep 5
Dismount-DiskImage -ImagePath $Windows_ISO

# As files are copied from Read Only media we need to disable read only on the copied files. 
Write-Host "Disable Read-Only on the copied files." -ForegroundColor Green
Get-ChildItem -Path "$ENV:TEMP\Slipstream" -Recurse -File | % { $_.IsReadOnly=$False }

# Read al indexes from the install.wim and ask which index to use.
$Indexes = Get-WindowsImage -ImagePath "$ENV:TEMP\Slipstream\Windows\sources\install.wim"
Write-Host 
Write-Host "Index list of the provided Windows ISO." -ForegroundColor Yellow
Write-Host "------------------------------------------------------------------"
ForEach ($Index in $Indexes) {
    Write-Host "Index number:" $Index.ImageIndex "-" $Index.ImageName
}
Write-Host
$WorkIndex = Read-Host "Please input the index number (Windows edition) you want to use"

# Strip all indexes except the given one.
Write-Host "Stripping all unused indexes (from top to bottom)." -ForegroundColor Green
$Counter = $Indexes.Count
While ($Counter -ge 1) {
    If ($Counter -ne $WorkIndex) { 
        Write-Host "Please wait removing index :" $Counter "-" $Indexes[($Counter-1)].ImageName
        Remove-WindowsImage -ImagePath "$ENV:TEMP\Slipstream\Windows\sources\install.wim" -Index $Counter -CheckIntegrity -LogLevel 1 >> null
    }
    $Counter = $Counter - 1
}
Write-Host "Only avable index in the ISO:" -ForegroundColor Green
$Index = Get-WindowsImage -ImagePath "$ENV:TEMP\Slipstream\Windows\sources\install.wim"
Write-Host $Index.ImageIndex "-" $Index.ImageName

# Mount the install.wim and install the VirtIO drivers to the given index.
Write-Host "Mounting install.wim to temporary location." -ForegroundColor Green
Mount-WindowsImage -Path "$ENV:TEMP\Slipstream\Mount_Point" -ImagePath "$ENV:TEMP\Slipstream\Windows\sources\install.wim" -Index 1 >> null
Write-Host "Slipstream VirtIO drivers into the mounted install.wim" -ForegroundColor Green
Add-WindowsDriver -Path "$ENV:TEMP\Slipstream\Mount_Point" -Driver "$ENV:TEMP\Slipstream\VirtIO" -Recurse -ForceUnsigned >> null
Write-Host "Installed drivers in the install.wim" -ForegroundColor Green
Get-WindowsDriver -Path "$ENV:TEMP\Slipstream\Mount_Point"
Write-Host "Unmouting install.wim and save changes."-ForegroundColor Green
Dismount-WindowsImage -Path "$ENV:TEMP\Slipstream\Mount_Point" -Save -CheckIntegrity

# Mount boot.wim to add drivers into Windows setup index. The PE index is not needed if not using PXE boot/SCCM. If needed, change the script ;)
Write-Host "Mount boot.wim to add the drivers into setup index." -ForegroundColor Green
Mount-WindowsImage -Path "$ENV:TEMP\Slipstream\Mount_Point" -ImagePath "$ENV:TEMP\Slipstream\Windows\sources\boot.wim" -Index 2 >> null
Write-Host "Slipstream VirtIO drivers into the mounted boot.wim" -ForegroundColor Green
Add-WindowsDriver -Path "$ENV:TEMP\Slipstream\Mount_Point" -Driver  "$ENV:TEMP\Slipstream\VirtIO" -Recurse -ForceUnsigned >> null
Write-Host "Installed drivers in the boot.wim" -ForegroundColor Green
Get-WindowsDriver -Path "$ENV:TEMP\Slipstream\Mount_Point"
Write-Host "Unmouting boot.wim and save changes."-ForegroundColor Green
Dismount-WindowsImage -Path "$ENV:TEMP\Slipstream\Mount_Point" -Save -CheckIntegrity

# Save the files into a new bootable Windows ISO.
Write-Host "Create bootable Windows ISO from the temporary location." -ForegroundColor Green
$BIOSBOOT = "$ENV:TEMP\Slipstream\Windows\boot\etfsboot.com"
$UEFIBOOT = "$ENV:TEMP\Slipstream\Windows\efi\microsoft\boot\efisys.bin"
$ISONAME  = "$ENV:TEMP\Slipstream\Output\" + $Index.ImageName + " - VirtIO.iso"
& $OSCDIMG -m -u2 -udfver102 -bootdata:2#p0,e,b$BIOSBOOT#pEF,e,b$UEFIBOOT "$ENV:TEMP\Slipstream\Windows" "$ISONAME" 2>$null

# Open Explorer with the created ISO. 
Explorer.exe $ENV:TEMP\Slipstream\Output

If someone has the magical powershell code to upload the image, via Rest API, to the Nutanix cluster. Please share it in the comments. The script will be updated with a honourable mention off course 😉

Posts created 113

4 thoughts on “Slipstream VirtIO Drivers into Windows ISO to use on Nutanix AHV

  1. Hi,
    2 things :
    – it’s more simple to create a qcow2 image with packer. Deployment is more quicker in the cluster.
    – if you want to upload the image the first thing is to create an temporary http server
    Open your PowerShell console and create an HTTP listener:
    $httpListener = New-Object System.Net.HttpListener
    Then specify the port to listen. In this example, I want to run an HTTP web server on Port 9090.
    $httpListener.Prefixes.Add(“http://localhost:9090/”)

    If you want to work with me on packer feel free to contact me (95% of the code is done).

    1. Hi Maxime, this post is not for creating an virtual harddisk but for slipstreaming the drivers into de installer iso.
      If you want I can test your packer work and write something about it 😉

      I tried the powershell http listener as well. But faced some issues. This is still on my radar to finalize.

  2. it would be great to publish an article about packer/windows/nutanix.
    How I can contact you ? I will try to have your email via Marouane Boutayeb.

  3. Brilliant, thank you 🙂 I just followed the official Nutanix guide and ended up with a BIOS ISO and was about to figure out how to create an UEFI ISO as well and stumbled upon your script.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top