156 lines
5.1 KiB
PowerShell
156 lines
5.1 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Pre-stage VirtIO drivers on a VMware Windows Server VM before migrating to Proxmox (KVM/QEMU).
|
|
|
|
.DESCRIPTION
|
|
- Optionally downloads the latest virtio-win.iso, or uses a provided local ISO path.
|
|
- Mounts the ISO.
|
|
- Injects key drivers into the online Windows driver store using pnputil:
|
|
* Storage: vioscsi + viostor
|
|
* Network: NetKVM
|
|
* Memory Balloon: Balloon
|
|
- (Optional) Adds QEMU guest extras (commented).
|
|
- Verifies presence.
|
|
- Unmounts the ISO.
|
|
|
|
.PARAMETER IsoPath
|
|
Path to an existing virtio-win.iso on disk. If omitted, use -Download to fetch it.
|
|
|
|
.PARAMETER Download
|
|
Download the latest virtio-win.iso from Fedora mirrors.
|
|
|
|
.EXAMPLE
|
|
.\Prep-VirtIODrivers.ps1 -Download
|
|
|
|
.EXAMPLE
|
|
.\Prep-VirtIODrivers.ps1 -IsoPath "C:\Temp\virtio-win.iso"
|
|
|
|
.NOTES
|
|
Run from an elevated PowerShell (Run as Administrator).
|
|
Tested on Windows Server 2025/2022/2019. Uses the 'w11\amd64' folder for 2025.
|
|
#>
|
|
|
|
param(
|
|
[string]$IsoPath,
|
|
[switch]$Download
|
|
)
|
|
|
|
# --- Require admin ---
|
|
$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
|
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
Write-Error "Please run this script as Administrator."
|
|
exit 1
|
|
}
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
# --- Determine working dir ---
|
|
$WorkDir = Join-Path $env:TEMP "virtio-prep"
|
|
New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null
|
|
|
|
# --- Ensure one of IsoPath or Download is set ---
|
|
if (-not $IsoPath -and -not $Download) { $Download = $true }
|
|
|
|
# --- Download logic ---
|
|
if ($Download) {
|
|
try {
|
|
$IsoPath = Join-Path $WorkDir "virtio-win.iso"
|
|
Write-Host "Downloading latest virtio-win.iso..." -ForegroundColor Cyan
|
|
$primary = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win.iso"
|
|
Invoke-WebRequest -Uri $primary -OutFile $IsoPath -UseBasicParsing -TimeoutSec 300
|
|
} catch {
|
|
Write-Warning "Primary download failed, trying Fedora mirror..."
|
|
$fallback = "https://dl.fedoraproject.org/pub/alt/virtio-win/latest-virtio/virtio-win.iso"
|
|
Invoke-WebRequest -Uri $fallback -OutFile $IsoPath -UseBasicParsing -TimeoutSec 300
|
|
}
|
|
Write-Host "Downloaded to $IsoPath" -ForegroundColor Green
|
|
} elseif (-not (Test-Path $IsoPath)) {
|
|
Write-Error "IsoPath not found: $IsoPath"
|
|
exit 1
|
|
}
|
|
|
|
# --- Mount ISO ---
|
|
Write-Host "Mounting ISO..." -ForegroundColor Cyan
|
|
$img = Mount-DiskImage -ImagePath $IsoPath -PassThru
|
|
$vol = $img | Get-Volume
|
|
$cd = ($vol | Select-Object -First 1).DriveLetter + ":"
|
|
Write-Host "Mounted at $cd" -ForegroundColor Green
|
|
|
|
# --- Choose driver root folders
|
|
# Windows Server 2025 uses 'w11\amd64'. Provide a small fallback search if missing.
|
|
$CandidateSubDirs = @(
|
|
"w11\amd64", # Server 2025
|
|
"2k22\amd64", # Server 2022
|
|
"w10\amd64" # Broad fallback
|
|
)
|
|
|
|
function Resolve-DriverPath {
|
|
param(
|
|
[string]$Base,
|
|
[string[]]$Candidates
|
|
)
|
|
foreach ($c in $Candidates) {
|
|
$p = Join-Path $Base $c
|
|
if (Test-Path $p) { return $p }
|
|
}
|
|
return $null
|
|
}
|
|
|
|
$Drivers = @()
|
|
$map = @{
|
|
"vioscsi" = Join-Path $cd "vioscsi"
|
|
"viostor" = Join-Path $cd "viostor"
|
|
"NetKVM" = Join-Path $cd "NetKVM"
|
|
"Balloon" = Join-Path $cd "Balloon"
|
|
}
|
|
|
|
foreach ($k in $map.Keys) {
|
|
$root = $map[$k]
|
|
$path = Resolve-DriverPath -Base $root -Candidates $CandidateSubDirs
|
|
if ($null -eq $path) {
|
|
Write-Warning "Could not find a suitable subfolder for $k under $root"
|
|
} else {
|
|
$Drivers += @{ Name=$k; Path=$path }
|
|
}
|
|
}
|
|
|
|
# --- Install drivers using pnputil (online install into current OS) ---
|
|
Write-Host "`nInstalling VirtIO drivers..." -ForegroundColor Cyan
|
|
foreach ($d in $Drivers) {
|
|
$infGlob = Join-Path $d.Path "*.inf"
|
|
if (Test-Path $d.Path) {
|
|
Write-Host (" -> {0} from {1}" -f $d.Name, $d.Path) -ForegroundColor DarkCyan
|
|
pnputil /add-driver $infGlob /install | Out-Null
|
|
} else {
|
|
Write-Warning "Driver path missing: $($d.Path)"
|
|
}
|
|
}
|
|
|
|
# --- Optional extras (uncomment to include) ---
|
|
# pnputil /add-driver "$cd\qxldod\w11\amd64\*.inf" /install | Out-Null # QXL Display
|
|
# pnputil /add-driver "$cd\vioserial\w11\amd64\*.inf" /install | Out-Null # VirtIO Serial
|
|
# pnputil /add-driver "$cd\pvpanic\w11\amd64\*.inf" /install | Out-Null # pvpanic
|
|
|
|
# --- Verify presence in driver store ---
|
|
Write-Host "`nVerifying driver presence..." -ForegroundColor Cyan
|
|
$enum = (pnputil /enum-drivers) -join "`n"
|
|
foreach ($n in @("vioscsi","viostor","NetKVM","Balloon")) {
|
|
if ($enum -match [Regex]::Escape($n)) {
|
|
Write-Host (" [+] {0} present" -f $n) -ForegroundColor Green
|
|
} else {
|
|
Write-Warning (" [!] {0} NOT found in driver store" -f $n)
|
|
}
|
|
}
|
|
|
|
# --- Unmount ISO ---
|
|
Write-Host "`nUnmounting ISO..." -ForegroundColor Cyan
|
|
Dismount-DiskImage -ImagePath $IsoPath
|
|
|
|
Write-Host "`nAll done." -ForegroundColor Green
|
|
Write-Host @"
|
|
Next steps:
|
|
1) Shut down this VM cleanly and take your Veeam backup/export OR run your migration.
|
|
2) In Proxmox, attach the disks as VirtIO-SCSI and NIC as VirtIO (paravirtualized).
|
|
3) First boot should succeed; if needed, keep virtio-win.iso mounted to install guest tools.
|
|
"@
|