feat(Hermmes): DebugDetails fuer Get-SCCMAppDeployment hinzufuegen

- neue Option -DebugDetails
- zeigt Pruefung der ChBcPfade, Log-Zugriff, Zeilen-/Trefferanzahlen
- zeigt Fallbeispiele aus AppEnforce, wenn keine Anwendung erkannt wird
- zeigt fuer jede Anwendung Detailzaehler fuer Regex, Cache, Detection
This commit is contained in:
buiaca 2026-07-29 08:07:52 +02:00
parent 6442edb2e2
commit 5666a8df0d

View File

@ -2,20 +2,12 @@
.SYNOPSIS .SYNOPSIS
Liest den SCCM/MECM-Deployment-Status der letzten Anwendungen auf einem entfernten Client aus. Liest den SCCM/MECM-Deployment-Status der letzten Anwendungen auf einem entfernten Client aus.
.DESCRIPTION .DESCRIPTION
Das Skript greift ueber dieAdmin-Freigabe C$ auf die CCM-Logs zu und fragt Das Skript greift ueber die Admin-Freigabe C$ auf die CCM-Logs zu und fragt
zu Beginn immer nach Benutzername und Passwort. zu Beginn immer nach Benutzername und Passwort.
Es zeigt standardmaessig die letzten Anwendungen mit Deployment-Aktivitaet an: Es zeigt standardmaessig die letzten Anwendungen mit Deployment-Aktivitaet an.
- Ob der Content bereits "angekommen" ist (Download abgeschlossen) Mit -DebugDetails werden zusaetzliche Pruefschritte ausgegeben, damit man
- In welchem Cache-Ordner der Content liegt erkennen kann, wo Log-Zugriff, App-Erkennung oder Regex-Parsing fehlschlaegt.
- Welche Erkennungsmethode (Detection) greift und deren Ergebnis
- Ob die Installation bereits begonnen hat, erfolgreich war oder warum nicht
(Fehlercode + begruendende Log-Zeilen)
Quellen:
- AppDiscovery.log
- AppEnforce.log
- CAS.log
.PARAMETER ComputerName .PARAMETER ComputerName
Name oder IP des Ziel-Clients. Name oder IP des Ziel-Clients.
.PARAMETER CCMPath .PARAMETER CCMPath
@ -24,10 +16,12 @@
Anzahl der letzten Anwendungen, die angezeigt werden sollen (Default: 5). Anzahl der letzten Anwendungen, die angezeigt werden sollen (Default: 5).
.PARAMETER Detailed .PARAMETER Detailed
Schaltet zusaetzliche Roh-Logauszuege ein. Schaltet zusaetzliche Roh-Logauszuege ein.
.PARAMETER DebugDetails
Schaltet Debug-Ausgaben zu Zugriff, Logs, Regex und Auswahl ein.
.EXAMPLE .EXAMPLE
.\Get-SCCMAppDeployment.ps1 -ComputerName CLIENT01 .\Get-SCCMAppDeployment.ps1 -ComputerName CLIENT01
.EXAMPLE .EXAMPLE
.\Get-SCCMAppDeployment.ps1 -ComputerName CLIENT01 -Last 10 -Detailed .\Get-SCCMAppDeployment.ps1 -ComputerName CLIENT01 -Last 10 -Detailed -DebugDetails
#> #>
[CmdletBinding()] [CmdletBinding()]
@ -39,7 +33,9 @@ param(
[int]$Last = 5, [int]$Last = 5,
[switch]$Detailed [switch]$Detailed,
[switch]$DebugDetails
) )
#region Nachschlagetabellen --------------------------------------------------- #region Nachschlagetabellen ---------------------------------------------------
@ -107,9 +103,10 @@ function Parse-SCCMLog {
[CmdletBinding()] [CmdletBinding()]
param([Parameter(Mandatory)][string[]]$Content) param([Parameter(Mandatory)][string[]]$Content)
$re = '^<!\[LOG\[(?<msg>.*?)\]LOG\]!><time="(?<time>[^"]*)" date="(?<date>[^"]*)" component="(?<comp>[^"]*)" context="[^"]*" type="(?<type>\d)" thread="[^"]*" file="[^"]*">' $re = '^<!\[LOG\[(?<msg>.*?)\]LOG\]!><time="(?<time>[^"]*)" date="(?<date>[^"]*)" component="(?<comp>[^"]*)" context="[^"]*" type="(?<type>\d)" thread="[^"]*" file="[^"]*">'
$parsed = @()
foreach ($line in $Content) { foreach ($line in $Content) {
if ($line -match $re) { if ($line -match $re) {
[PSCustomObject]@{ $parsed += [PSCustomObject]@{
Time = "$($Matches.date) $($Matches.time)" Time = "$($Matches.date) $($Matches.time)"
Component = $Matches.comp Component = $Matches.comp
Type = [int]$Matches.type Type = [int]$Matches.type
@ -117,6 +114,14 @@ function Parse-SCCMLog {
} }
} }
} }
return $parsed
}
function Write-DebugLine {
param([string]$Message)
if ($DebugDetails) {
Write-Host "[DEBUG] $Message" -ForegroundColor DarkYellow
}
} }
function Get-RemoteCCMLog { function Get-RemoteCCMLog {
@ -126,6 +131,8 @@ function Get-RemoteCCMLog {
[string]$LogName [string]$LogName
) )
$path = "\\$ComputerName\C$\Windows\CCM\Logs\$LogName" $path = "\\$ComputerName\C$\Windows\CCM\Logs\$LogName"
Write-DebugLine "Pruefe Log-Pfad: $path"
if (-not (Test-Path $path)) { if (-not (Test-Path $path)) {
Write-Warning "Log nicht vorhanden: $path" Write-Warning "Log nicht vorhanden: $path"
return @() return @()
@ -163,7 +170,9 @@ function Get-RemoteCCMLog {
} }
} }
Write-DebugLine "$LogName geladen: $($content.Count) Zeilen"
$entries = Parse-SCCMLog -Content $content $entries = Parse-SCCMLog -Content $content
Write-DebugLine "$LogName geparst: $($entries.Count) Eintraege"
return $entries return $entries
} }
@ -189,14 +198,21 @@ else {
} }
} }
$credPassPlain = $credPass
#endregion #endregion
#region Logs laden ------------------------------------------------------------ #region Logs laden ------------------------------------------------------------
Write-Host "`n=== Lade Logs ===" -ForegroundColor Cyan
$appAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'AppDiscovery.log' $appAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'AppDiscovery.log'
$appEnfAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'AppEnforce.log' $appEnfAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'AppEnforce.log'
$casAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'CAS.log' $casAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'CAS.log'
Write-DebugLine "AppDiscovery: $($appAll.Count) Eintraege"
Write-DebugLine "AppEnforce: $($appEnfAll.Count) Eintraege"
Write-DebugLine "CAS: $($casAll.Count) Eintraege"
#endregion #endregion
#region Apps ueber Logs ermitteln --------------------------------------------- #region Apps ueber Logs ermitteln ---------------------------------------------
@ -204,8 +220,14 @@ $casAll = Get-RemoteCCMLog -ComputerName $ComputerName -LogName 'CAS.log'
$appsWithChanges = @() $appsWithChanges = @()
$appNames = @{} $appNames = @{}
Write-DebugLine "Suche Application-Namen in AppEnforce..."
$sampleAppMatches = 0
foreach ($entry in $appEnfAll) { foreach ($entry in $appEnfAll) {
if ($entry.Message -match '(?i)application\s+"([^"]+)"') { if ($entry.Message -match '(?i)application\s+"([^"]+)"') {
$sampleAppMatches++
if ($sampleAppMatches -le 5) {
Write-DebugLine "Match: $($Matches[1])"
}
$name = $Matches[1] $name = $Matches[1]
if (-not $appNames.ContainsKey($name)) { if (-not $appNames.ContainsKey($name)) {
$appNames[$name] = $true $appNames[$name] = $true
@ -216,10 +238,21 @@ foreach ($entry in $appEnfAll) {
} }
} }
} }
Write-DebugLine "AppEnforce-Matches gesamt: $sampleAppMatches"
Write-DebugLine "Eindeutige Anwendungsnamen: $($appNames.Count)"
if ($appsWithChanges.Count -eq 0) {
Write-Warning "Keine Anwendungen ueber Regex in AppEnforce gefunden."
Write-Host "`n=== DEBUG: Fallbeispiele AppEnforce ===" -ForegroundColor Yellow
$appEnfAll | Select-Object -First 20 | ForEach-Object { Write-Host $_.Message }
Write-Host "=== Ende DEBUG ===`n" -ForegroundColor Yellow
}
$appsWithChanges = $appsWithChanges | Sort-Object -Property LastChange -Descending $appsWithChanges = $appsWithChanges | Sort-Object -Property LastChange -Descending
$selectedApps = $appsWithChanges | Select-Object -First $Last $selectedApps = $appsWithChanges | Select-Object -First $Last
Write-DebugLine "Ausgewaehlte Anwendungen: $($selectedApps.Count)"
#endregion #endregion
#region Auswertung ------------------------------------------------------------- #region Auswertung -------------------------------------------------------------
@ -229,6 +262,10 @@ $results = foreach ($entry in $selectedApps) {
$appEnf = $appEnfAll | Where-Object { $_.Message -like "*$ApplicationName*" } $appEnf = $appEnfAll | Where-Object { $_.Message -like "*$ApplicationName*" }
$appDisc = $appAll | Where-Object { $_.Message -like "*$ApplicationName*" } $appDisc = $appAll | Where-Object { $_.Message -like "*$ApplicationName*" }
Write-DebugLine "Auswerte: $ApplicationName"
Write-DebugLine " AppEnforce-Zeilen: $($appEnf.Count)"
Write-DebugLine " AppDiscovery-Zeilen: $($appDisc.Count)"
# Content # Content
$contentIds = @() $contentIds = @()
foreach ($e in $appEnf) { foreach ($e in $appEnf) {
@ -236,6 +273,7 @@ $results = foreach ($entry in $selectedApps) {
elseif ($e.Message -match 'ContentId ([A-Fa-f0-9-]{8,})') { $contentIds += $Matches[1] } elseif ($e.Message -match 'ContentId ([A-Fa-f0-9-]{8,})') { $contentIds += $Matches[1] }
} }
$contentIds = $contentIds | Sort-Object -Unique $contentIds = $contentIds | Sort-Object -Unique
Write-DebugLine " ContentIds gefunden: $($contentIds.Count)"
$cachePaths = @() $cachePaths = @()
foreach ($cid in $contentIds) { foreach ($cid in $contentIds) {
@ -244,6 +282,7 @@ $results = foreach ($entry in $selectedApps) {
} }
} }
$cachePaths = $cachePaths | Sort-Object -Unique $cachePaths = $cachePaths | Sort-Object -Unique
Write-DebugLine " Cache-Pfade gefunden: $($cachePaths.Count)"
$arrivedInCache = $cachePaths.Count -gt 0 $arrivedInCache = $cachePaths.Count -gt 0
$cacheFolder = $cachePaths -join "`n" $cacheFolder = $cachePaths -join "`n"
@ -253,6 +292,7 @@ $results = foreach ($entry in $selectedApps) {
if ($cacheParts) { if ($cacheParts) {
$cacheFolder = ($cacheParts -join "`n") $cacheFolder = ($cacheParts -join "`n")
$arrivedInCache = $true $arrivedInCache = $true
Write-DebugLine " Cache ueber Keywords erkannt"
} }
} }
@ -295,20 +335,26 @@ $results = foreach ($entry in $selectedApps) {
#region Ausgabe ---------------------------------------------------------------- #region Ausgabe ----------------------------------------------------------------
Write-Host "`n===== SCCM-Deployment-Status: letzte $Last Anwendungen @ $ComputerName =====" -ForegroundColor Cyan Write-Host "`n===== SCCM-Deployment-Status: letzte $Last Anwendungen @ $ComputerName =====" -ForegroundColor Cyan
foreach ($r in $results) { if ($results) {
Write-Host ("`nAnwendung : {0}" -f $r.Application) -ForegroundColor Cyan foreach ($r in $results) {
Write-Host ("InstallState : {0}" -f $r.InstallState) Write-Host ("`nAnwendung : {0}" -f $r.Application) -ForegroundColor Cyan
Write-Host ("Angekommen/Cache : {0}" -f $(if ($r.Angekommen) { 'JA' } else { 'NEIN' })) -ForegroundColor $(if ($r.Angekommen) { 'Green' } else { 'Yellow' }) Write-Host ("InstallState : {0}" -f $r.InstallState)
Write-Host ("Cache-Pfad : {0}" -f $r.CachePfad) Write-Host ("Angekommen/Cache : {0}" -f $(if ($r.Angekommen) { 'JA' } else { 'NEIN' })) -ForegroundColor $(if ($r.Angekommen) { 'Green' } else { 'Yellow' })
Write-Host ("Detection : {0}" -f $r.DetectionErgebnis) Write-Host ("Cache-Pfad : {0}" -f $r.CachePfad)
Write-Host ("Install gestartet: {0}" -f $(if ($r.InstallStarted) { 'JA' } else { 'NEIN' })) Write-Host ("Detection : {0}" -f $r.DetectionErgebnis)
Write-Host ("Install fertig : {0}" -f $(if ($r.InstallAbgeschlossen) { 'JA' } else { 'NEIN' })) Write-Host ("Install gestartet: {0}" -f $(if ($r.InstallStarted) { 'JA' } else { 'NEIN' }))
Write-Host ("Letzte Aenderung : {0}" -f $r.LetzteAenderung) Write-Host ("Install fertig : {0}" -f $(if ($r.InstallAbgeschlossen) { 'JA' } else { 'NEIN' }))
Write-Host ("Grund/Status : {0}" -f $r.Grund) -ForegroundColor $(if ($r.Grund -match 'Erfolg|abgeschlossen|erfolgreich|erkannt') { 'Green' } else { 'Yellow' }) Write-Host ("Letzte Aenderung : {0}" -f $r.LetzteAenderung)
if ($Detailed) { Write-Host ("Grund/Status : {0}" -f $r.Grund) -ForegroundColor $(if ($r.Grund -match 'Erfolg|abgeschlossen|erfolgreich|erkannt') { 'Green' } else { 'Yellow' })
Write-Host ("Fehlerdetails : {0}" -f $r.InstallFehler) -ForegroundColor DarkGray if ($Detailed) {
Write-Host ("Fehlerdetails : {0}" -f $r.InstallFehler) -ForegroundColor DarkGray
}
} }
} }
else {
Write-Host "Es wurden keine Anwendungen zur Anzeige ausgewaehlt." -ForegroundColor Red
Write-Host "Hinweis: Starte das Script mit -DebugDetails, um zu sehen, wo es haengt." -ForegroundColor Yellow
}
Write-Host "" Write-Host ""
# Als Objekte zurueckgeben # Als Objekte zurueckgeben