Scripts/New-GPOCache.ps1
|
function New-GPOCache { [CmdletBinding()] param( [string]$Domain = $env:USERDNSDOMAIN, [string]$CachePath = "$env:ProgramData\GPOCache", [ValidateRange(1,50)] [int]$Threads = 10 ) $CacheVersion = 1 Import-Module GroupPolicy -ErrorAction Stop if (!(Test-Path $CachePath)) { New-Item ` -Path $CachePath ` -ItemType Directory ` -Force | Out-Null } Write-Host "Enumerating GPOs..." -ForegroundColor Cyan $GPOs = Get-GPO -All -Domain $Domain if ($GPOs.Count -eq 0) { throw "No GPOs found." } # # Manifest # $Manifest = foreach ($GPO in $GPOs) { [PSCustomObject]@{ Guid = $GPO.Id.Guid DisplayName = $GPO.DisplayName ModificationTime = $GPO.ModificationTime } } $Manifest | ConvertTo-Json -Depth 5 | Set-Content ( Join-Path $CachePath "gpos.json" ) -Encoding UTF8 # # Parallel XML generation # $Pool = [RunspaceFactory]::CreateRunspacePool(1,$Threads) $Pool.Open() $Jobs = @() foreach ($GPO in $GPOs) { $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 ` -ErrorAction Stop }) [void]$PS.AddArgument($GPO.Id) [void]$PS.AddArgument($XmlFile) $Jobs += [PSCustomObject]@{ PS = $PS Handle = $PS.BeginInvoke() } } do { $Completed = ($Jobs | Where-Object { $_.Handle.IsCompleted }).Count Write-Progress ` -Activity "Generating GPO XML Cache" ` -Status "$Completed of $($Jobs.Count)" ` -PercentComplete ( ($Completed / $Jobs.Count) * 100 ) Start-Sleep -Milliseconds 500 } until ($Completed -eq $Jobs.Count) foreach ($Job in $Jobs) { try { $Job.PS.EndInvoke( $Job.Handle ) } finally { $Job.PS.Dispose() } } $Pool.Close() $Pool.Dispose() # # Build Search Index # Write-Host "Building SearchIndex..." ` -ForegroundColor Cyan $IgnoredNodes = @( "ReadTime", "Changed", "Identifier", "FilterDataAvailable", "VersionDirectory" ) $Index = New-Object System.Collections.Generic.List[object] $Count = 0 foreach ($GPO in $Manifest) { $Count++ Write-Progress ` -Activity "Building Search Index" ` -Status "$Count of $($Manifest.Count)" ` -PercentComplete ( ($Count / $Manifest.Count) * 100 ) $XmlFile = Join-Path ` $CachePath ` "$($GPO.Guid).xml" if (!(Test-Path $XmlFile)) { continue } try { [xml]$Xml = Get-Content ` $XmlFile ` -Raw # # Index GPO Name # $Index.Add( [PSCustomObject]@{ Guid = $GPO.Guid DisplayName = $GPO.DisplayName MatchType = "GPOName" Value = $GPO.DisplayName } ) # # Index XML Content # foreach ( $Node in $Xml.SelectNodes("//*") ) { if ( $Node.Name -in $IgnoredNodes ) { continue } if( !$Node.InnerText ){ continue } $Value = $Node.InnerText.Trim() if ( [string]::IsNullOrWhiteSpace( $Value ) ) { continue } $Index.Add( [PSCustomObject]@{ Guid = $GPO.Guid DisplayName = $GPO.DisplayName MatchType = $Node.Name Value = $Value } ) } } catch { Write-Warning $XmlFile } } $Index | ConvertTo-Json -Depth 6 | Set-Content ( Join-Path $CachePath "SearchIndex.json" ) -Encoding UTF8 # # Metadata # [PSCustomObject]@{ CacheVersion = $CacheVersion CreatedDate = Get-Date Domain = $Domain GPOCount = $GPOs.Count IndexCount = $Index.Count } | ConvertTo-Json -Depth 5 | Set-Content ( Join-Path $CachePath "cache.json" ) -Encoding UTF8 Write-Progress ` -Activity "Building Search Index" ` -Completed Write-Host "" Write-Host "Cache build completed." ` -ForegroundColor Green } |