Private/GroupPolicy/Helpers.ps1
|
function Get-PSIGpoSchema { [ordered]@{ gpos=@('Group Policy Objects',@('Name','GPO ID','Domain','Owner','Status','Created','Changed','Description')) versions=@('Configuration versions',@('Name','GPO ID','User AD version','User SYSVOL version','Computer AD version','Computer SYSVOL version')) containers=@('Link containers',@('Name','Type','DN','Block inheritance','Options raw','Link entries observed','Selected-domain links')) links=@('GPO links',@('Name','GPO ID','Container','Container type','Container DN','GPO DN','Link order','Enabled','Enforced','Options raw')) permissions=@('Reported GPO permissions',@('Name','GPO ID','Trustee','Trustee domain','Trustee SID','Trustee type','Permission','Inherited')) wmiFilters=@('Domain WMI filters',@('Name','Filter ID','Author','Description','Query definition raw','DN')) wmiReferences=@('GPO WMI references',@('Name','GPO ID','Filter reference raw','Filter ID','Filter domain')) settings=@('Settings structure',@('Name','GPO ID','Side','Extension','Setting types','Top-level setting nodes')) objectQueries=@('Object collection',@('Name','GPO ID','Target','Operation','Status','Record count','Duration ms','Error ID','Message','Collected at')) collection=@('Category collection',@('Name','Status','Record count','Method','Collected at','Duration ms','Error ID','Message')) } } function ConvertFrom-PSIGpoLinks([string]$Value,[string]$Container,[string]$Type,[string]$DN,[string]$DomainDN){ if([string]::IsNullOrEmpty($Value)){return} $tokens=[regex]::Matches($Value,'\[LDAP://(?<dn>[^\]]+);(?<options>\d+)\]','IgnoreCase') if(($tokens|ForEach-Object{$_.Value}) -join '' -ine $Value){throw 'Malformed gpLink value; link order cannot be determined.'} for($i=0;$i -lt $tokens.Count;$i++){ $gpc=$tokens[$i].Groups['dn'].Value;$bits=[int]$tokens[$i].Groups['options'].Value $idMatch=[regex]::Match($gpc,('(?i)^CN=\{(?<id>[0-9a-f-]{36})\},CN=Policies,CN=System,'+[regex]::Escape($DomainDN)+'$')) if(-not $idMatch.Success){continue} $id=([guid]$idMatch.Groups['id'].Value).ToString() [pscustomobject]@{Name=$Container+' / '+$id;'GPO ID'=$id;Container=$Container;'Container type'=$Type;'Container DN'=$DN;'GPO DN'=$gpc;'Link order'=$tokens.Count-$i;Enabled=($bits -band 1) -eq 0;Enforced=($bits -band 2) -ne 0;'Options raw'=$bits} } } function ConvertFrom-PSIGpoXml([string]$Xml,[string]$Id,[string]$Name){ $settings=New-Object xml.XmlReaderSettings;$settings.DtdProcessing=[xml.DtdProcessing]::Prohibit;$settings.XmlResolver=$null $stringReader=New-Object IO.StringReader($Xml);$reader=$null try{ $reader=[xml.XmlReader]::Create($stringReader,$settings);$doc=New-Object xml.XmlDocument;$doc.XmlResolver=$null;$doc.Load($reader) $root=$doc.DocumentElement if($root.LocalName -ne 'GPO'){throw 'Expected a GPO XML report.'} $identifier=$root.SelectSingleNode("*[local-name()='Identifier']/*[local-name()='Identifier']") if($null -eq $identifier -or ([guid]$identifier.InnerText).ToString() -ne ([guid]$Id).ToString()){throw 'Report GUID does not match the requested GPO.'} foreach($side in @('User','Computer')){ foreach($ext in $root.SelectNodes("*[local-name()='$side']/*[local-name()='ExtensionData']")){ $label=$ext.SelectSingleNode("*[local-name()='Name']");$extension=$ext.SelectSingleNode("*[local-name()='Extension']") $nodes=@();if($null -ne $extension){$nodes=@($extension.ChildNodes|Where-Object{$_.NodeType -eq [xml.XmlNodeType]::Element})} [pscustomobject]@{Name=$Name;'GPO ID'=$Id;Side=$side;Extension=if($null -ne $label){$label.InnerText}else{$null};'Setting types'=(@($nodes|ForEach-Object{$_.LocalName}|Sort-Object -Unique) -join '; ');'Top-level setting nodes'=$nodes.Count} } } }finally{if($null -ne $reader){$reader.Dispose()};$stringReader.Dispose()} } function ConvertTo-PSIGpoModel($Context,[object[]]$Envelopes){ $schema=Get-PSIGpoSchema;$data=[ordered]@{};$coverage=[ordered]@{} foreach($k in $schema.Keys){$data[$k]=@()} $seen=@{} foreach($e in $Envelopes){ $k=[string]$e.Key if(-not $schema.Contains($k) -or $k -eq 'collection' -or $seen.ContainsKey($k)){throw "Unknown/duplicate GPO category: $k"} if($e.Status -notin @('Succeeded','Partial','Failed','TimedOut','NotAttempted')){throw "Invalid status for $k"} $seen[$k]=$true;$coverage[$k]=$e.Status -eq 'Succeeded' if($e.Status -in @('Succeeded','Partial')){ $data[$k]=@($e.Records|Where-Object{$null -ne $_}|ForEach-Object{$row=[ordered]@{};foreach($c in $schema[$k][1]){$row[$c]=Get-PSISiteValue $_ $c};[pscustomobject]$row}) } $data.collection+= [pscustomobject]@{Name=$schema[$k][0];Status=$e.Status;'Record count'=$data[$k].Count;Method='Pinned DC / AD and GroupPolicy';'Collected at'=$e.CollectedAt;'Duration ms'=$e.DurationMs;'Error ID'=$e.ErrorId;Message=$e.ErrorMessage} } foreach($k in $schema.Keys){if($k -ne 'collection' -and -not $seen.ContainsKey($k)){throw "Missing GPO category: $k"}} $coverage.collection=$true [pscustomobject]@{Schema=$schema;Data=$data;Coverage=$coverage;Raw=[ordered]@{}} } |