Auto-commit: 2025-10-31 08:55:43

This commit is contained in:
David Wuibaille
2025-10-31 08:55:43 +01:00
parent 9bb5ad24bb
commit 24c0c6509f
33 changed files with 13144 additions and 0 deletions

231
Dashboard/report.ps1 Normal file
View File

@@ -0,0 +1,231 @@
#Requires -RunAsAdministrator
$ErrorActionPreference = 'Stop'
# ****************************** MODULE IMPORT & INITIALIZATION ******************************
# HTML reporting module
Import-Module -Name PSWriteHTML -ErrorAction Stop
Write-Host "Starting WSUS report generation..."
# Load WSUS admin assembly explicitly (no deprecated LoadWithPartialName)
$wsusDll = "$env:ProgramFiles\Update Services\Tools\Microsoft.UpdateServices.Administration.dll"
if (Test-Path $wsusDll) { Add-Type -Path $wsusDll } else { throw "WSUS Admin DLL not found: $wsusDll" }
# Connect to local WSUS and pull data
$wsusServer = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer()
$updates = $wsusServer.GetUpdates()
$computers = $wsusServer.GetComputerTargets()
$targetGroups = $wsusServer.GetComputerTargetGroups()
# Map groupId -> groupName for quick lookups
$groupHashTable = @{}
$targetGroups | ForEach-Object { $groupHashTable[$_.ID] = $_.Name }
Write-Host "Retrieved WSUS data: Updates, Computers, and Target Groups."
# Ensure output folder exists
$outFile = "C:\exploit\report\default.htm"
$outDir = Split-Path $outFile -Parent
if (-not (Test-Path $outDir)) { New-Item -Path $outDir -ItemType Directory -Force | Out-Null }
# ****************************** REPORT GENERATION START ******************************
New-HTML -TitleText 'WSUS Report' {
# ****************************** COMPUTERS BY TARGET GROUP ******************************
# Build a simple dataset: group name + computer count (skip 'All Computers')
$DataTable = New-Object System.Collections.Generic.List[Object]
foreach ($group in $targetGroups) {
$computersCount = $group.GetComputerTargets().Count
if ($computersCount -gt 0 -and $group.Name -ne 'All Computers') {
$DataTable.Add([PSCustomObject]@{
TargetName = $group.Name
Total = $computersCount
})
}
}
Write-Host "Populated DataTable with computer counts per Target Group."
New-HTMLSection -HeaderText 'Computers By TargetGroup' {
# Table
New-HTMLPanel {
New-HTMLTable -DataTable $DataTable -HideFooter -DataTableID 'IDtargetGroup' {
# Bind events to the correct table ID
New-TableEvent -ID 'IDtargetGroup' -SourceColumnID 0 -TargetColumnId 0
}
}
# Pie chart per group
New-HTMLPanel {
New-HTMLChart {
New-ChartToolbar -Download
foreach ($row in $DataTable) { New-ChartPie -Name $row.TargetName -Value $row.Total }
}
}
}
# ****************************** COMPUTER LIST (HIDDEN) ******************************
$ListeComputersWSUS = @()
foreach ($computer in $computers) {
foreach ($tg in $computer.GetComputerTargetGroups()) {
if ($tg.Name -ne 'All Computers') {
$ListeComputersWSUS += [PSCustomObject]@{
TargetName = $tg.Name
ComputerName = $computer.FullDomainName
}
}
}
}
New-HTMLSection -HeaderText 'Computers' -Invisible {
New-HTMLTable -DataTable $ListeComputersWSUS -DataTableID 'AllComputertargetGroup' -HideFooter
}
# ****************************** DEPLOYMENT STATUS OF KB UPDATES ******************************
$DateNow = Get-Date
$ListeKB = @()
$MaxDaysReport = 90 # (kept for future filtering if needed)
foreach ($update in $updates) {
foreach ($approval in $update.GetUpdateApprovals()) {
foreach ($tg in $targetGroups) {
if ($tg.Id -eq $approval.ComputerTargetGroupId) {
$DateKB = $approval.GoLiveTime
$LastChange = New-TimeSpan -Start $DateKB -End $DateNow
$ActionType = $approval.Action
if ($ActionType -eq 'Install') {
$KBTitre = $update.Title
$installKB = 0; $MissKB = 0
# Summaries per group for this update
foreach ($sum in $update.GetSummaryPerComputerTargetGroup()) {
$groupName = $groupHashTable[$sum.ComputerTargetGroupId]
if ($tg.Name -eq $groupName) {
$installKB = $sum.InstalledCount + $sum.InstalledPendingRebootCount
$MissKB = $sum.UnknownCount + $sum.NotInstalledCount + $sum.DownloadedCount + $sum.FailedCount
}
}
# Keep only meaningful KBs (exclude ARM64, SSU, Flash, specific KBs)
if (($installKB -ne 0 -or $MissKB -ne 0) -and
($KBTitre -notmatch 'ARM64') -and
($KBTitre -notmatch 'Servicing Stack Update') -and
($KBTitre -notmatch 'Flash Player') -and
($KBTitre -notlike '*KB4470788*') -and
($KBTitre -notlike '*KB4499728*')) {
$ListeKB += [PSCustomObject]@{
KBTitre = $KBTitre
KBGroup = $tg.Name
KBChang = $LastChange.Days
ActionType = $ActionType
installKB = $installKB
MissKB = $MissKB
}
}
}
}
}
}
}
# Sort by age then title; get unique titles for chart grouping
$ListeKB = $ListeKB | Sort-Object KBChang, KBTitre
$groupedByTitle = @{}
foreach ($o in $ListeKB) {
if (-not $groupedByTitle.ContainsKey($o.KBTitre)) { $groupedByTitle[$o.KBTitre] = @() }
$groupedByTitle[$o.KBTitre] += $o
}
foreach ($Title in $groupedByTitle.Keys) {
# Timeline per group (how many days since approval)
New-HTMLSection -HeaderText $Title {
New-HTMLChart -Title $Title -TitleAlignment center {
foreach ($o in $groupedByTitle[$Title]) {
New-ChartTimeline -DateFrom (Get-Date).AddDays(-$o.KBChang - 1) -DateTo (Get-Date) -Name $o.KBGroup
}
}
}
# Donut charts (Installed vs Missing) per group hidden
New-HTMLSection -HeaderText 'Computers' -Invisible {
foreach ($o in $groupedByTitle[$Title]) {
New-HTMLPanel {
New-HTMLChart -Title $o.KBGroup {
New-ChartLegend -Name "Installed","Missing" -Color Green,Red
New-ChartDonut -Name "Installed" -Value $o.installKB
New-ChartDonut -Name "Missing" -Value $o.MissKB
}
}
}
}
# ------------------------- KB and Computers status table (hidden) -------------------------
$updateScope2 = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateScope2.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved
$updateScope2.UpdateApprovalActions = [Microsoft.UpdateServices.Administration.UpdateApprovalActions]::Install
$updateScope2.UpdateSources = [Microsoft.UpdateServices.Administration.UpdateSources]::MicrosoftUpdate
$updateScope2.ExcludedInstallationStates = @('NotApplicable','Installed','InstalledPendingReboot')
$allComputers = $wsusServer.GetComputerTargets()
$resultsComputerStatus = @()
foreach ($pc in $allComputers) {
$updatelist = $pc.GetUpdateInstallationInfoPerUpdate($updateScope2)
foreach ($ui in $updatelist) {
$updInfo = $ui.GetUpdate()
$approvalGroup = $ui.GetUpdateApprovalTargetGroup().Name
if ($approvalGroup -ne 'All Computers' -and $updInfo.IsApproved) {
$resultsComputerStatus += [pscustomobject][Ordered]@{
ComputerName = $pc.FullDomainName
Status = $ui.UpdateInstallationState
ApprovalTargetGroup= $approvalGroup
Approved = $updInfo.IsApproved
Title = $updInfo.Title
}
}
}
}
New-HTMLSection -HeaderText 'Computers' -Invisible {
foreach ($o in $groupedByTitle[$Title]) {
New-HTMLPanel {
$specific = $resultsComputerStatus |
Where-Object { $_.Title -like "*$Title*" -and $_.ApprovalTargetGroup -like "*$($o.KBGroup)*" } |
Select-Object ComputerName, Status
New-HTMLTable -DataTable $specific -HideFooter -HideButtons
}
}
}
}
# ****************************** COMPUTERS IN ERROR ******************************
$computersInError = @()
$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateScope.IncludedInstallationStates = 'Failed'
$computerScope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$computerScope.IncludedInstallationStates = 'Failed'
$computersInError = $wsusServer.GetComputerTargets($computerScope) | ForEach-Object {
$compName = $_.FullDomainName
$failedTitles = ($_.GetUpdateInstallationInfoPerUpdate($updateScope) | ForEach-Object {
$_.GetUpdate().Title
}) -join ', '
if ($failedTitles) {
[pscustomobject]@{
Computername = $compName
TargetGroups = ($_.GetComputerTargetGroups() | Select-Object -ExpandProperty Name) -join ', '
Updates = $failedTitles
}
}
} | Sort-Object Computername
New-HTMLSection -HeaderText 'Computers in ERROR' {
New-HTMLTable -DataTable $computersInError -DataTableID 'NewIDtoSearchInChartERR' -HideFooter
}
# ****************************** FOOTER ******************************
New-HTMLFooter {
New-HTMLText -Text ("Report generated (GMT): {0:u}" -f (Get-Date).ToUniversalTime()) -Color Blue -Alignment center
}
} -FilePath $outFile -Online
Write-Host "WSUS report has been generated successfully at $outFile"