@@ -3604,6 +3604,244 @@ function Invoke-CLIAction {
36043604
36053605 if ($bootIssues -gt 0 ) { [Environment ]::Exit (1 ) }
36063606 }
3607+ ' GPOAudit' {
3608+ Write-OutputColor " Auditing Group Policy configuration..." - color " Info"
3609+ Write-OutputColor " " - color " Info"
3610+
3611+ $gpoIssues = 0
3612+ $gpoEntries = @ ()
3613+
3614+ # Check if domain-joined
3615+ $isDomainJoined = $false
3616+ try {
3617+ $cs = Get-CimInstance - ClassName Win32_ComputerSystem - ErrorAction Stop
3618+ $isDomainJoined = $cs.PartOfDomain -eq $true
3619+ } catch { }
3620+
3621+ if (-not $isDomainJoined ) {
3622+ Write-OutputColor " Not domain-joined — checking local policy only." - color " Info"
3623+ }
3624+
3625+ # Query applied GPOs via registry (works on all systems)
3626+ $gpoRegPaths = @ (
3627+ @ { Scope = ' Machine' ; Path = ' HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\History' }
3628+ @ { Scope = ' User' ; Path = ' HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\History' }
3629+ )
3630+
3631+ foreach ($gpoReg in $gpoRegPaths ) {
3632+ if (Test-Path $gpoReg.Path ) {
3633+ try {
3634+ $subkeys = Get-ChildItem - Path $gpoReg.Path - ErrorAction SilentlyContinue
3635+ foreach ($sk in $subkeys ) {
3636+ $innerKeys = Get-ChildItem - Path $sk.PSPath - ErrorAction SilentlyContinue
3637+ foreach ($ik in $innerKeys ) {
3638+ $props = Get-ItemProperty - Path $ik.PSPath - ErrorAction SilentlyContinue
3639+ if ($null -ne $props -and $null -ne $props.DisplayName ) {
3640+ $gpoEntries += @ {
3641+ Scope = $gpoReg.Scope
3642+ DisplayName = $props.DisplayName
3643+ GPOName = if ($null -ne $props.GPOName ) { $props.GPOName } else { " " }
3644+ Extensions = if ($null -ne $props.Extensions ) { $props.Extensions } else { " " }
3645+ Link = if ($null -ne $props.Link ) { $props.Link } else { " " }
3646+ Status = ' Applied'
3647+ }
3648+ }
3649+ }
3650+ }
3651+ } catch { }
3652+ }
3653+ }
3654+
3655+ # Deduplicate by DisplayName+Scope
3656+ $uniqueGPOs = @ ()
3657+ $seen = @ {}
3658+ foreach ($g in $gpoEntries ) {
3659+ $key = " $ ( $g.Scope ) :$ ( $g.DisplayName ) "
3660+ if (-not $seen.ContainsKey ($key )) {
3661+ $seen [$key ] = $true
3662+ $uniqueGPOs += $g
3663+ }
3664+ }
3665+
3666+ # Last gpupdate time
3667+ $lastGPUpdate = " Unknown"
3668+ try {
3669+ $gpResult = & gpresult / scope computer / v 2>&1
3670+ $gpStr = $gpResult -join " `n "
3671+ if ($gpStr -match ' Last time Group Policy was applied:\s*(.+)' ) {
3672+ $regexMatches = $Matches ; $lastGPUpdate = $regexMatches [1 ].Trim()
3673+ }
3674+ } catch { }
3675+
3676+ $machineGPOs = @ ($uniqueGPOs | Where-Object { $_.Scope -eq ' Machine' })
3677+ $userGPOs = @ ($uniqueGPOs | Where-Object { $_.Scope -eq ' User' })
3678+
3679+ # Console output
3680+ Write-OutputColor " ┌────────────────────────────────────────────────────────────────────────┐" - color " Info"
3681+ Write-OutputColor " │$ ( " GPO AUDIT - $env: COMPUTERNAME " .PadRight(72 )) │" - color " Info"
3682+ Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" - color " Info"
3683+ Write-OutputColor " │$ ( " Domain Joined: $isDomainJoined Last GP Update: $lastGPUpdate " .PadRight(72 )) │" - color " Info"
3684+ Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" - color " Info"
3685+ if (@ ($machineGPOs ).Count -gt 0 ) {
3686+ Write-OutputColor " │$ ( " MACHINE POLICIES ($ ( @ ($machineGPOs ).Count) )" .PadRight(72 )) │" - color " Info"
3687+ foreach ($g in $machineGPOs ) {
3688+ $gpoName = if ($g.DisplayName.Length -gt 65 ) { $g.DisplayName.Substring (0 , 62 ) + " ..." } else { $g.DisplayName }
3689+ Write-OutputColor " │$ ( " $gpoName " .PadRight(72 )) │" - color " Success"
3690+ }
3691+ }
3692+ if (@ ($userGPOs ).Count -gt 0 ) {
3693+ Write-OutputColor " │$ ( " USER POLICIES ($ ( @ ($userGPOs ).Count) )" .PadRight(72 )) │" - color " Info"
3694+ foreach ($g in $userGPOs ) {
3695+ $gpoName = if ($g.DisplayName.Length -gt 65 ) { $g.DisplayName.Substring (0 , 62 ) + " ..." } else { $g.DisplayName }
3696+ Write-OutputColor " │$ ( " $gpoName " .PadRight(72 )) │" - color " Success"
3697+ }
3698+ }
3699+ if (@ ($uniqueGPOs ).Count -eq 0 ) {
3700+ Write-OutputColor " │$ ( " (No applied Group Policies found)" .PadRight(72 )) │" - color " Info"
3701+ }
3702+ Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" - color " Info"
3703+ $summaryLine = " Machine: $ ( @ ($machineGPOs ).Count) User: $ ( @ ($userGPOs ).Count) Total: $ ( @ ($uniqueGPOs ).Count) "
3704+ Write-OutputColor " │$ ( $summaryLine.PadRight (72 )) │" - color " Success"
3705+ Write-OutputColor " └────────────────────────────────────────────────────────────────────────┘" - color " Info"
3706+
3707+ if ($script :CLIOutputFormat -eq ' JSON' ) {
3708+ $jsonResult = @ {
3709+ Tool = $script :ToolFullName
3710+ Version = $script :ScriptVersion
3711+ Action = ' GPOAudit'
3712+ Timestamp = (Get-Date - Format " yyyy-MM-ddTHH:mm:ss" )
3713+ Hostname = $env: COMPUTERNAME
3714+ Summary = @ {
3715+ DomainJoined = $isDomainJoined
3716+ LastGPUpdate = $lastGPUpdate
3717+ MachinePolicies = @ ($machineGPOs ).Count
3718+ UserPolicies = @ ($userGPOs ).Count
3719+ TotalPolicies = @ ($uniqueGPOs ).Count
3720+ Issues = $gpoIssues
3721+ }
3722+ Policies = $uniqueGPOs
3723+ }
3724+ Write-Output ($jsonResult | ConvertTo-Json - Depth 10 )
3725+ }
3726+
3727+ if ($gpoIssues -gt 0 ) { [Environment ]::Exit (1 ) }
3728+ }
3729+ ' MemoryAudit' {
3730+ Write-OutputColor " Auditing memory configuration..." - color " Info"
3731+ Write-OutputColor " " - color " Info"
3732+
3733+ $memIssues = 0
3734+
3735+ # Physical memory
3736+ $totalPhysicalGB = 0
3737+ $availableGB = 0
3738+ $usedGB = 0
3739+ $pctUsed = 0
3740+ try {
3741+ $os = Get-CimInstance - ClassName Win32_OperatingSystem - ErrorAction Stop
3742+ $totalPhysicalGB = [math ]::Round($os.TotalVisibleMemorySize / 1 MB , 1 )
3743+ $availableGB = [math ]::Round($os.FreePhysicalMemory / 1 MB , 1 )
3744+ $usedGB = [math ]::Round($totalPhysicalGB - $availableGB , 1 )
3745+ $pctUsed = if ($totalPhysicalGB -gt 0 ) { [math ]::Round(($usedGB / $totalPhysicalGB ) * 100 , 1 ) } else { 0 }
3746+ if ($pctUsed -gt 90 ) { $memIssues ++ }
3747+ } catch {
3748+ Write-OutputColor " ERROR: Failed to query memory: $ ( $_.Exception.Message ) " - color " Error"
3749+ [Environment ]::Exit (1 )
3750+ }
3751+
3752+ # Memory DIMMs
3753+ $dimms = @ ()
3754+ try {
3755+ $physMem = @ (Get-CimInstance - ClassName Win32_PhysicalMemory - ErrorAction Stop)
3756+ foreach ($m in $physMem ) {
3757+ $dimms += @ {
3758+ BankLabel = " $ ( $m.BankLabel ) "
3759+ DeviceLocator = " $ ( $m.DeviceLocator ) "
3760+ CapacityGB = [math ]::Round($m.Capacity / 1 GB , 1 )
3761+ Speed = $m.Speed
3762+ Manufacturer = " $ ( $m.Manufacturer ) "
3763+ PartNumber = " $ ( $m.PartNumber ) " .Trim()
3764+ }
3765+ }
3766+ } catch {
3767+ Write-OutputColor " WARNING: Could not enumerate DIMMs: $ ( $_.Exception.Message ) " - color " Warning"
3768+ }
3769+
3770+ # Page file
3771+ $pageFiles = @ ()
3772+ try {
3773+ $pf = @ (Get-CimInstance - ClassName Win32_PageFileUsage - ErrorAction Stop)
3774+ foreach ($p in $pf ) {
3775+ $pfSizeMB = $p.AllocatedBaseSize
3776+ $pfUsedMB = $p.CurrentUsage
3777+ $pfPctUsed = if ($pfSizeMB -gt 0 ) { [math ]::Round(($pfUsedMB / $pfSizeMB ) * 100 , 1 ) } else { 0 }
3778+ $pfStatus = ' OK'
3779+ if ($pfPctUsed -gt 80 ) { $pfStatus = ' WARN' ; $memIssues ++ }
3780+ $pageFiles += @ {
3781+ Name = $p.Name
3782+ SizeMB = $pfSizeMB
3783+ UsedMB = $pfUsedMB
3784+ PctUsed = $pfPctUsed
3785+ Status = $pfStatus
3786+ }
3787+ }
3788+ } catch { }
3789+
3790+ $memStatus = if ($pctUsed -gt 90 ) { ' CRITICAL' } elseif ($pctUsed -gt 80 ) { ' WARN' } else { ' OK' }
3791+
3792+ # Console output
3793+ Write-OutputColor " ┌────────────────────────────────────────────────────────────────────────┐" - color " Info"
3794+ Write-OutputColor " │$ ( " MEMORY AUDIT - $env: COMPUTERNAME " .PadRight(72 )) │" - color " Info"
3795+ Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" - color " Info"
3796+ $memColor = switch ($memStatus ) { ' OK' { ' Success' } ' WARN' { ' Warning' } ' CRITICAL' { ' Error' } default { ' Info' } }
3797+ Write-OutputColor " │$ ( " Physical RAM: ${usedGB} /${totalPhysicalGB} GB used (${pctUsed} %) [$memStatus ]" .PadRight(72 )) │" - color $memColor
3798+ Write-OutputColor " │$ ( " Available: ${availableGB} GB" .PadRight(72 )) │" - color " Info"
3799+ Write-OutputColor " │$ ( " DIMMs: $ ( @ ($dimms ).Count) installed" .PadRight(72 )) │" - color " Info"
3800+ if (@ ($dimms ).Count -gt 0 ) {
3801+ foreach ($d in $dimms ) {
3802+ $dimmLine = " $ ( $d.DeviceLocator ) : $ ( $d.CapacityGB ) GB @ $ ( $d.Speed ) MHz"
3803+ if ($d.Manufacturer -and $d.Manufacturer -ne ' Unknown' ) { $dimmLine += " ($ ( $d.Manufacturer ) )" }
3804+ if ($dimmLine.Length -gt 70 ) { $dimmLine = $dimmLine.Substring (0 , 67 ) + " ..." }
3805+ Write-OutputColor " │$ ( $dimmLine.PadRight (72 )) │" - color " Info"
3806+ }
3807+ }
3808+ if (@ ($pageFiles ).Count -gt 0 ) {
3809+ Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" - color " Info"
3810+ foreach ($pf in $pageFiles ) {
3811+ $pfColor = if ($pf.Status -eq ' OK' ) { ' Success' } else { ' Warning' }
3812+ $pfLine = " Page File: $ ( $pf.Name ) $ ( $pf.UsedMB ) /$ ( $pf.SizeMB ) MB ($ ( $pf.PctUsed ) %) [$ ( $pf.Status ) ]"
3813+ if ($pfLine.Length -gt 72 ) { $pfLine = $pfLine.Substring (0 , 69 ) + " ..." }
3814+ Write-OutputColor " │$ ( $pfLine.PadRight (72 )) │" - color $pfColor
3815+ }
3816+ }
3817+ Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" - color " Info"
3818+ Write-OutputColor " │$ ( " Issues: $memIssues " .PadRight(72 )) │" - color $ (if ($memIssues -gt 0 ) { " Warning" } else { " Success" })
3819+ Write-OutputColor " └────────────────────────────────────────────────────────────────────────┘" - color " Info"
3820+
3821+ if ($script :CLIOutputFormat -eq ' JSON' ) {
3822+ $jsonResult = @ {
3823+ Tool = $script :ToolFullName
3824+ Version = $script :ScriptVersion
3825+ Action = ' MemoryAudit'
3826+ Timestamp = (Get-Date - Format " yyyy-MM-ddTHH:mm:ss" )
3827+ Hostname = $env: COMPUTERNAME
3828+ Summary = @ {
3829+ TotalGB = $totalPhysicalGB
3830+ UsedGB = $usedGB
3831+ AvailableGB = $availableGB
3832+ PctUsed = $pctUsed
3833+ Status = $memStatus
3834+ DIMMs = @ ($dimms ).Count
3835+ Issues = $memIssues
3836+ }
3837+ DIMMs = $dimms
3838+ PageFiles = $pageFiles
3839+ }
3840+ Write-Output ($jsonResult | ConvertTo-Json - Depth 10 )
3841+ }
3842+
3843+ if ($memIssues -gt 0 ) { [Environment ]::Exit (1 ) }
3844+ }
36073845 default {
36083846 Write-OutputColor " Unknown CLI action: $ ( $script :CLIAction ) " - color " Error"
36093847 [Environment ]::Exit (1 )
0 commit comments