Scripts/Update-GPOCache.ps1
|
function Update-GPOCache { [CmdletBinding()] param( [string]$Domain = $env:USERDNSDOMAIN, [string]$CachePath = "$env:ProgramData\GPOCache", [ValidateRange(1,50)] [int]$Threads = 10 ) Import-Module GroupPolicy -ErrorAction Stop $ManifestFile = Join-Path $CachePath "gpos.json" $IndexFile = Join-Path $CachePath "SearchIndex.json" $CacheFile = Join-Path $CachePath "cache.json" if ( !(Test-Path $ManifestFile) -or !(Test-Path $IndexFile) ) { Write-Warning "Cache missing. Building full cache." New-GPOCache ` -Domain $Domain ` -CachePath $CachePath ` -Threads $Threads return } # # Existing data # $OldManifest = Get-Content ` $ManifestFile ` -Raw | ConvertFrom-Json $SearchIndex = Get-Content ` $IndexFile ` -Raw | ConvertFrom-Json # # Current domain data # $CurrentGPOs = Get-GPO -All -Domain $Domain # # Fast lookups # $OldLookup = @{} foreach ($Entry in $OldManifest) { $OldLookup[$Entry.Guid] = $Entry } $ChangedGPOs = @() # # Detect New/Changed # foreach ($GPO in $CurrentGPOs) { $Guid = $GPO.Id.Guid if ( !$OldLookup.ContainsKey($Guid) ) { $ChangedGPOs += $GPO continue } if ( [datetime]$OldLookup[$Guid].ModificationTime ` -lt ` $GPO.ModificationTime ) { $ChangedGPOs += $GPO } } # # Detect Deleted # $CurrentGuids = $CurrentGPOs.Id.Guid $DeletedGuids = $OldManifest | Where-Object { $_.Guid -notin $CurrentGuids } | Select-Object -ExpandProperty Guid Write-Host "" Write-Host "Changes detected:" ` -ForegroundColor Cyan Write-Host " New/Changed : $($ChangedGPOs.Count)" Write-Host " Deleted : $($DeletedGuids.Count)" Write-Host "" # # Remove deleted XML files # foreach ($Guid in $DeletedGuids) { $XmlFile = Join-Path ` $CachePath ` "$Guid.xml" if ( Test-Path $XmlFile ) { Remove-Item ` $XmlFile ` -Force } } # # Remove deleted entries # if ($DeletedGuids.Count) { $SearchIndex = $SearchIndex | Where-Object { $_.Guid -notin $DeletedGuids } } # # Nothing changed? # if ( $ChangedGPOs.Count -eq 0 ` -and ` $DeletedGuids.Count -eq 0 ) { Write-Host ` "Cache already up to date." ` -ForegroundColor Green return } # # Runspace Pool # $Pool = [RunspaceFactory]::CreateRunspacePool( 1, $Threads ) $Pool.Open() $Jobs = @() foreach ( $GPO in $ChangedGPOs ) { $XmlFile = Join-Path ` $CachePath ` "$($GPO.Id.Guid).xml" $PS = [PowerShell]::Create() $PS.RunspacePool = $Pool [void]$PS.AddScript({ param( $Guid, $XmlFile ) Import-Module GroupPolicy Get-GPOReport ` -Guid $Guid ` -ReportType Xml ` -Path $XmlFile }) [void]$PS.AddArgument( $GPO.Id ) [void]$PS.AddArgument( $XmlFile ) $Jobs += [PSCustomObject]@{ PS = $PS Handle = $PS.BeginInvoke() } } # # Progress # do { $Completed = ( $Jobs | Where-Object { $_.Handle.IsCompleted } ).Count Write-Progress ` -Activity "Updating GPO Cache" ` -Status "$Completed of $($Jobs.Count)" ` -PercentComplete ( ($Completed / [Math]::Max($Jobs.Count,1)) * 100 ) Start-Sleep -Milliseconds 500 } until ( $Completed -eq $Jobs.Count ) # # Cleanup # foreach ($Job in $Jobs) { try { $Job.PS.EndInvoke( $Job.Handle ) } finally { $Job.PS.Dispose() } } $Pool.Close() $Pool.Dispose() # # Remove old index entries # $ChangedIds = $ChangedGPOs.Id.Guid $SearchIndex = $SearchIndex | Where-Object { $_.Guid -notin $ChangedIds } # # Rebuild only changed entries # $IgnoredNodes = @( "ReadTime", "Changed", "Identifier", "FilterDataAvailable", "VersionDirectory" ) foreach ( $GPO in $ChangedGPOs ) { $XmlFile = Join-Path ` $CachePath ` "$($GPO.Id.Guid).xml" try { [xml]$Xml = Get-Content ` $XmlFile ` -Raw # # GPO name # $SearchIndex += [PSCustomObject]@{ Guid = $GPO.Id.Guid DisplayName = $GPO.DisplayName MatchType = "GPOName" Value = $GPO.DisplayName } foreach ( $Node in $Xml.SelectNodes("//*") ) { if ( $Node.Name -in $IgnoredNodes ) { continue } if ( !$Node.InnerText ) { continue } $Value = $Node.InnerText.Trim() if ( [string]::IsNullOrWhiteSpace( $Value ) ) { continue } $SearchIndex += [PSCustomObject]@{ Guid = $GPO.Id.Guid DisplayName = $GPO.DisplayName MatchType = $Node.Name Value = $Value } } } catch { Write-Warning ` "Failed indexing $($GPO.DisplayName)" } } # # Save Search Index # $SearchIndex | ConvertTo-Json -Depth 8 | Set-Content ` $IndexFile ` -Encoding UTF8 # # Save Manifest # $NewManifest = foreach ( $GPO in $CurrentGPOs ) { [PSCustomObject]@{ Guid = $GPO.Id.Guid DisplayName = $GPO.DisplayName ModificationTime = $GPO.ModificationTime } } $NewManifest | ConvertTo-Json -Depth 5 | Set-Content ` $ManifestFile ` -Encoding UTF8 # # Update metadata # $Metadata = [PSCustomObject]@{ CacheVersion = 1 CreatedDate = Get-Date Domain = $Domain GPOCount = $CurrentGPOs.Count IndexCount = $SearchIndex.Count } $Metadata | ConvertTo-Json | Set-Content ` $CacheFile ` -Encoding UTF8 Write-Progress ` -Activity "Updating GPO Cache" ` -Completed Write-Host "" Write-Host ` "Incremental cache update completed." ` -ForegroundColor Green } |