# preflight.ps1 - check the things PESetup fails on, before it fails on them. # # Called by startnet.cmd once the media is mapped. Prints a short report a tech # can read at the bay and exits non-zero if a check is fatal. # # WHY EACH CHECK IS HERE - all four come from PESetup's own behaviour # (docs/PESETUP-INTERNALS.md, decompiled 4.0.0.17, observed on 4.0.0.20): # # Secure boot GatherDataSelection fails the step outright when # SecurebootEnabled != 1. Hard failure, minutes into a build. # Disk size MinRequiredSpaceWithoutCompression is 128849018880 (120 GB). # Driver match GetDriverByModel returns null on a miss and PESetup logs a # WARNING and keeps going. The bay images with no drivers, so no # NIC, so DNS fails at first boot and enrollment cannot reach the # CDN. The symptom appears far from the cause - this is the check # that earns the script. # Media age The media expires 30 days after build. PESetup shows days-left # on a screen nobody reads. # # Lives on the enrollment share so it can be fixed without rebuilding boot.wim. [CmdletBinding()] param( [string]$MediaDrive = 'Z:', [int]$MinDiskGB = 120, [int]$MediaWarnDays = 25 ) $ErrorActionPreference = 'Continue' $fatal = 0 $warn = 0 function Ok { param($m) Write-Host (" [ OK ] " + $m) } function Warn { param($m) Write-Host (" [WARN] " + $m); $script:warn++ } function Fail { param($m) Write-Host (" [FAIL] " + $m); $script:fatal++ } Write-Host "" Write-Host "======== Pre-imaging checks ========" # --- 1. Secure boot ------------------------------------------------------ # Confirm-SecureBootUEFI is not always present in WinPE; read the state the # firmware exposes instead. try { $sb = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' ` -Name UEFISecureBootEnabled -ErrorAction Stop if ($sb.UEFISecureBootEnabled -eq 1) { Ok "Secure boot enabled" } else { Fail "Secure boot is OFF. PESetup will fail at GatherData. Enable it in BIOS." } } catch { Warn "Could not read secure boot state - if this is a legacy/CSM boot, PESetup will fail." } # --- 2. Disk size -------------------------------------------------------- try { $disk = Get-CimInstance Win32_DiskDrive -ErrorAction Stop | Where-Object { $_.MediaType -like '*Fixed*' } | Sort-Object Index | Select-Object -First 1 if ($disk) { $gb = [math]::Round($disk.Size / 1GB, 1) if ($gb -ge $MinDiskGB) { Ok ("Disk 0 is {0} GB ({1})" -f $gb, $disk.Model) } else { Fail ("Disk 0 is only {0} GB; PESetup needs {1} GB. ({2})" -f $gb, $MinDiskGB, $disk.Model) } } else { Fail "No fixed disk found. PESetup has nothing to image." } } catch { Warn "Could not enumerate disks: $_" } # --- 3. Driver match for THIS model -------------------------------------- # Reimplements GetDriverByModel: family filter first (it knows only Latitude, # OptiPlex and Precision), then a substring test of comma-separated tokens, # first match wins. Tokens are NOT trimmed, matching the C#. try { $model = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).Model $catalogue = Join-Path $MediaDrive 'Deploy\Control\HardwareDriver.json' if (-not (Test-Path $catalogue)) { Warn "HardwareDriver.json not found at $catalogue - cannot check drivers." } else { $entries = Get-Content $catalogue -Raw | ConvertFrom-Json $family = '' if ($model.ToUpper().Contains('LATITUDE')) { $family = 'Latitude' } if ($model.ToUpper().Contains('OPTIPLEX')) { $family = 'Optiplex' } if ($model.ToUpper().Contains('PRECISION')) { $family = 'Precision' } $hit = $null foreach ($e in $entries) { if ($family -and ($e.family -notlike "*$family*")) { continue } foreach ($tok in ([string]$e.modelswminame).Split(',')) { if ($tok -and $model.ToLower().Contains($tok.ToLower())) { $hit = $e; break } } if ($hit) { break } } if ($hit) { $zip = Join-Path $MediaDrive (([string]$hit.destinationDir) -replace '\*destinationdir\*\\?','') $zip = Join-Path $zip $hit.fileName if (Test-Path $zip) { Ok ("Driver pack for '{0}': {1}" -f $model, $hit.fileName) } else { Fail ("Driver pack for '{0}' is listed but MISSING on the media: {1}" -f $model, $hit.fileName) } } else { Fail ("NO driver pack matches '{0}'. PESetup logs this as a warning only - the bay will image with NO network drivers." -f $model) } } } catch { Warn "Driver check failed: $_" } # --- 4. Media age -------------------------------------------------------- # Approximate: PESetup expires media 30 days after build and Media.tag is # rewritten when the media is rebuilt, so its timestamp is the best local proxy. try { $tag = Join-Path $MediaDrive 'Deploy\Control\Media.tag' if (Test-Path $tag) { $age = [int]((Get-Date) - (Get-Item $tag).LastWriteTime).TotalDays if ($age -ge $MediaWarnDays) { Warn ("Media is about {0} days old; it expires at 30. Rebuild it soon." -f $age) } else { Ok ("Media is about {0} days old" -f $age) } } } catch { } Write-Host "====================================" if ($fatal -gt 0) { Write-Host "" Write-Host " $fatal BLOCKING problem(s) found. Imaging this bay will not work." Write-Host "" exit 1 } if ($warn -gt 0) { Write-Host " $warn warning(s), no blockers." } else { Write-Host " All checks passed." } Write-Host "" exit 0