diff --git a/m365/diag.ps1 b/m365/diag.ps1 index 1e7c8bb..1918d48 100644 --- a/m365/diag.ps1 +++ b/m365/diag.ps1 @@ -1,43 +1,97 @@ # diag.ps1 - Laptop/Surface diagnose voor Maandag -# Werkt met en zonder admin. Zonder admin worden sommige onderdelen overgeslagen. -# Gebruik: powershell -ep bypass -f diag.ps1 (of start diag.cmd) -# Rapport: map Reports naast het script, anders %TEMP% +# Gebruik: irm https:// | iex (OOBE via Shift+F10 > powershell, of bij gebruiker) +# Werkt met en zonder admin. Zonder admin worden enkele onderdelen overgeslagen en gemeld. +# Rapport: C:\Diag\__.txt + regel in C:\Diag\summary.csv $ErrorActionPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue' -# ---------- helpers ---------- +# ============ helpers ============ function Test-Admin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() (New-Object Security.Principal.WindowsPrincipal $id).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } -function GB($bytes) { if ($bytes) { [math]::Round($bytes / 1GB, 1) } else { 0 } } - +function GB($b) { if ($b) { [math]::Round($b / 1GB, 1) } else { 0 } } $isAdmin = Test-Admin $lines = New-Object System.Collections.Generic.List[string] -$verdict = @{} # onderdeel -> goed / beperkt / slecht / onbekend +$flags = New-Object System.Collections.Generic.List[object] # ROOD / ORANJE / INFO function Add($t = '') { $lines.Add($t) } +function Flag($sev, $txt) { $flags.Add([pscustomobject]@{ Sev = $sev; Txt = $txt }) } +$today = Get-Date -# ---------- systeem ---------- +# ============ systeem ============ $cs = Get-CimInstance Win32_ComputerSystem +$csp = Get-CimInstance Win32_ComputerSystemProduct $bios = Get-CimInstance Win32_BIOS $os = Get-CimInstance Win32_OperatingSystem $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 +$gpu = Get-CimInstance Win32_VideoController | Select-Object -First 1 -$model = $cs.Model -$serial = $bios.SerialNumber -$ramGB = GB $cs.TotalPhysicalMemory -$fw = $env:firmware_type -if (-not $fw) { $fw = 'onbekend' } - -$sbReg = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -$secureBoot = if ($sbReg) { [bool]$sbReg.UEFISecureBootEnabled } else { $null } - +$model = $cs.Model +$sku = $csp.Version +$serial = $bios.SerialNumber +$ramGB = GB $cs.TotalPhysicalMemory +$fw = $env:firmware_type; if (-not $fw) { $fw = 'onbekend' } +$fwVer = $bios.SMBIOSBIOSVersion +$fwDate = $bios.ReleaseDate $osName = $os.Caption $osBuild = $os.BuildNumber $osVer = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').DisplayVersion +$lastBoot = $os.LastBootUpTime +$uptime = if ($lastBoot) { [math]::Round(($today - $lastBoot).TotalDays, 1) } else { $null } +$sbReg = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' +$secureBoot = if ($sbReg) { [bool]$sbReg.UEFISecureBootEnabled } else { $null } -# ---------- accu ---------- +# Surface model > releasejaar (Microsoft geeft doorgaans 6 jaar firmware/driver-support na release) +$surfaceYears = @( + @('Surface Pro, 11th', 2024), @('Surface Pro 10', 2024), @('Surface Pro 9', 2022), @('Surface Pro 8', 2021), + @('Surface Pro 7+', 2021), @('Surface Pro 7', 2019), @('Surface Pro 6', 2018), @('Surface Pro X', 2019), + @('Surface Pro 4', 2015), @('Surface Pro$', 2017), + @('Surface Laptop, 7th', 2024), @('Surface Laptop 6', 2024), @('Surface Laptop 5', 2022), @('Surface Laptop 4', 2021), + @('Surface Laptop 3', 2019), @('Surface Laptop 2', 2018), @('Surface Laptop$', 2017), + @('Surface Laptop Go 3', 2023), @('Surface Laptop Go 2', 2022), @('Surface Laptop Go', 2020), + @('Surface Laptop Studio 2', 2023), @('Surface Laptop Studio', 2021), + @('Surface Go 4', 2023), @('Surface Go 3', 2021), @('Surface Go 2', 2020), @('Surface Go$', 2018), + @('Surface Book 3', 2020), @('Surface Book 2', 2017), @('Surface Book$', 2015), + @('Surface Studio 2\+', 2022), @('Surface Studio 2', 2018) +) +$releaseYear = $null +foreach ($e in $surfaceYears) { if ($model -match "^$($e[0])") { $releaseYear = $e[1]; break } } +$ageYears = if ($releaseYear) { $today.Year - $releaseYear } else { $null } +if ($ageYears -ne $null) { + if ($ageYears -ge 7) { Flag 'ROOD' "Model uit $releaseYear ($ageYears jaar): buiten het Surface-supportwindow, geen firmware/driver-updates meer te verwachten" } + elseif ($ageYears -ge 6) { Flag 'ORANJE' "Model uit $releaseYear ($ageYears jaar): aan het einde van het Surface-supportwindow" } + elseif ($ageYears -ge 4) { Flag 'INFO' "Model uit $releaseYear ($ageYears jaar)" } +} elseif ($model -like 'Surface*') { Flag 'INFO' "Surface-model niet in de jaartabel: $model" } + +if ($fwDate -and ($today - $fwDate).TotalDays -gt 548) { + Flag 'ORANJE' "Firmware van $($fwDate.ToString('yyyy-MM-dd')): ruim 1,5 jaar oud, device krijgt geen updates (meer) of is lang niet bijgewerkt" +} + +# ============ Windows-versie en support ============ +$isEnt = ($osName -match 'Enterprise|Education') +$eolTable = @{ + 'W10-22H2' = @('2025-10-14', '2025-10-14') + 'W11-22H2' = @('2024-10-08', '2025-10-14') + 'W11-23H2' = @('2025-11-11', '2026-11-10') + 'W11-24H2' = @('2026-10-13', '2027-10-12') + 'W11-25H2' = @('2027-10-12', '2028-10-10') +} +$osKey = if ($osName -like '*Windows 11*') { "W11-$osVer" } else { "W10-$osVer" } +$eol = $null +if ($eolTable[$osKey]) { $eol = [datetime]($eolTable[$osKey][ [int]$isEnt ]) } +if ($osName -like '*Windows 10*') { Flag 'ROOD' "Draait nog Windows 10 ($osVer), einde support" } +elseif ($eol -and $eol -lt $today) { Flag 'ORANJE' "Windows 11 $osVer is uit support sinds $($eol.ToString('yyyy-MM-dd')), feature-update nodig" } +elseif ($eol -and ($eol - $today).TotalDays -lt 90) { Flag 'INFO' "Windows 11 $osVer loopt uit support op $($eol.ToString('yyyy-MM-dd'))" } + +$lastHotfix = (Get-HotFix | Where-Object InstalledOn | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn +if ($lastHotfix -and ($today - $lastHotfix).TotalDays -gt 90) { Flag 'ORANJE' "Laatste Windows-update op $($lastHotfix.ToString('yyyy-MM-dd')), meer dan 90 dagen geleden" } + +$lic = Get-CimInstance SoftwareLicensingProduct -Filter "PartialProductKey IS NOT NULL AND Name LIKE 'Windows%'" | Select-Object -First 1 +$activated = if ($lic) { $lic.LicenseStatus -eq 1 } else { $null } +if ($activated -eq $false) { Flag 'INFO' 'Windows is niet geactiveerd' } + +# ============ accu ============ $batDesign = $null; $batFull = $null; $batCycles = $null; $batPct = $null $tmpXml = Join-Path $env:TEMP 'batteryreport.xml' Remove-Item $tmpXml -Force @@ -46,167 +100,216 @@ if (Test-Path $tmpXml) { [xml]$bx = Get-Content $tmpXml $b = $bx.BatteryReport.Batteries.Battery | Select-Object -First 1 if ($b) { - $batDesign = [int]$b.DesignCapacity - $batFull = [int]$b.FullChargeCapacity - $batCycles = [int]$b.CycleCount + $batDesign = [int]$b.DesignCapacity; $batFull = [int]$b.FullChargeCapacity; $batCycles = [int]$b.CycleCount if ($batDesign -gt 0) { $batPct = [math]::Round(100 * $batFull / $batDesign) } } Remove-Item $tmpXml -Force } -if ($batPct -eq $null) { - $verdict.Accu = 'nvt' -} elseif ($batPct -ge 70) { $verdict.Accu = 'goed' } -elseif ($batPct -ge 50) { $verdict.Accu = 'beperkt' } -else { $verdict.Accu = 'slecht' } +$wb = Get-CimInstance Win32_Battery | Select-Object -First 1 +$batStatus = switch ($wb.BatteryStatus) { 1 {'ontladen'} 2 {'op netstroom'} 3 {'vol'} 4 {'laag'} 5 {'kritiek'} 6 {'laden'} 7 {'laden, hoog'} 8 {'laden, laag'} 9 {'laden, kritiek'} default {'onbekend'} } +$batCharge = $wb.EstimatedChargeRemaining +if ($batPct -ne $null) { + if ($batPct -lt 50) { Flag 'ROOD' "Accu op $batPct% van ontwerpcapaciteit, vervangen of afvoeren" } + elseif ($batPct -lt 70) { Flag 'ORANJE' "Accu op $batPct% van ontwerpcapaciteit" } + if ($batCycles -gt 800) { Flag 'ORANJE' "Accu heeft $batCycles laadcycli" } +} elseif ($model -match 'Pro|Laptop|Go|Book') { Flag 'ORANJE' 'Geen accu-informatie beschikbaar op een mobiel device' } -# ---------- schijf ---------- -$diskModel = ''; $diskGB = 0; $diskHealth = 'onbekend'; $diskWear = $null; $diskTemp = $null; $diskPowerOn = $null; $diskStatus = '' +# ============ schijf ============ +$diskModel = ''; $diskGB = 0; $diskHealth = 'onbekend'; $diskMedia = ''; $diskBus = '' +$diskWear = $null; $diskTemp = $null; $diskPowerOn = $null; $diskRErr = $null; $diskWErr = $null $pd = Get-PhysicalDisk | Where-Object { $_.BusType -ne 'USB' } | Sort-Object Size -Descending | Select-Object -First 1 if ($pd) { - $diskModel = $pd.FriendlyName - $diskGB = GB $pd.Size - $diskHealth = "$($pd.HealthStatus)" + $diskModel = $pd.FriendlyName; $diskGB = GB $pd.Size; $diskHealth = "$($pd.HealthStatus)"; $diskMedia = "$($pd.MediaType)"; $diskBus = "$($pd.BusType)" if ($isAdmin) { $rc = $pd | Get-StorageReliabilityCounter - if ($rc) { - $diskWear = $rc.Wear - $diskTemp = $rc.Temperature - $diskPowerOn = $rc.PowerOnHours - } + if ($rc) { $diskWear = $rc.Wear; $diskTemp = $rc.Temperature; $diskPowerOn = $rc.PowerOnHours; $diskRErr = $rc.ReadErrorsUncorrected; $diskWErr = $rc.WriteErrorsUncorrected } } } else { $dd = Get-CimInstance Win32_DiskDrive | Where-Object { $_.InterfaceType -ne 'USB' } | Sort-Object Size -Descending | Select-Object -First 1 - if ($dd) { $diskModel = $dd.Model; $diskGB = GB $dd.Size; $diskStatus = $dd.Status } + if ($dd) { $diskModel = $dd.Model; $diskGB = GB $dd.Size; $diskHealth = $dd.Status } } $sysDrive = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='$($env:SystemDrive)'" $freeGB = GB $sysDrive.FreeSpace - -if ($diskHealth -and $diskHealth -ne 'Healthy' -and $diskHealth -ne 'onbekend') { $verdict.Schijf = 'slecht' } -elseif ($diskStatus -and $diskStatus -ne 'OK') { $verdict.Schijf = 'slecht' } -elseif ($diskWear -ne $null) { - if ($diskWear -ge 80) { $verdict.Schijf = 'slecht' } - elseif ($diskWear -ge 50) { $verdict.Schijf = 'beperkt' } - else { $verdict.Schijf = 'goed' } +if ($diskHealth -notin @('Healthy','OK','onbekend')) { Flag 'ROOD' "Schijf meldt status '$diskHealth'" } +if ($diskWear -ne $null) { + if ($diskWear -ge 80) { Flag 'ROOD' "SSD-slijtage $diskWear%, einde levensduur" } + elseif ($diskWear -ge 50) { Flag 'ORANJE' "SSD-slijtage $diskWear%" } } -elseif ($diskHealth -eq 'Healthy') { $verdict.Schijf = 'goed (basis)' } -else { $verdict.Schijf = 'onbekend' } +if (($diskRErr -gt 0) -or ($diskWErr -gt 0)) { Flag 'ROOD' "Schijf heeft niet-corrigeerbare fouten (lezen $diskRErr, schrijven $diskWErr)" } +if ($diskMedia -eq 'HDD') { Flag 'ORANJE' 'Mechanische harde schijf, traag voor dagelijks gebruik' } +if ($freeGB -lt 15) { Flag 'ORANJE' "Nog maar $freeGB GB vrij op $($env:SystemDrive)" } -# ---------- RAM ---------- -if ($ramGB -ge 15) { $verdict.RAM = 'goed' } -elseif ($ramGB -ge 7) { $verdict.RAM = 'beperkt' } -else { $verdict.RAM = 'slecht' } +# WinSAT-scores (als ooit gedraaid; schaal 1-9.9) +$ws = Get-CimInstance Win32_WinSAT +if ($ws -and $ws.WinSATAssessmentState -eq 1) { + if ($ws.DiskScore -lt 6.5) { Flag 'ORANJE' "WinSAT schijfscore $($ws.DiskScore) (laag)" } + if ($ws.CPUScore -lt 6) { Flag 'ORANJE' "WinSAT CPU-score $($ws.CPUScore) (laag)" } +} -# ---------- TPM ---------- -$tpmPresent = $null; $tpmVersion = '' +# BitLocker (admin) +$blStatus = 'admin vereist' +if ($isAdmin) { + $bl = Get-BitLockerVolume -MountPoint $env:SystemDrive + if ($bl) { $blStatus = "$($bl.ProtectionStatus) ($($bl.VolumeStatus), $($bl.EncryptionMethod))" } else { $blStatus = 'niet beschikbaar' } + if ($bl -and $bl.ProtectionStatus -ne 'On') { Flag 'INFO' 'BitLocker staat niet aan op de systeemschijf' } +} + +# ============ RAM ============ +$dimms = Get-CimInstance Win32_PhysicalMemory +$ramSpeed = ($dimms | Select-Object -First 1).ConfiguredClockSpeed +if ($ramGB -lt 7) { Flag 'ROOD' "$ramGB GB RAM, te weinig" } +elseif ($ramGB -lt 15) { Flag 'ORANJE' "$ramGB GB RAM, krap voor Teams + Office + browser" } + +# ============ TPM / Secure Boot / Win11 ============ +$tpmPresent = $null; $tpmVersion = ''; $tpmReady = $null if ($isAdmin) { $tpm = Get-CimInstance -Namespace root\cimv2\Security\MicrosoftTpm -ClassName Win32_Tpm - if ($tpm) { - $tpmPresent = $true - $tpmVersion = ($tpm.SpecVersion -split ',')[0].Trim() - } else { $tpmPresent = $false } + if ($tpm) { $tpmPresent = $true; $tpmVersion = ($tpm.SpecVersion -split ',')[0].Trim(); $tpmReady = $tpm.IsEnabled_InitialValue -and $tpm.IsActivated_InitialValue } else { $tpmPresent = $false } } - -# ---------- CPU / Windows 11 ---------- -$cpuName = $cpu.Name.Trim() -$cpuOk = $null +$cpuName = $cpu.Name.Trim(); $cpuOk = $null if ($cpuName -match 'Core\(TM\)\s+i[3579]-(\d{4,5})') { - $num = $matches[1] - $gen = if ($num.Length -eq 5) { [int]$num.Substring(0,2) } else { [int]$num.Substring(0,1) } - $cpuOk = $gen -ge 8 -} elseif ($cpuName -match 'Core\(TM\)\s+(Ultra|m3|m5|m7)') { - $cpuOk = ($matches[1] -eq 'Ultra') -} elseif ($cpuName -match 'Ryzen') { - $cpuOk = -not ($cpuName -match 'Ryzen\s+[357]\s+[12]\d{3}') # Ryzen 1000/2000 niet ondersteund -} elseif ($cpuName -match 'Snapdragon|Microsoft SQ') { - $cpuOk = $true -} + $num = $matches[1]; $gen = if ($num.Length -eq 5) { [int]$num.Substring(0,2) } else { [int]$num.Substring(0,1) }; $cpuOk = $gen -ge 8 +} elseif ($cpuName -match 'Core\(TM\)\s+Ultra|Core\s+\d+\s+\d{3}') { $cpuOk = $true } +elseif ($cpuName -match 'Core\(TM\)\s+m[357]') { $cpuOk = $false } +elseif ($cpuName -match 'Ryzen') { $cpuOk = -not ($cpuName -match 'Ryzen\s+[357]\s+[12]\d{3}') } +elseif ($cpuName -match 'Snapdragon|Microsoft SQ') { $cpuOk = $true } +elseif ($cpuName -match 'Pentium|Celeron') { $cpuOk = $null } -$w11Checks = @() -$w11Checks += [pscustomobject]@{ Naam='TPM 2.0'; Ok = if ($tpmPresent -eq $null) { $null } else { ($tpmPresent -and $tpmVersion -like '2.*') } } -$w11Checks += [pscustomobject]@{ Naam='Secure Boot'; Ok = $secureBoot } -$w11Checks += [pscustomobject]@{ Naam='UEFI'; Ok = ($fw -eq 'UEFI') } -$w11Checks += [pscustomobject]@{ Naam='RAM >= 4GB'; Ok = ($ramGB -ge 4) } -$w11Checks += [pscustomobject]@{ Naam='Schijf >= 64GB'; Ok = ($diskGB -ge 64) } -$w11Checks += [pscustomobject]@{ Naam='CPU'; Ok = $cpuOk } +$w11Checks = @( + [pscustomobject]@{ Naam='TPM 2.0'; Ok = if ($tpmPresent -eq $null) { $null } else { ($tpmPresent -and $tpmVersion -like '2.*') } } + [pscustomobject]@{ Naam='Secure Boot'; Ok = $secureBoot } + [pscustomobject]@{ Naam='UEFI'; Ok = ($fw -eq 'UEFI') } + [pscustomobject]@{ Naam='RAM >= 4GB'; Ok = ($ramGB -ge 4) } + [pscustomobject]@{ Naam='Schijf >= 64GB'; Ok = ($diskGB -ge 64) } + [pscustomobject]@{ Naam='CPU'; Ok = $cpuOk } +) +$w11Fail = @($w11Checks | Where-Object { $_.Ok -eq $false }).Count +$w11Unknown = @($w11Checks | Where-Object { $_.Ok -eq $null }).Count +if ($osName -like '*Windows 11*') { $w11Text = 'draait al Windows 11' } +elseif ($w11Fail -gt 0) { $w11Text = 'NIET geschikt'; Flag 'ROOD' "Niet geschikt voor Windows 11: $(($w11Checks | Where-Object { $_.Ok -eq $false }).Naam -join ', ')" } +elseif ($w11Unknown -gt 0) { $w11Text = 'onzeker (zie checks)' } +else { $w11Text = 'geschikt' } +if ($secureBoot -eq $false) { Flag 'ORANJE' 'Secure Boot staat uit' } -$w11Fail = ($w11Checks | Where-Object { $_.Ok -eq $false }).Count -$w11Unknown = ($w11Checks | Where-Object { $_.Ok -eq $null }).Count -if ($osName -like '*Windows 11*') { $verdict.Win11 = 'draait al' } -elseif ($w11Fail -gt 0) { $verdict.Win11 = 'slecht' } -elseif ($w11Unknown -gt 0) { $verdict.Win11 = 'onbekend' } -else { $verdict.Win11 = 'goed' } +# ============ hardware / randapparatuur ============ +$pnpErr = Get-PnpDevice -PresentOnly | Where-Object { $_.Status -eq 'Error' -or $_.Status -eq 'Degraded' } +$touch = Get-PnpDevice -PresentOnly | Where-Object { $_.FriendlyName -match 'touch screen|touchscreen' -and $_.Status -eq 'OK' } +$camera = Get-PnpDevice -PresentOnly -Class Camera,Image | Where-Object Status -eq 'OK' +$typeCover = Get-PnpDevice -PresentOnly | Where-Object { $_.FriendlyName -match 'Type Cover|Surface Keyboard|Surface Pro Keyboard' -and $_.Status -eq 'OK' } +$wifi = Get-NetAdapter -Physical | Where-Object { $_.PhysicalMediaType -match '802.11|Native' -or $_.Name -match 'Wi-?Fi' } | Select-Object -First 1 +$wifiDrv = if ($wifi) { Get-NetAdapterAdvancedProperty -Name $wifi.Name | Out-Null; (Get-NetAdapter -Name $wifi.Name).DriverVersion } else { '' } +$bt = Get-PnpDevice -PresentOnly -Class Bluetooth | Where-Object Status -eq 'OK' | Select-Object -First 1 +$monitors = Get-CimInstance -Namespace root\wmi WmiMonitorID +$res = if ($gpu) { "$($gpu.CurrentHorizontalResolution)x$($gpu.CurrentVerticalResolution)" } else { '' } -# ---------- eindoordeel ---------- -$vals = $verdict.Values -if ($vals -contains 'slecht') { $final = 'AFVOEREN' } -elseif ($vals -contains 'beperkt') { $final = 'BEPERKT BRUIKBAAR' } -elseif ($vals -contains 'onbekend') { $final = 'BRUIKBAAR (admin nodig voor volledige check)' } -else { $final = 'BRUIKBAAR' } +foreach ($d in $pnpErr) { Flag 'ORANJE' "Apparaat met probleem: $($d.FriendlyName) [$($d.Status), code $($d.ProblemCode)]" } +if ($model -like 'Surface*' -and -not $touch) { Flag 'ORANJE' 'Geen werkend touchscreen gevonden (alle Surfaces hebben touch)' } +if ($model -like 'Surface*' -and -not $camera) { Flag 'ORANJE' 'Geen werkende camera gevonden' } +if (-not $wifi) { Flag 'ROOD' 'Geen wifi-adapter gevonden' } +elseif ($wifi.Status -ne 'Up' -and $wifi.Status -ne 'Disconnected') { Flag 'ORANJE' "Wifi-adapter status: $($wifi.Status)" } +if (-not $bt) { Flag 'INFO' 'Geen Bluetooth gevonden' } +if ($model -match 'Surface Pro|Surface Go' -and -not $typeCover) { Flag 'INFO' 'Geen Type Cover aangesloten (niet getest)' } -# ---------- rapport ---------- -$now = Get-Date -Format 'yyyy-MM-dd HH:mm' -Add "==================================================" -Add " LAPTOP DIAGNOSE - $model" -Add " Serienummer: $serial" -Add " Datum: $now Admin: $(if ($isAdmin) {'ja'} else {'nee'})" -Add "==================================================" +# ============ stabiliteit (event log, 30 dagen) ============ +$since = $today.AddDays(-30) +$bsod = @(Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-WER-SystemErrorReporting'; Id=1001; StartTime=$since}).Count +$kp41 = @(Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-Kernel-Power'; Id=41; StartTime=$since}).Count +$whea = @(Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-WHEA-Logger'; StartTime=$since}).Count +$diskEv = @(Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='disk','storahci','stornvme','Ntfs'; Level=1,2; StartTime=$since}).Count +$minidumps = @(Get-ChildItem "$env:SystemRoot\Minidump" -Filter *.dmp).Count +$thermal = @(Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-Kernel-Processor-Power'; Id=37; StartTime=$since}).Count + +if ($bsod -ge 3) { Flag 'ROOD' "$bsod blue screens in 30 dagen" } +elseif ($bsod -ge 1){ Flag 'ORANJE' "$bsod blue screen(s) in 30 dagen" } +if ($kp41 -ge 3) { Flag 'ORANJE' "$kp41 onverwachte uitval/herstarts (Kernel-Power 41) in 30 dagen: accu, oververhitting of crash" } +if ($whea -gt 0) { Flag 'ROOD' "$whea hardwarefouten (WHEA) in 30 dagen: CPU/RAM/PCIe" } +if ($diskEv -gt 0) { Flag 'ORANJE' "$diskEv schijf/NTFS-fouten in event log in 30 dagen" } +if ($thermal -gt 5) { Flag 'ORANJE' "$thermal keer CPU teruggeschakeld (thermisch/power) in 30 dagen" } +if ($minidumps -gt 0 -and $bsod -eq 0) { Flag 'INFO' "$minidumps crashdump(s) aanwezig (ouder dan 30 dagen)" } + +# ============ beheer / identiteit ============ +$dsreg = dsregcmd /status 2>$null +function DsVal($k) { ($dsreg | Where-Object { $_ -match "^\s*$k\s*:" } | Select-Object -First 1) -replace "^\s*$k\s*:\s*", '' } +$aadJoined = DsVal 'AzureAdJoined'; $domainJoined = DsVal 'DomainJoined'; $tenant = DsVal 'TenantName'; $mdmUrl = DsVal 'MdmUrl' +$mdm = if ($mdmUrl) { 'ja (Intune)' } else { 'nee' } +$profiles = @(Get-CimInstance Win32_UserProfile | Where-Object { -not $_.Special }) +$profText = ($profiles | ForEach-Object { "$(Split-Path $_.LocalPath -Leaf) ($(GB $_.Size)GB, laatst $($_.LastUseTime.ToString('yyyy-MM-dd')))" }) -join '; ' +if ($aadJoined -eq 'YES' -and $mdm -eq 'nee') { Flag 'INFO' 'Entra joined maar niet in Intune' } +if ($profiles.Count -gt 0) { Flag 'INFO' "$($profiles.Count) gebruikersprofiel(en) aanwezig, wipe nodig voor hergebruik" } + +# ============ eindoordeel ============ +$red = @($flags | Where-Object Sev -eq 'ROOD'); $orange = @($flags | Where-Object Sev -eq 'ORANJE') +if ($red.Count -gt 0) { $final = 'NIET GOED BRUIKBAAR (zie rode punten: repareren of afvoeren)' } +elseif ($orange.Count -ge 3) { $final = 'TWIJFEL (meerdere oranje punten, beoordeel zelf)' } +elseif ($orange.Count -gt 0) { $final = 'BRUIKBAAR MET KANTTEKENINGEN' } +else { $final = 'GOED BRUIKBAAR' } +if (-not $isAdmin) { $final += ' [zonder admin: SSD-slijtage, TPM en BitLocker niet gecontroleerd]' } + +# ============ rapport ============ +$now = $today.ToString('yyyy-MM-dd HH:mm') +Add "==================================================================" +Add " DIAGNOSE $model" +Add " Serienummer $serial $now admin: $(if ($isAdmin) {'ja'} else {'nee'})" +Add "==================================================================" Add "" -Add " OORDEEL: $final" +Add " OORDEEL: $final" Add "" -Add " Accu ........ $($verdict.Accu)" -Add " Schijf ...... $($verdict.Schijf)" -Add " RAM ......... $($verdict.RAM)" -Add " Windows 11 .. $($verdict.Win11)" +Add " SIGNALEN" +if ($flags.Count -eq 0) { Add " geen" } +foreach ($sev in 'ROOD','ORANJE','INFO') { foreach ($f in ($flags | Where-Object Sev -eq $sev)) { Add (" [{0,-6}] {1}" -f $sev, $f.Txt) } } Add "" -Add "--- Systeem ---" -Add " Model: $model" -Add " Serienummer: $serial" -Add " CPU: $cpuName" -Add " RAM: $ramGB GB" -Add " Firmware: $fw Secure Boot: $(if ($secureBoot -eq $null) {'onbekend'} elseif ($secureBoot) {'aan'} else {'uit'})" -Add " TPM: $(if ($tpmPresent -eq $null) {'admin vereist'} elseif ($tpmPresent) {"aanwezig, versie $tpmVersion"} else {'niet gevonden'})" -Add " Windows: $osName $osVer (build $osBuild)" -Add " Device naam: $env:COMPUTERNAME" +Add "------ Device ------" +Add " Model: $model $(if ($sku) {"($sku)"}) release: $(if ($releaseYear) {"$releaseYear, $ageYears jaar"} else {'onbekend'})" +Add " Serienummer: $serial naam: $env:COMPUTERNAME" +Add " CPU: $cpuName" +Add " GPU: $($gpu.Name) scherm: $res monitoren: $(@($monitors).Count)" +Add " RAM: $ramGB GB $(if ($ramSpeed) {"@ $ramSpeed MHz"}) ($(@($dimms).Count) module(s))" +Add " Firmware: $fw versie $fwVer van $(if ($fwDate) {$fwDate.ToString('yyyy-MM-dd')})" +Add " Secure Boot: $(if ($secureBoot -eq $null) {'onbekend'} elseif ($secureBoot) {'aan'} else {'UIT'})" +Add " TPM: $(if ($tpmPresent -eq $null) {'admin vereist'} elseif ($tpmPresent) {"versie $tpmVersion, gereed: $tpmReady"} else {'NIET gevonden'})" Add "" -Add "--- Accu ---" -if ($batPct -eq $null) { - Add " Geen accu gevonden (of rapport niet beschikbaar)" -} else { - Add " Ontwerpcapaciteit: $batDesign mWh" - Add " Huidige capaciteit: $batFull mWh ($batPct %)" - Add " Laadcycli: $batCycles" -} +Add "------ Windows ------" +Add " Versie: $osName $osVer (build $osBuild) support tot: $(if ($eol) {$eol.ToString('yyyy-MM-dd')} else {'onbekend'})" +Add " Activatie: $(if ($activated -eq $null) {'onbekend'} elseif ($activated) {'geactiveerd'} else {'NIET geactiveerd'})" +Add " Laatste update: $(if ($lastHotfix) {$lastHotfix.ToString('yyyy-MM-dd')} else {'onbekend'}) uptime: $uptime dagen" +Add " Windows 11: $w11Text" +foreach ($c in $w11Checks) { $s = if ($c.Ok -eq $null) {'?'} elseif ($c.Ok) {'OK'} else {'NEE'}; Add (" {0,-16} {1}" -f $c.Naam, $s) } +Add " BitLocker: $blStatus" Add "" -Add "--- Schijf ---" -Add " Model: $diskModel" -Add " Grootte: $diskGB GB Vrij op $($env:SystemDrive): $freeGB GB" -Add " Health: $diskHealth $(if ($diskStatus) {"/ $diskStatus"})" -if ($diskWear -ne $null) { - Add " Slijtage: $diskWear % Temperatuur: $diskTemp C Uren aan: $diskPowerOn" -} elseif (-not $isAdmin) { - Add " Slijtage: admin vereist" -} +Add "------ Accu ------" +if ($batPct -eq $null) { Add " Geen accu-informatie" } else { +Add " Capaciteit: $batFull / $batDesign mWh = $batPct %" +Add " Laadcycli: $batCycles status nu: $batStatus, $batCharge %" } Add "" -Add "--- Windows 11 geschiktheid ---" -foreach ($c in $w11Checks) { - $s = if ($c.Ok -eq $null) { '?' } elseif ($c.Ok) { 'OK' } else { 'NEE' } - Add (" {0,-16} {1}" -f $c.Naam, $s) -} -if ($cpuOk -eq $null) { Add " (CPU niet automatisch herkend, handmatig controleren)" } +Add "------ Schijf ------" +Add " Model: $diskModel ($diskMedia, $diskBus)" +Add " Grootte: $diskGB GB vrij op $($env:SystemDrive): $freeGB GB" +Add " Health: $diskHealth" +if ($diskWear -ne $null) { Add " Slijtage: $diskWear % temp $diskTemp C uren aan $diskPowerOn fouten R/W $diskRErr/$diskWErr" } +elseif (-not $isAdmin) { Add " Slijtage: admin vereist" } +if ($ws -and $ws.WinSATAssessmentState -eq 1) { Add " WinSAT: CPU $($ws.CPUScore) RAM $($ws.MemoryScore) schijf $($ws.DiskScore) GPU $($ws.GraphicsScore)" } Add "" -Add "--- Toelichting oordeel ---" -Add " Accu: >=70% goed, 50-70% beperkt, <50% slecht" -Add " Schijf: slijtage <50% goed, 50-80% beperkt, >80% slecht; unhealthy = slecht" -Add " RAM: 16GB goed, 8GB beperkt, minder slecht" -Add " Win11: alle checks OK = goed, een NEE = slecht" +Add "------ Hardware ------" +Add " Touchscreen: $(if ($touch) {'OK'} else {'niet gevonden'}) camera: $(if ($camera) {'OK'} else {'niet gevonden'}) Bluetooth: $(if ($bt) {'OK'} else {'niet gevonden'})" +Add " Wifi: $(if ($wifi) {"$($wifi.InterfaceDescription), $($wifi.Status), driver $wifiDrv"} else {'NIET gevonden'})" +Add " Type Cover: $(if ($typeCover) {'aangesloten'} else {'niet aangesloten'})" +Add " PnP-fouten: $(@($pnpErr).Count)" +Add "" +Add "------ Stabiliteit (30 dagen) ------" +Add " Blue screens: $bsod onverwachte uitval: $kp41 WHEA: $whea schijffouten: $diskEv thermisch: $thermal dumps: $minidumps" +Add "" +Add "------ Beheer ------" +Add " Entra joined: $aadJoined domain joined: $domainJoined tenant: $tenant" +Add " Intune (MDM): $mdm" +Add " Profielen: $(if ($profiles.Count) {$profText} else {'geen'})" +Add "" +Add "------ Niet getest (handmatig of via Surface Diagnostic Toolkit) ------" +Add " Toetsenbord/trackpad, pixelfouten, scharnier/behuizing, luidspreker/microfoon, poorten, pen" Add "" -# ---------- opslaan ---------- -$safeModel = ($model -replace '[^\w\-]', '_') -$safeSerial = ($serial -replace '[^\w\-]', '_') -$fileName = "{0}_{1}_{2}.txt" -f $safeModel, $safeSerial, (Get-Date -Format 'yyyyMMdd-HHmm') - -# Script draait via "irm url | iex", dus geen scriptmap. Probeer C:\Diag, anders Desktop, anders TEMP. +# ============ opslaan ============ +$safeModel = ($model -replace '[^\w\-]', '_'); $safeSerial = ($serial -replace '[^\w\-]', '_') +$fileName = "{0}_{1}_{2}.txt" -f $safeModel, $safeSerial, $today.ToString('yyyyMMdd-HHmm') $outDir = $null foreach ($try in @('C:\Diag', [Environment]::GetFolderPath('Desktop'), $env:TEMP)) { if (-not $try) { continue } @@ -216,10 +319,11 @@ foreach ($try in @('C:\Diag', [Environment]::GetFolderPath('Desktop'), $env:TEMP $outFile = Join-Path $outDir $fileName $lines | Set-Content $outFile -Encoding UTF8 -$lines | ForEach-Object { Write-Host $_ } -Write-Host "Rapport opgeslagen: $outFile" -ForegroundColor Cyan +# samenvattingsregel voor vergelijken over meerdere devices +$csv = Join-Path $outDir 'summary.csv' +if (-not (Test-Path $csv)) { 'Datum;Model;Serienummer;Oordeel;Rood;Oranje;AccuPct;Cycli;SSDwear;RAMGB;DiskGB;CPU;Windows;Win11;Release;Intune' | Set-Content $csv -Encoding UTF8 } +"$now;$model;$serial;$($final -replace '\s{2,}.*$','');$($red.Count);$($orange.Count);$batPct;$batCycles;$diskWear;$ramGB;$diskGB;$cpuName;$osName $osVer;$w11Text;$releaseYear;$mdm" | Add-Content $csv -Encoding UTF8 -# Bij een gewone gebruiker (niet OOBE/SYSTEM): rapport openen zodat hij het kan doorsturen -if ($env:USERNAME -ne 'SYSTEM' -and $env:USERNAME -notlike '*$') { - Start-Process notepad.exe $outFile -} \ No newline at end of file +$lines | ForEach-Object { Write-Host $_ } +Write-Host "Rapport: $outFile" -ForegroundColor Cyan +if ($env:USERNAME -ne 'SYSTEM' -and $env:USERNAME -notlike '*$') { Start-Process notepad.exe $outFile } \ No newline at end of file