mirror of
https://github.com/itsdave-de/msp.git
synced 2026-08-13 23:20:05 -03:00
chore: commit uncommitted production changes for v16 migration
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Windows Update Diagnostics Script - JSON Output
|
||||
.DESCRIPTION
|
||||
Sammelt Windows Update Diagnoseinformationen und gibt sie als JSON aus.
|
||||
Berücksichtigt TacticalRMM-verwaltete Systeme (AUOptions=1 ist dort normal).
|
||||
.NOTES
|
||||
Version: 2.2
|
||||
Datum: 2026-01-07
|
||||
Output: JSON
|
||||
Changelog: Fixed connectivity check - use download.windowsupdate.com instead of SOAP endpoints
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
# Detect TacticalRMM Agent
|
||||
$tacticalInstalled = $false
|
||||
$tacticalService = Get-Service -Name "tacticalrmm" -ErrorAction SilentlyContinue
|
||||
if ($tacticalService) {
|
||||
$tacticalInstalled = $true
|
||||
}
|
||||
|
||||
# Result object
|
||||
$result = @{
|
||||
hostname = $env:COMPUTERNAME
|
||||
timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ss")
|
||||
managed_by = @{
|
||||
tactical_rmm = $tacticalInstalled
|
||||
tactical_service_status = if ($tacticalService) { $tacticalService.Status.ToString() } else { $null }
|
||||
}
|
||||
os = @{
|
||||
caption = ""
|
||||
build = ""
|
||||
version = ""
|
||||
}
|
||||
services = @()
|
||||
configuration = @{
|
||||
wsus_server = $null
|
||||
au_options = $null
|
||||
au_options_text = ""
|
||||
use_wsus = $false
|
||||
policies = @{}
|
||||
}
|
||||
updates = @{
|
||||
history_count = 0
|
||||
last_success = $null
|
||||
days_since_last_success = $null
|
||||
pending_count = 0
|
||||
pending_critical = 0
|
||||
pending_important = 0
|
||||
pending_driver = 0
|
||||
pending_list = @()
|
||||
recent_history = @()
|
||||
}
|
||||
storage = @{
|
||||
software_distribution_mb = 0
|
||||
download_folder_mb = 0
|
||||
datastore_mb = 0
|
||||
}
|
||||
errors = @()
|
||||
connectivity = @()
|
||||
issues = @()
|
||||
status = "OK"
|
||||
}
|
||||
|
||||
# OS Info
|
||||
try {
|
||||
$osInfo = Get-CimInstance Win32_OperatingSystem
|
||||
$result.os.caption = $osInfo.Caption
|
||||
$result.os.version = $osInfo.Version
|
||||
$ntVersion = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction SilentlyContinue
|
||||
$result.os.build = "$([System.Environment]::OSVersion.Version.Build).$($ntVersion.UBR)"
|
||||
} catch {}
|
||||
|
||||
# Services
|
||||
# Note: BITS and other services start on-demand, so we check StartType not current Status
|
||||
$serviceList = @(
|
||||
@{Name="wuauserv"; DisplayName="Windows Update"; Critical=$true; MustRun=$false},
|
||||
@{Name="bits"; DisplayName="BITS"; Critical=$false; MustRun=$false}, # Starts on demand
|
||||
@{Name="cryptsvc"; DisplayName="CryptSvc"; Critical=$false; MustRun=$false},
|
||||
@{Name="msiserver"; DisplayName="MSIServer"; Critical=$false; MustRun=$false},
|
||||
@{Name="TrustedInstaller"; DisplayName="TrustedInstaller"; Critical=$false; MustRun=$false}
|
||||
)
|
||||
|
||||
foreach ($svc in $serviceList) {
|
||||
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
|
||||
$startType = (Get-CimInstance Win32_Service -Filter "Name='$($svc.Name)'" -ErrorAction SilentlyContinue).StartMode
|
||||
|
||||
# Service is OK if: exists AND (running OR start type is Manual/Auto, not Disabled)
|
||||
$isDisabled = ($startType -eq "Disabled")
|
||||
$isOk = $service -and (-not $isDisabled)
|
||||
|
||||
$svcResult = @{
|
||||
name = $svc.Name
|
||||
display_name = $svc.DisplayName
|
||||
status = if ($service) { $service.Status.ToString() } else { "NotFound" }
|
||||
start_type = $startType
|
||||
critical = $svc.Critical
|
||||
disabled = $isDisabled
|
||||
ok = $isOk
|
||||
}
|
||||
$result.services += $svcResult
|
||||
|
||||
# Only flag as issue if service is disabled or not found
|
||||
if ($svc.Critical -and (-not $service -or $isDisabled)) {
|
||||
$result.issues += "Service '$($svc.DisplayName)' ist deaktiviert oder nicht vorhanden"
|
||||
}
|
||||
}
|
||||
|
||||
# Configuration
|
||||
$wuPolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
|
||||
if (Test-Path $wuPolicyPath) {
|
||||
$wuPolicy = Get-ItemProperty -Path $wuPolicyPath -ErrorAction SilentlyContinue
|
||||
$result.configuration.wsus_server = $wuPolicy.WUServer
|
||||
$result.configuration.use_wsus = ($wuPolicy.UseWUServer -eq 1)
|
||||
|
||||
$auPath = "$wuPolicyPath\AU"
|
||||
if (Test-Path $auPath) {
|
||||
$auPolicy = Get-ItemProperty -Path $auPath -ErrorAction SilentlyContinue
|
||||
$result.configuration.au_options = $auPolicy.AUOptions
|
||||
$result.configuration.policies = @{
|
||||
no_auto_update = $auPolicy.NoAutoUpdate
|
||||
au_options = $auPolicy.AUOptions
|
||||
scheduled_install_day = $auPolicy.ScheduledInstallDay
|
||||
scheduled_install_time = $auPolicy.ScheduledInstallTime
|
||||
}
|
||||
|
||||
# Translate AUOptions
|
||||
$auText = switch ($auPolicy.AUOptions) {
|
||||
1 { "Deaktiviert (RMM-verwaltet)" }
|
||||
2 { "Benachrichtigen vor Download" }
|
||||
3 { "Automatisch downloaden, benachrichtigen vor Installation" }
|
||||
4 { "Automatisch downloaden und installieren" }
|
||||
5 { "Lokaler Admin kann Einstellung wählen" }
|
||||
default { "Nicht konfiguriert" }
|
||||
}
|
||||
$result.configuration.au_options_text = $auText
|
||||
|
||||
# AUOptions=1 is EXPECTED when TacticalRMM is installed (TRMM manages updates)
|
||||
# Only flag as issue if AUOptions=1 AND no RMM is installed
|
||||
if ($auPolicy.AUOptions -eq 1 -and -not $tacticalInstalled) {
|
||||
$result.issues += "Windows Update ist per Policy deaktiviert (AUOptions=1) ohne RMM-Verwaltung"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Update History and Pending
|
||||
try {
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Searcher = $Session.CreateUpdateSearcher()
|
||||
$result.updates.history_count = $Searcher.GetTotalHistoryCount()
|
||||
|
||||
# Recent history
|
||||
if ($result.updates.history_count -gt 0) {
|
||||
$History = $Searcher.QueryHistory(0, [Math]::Min(10, $result.updates.history_count))
|
||||
foreach ($Update in $History) {
|
||||
$resultCode = switch ($Update.ResultCode) {
|
||||
0 { "NotStarted" }
|
||||
1 { "InProgress" }
|
||||
2 { "Succeeded" }
|
||||
3 { "SucceededWithErrors" }
|
||||
4 { "Failed" }
|
||||
5 { "Aborted" }
|
||||
default { "Unknown" }
|
||||
}
|
||||
|
||||
$result.updates.recent_history += @{
|
||||
date = $Update.Date.ToString("yyyy-MM-ddTHH:mm:ss")
|
||||
title = $Update.Title
|
||||
result = $resultCode
|
||||
succeeded = ($Update.ResultCode -eq 2)
|
||||
}
|
||||
}
|
||||
|
||||
# Find last success
|
||||
$allHistory = $Searcher.QueryHistory(0, $result.updates.history_count)
|
||||
foreach ($Update in $allHistory) {
|
||||
if ($Update.ResultCode -eq 2) {
|
||||
$result.updates.last_success = $Update.Date.ToString("yyyy-MM-ddTHH:mm:ss")
|
||||
$result.updates.days_since_last_success = [math]::Round(((Get-Date) - $Update.Date).TotalDays)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $result.updates.last_success) {
|
||||
$result.issues += "Kein erfolgreiches Update in der Historie gefunden"
|
||||
} elseif ($result.updates.days_since_last_success -gt 60) {
|
||||
$result.issues += "Letztes erfolgreiches Update vor $($result.updates.days_since_last_success) Tagen"
|
||||
}
|
||||
|
||||
# Pending updates
|
||||
$SearchResult = $Searcher.Search("IsInstalled=0")
|
||||
$result.updates.pending_count = $SearchResult.Updates.Count
|
||||
|
||||
foreach ($Update in $SearchResult.Updates) {
|
||||
$severity = if ($Update.MsrcSeverity) { $Update.MsrcSeverity } else { "Unspecified" }
|
||||
$isDriver = $Update.Categories | Where-Object { $_.Name -like "*Driver*" }
|
||||
|
||||
$pendingUpdate = @{
|
||||
title = $Update.Title
|
||||
kb = ($Update.KBArticleIDs -join ",")
|
||||
severity = $severity
|
||||
size_mb = [math]::Round($Update.MaxDownloadSize / 1MB, 1)
|
||||
downloaded = $Update.IsDownloaded
|
||||
is_driver = ($null -ne $isDriver)
|
||||
categories = @($Update.Categories | ForEach-Object { $_.Name })
|
||||
}
|
||||
$result.updates.pending_list += $pendingUpdate
|
||||
|
||||
if ($severity -eq "Critical") { $result.updates.pending_critical++ }
|
||||
elseif ($severity -eq "Important") { $result.updates.pending_important++ }
|
||||
if ($isDriver) { $result.updates.pending_driver++ }
|
||||
}
|
||||
|
||||
if ($result.updates.pending_critical -gt 0) {
|
||||
$result.issues += "$($result.updates.pending_critical) kritische Updates ausstehend"
|
||||
}
|
||||
} catch {
|
||||
$result.errors += "Update-Abfrage fehlgeschlagen: $_"
|
||||
}
|
||||
|
||||
# Storage
|
||||
$sdPath = "$env:SystemRoot\SoftwareDistribution"
|
||||
if (Test-Path $sdPath) {
|
||||
$sdSize = (Get-ChildItem -Path $sdPath -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||||
$result.storage.software_distribution_mb = [math]::Round($sdSize / 1MB, 2)
|
||||
|
||||
$dlPath = "$sdPath\Download"
|
||||
if (Test-Path $dlPath) {
|
||||
$dlSize = (Get-ChildItem -Path $dlPath -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||||
$result.storage.download_folder_mb = [math]::Round($dlSize / 1MB, 2)
|
||||
}
|
||||
|
||||
$dbPath = "$sdPath\DataStore\DataStore.edb"
|
||||
if (Test-Path $dbPath) {
|
||||
$result.storage.datastore_mb = [math]::Round((Get-Item $dbPath).Length / 1MB, 2)
|
||||
if ($result.storage.datastore_mb -gt 500) {
|
||||
$result.issues += "DataStore.edb sehr groß ($($result.storage.datastore_mb) MB)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Event Log Errors
|
||||
try {
|
||||
$StartDate = (Get-Date).AddDays(-7)
|
||||
$WUErrors = Get-WinEvent -FilterHashtable @{
|
||||
LogName = 'System'
|
||||
ProviderName = 'Microsoft-Windows-WindowsUpdateClient'
|
||||
Level = 2,3
|
||||
StartTime = $StartDate
|
||||
} -MaxEvents 5 -ErrorAction SilentlyContinue
|
||||
|
||||
foreach ($Event in $WUErrors) {
|
||||
$result.errors += @{
|
||||
date = $Event.TimeCreated.ToString("yyyy-MM-ddTHH:mm:ss")
|
||||
level = if ($Event.Level -eq 2) { "Error" } else { "Warning" }
|
||||
id = $Event.Id
|
||||
message = ($Event.Message -split "`n")[0]
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
# Connectivity
|
||||
# Note: update.microsoft.com and windowsupdate.microsoft.com are SOAP/WCF services
|
||||
# They don't respond to HTTP GET requests - use download URLs instead
|
||||
$testUrls = @(
|
||||
@{url="https://www.microsoft.com"; name="Microsoft"; critical=$false},
|
||||
@{url="http://download.windowsupdate.com"; name="Windows Update Download"; critical=$true},
|
||||
@{url="https://www.catalog.update.microsoft.com"; name="Windows Update Catalog"; critical=$false},
|
||||
@{url="https://download.microsoft.com"; name="Microsoft Download"; critical=$false}
|
||||
)
|
||||
|
||||
foreach ($test in $testUrls) {
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri $test.url -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
|
||||
$result.connectivity += @{
|
||||
name = $test.name
|
||||
url = $test.url
|
||||
status = $response.StatusCode
|
||||
ok = $true
|
||||
}
|
||||
} catch {
|
||||
$result.connectivity += @{
|
||||
name = $test.name
|
||||
url = $test.url
|
||||
status = 0
|
||||
error = $_.Exception.Message
|
||||
ok = $false
|
||||
}
|
||||
if ($test.critical) {
|
||||
$result.issues += "Keine Verbindung zu $($test.name)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Final status determination
|
||||
# Critical issues: connectivity problems, critical pending updates, disabled services
|
||||
# Warning issues: old updates, many pending updates
|
||||
if ($result.issues.Count -gt 0) {
|
||||
$hasCritical = $result.issues | Where-Object {
|
||||
$_ -match "kritisch|keine Verbindung|deaktiviert oder nicht vorhanden|ohne RMM"
|
||||
}
|
||||
$result.status = if ($hasCritical) { "CRITICAL" } else { "WARNING" }
|
||||
} else {
|
||||
$result.status = "OK"
|
||||
}
|
||||
|
||||
# Add summary
|
||||
$result.summary = @{
|
||||
total_issues = $result.issues.Count
|
||||
is_rmm_managed = $tacticalInstalled
|
||||
needs_attention = ($result.status -ne "OK")
|
||||
connectivity_ok = ($result.connectivity | Where-Object { -not $_.ok }).Count -eq 0
|
||||
}
|
||||
|
||||
# Output as JSON
|
||||
$result | ConvertTo-Json -Depth 5 -Compress
|
||||
@@ -0,0 +1,325 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Windows Update Diagnostics Script for TacticalRMM
|
||||
.DESCRIPTION
|
||||
Sammelt umfassende Diagnoseinformationen zum Windows Update Status.
|
||||
Ausgabe erfolgt als strukturierter Text fuer TacticalRMM.
|
||||
.NOTES
|
||||
Version: 1.0
|
||||
Datum: 2026-01-07
|
||||
Fuer: MSP Documentation / TacticalRMM
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
|
||||
function Write-Section {
|
||||
param([string]$Title)
|
||||
Write-Output ""
|
||||
Write-Output "=" * 60
|
||||
Write-Output " $Title"
|
||||
Write-Output "=" * 60
|
||||
}
|
||||
|
||||
function Write-SubSection {
|
||||
param([string]$Title)
|
||||
Write-Output ""
|
||||
Write-Output "--- $Title ---"
|
||||
}
|
||||
|
||||
# Header
|
||||
Write-Output "WINDOWS UPDATE DIAGNOSTICS"
|
||||
Write-Output "Hostname: $env:COMPUTERNAME"
|
||||
Write-Output "Datum: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
||||
Write-Output "OS: $((Get-CimInstance Win32_OperatingSystem).Caption)"
|
||||
Write-Output "Build: $([System.Environment]::OSVersion.Version.Build).$((Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').UBR)"
|
||||
|
||||
# ============================================================
|
||||
# 1. WINDOWS UPDATE DIENSTE
|
||||
# ============================================================
|
||||
Write-Section "1. WINDOWS UPDATE DIENSTE"
|
||||
|
||||
$services = @(
|
||||
@{Name="wuauserv"; DisplayName="Windows Update"},
|
||||
@{Name="bits"; DisplayName="Background Intelligent Transfer"},
|
||||
@{Name="cryptsvc"; DisplayName="Cryptographic Services"},
|
||||
@{Name="msiserver"; DisplayName="Windows Installer"},
|
||||
@{Name="TrustedInstaller"; DisplayName="Windows Modules Installer"}
|
||||
)
|
||||
|
||||
foreach ($svc in $services) {
|
||||
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
|
||||
if ($service) {
|
||||
$startType = (Get-CimInstance Win32_Service -Filter "Name='$($svc.Name)'").StartMode
|
||||
$status = if ($service.Status -eq "Running") { "[OK]" } else { "[!!]" }
|
||||
Write-Output "$status $($svc.DisplayName) ($($svc.Name)): $($service.Status) / StartType: $startType"
|
||||
} else {
|
||||
Write-Output "[??] $($svc.DisplayName) ($($svc.Name)): Nicht gefunden"
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 2. WSUS / WINDOWS UPDATE KONFIGURATION
|
||||
# ============================================================
|
||||
Write-Section "2. WINDOWS UPDATE KONFIGURATION"
|
||||
|
||||
Write-SubSection "Registry: WindowsUpdate Policy"
|
||||
$wuPolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
|
||||
if (Test-Path $wuPolicyPath) {
|
||||
$wuPolicy = Get-ItemProperty -Path $wuPolicyPath -ErrorAction SilentlyContinue
|
||||
Write-Output "WUServer: $($wuPolicy.WUServer)"
|
||||
Write-Output "WUStatusServer: $($wuPolicy.WUStatusServer)"
|
||||
Write-Output "UseWUServer: $($wuPolicy.UseWUServer)"
|
||||
Write-Output "DoNotConnectToWindowsUpdateInternetLocations: $($wuPolicy.DoNotConnectToWindowsUpdateInternetLocations)"
|
||||
|
||||
$auPath = "$wuPolicyPath\AU"
|
||||
if (Test-Path $auPath) {
|
||||
$auPolicy = Get-ItemProperty -Path $auPath -ErrorAction SilentlyContinue
|
||||
Write-Output ""
|
||||
Write-Output "AU Policy:"
|
||||
Write-Output " NoAutoUpdate: $($auPolicy.NoAutoUpdate)"
|
||||
Write-Output " AUOptions: $($auPolicy.AUOptions)"
|
||||
Write-Output " ScheduledInstallDay: $($auPolicy.ScheduledInstallDay)"
|
||||
Write-Output " ScheduledInstallTime: $($auPolicy.ScheduledInstallTime)"
|
||||
Write-Output " UseWUServer: $($auPolicy.UseWUServer)"
|
||||
}
|
||||
} else {
|
||||
Write-Output "Keine WSUS/GPO Konfiguration gefunden (Standard Windows Update)"
|
||||
}
|
||||
|
||||
Write-SubSection "Registry: Windows Update Settings"
|
||||
$wuSettingsPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate"
|
||||
if (Test-Path $wuSettingsPath) {
|
||||
$wuSettings = Get-ItemProperty -Path $wuSettingsPath -ErrorAction SilentlyContinue
|
||||
Write-Output "AccountDomainSid: $($wuSettings.AccountDomainSid)"
|
||||
Write-Output "SusClientId: $($wuSettings.SusClientId)"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 3. WINDOWS UPDATE HISTORIE
|
||||
# ============================================================
|
||||
Write-Section "3. WINDOWS UPDATE HISTORIE (letzte 20)"
|
||||
|
||||
try {
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Searcher = $Session.CreateUpdateSearcher()
|
||||
$HistoryCount = $Searcher.GetTotalHistoryCount()
|
||||
|
||||
Write-Output "Gesamte Historie-Eintraege: $HistoryCount"
|
||||
Write-Output ""
|
||||
|
||||
if ($HistoryCount -gt 0) {
|
||||
$History = $Searcher.QueryHistory(0, [Math]::Min(20, $HistoryCount))
|
||||
|
||||
foreach ($Update in $History) {
|
||||
$ResultCode = switch ($Update.ResultCode) {
|
||||
0 { "NotStarted" }
|
||||
1 { "InProgress" }
|
||||
2 { "Succeeded" }
|
||||
3 { "SucceededWithErrors" }
|
||||
4 { "Failed" }
|
||||
5 { "Aborted" }
|
||||
default { "Unknown" }
|
||||
}
|
||||
|
||||
$Status = if ($Update.ResultCode -eq 2) { "[OK]" } else { "[!!]" }
|
||||
$Date = $Update.Date.ToString("yyyy-MM-dd HH:mm")
|
||||
$Title = $Update.Title
|
||||
if ($Title.Length -gt 60) { $Title = $Title.Substring(0, 57) + "..." }
|
||||
|
||||
Write-Output "$Status [$Date] $ResultCode"
|
||||
Write-Output " $Title"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Output "Fehler beim Abrufen der Update-Historie: $_"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 4. AUSSTEHENDE UPDATES
|
||||
# ============================================================
|
||||
Write-Section "4. AUSSTEHENDE UPDATES"
|
||||
|
||||
try {
|
||||
$Searcher = $Session.CreateUpdateSearcher()
|
||||
$SearchResult = $Searcher.Search("IsInstalled=0")
|
||||
|
||||
Write-Output "Ausstehende Updates: $($SearchResult.Updates.Count)"
|
||||
Write-Output ""
|
||||
|
||||
if ($SearchResult.Updates.Count -gt 0) {
|
||||
foreach ($Update in $SearchResult.Updates) {
|
||||
$Severity = if ($Update.MsrcSeverity) { $Update.MsrcSeverity } else { "Unspecified" }
|
||||
$Size = [math]::Round($Update.MaxDownloadSize / 1MB, 1)
|
||||
|
||||
Write-Output "[$Severity] $($Update.Title)"
|
||||
Write-Output " KB: $($Update.KBArticleIDs -join ', ') | Groesse: ${Size}MB | Downloaded: $($Update.IsDownloaded)"
|
||||
}
|
||||
} else {
|
||||
Write-Output "Keine ausstehenden Updates gefunden."
|
||||
}
|
||||
} catch {
|
||||
Write-Output "Fehler beim Suchen nach Updates: $_"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 5. LETZTE ERFOLGREICHE INSTALLATION
|
||||
# ============================================================
|
||||
Write-Section "5. LETZTE ERFOLGREICHE UPDATE-INSTALLATION"
|
||||
|
||||
try {
|
||||
$LastSuccess = $null
|
||||
$History = $Searcher.QueryHistory(0, $HistoryCount)
|
||||
|
||||
foreach ($Update in $History) {
|
||||
if ($Update.ResultCode -eq 2) {
|
||||
$LastSuccess = $Update
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($LastSuccess) {
|
||||
$DaysAgo = [math]::Round((Get-Date) - $LastSuccess.Date).TotalDays
|
||||
Write-Output "Datum: $($LastSuccess.Date.ToString('yyyy-MM-dd HH:mm:ss'))"
|
||||
Write-Output "Tage her: $DaysAgo"
|
||||
Write-Output "Update: $($LastSuccess.Title)"
|
||||
|
||||
if ($DaysAgo -gt 60) {
|
||||
Write-Output ""
|
||||
Write-Output "[WARNUNG] Letztes erfolgreiches Update ist mehr als 60 Tage her!"
|
||||
}
|
||||
} else {
|
||||
Write-Output "Kein erfolgreiches Update in der Historie gefunden!"
|
||||
}
|
||||
} catch {
|
||||
Write-Output "Fehler: $_"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 6. SOFTWAREDISTRIBUTION ORDNER
|
||||
# ============================================================
|
||||
Write-Section "6. SOFTWAREDISTRIBUTION STATUS"
|
||||
|
||||
$sdPath = "$env:SystemRoot\SoftwareDistribution"
|
||||
if (Test-Path $sdPath) {
|
||||
$sdSize = (Get-ChildItem -Path $sdPath -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||||
$sdSizeMB = [math]::Round($sdSize / 1MB, 2)
|
||||
|
||||
Write-Output "Pfad: $sdPath"
|
||||
Write-Output "Groesse: ${sdSizeMB} MB"
|
||||
|
||||
$downloadPath = "$sdPath\Download"
|
||||
if (Test-Path $downloadPath) {
|
||||
$dlSize = (Get-ChildItem -Path $downloadPath -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||||
$dlSizeMB = [math]::Round($dlSize / 1MB, 2)
|
||||
Write-Output "Download-Ordner: ${dlSizeMB} MB"
|
||||
}
|
||||
|
||||
$dataStorePath = "$sdPath\DataStore\DataStore.edb"
|
||||
if (Test-Path $dataStorePath) {
|
||||
$dbSize = [math]::Round((Get-Item $dataStorePath).Length / 1MB, 2)
|
||||
Write-Output "DataStore.edb: ${dbSize} MB"
|
||||
|
||||
if ($dbSize -gt 500) {
|
||||
Write-Output "[WARNUNG] DataStore.edb ist sehr gross - Reset empfohlen"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Output "[FEHLER] SoftwareDistribution Ordner nicht gefunden!"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 7. WINDOWS UPDATE FEHLER (Event Log)
|
||||
# ============================================================
|
||||
Write-Section "7. WINDOWS UPDATE FEHLER (letzte 7 Tage)"
|
||||
|
||||
try {
|
||||
$StartDate = (Get-Date).AddDays(-7)
|
||||
$WUErrors = Get-WinEvent -FilterHashtable @{
|
||||
LogName = 'System'
|
||||
ProviderName = 'Microsoft-Windows-WindowsUpdateClient'
|
||||
Level = 2,3 # Error, Warning
|
||||
StartTime = $StartDate
|
||||
} -MaxEvents 10 -ErrorAction SilentlyContinue
|
||||
|
||||
if ($WUErrors) {
|
||||
Write-Output "Gefundene Fehler/Warnungen: $($WUErrors.Count)"
|
||||
Write-Output ""
|
||||
|
||||
foreach ($Event in $WUErrors) {
|
||||
$Level = if ($Event.Level -eq 2) { "[ERROR]" } else { "[WARN]" }
|
||||
Write-Output "$Level [$($Event.TimeCreated.ToString('yyyy-MM-dd HH:mm'))] ID:$($Event.Id)"
|
||||
Write-Output " $($Event.Message.Split("`n")[0])"
|
||||
}
|
||||
} else {
|
||||
Write-Output "Keine Windows Update Fehler in den letzten 7 Tagen."
|
||||
}
|
||||
} catch {
|
||||
Write-Output "Event Log konnte nicht abgefragt werden: $_"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 8. NETZWERK-KONNEKTIVITAET
|
||||
# ============================================================
|
||||
Write-Section "8. WINDOWS UPDATE KONNEKTIVITAET"
|
||||
|
||||
$testUrls = @(
|
||||
"https://www.microsoft.com",
|
||||
"https://update.microsoft.com",
|
||||
"https://windowsupdate.microsoft.com"
|
||||
)
|
||||
|
||||
foreach ($url in $testUrls) {
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
|
||||
Write-Output "[OK] $url (Status: $($response.StatusCode))"
|
||||
} catch {
|
||||
Write-Output "[!!] $url (Fehler: $($_.Exception.Message))"
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 9. ZUSAMMENFASSUNG
|
||||
# ============================================================
|
||||
Write-Section "9. ZUSAMMENFASSUNG"
|
||||
|
||||
$issues = @()
|
||||
|
||||
# Check services
|
||||
$wuService = Get-Service -Name wuauserv -ErrorAction SilentlyContinue
|
||||
if ($wuService.Status -ne "Running") {
|
||||
$issues += "Windows Update Dienst laeuft nicht"
|
||||
}
|
||||
|
||||
# Check last update age
|
||||
if ($LastSuccess -and $DaysAgo -gt 60) {
|
||||
$issues += "Letztes Update vor mehr als $DaysAgo Tagen"
|
||||
}
|
||||
|
||||
# Check WSUS
|
||||
if ((Test-Path $wuPolicyPath) -and $wuPolicy.UseWUServer -eq 1) {
|
||||
$issues += "WSUS konfiguriert: $($wuPolicy.WUServer)"
|
||||
}
|
||||
|
||||
# Check pending updates
|
||||
if ($SearchResult -and $SearchResult.Updates.Count -gt 5) {
|
||||
$issues += "$($SearchResult.Updates.Count) ausstehende Updates"
|
||||
}
|
||||
|
||||
if ($issues.Count -eq 0) {
|
||||
Write-Output "Status: OK - Keine offensichtlichen Probleme gefunden"
|
||||
} else {
|
||||
Write-Output "Status: PROBLEME ERKANNT"
|
||||
Write-Output ""
|
||||
foreach ($issue in $issues) {
|
||||
Write-Output " [!] $issue"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output ""
|
||||
Write-Output "=" * 60
|
||||
Write-Output " DIAGNOSTICS ENDE"
|
||||
Write-Output "=" * 60
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user