P01 — Triage & Qualification de Panne
Qualification rapide d'une panne poste Windows via les données du script de triage P01. Identifie la couche fautive, évalue l'impact et route vers le profil spécialisé le plus pertinent. Point d'entrée obligatoire de la chaîne diagnostique.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p01-triage-qualification-de-panne_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : Version OS · Config matérielle · Perf instantanée CPU/RAM/disque · Réseau/passerelle · TPM/SecureBoot/BitLocker · Spooler · Event IDs crashes, disque, réseau, applicatifs, boot/GPO, GPU/TDR, logon, Windows Update
Données à saisir manuellement : Symptômes précis observés par l'utilisateur · Nombre de personnes impactées · Tests N1 déjà effectués · Numéro de ticket · SLA applicable
# ════════════════════════════════════════════════════════════════════
# COLLECTE TRIAGE & QUALIFICATION DE PANNE v1.1
# Périmètre : signaux multi-domaines pour routing vers profil spécialisé
#
# Profil cible : P01 — Triage & Qualification de Panne
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
#
# Modifications v1.1 :
# - [MOD-P01-1] Détection TDR GPU via WER BugCheck (codes 0x116/0x117/0x141)
# en complément de l'ID 4101 Microsoft-Windows-Display
# - [MOD-P01-2] Résumé crashes enrichi : EventName + codes BugCheck extraits du message WER
# - [MOD-P01-3] Résumé AppTermFailure : fallback sur EventName si nom applicatif vide
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720 # plafond 30 jours
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir = Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p01-triage-qualification-de-panne_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (config, perf instantanée, état services)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte configuration système..."
# OS
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# CPU
try { $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 }
catch { $cpu = $null }
# RAM + charge
try {
$cpu1 = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average
Start-Sleep -Seconds 1
$cpu2 = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average
$cpuUsage = [math]::Round(($cpu1 + $cpu2) / 2, 1)
} catch { $cpuUsage = 'N/A' }
try {
$osPerf = Get-CimInstance Win32_OperatingSystem
$ramTotalMB = [math]::Round($osPerf.TotalVisibleMemorySize / 1KB)
$ramDispoMB = [math]::Round($osPerf.FreePhysicalMemory / 1KB)
$ramPct = [math]::Round((($ramTotalMB - $ramDispoMB) / $ramTotalMB) * 100, 1)
} catch { $ramTotalMB = 'N/A'; $ramDispoMB = 'N/A'; $ramPct = 'N/A' }
# Type équipement
try {
$cs = Get-CimInstance Win32_ComputerSystem
$pcTypeCode = $cs.PCSystemType
$pcTypeLabel = switch ($pcTypeCode) {
1 { "Poste de bureau" }
2 { "Portable" }
4 { "Serveur d'entreprise" }
7 { "Poste de travail" }
default { "Inconnu (code $pcTypeCode)" }
}
} catch { $cs = $null; $pcTypeLabel = 'N/A' }
# Disques physiques
try { $disquesPhysiques = Get-CimInstance Win32_DiskDrive | Select-Object Model, Size, MediaType, SerialNumber }
catch { $disquesPhysiques = $null }
# Volumes logiques et espace libre
try {
$volumes = Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 } |
Select-Object DeviceID, VolumeName,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 1)}},
@{N='LibreGB'; E={[math]::Round($_.FreeSpace / 1GB, 1)}},
@{N='LibrePct'; E={[math]::Round(($_.FreeSpace / $_.Size) * 100, 1)}}
} catch { $volumes = $null }
# Alertes espace disque
$alertesEspace = @()
if ($volumes) {
foreach ($v in $volumes) {
if ($v.LibrePct -lt 10) {
$alertesEspace += "⚠️ ALERTE CRITIQUE : $($v.DeviceID) espace < 10% ($($v.LibreGB) Go libres)"
} elseif ($v.LibrePct -lt 20) {
$alertesEspace += "⚠️ ATTENTION : $($v.DeviceID) espace faible < 20% ($($v.LibreGB) Go libres)"
}
}
}
Write-Host "[2/8] Collecte réseau et services..."
# Adaptateurs réseau
try { $adaptateurActif = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | Select-Object Name, InterfaceDescription, LinkSpeed, MacAddress }
catch { $adaptateurActif = $null }
# Connectivité passerelle
try {
$passerelle = (Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.DefaultIPGateway } | Select-Object -First 1).DefaultIPGateway | Select-Object -First 1
if ($passerelle) {
$pingGW = Test-NetConnection -ComputerName $passerelle -WarningAction SilentlyContinue
$gwOK = if ($pingGW.PingSucceeded) { "✅ OK ($passerelle)" } else { "⚠️ ECHEC ping passerelle ($passerelle)" }
} else {
$gwOK = "⚠️ Aucune passerelle configurée"
}
} catch { $gwOK = "Non disponible (erreur réseau : $($_.Exception.Message))" }
# Plan d'alimentation actif
try {
$planAlim = powercfg /getactivescheme 2>$null
} catch { $planAlim = "Non disponible" }
# État Spooler (signal P13)
try { $spooler = Get-Service -Name Spooler | Select-Object Status, StartType }
catch { $spooler = $null }
Write-Host "[3/8] Collecte TPM et Secure Boot..."
# TPM
try { $tpm = Get-Tpm | Select-Object TpmPresent, TpmReady, TpmEnabled, TpmActivated, ManufacturerVersion }
catch { $tpm = $null }
# Secure Boot
try { $secureBoot = Confirm-SecureBootUEFI }
catch { $secureBoot = "Non disponible (BIOS Legacy ou droits insuffisants)" }
# BitLocker synthétique (signal P08/P11)
try { $bitlocker = Get-BitLockerVolume | Select-Object MountPoint, VolumeStatus, ProtectionStatus, EncryptionMethod }
catch { $bitlocker = $null }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ÉVÉNEMENTS ADAPTATIFS
# ════════════════════════════════════════════════════════════════════
Write-Host "[4/8] Collecte événements crashes/instabilités (signal P02/P03)..."
# IDs : 41,6008 (arrêt brutal) | 1001 (BugCheck/WER) | 7023,7031,7034 (services crashés) | 2019,2020 (pool mémoire) | 4,219 (pilotes)
$resCrashes = Get-EventsAdaptatif -LogNames @('System','Application') -Ids @(41, 1001, 6008, 7023, 7031, 7034, 2019, 2020, 4, 219) -Label 'Crashes' -MinEvents 5 -MaxEvents 100
Write-Host "[5/8] Collecte événements disque/I/O (signal P04)..."
# IDs : 7,9,11,15,51 (bad block/timeout) | 55,98,140 (NTFS) | 129,153,157 (Storport)
$resDisque = Get-EventsAdaptatif -LogNames 'System' -Ids @(7, 9, 11, 15, 51, 55, 98, 129, 140, 153, 157) -Label 'Disque' -MinEvents 5 -MaxEvents 100
Write-Host "[6/8] Collecte événements réseau/services (signal P05/P09)..."
# IDs : 7001 (service start fail), 7034 (service crash), 7036 (service state), 5719 (Netlogon), 4202 (WLAN)
$resReseau = Get-EventsAdaptatif -LogNames 'System' -Ids @(7001, 7034, 7036, 5719, 4202) -Label 'Réseau/Services' -MinEvents 8 -MaxEvents 100
Write-Host "[7/8] Collecte événements applicatifs/boot/GPO/sécurité (signal P02/P06/P07/P08/P09/P11/P12/P13)..."
# Application crashes (signal P06) — IDs 1000,1001,1002 (WER crash/hang)
$resAppCrash = Get-EventsAdaptatif -LogNames 'Application' -Ids @(1000, 1001, 1002) -Label 'App Crashes' -MinEvents 5 -MaxEvents 80
# Boot : 100,101,102,103,109 | Profil : 1511,1515,1500,1502,1508,1509,1530 | GPO/DC : 1085,1006,1030,1058,1129
$resBootGPO = Get-EventsAdaptatif `
-LogNames @('System', 'Microsoft-Windows-GroupPolicy/Operational',
'Microsoft-Windows-User Profile Service/Operational',
'Microsoft-Windows-Diagnostics-Performance/Operational') `
-Ids @(100, 101, 102, 103, 109,
1511, 1515, 1500, 1502, 1508, 1509, 1530,
1085, 1006, 1030, 1058, 1129) `
-Label 'Boot/Profil/GPO' -MinEvents 5 -MaxEvents 100
# GPU/TDR principal (signal P07) — ID 4101 Microsoft-Windows-Display
try {
$resGPU = Get-EventsAdaptatif -LogNames 'Microsoft-Windows-Display' -Ids @(4101) -Label 'GPU/TDR' -MinEvents 3 -MaxEvents 30
} catch {
$resGPU = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'GPU/TDR' }
}
# [MOD-P01-1] GPU/TDR complémentaire — codes BugCheck dans les ID 1001 WER déjà collectés
# Codes ciblés : 0x116 VIDEO_TDR_FAILURE | 0x117 VIDEO_TDR_TIMEOUT_DETECTED
# 0x141 VIDEO_ENGINE_TIMEOUT_DETECTED | 0x1cc VIDEO_SCHEDULER_INTERNAL_ERROR
$resGPU_WER = @()
if ($resCrashes.Events) {
$resGPU_WER = @($resCrashes.Events | Where-Object {
$_.Message -match '(LiveKernelEvent|BlueScreen)' -and
$_.Message -match '\bP1\s*:\s*(116|117|141|1cc)\b'
})
}
$tdrWERCount = $resGPU_WER.Count
# Sécurité logon (signal P11) — ID 4625, 4648, 4720, 4728
try {
$resSec = Get-EventsAdaptatif -LogNames 'Security' -Ids @(4625, 4648, 4720, 4728) -Label 'Echecs logon/Secu' -MinEvents 5 -MaxEvents 60
} catch {
$resSec = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Echecs logon' }
}
# Windows Update (signal P12) — IDs 19, 20, 24, 25, 31
try {
$resWU = Get-EventsAdaptatif -LogNames 'Microsoft-Windows-WindowsUpdateClient/Operational' `
-Ids @(19, 20, 24, 25, 31) -Label 'Windows Update' -MinEvents 5 -MaxEvents 60
} catch {
$resWU = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Windows Update' }
}
Write-Host "[8/8] Construction du rapport..."
# ── Alertes de routing synthétiques ──────────────────────────────────
$alerte_CPU = if ($cpuUsage -ne 'N/A' -and $cpuUsage -gt 85) { "⚠️ ALERTE : CPU ELEVE ($cpuUsage %) → signal P02" } else { "✅ CPU OK ($cpuUsage %)" }
$alerte_RAM = if ($ramPct -ne 'N/A' -and $ramPct -gt 90) { "⚠️ ALERTE : RAM SATUREE ($ramPct %) → signal P02" } else { "✅ RAM OK ($ramPct %)" }
$alerte_crashes = if ($resCrashes.Count -gt 0) { "⚠️ $($resCrashes.Count) événement(s) crash/instabilité/service/mémoire → signal P02/P03" } else { "✅ Aucun crash détecté" }
$alerte_disque = if ($resDisque.Count -gt 5) { "⚠️ $($resDisque.Count) événement(s) disque/I/O → signal P04" } else { "✅ Peu d'événements disque ($($resDisque.Count))" }
$alerte_reseau = if ($resReseau.Count -gt 10) { "⚠️ $($resReseau.Count) événement(s) réseau/services → signal P05/P09" } else { "✅ Peu d'événements réseau ($($resReseau.Count))" }
$alerte_appCr = if ($resAppCrash.Count -gt 0) { "⚠️ $($resAppCrash.Count) crash(s) applicatif(s) → signal P06" } else { "✅ Aucun crash applicatif" }
$alerte_sec = if ($resSec.Count -gt 5) { "⚠️ $($resSec.Count) événement(s) sécurité/logon → signal P11" } else { "✅ Peu d'événements sécurité ($($resSec.Count))" }
$alerte_wu = if ($resWU.Count -gt 0) { "⚠️ $($resWU.Count) événement(s) WU en erreur → signal P12" } else { "✅ Aucune erreur Windows Update" }
$alerte_spooler = if ($spooler -and $spooler.Status -ne 'Running') { "⚠️ Spooler arrêté → signal P13" } else { "✅ Spooler OK" }
$alerte_gw = if ($gwOK -match "ECHEC|Aucune") { "⚠️ Passerelle non joignable → signal P05" } else { "✅ Passerelle OK" }
$alerteBootGPO = if ($resBootGPO.Count -gt 3) { "⚠️ $($resBootGPO.Count) événement(s) boot/profil/GPO → signal P02/P09" } else { "✅ Peu d'événements boot/GPO ($($resBootGPO.Count))" }
# [MOD-P01-1] Alerte GPU enrichie : ID 4101 + BugCheck WER
$alerte_gpu = if ($resGPU.Count -gt 0 -or $tdrWERCount -gt 0) {
$detail = @()
if ($resGPU.Count -gt 0) { $detail += "ID4101×$($resGPU.Count)" }
if ($tdrWERCount -gt 0) { $detail += "WER-BugCheck×$tdrWERCount" }
"⚠️ TDR GPU détecté(s) : $($detail -join ' + ') → signal P07"
} else { "✅ Aucun TDR GPU" }
# ── En-tête du rapport ───────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT TRIAGE & QUALIFICATION DE PANNE v1.1 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p01-triage-qualification-de-panne.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Type : $pcTypeLabel
CPU : $(if ($cpu) { $cpu.Name } else { 'N/A' })
RAM : $ramTotalMB Mo total / $ramDispoMB Mo libres ($ramPct % utilisée)
Disques : $(if ($disquesPhysiques) { ($disquesPhysiques | ForEach-Object { $_.Model }) -join ' | ' } else { 'N/A' })
ALERTES DE ROUTING — SIGNAUX DÉTECTÉS
|- CPU : $alerte_CPU
|- RAM : $alerte_RAM
|- Espace disque: $(if ($alertesEspace) { $alertesEspace -join ' | ' } else { '✅ OK' })
|- Crashes/BSOD : $alerte_crashes
|- Disque/I/O : $alerte_disque
|- Réseau/Svc : $alerte_reseau
|- App crashes : $alerte_appCr
|- Boot/GPO : $alerteBootGPO
|- GPU/TDR : $alerte_gpu
|- Logon/Sécu : $alerte_sec
|- Sécurité+ : 4648/4720/4728 inclus dans bloc Logon/Sécu
|- Windows Upd : $alerte_wu
|- Impression : $alerte_spooler
|- Passerelle : $alerte_gw
FENÊTRES RETENUES PAR BLOC
|- Crashes/instabilités : $($resCrashes.WindowHours) h -> $($resCrashes.Count) événements
|- Disque/I/O : $($resDisque.WindowHours) h -> $($resDisque.Count) événements
|- Réseau/Services : $($resReseau.WindowHours) h -> $($resReseau.Count) événements
|- App Crashes : $($resAppCrash.WindowHours) h -> $($resAppCrash.Count) événements
|- Boot/Profil/GPO : $($resBootGPO.WindowHours) h -> $($resBootGPO.Count) événements
|- GPU/TDR (ID4101) : $($resGPU.WindowHours) h -> $($resGPU.Count) événements
|- GPU/TDR (WER BugCheck): fenêtre crashes -> $tdrWERCount événements
|- Echecs logon : $($resSec.WindowHours) h -> $($resSec.Count) événements
|- Windows Update : $($resWU.WindowHours) h -> $($resWU.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Configuration matérielle ─────────────────────────────
"`n── CONFIGURATION MATÉRIELLE ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
try {
$csDetail = Get-CimInstance Win32_ComputerSystem |
Select-Object Manufacturer, Model, TotalPhysicalMemory,
@{N='RAM_Go'; E={[math]::Round($_.TotalPhysicalMemory / 1GB, 1)}}
$csDetail | Format-List | Out-File $outputFile -Append -Encoding UTF8
} catch { " Non disponible (WMI non accessible)" | Out-File $outputFile -Append -Encoding UTF8 }
"`n Disques physiques :" | Out-File $outputFile -Append -Encoding UTF8
if ($disquesPhysiques) {
$disquesPhysiques | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
"`n Volumes logiques :" | Out-File $outputFile -Append -Encoding UTF8
if ($volumes) {
$volumes | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Performances instantanées ────────────────────────────
"`n── PERFORMANCES INSTANTANÉES ───────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" CPU : $cpuUsage % $alerte_CPU" | Out-File $outputFile -Append -Encoding UTF8
" RAM : $ramPct % $alerte_RAM" | Out-File $outputFile -Append -Encoding UTF8
try {
$diskPerf = Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk |
Where-Object { $_.Name -eq '_Total' }
$diskQueue = if ($diskPerf) { [math]::Round($diskPerf.AvgDiskQueueLength, 2) } else { 'N/A' }
" I/O disque : queue=$diskQueue $(if ($diskQueue -ne 'N/A' -and $diskQueue -gt 2) { '⚠️ ALERTE disque lent → signal P04' } else { '✅ OK' })" |
Out-File $outputFile -Append -Encoding UTF8
} catch { " I/O disque : Non disponible (CIM PerfDisk)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : Réseau et connectivité ───────────────────────────────
"`n── RÉSEAU ET CONNECTIVITÉ ──────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($adaptateurActif) {
$adaptateurActif | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun adaptateur actif détecté" | Out-File $outputFile -Append -Encoding UTF8 }
" Connectivité passerelle : $gwOK" | Out-File $outputFile -Append -Encoding UTF8
try {
$ipConfig = Get-CimInstance Win32_NetworkAdapterConfiguration |
Where-Object { $_.IPEnabled -eq $true } |
Select-Object Description, IPAddress, DefaultIPGateway, DNSServerSearchOrder, DHCPEnabled
"`n Configuration IP :" | Out-File $outputFile -Append -Encoding UTF8
$ipConfig | Format-List | Out-File $outputFile -Append -Encoding UTF8
} catch { " Configuration IP : Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 4 : Plan d'alimentation, TPM, Secure Boot, BitLocker ─────
"`n── FIRMWARE / SÉCURITÉ PLATEFORME ──────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Plan d'alimentation actif : $planAlim" | Out-File $outputFile -Append -Encoding UTF8
" Secure Boot : $secureBoot" | Out-File $outputFile -Append -Encoding UTF8
if ($tpm) {
" TPM Présent=$($tpm.TpmPresent) | Activé=$($tpm.TpmEnabled) | Prêt=$($tpm.TpmReady) | Version=$($tpm.ManufacturerVersion)" |
Out-File $outputFile -Append -Encoding UTF8
} else { " TPM : Non disponible (module Get-Tpm absent ou droits insuffisants)" | Out-File $outputFile -Append -Encoding UTF8 }
if ($bitlocker) {
"`n BitLocker :" | Out-File $outputFile -Append -Encoding UTF8
$bitlocker | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " BitLocker : Non disponible (module BitLocker absent)" | Out-File $outputFile -Append -Encoding UTF8 }
" Spooler : $(if ($spooler) { "Status=$($spooler.Status) | Démarrage=$($spooler.StartType)" } else { 'Non disponible' })" |
Out-File $outputFile -Append -Encoding UTF8
# ── Section 5 : Événements crashes/instabilités ───────────────────────
"`n── ÉVÉNEMENTS CRASHES / INSTABILITÉS — signal P02/P03 ($($resCrashes.Count) evt / fenêtre $($resCrashes.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resCrashes.Events -and $resCrashes.Count -gt 0) {
# Résumé par ID (conservé pour compatibilité)
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resCrashes.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
# [MOD-P01-2] Résumé par type WER (EventName extrait du message)
"`n Résumé par type WER (EventName) :" | Out-File $outputFile -Append -Encoding UTF8
$resCrashes.Events | ForEach-Object {
if ($_.Message -match "Nom d.événement\s*:\s*(\S+)") { $Matches[1] }
elseif ($_.Message -match "Event Name\s*:\s*(\S+)") { $Matches[1] }
else { "Inconnu" }
} | Group-Object | Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
# [MOD-P01-2] Résumé BugCheck P1 pour LiveKernelEvent et BlueScreen
$bugCheckCodes = $resCrashes.Events | ForEach-Object {
if ($_.Message -match "\bP1\s*:\s*([0-9a-fA-F]+)\b") { "0x$($Matches[1].ToUpper())" }
} | Where-Object { $_ -ne $null }
if ($bugCheckCodes) {
"`n Codes BugCheck (P1) détectés :" | Out-File $outputFile -Append -Encoding UTF8
$bugCheckCodes | Group-Object | Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
}
# Détail chronologique
"`n Détail (20 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resCrashes.Events | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun événement détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 6 : Événements disque/I/O (signal P04) ───────────────────
"`n── ÉVÉNEMENTS DISQUE / I/O — signal P04 ($($resDisque.Count) evt / fenêtre $($resDisque.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resDisque.Events -and $resDisque.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resDisque.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resDisque.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun événement détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 7 : Événements réseau/services (signal P05/P09) ──────────
"`n── ÉVÉNEMENTS RÉSEAU / SERVICES — signal P05/P09 ($($resReseau.Count) evt / fenêtre $($resReseau.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resReseau.Events -and $resReseau.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resReseau.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resReseau.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun événement détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 8 : App crashes (signal P06) ─────────────────────────────
"`n── ÉVÉNEMENTS APPLICATIFS — signal P06 ($($resAppCrash.Count) evt / fenêtre $($resAppCrash.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resAppCrash.Events -and $resAppCrash.Count -gt 0) {
# [MOD-P01-3] Résumé par application avec fallback EventName si nom vide
"`n Résumé par application :" | Out-File $outputFile -Append -Encoding UTF8
$resAppCrash.Events | ForEach-Object {
$appName = $_.Properties[0].Value
if ([string]::IsNullOrWhiteSpace($appName)) {
if ($_.Message -match "Nom d.événement\s*:\s*(\S+)") { "[Kernel] $($Matches[1])" }
elseif ($_.Message -match "Event Name\s*:\s*(\S+)") { "[Kernel] $($Matches[1])" }
else { "[Kernel] Inconnu" }
} else { $appName }
} | Group-Object | Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resAppCrash.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Application'; E={
$n = $_.Properties[0].Value
if ([string]::IsNullOrWhiteSpace($n)) {
if ($_.Message -match "Nom d.événement\s*:\s*(\S+)") { "[Kernel] $($Matches[1])" }
else { "[Kernel] Inconnu" }
} else { $n }
}},
@{N='Module fautif'; E={ if ($_.Properties.Count -gt 3) { $_.Properties[3].Value } else { 'N/A' } }},
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun crash applicatif détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 9 : Boot/Profil/GPO (signal P02/P09) ─────────────────────
"`n── ÉVÉNEMENTS BOOT / PROFIL / GPO — signal P02/P09 ($($resBootGPO.Count) evt / fenêtre $($resBootGPO.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resBootGPO.Events -and $resBootGPO.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resBootGPO.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resBootGPO.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun événement boot/profil/GPO détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 10 : GPU/TDR (signal P07) ────────────────────────────────
"`n── ÉVÉNEMENTS GPU / TDR — signal P07 ($($resGPU.Count) evt ID4101 / fenêtre $($resGPU.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resGPU.Events -and $resGPU.Count -gt 0) {
$resGPU.Events | Sort-Object TimeCreated -Descending |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun TDR GPU (ID 4101) détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# [MOD-P01-1] Section GPU/TDR complémentaire via WER BugCheck
"`n -- TDR GPU via WER BugCheck (codes 0x116/0x117/0x141) : $tdrWERCount événement(s) --" |
Out-File $outputFile -Append -Encoding UTF8
if ($tdrWERCount -gt 0) {
" ⚠️ TDR GPU détectés dans les ID 1001 WER — codes BugCheck GPU confirmés :" |
Out-File $outputFile -Append -Encoding UTF8
$resGPU_WER | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id, ProviderName,
@{N='BugCheck'; E={
if ($_.Message -match "\bP1\s*:\s*([0-9a-fA-F]+)\b") { "0x$($Matches[1].ToUpper())" } else { '?' }
}},
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun TDR GPU détecté via WER BugCheck dans la fenêtre crashes." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 11 : Échecs logon (signal P11) ───────────────────────────
"`n── ÉCHECS LOGON — signal P11 ($($resSec.Count) evt / fenêtre $($resSec.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resSec.Events -and $resSec.Count -gt 0) {
"`n Résumé par code d'échec :" | Out-File $outputFile -Append -Encoding UTF8
$resSec.Events | Group-Object { $_.Properties[7].Value } | Sort-Object Count -Descending |
ForEach-Object { " Code $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resSec.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Peu ou aucun échec logon détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 12 : Windows Update (signal P12) ─────────────────────────
"`n── ÉVÉNEMENTS WINDOWS UPDATE — signal P12 ($($resWU.Count) evt / fenêtre $($resWU.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resWU.Events -and $resWU.Count -gt 0) {
$resWU.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucune erreur Windows Update détectée dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P01 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Résultat du script PowerShell COLLECTE TRIAGE DIAGNOSTIQUE [file]
- Symptômes précis observés par l'utilisateur
- Nombre de personnes impactées
- Tests N1 déjà effectués
- Numéro de ticket
- SLA applicable
P02 — Performances & Lenteurs Système
Diagnostic des lenteurs poste Windows 10/11 (boot, session, lenteur permanente, applicative). Analyse CIM/WMI, démarrage, profil, GPO, mémoire, pagefile. Distingue saturation logicielle, corruption et défaut matériel.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p02-performances-lenteurs-systeme_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : OS · Config CPU/RAM · Pagefile · Plan alimentation · Espace disque · SMART · Top processus CPU/RAM · Programmes démarrage · Services auto non démarrés · KB récentes · Event IDs boot, profil/GPO, lenteurs, I/O disque, crashes, pannes système
Données à saisir manuellement : Fiche de triage P01 · Symptôme exact · Durée du problème · Modifications récentes
# ════════════════════════════════════════════════════════════════════
# COLLECTE DIAGNOSTIQUE PERFORMANCES & LENTEURS v1.0
# Périmètre : boot, session, profil, GPO, mémoire, pagefile, disque,
# processus, démarrage, services, historique MAJ
#
# Profil cible : P02 — Performances & Lenteurs Système
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720 # plafond 30 jours
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p02-performances-lenteurs-systeme_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES
# (config, perf instantanée, pagefile, plan alim, espace disque)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/9] Collecte configuration système..."
# OS
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# CPU
try { $cpuInfo = Get-CimInstance Win32_Processor | Select-Object -First 1 }
catch { $cpuInfo = $null }
# Type de machine
try {
$cs = Get-CimInstance Win32_ComputerSystem
$pcTypeCode = $cs.PCSystemType
$pcTypeLabel = switch ($pcTypeCode) {
1 { "Poste de bureau" } 2 { "Portable" } 4 { "Serveur" } 7 { "Workstation" }
default { "Inconnu (code $pcTypeCode)" }
}
} catch { $cs = $null; $pcTypeLabel = 'N/A' }
# CPU charge — double mesure pour lisser les pics
Write-Host "[2/9] Mesure CPU/RAM (double mesure)..."
try {
$cpu1 = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average
Start-Sleep -Seconds 1
$cpu2 = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average
$cpuUsage = [math]::Round(($cpu1 + $cpu2) / 2, 1)
} catch { $cpuUsage = 'N/A' }
# RAM
try {
$osPerf = Get-CimInstance Win32_OperatingSystem
$ramTotalMB = [math]::Round($osPerf.TotalVisibleMemorySize / 1KB)
$ramDispoMB = [math]::Round($osPerf.FreePhysicalMemory / 1KB)
$ramPct = [math]::Round((($ramTotalMB - $ramDispoMB) / $ramTotalMB) * 100, 1)
} catch { $ramTotalMB = 'N/A'; $ramDispoMB = 'N/A'; $ramPct = 'N/A' }
# I/O disque instantané via CIM
try {
$diskPerf = Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk |
Where-Object { $_.Name -eq '_Total' }
$diskQueue = if ($diskPerf) { [math]::Round($diskPerf.AvgDiskQueueLength, 2) } else { 'N/A' }
$diskPct = if ($diskPerf) { [math]::Round($diskPerf.PercentDiskTime, 1) } else { 'N/A' }
} catch { $diskQueue = 'N/A'; $diskPct = 'N/A' }
# Pagefile — taille configurée vs utilisation réelle
Write-Host "[3/9] Collecte pagefile, plan d'alimentation, espace disque..."
try {
$pagefileConfig = Get-CimInstance Win32_PageFileSetting |
Select-Object Name,
@{N='InitialSizeMB'; E={$_.InitialSize}},
@{N='MaxSizeMB'; E={$_.MaximumSize}}
$pagefileUsage = Get-CimInstance Win32_PageFileUsage |
Select-Object Name,
@{N='TailleMB'; E={$_.AllocatedBaseSize}},
@{N='UtiliseMB'; E={$_.CurrentUsage}},
@{N='PicUtiliseMB';E={$_.PeakUsage}}
# Taux d'utilisation pagefile
$pagefilePct = if ($pagefileUsage -and $pagefileUsage.TailleMB -gt 0) {
[math]::Round(($pagefileUsage.UtiliseMB / $pagefileUsage.TailleMB) * 100, 1)
} else { 0 }
} catch {
$pagefileConfig = $null; $pagefileUsage = $null; $pagefilePct = 'N/A'
}
# Plan d'alimentation actif
try { $planAlim = powercfg /getactivescheme 2>$null }
catch { $planAlim = "Non disponible" }
# Volumes logiques et espace libre
try {
$volumes = Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 } |
Select-Object DeviceID, VolumeName,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 1)}},
@{N='LibreGB'; E={[math]::Round($_.FreeSpace / 1GB, 1)}},
@{N='LibrePct'; E={[math]::Round(($_.FreeSpace / $_.Size) * 100, 1)}}
} catch { $volumes = $null }
# Disques physiques + type (HDD/SSD)
try {
$disquesPhysiques = Get-CimInstance Win32_DiskDrive |
Select-Object Model, MediaType,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 0)}},
SerialNumber
} catch { $disquesPhysiques = $null }
# SMART synthétique via CIM
try {
$smartData = Get-CimInstance -Namespace root\wmi -ClassName MSStorageDriver_FailurePredictStatus |
Select-Object InstanceName,
@{N='DefaillancePredite'; E={$_.PredictFailure}},
@{N='Raison'; E={$_.Reason}}
} catch { $smartData = $null }
# État optimisation disque (SSD/HDD)
try {
$optim = Get-ScheduledTask | Where-Object { $_.TaskName -like '*Optim*' -or $_.TaskName -like '*Defrag*' } |
Select-Object TaskName, State,
@{N='DernièreExécution'; E={(Get-ScheduledTaskInfo $_.TaskName -ErrorAction SilentlyContinue).LastRunTime}}
} catch { $optim = $null }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES MOYENNES
# (top processus, démarrage, services, KB installées)
# ════════════════════════════════════════════════════════════════════
Write-Host "[4/9] Top processus CPU et RAM..."
try {
$topCPU = Get-CimInstance Win32_Process |
Sort-Object KernelModeTime -Descending | Select-Object -First 15 |
Select-Object Name,
@{N='CPU_s'; E={[math]::Round(($_.KernelModeTime + $_.UserModeTime) / 1e7, 1)}},
@{N='RAM_MB'; E={[math]::Round($_.WorkingSetSize / 1MB, 1)}},
ProcessId
} catch { $topCPU = $null }
try {
$topRAM = Get-CimInstance Win32_Process |
Sort-Object WorkingSetSize -Descending | Select-Object -First 15 |
Select-Object Name,
@{N='RAM_MB'; E={[math]::Round($_.WorkingSetSize / 1MB, 1)}},
@{N='CPU_s'; E={[math]::Round(($_.KernelModeTime + $_.UserModeTime) / 1e7, 1)}},
ProcessId
} catch { $topRAM = $null }
Write-Host "[5/9] Programmes au démarrage..."
try {
$startups = Get-CimInstance Win32_StartupCommand |
Select-Object Name, Command, Location, User
} catch { $startups = $null }
Write-Host "[6/9] Services auto non démarrés..."
try {
$svcNonDemarres = Get-Service |
Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running' } |
Select-Object Name, DisplayName, Status, StartType
} catch { $svcNonDemarres = $null }
Write-Host "[7/9] Historique des 10 dernières KB installées..."
try {
$kbHistorique = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 10 |
Select-Object HotFixID, Description,
@{N='InstalléLe'; E={$_.InstalledOn}}
} catch {
# Fallback via CIM si Get-HotFix échoue
try {
$kbHistorique = Get-CimInstance Win32_QuickFixEngineering |
Sort-Object InstalledOn -Descending | Select-Object -First 10 |
Select-Object HotFixID, Description,
@{N='InstalléLe'; E={$_.InstalledOn}}
} catch { $kbHistorique = $null }
}
# ════════════════════════════════════════════════════════════════════
# PHASE 3 — COLLECTES ADAPTATIVES (événements)
# Ordre : du plus rapide au plus long
# ════════════════════════════════════════════════════════════════════
Write-Host "[8/9] Collecte événements boot / durée démarrage (ID 100)..."
# ID 100 : rapport global boot | 101 : pilote lent | 102 : processus lent | 103 : service lent | 109 : reprise veille lente
$resBoot = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-Diagnostics-Performance/Operational' `
-Ids @(100, 101, 102, 103, 109) `
-Label 'Durée Boot' `
-MinEvents 5 `
-MaxEvents 50
# Note : ID 6013 n'est pas filtrable par Get-WinEvent facilement — collecte séparée
try {
$uptime = (Get-Date) - $os.LastBootUpTime
$uptimeStr = "{0}j {1}h {2}min" -f [math]::Floor($uptime.TotalDays), $uptime.Hours, $uptime.Minutes
} catch { $uptimeStr = 'N/A' }
Write-Host "[9/9] Collecte événements profil/GPO et lenteurs fonctionnement..."
# Profil : 1511,1515,1500,1502,1508,1509,1530,1534 | GPO/DC : 1085,1006,1030,1058,1129
$resProfil = Get-EventsAdaptatif `
-LogNames @('Microsoft-Windows-User Profile Service/Operational',
'Microsoft-Windows-GroupPolicy/Operational',
'System') `
-Ids @(1511, 1515, 1500, 1502, 1508, 1509, 1530, 1534,
1085, 1006, 1030, 1058, 1129) `
-Label 'Profil/GPO' `
-MinEvents 5 `
-MaxEvents 80
# Lenteurs fonctionnement : 200,201 (lenteur appli) | 2004 (mémoire insuffisante)
$resFonct = Get-EventsAdaptatif `
-LogNames @('Microsoft-Windows-Diagnostics-Performance/Operational',
'System', 'Application') `
-Ids @(1001, 200, 201, 2004) `
-Label 'Lenteurs Fonctionnement' `
-MinEvents 5 `
-MaxEvents 60
# I/O disque : 7,9,11,15,51 (bad block/timeout) | 129,153,157 (Storport) | 55,98,140 (NTFS)
$resDisque = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(7, 9, 11, 15, 51, 129, 153, 157, 55, 98, 140) `
-Label 'I/O Disque' `
-MinEvents 5 `
-MaxEvents 80
# App crashes : 1000,1001,1002 (WER crash/hang)
$resAppCrash = Get-EventsAdaptatif `
-LogNames 'Application' `
-Ids @(1000, 1001, 1002) `
-Label 'App Crashes' `
-MinEvents 5 `
-MaxEvents 40
# Pannes système : 41,6008 (arrêt brutal) | 7023,7031,7034 (services) | 4,219 (pilotes) | 2019,2020 (pool mémoire)
$resPannes = Get-EventsAdaptatif `
-LogNames @('System', 'Application') `
-Ids @(41, 6008, 7023, 7031, 7034, 4, 219, 2019, 2020) `
-Label 'Pannes Système' `
-MinEvents 10 `
-MaxEvents 150
Write-Host " Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerte_CPU = if ($cpuUsage -ne 'N/A' -and $cpuUsage -gt 85) { "⚠️ ALERTE : CPU ELEVE ($cpuUsage %)" } else { "✅ OK ($cpuUsage %)" }
$alerte_RAM = if ($ramPct -ne 'N/A' -and $ramPct -gt 90) { "⚠️ ALERTE : RAM SATUREE ($ramPct %)" } else { "✅ OK ($ramPct %)" }
$alerte_Disque = if ($diskQueue -ne 'N/A' -and $diskQueue -gt 2) { "⚠️ ALERTE : DISQUE LENT (queue $diskQueue)" } else { "✅ OK (queue $diskQueue)" }
$alerte_Page = if ($pagefilePct -ne 'N/A' -and $pagefilePct -gt 80) { "⚠️ ALERTE : PAGEFILE SATURE ($pagefilePct %)" } else { "✅ OK ($pagefilePct %)" }
$alertesEspace = @()
if ($volumes) {
foreach ($v in $volumes) {
if ($v.LibrePct -lt 10) { $alertesEspace += "⚠️ CRITIQUE : $($v.DeviceID) < 10% libre ($($v.LibreGB) Go)" }
elseif ($v.LibrePct -lt 20) { $alertesEspace += "⚠️ ATTENTION : $($v.DeviceID) < 20% libre ($($v.LibreGB) Go)" }
}
}
$alerte_Espace = if ($alertesEspace) { $alertesEspace -join ' | ' } else { "✅ OK" }
$alerte_Boot = if ($resBoot.Count -gt 0) { "⚠️ $($resBoot.Count) démarrage(s) lent(s) détecté(s) (ID 100)" } else { "✅ Aucun démarrage lent" }
$alerte_Profil = if ($resProfil.Count -gt 0) { "⚠️ $($resProfil.Count) événement(s) profil/GPO détecté(s)" } else { "✅ Aucun incident profil/GPO" }
$alerte_Fonct = if ($resFonct.Count -gt 0) { "⚠️ $($resFonct.Count) lenteur(s) fonctionnement détectée(s) (ID 1001)" } else { "✅ Aucune lenteur fonctionnement" }
$alerte_IO = if ($resDisque.Count -gt 5) { "⚠️ $($resDisque.Count) événement(s) I/O disque → possible goulot" } else { "✅ Peu d'événements I/O ($($resDisque.Count))" }
$alerte_SvcKO = if ($svcNonDemarres -and $svcNonDemarres.Count -gt 0) { "⚠️ $($svcNonDemarres.Count) service(s) auto non démarré(s)" } else { "✅ Tous les services auto démarrés" }
$alerte_Plan = if ($planAlim -match "Économies|Economy|Power saver") { "⚠️ Plan économies d'énergie actif — peut freiner les performances" } else { "✅ $planAlim" }
$alerte_Pannes = if ($resPannes.Count -gt 0) { "⚠️ $($resPannes.Count) panne(s) système détectée(s) (arrêt brutal, pilote, service, mémoire)" } else { "✅ Aucune panne système détectée" }
# ── En-tête du rapport ───────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT PERFORMANCES & LENTEURS SYSTÈME v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p02-performances-lenteurs-systeme.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Type : $pcTypeLabel
CPU : $(if ($cpuInfo) { $cpuInfo.Name } else { 'N/A' })
RAM : $ramTotalMB Mo total / $ramDispoMB Mo libres ($ramPct % utilisée)
Uptime : $uptimeStr
ALERTES PERFORMANCES
|- CPU : $alerte_CPU
|- RAM : $alerte_RAM
|- Pagefile : $alerte_Page
|- I/O disque : $alerte_Disque
|- Espace disque: $alerte_Espace
|- Plan alim. : $alerte_Plan
|- Services auto: $alerte_SvcKO
|- Boot lent : $alerte_Boot
|- Profil/GPO : $alerte_Profil
|- Lenteurs fonct.: $alerte_Fonct
|- I/O disque evt: $alerte_IO
|- Pannes système : $alerte_Pannes
FENÊTRES RETENUES PAR BLOC
|- Durée Boot : $($resBoot.WindowHours) h -> $($resBoot.Count) événements
|- Profil/GPO : $($resProfil.WindowHours) h -> $($resProfil.Count) événements
|- Lenteurs Fonctionnement: $($resFonct.WindowHours) h -> $($resFonct.Count) événements
|- I/O Disque : $($resDisque.WindowHours) h -> $($resDisque.Count) événements
|- App Crashes (corrél.) : $($resAppCrash.WindowHours) h -> $($resAppCrash.Count) événements
|- Pannes Système : $($resPannes.WindowHours) h -> $($resPannes.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Configuration matérielle ─────────────────────────────
"`n── CONFIGURATION MATÉRIELLE ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($cs) {
" Fabricant : $($cs.Manufacturer) | Modèle : $($cs.Model)" |
Out-File $outputFile -Append -Encoding UTF8
}
if ($cpuInfo) {
" CPU : $($cpuInfo.Name) | Cœurs : $($cpuInfo.NumberOfCores) | Threads : $($cpuInfo.NumberOfLogicalProcessors)" |
Out-File $outputFile -Append -Encoding UTF8
}
"`n Disques physiques :" | Out-File $outputFile -Append -Encoding UTF8
if ($disquesPhysiques) {
$disquesPhysiques | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
"`n Volumes logiques :" | Out-File $outputFile -Append -Encoding UTF8
if ($volumes) {
$volumes | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Performances instantanées ────────────────────────────
"`n── PERFORMANCES INSTANTANÉES ───────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" CPU : $cpuUsage % $alerte_CPU" | Out-File $outputFile -Append -Encoding UTF8
" RAM : $ramPct % $alerte_RAM" | Out-File $outputFile -Append -Encoding UTF8
" I/O : queue=$diskQueue $alerte_Disque" | Out-File $outputFile -Append -Encoding UTF8
# ── Section 3 : Pagefile ─────────────────────────────────────────────
"`n── PAGEFILE (MÉMOIRE VIRTUELLE) ────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($pagefileConfig) {
" Configuration :" | Out-File $outputFile -Append -Encoding UTF8
$pagefileConfig | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Configuration pagefile : Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
if ($pagefileUsage) {
" Utilisation actuelle :" | Out-File $outputFile -Append -Encoding UTF8
$pagefileUsage | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" Taux d'utilisation pagefile : $pagefilePct % $alerte_Page" |
Out-File $outputFile -Append -Encoding UTF8
} else { " Utilisation pagefile : Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 4 : Plan d'alimentation ──────────────────────────────────
"`n── PLAN D'ALIMENTATION ─────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $planAlim $alerte_Plan" | Out-File $outputFile -Append -Encoding UTF8
# ── Section 5 : Top processus CPU ────────────────────────────────────
"`n── TOP 15 PROCESSUS PAR CPU ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($topCPU) {
$topCPU | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 6 : Top processus RAM ────────────────────────────────────
"`n── TOP 15 PROCESSUS PAR RAM ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($topRAM) {
$topRAM | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 7 : Programmes au démarrage ──────────────────────────────
"`n── PROGRAMMES AU DÉMARRAGE ($( if ($startups) { $startups.Count } else { 0 } ) entrées) ───────" |
Out-File $outputFile -Append -Encoding UTF8
if ($startups -and $startups.Count -gt 0) {
$startups | Sort-Object Name | Format-Table -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun programme au démarrage détecté via WMI." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 8 : Services auto non démarrés ───────────────────────────
"`n── SERVICES AUTO NON DÉMARRÉS ($( if ($svcNonDemarres) { $svcNonDemarres.Count } else { 0 } ) service(s)) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($svcNonDemarres -and $svcNonDemarres.Count -gt 0) {
$svcNonDemarres | Sort-Object Name | Format-Table -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
" $alerte_SvcKO" | Out-File $outputFile -Append -Encoding UTF8
} else { " ✅ Tous les services en démarrage automatique sont actifs." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 9 : Historique des 10 dernières KB ───────────────────────
"`n── HISTORIQUE DES 10 DERNIÈRES MISES À JOUR INSTALLÉES ────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($kbHistorique) {
$kbHistorique | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Get-HotFix et Win32_QuickFixEngineering inaccessibles)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 10 : État SMART synthétique ──────────────────────────────
"`n── ÉTAT SMART DISQUES (synthétique) ───────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($smartData) {
$smartData | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
$alerteSmart = $smartData | Where-Object { $_.DefaillancePredite -eq $true }
if ($alerteSmart) {
"`n ⚠️ ALERTE SMART : défaillance prédite sur $($alerteSmart.Count) disque(s) — consulter P04 Stockage" |
Out-File $outputFile -Append -Encoding UTF8
} else { " ✅ Aucune défaillance SMART prédite." | Out-File $outputFile -Append -Encoding UTF8 }
} else {
" Non disponible (namespace WMI root\wmi non accessible ou droits insuffisants)" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 11 : Optimisation disque ─────────────────────────────────
"`n── OPTIMISATION / DÉFRAGMENTATION DISQUE ──────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($optim) {
$optim | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (tâche d'optimisation introuvable)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 12 : Événements boot (ID 100) ────────────────────────────
"`n── ÉVÉNEMENTS DURÉE DÉMARRAGE — ID 100 ($($resBoot.Count) evt / fenêtre $($resBoot.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resBoot.Events -and $resBoot.Count -gt 0) {
"`n Durées de démarrage extraites :" | Out-File $outputFile -Append -Encoding UTF8
$resBoot.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated,
@{N='Durée_ms'; E={
if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'N/A' }
}},
@{N='Message_court'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun événement de démarrage détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 13 : Événements profil/GPO ───────────────────────────────
"`n── ÉVÉNEMENTS PROFIL / GPO ($($resProfil.Count) evt / fenêtre $($resProfil.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resProfil.Events -and $resProfil.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resProfil.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resProfil.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun événement profil/GPO détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 14 : Lenteurs fonctionnement (ID 1001) ───────────────────
"`n── ÉVÉNEMENTS LENTEURS FONCTIONNEMENT — ID 1001 ($($resFonct.Count) evt / fenêtre $($resFonct.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resFonct.Events -and $resFonct.Count -gt 0) {
"`n Résumé par application :" | Out-File $outputFile -Append -Encoding UTF8
$resFonct.Events | Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resFonct.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id,
@{N='Application'; E={ if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'N/A' } }},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Aucune lenteur applicative détectée dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 15 : I/O disque (corrélation) ────────────────────────────
"`n── ÉVÉNEMENTS I/O DISQUE — corrélation ($($resDisque.Count) evt / fenêtre $($resDisque.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resDisque.Events -and $resDisque.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resDisque.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resDisque.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Peu ou aucun événement I/O disque dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 16 : App crashes corrélation ─────────────────────────────
"`n── APP CRASHES — corrélation ($($resAppCrash.Count) evt / fenêtre $($resAppCrash.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resAppCrash.Events -and $resAppCrash.Count -gt 0) {
"`n Résumé par application :" | Out-File $outputFile -Append -Encoding UTF8
$resAppCrash.Events | Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) crash(s)" } |
Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun crash applicatif détecté dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P02 terminée."
Write-Host " Fichier : $outputFile"
# ── Section : Pannes système ─────────────────────────────────────────
"`n── PANNES SYSTÈME — Arrêts brutaux / Pilotes / Services / Mémoire ($($resPannes.Count) evt / $($resPannes.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resPannes.Events -and $resPannes.Count -gt 0) {
$resPannes.Events | Sort-Object TimeCreated -Descending | Select-Object -First 30 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucune panne système détectée dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script COLLECTE DIAGNOSTIQUE ADAPTATIVE v2.2 [file]
- Symptôme exact (boot lent, session lente, lenteur permanente, lenteur applicative)
- Durée du problème (depuis quand)
- Modifications récentes (MAJ, logiciel, matériel)
P03 — Instabilités, BSOD & Pannes Matérielles
Diagnostic des BSOD, crashs système, redémarrages brutaux et instabilités matérielles sur poste Windows 10/11. Analyse des minidumps, codes stop, Event ID 41/1001, températures et pilotes suspects.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p03-instabilites-bsod-pannes-materielles_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : OS · Config matérielle · SMART · Pilotes (tous + récents 30j + non signés) · KB récentes · Event IDs crashes/pannes, BSOD, I/O disque
Données à saisir manuellement : Fiche de triage P01 · Codes BSOD affichés · Analyse WinDbg/BlueScreenView si disponible · Températures CPU/GPU · Dernières modifications
# ════════════════════════════════════════════════════════════════════
# COLLECTE INSTABILITÉS, BSOD & PANNES MATÉRIELLES v1.0
# Périmètre : crashs système, codes stop, minidumps, pilotes suspects,
# arrêts brutaux, services crash
#
# Profil cible : P03 — Instabilités, BSOD & Pannes Matérielles
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p03-instabilites-bsod-pannes-materielles_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/7] Collecte configuration système..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
try { $cpuInfo = Get-CimInstance Win32_Processor | Select-Object -First 1 }
catch { $cpuInfo = $null }
try {
$cs = Get-CimInstance Win32_ComputerSystem
$ramTotalGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1)
} catch { $cs = $null; $ramTotalGB = 'N/A' }
try {
$disquesPhysiques = Get-CimInstance Win32_DiskDrive |
Select-Object Model, MediaType,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 0)}},
SerialNumber
} catch { $disquesPhysiques = $null }
# GPU
try {
$gpus = Get-CimInstance Win32_VideoController |
Select-Object Name, DriverVersion,
@{N='DriverDate'; E={ if ($_.DriverDate) { $_.DriverDate.ToString('dd/MM/yyyy') } else { 'N/A' } }},
AdapterRAM, VideoProcessor
} catch { $gpus = $null }
# Uptime
try {
$uptime = (Get-Date) - $os.LastBootUpTime
$uptimeStr = "{0}j {1}h {2}min" -f [math]::Floor($uptime.TotalDays), $uptime.Hours, $uptime.Minutes
} catch { $uptimeStr = 'N/A' }
# ── Minidumps ────────────────────────────────────────────────────────
Write-Host "[2/7] Inventaire des minidumps..."
try {
$minidumpDir = "$env:SystemRoot\Minidump"
if (Test-Path $minidumpDir) {
$minidumps = Get-ChildItem $minidumpDir -Filter "*.dmp" -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object Name,
@{N='Date'; E={ $_.LastWriteTime.ToString('dd/MM/yyyy HH:mm:ss') }},
@{N='TailleKo'; E={ [math]::Round($_.Length / 1KB, 0) }}
$nbDumps = if ($minidumps) { $minidumps.Count } else { 0 }
} else {
$minidumps = $null
$nbDumps = 0
}
} catch { $minidumps = $null; $nbDumps = 0 }
# ── Pilotes — tous, triés par date d'installation ────────────────────
Write-Host "[3/7] Inventaire des pilotes (1 seule requete, tout derive du cache)..."
try {
# Un seul appel Win32_PnPSignedDriver - derive pilotesRecents et nonSignes du cache
$seuil30j = (Get-Date).AddDays(-30)
$rawPilotes = Get-CimInstance Win32_PnPSignedDriver
$tousLesPilotes = $rawPilotes |
Where-Object { $_.DriverVersion -ne $null } |
Select-Object DeviceName, DriverProviderName, DriverVersion,
@{N='DateInstall'; E={
if ($_.DriverDate) { $_.DriverDate.ToString('dd/MM/yyyy') } else { 'N/A' }
}},
IsSigned |
Sort-Object DriverDate -Descending
$aujourdhui = Get-Date
$pilotesRecents = $rawPilotes |
Where-Object { $_.DriverDate -ne $null -and $_.DriverDate -gt $seuil30j -and $_.DriverDate -le $aujourdhui } |
Select-Object DeviceName, DriverProviderName, DriverVersion,
@{N='DateInstall'; E={ $_.DriverDate.ToString('dd/MM/yyyy') }},
IsSigned |
Sort-Object DriverDate -Descending
$nbPilotesRecents = if ($pilotesRecents) { @($pilotesRecents).Count } else { 0 }
$pilotesNonSignes = $rawPilotes |
Where-Object {
($_.IsSigned -eq $false -or $_.IsSigned -eq $null) -and
$_.DeviceName -and $_.DeviceName.Trim().Length -gt 2
} |
Select-Object DeviceName, DriverProviderName, DriverVersion,
@{N='DateInstall'; E={
if ($_.DriverDate) { $_.DriverDate.ToString('dd/MM/yyyy') } else { 'N/A' }
}}
$nbNonSignes = if ($pilotesNonSignes) { @($pilotesNonSignes).Count } else { 0 }
} catch {
$tousLesPilotes = $null
$pilotesRecents = $null
$nbPilotesRecents = 0
$pilotesNonSignes = $null
$nbNonSignes = 0
}
# ── Historique KB ────────────────────────────────────────────────────
Write-Host "[4/7] Historique des 10 dernières KB..."
try {
$kbHistorique = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 10 |
Select-Object HotFixID, Description,
@{N='InstalléLe'; E={ $_.InstalledOn }}
} catch {
try {
$kbHistorique = Get-CimInstance Win32_QuickFixEngineering |
Sort-Object InstalledOn -Descending | Select-Object -First 10 |
Select-Object HotFixID, Description,
@{N='InstalléLe'; E={ $_.InstalledOn }}
} catch { $kbHistorique = $null }
}
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements)
# ════════════════════════════════════════════════════════════════════
Write-Host "[5/7] Collecte événements crashes/arrêts brutaux (ID 41, 1001, 6008)..."
# ID 41 : Kernel-Power — arrêt brutal sans séquence d'arrêt normale
# ID 1001: BugCheck — code stop BSOD enregistré post-redémarrage
# ID 6008: EventLog — arrêt inattendu du système
$resCrashes = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(41, 1001, 6008) `
-Label 'Crashes/BSOD' `
-MinEvents 5 `
-MaxEvents 80
Write-Host "[6/7] Collecte événements services crash (ID 7034)..."
# ID 7034 : Service Control Manager — un service s'est arrêté de façon inattendue
# Corrélation : un crash de service peut provoquer un BSOD (ex: pilote en mode noyau)
$resSvcCrash = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(7034) `
-Label 'Services crash' `
-MinEvents 3 `
-MaxEvents 40
Write-Host "[7/7] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerte_crashes = if ($resCrashes.Count -gt 0) { "⚠️ ALERTE : $($resCrashes.Count) crash(s)/arrêt(s) brutal(aux) détecté(s)" } else { "✅ Aucun crash/arrêt brutal détecté" }
$alerte_bsod = if ($resCrashes.Events | Where-Object { $_.Id -eq 1001 }) {
$nb = ($resCrashes.Events | Where-Object { $_.Id -eq 1001 }).Count
"⚠️ $nb BSOD enregistré(s) (ID 1001 — codes stop à analyser)"
} else { "✅ Aucun code stop BSOD enregistré" }
$alerte_dumps = if ($nbDumps -gt 0) { "⚠️ $nbDumps minidump(s) présent(s) — analyser avec BlueScreenView/WinDbg" } else { "✅ Aucun minidump présent" }
$alerte_recents = if ($nbPilotesRecents -gt 0) { "⚠️ $nbPilotesRecents pilote(s) installé(s) dans les 30 derniers jours" } else { "✅ Aucun pilote récent (< 30 j)" }
$alerte_nonSignes = if ($nbNonSignes -gt 0) { "⚠️ $nbNonSignes pilote(s) non signé(s) détecté(s)" } else { "✅ Tous les pilotes sont signés" }
$alerte_svcCrash = if ($resSvcCrash.Count -gt 0) { "⚠️ $($resSvcCrash.Count) crash(s) de service détecté(s) (ID 7034)" } else { "✅ Aucun crash de service" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT INSTABILITÉS, BSOD & PANNES MATÉRIELLES v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p03-instabilites-bsod-pannes-materielles.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
CPU : $(if ($cpuInfo) { $cpuInfo.Name } else { 'N/A' })
RAM : $ramTotalGB Go
Uptime : $uptimeStr
GPU : $(if ($gpus) { ($gpus | ForEach-Object { $_.Name }) -join ' | ' } else { 'N/A' })
ALERTES STABILITÉ
|- Crashes/arrêts brutaux : $alerte_crashes
|- BSOD (codes stop) : $alerte_bsod
|- Minidumps présents : $alerte_dumps
|- Pilotes récents (30j) : $alerte_recents
|- Pilotes non signés : $alerte_nonSignes
|- Crash services : $alerte_svcCrash
FENÊTRES RETENUES PAR BLOC
|- Crashes/BSOD : $($resCrashes.WindowHours) h -> $($resCrashes.Count) événements
|- Services crash: $($resSvcCrash.WindowHours) h -> $($resSvcCrash.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ── Section 1 : Configuration matérielle ─────────────────────────────
"`n── CONFIGURATION MATÉRIELLE ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($cs) {
" Fabricant : $($cs.Manufacturer) | Modèle : $($cs.Model) | RAM : $ramTotalGB Go" |
Out-File $outputFile -Append -Encoding UTF8
}
if ($cpuInfo) {
" CPU : $($cpuInfo.Name) | Cœurs : $($cpuInfo.NumberOfCores) | Threads : $($cpuInfo.NumberOfLogicalProcessors)" |
Out-File $outputFile -Append -Encoding UTF8
}
"`n GPU(s) :" | Out-File $outputFile -Append -Encoding UTF8
if ($gpus) {
$gpus | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
"`n Disques physiques :" | Out-File $outputFile -Append -Encoding UTF8
if ($disquesPhysiques) {
$disquesPhysiques | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Minidumps ─────────────────────────────────────────────
"`n── INVENTAIRE MINIDUMPS ($nbDumps fichier(s)) ──────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbDumps -gt 0) {
" Répertoire : $env:SystemRoot\Minidump" | Out-File $outputFile -Append -Encoding UTF8
" $alerte_dumps" | Out-File $outputFile -Append -Encoding UTF8
"`n Liste (plus récents en premier) :" | Out-File $outputFile -Append -Encoding UTF8
$minidumps | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Pour analyser : ouvrir BlueScreenView (NirSoft) ou WinDbg et coller le rapport dans la section DONNÉES MANUELLES." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun minidump présent dans $env:SystemRoot\Minidump" |
Out-File $outputFile -Append -Encoding UTF8
" Note : si les BSOD ne génèrent pas de dumps, vérifier : Paramètres système avancés > Démarrage et récupération > type de vidage mémoire." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 3 : Pilotes récents (< 30 jours) ─────────────────────────
"`n── PILOTES INSTALLÉS CES 30 DERNIERS JOURS ($nbPilotesRecents pilote(s)) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbPilotesRecents -gt 0) {
" $alerte_recents" | Out-File $outputFile -Append -Encoding UTF8
"`n ⚠️ Comparer les dates de ces pilotes avec l'apparition des premiers incidents." |
Out-File $outputFile -Append -Encoding UTF8
$pilotesRecents | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun pilote installé ou mis à jour dans les 30 derniers jours." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : Pilotes non signés ───────────────────────────────────
"`n── PILOTES NON SIGNÉS ($nbNonSignes pilote(s)) ────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbNonSignes -gt 0) {
" $alerte_nonSignes" | Out-File $outputFile -Append -Encoding UTF8
"`n ⚠️ Un pilote non signé peut provoquer des BSOD sur Windows 10/11 (Secure Boot + Driver Signing enforcement)." |
Out-File $outputFile -Append -Encoding UTF8
$pilotesNonSignes | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Tous les pilotes inventoriés sont signés." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 5 : Tous les pilotes (synthèse) ───────────────────────────
"`n── TOUS LES PILOTES — SYNTHÈSE PAR FOURNISSEUR ────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($tousLesPilotes) {
$nbTotal = @($tousLesPilotes).Count
" Total pilotes inventoriés : $nbTotal" | Out-File $outputFile -Append -Encoding UTF8
"`n Répartition par fournisseur (Top 10) :" | Out-File $outputFile -Append -Encoding UTF8
$tousLesPilotes | Group-Object DriverProviderName | Sort-Object Count -Descending |
Select-Object -First 10 |
ForEach-Object { " $($_.Name) : $($_.Count) pilote(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n 20 pilotes les plus récents :" | Out-File $outputFile -Append -Encoding UTF8
$tousLesPilotes | Select-Object -First 20 |
Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (Win32_PnPSignedDriver inaccessible)" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 6 : Historique KB ────────────────────────────────────────
"`n── HISTORIQUE DES 10 DERNIÈRES KB INSTALLÉES ───────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($kbHistorique) {
$kbHistorique | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 7 : Événements crashes / BSOD ────────────────────────────
"`n── ÉVÉNEMENTS CRASHES / BSOD ($($resCrashes.Count) evt / fenêtre $($resCrashes.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resCrashes.Events -and $resCrashes.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resCrashes.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'41' { "Kernel-Power (arrêt brutal)" }
'1001' { "BugCheck (code stop BSOD)" }
'6008' { "Arrêt inattendu (EventLog)" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
# Détail ID 1001 (codes stop) en priorité
$bsodEvents = $resCrashes.Events | Where-Object { $_.Id -eq 1001 }
if ($bsodEvents) {
"`n ── Codes stop BSOD (ID 1001) — détail :" | Out-File $outputFile -Append -Encoding UTF8
$bsodEvents | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
}
# Détail ID 41 (Kernel-Power)
$kpEvents = $resCrashes.Events | Where-Object { $_.Id -eq 41 }
if ($kpEvents) {
"`n ── Arrêts brutaux Kernel-Power (ID 41) — détail :" | Out-File $outputFile -Append -Encoding UTF8
$kpEvents | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated,
@{N='BugCheckCode'; E={
if ($_.Properties.Count -gt 0) { "0x{0:X8}" -f $_.Properties[0].Value } else { 'N/A' }
}},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
}
# Détail ID 6008
$shutdownEvents = $resCrashes.Events | Where-Object { $_.Id -eq 6008 }
if ($shutdownEvents) {
"`n ── Arrêts inattendus (ID 6008) — détail :" | Out-File $outputFile -Append -Encoding UTF8
$shutdownEvents | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
}
} else {
" ✅ Aucun crash/BSOD détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 8 : Crashs de services ───────────────────────────────────
"`n── ÉVÉNEMENTS CRASH SERVICES — ID 7034 ($($resSvcCrash.Count) evt / fenêtre $($resSvcCrash.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resSvcCrash.Events -and $resSvcCrash.Count -gt 0) {
"`n Résumé par service :" | Out-File $outputFile -Append -Encoding UTF8
$resSvcCrash.Events | Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) crash(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resSvcCrash.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id,
@{N='Service'; E={ if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'N/A' } }},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun crash de service détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Données manuelles attendues ───────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Codes BSOD photographiés ou notés (ex: IRQL_NOT_LESS_OR_EQUAL, 0x0000007E...)
[ ] Rapport BlueScreenView ou WinDbg (copier-coller le texte complet)
[ ] Températures CPU/GPU — HWiNFO64 > Sensors > Export Summary (lignes CPU/GPU Temp)
[ ] Résultat test mode sans échec (stable / toujours instable / non testé)
[ ] Modifications matérielles récentes (RAM ajoutée, GPU changé, disque ajouté...)
[ ] Tests mémoire effectués (MemTest86 / Windows Memory Diagnostic — résultat)
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P03 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Ouvrir le fichier et compléter la section DONNÉES MANUELLES"
Write-Host " 2. Si minidumps présents : analyser avec BlueScreenView et coller le rapport"
Write-Host " 3. Relever les températures avec HWiNFO64 et les coller dans le fichier"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS instabilités/BSOD [file]
- Codes BSOD affichés ou photographiés
- Analyse WinDbg ou BlueScreenView si disponible [file]
- Températures CPU/GPU si disponibles
- Dernières modifications (pilote, MAJ, matériel)
- Résultat mode sans échec si testé
P04 — Stockage, SMART & Erreurs I/O
Évaluation précise des incidents stockage sur poste Windows 10/11 : analyse SMART via CIM, Event ID disque/NTFS, chkdsk, distinction logique/physique. Priorise systématiquement la sécurité des données.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p04-stockage-smart-erreurs-i-o_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : Disques physiques · SMART détaillé · StorageReliabilityCounter · Volumes logiques · Partitions · Mapping device path · Chkdsk planifié · VSS Writers · Event IDs I/O filtrés par provider
Données à saisir manuellement : Fiche de triage P01 · Symptômes observés · Export CrystalDiskInfo si disponible · Résultat chkdsk si disponible · Âge estimé du disque · Présence sauvegarde
# ════════════════════════════════════════════════════════════════════
# COLLECTE STOCKAGE, SMART & ERREURS I/O v1.1
# Périmètre : disques physiques, SMART, volumes, VSS, événements I/O
#
# Profil cible : P04 — Stockage, SMART & Erreurs I/O
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
#
# Modifications v1.1 :
# - [MOD-P04-1] Filtrage ProviderName sur événements I/O — élimine les faux positifs
# ID11/Kernel-General (TxR VSS), ID51/UserModePowerService (power scheme),
# ID153/Kernel-Boot (VBS disabled)
# - [MOD-P04-2] Mapping \Device\HarddiskX → disque physique + lettre de volume
# - [MOD-P04-3] Get-StorageReliabilityCounter — SMART étendu (Wear, Temp, erreurs I/O)
# sans dépendance à CrystalDiskInfo
# - [MOD-P04-4] Alertes I/O différenciées : critique (ID129/153/157) vs standard (ID11/51)
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir = Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p04-stockage-smart-erreurs-i-o_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES
# (disques, volumes, SMART, chkdsk planifié)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte disques physiques (CIM)..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# Disques physiques via CIM
try {
$disquesCIM = Get-CimInstance Win32_DiskDrive |
Select-Object Model, MediaType, SerialNumber,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 0)}},
@{N='Partitions'; E={$_.Partitions}},
@{N='InterfaceType'; E={$_.InterfaceType}},
@{N='FirmwareRev'; E={$_.FirmwareRevision}}
} catch { $disquesCIM = $null }
# Disques physiques via Storage (Get-PhysicalDisk) — plus riche que CIM seul
try {
$disquesStorage = Get-PhysicalDisk |
Select-Object FriendlyName, MediaType, BusType,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 0)}},
OperationalStatus, HealthStatus,
@{N='AlloueGB'; E={[math]::Round($_.AllocatedSize / 1GB, 0)}}
} catch { $disquesStorage = $null }
Write-Host "[2/8] Collecte SMART via WMI..."
# SMART — défaillance prédite (synthétique)
try {
$smartStatus = Get-CimInstance -Namespace root\wmi -ClassName MSStorageDriver_FailurePredictStatus |
Select-Object InstanceName,
@{N='DefaillancePredite'; E={$_.PredictFailure}},
@{N='Raison'; E={$_.Reason}}
} catch { $smartStatus = $null }
# SMART — données brutes (attributs)
try {
$smartData = Get-CimInstance -Namespace root\wmi -ClassName MSStorageDriver_FailurePredictData |
Select-Object InstanceName,
@{N='VendorSpecific'; E={
if ($_.VendorSpecific) {
($_.VendorSpecific[0..511] | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
} else { 'N/A' }
}}
} catch { $smartData = $null }
# Tentative d'extraction attributs SMART critiques via seuils
try {
$smartThresholds = Get-CimInstance -Namespace root\wmi -ClassName MSStorageDriver_FailurePredictThresholds |
Select-Object InstanceName,
@{N='Seuils_hex'; E={
if ($_.VendorSpecific) {
($_.VendorSpecific[0..30] | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
} else { 'N/A' }
}}
} catch { $smartThresholds = $null }
# [MOD-P04-3] SMART étendu via Get-StorageReliabilityCounter
Write-Host "[2b/8] Collecte SMART étendu (StorageReliabilityCounter)..."
try {
$smartReliability = Get-PhysicalDisk | ForEach-Object {
$disk = $_
$rel = $disk | Get-StorageReliabilityCounter -ErrorAction SilentlyContinue
[PSCustomObject]@{
Disque = $disk.FriendlyName
Temperature_C = if ($rel.Temperature) { $rel.Temperature } else { 'N/A' }
Usure_Pct = if ($rel.Wear) { $rel.Wear } else { 'N/A' }
LecturesErreurs = if ($null -ne $rel.ReadErrorsTotal) { $rel.ReadErrorsTotal } else { 'N/A' }
EcrituresErreurs = if ($null -ne $rel.WriteErrorsTotal) { $rel.WriteErrorsTotal } else { 'N/A' }
PowerOnHours = if ($rel.PowerOnHours) { $rel.PowerOnHours } else { 'N/A' }
StartStopCount = if ($rel.StartStopCycleCount) { $rel.StartStopCycleCount } else { 'N/A' }
}
}
} catch { $smartReliability = $null }
Write-Host "[3/8] Collecte volumes, partitions et espace libre..."
# Volumes logiques
try {
$volumes = Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 } |
Select-Object DeviceID, VolumeName, FileSystem,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 1)}},
@{N='LibreGB'; E={[math]::Round($_.FreeSpace / 1GB, 1)}},
@{N='LibrePct'; E={[math]::Round(($_.FreeSpace / $_.Size) * 100, 1)}}
} catch { $volumes = $null }
# Volumes via module Storage (état intégrité)
try {
$volumesStorage = Get-Volume | Where-Object { $_.DriveLetter -ne $null } |
Select-Object DriveLetter, FileSystemLabel, FileSystem,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 1)}},
@{N='LibreGB'; E={[math]::Round($_.SizeRemaining / 1GB, 1)}},
OperationalStatus, HealthStatus, DriveType
} catch { $volumesStorage = $null }
# Toutes partitions incluses (sans filtre DriveLetter) —
# permet de voir EFI, Recovery et MSR, utiles au diagnostic boot/stockage
try {
$partitions = Get-Partition |
Select-Object DriveLetter, DiskNumber, PartitionNumber,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 1)}},
Type, IsSystem, IsBoot, IsActive
} catch { $partitions = $null }
# [MOD-P04-2] Mapping \Device\HarddiskX → disque physique + lettre de volume
Write-Host "[3b/8] Mapping device path vers disques physiques..."
try {
$diskMapping = Get-Disk | Select-Object Number, FriendlyName, BusType,
@{N='DevicePath'; E={ "\\Device\\Harddisk$($_.Number)" }},
@{N='DriveLetter'; E={
(Get-Partition -DiskNumber $_.Number -ErrorAction SilentlyContinue |
Where-Object { $_.DriveLetter } |
ForEach-Object { $_.DriveLetter }) -join ', '
}},
@{N='TailleGB'; E={ [math]::Round($_.Size / 1GB, 0) }}
} catch { $diskMapping = $null }
Write-Host "[4/8] Vérification chkdsk planifié et état VSS..."
# Chkdsk planifié au prochain boot
$chkdskStatus = @{}
try {
$lettres = (Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 }).DeviceID
foreach ($lettre in $lettres) {
$drive = $lettre.TrimEnd(':')
$result = & "$env:SystemRoot\System32\chkntfs.exe" "${drive}:" 2>&1
$chkdskStatus[$lettre] = $result -join ' '
}
} catch { $chkdskStatus['erreur'] = "Non disponible (chkntfs inaccessible)" }
# État VSS Writers
try {
$vssWriters = & "$env:SystemRoot\System32\vssadmin.exe" list writers 2>&1
$vssOK = $vssWriters | Where-Object { $_ -match 'État' -or $_ -match 'State' }
} catch { $vssWriters = "Non disponible (vssadmin inaccessible)"; $vssOK = $null }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements I/O)
# ════════════════════════════════════════════════════════════════════
Write-Host "[5/8] Collecte événements I/O disque — System (ID 11, 51, 129, 153, 157)..."
# ID 11 : disk — erreur I/O (contrôleur signale une erreur)
# ID 51 : disk — erreur pendant une opération de pagination
# ID 129 : StorPort/iaStorVD — reset du contrôleur de stockage
# ID 153 : StorPort — timeout I/O (requête non honorée dans le délai)
# ID 157 : disk — disque retiré de façon inattendue
$resIO = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(11, 51, 129, 153, 157) `
-Label 'I/O Disque' `
-MinEvents 5 `
-MaxEvents 100
# [MOD-P04-1] Filtrage par ProviderName — élimine les faux positifs connus
# Providers légitimes par ID :
# ID 11 : disk, iaStorA, iaStorVD, storahci, stornvme (pas Kernel-General = TxR VSS)
# ID 51 : disk (pas UserModePowerService = power scheme)
# ID 129 : iaStorA, iaStorVD, storahci, stornvme, LSI_SAS
# ID 153 : iaStorA, iaStorVD, storahci, stornvme, LSI_SAS (pas Kernel-Boot = VBS status)
# ID 157 : disk
$providersIO = @{
11 = @('disk', 'iaStorA', 'iaStorVD', 'storahci', 'stornvme')
51 = @('disk')
129 = @('iaStorA', 'iaStorVD', 'storahci', 'stornvme', 'LSI_SAS')
153 = @('iaStorA', 'iaStorVD', 'storahci', 'stornvme', 'LSI_SAS')
157 = @('disk')
}
$ioEventsFiltres = @()
$ioEventsEcartes = @()
if ($resIO.Events) {
foreach ($evt in $resIO.Events) {
$id = [int]$evt.Id
$prv = $evt.ProviderName
if ($providersIO.ContainsKey($id)) {
if ($prv -in $providersIO[$id]) { $ioEventsFiltres += $evt }
else { $ioEventsEcartes += $evt }
} else {
$ioEventsFiltres += $evt
}
}
}
$resIO = [PSCustomObject]@{
Events = $ioEventsFiltres
WindowHours = $resIO.WindowHours
Count = $ioEventsFiltres.Count
Label = $resIO.Label
EcartesFP = $ioEventsEcartes.Count
}
Write-Host "[6/8] Collecte événements NTFS — corruption (ID 55, 98, 130, 131, 137, 140)..."
# ID 55 : Ntfs — corruption détectée dans la structure du système de fichiers
# ID 98 : Ntfs — volume en lecture seule suite à une erreur
# ID 130 : Ntfs — tentative d'écriture sur volume en lecture seule
# ID 131 : Ntfs — impossible d'allouer de l'espace
# ID 137 : Ntfs — le journal n'a pas pu être écrit
# ID 140 : Ntfs — mode de journalisation dégradé
try {
$resNTFS = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-Ntfs/Operational' `
-Ids @(55, 98, 130, 131, 137, 140) `
-Label 'NTFS Corruption' `
-MinEvents 3 `
-MaxEvents 60
} catch {
# Fallback sur System pour ID 55 si le journal NTFS/Operational n'existe pas
$resNTFS = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(55) `
-Label 'NTFS Corruption (System)' `
-MinEvents 3 `
-MaxEvents 60
}
Write-Host "[7/8] Construction des alertes..."
# ── Alerte SMART WMI ─────────────────────────────────────────────────
$alerteSMART = if ($smartStatus) {
$defaillants = $smartStatus | Where-Object { $_.DefaillancePredite -eq $true }
if ($defaillants) {
"⚠️ ALERTE CRITIQUE : défaillance SMART prédite sur $(@($defaillants).Count) disque(s) — SAUVEGARDER IMMÉDIATEMENT"
} else { "✅ Aucune défaillance SMART prédite" }
} else { "⚠️ SMART non disponible via WMI — utiliser CrystalDiskInfo" }
# [MOD-P04-3] Alerte StorageReliabilityCounter
$alerteReliability = if ($smartReliability) {
$alertesRel = @()
foreach ($r in $smartReliability) {
if ($r.Usure_Pct -ne 'N/A' -and [int]$r.Usure_Pct -ge 90) { $alertesRel += "⚠️ $($r.Disque) : usure $($r.Usure_Pct)% → remplacement préventif recommandé" }
if ($r.Temperature_C -ne 'N/A' -and [int]$r.Temperature_C -ge 65) { $alertesRel += "⚠️ $($r.Disque) : température $($r.Temperature_C)°C → vérifier ventilation" }
if ($r.LecturesErreurs -ne 'N/A' -and [int]$r.LecturesErreurs -gt 0) { $alertesRel += "⚠️ $($r.Disque) : $($r.LecturesErreurs) erreur(s) de lecture" }
if ($r.EcrituresErreurs -ne 'N/A' -and [int]$r.EcrituresErreurs -gt 0) { $alertesRel += "⚠️ $($r.Disque) : $($r.EcrituresErreurs) erreur(s) d'écriture" }
}
if ($alertesRel) { $alertesRel -join ' | ' } else { "✅ StorageReliability OK (usure, temp, erreurs normaux)" }
} else { "ℹ️ Non disponible (module Storage ou droits insuffisants)" }
# ── Alerte santé disques Storage ─────────────────────────────────────
$alerteSante = if ($disquesStorage) {
$malades = $disquesStorage | Where-Object { $_.HealthStatus -ne 'Healthy' -and $_.HealthStatus -ne $null }
if ($malades) {
"⚠️ $(@($malades).Count) disque(s) avec HealthStatus anormal : $(($malades | ForEach-Object { "$($_.FriendlyName)=$($_.HealthStatus)" }) -join ', ')"
} else { "✅ Tous les disques : HealthStatus = Healthy" }
} else { "⚠️ Get-PhysicalDisk non disponible" }
# ── Alerte espace disque ──────────────────────────────────────────────
$alertesEspace = @()
if ($volumes) {
foreach ($v in $volumes) {
if ($v.LibrePct -lt 10) { $alertesEspace += "⚠️ CRITIQUE $($v.DeviceID) : $($v.LibrePct)% libre ($($v.LibreGB) Go)" }
elseif ($v.LibrePct -lt 20) { $alertesEspace += "⚠️ ATTENTION $($v.DeviceID) : $($v.LibrePct)% libre ($($v.LibreGB) Go)" }
}
}
$alerteEspace = if ($alertesEspace) { $alertesEspace -join ' | ' } else { "✅ Espace disque OK" }
# ── Alerte intégrité volumes ──────────────────────────────────────────
$alerteVolumes = if ($volumesStorage) {
$ko = $volumesStorage | Where-Object { $_.HealthStatus -ne 'Healthy' -and $_.HealthStatus -ne $null }
if ($ko) { "⚠️ $(@($ko).Count) volume(s) avec HealthStatus anormal" } else { "✅ Tous les volumes sains" }
} else { "ℹ️ Module Storage non disponible" }
# [MOD-P04-4] Alertes I/O différenciées critique vs standard
$ioCount_critique = if ($resIO.Events) {
@($resIO.Events | Where-Object { $_.Id -in @(129, 153, 157) }).Count
} else { 0 }
$ioCount_standard = if ($resIO.Events) {
@($resIO.Events | Where-Object { $_.Id -in @(11, 51) }).Count
} else { 0 }
$alerteIO = if ($ioCount_critique -gt 0) {
"⚠️ CRITIQUE : $ioCount_critique reset(s)/timeout(s) contrôleur (ID129/153/157) → investigation urgente"
} elseif ($ioCount_standard -gt 3) {
"⚠️ $ioCount_standard erreur(s) I/O/pagination (ID11/51) → à surveiller"
} elseif ($resIO.Count -gt 0) {
"ℹ️ $($resIO.Count) événement(s) I/O détectés — niveau faible"
} else {
"✅ Aucun événement I/O disque détecté"
}
$alerteNTFS = if ($resNTFS.Count -gt 0) { "⚠️ ALERTE : $($resNTFS.Count) événement(s) corruption NTFS détecté(s)" } else { "✅ Aucune corruption NTFS détectée" }
# ── Alerte chkdsk ─────────────────────────────────────────────────────
$alerteChkdsk = @()
foreach ($k in $chkdskStatus.Keys) {
if ($chkdskStatus[$k] -match 'planifié|scheduled|will be checked') {
$alerteChkdsk += "⚠️ chkdsk planifié sur $k au prochain démarrage"
}
}
$alerteChkdskStr = if ($alerteChkdsk) { $alerteChkdsk -join ' | ' } else { "✅ Aucun chkdsk planifié" }
# ── Résumé faux positifs écartés ─────────────────────────────────────
$alerteFP = if ($resIO.EcartesFP -gt 0) {
"ℹ️ $($resIO.EcartesFP) événement(s) écartés (faux positifs provider non-disque)"
} else { "✅ Aucun faux positif provider détecté" }
Write-Host "[8/8] Construction du rapport..."
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT STOCKAGE, SMART & ERREURS I/O v1.1 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p04-stockage-smart-erreurs-i-o.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Disques : $(if ($disquesCIM) { ($disquesCIM | ForEach-Object { "$($_.Model) ($($_.TailleGB) Go)" }) -join ' | ' } else { 'N/A' })
ALERTES STOCKAGE — PRIORISER LA SAUVEGARDE SI ALERTE CRITIQUE
|- SMART (WMI) : $alerteSMART
|- SMART étendu (Reliab.) : $alerteReliability
|- Santé disques : $alerteSante
|- Intégrité volumes : $alerteVolumes
|- Espace disque : $alerteEspace
|- Événements I/O (réels) : $alerteIO
|- Faux positifs écartés : $alerteFP
|- Corruption NTFS : $alerteNTFS
|- Chkdsk planifié : $alerteChkdskStr
FENÊTRES RETENUES PAR BLOC
|- I/O Disque (System) : $($resIO.WindowHours) h -> $($resIO.Count) événements réels ($($resIO.EcartesFP) faux positifs écartés)
|- NTFS Corruption : $($resNTFS.WindowHours) h -> $($resNTFS.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Disques physiques (CIM) ──────────────────────────────
"`n── DISQUES PHYSIQUES — Win32_DiskDrive ────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($disquesCIM) {
$disquesCIM | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Win32_DiskDrive inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Disques physiques (Storage) ──────────────────────────
"`n── DISQUES PHYSIQUES — Get-PhysicalDisk (santé opérationnelle) ────" |
Out-File $outputFile -Append -Encoding UTF8
if ($disquesStorage) {
$disquesStorage | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" $alerteSante" | Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (module Storage non présent — Windows Server Core ou droits insuffisants)" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 3 : SMART synthétique WMI ────────────────────────────────
"`n── SMART — DÉFAILLANCE PRÉDITE (MSStorageDriver_FailurePredictStatus) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($smartStatus) {
$smartStatus | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" $alerteSMART" | Out-File $outputFile -Append -Encoding UTF8
if ($smartStatus | Where-Object { $_.DefaillancePredite -eq $true }) {
"`n ⚠️ ACTION IMMÉDIATE REQUISE : effectuer une sauvegarde complète avant toute intervention." |
Out-File $outputFile -Append -Encoding UTF8
" Utiliser Clonezilla, Macrium Reflect ou Windows Backup pour cloner/sauvegarder le disque." |
Out-File $outputFile -Append -Encoding UTF8
}
} else {
" Non disponible via WMI (namespace root\wmi non accessible ou pilote SMART absent)." |
Out-File $outputFile -Append -Encoding UTF8
" → Utiliser CrystalDiskInfo et coller l'export dans la section DONNÉES MANUELLES." |
Out-File $outputFile -Append -Encoding UTF8
}
# [MOD-P04-3] Section SMART étendu StorageReliabilityCounter
"`n── SMART ÉTENDU — StorageReliabilityCounter ────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($smartReliability) {
$smartReliability | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" $alerteReliability" | Out-File $outputFile -Append -Encoding UTF8
" Note : Wear=% d'endurance consommée | Temp en °C | Erreurs = compteurs cumulés depuis fabrication" |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (Get-StorageReliabilityCounter — module Storage ou droits insuffisants)." |
Out-File $outputFile -Append -Encoding UTF8
" → Utiliser CrystalDiskInfo pour les attributs détaillés." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : Volumes logiques ─────────────────────────────────────
"`n── VOLUMES LOGIQUES — Espace libre ────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($volumes) {
$volumes | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
if ($alertesEspace) {
foreach ($a in $alertesEspace) { " $a" | Out-File $outputFile -Append -Encoding UTF8 }
} else { " ✅ Espace disponible suffisant sur tous les volumes." | Out-File $outputFile -Append -Encoding UTF8 }
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 5 : Volumes Storage (état intégrité) ─────────────────────
"`n── VOLUMES — État intégrité (Get-Volume) ───────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($volumesStorage) {
$volumesStorage | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (module Storage absent)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 6 : Partitions ───────────────────────────────────────────
"`n── PARTITIONS ──────────────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($partitions) {
$partitions | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (module Storage absent)" | Out-File $outputFile -Append -Encoding UTF8 }
# [MOD-P04-2] Section mapping device path
"`n── MAPPING \Device\HarddiskX → DISQUE PHYSIQUE ─────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($diskMapping) {
$diskMapping | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" Utiliser ce tableau pour identifier \Device\HarddiskX dans les erreurs I/O Event Viewer." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (Get-Disk inaccessible ou droits insuffisants)." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 7 : Chkdsk planifié ──────────────────────────────────────
"`n── CHKDSK — VÉRIFICATION PLANIFIÉE AU PROCHAIN BOOT ───────────────" |
Out-File $outputFile -Append -Encoding UTF8
foreach ($lettre in $chkdskStatus.Keys) {
" $lettre : $($chkdskStatus[$lettre])" | Out-File $outputFile -Append -Encoding UTF8
}
" $alerteChkdskStr" | Out-File $outputFile -Append -Encoding UTF8
# ── Section 8 : VSS Writers ──────────────────────────────────────────
"`n── VSS WRITERS — État ──────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($vssWriters -is [string] -and $vssWriters -match 'Non disponible') {
" $vssWriters" | Out-File $outputFile -Append -Encoding UTF8
} else {
$vssWriters | Out-File $outputFile -Append -Encoding UTF8
$vssErreurs = $vssWriters | Where-Object { $_ -match 'Failed|En échec|Stable with errors' }
if ($vssErreurs) {
"`n ⚠️ Writers VSS en erreur détectés — impact possible sur les sauvegardes." |
Out-File $outputFile -Append -Encoding UTF8
$vssErreurs | Out-File $outputFile -Append -Encoding UTF8
}
}
# ── Section 9 : Événements I/O disque (filtrés) ──────────────────────
"`n── ÉVÉNEMENTS I/O DISQUE — System ($($resIO.Count) evt réels / fenêtre $($resIO.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
" [MOD-P04-1] Filtrage ProviderName actif — $($resIO.EcartesFP) faux positif(s) écartés" |
Out-File $outputFile -Append -Encoding UTF8
if ($resIO.Events -and $resIO.Count -gt 0) {
"`n Résumé par ID (après filtrage) :" | Out-File $outputFile -Append -Encoding UTF8
$resIO.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'11' { "Erreur I/O contrôleur" }
'51' { "Erreur pagination" }
'129' { "Reset contrôleur StorPort" }
'153' { "Timeout I/O StorPort" }
'157' { "Disque retiré de façon inattendue" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
# [MOD-P04-4] Détail critique séparé
$evtCritiques = $resIO.Events | Where-Object { $_.Id -in @(129, 153, 157) }
if ($evtCritiques) {
"`n -- Événements critiques (reset/timeout/retrait) : $(@($evtCritiques).Count) --" |
Out-File $outputFile -Append -Encoding UTF8
$evtCritiques | Sort-Object TimeCreated -Descending |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={ ($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length)) }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
}
"`n Détail (20 plus récents — tous types) :" | Out-File $outputFile -Append -Encoding UTF8
$resIO.Events | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
if ($resIO.EcartesFP -gt 0) {
"`n -- Faux positifs écartés ($($resIO.EcartesFP) total — non I/O disque) --" |
Out-File $outputFile -Append -Encoding UTF8
$ioEventsEcartes | Group-Object { "$($_.Id) / $($_.ProviderName)" } | Sort-Object Count -Descending |
ForEach-Object { " ID $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
}
} else {
" ✅ Aucun événement I/O disque réel détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Événements corruption NTFS ──────────────────────────
"`n── ÉVÉNEMENTS CORRUPTION NTFS ($($resNTFS.Count) evt / fenêtre $($resNTFS.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resNTFS.Events -and $resNTFS.Count -gt 0) {
" $alerteNTFS" | Out-File $outputFile -Append -Encoding UTF8
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resNTFS.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'55' { "Corruption structure FS" }
'98' { "Volume passé en lecture seule" }
'130' { "Écriture sur volume RO" }
'131' { "Impossible allouer espace" }
'137' { "Journal non écrit" }
'140' { "Mode journalisation dégradé" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resNTFS.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName, ProviderName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucune corruption NTFS détectée dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 11 : Données manuelles attendues ─────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptômes observés (freeze, bruit anormal, lenteur, erreur, corruption...)
[ ] Export CrystalDiskInfo — Menu Fichier > Enregistrer le rapport texte
(coller le contenu complet du fichier .txt ici)
[ ] Résultat chkdsk /scan C: si déjà lancé
Commande : chkdsk C: /scan
(coller la sortie complète ici)
[ ] Âge estimé du disque (si connu — ex: "acheté en 2019, ~5 ans")
[ ] Sauvegarde récente disponible : OUI / NON / PARTIELLE
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P04 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Si SMART indique défaillance prédite → SAUVEGARDER AVANT TOUTE INTERVENTION"
Write-Host " 2. Consulter la section SMART ÉTENDU (StorageReliabilityCounter)"
Write-Host " 3. Ouvrir CrystalDiskInfo et coller l'export dans la section DONNÉES MANUELLES"
Write-Host " 4. Compléter les symptômes et l'état de sauvegarde dans le fichier"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS diagnostic stockage [file]
- Symptômes observés (freeze, bruit, lenteur, corruption, message d'erreur)
- Export CrystalDiskInfo si disponible [file]
- Résultat chkdsk /scan si disponible [file]
- Âge estimé du disque
- Présence d'une sauvegarde récente (oui/non)
P05 — Réseau & Connectivité Poste
Diagnostic méthodique par couches des problèmes réseau sur poste Windows 10/11 : TCP/IP, DNS, DHCP, Wi-Fi, VPN, pilotes adaptateurs. Distingue les causes poste des causes infrastructure.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p05-reseau-connectivite-poste_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : Adaptateurs réseau · Config IP/DNS/DHCP · Tests connectivité par couche · Pare-feu · Proxy · Profils Wi-Fi · nslookup · Event IDs réseau/services
Données à saisir manuellement : Fiche de triage P01 · Type de connexion · Symptôme précis · Tests déjà effectués · Nombre de postes impactés
# ════════════════════════════════════════════════════════════════════
# COLLECTE RÉSEAU & CONNECTIVITÉ POSTE v1.0
# Périmètre : adaptateurs, IP, DNS, DHCP, connectivité, pare-feu,
# Wi-Fi, VPN, événements réseau
#
# Profil cible : P05 — Réseau & Connectivité Poste
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p05-reseau-connectivite-poste_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ── Fonction test connectivité ────────────────────────────────────────
function Test-Connectivite {
param([string]$Cible, [string]$Label, [int]$Port = 0)
try {
if ($Port -gt 0) {
$r = Test-NetConnection -ComputerName $Cible -Port $Port -WarningAction SilentlyContinue
$ok = $r.TcpTestSucceeded
$latence = if ($r.PingReplyDetails) { "$($r.PingReplyDetails.RoundtripTime) ms" } else { 'N/A' }
return [PSCustomObject]@{
Cible = $Cible
Label = $Label
Port = $Port
Résultat = if ($ok) { "✅ OK" } else { "⚠️ ECHEC" }
Latence = $latence
}
} else {
$r = Test-NetConnection -ComputerName $Cible -WarningAction SilentlyContinue
$latence = if ($r.PingReplyDetails) { "$($r.PingReplyDetails.RoundtripTime) ms" } else { 'N/A' }
return [PSCustomObject]@{
Cible = $Cible
Label = $Label
Port = 'ICMP'
Résultat = if ($r.PingSucceeded) { "✅ OK" } else { "⚠️ ECHEC" }
Latence = $latence
}
}
} catch {
return [PSCustomObject]@{
Cible = $Cible
Label = $Label
Port = $Port
Résultat = "❌ Erreur ($($_.Exception.Message.Substring(0,[Math]::Min(60,$_.Exception.Message.Length))))"
Latence = 'N/A'
}
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (adaptateurs, IP, config)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte adaptateurs réseau..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# Adaptateurs — état complet
try {
$adaptateurs = Get-NetAdapter |
Select-Object Name, InterfaceDescription, Status,
LinkSpeed, MacAddress, MediaType,
@{N='DriverVersion'; E={ (Get-NetAdapterAdvancedProperty $_.Name -RegistryKeyword DriverVersion -ErrorAction SilentlyContinue).RegistryValue }},
InterfaceIndex
} catch { $adaptateurs = $null }
# Adaptateurs actifs uniquement
try {
$adaptActifs = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
} catch { $adaptActifs = $null }
Write-Host "[2/8] Collecte configuration IP..."
# ipconfig /all brut — conservé car plus fiable pour bail DHCP et suffixe DNS
try {
$ipconfigBrut = & ipconfig /all 2>&1
} catch { $ipconfigBrut = "Non disponible (ipconfig inaccessible)" }
# Configuration IP via CIM (structurée pour parsing)
try {
$ipConfig = Get-CimInstance Win32_NetworkAdapterConfiguration |
Where-Object { $_.IPEnabled -eq $true } |
Select-Object Description,
@{N='IPAddress'; E={ $_.IPAddress -join ', ' }},
@{N='SubnetMask'; E={ $_.IPSubnet -join ', ' }},
@{N='DefaultGateway'; E={ $_.DefaultIPGateway -join ', ' }},
@{N='DNS'; E={ $_.DNSServerSearchOrder -join ', ' }},
DHCPEnabled,
@{N='DHCPServer'; E={ $_.DHCPServer }},
@{N='DHCPLeaseObt'; E={ if ($_.DHCPLeaseObtained) { $_.DHCPLeaseObtained.ToString('dd/MM/yyyy HH:mm') } else { 'N/A' } }},
@{N='DHCPLeaseExp'; E={ if ($_.DHCPLeaseExpires) { $_.DHCPLeaseExpires.ToString('dd/MM/yyyy HH:mm') } else { 'N/A' } }}
} catch { $ipConfig = $null }
# Récupération passerelle pour les tests
try {
$passerelle = (Get-CimInstance Win32_NetworkAdapterConfiguration |
Where-Object { $_.DefaultIPGateway } |
Select-Object -First 1).DefaultIPGateway | Select-Object -First 1
} catch { $passerelle = $null }
# DNS configuré
try {
$dnsServeurs = (Get-CimInstance Win32_NetworkAdapterConfiguration |
Where-Object { $_.DNSServerSearchOrder } |
Select-Object -First 1).DNSServerSearchOrder
$dns1 = if ($dnsServeurs) { $dnsServeurs[0] } else { $null }
} catch { $dns1 = $null }
Write-Host "[3/8] Table de routage et pare-feu..."
# Routes actives (synthèse — top 20 routes non-locales)
try {
$routes = Get-NetRoute -AddressFamily IPv4 |
Where-Object { $_.RouteMetric -lt 999 -and $_.DestinationPrefix -ne '255.255.255.255/32' } |
Sort-Object RouteMetric |
Select-Object -First 20 |
Select-Object DestinationPrefix, NextHop, RouteMetric,
@{N='Interface'; E={ (Get-NetAdapter -InterfaceIndex $_.InterfaceIndex -ErrorAction SilentlyContinue).Name }}
} catch { $routes = $null }
# Pare-feu Windows — profils
try {
$parefeu = Get-NetFirewallProfile |
Select-Object Name, Enabled, DefaultInboundAction, DefaultOutboundAction, LogAllowed, LogBlocked
} catch { $parefeu = $null }
# Proxy configuré (registre)
try {
$proxyReg = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' `
-ErrorAction SilentlyContinue |
Select-Object ProxyEnable, ProxyServer, ProxyOverride, AutoConfigURL
$proxyActif = if ($proxyReg -and $proxyReg.ProxyEnable -eq 1) {
"⚠️ PROXY ACTIF : $($proxyReg.ProxyServer)"
} else { "✅ Pas de proxy manuel configuré" }
} catch { $proxyReg = $null; $proxyActif = "Non disponible" }
# Profils Wi-Fi enregistrés (noms uniquement)
try {
# Forcer encodage OEM pour netsh (evite les caracteres corrompus)
$prevEncoding = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(850)
$netshRaw = & netsh wlan show profiles 2>$null
[Console]::OutputEncoding = $prevEncoding
$wifiProfiles = $netshRaw |
Where-Object { $_ -match 'Profil utilisateur\s*:|User Profile\s*:|All User Profile\s*:' } |
ForEach-Object { ($_ -split ':', 2)[1].Trim() } |
Where-Object { $_ -and $_.Length -gt 0 }
$nbWifi = if ($wifiProfiles) { @($wifiProfiles).Count } else { 0 }
} catch { $wifiProfiles = $null; $nbWifi = 0 }
# Wi-Fi actuel (SSID connecté)
try {
$prevEncoding2 = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(850)
$wifiActuel = & netsh wlan show interfaces 2>$null
[Console]::OutputEncoding = $prevEncoding2
} catch { $wifiActuel = "Non disponible (Wi-Fi absent ou désactivé)" }
Write-Host "[4/8] Tests de connectivité séquencés (couche par couche)..."
$testsConnectivite = @()
# Couche 1 — Passerelle (couche IP locale)
if ($passerelle) {
$testsConnectivite += Test-Connectivite -Cible $passerelle -Label "Passerelle ($passerelle)"
} else {
$testsConnectivite += [PSCustomObject]@{ Cible='N/A'; Label='Passerelle'; Port='ICMP'; Résultat='⚠️ Aucune passerelle configurée'; Latence='N/A' }
}
# Couche 2 — DNS configuré (résolution interne)
if ($dns1) {
$testsConnectivite += Test-Connectivite -Cible $dns1 -Label "DNS configuré ($dns1)"
} else {
$testsConnectivite += [PSCustomObject]@{ Cible='N/A'; Label='DNS configuré'; Port='ICMP'; Résultat='⚠️ Aucun DNS configuré'; Latence='N/A' }
}
# Couche 3 — Internet (IP — sans résolution DNS)
$testsConnectivite += Test-Connectivite -Cible '8.8.8.8' -Label "Internet IP (8.8.8.8)"
# Couche 4 — Internet (nom — avec résolution DNS)
$testsConnectivite += Test-Connectivite -Cible 'www.google.com' -Label "Internet DNS (www.google.com)"
# Couche 5 — DNS alternatif (discriminant DNS poste vs DNS infra)
$testsConnectivite += Test-Connectivite -Cible '1.1.1.1' -Label "DNS alternatif Cloudflare (1.1.1.1)"
# Couche 6 — M365 (port 443 — signal P10)
$testsConnectivite += Test-Connectivite -Cible 'outlook.office365.com' -Label "M365 Outlook (port 443)" -Port 443
Write-Host "[5/8] Test de résolution DNS (nslookup)..."
# nslookup avec DNS configuré
try {
# 2>$null supprime "Reponse ne faisant pas autorite" capture comme erreur PS
$nslookupConfigured = & nslookup www.google.com $dns1 2>$null
} catch { $nslookupConfigured = "Non disponible" }
# nslookup avec DNS alternatif (discriminant)
try {
$nslookupAlternatif = & nslookup www.google.com 8.8.8.8 2>$null
} catch { $nslookupAlternatif = "Non disponible" }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements réseau)
# ════════════════════════════════════════════════════════════════════
Write-Host "[6/8] Collecte événements services réseau (ID 7001, 7036)..."
# ID 7001 : Service Control Manager — dépendance service non démarrée
# ID 7036 : Service Control Manager — service démarré ou arrêté
$resSvcReseau = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(7001, 7036) `
-Label 'Services réseau' `
-MinEvents 5 `
-MaxEvents 60
Write-Host "[7/8] Collecte événements WLAN et VPN..."
# WLAN AutoConfig (ID 4202 déconnexion, 5000 association, 5001 désassociation)
try {
$resWLAN = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-WLAN-AutoConfig/Operational' `
-Ids @(4202, 5000, 5001, 11001, 11002) `
-Label 'WLAN' `
-MinEvents 5 `
-MaxEvents 60
} catch {
$resWLAN = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'WLAN' }
}
# VPN RasClient (ID 20221 connecté, 20226 déconnecté, 20227 échec)
try {
$resVPN = Get-EventsAdaptatif `
-LogNames 'Application' `
-Ids @(20221, 20226, 20227) `
-Label 'VPN RasClient' `
-MinEvents 3 `
-MaxEvents 40
} catch {
$resVPN = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'VPN RasClient' }
}
Write-Host "[8/8] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerteAdapt = if ($adaptActifs) {
"✅ $(@($adaptActifs).Count) adaptateur(s) actif(s) : $(($adaptActifs | ForEach-Object { $_.Name }) -join ', ')"
} else { "⚠️ Aucun adaptateur réseau actif détecté" }
$alerteGW = if ($testsConnectivite | Where-Object { $_.Label -like 'Passerelle*' -and $_.Résultat -like '✅*' }) {
"✅ Passerelle joignable"
} else { "⚠️ Passerelle non joignable — problème couche physique ou IP locale" }
$alerteInternet = if ($testsConnectivite | Where-Object { $_.Label -like 'Internet IP*' -and $_.Résultat -like '✅*' }) {
"✅ Accès Internet (IP) OK"
} else { "⚠️ Accès Internet IP échoué — vérifier routeur/FAI" }
$alerteDNS = if (
($testsConnectivite | Where-Object { $_.Label -like 'Internet IP*' -and $_.Résultat -like '✅*' }) -and
-not ($testsConnectivite | Where-Object { $_.Label -like 'Internet DNS*' -and $_.Résultat -like '✅*' })
) { "⚠️ DNS défaillant (IP ok mais résolution nom échoue) → vérifier DNS poste" }
else { "✅ Résolution DNS fonctionnelle" }
$alerteProxy = $proxyActif
$alerteWLAN = if ($resWLAN.Count -gt 5) { "⚠️ $($resWLAN.Count) événement(s) WLAN (déconnexions/associations)" } else { "✅ Peu d'événements WLAN ($($resWLAN.Count))" }
$alerteVPN = if ($resVPN.Count -gt 0) { "ℹ️ $($resVPN.Count) événement(s) VPN détecté(s) (connexion/déconnexion)" } else { "✅ Aucun événement VPN" }
$alerteSvcRs = if ($resSvcReseau.Count -gt 10) { "⚠️ $($resSvcReseau.Count) événements services réseau — instabilité possible" } else { "✅ Services réseau stables ($($resSvcReseau.Count) evt)" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT RÉSEAU & CONNECTIVITÉ POSTE v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p05-reseau-connectivite-poste.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Passerelle : $(if ($passerelle) { $passerelle } else { 'Non configurée' })
DNS : $(if ($dns1) { $dns1 } else { 'Non configuré' })
ALERTES RÉSEAU — DIAGNOSTIC PAR COUCHE
|- Adaptateurs actifs : $alerteAdapt
|- Couche IP locale : $alerteGW
|- Accès Internet IP : $alerteInternet
|- Résolution DNS : $alerteDNS
|- Proxy : $alerteProxy
|- Wi-Fi (événements) : $alerteWLAN
|- VPN (événements) : $alerteVPN
|- Services réseau : $alerteSvcRs
RÉSUMÉ TESTS DE CONNECTIVITÉ
$( $testsConnectivite | ForEach-Object { " |- $($_.Label.PadRight(40)) $($_.Résultat) ($($_.Latence))" })
FENÊTRES RETENUES PAR BLOC
|- Services réseau : $($resSvcReseau.WindowHours) h -> $($resSvcReseau.Count) événements
|- WLAN : $($resWLAN.WindowHours) h -> $($resWLAN.Count) événements
|- VPN RasClient : $($resVPN.WindowHours) h -> $($resVPN.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Adaptateurs réseau ───────────────────────────────────
"`n── ADAPTATEURS RÉSEAU ──────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($adaptateurs) {
$adaptateurs | Format-Table Name, InterfaceDescription, Status, LinkSpeed, MediaType, MacAddress -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Get-NetAdapter inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Configuration IP (structurée) ────────────────────────
"`n── CONFIGURATION IP (CIM) ──────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($ipConfig) {
$ipConfig | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : ipconfig /all brut ───────────────────────────────────
"`n── IPCONFIG /ALL (brut) ────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
$ipconfigBrut | Out-File $outputFile -Append -Encoding UTF8
# ── Section 4 : Tests de connectivité séquencés ──────────────────────
"`n── TESTS DE CONNECTIVITÉ — RÉSULTATS PAR COUCHE ───────────────────" |
Out-File $outputFile -Append -Encoding UTF8
"`n Logique de lecture :" | Out-File $outputFile -Append -Encoding UTF8
" Passerelle ✅ + Internet IP ⚠️ → problème routeur/FAI ou filtrage" |
Out-File $outputFile -Append -Encoding UTF8
" Internet IP ✅ + Internet DNS ⚠️ → DNS poste défaillant" |
Out-File $outputFile -Append -Encoding UTF8
" DNS configuré ⚠️ + DNS alternatif ✅ → DNS poste incorrect" |
Out-File $outputFile -Append -Encoding UTF8
"`n Résultats :" | Out-File $outputFile -Append -Encoding UTF8
$testsConnectivite | Format-Table Cible, Label, Port, Résultat, Latence -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
# ── Section 5 : Résolution DNS (nslookup) ────────────────────────────
"`n── RÉSOLUTION DNS (nslookup) ───────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
"`n Via DNS configuré ($dns1) :" | Out-File $outputFile -Append -Encoding UTF8
$nslookupConfigured | Out-File $outputFile -Append -Encoding UTF8
"`n Via DNS alternatif (8.8.8.8) — valeur discriminante :" |
Out-File $outputFile -Append -Encoding UTF8
$nslookupAlternatif | Out-File $outputFile -Append -Encoding UTF8
# ── Section 6 : Table de routage ─────────────────────────────────────
"`n── TABLE DE ROUTAGE (20 routes actives) ────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($routes) {
$routes | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Get-NetRoute inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 7 : Pare-feu Windows ─────────────────────────────────────
"`n── PARE-FEU WINDOWS — PROFILS ──────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($parefeu) {
$parefeu | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
$profilsActifs = $parefeu | Where-Object { $_.Enabled -eq $true }
if (-not $profilsActifs) {
" ⚠️ Pare-feu Windows désactivé sur tous les profils — vérifier si un pare-feu tiers est actif." |
Out-File $outputFile -Append -Encoding UTF8
}
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 8 : Proxy ────────────────────────────────────────────────
"`n── CONFIGURATION PROXY ─────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteProxy" | Out-File $outputFile -Append -Encoding UTF8
if ($proxyReg) {
" ProxyEnable : $($proxyReg.ProxyEnable)" | Out-File $outputFile -Append -Encoding UTF8
" ProxyServer : $($proxyReg.ProxyServer)" | Out-File $outputFile -Append -Encoding UTF8
" AutoConfigURL : $($proxyReg.AutoConfigURL)" | Out-File $outputFile -Append -Encoding UTF8
" ProxyOverride : $($proxyReg.ProxyOverride)" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Wi-Fi ────────────────────────────────────────────────
"`n── WI-FI — INTERFACE ACTIVE ET PROFILS ENREGISTRÉS ────────────────" |
Out-File $outputFile -Append -Encoding UTF8
"`n Interface Wi-Fi active :" | Out-File $outputFile -Append -Encoding UTF8
$wifiActuel | Out-File $outputFile -Append -Encoding UTF8
if ($nbWifi -gt 0) {
"`n Profils Wi-Fi enregistrés ($nbWifi) — noms uniquement :" |
Out-File $outputFile -Append -Encoding UTF8
$wifiProfiles | ForEach-Object { " · $_" } | Out-File $outputFile -Append -Encoding UTF8
} else {
"`n Aucun profil Wi-Fi enregistré (ou Wi-Fi absent)." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Événements services réseau ───────────────────────────
"`n── ÉVÉNEMENTS SERVICES RÉSEAU — ID 7001/7036 ($($resSvcReseau.Count) evt / $($resSvcReseau.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resSvcReseau.Events -and $resSvcReseau.Count -gt 0) {
# Filtrer sur les services réseau connus
$svcReseauNoms = @('Dnscache','Dhcp','NlaSvc','netprofm','WlanSvc','RasMan','RemoteAccess',
'LanmanWorkstation','LanmanServer','Netlogon','W32Time','PolicyAgent')
$evtReseauFiltres = $resSvcReseau.Events | Where-Object {
$msg = $_.Message
$svcReseauNoms | Where-Object { $msg -match $_ }
}
if ($evtReseauFiltres) {
"`n Événements services réseau spécifiques :" | Out-File $outputFile -Append -Encoding UTF8
$evtReseauFiltres | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
}
"`n Tous événements 7001/7036 (résumé par service) :" | Out-File $outputFile -Append -Encoding UTF8
$resSvcReseau.Events | Group-Object {
if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' }
} | Sort-Object Count -Descending | Select-Object -First 15 |
ForEach-Object { " $($_.Name) : $($_.Count) occurrence(s)" } |
Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Peu ou aucun événement services réseau détecté." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 11 : Événements WLAN ─────────────────────────────────────
"`n── ÉVÉNEMENTS WLAN — AutoConfig ($($resWLAN.Count) evt / $($resWLAN.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resWLAN.Events -and $resWLAN.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resWLAN.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'4202' { "Déconnexion réseau Wi-Fi" }
'5000' { "Association réseau Wi-Fi" }
'5001' { "Désassociation réseau Wi-Fi" }
'11001' { "Connexion Wi-Fi réussie" }
'11002' { "Échec connexion Wi-Fi" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resWLAN.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun événement WLAN détecté (Wi-Fi absent, désactivé ou stable)." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 12 : Événements VPN ──────────────────────────────────────
"`n── ÉVÉNEMENTS VPN — RasClient ($($resVPN.Count) evt / $($resVPN.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resVPN.Events -and $resVPN.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resVPN.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'20221' { "Connexion VPN réussie" }
'20226' { "Déconnexion VPN" }
'20227' { "Échec connexion VPN" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resVPN.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun événement VPN détecté." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P05 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " LOGIQUE DE LECTURE DES RÉSULTATS :"
Write-Host " Passerelle ✅ + Internet IP ⚠️ → problème routeur/FAI ou filtrage"
Write-Host " Internet IP ✅ + Internet DNS ⚠️ → DNS poste défaillant"
Write-Host " DNS configuré ⚠️ + DNS alternat ✅ → changer DNS poste vers 8.8.8.8"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS diagnostic réseau [file]
- Type de connexion (filaire, Wi-Fi, VPN)
- Symptôme précis (pas d'accès, lenteur, coupures, DNS, VPN)
- Tests déjà effectués
- Nombre de postes impactés (isolé ou collectif)
P06 — Incidents Applicatifs
Diagnostic des dysfonctionnements applicatifs sur poste Windows 10/11 : crashs, erreurs au lancement, dysfonctionnements silencieux. Analyse Event ID 1000/1001, runtimes, droits, profils utilisateurs et dépendances.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p06-incidents-applicatifs_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : OS · Runtimes installés (.NET, VC++) · Event IDs applicatifs (1000/1001/1002) · Programmes démarrage · Services · KB récentes
Données à saisir manuellement : Fiche de triage P01 · Nom et version de l'application · Message d'erreur exact · Contexte du dysfonctionnement · Dernière action avant l'erreur · Test avec autre compte
# ════════════════════════════════════════════════════════════════════
# COLLECTE INCIDENTS APPLICATIFS v1.0
# Périmètre : crashs applicatifs, modules fautifs, codes exception,
# runtimes (VC++, .NET), applications installées
#
# Profil cible : P06 — Incidents Applicatifs
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p06-incidents-applicatifs_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ── Table des codes d'exception courants ─────────────────────────────
function Get-ExceptionLabel {
param([string]$Code)
$codes = @{
'0xc0000005' = 'Access Violation (lecture/écriture mémoire interdite)'
'0xc000001d' = 'Illegal Instruction'
'0xc0000034' = 'Object Name Not Found'
'0xc0000409' = 'Stack Buffer Overrun'
'0xc00000fd' = 'Stack Overflow'
'0x80000003' = 'Breakpoint (mode debug actif ?)'
'0x80000004' = 'Single Step Exception'
'0xe0434352' = 'Exception .NET CLR (crash .NET)'
'0xe0564552' = 'Exception VBScript/ActiveX'
'0xc0020001' = 'RPC_S_OUT_OF_RESOURCES'
'0x40000015' = 'STATUS_FATAL_USER_CALLBACK_EXCEPTION'
}
$key = $Code.ToLower()
if ($codes.ContainsKey($key)) { return $codes[$key] }
return 'Code non répertorié'
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (runtimes, apps installées)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/6] Collecte configuration OS..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
Write-Host "[2/6] Collecte redistribuables Visual C++..."
# Redistribuables VC++ — registre 32 et 64 bits
try {
$regPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$vcRedist = foreach ($path in $regPaths) {
Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like '*Visual C++*' -or $_.DisplayName -like '*VC++ *' } |
Select-Object DisplayName, DisplayVersion,
@{N='Architecture'; E={ if ($path -like '*WOW6432*') { 'x86' } else { 'x64' } }},
@{N='InstalléLe'; E={ $_.InstallDate }}
}
$vcRedist = $vcRedist | Sort-Object DisplayName
} catch { $vcRedist = $null }
Write-Host "[3/6] Collecte versions .NET Framework..."
# .NET Framework — clés NDP dans le registre
try {
$dotnetVersions = @()
# .NET 1.0 à 4.x
$ndpPath = 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP'
if (Test-Path $ndpPath) {
Get-ChildItem $ndpPath -ErrorAction SilentlyContinue | ForEach-Object {
$ver = $_.PSChildName
$props = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue
if ($props.Version) {
$dotnetVersions += [PSCustomObject]@{
Version = "NET $ver"
Build = $props.Version
SP = if ($props.SP) { "SP$($props.SP)" } else { '' }
Installé = if ($props.Install -eq 1) { 'Oui' } else { 'Non' }
}
}
# Sous-clés (ex: v4\Full, v4\Client)
Get-ChildItem $_.PSPath -ErrorAction SilentlyContinue | ForEach-Object {
$subProps = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue
if ($subProps.Version -and $subProps.Install -eq 1) {
$dotnetVersions += [PSCustomObject]@{
Version = "NET $ver\$($_.PSChildName)"
Build = $subProps.Version
SP = if ($subProps.SP) { "SP$($subProps.SP)" } else { '' }
Installé = 'Oui'
}
}
}
}
}
# .NET 5+ (via registre ou détection PowerShell)
try {
$dotnet5plus = & dotnet --list-runtimes 2>&1
if ($LASTEXITCODE -eq 0) {
$dotnetVersions += [PSCustomObject]@{
Version = '.NET 5+ (dotnet --list-runtimes)'
Build = ($dotnet5plus -join ' | ').Substring(0, [Math]::Min(300, ($dotnet5plus -join ' | ').Length))
SP = ''
Installé = 'Oui'
}
}
} catch {}
} catch { $dotnetVersions = $null }
Write-Host "[4/6] Collecte applications installées (30 dernières + récentes 30j)..."
# Toutes les apps — triées par date
try {
$regPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$toutesApps = foreach ($path in $regPaths) {
Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -and $_.DisplayName -ne '' -and
$_.DisplayName -notlike '*Visual C++*' -and
$_.DisplayName -notlike '*Microsoft Visual*' } |
Select-Object DisplayName, DisplayVersion, Publisher,
@{N='InstalléLe'; E={
if ($_.InstallDate -and $_.InstallDate -match '^\d{8}$') {
(try { [datetime]::ParseExact($_.InstallDate, 'yyyyMMdd', $null).ToString('dd/MM/yyyy') } catch { 'N/A' })
} else { 'N/A' }
}},
@{N='DateBrute'; E={ $_.InstallDate }}
}
$toutesApps = $toutesApps | Sort-Object DateBrute -Descending |
Select-Object -Unique -Property DisplayName, DisplayVersion, Publisher, InstalléLe
# 30 dernières installées
$dernieresApps = $toutesApps | Select-Object -First 30
# Apps installées dans les 30 derniers jours
$seuil30j = (Get-Date).AddDays(-30).ToString('yyyyMMdd')
$appsRecentes = foreach ($path in $regPaths) {
Get-ItemProperty $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -and $_.InstallDate -ge $seuil30j } |
Select-Object DisplayName, DisplayVersion, Publisher,
@{N='InstalléLe'; E={
if ($_.InstallDate -match '^\d{8}$') {
(try { [datetime]::ParseExact($_.InstallDate, 'yyyyMMdd', $null).ToString('dd/MM/yyyy') } catch { 'N/A' })
} else { $_.InstallDate }
}}
}
$appsRecentes = $appsRecentes | Sort-Object InstalléLe -Descending
$nbRecentes = if ($appsRecentes) { @($appsRecentes).Count } else { 0 }
} catch {
$dernieresApps = $null
$appsRecentes = $null
$nbRecentes = 0
}
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements applicatifs)
# ════════════════════════════════════════════════════════════════════
Write-Host "[5/6] Collecte événements applicatifs (ID 1000, 1001, 1002)..."
# ID 1000 : Application Error — crash avec module fautif et code exception
# ID 1001 : Windows Error Reporting / Application Hang — rapport post-crash
# ID 1002 : Application Hang — application bloquée (ne répond plus)
$resAppCrash = Get-EventsAdaptatif `
-LogNames 'Application' `
-Ids @(1000, 1001, 1002) `
-Label 'Crashes/Hangs applicatifs' `
-MinEvents 5 `
-MaxEvents 100
Write-Host "[6/6] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerteCrash = if ($resAppCrash.Count -gt 0) {
"⚠️ $($resAppCrash.Count) crash(s)/hang(s) applicatif(s) détecté(s)"
} else { "✅ Aucun crash applicatif détecté" }
# Application la plus crashante
$appTopCrash = if ($resAppCrash.Events) {
$resAppCrash.Events | Where-Object { $_.Id -eq 1000 } |
Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending | Select-Object -First 1
}
$alerteTopApp = if ($appTopCrash) {
"⚠️ Application la plus crashante : $($appTopCrash.Name) ($($appTopCrash.Count) crash(s))"
} else { "✅ Aucune application en erreur répétée" }
# Codes exception détectés
$codesException = if ($resAppCrash.Events) {
$resAppCrash.Events | Where-Object { $_.Id -eq 1000 -and $_.Properties.Count -gt 6 } |
ForEach-Object {
$code = "0x{0:x8}" -f [int64]$_.Properties[6].Value
[PSCustomObject]@{ Application = $_.Properties[0].Value; Code = $code; Label = Get-ExceptionLabel $code }
}
}
$alerteVC = if (-not $vcRedist) { "⚠️ Redistribuables VC++ non inventoriés" } else { "✅ $(@($vcRedist).Count) redistribuable(s) VC++ inventorié(s)" }
$alerteNET = if (-not $dotnetVersions) { "⚠️ .NET Framework non inventorié" } else { "✅ .NET inventorié ($(@($dotnetVersions).Count) entrée(s))" }
$alerteRec = if ($nbRecentes -gt 0) { "ℹ️ $nbRecentes application(s) installée(s) dans les 30 derniers jours" } else { "✅ Aucune application récente (< 30j)" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT INCIDENTS APPLICATIFS v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p06-incidents-applicatifs.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
ALERTES APPLICATIFS
|- Crashes/hangs : $alerteCrash
|- App la + crashante : $alerteTopApp
|- Redistribuables VC++: $alerteVC
|- .NET Framework : $alerteNET
|- Apps récentes (30j) : $alerteRec
FENÊTRES RETENUES PAR BLOC
|- Crashes/Hangs applicatifs : $($resAppCrash.WindowHours) h -> $($resAppCrash.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Redistribuables Visual C++ ───────────────────────────
"`n── REDISTRIBUABLES VISUAL C++ ──────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($vcRedist) {
$vcRedist | Format-Table DisplayName, DisplayVersion, Architecture, InstalléLe -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Versions clés à vérifier selon l'application : 2005, 2008, 2010, 2012, 2013, 2015-2022 (x86 et x64)" |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (registre inaccessible)" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 2 : .NET Framework ───────────────────────────────────────
"`n── VERSIONS .NET FRAMEWORK INSTALLÉES ─────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($dotnetVersions) {
$dotnetVersions | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ .NET 4.8 est la dernière version 4.x intégrée à Windows 10/11." |
Out-File $outputFile -Append -Encoding UTF8
" Pour les apps modernes, vérifier aussi les runtimes .NET 5/6/7/8 via 'dotnet --list-runtimes'." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (clé registre NDP introuvable)" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 3 : Applications récentes (< 30 jours) ───────────────────
"`n── APPLICATIONS INSTALLÉES CES 30 DERNIERS JOURS ($nbRecentes app(s)) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbRecentes -gt 0) {
" $alerteRec" | Out-File $outputFile -Append -Encoding UTF8
"`n ⚠️ Corréler les dates d'installation avec l'apparition des incidents." |
Out-File $outputFile -Append -Encoding UTF8
$appsRecentes | Format-Table DisplayName, DisplayVersion, Publisher, InstalléLe -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucune application installée ou mise à jour dans les 30 derniers jours." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : 30 dernières applications installées ─────────────────
"`n── 30 DERNIÈRES APPLICATIONS INSTALLÉES (toutes dates) ────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($dernieresApps) {
$dernieresApps | Format-Table DisplayName, DisplayVersion, Publisher, InstalléLe -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (registre inaccessible)" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 5 : Événements crashes applicatifs ───────────────────────
"`n── ÉVÉNEMENTS CRASHES / HANGS APPLICATIFS ($($resAppCrash.Count) evt / $($resAppCrash.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resAppCrash.Events -and $resAppCrash.Count -gt 0) {
# Résumé par application
"`n Résumé par application (ID 1000 — crashes) :" | Out-File $outputFile -Append -Encoding UTF8
$resAppCrash.Events | Where-Object { $_.Id -eq 1000 } |
Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) crash(s)" } |
Out-File $outputFile -Append -Encoding UTF8
# Résumé hangs (ID 1002)
$hangs = $resAppCrash.Events | Where-Object { $_.Id -eq 1002 }
if ($hangs) {
"`n Applications bloquées (ID 1002 — hangs) :" | Out-File $outputFile -Append -Encoding UTF8
$hangs | Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) blocage(s)" } |
Out-File $outputFile -Append -Encoding UTF8
}
# Codes d'exception détectés
if ($codesException) {
"`n Codes d'exception détectés :" | Out-File $outputFile -Append -Encoding UTF8
$codesException | Select-Object -Unique Application, Code, Label |
Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
}
# Détail ID 1000 — 20 plus récents avec module fautif
"`n ── Détail crashes ID 1000 (20 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resAppCrash.Events | Where-Object { $_.Id -eq 1000 } |
Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated,
@{N='Application'; E={ if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'N/A' } }},
@{N='Version_App'; E={ if ($_.Properties.Count -gt 1) { $_.Properties[1].Value } else { 'N/A' } }},
@{N='Module_Fautif'; E={ if ($_.Properties.Count -gt 3) { $_.Properties[3].Value } else { 'N/A' } }},
@{N='Version_Module'; E={ if ($_.Properties.Count -gt 4) { $_.Properties[4].Value } else { 'N/A' } }},
@{N='Code_Exception'; E={
if ($_.Properties.Count -gt 6) {
$code = "0x{0:x8}" -f [int64]$_.Properties[6].Value
"$code ($(Get-ExceptionLabel $code))"
} else { 'N/A' }
}},
@{N='Offset'; E={ if ($_.Properties.Count -gt 7) { "0x{0:x}" -f [int64]$_.Properties[7].Value } else { 'N/A' } }} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
# Détail ID 1002 — hangs
if ($hangs) {
"`n ── Détail hangs ID 1002 (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$hangs | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated,
@{N='Application'; E={ if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'N/A' } }},
@{N='Version'; E={ if ($_.Properties.Count -gt 1) { $_.Properties[1].Value } else { 'N/A' } }},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
}
} else {
" ✅ Aucun crash ou blocage applicatif détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 6 : Données manuelles attendues ───────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Nom exact de l'application et sa version (ex: "Adobe Acrobat Reader DC 23.006")
[ ] Message d'erreur exact (copier-coller le texte complet ou joindre une capture)
[ ] Contexte : au lancement / pendant utilisation / à la fermeture / aléatoire
[ ] Dernière action effectuée avant l'erreur
[ ] Test avec autre compte utilisateur :
Résultat = STABLE (problème lié au profil utilisateur)
/ MÊME ERREUR (problème application ou système)
/ NON TESTÉ
[ ] Résultat en mode sans échec si testé (stable / même erreur / non testé)
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P06 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Compléter la section DONNÉES MANUELLES avec le nom de l'app et le message d'erreur"
Write-Host " 2. Effectuer le test avec un autre compte et noter le résultat"
Write-Host " 3. Soumettre le fichier complété au profil P06"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS collecte Event ID applicatifs [file]
- Nom exact de l'application et sa version
- Message d'erreur exact (copie texte ou capture)
- Contexte du dysfonctionnement (au lancement, pendant utilisation, à la fermeture)
- Dernière action avant l'erreur
- Test avec autre compte utilisateur si effectué
P07 — Affichage, GPU & Docks Vidéo
Diagnostic des incidents d'affichage, de GPU et de docks USB-C/Thunderbolt sur poste Windows 10/11. Analyse de la chaîne vidéo complète : dalle interne, écrans externes, adaptateurs, docks et pilotes GPU.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p07-affichage-gpu-docks-video_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : GPU (Win32_VideoController) · Pilotes GPU · Moniteurs EDID · Connecteurs vidéo · Docks/Thunderbolt · Event IDs TDR GPU (4101/14)
Données à saisir manuellement : Fiche de triage P01 · Symptôme précis · Type de matériel vidéo · Firmware dock · Tests croisés effectués
# ════════════════════════════════════════════════════════════════════
# COLLECTE AFFICHAGE, GPU & DOCKS VIDÉO v1.0
# Périmètre : GPU, pilotes vidéo, moniteurs, TDR, configuration affichage
#
# Profil cible : P07 — Affichage, GPU & Docks Vidéo
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p07-affichage-gpu-docks-video_$timestamp.txt"
# Trap global : capture toute erreur terminante et l'ecrit dans le fichier de sortie
trap {
$errMsg = "ERREUR FATALE (trap) : $($_.Exception.Message) | Ligne : $($_.InvocationInfo.ScriptLineNumber)"
Write-Host $errMsg -ForegroundColor Red
if ($outputFile) {
"`n[CRASH] $errMsg" | Out-File $outputFile -Append -Encoding UTF8 -ErrorAction SilentlyContinue
}
continue
}
# Creation immediate du fichier (permet de detecter les crashes mi-script)
"[DEBUT COLLECTE $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')]" | Out-File $outputFile -Encoding UTF8
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (GPU, moniteurs, pilotes vidéo)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/6] Collecte GPU via Win32_VideoController..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# GPU complet via CIM
try {
$gpus = Get-CimInstance Win32_VideoController |
Select-Object Name, VideoProcessor, AdapterCompatibility,
@{N='RAM_Mo'; E={ if ($_.AdapterRAM) { [math]::Round($_.AdapterRAM / 1MB, 0) } else { 'N/A' } }},
@{N='DriverVer'; E={ $_.DriverVersion }},
@{N='DriverDate'; E={ if ($_.DriverDate) { $_.DriverDate.ToString('dd/MM/yyyy') } else { 'N/A' } }},
@{N='Resolution'; E={ "$($_.CurrentHorizontalResolution)x$($_.CurrentVerticalResolution)" }},
@{N='RefreshHz'; E={ $_.CurrentRefreshRate }},
@{N='Status'; E={ $_.Status }},
@{N='Availability'; E={
switch ($_.Availability) {
2 { 'Inconnu' }
3 { '✅ Fonctionnel' }
8 { '⚠️ Hors ligne' }
11 { '⚠️ Test' }
default { "Code $($_.Availability)" }
}
}},
@{N='BitsParPixel'; E={ $_.CurrentBitsPerPixel }}
} catch { $gpus = $null }
Write-Host "[2/6] Collecte pilotes GPU via Win32_PnPSignedDriver..."
# Pilotes GPU/Display via PnPSignedDriver — plus précis sur la date d'installation
try {
$pilotesGPU = Get-CimInstance Win32_PnPSignedDriver |
Where-Object {
$_.DeviceClass -eq 'DISPLAY' -or
$_.DeviceName -like '*NVIDIA*' -or
$_.DeviceName -like '*AMD*' -or
$_.DeviceName -like '*Radeon*' -or
$_.DeviceName -like '*Intel*UHD*' -or
$_.DeviceName -like '*Intel*HD*' -or
$_.DeviceName -like '*Intel*Iris*' -or
$_.DeviceName -like '*Display*'
} |
Select-Object DeviceName, DriverProviderName, DriverVersion,
@{N='DateInstall'; E={
if ($_.DriverDate) { $_.DriverDate.ToString('dd/MM/yyyy') } else { 'N/A' }
}},
IsSigned |
Sort-Object DriverDate -Descending
} catch { $pilotesGPU = $null }
Write-Host "[3/6] Collecte moniteurs et configuration affichage..."
# Moniteurs via CIM
try {
$moniteurs = Get-CimInstance Win32_DesktopMonitor |
Select-Object Name, Description,
@{N='LargeurPx'; E={ $_.ScreenWidth }},
@{N='HauteurPx'; E={ $_.ScreenHeight }},
@{N='Status'; E={ $_.Status }},
@{N='Disponible'; E={ $_.Availability }}
} catch { $moniteurs = $null }
# Moniteurs via WMI étendu (informations EDID — modèle précis)
try {
$moniteurWMI = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID |
Select-Object InstanceName,
@{N='Fabricant'; E={
if ($_.ManufacturerName) {
[System.Text.Encoding]::ASCII.GetString($($_.ManufacturerName | Where-Object { $_ -gt 0 }))
} else { 'N/A' }
}},
@{N='Modele'; E={
if ($_.UserFriendlyName) {
[System.Text.Encoding]::ASCII.GetString($($_.UserFriendlyName | Where-Object { $_ -gt 0 }))
} else { 'N/A' }
}},
@{N='SerialNumber'; E={
if ($_.SerialNumberID) {
[System.Text.Encoding]::ASCII.GetString($($_.SerialNumberID | Where-Object { $_ -gt 0 }))
} else { 'N/A' }
}}
} catch { $moniteurWMI = $null }
# Connexions affichage actives (noms des sorties vidéo)
try {
$connecteurs = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorConnectionParams |
Select-Object InstanceName,
@{N='TypeConnexion'; E={
switch ($_.VideoOutputTechnology) {
0 { 'VGA' }
4 { 'DVI' }
5 { 'HDMI' }
10 { 'DisplayPort' }
11 { 'DisplayPort (externe)' }
12 { 'DisplayPort (intégré)' }
-1 { 'Interne (eDP/LVDS)' }
default { "Inconnu (code $($_.VideoOutputTechnology))" }
}
}}
} catch { $connecteurs = $null }
Write-Host "[4/6] Détection docks et périphériques USB-C/Thunderbolt..."
# Docks et hubs USB-C/Thunderbolt via PnP
try {
$docks = Get-CimInstance Win32_PnPEntity |
Where-Object {
$_.Name -like '*Thunderbolt*' -or
$_.Name -like '*Dock*' -or
$_.Name -like '*DisplayLink*' -or
$_.Name -like '*USB-C*' -or
$_.Description -like '*Dock*' -or
$_.Description -like '*DisplayLink*'
} |
Select-Object Name, Description, Status,
@{N='DeviceID_court'; E={ $_.DeviceID.Substring(0, [Math]::Min(60, $_.DeviceID.Length)) }}
} catch { $docks = $null }
# Pilotes Thunderbolt/DisplayLink
try {
$pilotesDock = Get-CimInstance Win32_PnPSignedDriver |
Where-Object {
$_.DeviceName -like '*Thunderbolt*' -or
$_.DeviceName -like '*DisplayLink*' -or
$_.DeviceName -like '*Dock*' -or
$_.DriverProviderName -like '*Intel*Thunderbolt*' -or
$_.DriverProviderName -like '*DisplayLink*'
} |
Select-Object DeviceName, DriverProviderName, DriverVersion,
@{N='DateInstall'; E={
if ($_.DriverDate) { $_.DriverDate.ToString('dd/MM/yyyy') } else { 'N/A' }
}}, IsSigned |
Sort-Object DriverDate -Descending
} catch { $pilotesDock = $null }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements TDR)
# ════════════════════════════════════════════════════════════════════
Write-Host "[5/6] Collecte événements TDR GPU (ID 4101) et atdr (ID 14)..."
# ID 4101 : Microsoft-Windows-Display — TDR (Timeout Detection and Recovery)
# Le GPU n'a pas répondu dans le délai imparti — Windows a récupéré le pilote
try {
$resTDR = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(4101) `
-Label 'TDR GPU (4101)' `
-MinEvents 3 `
-MaxEvents 50
} catch {
$resTDR = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'TDR GPU (4101)' }
}
# ID 14 : atdr — TDR niveau système (complément kernel)
try {
$resTDRsys = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(14) `
-Label 'TDR Système (14)' `
-MinEvents 3 `
-MaxEvents 30
} catch {
$resTDRsys = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'TDR Système (14)' }
}
# Événements display supplémentaires — journaux Microsoft-Windows-Display
try {
$resDisplay = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-Display' `
-Ids @(4101, 4102, 4103) `
-Label 'Display Journal' `
-MinEvents 3 `
-MaxEvents 40
} catch {
$resDisplay = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Display Journal' }
}
Write-Host "[6/6] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$nbGPU = if ($gpus) { @($gpus).Count } else { 0 }
$nbDocks = if ($docks) { @($docks).Count } else { 0 }
$nbTDR = $resTDR.Count + $resTDRsys.Count + $resDisplay.Count
$alerteTDR = if ($nbTDR -gt 0) { "⚠️ ALERTE : $nbTDR TDR détecté(s) — GPU a perdu la réponse" } else { "✅ Aucun TDR GPU détecté" }
$alerteGPUSt = if ($gpus) {
$ko = @($gpus) | Where-Object { $_.Status -ne 'OK' -and $_.Status -ne $null }
if ($ko) { "⚠️ $(@($ko).Count) GPU avec statut anormal : $(($ko | ForEach-Object { $_.Name }) -join ', ')" }
else { "✅ Tous les GPU : Status = OK" }
} else { "⚠️ Aucun GPU détecté via CIM" }
$alerteDock = if ($nbDocks -gt 0) { "ℹ️ $nbDocks périphérique(s) dock/Thunderbolt détecté(s)" } else { "✅ Aucun dock détecté" }
$alerteMonit = if ($moniteurWMI) { "✅ $(@($moniteurWMI).Count) moniteur(s) identifié(s) via EDID" } else { "ℹ️ EDID moniteurs non disponible" }
# Pilote GPU récent (< 30j)
$seuil30j = (Get-Date).AddDays(-30)
$piloteRecent = if ($pilotesGPU) {
$pilotesGPU | Where-Object {
if ($_.DateInstall -eq 'N/A' -or -not $_.DateInstall) { return $false }
try { [datetime]::ParseExact($_.DateInstall, 'dd/MM/yyyy', $null) -gt $seuil30j }
catch { $false }
}
} else { $null }
$alertePiloteRec = if ($piloteRecent) {
"⚠️ Pilote GPU mis à jour dans les 30 derniers jours — corréler avec l'apparition du symptôme"
} else { "✅ Aucun pilote GPU récent (< 30j)" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT AFFICHAGE, GPU & DOCKS VIDÉO v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p07-affichage-gpu-docks-video.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
GPU(s) : $(if ($gpus) { (@($gpus) | ForEach-Object { $_.Name }) -join ' | ' } else { 'N/A' })
Moniteurs : $(if ($moniteurWMI) { (@($moniteurWMI) | ForEach-Object { $_.Modele }) -join ' | ' } else { 'N/A' })
Docks : $(if ($nbDocks -gt 0) { (@($docks) | ForEach-Object { $_.Name }) -join ' | ' } else { 'Aucun détecté' })
ALERTES AFFICHAGE / GPU
|- TDR GPU : $alerteTDR
|- Statut GPU : $alerteGPUSt
|- Pilote GPU récent : $alertePiloteRec
|- Docks détectés : $alerteDock
|- Moniteurs EDID : $alerteMonit
FENÊTRES RETENUES PAR BLOC
|- TDR GPU (ID 4101) : $($resTDR.WindowHours) h -> $($resTDR.Count) événements
|- TDR Système (ID 14) : $($resTDRsys.WindowHours) h -> $($resTDRsys.Count) événements
|- Display Journal : $($resDisplay.WindowHours) h -> $($resDisplay.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : GPU (Win32_VideoController) ───────────────────────────
"`n── GPU — Win32_VideoController ────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($gpus) {
@($gpus) | Format-List Name, VideoProcessor, AdapterCompatibility, RAM_Mo,
DriverVer, DriverDate, Resolution, RefreshHz, Status, Availability, BitsParPixel |
Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Win32_VideoController inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Pilotes GPU (PnPSignedDriver) ────────────────────────
"`n── PILOTES GPU — Win32_PnPSignedDriver ────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($pilotesGPU) {
$pilotesGPU | Format-Table DeviceName, DriverProviderName, DriverVersion, DateInstall, IsSigned -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
if ($piloteRecent) {
"`n ⚠️ Pilote(s) GPU récent(s) (< 30j) :" | Out-File $outputFile -Append -Encoding UTF8
$piloteRecent | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
}
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : Moniteurs (EDID) ─────────────────────────────────────
"`n── MONITEURS — Identification EDID ────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($moniteurWMI) {
$moniteurWMI | Format-Table Fabricant, Modele, SerialNumber -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (WmiMonitorID inaccessible — pas de moniteur connecté ou droits insuffisants)" |
Out-File $outputFile -Append -Encoding UTF8
}
"`n Moniteurs via Win32_DesktopMonitor :" | Out-File $outputFile -Append -Encoding UTF8
if ($moniteurs) {
$moniteurs | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 4 : Connecteurs vidéo actifs ─────────────────────────────
"`n── CONNECTEURS VIDÉO ACTIFS (type de liaison) ──────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($connecteurs) {
$connecteurs | Format-Table TypeConnexion, InstanceName -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Logique de lecture : eDP/LVDS = dalle interne, DisplayPort/HDMI = écran externe" |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (WmiMonitorConnectionParams inaccessible)" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 5 : Docks et périphériques Thunderbolt/USB-C ─────────────
"`n── DOCKS & PÉRIPHÉRIQUES THUNDERBOLT/USB-C ($nbDocks détecté(s)) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbDocks -gt 0) {
$docks | Format-Table Name, Description, Status -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
"`n Pilotes dock/Thunderbolt/DisplayLink :" | Out-File $outputFile -Append -Encoding UTF8
if ($pilotesDock) {
$pilotesDock | Format-Table DeviceName, DriverProviderName, DriverVersion, DateInstall, IsSigned -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun pilote dock/DisplayLink/Thunderbolt identifié." |
Out-File $outputFile -Append -Encoding UTF8
}
"`n ⚠️ Firmware dock : non récupérable automatiquement." |
Out-File $outputFile -Append -Encoding UTF8
" → Pour récupérer la version firmware : ouvrir le logiciel constructeur (Dell Display Manager," |
Out-File $outputFile -Append -Encoding UTF8
" Lenovo Dock Manager, HP Thunderbolt Dock Firmware...) ou aller dans Gestionnaire de" |
Out-File $outputFile -Append -Encoding UTF8
" périphériques > Périphériques universels > dock > Propriétés > Détails > Version du pilote." |
Out-File $outputFile -Append -Encoding UTF8
" → Coller la version firmware dans la section DONNÉES MANUELLES ci-dessous." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun dock ou périphérique Thunderbolt/USB-C/DisplayLink détecté." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 6 : Événements TDR GPU (ID 4101) ─────────────────────────
"`n── ÉVÉNEMENTS TDR GPU — ID 4101 ($($resTDR.Count) evt / $($resTDR.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resTDR.Events -and $resTDR.Count -gt 0) {
" $alerteTDR" | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Un TDR signifie que le GPU n'a pas répondu dans le délai (2s par défaut)." |
Out-File $outputFile -Append -Encoding UTF8
" Windows a réinitialisé le pilote GPU. Si fréquent → overheating, pilote ou GPU défaillant." |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (20 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resTDR.Events | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='GPU'; E={
if ($_.Properties.Count -gt 1) { $_.Properties[1].Value } else { 'N/A' }
}},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun TDR GPU (ID 4101) détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 7 : TDR Système (ID 14) ──────────────────────────────────
"`n── ÉVÉNEMENTS TDR SYSTÈME — ID 14 ($($resTDRsys.Count) evt / $($resTDRsys.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resTDRsys.Events -and $resTDRsys.Count -gt 0) {
$resTDRsys.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun TDR système (ID 14) détecté." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 8 : Journal Microsoft-Windows-Display ────────────────────
"`n── JOURNAL DISPLAY — Événements ($($resDisplay.Count) evt / $($resDisplay.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resDisplay.Events -and $resDisplay.Count -gt 0) {
$resDisplay.Events | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun événement dans le journal Microsoft-Windows-Display (normal si aucun TDR)." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Données manuelles attendues ──────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptôme précis (écran noir / scintillement / artefacts / résolution
incorrecte / multi-écran défaillant / freeze graphique / autre)
[ ] Matériel vidéo impliqué :
Dalle interne / Écran externe (modèle) / Dock (modèle et version firmware)
Câble (HDMI/DP/USB-C) / Adaptateur (type)
[ ] Version firmware dock (voir instructions section 5 ci-dessus)
[ ] Dernière mise à jour pilote GPU ou firmware dock (date approximative)
[ ] Tests croisés effectués :
Autre écran testé → résultat : ________
Autre câble testé → résultat : ________
Autre port testé → résultat : ________
Sans dock testé → résultat : ________
Autre poste testé → résultat : ________
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P07 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Si dock présent : récupérer la version firmware (voir section 5)"
Write-Host " 2. Effectuer les tests croisés (autre écran, câble, port, sans dock)"
Write-Host " 3. Compléter la section DONNÉES MANUELLES et soumettre au profil P07"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS diagnostic affichage/GPU [file]
- Symptôme précis (écran noir, scintillement, résolution incorrecte, multi-écran défaillant, artefacts)
- Type de matériel vidéo (écran, dock, câble, GPU intégré/dédié)
- Dernières mises à jour pilote GPU ou firmware dock
- Tests croisés effectués (autre écran, câble, port)
P08 — BIOS, UEFI, TPM & Secure Boot
Diagnostic des incidents firmware, boot avancé, TPM et Secure Boot sur poste Windows 10/11. Couvre les pannes pré-OS, les blocages BitLocker, les conflits Secure Boot et les mises à jour BIOS problématiques.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p08-bios-uefi-tpm-secure-boot_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : OS · TPM · Secure Boot · BitLocker volumes · Mode boot (UEFI/Legacy via bcdedit) · Uptime · Fabricant/modèle
Données à saisir manuellement : Fiche de triage P01 · Symptôme précis · Messages d'erreur exacts · Mode démarrage actuel · Dernière mise à jour BIOS · Modifications matérielles récentes
# ════════════════════════════════════════════════════════════════════
# COLLECTE BIOS, UEFI, TPM & SECURE BOOT v1.0
# Périmètre : firmware, TPM, Secure Boot, BitLocker, mode démarrage
#
# Profil cible : P08 — BIOS, UEFI, TPM & Secure Boot
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p08-bios-uefi-tpm-secure-boot_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (BIOS, TPM, Secure Boot, BitLocker)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/7] Collecte OS et uptime..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
try {
$uptime = (Get-Date) - $os.LastBootUpTime
$uptimeStr = "{0}j {1}h {2}min" -f [math]::Floor($uptime.TotalDays), $uptime.Hours, $uptime.Minutes
} catch { $uptimeStr = 'N/A' }
Write-Host "[2/7] Collecte BIOS et carte mère..."
# BIOS / UEFI
try {
$bios = Get-CimInstance Win32_BIOS |
Select-Object Manufacturer, Name, Version, SMBIOSBIOSVersion,
@{N='DateFirmware'; E={
if ($_.ReleaseDate) { $_.ReleaseDate.ToString('dd/MM/yyyy') } else { 'N/A' }
}},
SerialNumber, SystemBIOSMajorVersion, SystemBIOSMinorVersion
} catch { $bios = $null }
# Carte mère
try {
$cartemere = Get-CimInstance Win32_BaseBoard |
Select-Object Manufacturer, Product, Version, SerialNumber
} catch { $cartemere = $null }
# Système complet
try {
$cs = Get-CimInstance Win32_ComputerSystem |
Select-Object Manufacturer, Model, SystemFamily,
@{N='Type'; E={
switch ($_.PCSystemType) {
1 { 'Poste de bureau' } 2 { 'Portable' }
4 { 'Serveur' } 7 { 'Workstation' }
default { "Inconnu ($($_.PCSystemType))" }
}
}}
} catch { $cs = $null }
Write-Host "[3/7] Collecte TPM..."
# TPM complet
try {
$tpm = Get-Tpm |
Select-Object TpmPresent, TpmReady, TpmEnabled, TpmActivated,
ManufacturerVersion, ManufacturerVersionInfo,
@{N='LockoutCount'; E={ $_.LockoutCount }},
@{N='LockoutHealTime'; E={ $_.LockoutHealTime }},
@{N='SelfTestResult'; E={ $_.SelfTestResult }}
} catch { $tpm = $null }
# Version TPM via WMI (plus de détails)
try {
$tpmWMI = Get-CimInstance -Namespace root\cimv2\security\microsofttpm -ClassName Win32_Tpm |
Select-Object IsActivated_InitialValue, IsEnabled_InitialValue,
IsOwned_InitialValue, ManufacturerId, ManufacturerVersion,
ManufacturerVersionInfo, PhysicalPresenceVersionInfo,
SpecVersion
} catch { $tpmWMI = $null }
Write-Host "[4/7] Collecte Secure Boot et mode démarrage..."
# Secure Boot
try {
$secureBoot = Confirm-SecureBootUEFI
$secureBootStr = if ($secureBoot) { "✅ ACTIVÉ" } else { "⚠️ DÉSACTIVÉ" }
} catch {
$secureBoot = $null
$secureBootStr = "⚠️ Non disponible (BIOS Legacy ou cmdlet absente : $($_.Exception.Message.Substring(0,[Math]::Min(80,$_.Exception.Message.Length))))"
}
# Mode de démarrage (UEFI vs Legacy) via bcdedit
try {
$bcdedit = & "$env:SystemRoot\System32\bcdedit.exe" /enum ACTIVE 2>&1
$modeBoot = if ($bcdedit -match 'bootmgfw\.efi|efi') {
"✅ UEFI (EFI bootloader détecté)"
} elseif ($bcdedit -match 'bootmgr') {
"ℹ️ Probablement Legacy/MBR (bootmgr sans EFI)"
} else {
"ℹ️ Indéterminé — voir extrait bcdedit ci-dessous"
}
} catch {
$bcdedit = "Non disponible (bcdedit inaccessible)"
$modeBoot = "Non disponible"
}
# Firmware type via registre
try {
$firmwareType = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control' -Name SystemBootDevice -ErrorAction SilentlyContinue).SystemBootDevice
$uefiReg = if (Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State') {
$sbState = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -ErrorAction SilentlyContinue).UEFISecureBootEnabled
"Secure Boot registre : $(if ($sbState -eq 1) { '✅ Activé' } else { '⚠️ Désactivé ou Legacy' })"
} else { "Clé SecureBoot absente — probablement BIOS Legacy" }
} catch { $uefiReg = "Non disponible"; $firmwareType = "N/A" }
Write-Host "[5/7] Collecte BitLocker..."
# BitLocker — état détaillé par volume
try {
$bitlockerVolumes = Get-BitLockerVolume |
Select-Object MountPoint, VolumeType, VolumeStatus,
EncryptionPercentage, EncryptionMethod,
ProtectionStatus,
@{N='Protecteurs'; E={
($_.KeyProtector | ForEach-Object { $_.KeyProtectorType }) -join ', '
}},
@{N='IDProtecteurs'; E={
($_.KeyProtector | ForEach-Object { $_.KeyProtectorId }) -join ' | '
}}
$nbVolBL = if ($bitlockerVolumes) { @($bitlockerVolumes).Count } else { 0 }
} catch {
$bitlockerVolumes = $null
$nbVolBL = 0
}
# Vérification clé recovery présente (sans afficher la clé elle-même)
try {
$recoveryPresente = if ($bitlockerVolumes) {
$bitlockerVolumes | ForEach-Object {
$vol = $_
$hasRec = $vol.Protecteurs -match 'RecoveryPassword'
[PSCustomObject]@{
Volume = $vol.MountPoint
RecoveryPassword = if ($vol.ProtectionStatus -eq 'Off' -or $vol.VolumeStatus -eq 'FullyDecrypted') {
"N/A (non chiffré)"
} elseif ($hasRec) { "✅ Présente" } else { "⚠️ ABSENTE — vérifier avant toute action BIOS/TPM" }
ProtectionStatus = $vol.ProtectionStatus
VolumeStatus = $vol.VolumeStatus
}
}
} else { $null }
} catch { $recoveryPresente = $null }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements firmware/boot/BitLocker)
# ════════════════════════════════════════════════════════════════════
Write-Host "[6/7] Collecte événements boot et BitLocker..."
# ID 6008 : arrêt inattendu — corrélation boot/UEFI
$resBootInattendu = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(6008) `
-Label 'Arrêt inattendu (6008)' `
-MinEvents 3 `
-MaxEvents 30
# BitLocker events — ID 24597 (suspend protection), 24598 (resume), 24621 (backup clé AD)
try {
$resBitlocker = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-BitLocker/BitLocker Management' `
-Ids @(24597, 24598, 24621, 769, 770, 773) `
-Label 'BitLocker' `
-MinEvents 3 `
-MaxEvents 40
} catch {
# Fallback sur Application si le journal BitLocker n'est pas disponible
try {
$resBitlocker = Get-EventsAdaptatif `
-LogNames 'Application' `
-Ids @(24597, 24598) `
-Label 'BitLocker (Application)' `
-MinEvents 3 `
-MaxEvents 30
} catch {
$resBitlocker = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'BitLocker' }
}
}
# Événements UEFI/SecureBoot — journal Microsoft-Windows-Kernel-Boot
try {
$resKernelBoot = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-Kernel-Boot/Operational' `
-Ids @(20, 153, 16) `
-Label 'Kernel Boot' `
-MinEvents 3 `
-MaxEvents 30
} catch {
$resKernelBoot = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Kernel Boot' }
}
Write-Host "[7/7] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerteTPM = if ($tpm) {
if (-not $tpm.TpmPresent) { "⚠️ TPM ABSENT" }
elseif (-not $tpm.TpmEnabled) { "⚠️ TPM PRÉSENT mais DÉSACTIVÉ" }
elseif (-not $tpm.TpmReady) { "⚠️ TPM présent et activé mais NON PRÊT" }
else { "✅ TPM présent, activé et prêt (v$($tpm.ManufacturerVersion))" }
} else { "⚠️ Get-Tpm non disponible (module absent ou droits insuffisants)" }
$alerteSB = $secureBootStr
$alerteBL = if ($bitlockerVolumes) {
$chiffres = @($bitlockerVolumes) | Where-Object { $_.VolumeStatus -eq 'FullyEncrypted' -or $_.VolumeStatus -eq 'EncryptionInProgress' }
if ($chiffres) { "ℹ️ $(@($chiffres).Count) volume(s) chiffré(s) BitLocker — toute action BIOS/TPM peut déclencher recovery" }
else { "✅ Aucun volume BitLocker actif" }
} else { "ℹ️ BitLocker non inventorié (module absent)" }
$alerteRecovery = if ($recoveryPresente) {
$manquants = @($recoveryPresente) | Where-Object { $_.RecoveryPassword -match 'ABSENTE' }
$chiffresActifs = @($recoveryPresente) | Where-Object { $_.RecoveryPassword -notmatch 'N/A' }
if ($manquants) { "⚠️ Clé recovery ABSENTE sur $(@($manquants).Count) volume(s) chiffré(s) — RISQUE DE BLOCAGE" }
elseif ($chiffresActifs) { "✅ Clé recovery présente sur tous les volumes chiffrés" }
else { "✅ Aucun volume BitLocker actif — clé recovery non applicable" }
} else { "ℹ️ Non vérifié (BitLocker non disponible)" }
$alerteBootMode = $modeBoot
$alerteBootInat = if ($resBootInattendu.Count -gt 0) { "⚠️ $($resBootInattendu.Count) arrêt(s) inattendu(s) détecté(s) (ID 6008)" } else { "✅ Aucun arrêt inattendu" }
$alerteBLevt = if ($resBitlocker.Count -gt 0) { "ℹ️ $($resBitlocker.Count) événement(s) BitLocker (suspend/resume/backup)" } else { "✅ Aucun événement BitLocker notable" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT BIOS, UEFI, TPM & SECURE BOOT v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p08-bios-uefi-tpm-secure-boot.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Système : $(if ($cs) { "$($cs.Manufacturer) $($cs.Model)" } else { 'N/A' })
BIOS : $(if ($bios) { "$($bios.Manufacturer) v$($bios.SMBIOSBIOSVersion) ($($bios.DateFirmware))" } else { 'N/A' })
Uptime : $uptimeStr
⚠️ AVERTISSEMENT CRITIQUE
Toute action sur TPM, Secure Boot ou BIOS peut déclencher une recovery
BitLocker et rendre le poste temporairement inaccessible sans la clé.
Vérifier IMPÉRATIVEMENT la disponibilité de la clé recovery avant toute action.
ALERTES FIRMWARE / SÉCURITÉ PLATEFORME
|- TPM : $alerteTPM
|- Secure Boot : $alerteSB
|- Mode démarrage : $alerteBootMode
|- BitLocker volumes : $alerteBL
|- Clé recovery : $alerteRecovery
|- Arrêts inattendus : $alerteBootInat
|- Événements BitLocker: $alerteBLevt
|- UEFI registre : $uefiReg
FENÊTRES RETENUES PAR BLOC
|- Arrêts inattendus (6008) : $($resBootInattendu.WindowHours) h -> $($resBootInattendu.Count) événements
|- BitLocker : $($resBitlocker.WindowHours) h -> $($resBitlocker.Count) événements
|- Kernel Boot : $($resKernelBoot.WindowHours) h -> $($resKernelBoot.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : BIOS et carte mère ───────────────────────────────────
"`n── BIOS / UEFI FIRMWARE ────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($bios) {
$bios | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Win32_BIOS inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
"`n Système :" | Out-File $outputFile -Append -Encoding UTF8
if ($cs) {
$cs | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
"`n Carte mère :" | Out-File $outputFile -Append -Encoding UTF8
if ($cartemere) {
$cartemere | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Win32_BaseBoard inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 2 : Mode de démarrage ────────────────────────────────────
"`n── MODE DE DÉMARRAGE (UEFI vs LEGACY) ──────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Détection : $modeBoot" | Out-File $outputFile -Append -Encoding UTF8
" $uefiReg" | Out-File $outputFile -Append -Encoding UTF8
"`n Extrait bcdedit :" | Out-File $outputFile -Append -Encoding UTF8
if ($bcdedit -is [array]) {
# Limiter à 40 lignes pour contenir la taille
$bcdedit | Select-Object -First 40 | Out-File $outputFile -Append -Encoding UTF8
} else {
$bcdedit | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 3 : TPM ──────────────────────────────────────────────────
"`n── TPM — Get-Tpm ───────────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteTPM" | Out-File $outputFile -Append -Encoding UTF8
if ($tpm) {
"`n Détail Get-Tpm :" | Out-File $outputFile -Append -Encoding UTF8
$tpm | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
"`n Non disponible (module Get-Tpm absent — PowerShell sur Server Core ?" |
Out-File $outputFile -Append -Encoding UTF8
" Alternative : tpm.msc > État du TPM)" | Out-File $outputFile -Append -Encoding UTF8
}
if ($tpmWMI) {
"`n Détail WMI Win32_Tpm :" | Out-File $outputFile -Append -Encoding UTF8
$tpmWMI | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
"`n WMI Win32_Tpm : Non disponible (namespace root\cimv2\security\microsofttpm inaccessible)" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : Secure Boot ──────────────────────────────────────────
"`n── SECURE BOOT ─────────────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Confirm-SecureBootUEFI : $secureBootStr" | Out-File $outputFile -Append -Encoding UTF8
" $uefiReg" | Out-File $outputFile -Append -Encoding UTF8
if ($secureBoot -eq $false) {
"`n ⚠️ Secure Boot désactivé — peut bloquer le chargement de certains pilotes signés" |
Out-File $outputFile -Append -Encoding UTF8
" et peut indiquer un poste préparé pour dual-boot ou un ancien paramétrage BIOS." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 5 : BitLocker volumes ────────────────────────────────────
"`n── BITLOCKER — État par volume ($nbVolBL volume(s)) ───────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteBL" | Out-File $outputFile -Append -Encoding UTF8
" $alerteRecovery" | Out-File $outputFile -Append -Encoding UTF8
if ($bitlockerVolumes) {
"`n Détail par volume :" | Out-File $outputFile -Append -Encoding UTF8
$bitlockerVolumes | Format-List MountPoint, VolumeType, VolumeStatus,
EncryptionPercentage, EncryptionMethod, ProtectionStatus, Protecteurs |
Out-File $outputFile -Append -Encoding UTF8
if ($recoveryPresente) {
"`n Synthèse clé recovery :" | Out-File $outputFile -Append -Encoding UTF8
$recoveryPresente | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
}
"`n ⚠️ RAPPEL : avant toute action BIOS/TPM/Secure Boot :" |
Out-File $outputFile -Append -Encoding UTF8
" 1. S'assurer que la clé recovery BitLocker est disponible (AD, Azure AD, fichier, USB)" |
Out-File $outputFile -Append -Encoding UTF8
" 2. Tester l'accès à la clé recovery AVANT l'intervention" |
Out-File $outputFile -Append -Encoding UTF8
" 3. Ne jamais désactiver BitLocker comme 'solution' sans validation" |
Out-File $outputFile -Append -Encoding UTF8
} else {
"`n Non disponible (module BitLocker absent ou droits insuffisants)" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 6 : Événements arrêts inattendus (ID 6008) ───────────────
"`n── ARRÊTS INATTENDUS — ID 6008 ($($resBootInattendu.Count) evt / $($resBootInattendu.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resBootInattendu.Events -and $resBootInattendu.Count -gt 0) {
" $alerteBootInat" | Out-File $outputFile -Append -Encoding UTF8
"`n Détail :" | Out-File $outputFile -Append -Encoding UTF8
$resBootInattendu.Events | Sort-Object TimeCreated -Descending |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun arrêt inattendu détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 7 : Événements BitLocker ─────────────────────────────────
"`n── ÉVÉNEMENTS BITLOCKER ($($resBitlocker.Count) evt / $($resBitlocker.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resBitlocker.Events -and $resBitlocker.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resBitlocker.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'769' { "Chiffrement démarré" }
'770' { "Chiffrement terminé" }
'773' { "Clé recovery générée" }
'24597' { "Protection suspendue" }
'24598' { "Protection reprise" }
'24621' { "Clé sauvegardée dans AD" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resBitlocker.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun événement BitLocker notable dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 8 : Événements Kernel Boot ───────────────────────────────
"`n── ÉVÉNEMENTS KERNEL BOOT ($($resKernelBoot.Count) evt / $($resKernelBoot.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resKernelBoot.Events -and $resKernelBoot.Count -gt 0) {
$resKernelBoot.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun événement Kernel Boot notable détecté." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Données manuelles attendues ───────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
⚠️ RAPPEL : vérifier la clé recovery BitLocker AVANT toute intervention.
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptôme précis :
Blocage au boot / Message BIOS-UEFI affiché / Erreur BitLocker recovery /
Secure Boot refus démarrage OS / Panne pré-OS / Autre : ________
[ ] Message(s) d'erreur exact(s) (copier-coller ou photo)
[ ] Dernière mise à jour BIOS/firmware (date si connue)
[ ] Modifications matérielles récentes (RAM, SSD, GPU, dock...)
[ ] Clé recovery BitLocker disponible : OUI (où : AD / Azure AD / fichier / USB)
/ NON / NON VÉRIFIÉ
[ ] Export msinfo32 (optionnel mais utile) :
Windows + R > msinfo32 > Fichier > Enregistrer > .nfo
Coller ici le contenu des sections : Résumé du système + Environnement BIOS
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P08 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ⚠️ AVANT TOUTE INTERVENTION :"
Write-Host " → Vérifier la clé recovery BitLocker (AD, Azure AD, fichier, USB)"
Write-Host " → Ne pas modifier BIOS/TPM/Secure Boot sans clé recovery accessible"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Compléter la section DONNÉES MANUELLES (symptôme, messages d'erreur)"
Write-Host " 2. Vérifier et noter la disponibilité de la clé recovery BitLocker"
Write-Host " 3. Soumettre le fichier complété au profil P08"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS TPM/Secure Boot/UEFI [file]
- Symptôme précis (blocage au boot, message BIOS/UEFI, erreur BitLocker, Secure Boot refus)
- Messages d'erreur exacts (photos si possible)
- Mode de démarrage actuel (UEFI/Legacy si connu)
- Dernière mise à jour BIOS/firmware
- Modifications matérielles récentes
P09 — Logon, Profils & GPO
Diagnostic des incidents de session, profils utilisateurs et GPO sur poste Windows 10/11 en environnement d'entreprise. Analyse gpresult, Event ID logon/profil, profils temporaires et dépendances réseau au logon.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p09-logon-profils-gpo_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : OS · Profils utilisateur (registre) · Event IDs logon/profil/GPO (1500-1530, 1006-1129) · gpresult partiel · Connectivité DC
Données à saisir manuellement : Fiche de triage P01 · Symptôme précis · Type d'environnement (AD/hybride/local) · Résultat gpresult /r si disponible · Test autre compte
# ════════════════════════════════════════════════════════════════════
# COLLECTE LOGON, PROFILS & GPO v1.0
# Périmètre : profils utilisateurs, GPO, canal AD, services logon,
# connectivité DC, événements session
#
# Profil cible : P09 — Logon, Profils & GPO
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p09-logon-profils-gpo_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES
# (OS, domaine, services, profils, canal AD)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte OS et appartenance domaine..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
try {
$cs = Get-CimInstance Win32_ComputerSystem |
Select-Object Name, Domain, PartOfDomain, Workgroup,
@{N='TypeJonction'; E={
if ($_.PartOfDomain) { "Joint au domaine : $($_.Domain)" }
else { "Groupe de travail : $($_.Workgroup)" }
}}
} catch { $cs = $null }
# Détection Azure AD / Entra ID join
try {
$dsregCmd = & dsregcmd /status 2>&1
$azureADJoined = if ($dsregCmd -match 'AzureAdJoined\s*:\s*YES') { "✅ OUI" } else { "NON" }
$domainJoined = if ($dsregCmd -match 'DomainJoined\s*:\s*YES') { "✅ OUI" } else { "NON" }
$enterpriseJoin = if ($dsregCmd -match 'EnterpriseJoined\s*:\s*YES') { "✅ OUI" } else { "NON" }
$workplaceJoin = if ($dsregCmd -match 'WorkplaceJoined\s*:\s*YES') { "✅ OUI" } else { "NON" }
} catch {
$dsregCmd = "Non disponible (dsregcmd inaccessible)"
$azureADJoined = "N/A"; $domainJoined = "N/A"
$enterpriseJoin = "N/A"; $workplaceJoin = "N/A"
}
Write-Host "[2/8] Collecte services logon critiques..."
# Services critiques pour le logon
$servicesLogon = @('Netlogon', 'ProfSvc', 'gpsvc', 'UserManager',
'CryptSvc', 'LanmanWorkstation', 'Dnscache', 'NlaSvc')
try {
$svcLogon = foreach ($svc in $servicesLogon) {
$s = Get-Service -Name $svc -ErrorAction SilentlyContinue
if ($s) {
[PSCustomObject]@{
Nom = $svc
Statut = $s.Status
Démarrage = $s.StartType
Alerte = if ($s.Status -ne 'Running' -and $s.StartType -eq 'Automatic') {
"⚠️ AUTO non démarré"
} elseif ($s.Status -ne 'Running') { "ℹ️ Arrêté" }
else { "✅ OK" }
}
} else {
[PSCustomObject]@{ Nom = $svc; Statut = 'Absent'; Démarrage = 'N/A'; Alerte = "ℹ️ Service absent" }
}
}
} catch { $svcLogon = $null }
Write-Host "[3/8] Collecte ProfileList (profils registre)..."
# ProfileList — chemins et détection .bak
try {
$profileListPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
$profilsReg = Get-ChildItem $profileListPath -ErrorAction SilentlyContinue |
ForEach-Object {
$props = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue
$sid = $_.PSChildName
$path = $props.ProfileImagePath
$isBak = $sid -match '\.bak$'
$etat = if ($isBak) { "⚠️ ENTRÉE .BAK — profil potentiellement corrompu" }
elseif (-not (Test-Path $path -ErrorAction SilentlyContinue)) { "⚠️ Chemin introuvable" }
else { "✅ OK" }
[PSCustomObject]@{
SID = $sid.Substring(0, [Math]::Min(30, $sid.Length))
CheminProfil = $path
EstBAK = $isBak
Etat = $etat
RefCount = $props.RefCount
}
} | Sort-Object EstBAK -Descending
$nbBAK = @($profilsReg | Where-Object { $_.EstBAK -eq $true }).Count
} catch { $profilsReg = $null; $nbBAK = 0 }
# Taille des profils utilisateurs (dossiers dans C:\Users — sans données nominatives)
try {
$usersDir = "$env:SystemDrive\Users"
$tailleProfils = Get-ChildItem $usersDir -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notin @('Public', 'Default', 'Default User', 'All Users') } |
ForEach-Object {
$taille = (Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
[PSCustomObject]@{
NomDossier = $_.Name
TailleMo = if ($taille) { [math]::Round($taille / 1MB, 0) } else { 0 }
DateModif = $_.LastWriteTime.ToString('dd/MM/yyyy HH:mm')
}
} | Sort-Object TailleMo -Descending
} catch { $tailleProfils = $null }
Write-Host "[4/8] Canal sécurisé AD (Test-ComputerSecureChannel + nltest)..."
# Test-ComputerSecureChannel
try {
$secureChannel = Test-ComputerSecureChannel -ErrorAction SilentlyContinue
$secureChannelStr = if ($secureChannel -eq $true) {
"✅ Canal sécurisé AD opérationnel"
} elseif ($secureChannel -eq $false) {
"⚠️ Canal sécurisé AD DÉFAILLANT — réparation possible : Test-ComputerSecureChannel -Repair"
} else { "ℹ️ Non disponible (poste non joint au domaine ?)" }
} catch {
$secureChannel = $null
$secureChannelStr = "Non disponible ($($_.Exception.Message.Substring(0,[Math]::Min(80,$_.Exception.Message.Length))))"
}
# nltest /sc_query — état Netlogon vers le DC
try {
$domainName = if ($cs -and $cs.Domain) { $cs.Domain } else { $null }
if ($domainName) {
$nltest = & nltest /sc_query:$domainName 2>&1
$nltestOK = if ($nltest -match 'NERR_Success|0x0\b') { "✅ Netlogon OK vers $domainName" }
else { "⚠️ Netlogon ECHEC vers $domainName" }
} else {
$nltest = "Poste non joint au domaine — nltest ignoré"
$nltestOK = "ℹ️ Hors domaine"
}
} catch {
$nltest = "Non disponible (nltest inaccessible)"
$nltestOK = "Non disponible"
}
# Connectivité DC (ping)
try {
if ($domainName) {
$pingDC = Test-NetConnection -ComputerName $domainName -WarningAction SilentlyContinue
$pingDCStr = if ($pingDC.PingSucceeded) {
"✅ DC joignable ($domainName) — $($pingDC.PingReplyDetails.RoundtripTime) ms"
} else { "⚠️ DC non joignable ($domainName)" }
} else { $pingDCStr = "ℹ️ Hors domaine — ping DC ignoré" }
} catch { $pingDCStr = "Non disponible" }
Write-Host "[5/8] gpresult /r..."
# gpresult /r
try {
$gpresult = & gpresult /r 2>&1
# Extraire les sections clés
$gpresultStr = $gpresult -join "`n"
} catch {
$gpresultStr = "Non disponible (gpresult inaccessible ou poste hors domaine)"
}
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements profil/GPO)
# ════════════════════════════════════════════════════════════════════
Write-Host "[6/8] Collecte événements profil (ID 1511, 1515, 1530)..."
# ID 1511 : Impossible de charger le profil — profil temporaire chargé
# ID 1515 : Profil de secours chargé (profil temporaire)
# ID 1530 : Suppression des ruches de registre (session précédente non fermée)
$resProfil = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-User Profile Service/Operational' `
-Ids @(1511, 1515, 1530, 1500, 1502, 1508, 1509) `
-Label 'Profil utilisateur' `
-MinEvents 5 `
-MaxEvents 60
Write-Host "[7/8] Collecte événements GPO (ID 1085, 1006, 1030, 1096)..."
# ID 1085 : Traitement de l'extension de stratégie en échec
# ID 1006 : Erreur de traitement de la stratégie de groupe
# ID 1030 : Impossible d'accéder à la liste de GPO
# ID 1096 : Extension de traitement en échec (scripts, redirection dossier...)
$resGPO = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-GroupPolicy/Operational' `
-Ids @(1085, 1006, 1030, 1096, 1058, 1129) `
-Label 'GPO erreurs' `
-MinEvents 5 `
-MaxEvents 60
Write-Host "[8/8] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerteProfils = if ($nbBAK -gt 0) {
"⚠️ $nbBAK entrée(s) .BAK dans ProfileList — profil(s) potentiellement corrompu(s)"
} else { "✅ Aucune entrée .bak dans ProfileList" }
$alerteSvcKO = if ($svcLogon) {
$ko = @($svcLogon) | Where-Object { $_.Alerte -match '⚠️' }
if ($ko) { "⚠️ $(@($ko).Count) service(s) logon en anomalie : $(($ko | ForEach-Object { $_.Nom }) -join ', ')" }
else { "✅ Tous les services logon sont actifs" }
} else { "⚠️ Services logon non inventoriés" }
$alerteAD = $secureChannelStr
$alerteNltest = $nltestOK
$alerteDC = $pingDCStr
$alerteProfilEvt = if ($resProfil.Count -gt 0) {
"⚠️ $($resProfil.Count) événement(s) profil détecté(s) (chargement échoué / profil temp)"
} else { "✅ Aucun incident profil détecté" }
$alerteGPOevt = if ($resGPO.Count -gt 0) {
"⚠️ $($resGPO.Count) événement(s) GPO en erreur"
} else { "✅ Aucune erreur GPO détectée" }
# Jonction type synthèse
$jonctionType = if ($cs -and $cs.PartOfDomain) {
"AD Domain join : OUI ($($cs.Domain)) | Azure AD : $azureADJoined"
} else {
"Hors domaine (workgroup) | Azure AD : $azureADJoined | Enterprise join : $enterpriseJoin"
}
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT LOGON, PROFILS & GPO v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p09-logon-profils-gpo.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Jonction : $jonctionType
ALERTES LOGON / PROFIL / GPO
|- Profils (.bak) : $alerteProfils
|- Services logon : $alerteSvcKO
|- Canal sécurisé AD : $alerteAD
|- Netlogon (nltest) : $alerteNltest
|- Connectivité DC : $alerteDC
|- Événements profil : $alerteProfilEvt
|- Événements GPO : $alerteGPOevt
FENÊTRES RETENUES PAR BLOC
|- Profil utilisateur : $($resProfil.WindowHours) h -> $($resProfil.Count) événements
|- GPO erreurs : $($resGPO.WindowHours) h -> $($resGPO.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Appartenance domaine / jonction ───────────────────────
"`n── APPARTENANCE DOMAINE / JONCTION ─────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($cs) {
$cs | Format-List TypeJonction, Domain, PartOfDomain, Workgroup, Name |
Out-File $outputFile -Append -Encoding UTF8
}
" Azure AD Joined : $azureADJoined" | Out-File $outputFile -Append -Encoding UTF8
" Domain Joined : $domainJoined" | Out-File $outputFile -Append -Encoding UTF8
" Enterprise Joined : $enterpriseJoin" | Out-File $outputFile -Append -Encoding UTF8
" Workplace Joined : $workplaceJoin" | Out-File $outputFile -Append -Encoding UTF8
# ── Section 2 : Services logon ───────────────────────────────────────
"`n── SERVICES LOGON CRITIQUES ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($svcLogon) {
$svcLogon | Format-Table Nom, Statut, Démarrage, Alerte -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
" $alerteSvcKO" | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : ProfileList registre ─────────────────────────────────
"`n── PROFILELIST REGISTRE — Profils et entrées .bak ($nbBAK .bak détecté(s)) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($profilsReg) {
" $alerteProfils" | Out-File $outputFile -Append -Encoding UTF8
if ($nbBAK -gt 0) {
"`n ⚠️ Une entrée .bak indique que Windows a créé un profil temporaire car le profil" |
Out-File $outputFile -Append -Encoding UTF8
" original était verrouillé ou corrompu au moment du logon." |
Out-File $outputFile -Append -Encoding UTF8
" Correction : supprimer l'entrée .bak et renommer l'entrée sans .bak si les données sont intactes." |
Out-File $outputFile -Append -Encoding UTF8
}
"`n Détail ProfileList :" | Out-File $outputFile -Append -Encoding UTF8
$profilsReg | Format-Table SID, CheminProfil, EstBAK, Etat, RefCount -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (registre ProfileList inaccessible)" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : Taille dossiers profils ──────────────────────────────
"`n── TAILLE DES DOSSIERS PROFILS (C:\Users) ──────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($tailleProfils) {
$tailleProfils | Format-Table NomDossier, TailleMo, DateModif -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
$grosProfils = @($tailleProfils) | Where-Object { $_.TailleMo -gt 5000 }
if ($grosProfils) {
"`n ℹ️ Profil(s) volumineux (> 5 Go) — peut ralentir le chargement de session :" |
Out-File $outputFile -Append -Encoding UTF8
$grosProfils | ForEach-Object { " $($_.NomDossier) : $($_.TailleMo) Mo" } |
Out-File $outputFile -Append -Encoding UTF8
}
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 5 : Canal sécurisé AD et Netlogon ────────────────────────
"`n── CANAL SÉCURISÉ AD & NETLOGON ────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Test-ComputerSecureChannel : $secureChannelStr" | Out-File $outputFile -Append -Encoding UTF8
" Netlogon (nltest) : $nltestOK" | Out-File $outputFile -Append -Encoding UTF8
" Connectivité DC : $alerteDC" | Out-File $outputFile -Append -Encoding UTF8
if ($secureChannel -eq $false) {
"`n ⚠️ Canal défaillant — action possible :" | Out-File $outputFile -Append -Encoding UTF8
" Test-ComputerSecureChannel -Repair (réinitialise le canal sans quitter le domaine)" |
Out-File $outputFile -Append -Encoding UTF8
" Ou : netdom resetpwd /server:<DC> /userd:<admin> /passwordd:*" |
Out-File $outputFile -Append -Encoding UTF8
}
"`n Détail nltest :" | Out-File $outputFile -Append -Encoding UTF8
$nltest | Out-File $outputFile -Append -Encoding UTF8
# ── Section 6 : gpresult /r ──────────────────────────────────────────
"`n── GPRESULT /R — GPO appliquées et refusées ────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
# Limiter à 200 lignes pour maîtriser la taille
$gpresultLines = $gpresultStr -split "`n"
if ($gpresultLines.Count -gt 200) {
$gpresultLines[0..199] | Out-File $outputFile -Append -Encoding UTF8
"`n [... sortie tronquée à 200 lignes — voir complet en relançant gpresult /r > gpresult.txt]" |
Out-File $outputFile -Append -Encoding UTF8
} else {
$gpresultStr | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 7 : Événements profil utilisateur ─────────────────────────
"`n── ÉVÉNEMENTS PROFIL UTILISATEUR ($($resProfil.Count) evt / $($resProfil.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resProfil.Events -and $resProfil.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resProfil.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'1500' { "Profil chargé avec succès" }
'1502' { "Profil de cache chargé" }
'1509' { "Paramètre profil impossible à copier" }
'1511' { "Profil temporaire chargé (profil original inaccessible)" }
'1515' { "Profil de secours créé" }
'1530' { "Ruche registre utilisateur supprimée (session précédente non fermée)" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resProfil.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun incident profil détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 8 : Événements GPO ───────────────────────────────────────
"`n── ÉVÉNEMENTS GPO ERREURS ($($resGPO.Count) evt / $($resGPO.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resGPO.Events -and $resGPO.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resGPO.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'1006' { "Traitement GPO : erreur de traitement" }
'1030' { "Liste GPO inaccessible" }
'1058' { "Impossible d'accéder au fichier de paramètres GPO" }
'1085' { "Extension de stratégie en échec" }
'1096' { "Traitement extension en échec (scripts/redirection)" }
'1129' { "GPO non appliquée — accès réseau impossible" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resGPO.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucune erreur GPO détectée dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : dsregcmd /status complet (tronqué) ───────────────────
"`n── DSREGCMD /STATUS (extrait) ──────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($dsregCmd -is [array] -or $dsregCmd -is [string]) {
$dsregLines = if ($dsregCmd -is [array]) { $dsregCmd } else { $dsregCmd -split "`n" }
$dsregLines | Select-Object -First 60 | Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible" | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Données manuelles attendues ─────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptôme précis :
Session lente / Profil temporaire affiché / GPO non appliquée /
Échec logon (message exact) / Autre : ________
[ ] Type d'environnement :
Joint domaine AD classique / Hybride Azure AD + AD / Entra ID seul /
Groupe de travail local
[ ] Test avec autre compte utilisateur :
Résultat = STABLE (problème lié au profil) / MÊME ERREUR (domaine/OS) / NON TESTÉ
[ ] Réseau présent au moment du logon (câble/Wi-Fi connecté) : OUI / NON / INCONNU
[ ] Connexion VPN active au logon : OUI / NON / NON APPLICABLE
[ ] Heure et contexte d'apparition (ex: après MAJ, après changement réseau...)
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P09 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Tester le logon avec un autre compte et noter le résultat"
Write-Host " 2. Compléter la section DONNÉES MANUELLES"
Write-Host " 3. Soumettre le fichier complété au profil P09"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS logon/GPO/profils [file]
- Symptôme précis (session lente, profil temporaire, GPO non appliquée, échec logon)
- Type d'environnement (joint au domaine AD, hybride Azure AD, local)
- Résultat gpresult /r si disponible [file]
- Test sur autre compte ou autre poste si effectué
P10 — Microsoft 365 Client & Identité Moderne
Diagnostic des incidents Microsoft 365 côté client poste Windows 10/11 : Outlook, Teams, OneDrive, activation Office, authentification moderne, synchronisation. Distingue les causes poste des causes tenant/service.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p10-microsoft-365-client-identite-moderne_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : Office installé (ClickToRun/MSI) · Service ClickToRun · Activation OSPP · Cache OST Outlook · OneDrive · Identité Entra ID (dsregcmd) · Tests endpoints M365 · Event IDs crashes Office
Données à saisir manuellement : Fiche de triage P01 · Application M365 concernée · Message d'erreur exact · Contexte d'authentification · Tests navigateur · Captures journaux si disponibles
# ════════════════════════════════════════════════════════════════════
# COLLECTE MICROSOFT 365 CLIENT & IDENTITÉ MODERNE v1.0
# Périmètre : Office, Teams, OneDrive, activation, identité Entra ID,
# connectivité endpoints M365, caches clients
#
# Profil cible : P10 — Microsoft 365 Client & Identité Moderne
# Compatibilité : Windows 10/11 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p10-microsoft-365-client-identite-moderne_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ── Fonction test endpoint M365 ───────────────────────────────────────
function Test-EndpointM365 {
param([string]$Hote, [int]$Port, [string]$Label)
try {
$r = Test-NetConnection -ComputerName $Hote -Port $Port -WarningAction SilentlyContinue
[PSCustomObject]@{
Label = $Label
Hote = $Hote
Port = $Port
Résultat = if ($r.TcpTestSucceeded) { "✅ OK" } else { "⚠️ ECHEC" }
Latence = if ($r.PingReplyDetails) { "$($r.PingReplyDetails.RoundtripTime) ms" } else { 'N/A' }
}
} catch {
[PSCustomObject]@{
Label = $Label
Hote = $Hote
Port = $Port
Résultat = "❌ Erreur"
Latence = 'N/A'
}
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (Office, ClickToRun, identité)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte OS et informations système..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
Write-Host "[2/8] Détection et version Office installé..."
# Office ClickToRun — registre principal
$officeInfo = $null
$officeType = 'Non détecté'
try {
# Office 365/2019/2021/2024 (ClickToRun)
$c2rPaths = @(
'HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Office\ClickToRun\Configuration'
)
foreach ($path in $c2rPaths) {
if (Test-Path $path) {
$c2r = Get-ItemProperty $path -ErrorAction SilentlyContinue
$officeInfo = [PSCustomObject]@{
Type = 'ClickToRun (M365/Office 2019-2024)'
ProductIDs = $c2r.ProductReleaseIds
Canal = $c2r.CDNBaseUrl -replace 'https://officecdn.microsoft.com/pr/', '' -replace '/.*', ''
BuildVersion = $c2r.VersionToReport
LastUpdate = $c2r.UpdatesLastTimeApplied
Platform = $c2r.Platform
LicenseType = $c2r.LicensingACCESStoken
TenantId = if ($c2r.AADTenantId) { $c2r.AADTenantId.Substring(0, [Math]::Min(36, $c2r.AADTenantId.Length)) } else { 'N/A' }
UpdateEnabled = $c2r.UpdateEnabled
}
$officeType = 'ClickToRun'
break
}
}
} catch { $officeInfo = $null }
# Office MSI (legacy — 2016 et antérieur)
if (-not $officeInfo) {
try {
$officeMSI = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Office\*\Common\*' `
-ErrorAction SilentlyContinue |
Where-Object { $_.ProductVersion } |
Select-Object -First 1
if ($officeMSI) {
$officeInfo = [PSCustomObject]@{
Type = 'MSI (Office 2016 ou antérieur)'
BuildVersion = $officeMSI.ProductVersion
Canal = 'N/A (MSI)'
ProductIDs = 'N/A'
TenantId = 'N/A'
}
$officeType = 'MSI'
}
} catch {}
}
# Fallback — liste des apps Office installées
try {
$appsOffice = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' `
-ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like 'Microsoft Office*' -or
$_.DisplayName -like 'Microsoft 365*' -or
$_.DisplayName -like '*Microsoft Teams*' -or
$_.DisplayName -like '*OneDrive*' } |
Select-Object DisplayName, DisplayVersion, Publisher,
@{N='InstalléLe'; E={ $_.InstallDate }}
} catch { $appsOffice = $null }
Write-Host "[3/8] Service ClickToRun et activation..."
# Service ClickToRun
try {
$svcC2R = Get-Service -Name 'ClickToRunSvc' -ErrorAction SilentlyContinue |
Select-Object Name, Status, StartType
} catch { $svcC2R = $null }
# Activation Office via OSPP (nécessite cscript)
$activationOffice = $null
try {
$osppPaths = @(
"$env:ProgramFiles\Microsoft Office\Office16\OSPP.VBS",
"$env:ProgramFiles\Microsoft Office\Office15\OSPP.VBS",
"${env:ProgramFiles(x86)}\Microsoft Office\Office16\OSPP.VBS",
"${env:ProgramFiles(x86)}\Microsoft Office\Office15\OSPP.VBS"
)
$osppPath = $osppPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($osppPath) {
$osppResult = & cscript //NoLogo $osppPath /dstatus 2>&1
$activationOffice = ($osppResult -join "`n").Substring(
0, [Math]::Min(2000, ($osppResult -join "`n").Length))
} else {
$activationOffice = "OSPP.VBS non trouvé — Office ClickToRun récent (activation via compte Microsoft/M365)"
}
} catch { $activationOffice = "Non disponible (cscript inaccessible)" }
Write-Host "[4/8] Cache Outlook OST et profils Outlook..."
# Profils Outlook et fichiers OST
try {
$outlookProfilesPath = 'HKCU:\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles'
$outlookProfiles = @()
if (Test-Path $outlookProfilesPath) {
$outlookProfiles = Get-ChildItem $outlookProfilesPath -ErrorAction SilentlyContinue |
ForEach-Object { $_.PSChildName }
}
$nbProfiles = $outlookProfiles.Count
} catch { $outlookProfiles = @(); $nbProfiles = 0 }
# Fichiers OST (recherche dans AppData)
try {
$ostPath = "$env:LOCALAPPDATA\Microsoft\Outlook"
$ostFiles = if (Test-Path $ostPath) {
Get-ChildItem $ostPath -Filter '*.ost' -ErrorAction SilentlyContinue |
Select-Object Name,
@{N='TailleMo'; E={ [math]::Round($_.Length / 1MB, 0) }},
@{N='DateModif'; E={ $_.LastWriteTime.ToString('dd/MM/yyyy HH:mm') }}
} else { $null }
} catch { $ostFiles = $null }
# OneDrive — état synchronisation
try {
$oneDriveStatus = Get-ItemProperty 'HKCU:\SOFTWARE\Microsoft\OneDrive' `
-ErrorAction SilentlyContinue |
Select-Object CurrentVersion,
@{N='UserEmail'; E={ if ($_.UserEmail) { '(email présent — non affiché)' } else { 'Non configuré' } }},
@{N='SyncFolder'; E={ $_.UserSkyDriveFolder }},
@{N='LastSyncTime'; E={ $_.LastKnownUpdateTime }}
} catch { $oneDriveStatus = $null }
Write-Host "[5/8] Identité Entra ID / Azure AD (dsregcmd)..."
# dsregcmd /status
try {
$dsregCmd = & dsregcmd /status 2>&1
$azureADJoined = if ($dsregCmd -match 'AzureAdJoined\s*:\s*YES') { "✅ OUI" } else { "NON" }
$domainJoined = if ($dsregCmd -match 'DomainJoined\s*:\s*YES') { "✅ OUI" } else { "NON" }
$ssoState = if ($dsregCmd -match 'AzureAdPrt\s*:\s*YES') { "✅ Token SSO présent" } else { "⚠️ Token SSO absent" }
$deviceCompliant = if ($dsregCmd -match 'IsCompliant\s*:\s*YES') { "✅ Conforme" }
elseif ($dsregCmd -match 'IsCompliant\s*:\s*NO') { "⚠️ Non conforme" }
else { "N/A" }
} catch {
$dsregCmd = "Non disponible"
$azureADJoined = "N/A"; $domainJoined = "N/A"
$ssoState = "N/A"; $deviceCompliant = "N/A"
}
Write-Host "[6/8] Tests connectivité endpoints M365 (6 cibles)..."
$testsM365 = @(
(Test-EndpointM365 -Hote 'outlook.office365.com' -Port 443 -Label 'Outlook / Exchange Online'),
(Test-EndpointM365 -Hote 'teams.microsoft.com' -Port 443 -Label 'Microsoft Teams'),
(Test-EndpointM365 -Hote 'login.microsoftonline.com' -Port 443 -Label 'Azure AD / Entra ID Login'),
(Test-EndpointM365 -Hote 'graph.microsoft.com' -Port 443 -Label 'Microsoft Graph API'),
(Test-EndpointM365 -Hote 'onedrive.live.com' -Port 443 -Label 'OneDrive'),
(Test-EndpointM365 -Hote 'www.office.com' -Port 443 -Label 'Portail Office.com')
)
# Proxy
try {
$proxyReg = Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' `
-ErrorAction SilentlyContinue
$proxyActif = if ($proxyReg -and $proxyReg.ProxyEnable -eq 1) {
"⚠️ PROXY ACTIF : $($proxyReg.ProxyServer)"
} else { "✅ Pas de proxy manuel" }
} catch { $proxyActif = "Non disponible" }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements Office/M365)
# ════════════════════════════════════════════════════════════════════
Write-Host "[7/8] Collecte événements applicatifs Office/M365..."
# Événements applicatifs Office (sources connues)
$resOffice = Get-EventsAdaptatif `
-LogNames 'Application' `
-Ids @(1000, 1001, 1002) `
-Label 'Crashes Office/M365' `
-MinEvents 5 `
-MaxEvents 60
# Événements User Device Registration (Entra ID join — ID 300)
try {
$resDevReg = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-User Device Registration/Admin' `
-Ids @(300, 301, 302, 304, 305) `
-Label 'Device Registration' `
-MinEvents 3 `
-MaxEvents 30
} catch {
$resDevReg = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Device Registration' }
}
Write-Host "[8/8] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$echecsM365 = @($testsM365) | Where-Object { $_.Résultat -notlike '✅*' }
$alerteConn = if ($echecsM365.Count -gt 0) {
"⚠️ $($echecsM365.Count) endpoint(s) M365 inaccessible(s) — vérifier réseau/proxy"
} else { "✅ Tous les endpoints M365 joignables" }
$alerteOffice = if ($officeInfo) { "✅ Office détecté : $($officeInfo.Type) — Build $($officeInfo.BuildVersion)" }
else { "⚠️ Office non détecté (aucune installation trouvée)" }
$alerteC2R = if ($svcC2R) {
if ($svcC2R.Status -eq 'Running') { "✅ Service ClickToRun actif" }
else { "⚠️ Service ClickToRun : $($svcC2R.Status) — mise à jour et activation potentiellement bloquées" }
} else { "ℹ️ Service ClickToRun absent (MSI ou Office non installé)" }
$alerteOST = if ($ostFiles) {
$gros = @($ostFiles) | Where-Object { $_.TailleMo -gt 25000 }
if ($gros) { "⚠️ Fichier(s) OST volumineux (> 25 Go) — possible source de lenteur Outlook : $(($gros | ForEach-Object { "$($_.Name) $($_.TailleMo) Mo" }) -join ', ')" }
else { "✅ Fichier(s) OST dans les limites normales" }
} else { "ℹ️ Aucun fichier OST détecté dans le profil courant" }
$alerteSSO = $ssoState
$alerteDevComp = $deviceCompliant
# Pré-calcul des crashs filtrés sur processus Office réels (evite compteur trompeur)
$officeProcsAlerte = @('OUTLOOK', 'WINWORD', 'EXCEL', 'POWERPNT', 'TEAMS', 'OneDrive',
'ONENOTE', 'MSACCESS', 'MSPUB', 'olk', 'ms-teams')
$nbCrashsOfficeReels = if ($resOffice.Events) {
@($resOffice.Events | Where-Object {
$app = if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { '' }
$officeProcsAlerte | Where-Object { $app -like "*$_*" }
}).Count
} else { 0 }
$alerteOfficeEvt = if ($nbCrashsOfficeReels -gt 0) {
"⚠️ $nbCrashsOfficeReels crash(s)/hang(s) processus Office/M365 identifiés"
} elseif ($resOffice.Count -gt 0) {
"ℹ️ $($resOffice.Count) événement(s) applicatifs dans la fenêtre, aucun processus Office/M365 identifié"
} else { "✅ Aucun crash applicatif détecté" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT MICROSOFT 365 CLIENT & IDENTITÉ MODERNE v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p10-microsoft-365-client-identite-moderne.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Office : $(if ($officeInfo) { "$($officeInfo.Type) — $($officeInfo.BuildVersion)" } else { 'Non détecté' })
⚠️ VÉRIFIER STATUS.OFFICE.COM AVANT TOUTE INTERVENTION
Un incident côté Microsoft doit être exclu avant de diagnostiquer côté poste.
ALERTES M365 CLIENT
|- Office installé : $alerteOffice
|- Service ClickToRun : $alerteC2R
|- Cache OST Outlook : $alerteOST
|- Connectivité M365 : $alerteConn
|- Proxy : $proxyActif
|- Token SSO Entra ID : $alerteSSO
|- Conformité device : $alerteDevComp
|- Crashs Office : $alerteOfficeEvt
RÉSUMÉ TESTS ENDPOINTS M365
$( $testsM365 | ForEach-Object { " |- $($_.Label.PadRight(38)) $($_.Résultat) ($($_.Latence))" })
FENÊTRES RETENUES PAR BLOC
|- Crashes Office/M365 : $($resOffice.WindowHours) h -> $($resOffice.Count) événements
|- Device Registration : $($resDevReg.WindowHours) h -> $($resDevReg.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Installation Office ──────────────────────────────────
"`n── INSTALLATION OFFICE / M365 ──────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($officeInfo) {
$officeInfo | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " Office non détecté via les chemins registre standards." | Out-File $outputFile -Append -Encoding UTF8 }
if ($appsOffice) {
"`n Applications Microsoft installées :" | Out-File $outputFile -Append -Encoding UTF8
$appsOffice | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 2 : Service ClickToRun ───────────────────────────────────
"`n── SERVICE CLICKTORUN ──────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($svcC2R) {
" Nom : $($svcC2R.Name) | Statut : $($svcC2R.Status) | Démarrage : $($svcC2R.StartType)" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteC2R" | Out-File $outputFile -Append -Encoding UTF8
if ($svcC2R.Status -ne 'Running') {
"`n ⚠️ Service arrêté — impacts : mises à jour auto bloquées, activation potentiellement expirée." |
Out-File $outputFile -Append -Encoding UTF8
" Correction : Start-Service ClickToRunSvc" | Out-File $outputFile -Append -Encoding UTF8
}
} else { " Service ClickToRunSvc absent (installation MSI ou Office non présent)." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : Activation Office ────────────────────────────────────
"`n── ACTIVATION OFFICE (OSPP) ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($activationOffice) {
$activationOffice | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 4 : Cache Outlook OST ────────────────────────────────────
"`n── CACHE OUTLOOK — Fichiers OST ────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Profils Outlook configurés : $nbProfiles ($($outlookProfiles -join ', '))" |
Out-File $outputFile -Append -Encoding UTF8
if ($ostFiles) {
"`n Fichiers OST détectés :" | Out-File $outputFile -Append -Encoding UTF8
$ostFiles | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" $alerteOST" | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Taille OST > 25 Go → ralentit Outlook. Corriger via : Paramètres du compte > Modifier > réduire la période de synchronisation." |
Out-File $outputFile -Append -Encoding UTF8
" ℹ️ OST corrompu → renommer le fichier .ost (Outlook le recréera) après avoir vérifié la connectivité Exchange." |
Out-File $outputFile -Append -Encoding UTF8
} else { " Aucun fichier OST détecté dans $env:LOCALAPPDATA\Microsoft\Outlook" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 5 : OneDrive ─────────────────────────────────────────────
"`n── ONEDRIVE — État ─────────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($oneDriveStatus) {
$oneDriveStatus | Format-List | Out-File $outputFile -Append -Encoding UTF8
} else { " OneDrive non configuré ou clé registre absente." | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 6 : Identité Entra ID / Azure AD ─────────────────────────
"`n── IDENTITÉ ENTRA ID / AZURE AD (dsregcmd) ─────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Azure AD Joined : $azureADJoined" | Out-File $outputFile -Append -Encoding UTF8
" Domain Joined : $domainJoined" | Out-File $outputFile -Append -Encoding UTF8
" Token SSO (PRT) : $alerteSSO" | Out-File $outputFile -Append -Encoding UTF8
" Conformité device : $alerteDevComp" | Out-File $outputFile -Append -Encoding UTF8
if ($ssoState -match '⚠️') {
"`n ⚠️ Token SSO (PRT) absent — peut causer des invites d'authentification répétées." |
Out-File $outputFile -Append -Encoding UTF8
" Causes possibles : heure système incorrecte, certificat manquant, déjointure Azure AD." |
Out-File $outputFile -Append -Encoding UTF8
" Action : dsregcmd /refreshprt (nécessite connectivité Entra ID)" |
Out-File $outputFile -Append -Encoding UTF8
}
"`n Extrait dsregcmd (50 premières lignes) :" | Out-File $outputFile -Append -Encoding UTF8
if ($dsregCmd -is [array]) {
$dsregCmd | Select-Object -First 50 | Out-File $outputFile -Append -Encoding UTF8
} else { $dsregCmd | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 7 : Tests endpoints M365 ─────────────────────────────────
"`n── TESTS CONNECTIVITÉ ENDPOINTS M365 ──────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteConn" | Out-File $outputFile -Append -Encoding UTF8
" Proxy : $proxyActif" | Out-File $outputFile -Append -Encoding UTF8
"`n Résultats détaillés :" | Out-File $outputFile -Append -Encoding UTF8
$testsM365 | Format-Table Label, Hote, Port, Résultat, Latence -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
if ($echecsM365.Count -gt 0) {
"`n ⚠️ Endpoints inaccessibles : $(($echecsM365 | ForEach-Object { $_.Label }) -join ', ')" |
Out-File $outputFile -Append -Encoding UTF8
" Vérifier : pare-feu entreprise, proxy, règles de filtrage DNS." |
Out-File $outputFile -Append -Encoding UTF8
" Référence endpoints obligatoires : https://docs.microsoft.com/fr-fr/microsoft-365/enterprise/urls-and-ip-address-ranges" |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 8 : Événements applicatifs Office ────────────────────────
"`n── ÉVÉNEMENTS CRASHES OFFICE / M365 ($($resOffice.Count) evt / $($resOffice.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resOffice.Events -and $resOffice.Count -gt 0) {
# Filtrer sur les processus Office connus
$officeProcs = @('OUTLOOK', 'WINWORD', 'EXCEL', 'POWERPNT', 'TEAMS', 'OneDrive',
'ONENOTE', 'MSACCESS', 'MSPUB', 'olk', 'ms-teams')
$evtOffice = $resOffice.Events | Where-Object {
$app = if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { '' }
$officeProcs | Where-Object { $app -like "*$_*" }
}
if ($evtOffice) {
"`n Résumé par application :" | Out-File $outputFile -Append -Encoding UTF8
$evtOffice | Group-Object { if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'Inconnu' } } |
Sort-Object Count -Descending |
ForEach-Object { " $($_.Name) : $($_.Count) crash(s)" } |
Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$evtOffice | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id,
@{N='Application'; E={ if ($_.Properties.Count -gt 0) { $_.Properties[0].Value } else { 'N/A' } }},
@{N='Module'; E={ if ($_.Properties.Count -gt 3) { $_.Properties[3].Value } else { 'N/A' } }},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" Crashs applicatifs présents mais aucun processus Office/M365 identifié." |
Out-File $outputFile -Append -Encoding UTF8
}
} else {
" ✅ Aucun crash applicatif Office/M365 détecté dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Événements Device Registration ───────────────────────
"`n── ÉVÉNEMENTS DEVICE REGISTRATION ($($resDevReg.Count) evt / $($resDevReg.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resDevReg.Events -and $resDevReg.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resDevReg.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'300' { "Enregistrement device réussi" }
'301' { "Enregistrement device en cours" }
'302' { "Échec enregistrement device" }
'304' { "Tentative refresh PRT" }
'305' { "Refresh PRT échoué" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resDevReg.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun événement Device Registration notable détecté." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Données manuelles attendues ─────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
⚠️ AVANT TOUT : vérifier https://status.office.com pour exclure un incident Microsoft.
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Application concernée : Outlook / Teams / OneDrive / Activation Office / Autre : ____
[ ] Message d'erreur exact (copier-coller le texte ou joindre une capture)
[ ] Contexte d'authentification :
Compte local Windows / Compte Microsoft personnel /
Compte Entra ID (compte pro @domaine.com) / Hybride AD + Entra ID
[ ] Test sur navigateur web (même application) :
Résultat = FONCTIONNE / MÊME ERREUR / NON TESTÉ
[ ] Status.office.com au moment du test :
Aucun incident signalé / Incident en cours (lequel : __________)
[ ] Symptôme de synchronisation ou d'activation :
Invite de connexion répétée / Badge rouge OneDrive /
Activation expirée / Fichiers non synchronisés / Autre : ____
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P10 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ⚠️ ÉTAPE PRÉALABLE OBLIGATOIRE :"
Write-Host " → Consulter https://status.office.com pour exclure un incident Microsoft"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Vérifier status.office.com"
Write-Host " 2. Compléter la section DONNÉES MANUELLES"
Write-Host " 3. Soumettre le fichier complété au profil P10"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Application M365 concernée (Outlook, Teams, OneDrive, Office activation, compte pro)
- Message d'erreur exact
- Contexte d'authentification (compte local, Microsoft, Entra ID, hybride)
- Résultats de tests sur navigateur web
- Captures ou journaux applicatifs si disponibles [file]
P11 — Sécurité, Antivirus, EDR & BitLocker
Diagnostic des incidents de sécurité poste Windows 10/11 : blocages antivirus/EDR, faux positifs, politiques locales, incidents BitLocker. Concilie exigence de protection et continuité de service.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p11-securite-antivirus-edr-bitlocker_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : Defender/antivirus · Pare-feu profils · BitLocker volumes + clé recovery · AppLocker · Event IDs sécurité (4625/4648/4720/4728)
Données à saisir manuellement : Fiche de triage P01 · Symptôme précis · Produit de sécurité concerné · Message/alerte exact · Actions déjà tentées
# ════════════════════════════════════════════════════════════════════
# COLLECTE SÉCURITÉ, ANTIVIRUS, EDR & BITLOCKER v1.0
# Périmètre : Defender, menaces, AppLocker, BitLocker, sécurité locale,
# événements sécurité (logon, privilèges, audit)
#
# Profil cible : P11 — Sécurité, Antivirus, EDR & BitLocker
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p11-securite-antivirus-edr-bitlocker_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (Defender, services, BitLocker, AppLocker)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte OS..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
Write-Host "[2/8] État Windows Defender..."
# Defender — état complet
try {
$defenderStatus = Get-MpComputerStatus |
Select-Object AMServiceEnabled, AntispywareEnabled,
AntivirusEnabled, BehaviorMonitorEnabled,
IoavProtectionEnabled, NISEnabled,
OnAccessProtectionEnabled, RealTimeProtectionEnabled,
@{N='VersionDefs'; E={ $_.AntivirusSignatureVersion }},
@{N='DateDefs'; E={ $_.AntivirusSignatureLastUpdated.ToString('dd/MM/yyyy HH:mm') }},
@{N='VersionMoteur'; E={ $_.AMEngineVersion }},
@{N='VersionPlatforme'; E={ $_.AMProductVersion }},
@{N='EtatService'; E={ $_.AMServiceVersion }},
@{N='DernierScan'; E={ $_.QuickScanEndTime.ToString('dd/MM/yyyy HH:mm') }},
@{N='TypeDernierScan'; E={ $_.QuickScanAge }},
TamperProtectionSource,
IsTamperProtected
} catch { $defenderStatus = $null }
# Menaces détectées par Defender
try {
$menaces = Get-MpThreatDetection |
Sort-Object InitialDetectionTime -Descending |
Select-Object -First 20 |
Select-Object ThreatID,
@{N='Menace'; E={ (Get-MpThreat -ThreatID $_.ThreatID -ErrorAction SilentlyContinue).ThreatName }},
@{N='Détecté'; E={ $_.InitialDetectionTime.ToString('dd/MM/yyyy HH:mm') }},
@{N='Statut'; E={ $_.ThreatStatusID }},
@{N='ActionPrise'; E={ $_.CleaningActionID }},
@{N='Ressource'; E={
if ($_.Resources) {
($_.Resources -join '; ').Substring(0, [Math]::Min(150, ($_.Resources -join '; ').Length))
} else { 'N/A' }
}}
$nbMenaces = if ($menaces) { @($menaces).Count } else { 0 }
} catch { $menaces = $null; $nbMenaces = 0 }
Write-Host "[3/8] Centre de sécurité Windows (antivirus tiers)..."
# Antivirus enregistré dans le WSC (Windows Security Center)
try {
$wsAntivirus = Get-CimInstance -Namespace root\SecurityCenter2 -ClassName AntiVirusProduct |
Select-Object displayName, productState,
@{N='EtatProtection'; E={
$state = '{0:X6}' -f $_.productState
$rt = [int]('0x' + $state.Substring(2, 2))
$def = [int]('0x' + $state.Substring(4, 2))
"ProtectionTempsReel=$(if ($rt -eq 16) {'✅ Activée'} else {'⚠️ Désactivée'}) | Définitions=$(if ($def -eq 0) {'✅ OK'} else {'⚠️ Obsolètes'})"
}},
pathToSignedProductExe
} catch { $wsAntivirus = $null }
# Firewall enregistré
try {
$wsFirewall = Get-CimInstance -Namespace root\SecurityCenter2 -ClassName FirewallProduct |
Select-Object displayName, productState,
@{N='Actif'; E={ if ($_.productState -eq 266256) { '✅ Actif' } else { '⚠️ Inactif/Inconnu' } }}
} catch { $wsFirewall = $null }
Write-Host "[4/8] Services sécurité..."
$servicesSecu = @(
@{Nom='WinDefend'; Label='Windows Defender Antivirus'},
@{Nom='Sense'; Label='Microsoft Defender for Endpoint (EDR)'},
@{Nom='MsMpSvc'; Label='Microsoft Malware Protection'},
@{Nom='wscsvc'; Label='Centre de sécurité Windows'},
@{Nom='EventLog'; Label='Journal des événements'},
@{Nom='SecurityHealthService'; Label='Service Santé Sécurité Windows'}
)
try {
$svcSecu = foreach ($s in $servicesSecu) {
$svc = Get-Service -Name $s.Nom -ErrorAction SilentlyContinue
[PSCustomObject]@{
Service = $s.Nom
Label = $s.Label
Statut = if ($svc) { $svc.Status } else { 'Absent' }
Démarrage = if ($svc) { $svc.StartType } else { 'N/A' }
Alerte = if (-not $svc) { "ℹ️ Non installé" }
elseif ($svc.Status -ne 'Running' -and $svc.StartType -eq 'Automatic') { "⚠️ AUTO non démarré" }
elseif ($svc.Status -ne 'Running') { "ℹ️ Arrêté" }
else { "✅ OK" }
}
}
} catch { $svcSecu = $null }
Write-Host "[5/8] AppLocker et BitLocker..."
# AppLocker — politique active
try {
$applockerPolicy = Get-AppLockerPolicy -Effective -ErrorAction SilentlyContinue
$applockerRules = if ($applockerPolicy) {
$total = ($applockerPolicy.RuleCollections | ForEach-Object { $_.Count } | Measure-Object -Sum).Sum
"Politique active : $total règle(s) AppLocker configurée(s)"
} else { "Aucune politique AppLocker active" }
# Export synthétique des collections
$applockerDetail = if ($applockerPolicy) {
$applockerPolicy.RuleCollections | ForEach-Object {
[PSCustomObject]@{
Collection = $_.GetType().Name -replace 'RuleCollection', ''
NbRègles = $_.Count
EnforceMode = if ($_.EnforcementMode) { $_.EnforcementMode } else { 'NotConfigured' }
}
}
} else { $null }
} catch { $applockerPolicy = $null; $applockerRules = "Non disponible"; $applockerDetail = $null }
# BitLocker synthétique
try {
$bitlockerBrief = Get-BitLockerVolume |
Select-Object MountPoint, VolumeStatus, ProtectionStatus,
EncryptionMethod,
@{N='TypeProtecteurs'; E={
($_.KeyProtector | ForEach-Object { $_.KeyProtectorType }) -join ', '
}}
$nbVolBL = if ($bitlockerBrief) { @($bitlockerBrief).Count } else { 0 }
} catch { $bitlockerBrief = $null; $nbVolBL = 0 }
Write-Host "[6/8] Politique de sécurité locale (secedit export)..."
# Export politique sécurité locale — sections Comptes et Audit uniquement
try {
$seceditFile = Join-Path $collectesDir "secedit_export_$timestamp.inf"
& secedit /export /cfg $seceditFile /areas SECURITYPOLICY /quiet 2>&1 | Out-Null
if (Test-Path $seceditFile) {
$seceditContent = Get-Content $seceditFile -ErrorAction SilentlyContinue |
Select-Object -First 80
Remove-Item $seceditFile -Force -ErrorAction SilentlyContinue
} else { $seceditContent = "Export secedit échoué ou fichier non généré" }
} catch { $seceditContent = "Non disponible (secedit inaccessible)" }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements sécurité)
# ════════════════════════════════════════════════════════════════════
Write-Host "[7/8] Collecte événements sécurité..."
# ID 4625 : Échec d'ouverture de session — tentatives d'intrusion
$resLogonFail = Get-EventsAdaptatif `
-LogNames 'Security' `
-Ids @(4625, 4648, 4720, 4728) `
-Label 'Echecs logon/Secu (4625/4648/4720/4728)' `
-MinEvents 5 `
-MaxEvents 80
# ID 4688 : Création de processus — activité suspecte (nécessite audit activé)
$resProc = Get-EventsAdaptatif `
-LogNames 'Security' `
-Ids @(4688) `
-Label 'Création processus (4688)' `
-MinEvents 5 `
-MaxEvents 60
# ID 4673 : Utilisation de privilège sensible
$resPriv = Get-EventsAdaptatif `
-LogNames 'Security' `
-Ids @(4673) `
-Label 'Utilisation privilège (4673)' `
-MinEvents 5 `
-MaxEvents 40
# ID 1102 : Journal d'audit effacé — signal critique
try {
$resAuditCleared = Get-EventsAdaptatif `
-LogNames 'Security' `
-Ids @(1102) `
-Label 'Audit effacé (1102)' `
-MinEvents 1 `
-MaxEvents 20
} catch {
$resAuditCleared = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Audit effacé' }
}
# Événements Defender (journal Microsoft-Windows-Windows Defender)
try {
$resDefender = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-Windows Defender/Operational' `
-Ids @(1006, 1007, 1008, 1009, 1116, 1117, 1118, 1119) `
-Label 'Defender menaces' `
-MinEvents 3 `
-MaxEvents 50
} catch {
$resDefender = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'Defender menaces' }
}
Write-Host "[8/8] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerteDefender = if ($defenderStatus) {
if (-not $defenderStatus.RealTimeProtectionEnabled) {
"⚠️ ALERTE : Protection temps réel Defender DÉSACTIVÉE"
} elseif (-not $defenderStatus.AntivirusEnabled) {
"⚠️ ALERTE : Antivirus Defender DÉSACTIVÉ"
} else {
$ageSign = if ($defenderStatus.DateDefs) {
try { $d = [datetime]::ParseExact($defenderStatus.DateDefs, 'dd/MM/yyyy HH:mm', $null); ((Get-Date) - $d).Days } catch { 'N/A' }
} else { 999 }
if ($ageSign -gt 7) { "⚠️ Définitions Defender anciennes ($ageSign jours) — mise à jour requise" }
else { "✅ Defender actif — définitions du $($defenderStatus.DateDefs)" }
}
} else { "⚠️ Get-MpComputerStatus non disponible (Defender absent ou EDR tiers actif)" }
$alerteMenaces = if ($nbMenaces -gt 0) {
"⚠️ $nbMenaces menace(s) détectée(s) par Defender dans l'historique"
} else { "✅ Aucune menace dans l'historique Defender" }
$alerteAudit = if ($resAuditCleared.Count -gt 0) {
"🚨 ALERTE CRITIQUE : $($resAuditCleared.Count) effacement(s) du journal d'audit détecté(s) (ID 1102)"
} else { "✅ Journal d'audit non effacé" }
$alerteLogon = if ($resLogonFail.Count -gt 20) {
"⚠️ $($resLogonFail.Count) échecs logon détectés — possible tentative de force brute"
} elseif ($resLogonFail.Count -gt 0) {
"ℹ️ $($resLogonFail.Count) échec(s) logon détecté(s)"
} else { "✅ Peu ou aucun échec logon" }
$alerteSvcKO = if ($svcSecu) {
$ko = @($svcSecu) | Where-Object { $_.Alerte -match '⚠️' }
if ($ko) { "⚠️ $(@($ko).Count) service(s) sécurité en anomalie : $(($ko | ForEach-Object { $_.Service }) -join ', ')" }
else { "✅ Tous les services sécurité actifs" }
} else { "⚠️ Services sécurité non inventoriés" }
$alerteAppLocker = if ($applockerPolicy -and ($applockerDetail | Where-Object { $_.EnforceMode -eq 'Enabled' })) {
"ℹ️ AppLocker ACTIF en mode Enforced — peut bloquer des applications légitimes"
} elseif ($applockerPolicy) {
"ℹ️ AppLocker configuré en mode Audit (pas de blocage actif)"
} else { "✅ Aucune politique AppLocker active" }
$alerteBL = if ($bitlockerBrief) {
$actifs = @($bitlockerBrief) | Where-Object { $_.ProtectionStatus -eq 'On' }
if ($actifs) { "ℹ️ $(@($actifs).Count) volume(s) chiffré(s) BitLocker actif" }
else { "✅ Aucun volume BitLocker avec protection active" }
} else { "ℹ️ BitLocker non inventorié" }
$alerteDefEvt = if ($resDefender.Count -gt 0) {
"⚠️ $($resDefender.Count) événement(s) Defender (menaces/actions)"
} else { "✅ Aucun événement Defender notable" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT SÉCURITÉ, ANTIVIRUS, EDR & BITLOCKER v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p11-securite-antivirus-edr-bitlocker.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
ALERTES SÉCURITÉ
|- Defender état : $alerteDefender
|- Menaces Defender : $alerteMenaces
|- Journal audit : $alerteAudit
|- Échecs logon : $alerteLogon
|- Services sécurité : $alerteSvcKO
|- AppLocker : $alerteAppLocker
|- BitLocker : $alerteBL
|- Événements Defender : $alerteDefEvt
FENÊTRES RETENUES PAR BLOC
|- Échecs logon (4625) : $($resLogonFail.WindowHours) h -> $($resLogonFail.Count) événements
|- Création processus (4688) : $($resProc.WindowHours) h -> $($resProc.Count) événements
|- Utilisation privilège (4673): $($resPriv.WindowHours) h -> $($resPriv.Count) événements
|- Audit effacé (1102) : $($resAuditCleared.WindowHours) h -> $($resAuditCleared.Count) événements
|- Defender menaces : $($resDefender.WindowHours) h -> $($resDefender.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Windows Defender état ────────────────────────────────
"`n── WINDOWS DEFENDER — État ─────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($defenderStatus) {
$defenderStatus | Format-List | Out-File $outputFile -Append -Encoding UTF8
" $alerteDefender" | Out-File $outputFile -Append -Encoding UTF8
} else {
" Non disponible (module Defender absent ou EDR tiers a désactivé Defender)." |
Out-File $outputFile -Append -Encoding UTF8
" → Vérifier dans Paramètres > Sécurité Windows > Protection contre les virus et menaces." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 2 : Menaces détectées ────────────────────────────────────
"`n── MENACES DÉTECTÉES PAR DEFENDER ($nbMenaces entrée(s)) ──────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbMenaces -gt 0) {
" $alerteMenaces" | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (20 plus récentes) :" | Out-File $outputFile -Append -Encoding UTF8
$menaces | Format-Table ThreatID, Menace, Détecté, Statut, ActionPrise, Ressource -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Statut : 1=Actif, 2=Quarantaine, 3=Supprimé, 4=Autorisé, 5=Non-action" |
Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucune menace dans l'historique Defender." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 3 : Centre de sécurité Windows ───────────────────────────
"`n── CENTRE DE SÉCURITÉ WINDOWS — Antivirus et pare-feu enregistrés ─" |
Out-File $outputFile -Append -Encoding UTF8
if ($wsAntivirus) {
"`n Antivirus enregistré(s) :" | Out-File $outputFile -Append -Encoding UTF8
$wsAntivirus | Format-Table displayName, EtatProtection, pathToSignedProductExe -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
$tiers = @($wsAntivirus) | Where-Object { $_.displayName -notlike '*Windows Defender*' -and $_.displayName -notlike '*Microsoft Defender*' }
if ($tiers) {
"`n ⚠️ EDR/Antivirus TIERS détecté(s) : $(($tiers | ForEach-Object { $_.displayName }) -join ', ')" |
Out-File $outputFile -Append -Encoding UTF8
" → Logs EDR tiers non collectables automatiquement — voir instructions DONNÉES MANUELLES." |
Out-File $outputFile -Append -Encoding UTF8
}
} else { " Non disponible (SecurityCenter2 inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
if ($wsFirewall) {
"`n Pare-feu enregistré(s) :" | Out-File $outputFile -Append -Encoding UTF8
$wsFirewall | Format-Table displayName, Actif -AutoSize | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : Services sécurité ────────────────────────────────────
"`n── SERVICES SÉCURITÉ ───────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($svcSecu) {
$svcSecu | Format-Table Service, Label, Statut, Démarrage, Alerte -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
" $alerteSvcKO" | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 5 : AppLocker ─────────────────────────────────────────────
"`n── APPLOCKER — Politique active ────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $applockerRules" | Out-File $outputFile -Append -Encoding UTF8
" $alerteAppLocker" | Out-File $outputFile -Append -Encoding UTF8
if ($applockerDetail) {
"`n Collections de règles :" | Out-File $outputFile -Append -Encoding UTF8
$applockerDetail | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ EnforceMode = Enabled → blocage actif. AuditOnly → journalisation sans blocage." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 6 : BitLocker synthétique ────────────────────────────────
"`n── BITLOCKER — État synthétique ────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteBL" | Out-File $outputFile -Append -Encoding UTF8
if ($bitlockerBrief) {
$bitlockerBrief | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Pour le détail complet (clés recovery, vérification) → utiliser le profil P08." |
Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (module BitLocker absent)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 7 : Politique sécurité locale ────────────────────────────
"`n── POLITIQUE DE SÉCURITÉ LOCALE (secedit — extrait) ───────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($seceditContent) {
$seceditContent | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 8 : Événements Defender ──────────────────────────────────
"`n── ÉVÉNEMENTS DEFENDER — Menaces/Actions ($($resDefender.Count) evt / $($resDefender.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resDefender.Events -and $resDefender.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resDefender.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'1006' { "Analyse terminée — menace détectée" }
'1007' { "Action prise sur menace" }
'1008' { "Action échouée sur menace" }
'1009' { "Élément restauré depuis quarantaine" }
'1116' { "Malware détecté" }
'1117' { "Action prise sur malware" }
'1118' { "Action optionnelle échouée" }
'1119' { "Erreur critique action sur malware" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (15 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resDefender.Events | Sort-Object TimeCreated -Descending | Select-Object -First 15 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun événement Defender (menace/action) dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Journal d'audit effacé (ID 1102) ─────────────────────
"`n── JOURNAL D'AUDIT EFFACÉ — ID 1102 ($($resAuditCleared.Count) evt) ────" |
Out-File $outputFile -Append -Encoding UTF8
if ($resAuditCleared.Events -and $resAuditCleared.Count -gt 0) {
" $alerteAudit" | Out-File $outputFile -Append -Encoding UTF8
"`n 🚨 Un effacement du journal d'audit peut indiquer une tentative de dissimulation d'activité." |
Out-File $outputFile -Append -Encoding UTF8
" Escalader immédiatement vers l'équipe sécurité/RSSI si non planifié." |
Out-File $outputFile -Append -Encoding UTF8
$resAuditCleared.Events | Sort-Object TimeCreated -Descending |
Select-Object TimeCreated, Id,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Journal d'audit non effacé." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Échecs logon (ID 4625) ──────────────────────────────
"`n── ÉCHECS LOGON — ID 4625 ($($resLogonFail.Count) evt / $($resLogonFail.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resLogonFail.Events -and $resLogonFail.Count -gt 0) {
" $alerteLogon" | Out-File $outputFile -Append -Encoding UTF8
"`n Résumé par code d'échec :" | Out-File $outputFile -Append -Encoding UTF8
$resLogonFail.Events | Group-Object { if ($_.Properties.Count -gt 7) { "0x{0:X}" -f [int64]$_.Properties[7].Value } else { 'N/A' } } |
Sort-Object Count -Descending | Select-Object -First 10 |
ForEach-Object {
$label = switch ($_.Name) {
'0xC000006A' { "Mot de passe incorrect" }
'0xC0000064' { "Nom d'utilisateur inexistant" }
'0xC000006D' { "Échec générique (nom/mdp)" }
'0xC000006F' { "En dehors des heures autorisées" }
'0xC0000070' { "Station de travail non autorisée" }
'0xC0000072' { "Compte désactivé" }
'0xC000015B' { "Type de logon non accordé" }
'0xC0000234' { "Compte verrouillé" }
default { "" }
}
" Code $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (10 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resLogonFail.Events | Sort-Object TimeCreated -Descending | Select-Object -First 10 |
Select-Object TimeCreated, Id,
@{N='TypeLogon'; E={ if ($_.Properties.Count -gt 10) { $_.Properties[10].Value } else { 'N/A' } }},
@{N='CodeEchec'; E={ if ($_.Properties.Count -gt 7) { "0x{0:X}" -f [int64]$_.Properties[7].Value } else { 'N/A' } }},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Peu ou aucun échec logon détecté." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 11 : Création processus (ID 4688) ────────────────────────
"`n── CRÉATION PROCESSUS — ID 4688 ($($resProc.Count) evt / $($resProc.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resProc.Events -and $resProc.Count -gt 0) {
"`n ℹ️ ID 4688 nécessite l'audit 'Création de processus' activé (secpol.msc)." |
Out-File $outputFile -Append -Encoding UTF8
"`n Top processus créés :" | Out-File $outputFile -Append -Encoding UTF8
$resProc.Events | Group-Object { if ($_.Properties.Count -gt 5) { $_.Properties[5].Value } else { 'N/A' } } |
Sort-Object Count -Descending | Select-Object -First 15 |
ForEach-Object { " $($_.Name) : $($_.Count) lancement(s)" } |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Audit 'Création de processus' non activé ou aucun événement dans la fenêtre." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 12 : Données manuelles attendues ─────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptôme précis :
Blocage application / Blocage script / Alerte antivirus affichée /
Isolement réseau (EDR) / BitLocker recovery / Autre : ________
[ ] Produit de sécurité concerné :
Windows Defender / CrowdStrike Falcon / SentinelOne / Cortex XDR /
BitLocker / AppLocker / Politique locale / Autre : ________
[ ] Message exact ou alerte affichée (copier-coller)
[ ] Actions déjà tentées
[ ] Logs EDR tiers (si applicable) :
CrowdStrike : C:\Windows\System32\drivers\CrowdStrike\*.log
SentinelOne : Panneau de contrôle > SentinelOne > Logs > Exporter
MDE/Defender : Portail security.microsoft.com > Incidents > appareil
(Coller ici les extraits pertinents)
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P11 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Si EDR tiers détecté : récupérer les logs manuellement (voir DONNÉES MANUELLES)"
Write-Host " 2. Si ID 1102 détecté : escalader vers l'équipe sécurité/RSSI"
Write-Host " 3. Compléter la section DONNÉES MANUELLES et soumettre au profil P11"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS sécurité/politique locale/BitLocker [file]
- Symptôme précis (blocage application, blocage script, blocage boot, chiffrement, isolement réseau)
- Produit concerné (antivirus, EDR, BitLocker, AppLocker)
- Message exact ou alerte affichée
- Actions déjà tentées
P12 — Windows Update & Mises à Jour
Diagnostic des blocages, échecs et régressions liés à Windows Update sur poste Windows 10/11. Analyse CBS.log, WU history, codes d'erreur WU et stratégies de rollback ou de réparation des composants de mise à jour.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p12-windows-update-mises-a-jour_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : OS · KB installées · Services WU · CBS.log/DISM.log extraits · SoftwareDistribution · WSUS config · Connectivité WU · Event IDs Windows Update (19/20/24/25/31)
Données à saisir manuellement : Fiche de triage P01 · Symptôme précis · Code erreur WU exact · KB impliquée · Durée du problème · Gestion WSUS
# ════════════════════════════════════════════════════════════════════
# COLLECTE WINDOWS UPDATE & MISES À JOUR v1.0
# Périmètre : historique KB, services WU, CBS.log, DISM.log,
# SoftwareDistribution, WSUS, connectivité, erreurs WU
#
# Profil cible : P12 — Windows Update & Mises à Jour
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p12-windows-update-mises-a-jour_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ── Table des codes d'erreur WU courants ─────────────────────────────
function Get-WUErrorLabel {
param([string]$Code)
$codes = @{
'0x80070002' = 'Fichier introuvable — composants WU corrompus'
'0x80070005' = 'Accès refusé — droits insuffisants'
'0x8007000e' = 'Mémoire insuffisante'
'0x80070057' = 'Paramètre incorrect'
'0x800705b4' = 'Timeout — réseau ou service WU lent'
'0x80072ee2' = 'Timeout réseau — connectivité WU impossible'
'0x80072efd' = 'Connexion refusée — pare-feu ou proxy'
'0x80072efe' = 'Connexion interrompue'
'0x800736b3' = 'Assemblage manquant — réparation SxS requise'
'0x80073712' = 'Fichier requis pour WU manquant'
'0x8007370b' = 'Identifiant de composant incorrect'
'0x8007371b' = 'Transaction en cours — CBS occupé'
'0x80240001' = 'WU non initialisé'
'0x80240034' = 'Mise à jour non applicable'
'0x8024000b' = 'Mise à jour annulée'
'0x8024200b' = 'Pas de mises à jour disponibles'
'0x80242006' = 'Erreur de scan WU'
'0x8024402c' = 'Proxy non configuré correctement pour WU'
'0x800f0922' = 'Partition EFI ou espace système réservé insuffisant'
'0x800f0823' = 'Driver bloquant la mise à niveau'
'0x800f081f' = 'Fichier source SFC/DISM introuvable'
'0x800f0900' = 'Composant magasin corrompu'
'0xc1900101' = 'Driver incompatible lors de mise à niveau Windows'
'0xc1900208' = 'Application incompatible bloquant la mise à niveau'
'0xc190020e' = 'Espace disque insuffisant pour la mise à niveau'
'0xc1900200' = 'Configuration PC ne satisfait pas les prérequis'
'0xe0000100' = 'Annulation de la mise à niveau par l utilisateur'
}
$key = $Code.ToLower()
if ($codes.ContainsKey($key)) { return $codes[$key] }
return 'Code non répertorié — vérifier https://learn.microsoft.com/fr-fr/windows/deployment/upgrade/upgrade-error-codes'
}
# ════════════════════════════════════════════════════════════════════
# PHASE 1 — COLLECTES RAPIDES (OS, services, espace, SoftwareDist)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] Collecte OS et espace disque..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# Espace disque système (prérequis critique pour DISM)
try {
$disqueSysteme = Get-CimInstance Win32_LogicalDisk |
Where-Object { $_.DeviceID -eq $env:SystemDrive } |
Select-Object DeviceID,
@{N='TailleGB'; E={[math]::Round($_.Size / 1GB, 1)}},
@{N='LibreGB'; E={[math]::Round($_.FreeSpace / 1GB, 1)}},
@{N='LibrePct'; E={[math]::Round(($_.FreeSpace / $_.Size) * 100, 1)}}
} catch { $disqueSysteme = $null }
Write-Host "[2/8] Services Windows Update..."
$servicesWU = @(
@{Nom='wuauserv'; Label='Windows Update'},
@{Nom='bits'; Label='BITS (transfert intelligent)'},
@{Nom='cryptsvc'; Label='Services de chiffrement'},
@{Nom='trustedinstaller';Label='Windows Modules Installer'},
@{Nom='wudfhost'; Label='WU Device Framework Host'},
@{Nom='usosvc'; Label='Update Session Orchestrator'},
@{Nom='waasmedicsvc'; Label='WaaS Medic (auto-réparation WU)'}
)
try {
$svcWU = foreach ($s in $servicesWU) {
$svc = Get-Service -Name $s.Nom -ErrorAction SilentlyContinue
[PSCustomObject]@{
Service = $s.Nom
Label = $s.Label
Statut = if ($svc) { $svc.Status } else { 'Absent' }
Démarrage = if ($svc) { $svc.StartType } else { 'N/A' }
Alerte = if (-not $svc) { "ℹ️ Absent" }
elseif ($svc.Status -ne 'Running' -and $svc.StartType -in @('Automatic','Manual')) {
"⚠️ Arrêté"
} else { "✅ OK" }
}
}
} catch { $svcWU = $null }
Write-Host "[3/8] Dossier SoftwareDistribution et WinSxS..."
# Taille SoftwareDistribution\Download
try {
$sdPath = "$env:SystemRoot\SoftwareDistribution\Download"
$sdTaille = if (Test-Path $sdPath) {
$t = (Get-ChildItem $sdPath -Recurse -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
[math]::Round($t / 1MB, 0)
} else { 0 }
$sdAlerte = if ($sdTaille -gt 2048) {
"⚠️ SoftwareDistribution\Download volumineux ($sdTaille Mo) — reset WU recommandé"
} else { "✅ Taille normale ($sdTaille Mo)" }
} catch { $sdTaille = 'N/A'; $sdAlerte = "Non disponible" }
# Taille WinSxS (estimation — ne pas parcourir récursivement, trop lent)
try {
$winsxsPath = "$env:SystemRoot\WinSxS"
$winsxsInfo = Get-Item $winsxsPath -ErrorAction SilentlyContinue
$winsxsAlerte = "ℹ️ WinSxS présent — nettoyage possible via DISM /Cleanup-Image /StartComponentCleanup"
} catch { $winsxsAlerte = "Non disponible" }
Write-Host "[4/8] Paramètres WSUS (registre GPO WU)..."
# WSUS — configuration GPO WU
try {
$wuPolicyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
$wuauPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU'
$wsusConfig = if (Test-Path $wuPolicyPath) {
$props = Get-ItemProperty $wuPolicyPath -ErrorAction SilentlyContinue
[PSCustomObject]@{
WUServer = $props.WUServer
WUStatusServer = $props.WUStatusServer
DisableWindowsUpdateAccess = $props.DisableWindowsUpdateAccess
DoNotConnectToWindowsUpdateInternetLocations = $props.DoNotConnectToWindowsUpdateInternetLocations
TargetGroup = $props.TargetGroup
}
} else { $null }
$wuauConfig = if (Test-Path $wuauPath) {
$props2 = Get-ItemProperty $wuauPath -ErrorAction SilentlyContinue
[PSCustomObject]@{
UseWUServer = $props2.UseWUServer
AUOptions = $props2.AUOptions
ScheduledInstallDay = $props2.ScheduledInstallDay
NoAutoUpdate = $props2.NoAutoUpdate
}
} else { $null }
$wsusActif = if ($wsusConfig -and $wsusConfig.WUServer) {
"⚠️ WSUS CONFIGURÉ : $($wsusConfig.WUServer) — les approbations côté serveur sont hors périmètre poste"
} else { "✅ Pas de WSUS configuré (WU direct Microsoft)" }
} catch { $wsusConfig = $null; $wuauConfig = $null; $wsusActif = "Non disponible" }
Write-Host "[5/8] Historique KB et MAJ en attente..."
# Historique KB installées (20 dernières)
try {
$kbHistorique = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 20 |
Select-Object HotFixID, Description,
@{N='InstalléLe'; E={ if ($_.InstalledOn) { $_.InstalledOn.ToString('dd/MM/yyyy') } else { 'N/A' } }},
InstalledBy
} catch {
try {
$kbHistorique = Get-CimInstance Win32_QuickFixEngineering |
Sort-Object InstalledOn -Descending | Select-Object -First 20 |
Select-Object HotFixID, Description,
@{N='InstalléLe'; E={ if ($_.InstalledOn) { $_.InstalledOn.ToString('dd/MM/yyyy') } else { 'N/A' } }}
} catch { $kbHistorique = $null }
}
# MAJ en attente — via COM (pas de module PSWindowsUpdate requis)
try {
$updateSession = New-Object -ComObject Microsoft.Update.Session
$updateSearcher = $updateSession.CreateUpdateSearcher()
$searchResult = $updateSearcher.Search("IsInstalled=0 and IsHidden=0")
$majEnAttente = $searchResult.Updates | ForEach-Object {
[PSCustomObject]@{
Titre = $_.Title.Substring(0, [Math]::Min(80, $_.Title.Length))
KB = if ($_.KBArticleIDs.Count -gt 0) { "KB$($_.KBArticleIDs[0])" } else { 'N/A' }
Obligatoire = $_.AutoSelectOnWebSites
TaillesMo = [math]::Round($_.MaxDownloadSize / 1MB, 0)
}
}
$nbAttente = if ($majEnAttente) { @($majEnAttente).Count } else { 0 }
} catch {
$majEnAttente = $null
$nbAttente = 0
}
Write-Host "[6/8] CBS.log et DISM.log (extraits)..."
# CBS.log — 150 dernières lignes
try {
$cbsPath = "$env:SystemRoot\Logs\CBS\CBS.log"
if (Test-Path $cbsPath) {
$cbsLines = Get-Content $cbsPath -Tail 150 -ErrorAction SilentlyContinue
$cbsErrors = $cbsLines | Where-Object { $_ -match 'Error|Cannot|Failed|corrupt|0x8' }
$nbCbsErrors = if ($cbsErrors) { @($cbsErrors).Count } else { 0 }
} else {
$cbsLines = @("CBS.log non trouvé dans $env:SystemRoot\Logs\CBS\")
$nbCbsErrors = 0
}
} catch { $cbsLines = @("CBS.log inaccessible"); $nbCbsErrors = 0 }
# DISM.log — 80 dernières lignes
try {
$dismPath = "$env:SystemRoot\Logs\DISM\dism.log"
if (Test-Path $dismPath) {
$dismLines = Get-Content $dismPath -Tail 80 -ErrorAction SilentlyContinue
$dismErrors = $dismLines | Where-Object { $_ -match 'Error|Failed|0x8' }
$nbDismErrors = if ($dismErrors) { @($dismErrors).Count } else { 0 }
} else {
$dismLines = @("dism.log non trouvé dans $env:SystemRoot\Logs\DISM\")
$nbDismErrors = 0
}
} catch { $dismLines = @("dism.log inaccessible"); $nbDismErrors = 0 }
# Connectivité endpoints WU
Write-Host "[7/8] Connectivité endpoints Windows Update..."
$testsWU = @()
$endpointsWU = @(
@{Hote='windowsupdate.microsoft.com'; Port=443; Label='Windows Update principal'},
@{Hote='update.microsoft.com'; Port=443; Label='Update Microsoft'},
@{Hote='download.windowsupdate.com'; Port=80; Label='Download WU (HTTP)'},
@{Hote='download.microsoft.com'; Port=443; Label='Download Microsoft'},
@{Hote='go.microsoft.com'; Port=443; Label='Redirections Microsoft'}
)
foreach ($ep in $endpointsWU) {
try {
$r = Test-NetConnection -ComputerName $ep.Hote -Port $ep.Port -WarningAction SilentlyContinue
$testsWU += [PSCustomObject]@{
Label = $ep.Label
Hote = $ep.Hote
Port = $ep.Port
Résultat = if ($r.TcpTestSucceeded) { "✅ OK" } else { "⚠️ ECHEC" }
}
} catch {
$testsWU += [PSCustomObject]@{
Label = $ep.Label; Hote = $ep.Hote; Port = $ep.Port; Résultat = "❌ Erreur"
}
}
}
# Collecte événements WU
Write-Host "[8/8] Collecte événements WU et construction rapport..."
# ID 20 : Échec installation MAJ
# ID 24 : Échec de téléchargement
# ID 25 : Annulation installation
# ID 31 : Redémarrage requis
# ID 19 : Installation réussie (référence chronologique)
$resWU = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-WindowsUpdateClient/Operational' `
-Ids @(19, 20, 24, 25, 31) `
-Label 'WU Opérationnel' `
-MinEvents 5 `
-MaxEvents 80
# Événements CBS System
$resCBS = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(8193, 8194, 8195, 8196) `
-Label 'CBS Système' `
-MinEvents 3 `
-MaxEvents 30
# ── Alertes ──────────────────────────────────────────────────────────
$alerteEspace = if ($disqueSysteme) {
if ($disqueSysteme.LibrePct -lt 10) {
"⚠️ CRITIQUE : espace système < 10% ($($disqueSysteme.LibreGB) Go) — DISM /RestoreHealth impossible"
} elseif ($disqueSysteme.LibrePct -lt 20) {
"⚠️ ATTENTION : espace système < 20% ($($disqueSysteme.LibreGB) Go) — risque lors des MAJ"
} else { "✅ Espace disque OK ($($disqueSysteme.LibreGB) Go libres / $($disqueSysteme.LibrePct)%)" }
} else { "⚠️ Espace disque non vérifié" }
$svcKO = if ($svcWU) {
@($svcWU) | Where-Object { $_.Alerte -match '⚠️' }
}
$alerteSvc = if ($svcKO -and @($svcKO).Count -gt 0) {
"⚠️ $(@($svcKO).Count) service(s) WU en anomalie : $((@($svcKO) | ForEach-Object { $_.Service }) -join ', ')"
} else { "✅ Tous les services WU actifs" }
$alerteWUevt = if (($resWU.Events | Where-Object { $_.Id -in @(20,24,25) }).Count -gt 0) {
$nb = ($resWU.Events | Where-Object { $_.Id -in @(20,24,25) }).Count
"⚠️ $nb échec(s)/annulation(s) WU détecté(s)"
} else { "✅ Aucun échec WU dans la fenêtre" }
$alerteCBS = if ($nbCbsErrors -gt 0) { "⚠️ $nbCbsErrors ligne(s) d'erreur dans CBS.log" } else { "✅ Pas d'erreur notable dans CBS.log" }
$alerteDISM = if ($nbDismErrors -gt 0) { "⚠️ $nbDismErrors ligne(s) d'erreur dans DISM.log" } else { "✅ Pas d'erreur notable dans DISM.log" }
$alerteAttente = if ($nbAttente -gt 0) { "ℹ️ $nbAttente MAJ en attente d'installation" } else { "✅ Aucune MAJ en attente" }
$echecsWU = @($testsWU) | Where-Object { $_.Résultat -notlike '✅*' }
$alerteConn = if ($echecsWU.Count -gt 0) {
"⚠️ $($echecsWU.Count) endpoint(s) WU inaccessible(s) — vérifier réseau/proxy/WSUS"
} else { "✅ Tous les endpoints WU accessibles" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT WINDOWS UPDATE & MISES À JOUR v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p12-windows-update-mises-a-jour.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
ALERTES WINDOWS UPDATE
|- Espace disque système : $alerteEspace
|- Services WU : $alerteSvc
|- WSUS : $wsusActif
|- MAJ en attente : $alerteAttente
|- Échecs WU (événements) : $alerteWUevt
|- CBS.log erreurs : $alerteCBS
|- DISM.log erreurs : $alerteDISM
|- Connectivité WU : $alerteConn
|- SoftwareDistribution : $sdAlerte
RÉSUMÉ CONNECTIVITÉ ENDPOINTS WU
$( $testsWU | ForEach-Object { " |- $($_.Label.PadRight(38)) $($_.Résultat)" })
FENÊTRES RETENUES PAR BLOC
|- WU Opérationnel : $($resWU.WindowHours) h -> $($resWU.Count) événements
|- CBS Système : $($resCBS.WindowHours) h -> $($resCBS.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : OS et espace disque ──────────────────────────────────
"`n── SYSTÈME ET ESPACE DISQUE ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($disqueSysteme) {
$disqueSysteme | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" $alerteEspace" | Out-File $outputFile -Append -Encoding UTF8
if ($disqueSysteme.LibrePct -lt 20) {
"`n ⚠️ Espace insuffisant — DISM /RestoreHealth nécessite min. 8-10 Go libres." |
Out-File $outputFile -Append -Encoding UTF8
" Nettoyer d'abord via : DISM /Online /Cleanup-Image /StartComponentCleanup" |
Out-File $outputFile -Append -Encoding UTF8
}
}
# ── Section 2 : Services WU ──────────────────────────────────────────
"`n── SERVICES WINDOWS UPDATE ─────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($svcWU) {
$svcWU | Format-Table Service, Label, Statut, Démarrage, Alerte -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
" $alerteSvc" | Out-File $outputFile -Append -Encoding UTF8
if ($svcKO -and @($svcKO).Count -gt 0) {
"`n ℹ️ Procédure reset services WU :" | Out-File $outputFile -Append -Encoding UTF8
" net stop wuauserv && net stop bits && net stop cryptsvc && net stop trustedinstaller" |
Out-File $outputFile -Append -Encoding UTF8
" Ren %SystemRoot%\SoftwareDistribution SoftwareDistribution.old" |
Out-File $outputFile -Append -Encoding UTF8
" Ren %SystemRoot%\System32\catroot2 catroot2.old" |
Out-File $outputFile -Append -Encoding UTF8
" net start wuauserv && net start bits && net start cryptsvc && net start trustedinstaller" |
Out-File $outputFile -Append -Encoding UTF8
}
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : WSUS ─────────────────────────────────────────────────
"`n── CONFIGURATION WSUS / GPO WU ─────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $wsusActif" | Out-File $outputFile -Append -Encoding UTF8
if ($wsusConfig) {
"`n Paramètres WindowsUpdate :" | Out-File $outputFile -Append -Encoding UTF8
$wsusConfig | Format-List | Out-File $outputFile -Append -Encoding UTF8
}
if ($wuauConfig) {
" Paramètres AU :" | Out-File $outputFile -Append -Encoding UTF8
$wuauConfig | Format-List | Out-File $outputFile -Append -Encoding UTF8
$auOpt = switch ($wuauConfig.AUOptions) {
2 { "Notifier avant téléchargement" }
3 { "Télécharger automatiquement, notifier pour installer" }
4 { "Télécharger et installer automatiquement" }
5 { "Permettre à l'admin local de choisir" }
default { "Option $($wuauConfig.AUOptions)" }
}
" AUOptions = $($wuauConfig.AUOptions) : $auOpt" | Out-File $outputFile -Append -Encoding UTF8
}
if (-not $wsusConfig -and -not $wuauConfig) {
" Aucune GPO WU configurée (pas de clé registre WindowsUpdate\Policies)." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 4 : MAJ en attente ───────────────────────────────────────
"`n── MISES À JOUR EN ATTENTE ($nbAttente) ────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbAttente -gt 0) {
" $alerteAttente" | Out-File $outputFile -Append -Encoding UTF8
$majEnAttente | Format-Table KB, Titre, TaillesMo, Obligatoire -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} elseif ($majEnAttente -eq $null) {
" Session COM WU non disponible (service wuauserv arrêté ou WSUS bloquant)." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucune mise à jour en attente." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 5 : Historique KB ────────────────────────────────────────
"`n── HISTORIQUE KB INSTALLÉES (20 dernières) ─────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($kbHistorique) {
$kbHistorique | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 6 : SoftwareDistribution ─────────────────────────────────
"`n── DOSSIER SOFTWAREDISTRIBUTION\DOWNLOAD ───────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" Taille : $sdTaille Mo $sdAlerte" | Out-File $outputFile -Append -Encoding UTF8
" $winsxsAlerte" | Out-File $outputFile -Append -Encoding UTF8
# ── Section 7 : CBS.log extrait ───────────────────────────────────────
"`n── CBS.LOG — 150 dernières lignes ($nbCbsErrors ligne(s) d'erreur) ─" |
Out-File $outputFile -Append -Encoding UTF8
if ($cbsErrors -and $nbCbsErrors -gt 0) {
"`n Lignes d'erreur extraites :" | Out-File $outputFile -Append -Encoding UTF8
$cbsErrors | Select-Object -First 30 |
ForEach-Object {
$_.Substring(0, [Math]::Min(200, $_.Length))
} | Out-File $outputFile -Append -Encoding UTF8
}
"`n Extrait brut (50 dernières lignes) :" | Out-File $outputFile -Append -Encoding UTF8
$cbsLines | Select-Object -Last 50 |
ForEach-Object { $_.Substring(0, [Math]::Min(200, $_.Length)) } |
Out-File $outputFile -Append -Encoding UTF8
# ── Section 8 : DISM.log extrait ─────────────────────────────────────
"`n── DISM.LOG — 80 dernières lignes ($nbDismErrors ligne(s) d'erreur) ─" |
Out-File $outputFile -Append -Encoding UTF8
if ($dismErrors -and $nbDismErrors -gt 0) {
"`n Lignes d'erreur extraites :" | Out-File $outputFile -Append -Encoding UTF8
$dismErrors | Select-Object -First 20 |
ForEach-Object {
$_.Substring(0, [Math]::Min(200, $_.Length))
} | Out-File $outputFile -Append -Encoding UTF8
}
"`n Extrait brut (30 dernières lignes) :" | Out-File $outputFile -Append -Encoding UTF8
$dismLines | Select-Object -Last 30 |
ForEach-Object { $_.Substring(0, [Math]::Min(200, $_.Length)) } |
Out-File $outputFile -Append -Encoding UTF8
# ── Section 9 : Événements WU opérationnel ───────────────────────────
"`n── ÉVÉNEMENTS WU OPÉRATIONNEL ($($resWU.Count) evt / $($resWU.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resWU.Events -and $resWU.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resWU.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'19' { "Installation réussie" }
'20' { "Échec installation" }
'24' { "Échec téléchargement" }
'25' { "Annulation installation" }
'31' { "Redémarrage requis" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
# Extraire et décoder les codes d'erreur des ID 20/24
$evtEchecs = $resWU.Events | Where-Object { $_.Id -in @(20, 24) }
if ($evtEchecs) {
"`n Codes d'erreur détectés dans les échecs :" | Out-File $outputFile -Append -Encoding UTF8
$evtEchecs | ForEach-Object {
$msg = $_.Message
$code = if ($msg -match '0x[0-9A-Fa-f]{8}') { $Matches[0].ToLower() } else { $null }
if ($code) {
" $(if ($_.TimeCreated) { $_.TimeCreated.ToString('dd/MM/yyyy HH:mm') } else { 'date inconnue' }) — Code $code : $(Get-WUErrorLabel $code)"
}
} | Where-Object { $_ } | Select-Object -Unique | Select-Object -First 15 |
Out-File $outputFile -Append -Encoding UTF8
}
"`n Détail (20 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resWU.Events | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id,
@{N='Label'; E={
switch ($_.Id) {
19 { 'Réussi' } 20 { 'Échec install' }
24 { 'Échec DL' } 25 { 'Annulé' }
31 { 'Redémarre requis' } default { '' }
}
}},
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun événement WU dans la fenêtre retenue." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Données manuelles attendues ─────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptôme précis :
WU bloqué à ___% / Échec avec code erreur / Poste ne redémarre plus
après MAJ / Régression post-MAJ (symptômes depuis : ________) /
WU jamais installées depuis longtemps
[ ] Code d'erreur WU exact (copier-coller depuis Windows Update ou l'événement)
→ Référence : https://learn.microsoft.com/fr-fr/windows/deployment/upgrade/upgrade-error-codes
[ ] KB impliquée (si connue) : KB__________
[ ] Depuis combien de temps : ________
[ ] Le poste est-il géré par WSUS :
OUI (serveur : ________) / NON / INCONNU
[ ] Si géré par WSUS : la KB est-elle approuvée côté serveur WSUS ? OUI / NON / INCONNU
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P12 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " ÉTAPES SUIVANTES :"
Write-Host " 1. Compléter la section DONNÉES MANUELLES (code erreur, KB, durée)"
Write-Host " 2. Si WSUS configuré : vérifier côté serveur si la KB est approuvée"
Write-Host " 3. Soumettre le fichier complété au profil P12"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS Windows Update [file]
- Symptôme précis (WU bloqué, échec avec code erreur, poste ne redémarre plus après MAJ, régression post-MAJ)
- Code d'erreur WU exact (ex: 0x80070002, 0x800f0922)
- KB impliquée si connue
- Depuis combien de temps le problème existe
P13 — Impression & Gestion des Imprimantes
Diagnostic des incidents d'impression sur poste Windows 10/11 : spooler, pilotes d'impression, imprimantes réseau, GPO d'impression. Distingue les problèmes poste des problèmes serveur d'impression ou réseau.
📝 Données d'Entrée
Pour obtenir les données d'entrée techniques, exécutez le script ci-dessous dans PowerShell ISE en mode Administrateur. Vous obtiendrez le fichier `collecte_p13-impression-gestion-des-imprimantes_YYYYMMDD_HHMMSS.txt` sur le Bureau dans le dossier `collectes\`.
Données couvertes par le script : Spooler état · Imprimantes installées · Pilotes impression · Ports TCP/IP · Files d'attente · GPO impression · Event IDs spooler
Données à saisir manuellement : Fiche de triage P01 · Symptôme précis · Type d'imprimante · Nombre d'utilisateurs impactés
# ════════════════════════════════════════════════════════════════════
# COLLECTE IMPRESSION & GESTION DES IMPRIMANTES v1.0
# Périmètre : spooler, imprimantes, pilotes, ports, réseau, GPO,
# événements impression
#
# Profil cible : P13 — Impression & Gestion des Imprimantes
# Compatibilité : Windows 10/11, Server 2016/2019/2022 — PS 5.1+
# Exécution : PowerShell ISE en mode Administrateur
# ════════════════════════════════════════════════════════════════════
# ── Paramètres généraux ──────────────────────────────────────────────
$stepHours = 24
$maxWindowHours = 720
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$collectesDir =
Join-Path ([Environment]::GetFolderPath("Desktop")) "collectes"
if (-not (Test-Path $collectesDir)) { md $collectesDir -Force | Out-Null }
$outputFile = Join-Path $collectesDir "collecte_p13-impression-gestion-des-imprimantes_$timestamp.txt"
# ── Fonction collecte adaptative ─────────────────────────────────────
function Get-EventsAdaptatif {
param(
[string[]]$LogNames,
[int[]]$Ids,
[string]$Label,
[int]$MinEvents,
[int]$MaxEvents
)
$windowHours = $stepHours
$events = @()
$lastCount = -1
$stagnation = 0
do {
$startTime = (Get-Date).AddHours(-$windowHours)
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $LogNames
Id = $Ids
StartTime = $startTime
} -ErrorAction SilentlyContinue
} catch { $events = @() }
$currentCount = if ($events) { $events.Count } else { 0 }
Write-Host (" [{0}] -{1} h -> {2} événements (min cible : {3})" -f $Label, $windowHours, $currentCount, $MinEvents)
if ($currentCount -ge $MinEvents) { break }
if ($windowHours -ge $maxWindowHours) {
Write-Host " [$Label] Fenêtre max atteinte — $currentCount événements retenus"
break
}
if ($currentCount -eq $lastCount) {
$stagnation++
if ($stagnation -ge 3) {
Write-Host " [$Label] Stagnation détectée ($stagnation pas) — arrêt anticipé"
break
}
} else { $stagnation = 0 }
$lastCount = $currentCount
$windowHours += $stepHours
} while ($true)
if ($events -and $events.Count -gt $MaxEvents) {
$events = $events | Sort-Object TimeCreated -Descending | Select-Object -First $MaxEvents
}
return [PSCustomObject]@{
Events = $events
WindowHours = $windowHours
Count = if ($events) { $events.Count } else { 0 }
Label = $Label
}
}
# ════════════════════════════════════════════════════════════════════
# CYCLE 1 — SPOOLER (résout 40% des cas — toujours en premier)
# ════════════════════════════════════════════════════════════════════
Write-Host "[1/8] CYCLE 1 — État Spooler et files d'attente..."
try { $os = Get-CimInstance Win32_OperatingSystem }
catch { $os = $null }
# Spooler — état service
try {
$spooler = Get-Service -Name Spooler -ErrorAction SilentlyContinue |
Select-Object Name, Status, StartType,
@{N='DépendancesManquantes'; E={
$deps = $_.ServicesDependedOn | Where-Object { $_.Status -ne 'Running' }
if ($deps) { ($deps | ForEach-Object { $_.Name }) -join ', ' }
else { 'Aucune' }
}}
} catch { $spooler = $null }
# Fichiers dans le spool — .SHD et .SPL (travaux bloqués)
try {
$spoolDir = "$env:SystemRoot\System32\spool\PRINTERS"
$fichiersSpool = if (Test-Path $spoolDir) {
$items = Get-ChildItem $spoolDir -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -in @('.SHD', '.SPL') }
$items | Select-Object Name, Extension,
@{N='TailleKo'; E={[math]::Round($_.Length / 1KB, 0)}},
@{N='DateModif'; E={$_.LastWriteTime.ToString('dd/MM/yyyy HH:mm')}}
} else { $null }
$nbFichiersSpool = if ($fichiersSpool) { @($fichiersSpool).Count } else { 0 }
} catch { $fichiersSpool = $null; $nbFichiersSpool = 0 }
# ════════════════════════════════════════════════════════════════════
# CYCLE 2 — PILOTES (version, date, signé)
# ════════════════════════════════════════════════════════════════════
Write-Host "[2/8] CYCLE 2 — Pilotes d'impression..."
try {
$pilotesImpression = Get-PrinterDriver |
Select-Object Name, DriverVersion,
@{N='MajVersion'; E={$_.MajorVersion}},
@{N='Fabricant'; E={$_.Manufacturer}},
@{N='Environnement'; E={$_.PrinterEnvironment}},
InfPath,
@{N='DateFichier'; E={
# Récupérer la date via PnPSignedDriver si possible
$match = Get-CimInstance Win32_PnPSignedDriver `
-Filter "DeviceName LIKE '%$($_.Name.Replace("'","''").Substring(0,[Math]::Min(30,$_.Name.Length)))%'" `
-ErrorAction SilentlyContinue | Select-Object -First 1
if ($match -and $match.DriverDate) {
$match.DriverDate.ToString('dd/MM/yyyy')
} else { 'N/A' }
}}
} catch { $pilotesImpression = $null }
# Pilotes via Win32_PrinterDriver (complément)
try {
$pilotesCIM = Get-CimInstance Win32_PrinterDriver |
Select-Object Name, Version, SupportedPlatform,
@{N='CheminFichier'; E={
$_.DriverPath.Substring(0, [Math]::Min(80, $_.DriverPath.Length))
}}
} catch { $pilotesCIM = $null }
# ════════════════════════════════════════════════════════════════════
# CYCLE 3 — IMPRIMANTES ET PORTS RÉSEAU
# ════════════════════════════════════════════════════════════════════
Write-Host "[3/8] CYCLE 3 — Imprimantes et ports réseau..."
# Imprimantes installées
try {
$imprimantes = Get-Printer |
Select-Object Name, DriverName, PortName,
PrinterStatus, Shared, Default,
@{N='Alerte'; E={
switch ($_.PrinterStatus) {
1 { '✅ OK' }
2 { '⚠️ Inconnu' }
3 { '⚠️ Inactif' }
4 { '⚠️ Impression' }
5 { '⚠️ Chaud' }
6 { '⚠️ Hors ligne' }
7 { '⚠️ Hors ligne — vérifier connexion' }
default { "Code $($_.PrinterStatus)" }
}
}}
} catch { $imprimantes = $null }
# Ports d'impression
try {
$ports = Get-PrinterPort |
Select-Object Name, Description,
@{N='Protocole'; E={
if ($_.Name -like 'IP_*' -or $_.Name -like 'TCP*') { 'TCP/IP' }
elseif ($_.Name -like 'USB*') { 'USB' }
elseif ($_.Name -like 'LPT*') { 'Parallèle' }
elseif ($_.Name -like '\\*') { 'Réseau UNC' }
else { 'Autre' }
}},
@{N='AdresseIP'; E={
# Extraire IP depuis le nom de port TCP/IP
if ($_.Name -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') {
$Matches[0]
} else { $null }
}}
} catch { $ports = $null }
# Imprimantes partagées depuis ce poste
try {
$partagees = Get-Printer | Where-Object { $_.Shared -eq $true } |
Select-Object Name, ShareName, DriverName, PortName
} catch { $partagees = $null }
# Tests connectivité imprimantes réseau (port 9100)
Write-Host "[4/8] CYCLE 3 — Tests connectivité imprimantes réseau (port 9100)..."
$testsReseau = @()
if ($ports) {
$portsReseau = @($ports) | Where-Object { $_.AdresseIP -ne $null }
foreach ($port in $portsReseau) {
try {
$r = Test-NetConnection -ComputerName $port.AdresseIP -Port 9100 -WarningAction SilentlyContinue
$testsReseau += [PSCustomObject]@{
NomPort = $port.Name
IP = $port.AdresseIP
Port9100 = if ($r.TcpTestSucceeded) { "✅ OK" } else { "⚠️ ECHEC" }
Ping = if ($r.PingSucceeded) { "✅ Joignable" } else { "⚠️ Non joignable" }
LatenceMs = if ($r.PingReplyDetails) { "$($r.PingReplyDetails.RoundtripTime) ms" } else { 'N/A' }
}
} catch {
$testsReseau += [PSCustomObject]@{
NomPort = $port.Name; IP = $port.AdresseIP
Port9100 = "❌ Erreur"; Ping = "❌ Erreur"; LatenceMs = 'N/A'
}
}
}
}
# ════════════════════════════════════════════════════════════════════
# CYCLE 4 — SERVEUR D'IMPRESSION
# ════════════════════════════════════════════════════════════════════
Write-Host "[5/8] CYCLE 4 — Serveur d'impression (si applicable)..."
# Imprimantes connectées via un serveur d'impression (port \\serveur\)
try {
$viaSrvImpression = @($imprimantes) | Where-Object {
$_.PortName -like '\\*' -or $_.Name -like '\\*'
} | Select-Object Name, PortName, DriverName, PrinterStatus
$nbViaSrv = if ($viaSrvImpression) { @($viaSrvImpression).Count } else { 0 }
} catch { $viaSrvImpression = $null; $nbViaSrv = 0 }
# Vérifier si le service Print Spooler est actif sur un serveur distant
# (non faisable localement sans accès distant — noté dans le rapport)
# ════════════════════════════════════════════════════════════════════
# CYCLE 5 — GPO IMPRESSION
# ════════════════════════════════════════════════════════════════════
Write-Host "[6/8] CYCLE 5 — GPO impression et politiques..."
# GPO impression — registre
try {
$gpoPrintersPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers'
$gpoImpression = if (Test-Path $gpoPrintersPath) {
$props = Get-ItemProperty $gpoPrintersPath -ErrorAction SilentlyContinue
[PSCustomObject]@{
KMPrintersAreBlocked = $props.KMPrintersAreBlocked
DisableWebPrinting = $props.DisableWebPrinting
PhysicalLocation = $props.PhysicalLocation
PackagePointAndPrintOnly = $props.PackagePointAndPrintOnly
RestrictDriverInstallationToAdministrators = $props.RestrictDriverInstallationToAdministrators
}
} else { $null }
$gpoActif = if ($gpoImpression) { "ℹ️ GPO impression configurée — peut bloquer l'installation de pilotes" }
else { "✅ Aucune GPO impression dans le registre Policies" }
} catch { $gpoImpression = $null; $gpoActif = "Non disponible" }
# PrintNightmare — vérification clé de mitigation
try {
$printNightmareKey = Get-ItemProperty `
'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint' `
-ErrorAction SilentlyContinue
$noWarningNoElevation = $printNightmareKey.NoWarningNoElevationOnInstall
$updatePromptSettings = $printNightmareKey.UpdatePromptSettings
$printNightmareRisk = if ($noWarningNoElevation -eq 1 -and $updatePromptSettings -eq 0) {
"⚠️ Configuration risquée (CVE-2021-34527) : NoWarningNoElevation=1 + UpdatePrompt=0"
} else { "✅ Configuration Print Nightmare non vulnérable selon le registre" }
} catch { $printNightmareRisk = "ℹ️ Clé PointAndPrint absente — configuration standard" }
# ════════════════════════════════════════════════════════════════════
# PHASE 2 — COLLECTES ADAPTATIVES (événements impression)
# ════════════════════════════════════════════════════════════════════
Write-Host "[7/8] Collecte événements Spooler et impression..."
# ID 7031 : Spooler s'est arrêté de façon inattendue (System)
$resSpoolerCrash = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(7031) `
-Label 'Spooler crash (7031)' `
-MinEvents 3 `
-MaxEvents 30
# Journal PrintService — erreurs travaux et pilotes
try {
$resPrintService = Get-EventsAdaptatif `
-LogNames 'Microsoft-Windows-PrintService/Operational' `
-Ids @(372, 375, 215, 216, 300, 301, 820, 821) `
-Label 'PrintService' `
-MinEvents 3 `
-MaxEvents 60
} catch {
$resPrintService = [PSCustomObject]@{ Events = @(); WindowHours = 0; Count = 0; Label = 'PrintService' }
}
# Filtre spooler dans les événements 7031 (parmi tous les crashs services)
$resSpoolerSystem = Get-EventsAdaptatif `
-LogNames 'System' `
-Ids @(7034, 7031) `
-Label 'Services arrêtés' `
-MinEvents 3 `
-MaxEvents 40
Write-Host "[8/8] Construction du rapport..."
# ── Alertes ──────────────────────────────────────────────────────────
$alerteSpooler = if ($spooler) {
if ($spooler.Status -ne 'Running') {
"⚠️ ALERTE : Spooler ARRÊTÉ ($($spooler.Status)) — cause principale de 40% des incidents impression"
} else { "✅ Spooler actif (Running)" }
} else { "⚠️ Service Spooler introuvable" }
$alerteSpool = if ($nbFichiersSpool -gt 0) {
"⚠️ $nbFichiersSpool fichier(s) bloqué(s) dans le spool — cause fréquente de Spooler planté"
} else { "✅ Spool vide (aucun fichier .SHD/.SPL)" }
$alerteReseau = if ($testsReseau.Count -gt 0) {
$echecs = @($testsReseau) | Where-Object { $_.Port9100 -notlike '✅*' }
if ($echecs.Count -gt 0) {
"⚠️ $($echecs.Count) imprimante(s) réseau non joignable(s) sur port 9100"
} else { "✅ Toutes les imprimantes réseau joignables" }
} else { "ℹ️ Aucun port TCP/IP détecté (imprimante locale USB uniquement ?)" }
$alertePilotes = if ($pilotesImpression) {
$nb = @($pilotesImpression).Count
"ℹ️ $nb pilote(s) d'impression installé(s)"
} else { "⚠️ Pilotes non inventoriés (Get-PrinterDriver inaccessible)" }
$alerteSrv = if ($nbViaSrv -gt 0) {
"ℹ️ $nbViaSrv imprimante(s) via serveur d'impression — si incident collectif, escalader côté serveur"
} else { "✅ Aucune imprimante via serveur d'impression UNC" }
$alerteSpoolerEvt = if ($resSpoolerCrash.Count -gt 0) {
"⚠️ $($resSpoolerCrash.Count) crash(s) Spooler détecté(s) (ID 7031)"
} else { "✅ Aucun crash Spooler (ID 7031)" }
$alertePrintNight = $printNightmareRisk
# Imprimante par défaut
$imprimanteDefaut = if ($imprimantes) {
@($imprimantes) | Where-Object { $_.Default -eq $true } | Select-Object -First 1
}
$alerteDefaut = if ($imprimanteDefaut) {
"✅ Imprimante par défaut : $($imprimanteDefaut.Name) — Port : $($imprimanteDefaut.PortName)"
} else { "⚠️ Aucune imprimante définie par défaut" }
# ── En-tête ──────────────────────────────────────────────────────────
$header = @"
════════════════════════════════════════════════════════════════════
RAPPORT IMPRESSION & GESTION DES IMPRIMANTES v1.0 — $env:COMPUTERNAME
Généré le : $(Get-Date -Format 'dd/MM/yyyy HH:mm:ss')
Script : script_p13-impression-gestion-des-imprimantes.txt
════════════════════════════════════════════════════════════════════
OS : $(if ($os) { "$($os.Caption) — Build $($os.BuildNumber)" } else { 'N/A' })
Imprimantes : $(if ($imprimantes) { @($imprimantes).Count } else { 0 }) installée(s)
ALERTES IMPRESSION — CYCLE SPOOLER EN PREMIER
|- Spooler état : $alerteSpooler
|- Fichiers spool : $alerteSpool
|- Pilotes : $alertePilotes
|- Connectivité réseau : $alerteReseau
|- Serveur impression : $alerteSrv
|- Crashs Spooler : $alerteSpoolerEvt
|- GPO impression : $gpoActif
|- Print Nightmare : $alertePrintNight
|- Imprimante défaut : $alerteDefaut
FENÊTRES RETENUES PAR BLOC
|- Spooler crash (7031) : $($resSpoolerCrash.WindowHours) h -> $($resSpoolerCrash.Count) événements
|- PrintService : $($resPrintService.WindowHours) h -> $($resPrintService.Count) événements
|- Services arrêtés : $($resSpoolerSystem.WindowHours) h -> $($resSpoolerSystem.Count) événements
════════════════════════════════════════════════════════════════════
"@
$header | Out-File $outputFile -Encoding UTF8
# ════════════════════════════════════════════════════════════════════
# SECTIONS DE DÉTAIL
# ════════════════════════════════════════════════════════════════════
# ── Section 1 : Spooler et spool ─────────────────────────────────────
"`n── CYCLE 1 : SPOOLER & FILES D'ATTENTE ────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($spooler) {
" Statut : $($spooler.Status) | Démarrage : $($spooler.StartType)" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteSpooler" | Out-File $outputFile -Append -Encoding UTF8
if ($spooler.DépendancesManquantes -ne 'Aucune') {
" ⚠️ Dépendances manquantes : $($spooler.DépendancesManquantes)" |
Out-File $outputFile -Append -Encoding UTF8
}
} else { " Service Spooler introuvable." | Out-File $outputFile -Append -Encoding UTF8 }
"`n Fichiers dans C:\Windows\System32\spool\PRINTERS ($nbFichiersSpool fichier(s)) :" |
Out-File $outputFile -Append -Encoding UTF8
if ($nbFichiersSpool -gt 0) {
" $alerteSpool" | Out-File $outputFile -Append -Encoding UTF8
$fichiersSpool | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Procédure nettoyage spool :" | Out-File $outputFile -Append -Encoding UTF8
" 1. net stop spooler" | Out-File $outputFile -Append -Encoding UTF8
" 2. del /Q /F %SystemRoot%\System32\spool\PRINTERS\*.*" |
Out-File $outputFile -Append -Encoding UTF8
" 3. net start spooler" | Out-File $outputFile -Append -Encoding UTF8
" ⚠️ Les travaux en cours seront perdus. Prévenir l'utilisateur." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Spool vide." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 2 : Pilotes d'impression ─────────────────────────────────
"`n── CYCLE 2 : PILOTES D'IMPRESSION ─────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($pilotesImpression) {
$pilotesImpression | Format-Table Name, Fabricant, DriverVersion, DateFichier, Environnement -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
# Pilotes 32 bits sur OS 64 bits — problème fréquent
$pilotes32sur64 = @($pilotesImpression) | Where-Object { $_.Environnement -like '*x86*' }
if ($pilotes32sur64) {
"`n ⚠️ Pilote(s) 32 bits détecté(s) sur OS 64 bits :" | Out-File $outputFile -Append -Encoding UTF8
$pilotes32sur64 | ForEach-Object { " - $($_.Name)" } | Out-File $outputFile -Append -Encoding UTF8
" Peut causer instabilité du Spooler sous Windows 10/11 64 bits." |
Out-File $outputFile -Append -Encoding UTF8
}
} else { " Non disponible (Get-PrinterDriver inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 3 : Imprimantes installées ───────────────────────────────
"`n── CYCLE 3 : IMPRIMANTES INSTALLÉES ───────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($imprimantes) {
$imprimantes | Format-Table Name, DriverName, PortName, PrinterStatus, Shared, Default, Alerte -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
" $alerteDefaut" | Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Get-Printer inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 4 : Ports d'impression ───────────────────────────────────
"`n── PORTS D'IMPRESSION ──────────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
if ($ports) {
$ports | Format-Table Name, Description, Protocole, AdresseIP -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
} else { " Non disponible (Get-PrinterPort inaccessible)" | Out-File $outputFile -Append -Encoding UTF8 }
# ── Section 5 : Tests connectivité réseau ────────────────────────────
"`n── CYCLE 3 : TESTS CONNECTIVITÉ IMPRIMANTES RÉSEAU (port 9100) ────" |
Out-File $outputFile -Append -Encoding UTF8
if ($testsReseau.Count -gt 0) {
" $alerteReseau" | Out-File $outputFile -Append -Encoding UTF8
$testsReseau | Format-Table NomPort, IP, Port9100, Ping, LatenceMs -AutoSize |
Out-File $outputFile -Append -Encoding UTF8
$echecsNet = @($testsReseau) | Where-Object { $_.Port9100 -notlike '✅*' }
if ($echecsNet.Count -gt 0) {
"`n ⚠️ Imprimante(s) non joignable(s) :" | Out-File $outputFile -Append -Encoding UTF8
" Vérifier : imprimante allumée, IP fixe ou réservation DHCP, câble réseau." |
Out-File $outputFile -Append -Encoding UTF8
" IP changée ? Accéder au panel web de l'imprimante pour vérifier." |
Out-File $outputFile -Append -Encoding UTF8
}
} else {
" Aucun port TCP/IP détecté — imprimante(s) USB locale(s) uniquement ou hors réseau." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 6 : Serveur d'impression ─────────────────────────────────
"`n── CYCLE 4 : SERVEUR D'IMPRESSION ─────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $alerteSrv" | Out-File $outputFile -Append -Encoding UTF8
if ($nbViaSrv -gt 0) {
$viaSrvImpression | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
"`n ⚠️ Si l'incident est COLLECTIF (plusieurs postes) :" | Out-File $outputFile -Append -Encoding UTF8
" → Cause probable côté serveur d'impression (file bloquée, pilote serveur)." |
Out-File $outputFile -Append -Encoding UTF8
" → Test discriminant : connecter directement l'imprimante en IP (bypass serveur)." |
Out-File $outputFile -Append -Encoding UTF8
" → Si fonctionne en direct = problème serveur d'impression (hors périmètre poste)." |
Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucune imprimante connectée via UNC serveur d'impression." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 7 : GPO impression ───────────────────────────────────────
"`n── CYCLE 5 : GPO IMPRESSION ────────────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
" $gpoActif" | Out-File $outputFile -Append -Encoding UTF8
" $alertePrintNight" | Out-File $outputFile -Append -Encoding UTF8
if ($gpoImpression) {
"`n Paramètres GPO Printers :" | Out-File $outputFile -Append -Encoding UTF8
$gpoImpression | Format-List | Out-File $outputFile -Append -Encoding UTF8
if ($gpoImpression.KMPrintersAreBlocked -eq 1) {
" ⚠️ KMPrintersAreBlocked = 1 → Pilotes en mode noyau bloqués (sécurité Print Nightmare)" |
Out-File $outputFile -Append -Encoding UTF8
}
if ($gpoImpression.RestrictDriverInstallationToAdministrators -eq 1) {
" ⚠️ RestrictDriverInstallation = 1 → Installation pilotes réservée aux admins" |
Out-File $outputFile -Append -Encoding UTF8
}
}
# ── Section 8 : Événements Spooler (ID 7031) ─────────────────────────
"`n── CRASHS SPOOLER — ID 7031 ($($resSpoolerCrash.Count) evt / $($resSpoolerCrash.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resSpoolerCrash.Events -and $resSpoolerCrash.Count -gt 0) {
" $alerteSpoolerEvt" | Out-File $outputFile -Append -Encoding UTF8
"`n ℹ️ Crash Spooler répété → pilote défaillant, fichier spool corrompu ou débordement." |
Out-File $outputFile -Append -Encoding UTF8
$resSpoolerCrash.Events | Sort-Object TimeCreated -Descending |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" ✅ Aucun crash Spooler (ID 7031) détecté." | Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 9 : Événements PrintService ──────────────────────────────
"`n── ÉVÉNEMENTS PRINTSERVICE ($($resPrintService.Count) evt / $($resPrintService.WindowHours) h) ──" |
Out-File $outputFile -Append -Encoding UTF8
if ($resPrintService.Events -and $resPrintService.Count -gt 0) {
"`n Résumé par ID :" | Out-File $outputFile -Append -Encoding UTF8
$resPrintService.Events | Group-Object Id | Sort-Object Count -Descending |
ForEach-Object {
$label = switch ($_.Name) {
'215' { "Pilote d'impression installé" }
'216' { "Pilote d'impression supprimé" }
'300' { "Imprimante ajoutée" }
'301' { "Imprimante supprimée" }
'372' { "Travail d'impression en erreur" }
'375' { "Erreur document (fichier spool corrompu)" }
'820' { "Échec connexion imprimante" }
'821' { "Erreur communication pilote" }
default { "" }
}
" ID $($_.Name) ($label) : $($_.Count) occurrence(s)"
} | Out-File $outputFile -Append -Encoding UTF8
"`n Détail (20 plus récents) :" | Out-File $outputFile -Append -Encoding UTF8
$resPrintService.Events | Sort-Object TimeCreated -Descending | Select-Object -First 20 |
Select-Object TimeCreated, Id, LevelDisplayName,
@{N='Message'; E={
($_.Message -replace '\s+', ' ').Substring(0, [Math]::Min(200, $_.Message.Length))
}} |
Format-List | Out-File $outputFile -Append -Encoding UTF8
} else {
" Aucun événement PrintService notable dans la fenêtre retenue." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 10 : Imprimantes partagées ───────────────────────────────
if ($partagees -and @($partagees).Count -gt 0) {
"`n── IMPRIMANTES PARTAGÉES DEPUIS CE POSTE ───────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
$partagees | Format-Table -AutoSize | Out-File $outputFile -Append -Encoding UTF8
" ℹ️ Ce poste est potentiellement serveur d'impression pour d'autres postes." |
Out-File $outputFile -Append -Encoding UTF8
}
# ── Section 11 : Données manuelles attendues ─────────────────────────
"`n── DONNÉES MANUELLES À COMPLÉTER ───────────────────────────────────" |
Out-File $outputFile -Append -Encoding UTF8
@"
Coller ci-dessous les données suivantes avant de soumettre à l'IA :
[ ] Symptôme précis :
Impression impossible / Spooler planté / Pilote en erreur /
Imprimante réseau absente / Travaux bloqués en file /
Impression partielle ou corrompue / Autre : ________
[ ] Type d'imprimante concernée :
Locale USB / Réseau IP directe / Partagée via serveur d'impression
Nom exact de l'imprimante : ________
[ ] Nombre d'utilisateurs impactés :
1 poste isolé / Plusieurs postes / Tous les postes
[ ] Tests déjà effectués :
Nettoyage spool : OUI / NON
Redémarrage Spooler : OUI / NON
Réinstallation pilote : OUI / NON
Test impression depuis autre poste : OUI (résultat : ____) / NON
[ ] Si imprimante réseau : l'imprimante est-elle accessible depuis son
panel web (http://[IP imprimante]) ? OUI / NON / NON TESTÉ
"@ | Out-File $outputFile -Append -Encoding UTF8
# ── Fin ──────────────────────────────────────────────────────────────
"`n════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
" FIN DU RAPPORT — Fichier : $outputFile" |
Out-File $outputFile -Append -Encoding UTF8
"════════════════════════════════════════════════════════════════════" |
Out-File $outputFile -Append -Encoding UTF8
Write-Host ""
Write-Host "✅ Collecte P13 terminée."
Write-Host " Fichier : $outputFile"
Write-Host ""
Write-Host " LOGIQUE DE DIAGNOSTIC RECOMMANDÉE :"
Write-Host " 1. Spooler arrêté → net stop spooler, vider spool, net start spooler"
Write-Host " 2. Fichiers bloqués → nettoyer spool (procédure section 1)"
Write-Host " 3. Réseau ECHEC → vérifier IP imprimante, câble, DHCP/réservation"
Write-Host " 4. Incident collectif → escalader vers serveur d'impression"
Write-Host " 5. GPO bloquante → vérifier RestrictDriverInstallation et KMPrintersAreBlocked"
Write-Host ""
Write-Host "Collecte terminee. Vous pouvez fermer cette fenetre." -ForegroundColor Green
Write-Host "Retournez dans le lanceur pour saisir les donnees et generer le prompt." -ForegroundColor Cyan
- Fiche de triage P01 [file]
- Résultat script PS diagnostic spooler/imprimantes [file]
- Symptôme précis (impression impossible, spooler planté, pilote en erreur, imprimante réseau absente, travaux bloqués)
- Type d'imprimante (locale USB, réseau IP, partagée via serveur d'impression)
- Nombre d'utilisateurs impactés (isolé ou collectif)