# JDS — Outil tech (diagnostic Windows / Office / inventaire) # Lancement: irm https://tool.fenrir.pw | iex # Version: 1.1.0 $script:JdsVersion = '1.1.0' $script:JdsLaunchUrl = 'https://tool.fenrir.pw' $ErrorActionPreference = 'Continue' $ProgressPreference = 'SilentlyContinue' # --------------------------------------------------------------------------- # Élévation admin + STA (WPF) # --------------------------------------------------------------------------- function Test-JdsAdmin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object Security.Principal.WindowsPrincipal($id) return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Get-JdsProcessPath { try { return [Diagnostics.Process]::GetCurrentProcess().MainModule.FileName } catch { return $null } } function Start-JdsElevated { $exe = Get-JdsProcessPath $isOwnExe = $exe -and ($exe -match '\.exe$') -and ($exe -notmatch '(?i)\\(powershell|pwsh|powershell_ise)\.exe$') if ($isOwnExe) { Start-Process -FilePath $exe -Verb RunAs | Out-Null return } if ($PSCommandPath -and (Test-Path -LiteralPath $PSCommandPath)) { Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList @( '-STA', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $PSCommandPath ) | Out-Null return } Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList @( '-STA', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', "irm $script:JdsLaunchUrl | iex" ) | Out-Null } $sta = [Threading.Thread]::CurrentThread.GetApartmentState() $needRelaunch = -not (Test-JdsAdmin) -or ($sta -ne 'STA') if ($needRelaunch) { if (-not (Test-JdsAdmin)) { Write-Host 'JDS: elevation administrateur requise...' } Start-JdsElevated exit } Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, Microsoft.VisualBasic | Out-Null function Hide-JdsConsole { try { $def = @' using System; using System.Runtime.InteropServices; public static class JdsConsole { [DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); } '@ Add-Type -TypeDefinition $def -ErrorAction SilentlyContinue $hwnd = [JdsConsole]::GetConsoleWindow() if ($hwnd -ne [IntPtr]::Zero) { [JdsConsole]::ShowWindow($hwnd, 0) | Out-Null } } catch { } } # --------------------------------------------------------------------------- # Collecte # --------------------------------------------------------------------------- function Get-JdsSafe([scriptblock]$Block, $Fallback = '') { try { & $Block } catch { $Fallback } } function Get-JdsLicenseStatusText([int]$Code) { switch ($Code) { 0 { 'Non licencié' } 1 { 'Licencié (activé)' } 2 { 'Grace OOB' } 3 { 'Grace OOT' } 4 { 'Grace non authentique' } 5 { 'Notification' } 6 { 'Grace étendue' } default { "Inconnu ($Code)" } } } function Get-JdsOsInfo { $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cv = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction Stop $boot = $os.LastBootUpTime $uptime = (Get-Date) - $boot $arch = if ([Environment]::Is64BitOperatingSystem) { '64 bits' } else { '32 bits' } $display = $cv.DisplayVersion if (-not $display) { $display = $cv.ReleaseId } $ubr = $cv.UBR $buildFull = if ($null -ne $ubr) { "$($cv.CurrentBuild).$ubr" } else { [string]$cv.CurrentBuild } [pscustomobject]@{ ComputerName = $cs.Name User = "$env:USERDOMAIN\$env:USERNAME" Domain = $cs.Domain PartOfDomain = [bool]$cs.PartOfDomain Manufacturer = $cs.Manufacturer Model = $cs.Model Caption = $os.Caption EditionId = $cv.EditionID DisplayVersion = $display Build = $buildFull CurrentBuild = $cv.CurrentBuild InstallDate = $os.InstallDate LastBoot = $boot Uptime = '{0}j {1}h {2}min' -f [int]$uptime.TotalDays, $uptime.Hours, $uptime.Minutes Arch = $arch InstallType = $cv.InstallationType RegisteredOwner = $cv.RegisteredOwner ProductName = $cv.ProductName OsVersion = $os.Version TotalRamGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1) } } function Get-JdsWindowsLicense { $winAppId = '55c92734-d682-4d71-983e-d6ec3f16059f' $products = @(Get-CimInstance -ClassName SoftwareLicensingProduct -ErrorAction SilentlyContinue | Where-Object { $_.ApplicationID -eq $winAppId -and $_.PartialProductKey }) $p = $products | Select-Object -First 1 if (-not $p) { return [pscustomobject]@{ Found = $false Name = 'Introuvable' Description = '' Status = 'Inconnu' StatusCode = -1 Licensed = $false PartialKey = '' Channel = '' RemainingGrace = '' } } $grace = '' if ($p.GracePeriodRemaining -and $p.GracePeriodRemaining -gt 0) { $days = [math]::Round($p.GracePeriodRemaining / 1440, 1) $grace = "$days jours" } [pscustomobject]@{ Found = $true Name = $p.Name Description = $p.Description Status = Get-JdsLicenseStatusText ([int]$p.LicenseStatus) StatusCode = [int]$p.LicenseStatus Licensed = ([int]$p.LicenseStatus -eq 1) PartialKey = $p.PartialProductKey Channel = $p.ProductKeyChannel RemainingGrace = $grace EvaluationEnd = $p.EvaluationEndDate } } function Get-JdsOfficeInfo { $officeAppId = '0ff1ce15-a989-479d-af46-f275c6370663' $c2rPath = 'HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration' $c2r = $null if (Test-Path $c2rPath) { $c2r = Get-ItemProperty -Path $c2rPath -ErrorAction SilentlyContinue } $licenses = @(Get-CimInstance -ClassName SoftwareLicensingProduct -ErrorAction SilentlyContinue | Where-Object { $_.ApplicationID -eq $officeAppId -and $_.PartialProductKey }) $osppText = '' $osppCandidates = @( "$env:ProgramFiles\Microsoft Office\Office16\ospp.vbs", "${env:ProgramFiles(x86)}\Microsoft Office\Office16\ospp.vbs", "$env:ProgramFiles\Microsoft Office\Office15\ospp.vbs", "${env:ProgramFiles(x86)}\Microsoft Office\Office15\ospp.vbs" ) $ospp = $osppCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 if ($ospp) { try { $osppText = & cscript.exe //Nologo $ospp /dstatus 2>&1 | Out-String } catch { $osppText = $_.Exception.Message } } $licensed = $false $statusSummary = @() foreach ($lic in $licenses) { $ok = [int]$lic.LicenseStatus -eq 1 if ($ok) { $licensed = $true } $statusSummary += '{0} — {1} (clé …{2})' -f $lic.Name, (Get-JdsLicenseStatusText ([int]$lic.LicenseStatus)), $lic.PartialProductKey } $found = [bool]$c2r -or $licenses.Count -gt 0 -or $ospp [pscustomobject]@{ Found = $found Licensed = $licensed Version = if ($c2r) { $c2r.VersionToReport } else { '' } ProductIds = if ($c2r) { $c2r.ProductReleaseIds } else { '' } Channel = if ($c2r) { $c2r.UpdateChannel } else { '' } ClientFolder = if ($c2r) { $c2r.ClientFolder } else { '' } Platform = if ($c2r) { $c2r.Platform } else { '' } Licenses = $licenses StatusLines = $statusSummary OsppPath = $ospp OsppText = $osppText } } function Get-JdsApps { $rows = New-Object System.Collections.Generic.List[object] $targets = @( @{ Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'; Scope = 'Machine 64' }, @{ Path = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'; Scope = 'Machine 32' }, @{ Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'; Scope = 'Utilisateur' } ) foreach ($t in $targets) { Get-ItemProperty -Path $t.Path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.SystemComponent -ne 1 } | ForEach-Object { $sizeMb = $null if ($_.EstimatedSize) { $sizeMb = [math]::Round($_.EstimatedSize / 1024, 1) } $rows.Add([pscustomobject]@{ Name = [string]$_.DisplayName Version = [string]$_.DisplayVersion Publisher = [string]$_.Publisher InstallDate = [string]$_.InstallDate SizeMB = $sizeMb Scope = $t.Scope UninstallString = [string]$_.UninstallString QuietUninstallString = [string]$_.QuietUninstallString }) | Out-Null } } $rows | Sort-Object Name -Unique } function Get-JdsHardware { $cpu = Get-CimInstance -ClassName Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1 $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue $csp = Get-CimInstance -ClassName Win32_ComputerSystemProduct -ErrorAction SilentlyContinue $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction SilentlyContinue $gpu = @(Get-CimInstance -ClassName Win32_VideoController -ErrorAction SilentlyContinue | Where-Object { $_.Name }) $ram = @(Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction SilentlyContinue) $ramGb = [math]::Round(($ram | Measure-Object Capacity -Sum).Sum / 1GB, 1) $ramSticks = $ram | ForEach-Object { '{0} Go {1} {2}' -f [math]::Round($_.Capacity / 1GB, 1), $_.Manufacturer, $_.PartNumber } [pscustomobject]@{ CpuName = $cpu.Name CpuCores = $cpu.NumberOfCores CpuLogical = $cpu.NumberOfLogicalProcessors CpuMaxMHz = $cpu.MaxClockSpeed RamGB = $ramGb RamSticks = $ramSticks Manufacturer = $cs.Manufacturer Model = $cs.Model SystemSku = $cs.SystemSKUNumber Vendor = $csp.Vendor ProductName = $csp.Name Serial = $csp.IdentifyingNumber Uuid = $csp.UUID BiosVersion = $bios.SMBIOSBIOSVersion BiosSerial = $bios.SerialNumber Gpus = @($gpu | ForEach-Object { $_.Name }) } } function Get-JdsNetwork { $rows = New-Object System.Collections.Generic.List[object] try { $configs = @(Get-NetIPConfiguration -ErrorAction Stop | Where-Object { $_.NetAdapter.Status -eq 'Up' }) foreach ($c in $configs) { $ipv4 = @($c.IPv4Address | ForEach-Object { $_.IPAddress }) -join ', ' $ipv6 = @($c.IPv6Address | Where-Object { $_.PrefixOrigin -ne 'WellKnown' } | ForEach-Object { $_.IPAddress }) -join ', ' $gw = @($c.IPv4DefaultGateway | ForEach-Object { $_.NextHop }) -join ', ' $dns = @($c.DNSServer | ForEach-Object { $_.ServerAddresses } | ForEach-Object { $_ }) -join ', ' $rows.Add([pscustomobject]@{ Adapter = $c.InterfaceAlias Mac = $c.NetAdapter.MacAddress IPv4 = $ipv4 IPv6 = $ipv6 Gateway = $gw Dns = $dns }) | Out-Null } } catch { Get-CimInstance Win32_NetworkAdapterConfiguration -Filter 'IPEnabled=TRUE' | ForEach-Object { $rows.Add([pscustomobject]@{ Adapter = $_.Description Mac = $_.MACAddress IPv4 = @($_.IPAddress | Where-Object { $_ -match '^\d+\.' }) -join ', ' IPv6 = @($_.IPAddress | Where-Object { $_ -match ':' }) -join ', ' Gateway = @($_.DefaultIPGateway) -join ', ' Dns = @($_.DNSServerSearchOrder) -join ', ' }) | Out-Null } } $ssid = '' try { $wlan = netsh wlan show interfaces 2>$null | Out-String if ($wlan -match '(?m)^\s+SSID\s+:\s+(.+)$') { $ssid = $Matches[1].Trim() } } catch { } [pscustomobject]@{ Adapters = $rows Ssid = $ssid } } function Get-JdsDisks { $vols = @(Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ErrorAction SilentlyContinue) $volumes = foreach ($v in $vols) { $size = [math]::Round($v.Size / 1GB, 1) $free = [math]::Round($v.FreeSpace / 1GB, 1) $pct = if ($v.Size -gt 0) { [math]::Round(100 * $v.FreeSpace / $v.Size, 0) } else { 0 } [pscustomobject]@{ DeviceId = $v.DeviceID Label = $v.VolumeName FileSystem = $v.FileSystem SizeGB = $size FreeGB = $free FreePct = $pct } } $bitlocker = @() try { $bitlocker = @(Get-BitLockerVolume -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ Mount = $_.MountPoint Protect = [string]$_.ProtectionStatus Volume = [string]$_.VolumeStatus Method = (@($_.KeyProtector | ForEach-Object { $_.KeyProtectorType }) -join ', ') EncPct = $_.EncryptionPercentage } }) } catch { } $physical = @() try { $physical = @(Get-PhysicalDisk -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ Friendly = $_.FriendlyName Media = [string]$_.MediaType Health = [string]$_.HealthStatus SizeGB = [math]::Round($_.Size / 1GB, 0) Bus = [string]$_.BusType } }) } catch { } [pscustomobject]@{ Volumes = $volumes BitLocker = $bitlocker Physical = $physical } } function Get-JdsUsers { $local = @() try { $local = @(Get-LocalUser | ForEach-Object { [pscustomobject]@{ Name = $_.Name Enabled = $_.Enabled Description = $_.Description LastLogon = $_.LastLogon PasswordRequired = $_.PasswordRequired } }) } catch { } $admins = @() try { $admins = @(Get-LocalGroupMember -Group 'Administrateurs' -ErrorAction Stop | ForEach-Object { $_.Name }) } catch { try { $admins = @(Get-LocalGroupMember -Group 'Administrators' -ErrorAction Stop | ForEach-Object { $_.Name }) } catch { } } $cs = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue [pscustomobject]@{ Local = $local Admins = $admins CurrentUser = "$env:USERDOMAIN\$env:USERNAME" LoggedOn = $cs.UserName Domain = $cs.Domain PartOfDomain = [bool]$cs.PartOfDomain Workgroup = $cs.Workgroup } } function Get-JdsUpdates { $os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue $hotfixes = @() try { $hotfixes = @(Get-HotFix -ErrorAction Stop | Sort-Object InstalledOn -Descending | Select-Object -First 20 | ForEach-Object { [pscustomobject]@{ Id = $_.HotFixID Description = $_.Description InstalledOn = $_.InstalledOn By = $_.InstalledBy } }) } catch { } [pscustomobject]@{ LastBoot = $os.LastBootUpTime Hotfixes = $hotfixes } } # --------------------------------------------------------------------------- # Formatage texte # --------------------------------------------------------------------------- function Format-JdsWindowsText($os, $lic) { $role = if ($os.PartOfDomain) { "Domaine : $($os.Domain)" } else { "Groupe de travail : $($os.Domain)" } $grace = if ($lic.RemainingGrace) { $lic.RemainingGrace } else { '—' } $channel = if ($lic.Channel) { $lic.Channel } else { '—' } $key = if ($lic.PartialKey) { $lic.PartialKey } else { '—' } @" Ordinateur : $($os.ComputerName) Utilisateur : $($os.User) $role Fabricant / modèle: $($os.Manufacturer) / $($os.Model) Système : $($os.Caption) Édition : $($os.EditionId) Version : $($os.DisplayVersion) Build : $($os.Build) Architecture : $($os.Arch) Type d'install. : $($os.InstallType) Installé le : $($os.InstallDate) Dernier boot : $($os.LastBoot) Uptime : $($os.Uptime) --- Licence Windows --- Produit : $($lic.Name) Statut : $($lic.Status) Canal (clé) : $channel Description : $($lic.Description) Clé (5 derniers) : $key Grace restante : $grace "@ } function Format-JdsOfficeText($off) { if (-not $off.Found) { return "Aucun Microsoft Office / Microsoft 365 détecté (Click-to-Run, licence WMI ou ospp.vbs)." } $lines = New-Object System.Collections.Generic.List[string] if ($off.Version) { $lines.Add("Click-to-Run") $lines.Add("Version : $($off.Version)") $lines.Add("Produits : $($off.ProductIds)") $lines.Add("Canal de MAJ : $($off.Channel)") $lines.Add("Plateforme : $($off.Platform)") $lines.Add("Dossier client : $($off.ClientFolder)") $lines.Add('') } $lines.Add('--- Licences (SoftwareLicensingProduct) ---') if ($off.StatusLines.Count -gt 0) { foreach ($s in $off.StatusLines) { $lines.Add($s) } } else { $lines.Add('Aucune licence Office avec clé partielle via WMI.') } if ($off.OsppText) { $lines.Add('') $lines.Add("--- ospp.vbs /dstatus ---") $lines.Add($off.OsppText.Trim()) } $lines -join "`r`n" } function Format-JdsHardwareText($hw) { $gpus = if ($hw.Gpus.Count) { $hw.Gpus -join ', ' } else { '—' } $sticks = if ($hw.RamSticks.Count) { $hw.RamSticks -join "`r`n " } else { '—' } @" Processeur : $($hw.CpuName) Cœurs / logiques : $($hw.CpuCores) / $($hw.CpuLogical) Fréquence max : $($hw.CpuMaxMHz) MHz Mémoire : $($hw.RamGB) Go Barrettes : $sticks Carte mère / PC : $($hw.Manufacturer) Modèle : $($hw.Model) SKU : $($hw.SystemSku) Produit SMBIOS : $($hw.Vendor) $($hw.ProductName) N° de série : $($hw.Serial) UUID : $($hw.Uuid) BIOS : $($hw.BiosVersion) Série BIOS : $($hw.BiosSerial) GPU : $gpus "@ } function Format-JdsNetworkText($net) { $sb = New-Object System.Text.StringBuilder if ($net.Ssid) { [void]$sb.AppendLine("Wi-Fi SSID : $($net.Ssid)") ; [void]$sb.AppendLine('') } foreach ($a in $net.Adapters) { [void]$sb.AppendLine("Adaptateur : $($a.Adapter)") [void]$sb.AppendLine("MAC : $($a.Mac)") [void]$sb.AppendLine("IPv4 : $($a.IPv4)") [void]$sb.AppendLine("IPv6 : $($a.IPv6)") [void]$sb.AppendLine("Passerelle : $($a.Gateway)") [void]$sb.AppendLine("DNS : $($a.Dns)") [void]$sb.AppendLine('') } if ($net.Adapters.Count -eq 0) { [void]$sb.AppendLine('Aucun adaptateur actif.') } $sb.ToString() } function Format-JdsDisksText($d) { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('--- Volumes ---') foreach ($v in $d.Volumes) { $label = if ($v.Label) { $v.Label } else { '(sans nom)' } [void]$sb.AppendLine(('{0} {1} {2} {3} Go libres / {4} Go ({5}% libre)' -f $v.DeviceId, $label, $v.FileSystem, $v.FreeGB, $v.SizeGB, $v.FreePct)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('--- Disques physiques ---') if ($d.Physical.Count) { foreach ($p in $d.Physical) { [void]$sb.AppendLine(('{0} {1} {2} {3} Go {4}' -f $p.Friendly, $p.Media, $p.Health, $p.SizeGB, $p.Bus)) } } else { [void]$sb.AppendLine('Non disponible') } [void]$sb.AppendLine('') [void]$sb.AppendLine('--- BitLocker ---') if ($d.BitLocker.Count) { foreach ($b in $d.BitLocker) { [void]$sb.AppendLine(('{0} protection={1} volume={2} {3}% protecteurs={4}' -f $b.Mount, $b.Protect, $b.Volume, $b.EncPct, $b.Method)) } } else { [void]$sb.AppendLine('BitLocker non disponible ou aucun volume chiffré / module absent.') } $sb.ToString() } function Format-JdsUsersText($u) { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine("Session courante : $($u.CurrentUser)") [void]$sb.AppendLine("Utilisateur WMI : $($u.LoggedOn)") if ($u.PartOfDomain) { [void]$sb.AppendLine("Domaine : $($u.Domain)") } else { [void]$sb.AppendLine("Groupe de travail : $($u.Domain)") } [void]$sb.AppendLine('') [void]$sb.AppendLine('--- Administrateurs locaux ---') if ($u.Admins.Count) { foreach ($a in $u.Admins) { [void]$sb.AppendLine(" $a") } } else { [void]$sb.AppendLine(' (liste indisponible)') } [void]$sb.AppendLine('') [void]$sb.AppendLine('--- Comptes locaux ---') foreach ($lu in $u.Local) { $en = if ($lu.Enabled) { 'actif' } else { 'désactivé' } [void]$sb.AppendLine(('{0} [{1}] last={2} {3}' -f $lu.Name, $en, $lu.LastLogon, $lu.Description)) } $sb.ToString() } function Format-JdsUpdatesText($up) { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine("Dernier démarrage : $($up.LastBoot)") [void]$sb.AppendLine('') [void]$sb.AppendLine('--- Correctifs récents (Get-HotFix, 20 max) ---') if ($up.Hotfixes.Count) { foreach ($h in $up.Hotfixes) { [void]$sb.AppendLine(('{0} {1} {2} {3}' -f $h.Id, $h.InstalledOn, $h.Description, $h.By)) } } else { [void]$sb.AppendLine('Aucun correctif listé (politique, ou Get-HotFix indisponible).') } $sb.ToString() } # --------------------------------------------------------------------------- # Actions # --------------------------------------------------------------------------- function Invoke-JdsSlmgr { param([string[]]$SlmgrArgs) $slmgr = Join-Path $env:SystemRoot 'System32\slmgr.vbs' $out = & cscript.exe //Nologo $slmgr @SlmgrArgs 2>&1 | Out-String return $out.Trim() } function Clear-JdsTemp { $targets = @($env:TEMP, $env:TMP, (Join-Path $env:SystemRoot 'Temp')) | Select-Object -Unique $removed = 0 foreach ($dir in $targets) { if (-not (Test-Path $dir)) { continue } Get-ChildItem -LiteralPath $dir -Force -ErrorAction SilentlyContinue | ForEach-Object { try { Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction Stop $removed++ } catch { } } } return $removed } function Start-JdsUninstall { param($App) if (-not $App) { return 'Aucune application sélectionnée.' } $line = $App.QuietUninstallString $silent = $true if (-not $line) { $line = $App.UninstallString $silent = $false } if (-not $line) { return "Pas de chaîne de désinstallation pour $($App.Name)." } $exe = $null $arg = '' if ($line -match '^"([^"]+)"\s*(.*)$') { $exe = $Matches[1] $arg = $Matches[2] } elseif ($line -match '^(\S+)\s*(.*)$') { $exe = $Matches[1] $arg = $Matches[2] } else { $exe = $line } if ($exe -match '(?i)msiexec(\.exe)?') { if ($arg -notmatch '/qn' -and $arg -notmatch '/quiet') { $arg = ($arg -replace '(?i)/i\b', '/x') if ($arg -notmatch '(?i)/x') { $arg = '/x ' + $arg } $arg = "$arg /qn" } $silent = $true } try { if ($arg) { Start-Process -FilePath $exe -ArgumentList $arg -Wait -ErrorAction Stop } else { Start-Process -FilePath $exe -Wait -ErrorAction Stop } if ($silent) { return "Désinstallation lancée (silencieuse si supportée) : $($App.Name)" } return "Désinstalleur ouvert : $($App.Name)" } catch { return "Échec désinstallation $($App.Name) : $($_.Exception.Message)" } } function Confirm-Jds([string]$Message, [string]$Title) { return ([Windows.MessageBox]::Show($Message, $Title, 'YesNo', 'Question') -eq 'Yes') } function Start-JdsConsoleCmd([string]$Command, [string]$Log) { Start-Process -FilePath 'cmd.exe' -ArgumentList @('/k', $Command) | Out-Null Write-JdsToolLog $Log } function Get-JdsEventsSafe { param( [string]$LogName, [int[]]$Id, [int]$Max = 40, [int]$Days = 30, [string]$Provider ) try { $f = @{ LogName = $LogName; StartTime = (Get-Date).AddDays(-$Days) } if ($Id) { $f.Id = $Id } if ($Provider) { $f.ProviderName = $Provider } return @(Get-WinEvent -FilterHashtable $f -MaxEvents $Max -ErrorAction Stop) } catch { return @() } } function Get-JdsBugCheckCatalog { if ($script:JdsBugCatalog) { return $script:JdsBugCatalog } $script:JdsBugCatalog = @{ '0x0000000A' = @{ Name = 'IRQL_NOT_LESS_OR_EQUAL'; Tips = "Pilote (antivirus, VPN, Wi-Fi, GPU). Tester la RAM. SFC + DISM. MAJ pilotes." } '0x0000001A' = @{ Name = 'MEMORY_MANAGEMENT'; Tips = "RAM défectueuse (mdsched). XMP trop agressif. SFC. Vérifier le disque (inpage)." } '0x0000001E' = @{ Name = 'KMODE_EXCEPTION_NOT_HANDLED'; Tips = "Pilote / antivirus. SFC + DISM. Désinstaller le dernier logiciel/pilote." } '0x00000024' = @{ Name = 'NTFS_FILE_SYSTEM'; Tips = "chkdsk /f /r. Santé SMART. Câbles SATA / NVMe. Sauvegarder." } '0x0000003B' = @{ Name = 'SYSTEM_SERVICE_EXCEPTION'; Tips = "Pilote GPU/stockage. SFC + DISM. MAJ Windows. Tester RAM." } '0x00000050' = @{ Name = 'PAGE_FAULT_IN_NONPAGED_AREA'; Tips = "RAM, disque, antivirus. chkdsk. mdsched. Pilote récemment installé." } '0x0000007A' = @{ Name = 'KERNEL_DATA_INPAGE_ERROR'; Tips = "Disque / contrôleur. chkdsk /f /r. SMART. RAM. Antivirus au boot." } '0x0000007E' = @{ Name = 'SYSTEM_THREAD_EXCEPTION_NOT_HANDLED'; Tips = "Pilote (souvent GPU). Rollback pilote. SFC + DISM." } '0x0000009F' = @{ Name = 'DRIVER_POWER_STATE_FAILURE'; Tips = "Veille / USB / GPU. Désactiver démarrage rapide. MAJ pilotes d’alimentation." } '0x000000C2' = @{ Name = 'BAD_POOL_CALLER'; Tips = "Pilote bugué. Antivirus. Driver Verifier seulement en atelier." } '0x000000D1' = @{ Name = 'DRIVER_IRQL_NOT_LESS_OR_EQUAL'; Tips = "Pilote réseau/stockage. MAJ ou rollback. SFC." } '0x000000EF' = @{ Name = 'CRITICAL_PROCESS_DIED'; Tips = "SFC + DISM (critique). Disque. Antivirus. Réparer l’image Windows." } '0x000000F4' = @{ Name = 'CRITICAL_OBJECT_TERMINATION'; Tips = "Comme CRITICAL_PROCESS_DIED : SFC, DISM, disque, malware scan." } '0x00000101' = @{ Name = 'CLOCK_WATCHDOG_TIMEOUT'; Tips = "CPU (boost, alim, surchauffe). BIOS. Désactiver overclock. RAM." } '0x00000109' = @{ Name = 'CRITICAL_STRUCTURE_CORRUPTION'; Tips = "RAM / overclock. SFC. BIOS. Parfois rootkit — scan hors ligne." } '0x00000116' = @{ Name = 'VIDEO_TDR_FAILURE'; Tips = "GPU : pilote WHQL, TDR, alim GPU, surchauffe. Rollback NVIDIA/AMD." } '0x00000119' = @{ Name = 'VIDEO_TDR_TIMEOUT_DETECTED'; Tips = "Identique TDR : GPU/pilote/écran. Désactiver overclock GPU." } '0x00000124' = @{ Name = 'WHEA_UNCORRECTABLE_ERROR'; Tips = "Matériel : CPU, RAM, alim, surchauffe. BIOS. Memtest. WHEA-Logger." } '0x00000133' = @{ Name = 'DPC_WATCHDOG_VIOLATION'; Tips = "Stockage NVMe/SATA, USB. MAJ pilotes chipset. Fast startup OFF." } '0x00000139' = @{ Name = 'KERNEL_SECURITY_CHECK_FAILURE'; Tips = "RAM, SFC, DISM, antivirus. Pilote incompatible." } '0x00000154' = @{ Name = 'UNEXPECTED_STORE_EXCEPTION'; Tips = "Disque système / Storage Spaces. chkdsk. SMART. SFC." } } $script:JdsBugCatalog } function ConvertTo-JdsBugKey([string]$Hex) { if (-not $Hex) { return '' } try { return ('0x{0:X8}' -f [uint32]$Hex) } catch { return $Hex.ToUpperInvariant() } } function Get-JdsBugHelp([string]$Hex) { $key = ConvertTo-JdsBugKey $Hex $cat = Get-JdsBugCheckCatalog $hit = $cat[$key] if ($hit) { return "Stop $key — $($hit.Name)`r`n`r`n$($hit.Tips)`r`n`r`nActions JDS : SFC, DISM, chkdsk, mdsched, désactiver démarrage rapide, MAJ pilotes." } if ($key) { return "Stop $key (non catalogué).`r`nPistes génériques : SFC + DISM, chkdsk, mdsched, Minidump, MAJ Windows/pilotes, désactiver le dernier matériel." } return 'Sélectionne un BSOD ou lance Actualiser.' } function Get-JdsHealthHints { $hints = New-Object System.Collections.Generic.List[string] $os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue $sys = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" -ErrorAction SilentlyContinue if ($sys -and $sys.Size -gt 0) { $pct = [math]::Round(100 * $sys.FreeSpace / $sys.Size, 0) if ($pct -lt 10) { $hints.Add("[CRITIQUE] C: n'a plus que $pct % libres. Libérer de l'espace avant SFC/DISM/MAJ.") } elseif ($pct -lt 20) { $hints.Add("[ATTENTION] C: : $pct % libres.") } else { $hints.Add("[OK] Espace C: : $pct % libres.") } } $bc = Get-JdsEventsSafe -LogName System -Id 1001 -Max 5 -Days 90 if ($bc.Count) { $hints.Add("[CRITIQUE] $($bc.Count)+ BugCheck (BSOD) récents. Voir Incidents.") } else { $hints.Add('[OK] Pas de BugCheck (Event 1001) sur 90 j.') } $k41 = Get-JdsEventsSafe -LogName System -Id 41 -Max 5 -Days 30 if ($k41.Count) { $hints.Add("[ATTENTION] $($k41.Count)+ extinctions inattendues (Kernel-Power 41). Alim / crash / hold power.") } $dumps = @() if (Test-Path 'C:\Windows\Minidump') { $dumps = @(Get-ChildItem 'C:\Windows\Minidump\*.dmp' -ErrorAction SilentlyContinue) } if ($dumps.Count) { $hints.Add("[ATTENTION] $($dumps.Count) minidump(s) dans C:\Windows\Minidump.") } Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3' -ErrorAction SilentlyContinue | ForEach-Object { $letter = $_.DeviceID.TrimEnd('\') $q = cmd /c "fsutil dirty query $letter" 2>$null | Out-String if ($q -match 'is dirty' -or $q -match 'est sale') { $hints.Add("[CRITIQUE] Volume $letter dirty — chkdsk /f au reboot.") } } try { Get-PhysicalDisk -ErrorAction Stop | Where-Object { $_.HealthStatus -and $_.HealthStatus -ne 'Healthy' } | ForEach-Object { $hints.Add("[CRITIQUE] Disque $($_.FriendlyName) santé=$($_.HealthStatus).") } } catch { } try { $badPnp = @(Get-PnpDevice -PresentOnly -ErrorAction Stop | Where-Object { $_.Status -eq 'Error' }) if ($badPnp.Count) { $hints.Add("[ATTENTION] $($badPnp.Count) périphérique(s) en erreur (Gestionnaire de périphériques).") } } catch { } $pending = $false foreach ($p in @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' )) { if (Test-Path $p) { $pending = $true } } try { $sm = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Name PendingFileRenameOperations -ErrorAction SilentlyContinue if ($sm.PendingFileRenameOperations) { $pending = $true } } catch { } if ($pending) { $hints.Add('[ATTENTION] Redémarrage en attente (MAJ / fichiers).') } else { $hints.Add('[OK] Pas de reboot pending détecté.') } $critSvc = @('wuauserv','bits','cryptsvc','TrustedInstaller','spooler','Dhcp','Dnscache','WSearch') foreach ($n in $critSvc) { $s = Get-Service -Name $n -ErrorAction SilentlyContinue if ($s -and $s.Status -ne 'Running' -and $s.StartType -eq 'Automatic') { $hints.Add("[ATTENTION] Service $n = $($s.Status) (Automatic).") } } $wuFail = Get-JdsEventsSafe -LogName 'Microsoft-Windows-WindowsUpdateClient/Operational' -Id 20,24,25,31,34 -Max 8 -Days 14 if ($wuFail.Count) { $hints.Add("[ATTENTION] Échecs Windows Update récents ($($wuFail.Count)). Reset WU / DISM.") } $mem = Get-JdsEventsSafe -LogName System -Id 1201,1202 -Max 3 -Days 365 -Provider 'Microsoft-Windows-MemoryDiagnostics-Results' if (-not $mem.Count) { $mem = Get-JdsEventsSafe -LogName System -Max 5 -Days 365 -Provider 'Microsoft-Windows-MemoryDiagnostics-Results' } foreach ($e in $mem) { if ($e.Message -match 'fail|échou|error|erreur') { $hints.Add('[CRITIQUE] Diagnostic mémoire Windows : échec. Remplacer/tester la RAM.') } elseif ($e.Message) { $hints.Add('[OK] Dernier diagnostic mémoire : ' + (($e.Message -split "`n")[0])) } } $hb = 0 try { $hb = [int](Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -Name HiberbootEnabled -ErrorAction SilentlyContinue).HiberbootEnabled } catch { } if ($hb -eq 1 -and ($bc.Count -gt 0 -or $k41.Count -gt 0)) { $hints.Add('[ATTENTION] Démarrage rapide activé + incidents récents → le désactiver (Réparer).') } if ($hints.Count -eq 0) { $hints.Add('[OK] Rien de flagrant. Tu peux quand même lancer SFC en préventif.') } return ($hints -join "`r`n") } function Get-JdsIncidentsText { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('=== BSOD (System 1001) ===') $bcs = Get-JdsEventsSafe -LogName System -Id 1001 -Max 25 -Days 180 if ($bcs.Count -eq 0) { [void]$sb.AppendLine('(aucun)') } foreach ($e in $bcs) { $hex = '' if ($e.Message -match '0x[0-9A-Fa-f]+') { $hex = $Matches[0] } $key = ConvertTo-JdsBugKey $hex $name = '' $hit = (Get-JdsBugCheckCatalog)[$key] if ($hit) { $name = $hit.Name } [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1} {2}' -f $e.TimeCreated, $key, $name)) $line = (($e.Message -replace '\s+', ' ').Trim()) if ($line.Length -gt 220) { $line = $line.Substring(0, 220) + '…' } [void]$sb.AppendLine(' ' + $line) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Minidumps ===') if (Test-Path 'C:\Windows\Minidump') { $dmp = @(Get-ChildItem 'C:\Windows\Minidump\*.dmp' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending) if ($dmp.Count -eq 0) { [void]$sb.AppendLine('(aucun fichier)') } foreach ($f in $dmp) { [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1:N1} Ko {2}' -f $f.LastWriteTime, ($f.Length / 1KB), $f.Name)) } } else { [void]$sb.AppendLine('(dossier Minidump absent)') } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Kernel-Power 41 (extinction inattendue) ===') $k = Get-JdsEventsSafe -LogName System -Id 41 -Max 20 -Days 90 if ($k.Count -eq 0) { [void]$sb.AppendLine('(aucun)') } foreach ($e in $k) { $m = (($e.Message -replace '\s+', ' ').Trim()) if ($m.Length -gt 160) { $m = $m.Substring(0, 160) + '…' } [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1}' -f $e.TimeCreated, $m)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Plantages applications (Application 1000) ===') $apps = Get-JdsEventsSafe -LogName Application -Id 1000 -Max 20 -Days 30 if ($apps.Count -eq 0) { [void]$sb.AppendLine('(aucun)') } foreach ($e in $apps) { $m = (($e.Message -replace '\s+', ' ').Trim()) if ($m.Length -gt 200) { $m = $m.Substring(0, 200) + '…' } [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1}' -f $e.TimeCreated, $m)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Reliability (si WMI dispo) ===') try { $rel = @(Get-CimInstance -Namespace 'root/cimv2' -ClassName Win32_ReliabilityRecords -ErrorAction Stop | Select-Object -First 15) if ($rel.Count -eq 0) { [void]$sb.AppendLine('(vide)') } foreach ($r in $rel) { [void]$sb.AppendLine(('{0} {1} {2}' -f $r.TimeGenerated, $r.EventIdentifier, $r.ProductName)) } } catch { [void]$sb.AppendLine('(classe Reliability absente sur ce PC)') } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Diagnostic mémoire ===') $mem = Get-JdsEventsSafe -LogName System -Max 8 -Days 730 -Provider 'Microsoft-Windows-MemoryDiagnostics-Results' if ($mem.Count -eq 0) { [void]$sb.AppendLine('(aucun résultat mdsched)') } foreach ($e in $mem) { [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1}' -f $e.TimeCreated, (($e.Message -replace '\s+', ' ').Trim()))) } $sb.ToString() } function Get-JdsDiskToolsText { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('=== Volumes + dirty ===') Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3' -ErrorAction SilentlyContinue | ForEach-Object { $letter = $_.DeviceID.TrimEnd('\') $free = [math]::Round($_.FreeSpace / 1GB, 1) $size = [math]::Round($_.Size / 1GB, 1) $q = cmd /c "fsutil dirty query $letter" 2>$null | Out-String $dirty = 'clean' if ($q -match 'is dirty' -or $q -match 'est sale') { $dirty = 'DIRTY' } [void]$sb.AppendLine(('{0} {1} {2}/{3} Go libres {4}' -f $letter, $_.FileSystem, $free, $size, $dirty)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== SMART / PhysicalDisk ===') try { Get-PhysicalDisk -ErrorAction Stop | ForEach-Object { [void]$sb.AppendLine(('{0} {1} santé={2} {3} Go {4}' -f $_.FriendlyName, $_.MediaType, $_.HealthStatus, [math]::Round($_.Size / 1GB, 0), $_.BusType)) } Get-PhysicalDisk -ErrorAction SilentlyContinue | ForEach-Object { try { $c = $_ | Get-StorageReliabilityCounter -ErrorAction Stop [void]$sb.AppendLine((' temp={0}°C readErr={1} writeErr={2} wear={3}' -f $c.Temperature, $c.ReadErrorsTotal, $c.WriteErrorsTotal, $c.Wear)) } catch { } } } catch { [void]$sb.AppendLine($_.Exception.Message) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Journal disque (7, 51, 55, 153) ===') $de = Get-JdsEventsSafe -LogName System -Id 7,51,55,153 -Max 20 -Days 60 if ($de.Count -eq 0) { [void]$sb.AppendLine('(aucune)') } foreach ($e in $de) { $m = (($e.Message -replace '\s+', ' ').Trim()) if ($m.Length -gt 180) { $m = $m.Substring(0, 180) + '…' } [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} id={1} {2}' -f $e.TimeCreated, $e.Id, $m)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Espaces Windows (approx., sans WinSxS) ===') foreach ($p in @("$env:SystemRoot\Temp", "$env:TEMP", "$env:SystemRoot\Logs\CBS")) { if (Test-Path $p) { $bytes = (Get-ChildItem $p -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum [void]$sb.AppendLine(('{0:N1} Mo {1}' -f ($bytes / 1MB), $p)) } } [void]$sb.AppendLine('WinSxS : trop lourd à mesurer ici — utiliser DISM StartComponentCleanup.') [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Batterie ===') $bat = @(Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue) if ($bat.Count -eq 0) { [void]$sb.AppendLine('(pas de batterie WMI — PC fixe ?)') } foreach ($b in $bat) { [void]$sb.AppendLine(('{0} charge={1}% statut={2}' -f $b.Name, $b.EstimatedChargeRemaining, $b.BatteryStatus)) } $sb.ToString() } function Get-JdsDevicesText { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('=== Périphériques en erreur ===') try { $bad = @(Get-PnpDevice -ErrorAction Stop | Where-Object { $_.Status -eq 'Error' -or ($_.ConfigManagerErrorCode -and $_.ConfigManagerErrorCode -ne 0) }) if ($bad.Count -eq 0) { [void]$sb.AppendLine('(aucun)') } foreach ($d in $bad) { [void]$sb.AppendLine(('{0} [{1}] {2} code={3}' -f $d.FriendlyName, $d.Class, $d.Status, $d.ConfigManagerErrorCode)) } } catch { [void]$sb.AppendLine($_.Exception.Message) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Pilotes (40 plus récents) ===') try { $dr = @(Get-CimInstance Win32_PnPSignedDriver -ErrorAction Stop | Sort-Object DriverDate -Descending | Select-Object -First 40) foreach ($d in $dr) { $sig = if ($d.IsSigned) { 'signé' } else { 'NON SIGNÉ' } [void]$sb.AppendLine(('{0:yyyy-MM-dd} {1} {2} {3} {4}' -f $d.DriverDate, $d.DeviceName, $d.DriverVersion, $sig, $d.Manufacturer)) } } catch { [void]$sb.AppendLine($_.Exception.Message) } $sb.ToString() } function Get-JdsSystemToolsText { $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('=== Démarrage rapide / hibernation ===') try { $hb = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -Name HiberbootEnabled -ErrorAction SilentlyContinue).HiberbootEnabled [void]$sb.AppendLine("HiberbootEnabled = $hb (1 = démarrage rapide ON)") } catch { } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Secure Boot / TPM ===') try { [void]$sb.AppendLine('Secure Boot = ' + (Confirm-SecureBootUEFI)) } catch { [void]$sb.AppendLine('Secure Boot : ' + $_.Exception.Message) } try { $tpm = Get-Tpm -ErrorAction Stop [void]$sb.AppendLine(('TPM Present={0} Ready={1} Enabled={2}' -f $tpm.TpmPresent, $tpm.TpmReady, $tpm.TpmEnabled)) } catch { [void]$sb.AppendLine('TPM : ' + $_.Exception.Message) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Pare-feu ===') try { Get-NetFirewallProfile -ErrorAction Stop | ForEach-Object { [void]$sb.AppendLine(('{0} enabled={1} inbound={2} outbound={3}' -f $_.Name, $_.Enabled, $_.DefaultInboundAction, $_.DefaultOutboundAction)) } } catch { [void]$sb.AppendLine($_.Exception.Message) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Proxy WinHTTP ===') [void]$sb.AppendLine((netsh winhttp show proxy 2>&1 | Out-String).Trim()) try { $inet = Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -ErrorAction SilentlyContinue [void]$sb.AppendLine(('WinInet ProxyEnable={0} ProxyServer={1}' -f $inet.ProxyEnable, $inet.ProxyServer)) } catch { } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Programmes au démarrage (Run) ===') foreach ($rp in @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' )) { if (-not (Test-Path $rp)) { continue } [void]$sb.AppendLine("-- $rp") Get-ItemProperty $rp -ErrorAction SilentlyContinue | ForEach-Object { $_.PSObject.Properties | Where-Object { $_.Name -notmatch '^PS' } | ForEach-Object { [void]$sb.AppendLine((' {0} = {1}' -f $_.Name, $_.Value)) } } } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Points de restauration ===') try { Get-ComputerRestorePoint -ErrorAction Stop | Select-Object -Last 12 | ForEach-Object { [void]$sb.AppendLine(('{0} {1} {2}' -f $_.CreationTime, $_.SequenceNumber, $_.Description)) } } catch { [void]$sb.AppendLine($_.Exception.Message) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Services auto arrêtés ===') Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running' } | Select-Object -First 30 | ForEach-Object { [void]$sb.AppendLine(('{0} {1} {2}' -f $_.Status, $_.Name, $_.DisplayName)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Tâches planifiées en échec ===') try { Get-ScheduledTask -ErrorAction Stop | ForEach-Object { $i = $_ | Get-ScheduledTaskInfo -ErrorAction SilentlyContinue if ($i -and $null -ne $i.LastTaskResult -and $i.LastTaskResult -ne 0 -and $i.LastTaskResult -ne 267009) { [void]$sb.AppendLine(('{0} last={1} {2}' -f $i.LastTaskResult, $i.LastRunTime, $_.TaskName)) } } } catch { [void]$sb.AppendLine($_.Exception.Message) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Windows Update (échecs opérationnels) ===') $wu = Get-JdsEventsSafe -LogName 'Microsoft-Windows-WindowsUpdateClient/Operational' -Max 15 -Days 21 $wu = @($wu | Where-Object { $_.LevelDisplayName -match 'Error|Erreur|Critique|Critical' -or $_.Id -in 20,24,25,31,34 }) if ($wu.Count -eq 0) { [void]$sb.AppendLine('(pas d''échec listé)') } foreach ($e in $wu | Select-Object -First 15) { $m = (($e.Message -replace '\s+', ' ').Trim()) if ($m.Length -gt 180) { $m = $m.Substring(0, 180) + '…' } [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1}' -f $e.TimeCreated, $m)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== Event Viewer erreurs/critiques 7 j (System+App, 40 max) ===') $ev = @() try { $ev += @(Get-WinEvent -FilterHashtable @{ LogName = 'System'; Level = 1,2; StartTime = (Get-Date).AddDays(-7) } -MaxEvents 20 -ErrorAction Stop) } catch { } try { $ev += @(Get-WinEvent -FilterHashtable @{ LogName = 'Application'; Level = 1,2; StartTime = (Get-Date).AddDays(-7) } -MaxEvents 20 -ErrorAction Stop) } catch { } foreach ($e in $ev | Sort-Object TimeCreated -Descending | Select-Object -First 40) { $m = (($e.Message -replace '\s+', ' ').Trim()) if ($m.Length -gt 140) { $m = $m.Substring(0, 140) + '…' } [void]$sb.AppendLine(('{0:yyyy-MM-dd HH:mm} {1}/{2} {3}' -f $e.TimeCreated, $e.LogName, $e.Id, $m)) } [void]$sb.AppendLine('') [void]$sb.AppendLine('=== GPO (gpresult /R, extrait) ===') try { $gpr = gpresult /R 2>&1 | Out-String $lines = $gpr -split "`r?`n" $take = $lines | Select-Object -First 60 [void]$sb.AppendLine(($take -join "`r`n")) } catch { [void]$sb.AppendLine($_.Exception.Message) } $sb.ToString() } function Invoke-JdsWuReset { $names = @('wuauserv', 'bits', 'cryptsvc', 'msiserver') foreach ($n in $names) { Stop-Service -Name $n -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 2 $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' foreach ($pair in @( @{ Path = "$env:SystemRoot\SoftwareDistribution"; Name = "SoftwareDistribution.bak-$stamp" }, @{ Path = "$env:SystemRoot\System32\catroot2"; Name = "catroot2.bak-$stamp" } )) { if (Test-Path $pair.Path) { Rename-Item -LiteralPath $pair.Path -NewName $pair.Name -ErrorAction SilentlyContinue } } foreach ($n in $names) { Start-Service -Name $n -ErrorAction SilentlyContinue } return 'Windows Update : caches renommés, services relancés. Relance une recherche de MAJ.' } function Clear-JdsPrefetch { $n = 0 Get-ChildItem -LiteralPath "$env:SystemRoot\Prefetch" -Force -ErrorAction SilentlyContinue | ForEach-Object { try { Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop; $n++ } catch { } } return $n } function Reset-JdsSpooler { Stop-Service -Name spooler -Force -ErrorAction SilentlyContinue Get-ChildItem -LiteralPath "$env:SystemRoot\System32\spool\PRINTERS" -Force -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue } Start-Service -Name spooler -ErrorAction SilentlyContinue return 'Spooler réinitialisé (file d''attente vidée).' } function Repair-JdsStartMenu { $pkgs = Get-AppxPackage -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'ShellExperienceHost|StartMenuExperienceHost' } foreach ($p in $pkgs) { $man = Join-Path $p.InstallLocation 'AppXManifest.xml' if (Test-Path $man) { Add-AppxPackage -DisableDevelopmentMode -Register $man -ErrorAction SilentlyContinue | Out-Null } } return 'Re-register Start/ShellExperienceHost lancé.' } function Get-JdsHtmlReport($Data) { function Enc([string]$s) { [System.Net.WebUtility]::HtmlEncode($s) } function Pre($text) { '
' + (Enc ([string]$text)) + '' } $os = $Data.Os $lic = $Data.License $winOk = if ($lic.Licensed) { 'ok' } else { 'ko' } $offOk = if ($Data.Office.Licensed) { 'ok' } else { 'ko' } $when = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $appRows = ($Data.Apps | ForEach-Object { '
| Nom | Version | Éditeur | Installé | Portée |
|---|