<# .SYNOPSIS inventaire_windows.ps1 — Agent Vigie (FactualRisk) — inventaire en LECTURE SEULE. N'installe rien, ne modifie rien, n'envoie rien sur le réseau. Relisez le fichier avant envoi. .EXAMPLE powershell -ExecutionPolicy Bypass -File .\inventaire_windows.ps1 powershell -ExecutionPolicy Bypass -File .\inventaire_windows.ps1 -Hote mail.votre-domaine.fr -Tout .NOTES Compatible Windows PowerShell 5.1 et PowerShell 7. Plus complet en administrateur. Colonnes : Editeur;Produit;Version;Hôte;Exposé;Origine;Paquet;Cpe #> param( [string]$Sortie = "", [string]$Hote = "", [switch]$Tout ) $ErrorActionPreference = 'SilentlyContinue' if (-not $Sortie) { $Sortie = "parc-$($env:COMPUTERNAME).csv" } $rows = New-Object System.Collections.Generic.List[object] $seen = @{} function Add-Row([string]$ed, [string]$pr, [string]$ver, [string]$h, [string]$exp, [string]$orig, [string]$cpe) { if (-not $pr -or -not $ver) { return } $k = ("{0}|{1}|{2}" -f $ed, $pr, $ver).ToLower() if ($seen.ContainsKey($k)) { return } $seen[$k] = $true $rows.Add([pscustomobject][ordered]@{ 'Editeur' = $ed.Trim(); 'Produit' = $pr.Trim(); 'Version' = $ver.Trim(); 'Hôte' = $h 'Exposé' = $exp; 'Origine' = $orig; 'Paquet' = ''; 'Cpe' = $cpe }) } # ---------------------------------------------------------------- écoutes -- # Port → « écoute » si une adresse non locale écoute, « non » si seulement 127.0.0.1 / ::1 $ports = @{} foreach ($c in @(Get-NetTCPConnection -State Listen)) { $local = ($c.LocalAddress -eq '127.0.0.1' -or $c.LocalAddress -eq '::1') $p = [int]$c.LocalPort if (-not $local) { $ports[$p] = 'écoute' } elseif (-not $ports.ContainsKey($p)) { $ports[$p] = 'non' } } function Expo([int[]]$list, [string]$def) { $r = $null foreach ($p in $list) { if ($ports.ContainsKey($p)) { if ($ports[$p] -eq 'écoute') { return 'écoute' } $r = 'non' } } if ($r) { return $r } else { return $def } } # --------------------------------------------------------------------- OS -- $cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $caption = (Get-CimInstance Win32_OperatingSystem).Caption if ($cv -and $caption) { $build = "10.0.$($cv.CurrentBuild).$($cv.UBR)" $disp = "$($cv.DisplayVersion)".ToLower() # 22h2, 23h2, 24h2… $cpeProd = $null; $prod = $null if ($caption -match 'Server\s+(20\d\d)') { $prod = "Windows Server $($Matches[1])"; $cpeProd = "windows_server_$($Matches[1])" } elseif ($caption -match 'Windows\s+(10|11)') { $prod = "Windows $($Matches[1])" if ($disp) { $prod = "$prod $($disp.ToUpper())"; $cpeProd = "windows_$($Matches[1])_$disp" } } if ($prod) { $cpe = ""; if ($cpeProd) { $cpe = "cpe:2.3:o:microsoft:$($cpeProd):*:*:*:*:*:*:*:*" } Add-Row 'Microsoft' $prod $build $Hote (Expo @(3389, 445) 'non') 'editeur' $cpe } } # ------------------------------------------------------- rôles Microsoft -- $exch = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup' if ($exch -and $exch.MsiProductMajor) { $v = "$($exch.MsiProductMajor).$($exch.MsiProductMinor).$($exch.MsiBuildMajor).$($exch.MsiBuildMinor)" Add-Row 'Microsoft' 'Exchange Server' $v $Hote (Expo @(443, 25) 'oui') 'editeur' '' } $iis = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\InetStp' if ($iis -and $iis.MajorVersion) { Add-Row 'Microsoft' 'IIS' "$($iis.MajorVersion).$($iis.MinorVersion)" $Hote (Expo @(80, 443) 'non') 'editeur' '' } $sqlKey = 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' $sql = Get-ItemProperty $sqlKey if ($sql) { foreach ($prop in $sql.PSObject.Properties) { if ($prop.Name -like 'PS*') { continue } $setup = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$($prop.Value)\Setup" if ($setup -and $setup.Version) { Add-Row 'Microsoft' 'SQL Server' $setup.Version '' (Expo @(1433) 'non') 'editeur' '' } } } # ------------------------------------------------- logiciels installés ---- $pertinent = @( 'Fortinet','FortiClient','Palo Alto','GlobalProtect','Cisco','AnyConnect','Secure Client','SonicWall','Ivanti','Pulse', 'Citrix','VMware','Veeam','Exchange','SharePoint','SQL Server','Oracle','Java','JRE','JDK','Adobe Acrobat','Adobe Reader', '7-Zip','WinRAR','Notepad\+\+','PuTTY','WinSCP','FileZilla','OpenSSL','OpenSSH','TeamViewer','AnyDesk','Zoom','Chrome', 'Firefox','Edge','Microsoft 365','Office','Git','Python','Node','PHP','Apache','nginx','Tomcat','MySQL','MariaDB', 'PostgreSQL','Sophos','ESET','Kaspersky','Bitdefender','Trend Micro','GLPI','KeePass','VLC','Wireshark','Docker', 'Atlassian','Confluence','Jira','GitLab','Jenkins','Splunk','Elastic','Zabbix','Nagios','PRTG','ManageEngine','SolarWinds', 'Backup Exec','Acronis','Synology','QNAP','MOVEit','Kaseya','ConnectWise','ScreenConnect','Nessus','Qualys','Rapid7' ) $bruit = 'KB\d{6,}|Update for|Security Update|Hotfix|Redistributable|Language Pack|Pack de langue|Runtime - |Driver|Pilote|\(x64\) Additional|Minimum Runtime|Tools for .NET' $regs = @('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') foreach ($app in @(Get-ItemProperty $regs)) { $name = "$($app.DisplayName)"; $ver = "$($app.DisplayVersion)"; $pub = "$($app.Publisher)" if (-not $name -or -not $ver) { continue } if ($app.SystemComponent -eq 1) { continue } if ($name -match $bruit) { continue } if (-not $Tout) { $keep = $false foreach ($m in $pertinent) { if ($name -match $m -or $pub -match $m) { $keep = $true; break } } if (-not $keep) { continue } } $ed = $pub -replace ',?\s+(Inc\.?|LLC|Ltd\.?|Corporation|Corp\.?|GmbH|S\.A\.|SAS|Limited)$', '' Add-Row $ed $name $ver '' 'non' 'editeur' '' } # ------------------------------------------------------------- écriture --- $lines = New-Object System.Collections.Generic.List[string] $lines.Add('Editeur;Produit;Version;Hôte;Exposé;Origine;Paquet;Cpe') foreach ($r in $rows) { $vals = @($r.'Editeur', $r.'Produit', $r.'Version', $r.'Hôte', $r.'Exposé', $r.'Origine', $r.'Paquet', $r.'Cpe') | ForEach-Object { ("$_" -replace ';', ',' -replace '[\r\n]+', ' ') } $lines.Add(($vals -join ';')) } $utf8 = New-Object System.Text.UTF8Encoding($true) [System.IO.File]::WriteAllLines((Join-Path (Get-Location) $Sortie), $lines, $utf8) Write-Host ("{0} logiciels relevés -> {1}" -f $rows.Count, $Sortie) Write-Host "Relisez le fichier, puis envoyez-le avec la liste de vos domaines et des destinataires du bulletin."