Powershell GPO XML to CSV Parser/Transformer

Posted on: October 17, 2016 Posted by: Philipp Comments: 0

Powershell GPO XML to CSV Parser/Transformer

Wenn mittes Powershell GPO´s ausgewertet oder verglichen werden sollen dann entsteht eine großen Herausforderung.
Die von Microsoft gelieferten XML Daten sind grütze. Es ist nicht möglich Vergleiche oder Reports out of the box zu erstellen.
Genau das war eine Anforderungen in einem Projekt. Eine hohe Anzahl an Domänen mit einigen hundert GPO´s sollten analysiert, verglichen, konsolidieren und anschließend restrukturiert werden.

UPDATE DEZEMBER 2019: Es gibt eine bessere Möglichkeit! Wird hier auf dem Blog irgendwann mal veröffentlich..

Da ich im Internet aber auch so gar nichts brauchbares gefunden habe. Hier die Lösung:

Das Skript ermittelt alle Einstellungen und gruppiert die zusammengehörigen Elemente.
Derzeit werden selbstgeschriebene ADM Settings (ja sowas gibt es noch) ignoriert.

Dieses Skript ist die Kernkomponente eines sehr umfangreichen Tools welches ich entwickelt habe. Das Tool ist in der Lage GPO´s welche auf eine Quell-OU verlinkt sind mit Analyse der Rechte ob deny/Gpo Apply angewandt wird, gegen eine Ziel-OU zu vergleichen und die Unterschiede aufzuzeigen und das auch Domänenübergreifend. Das bedeutet es kann ermittelt werden welche Gruppenrichtline in der neuen OU wirken und ob Einstellungen fehlen. Dieses Skript kann gerne per E-Mail angefordert werden.

function get-gpoxmldata{
param([string]$guid)
$globalCount = 0
$global:output = @()
function run-recurseinfo
{
param ($Name,$Node)
$Definitions = $Node | Get-Member -MemberType Properties | where { $_.Definition -like "System*" }
$ob = ($Node | Get-Member -MemberType Properties | where { $_.Definition -like "string*" }).Name
foreach ($o in $ob)
{
if ($o -eq "xmlns") { break }
if ($o -eq "#text") { $Path = $Name }
else { $path = "$($Name)\$($o)" }
write-db -objcount $globalCount -Path $path -Value $Node.$o
}
foreach ($Definition in $Definitions)
{
checknode $Node -Name ("$($Name)\$($Definition.Name)") -Node $Node.($Definition.Name)
}
}
function checknode
{
param($Node,$Name,$direct)
$NodeCount = ($Node | Measure-Object).count
if ($NodeCount -gt 1)
{
for ($j = 0; $j -lt $NodeCount; $j++)
{
if ($direct -eq $true){$globalCount++}
$ob = ($Node[$j] | Get-Member -MemberType Properties | where { $_.Definition -like "string*" }).Name
foreach ($o in $ob)
{
if ($o -eq "xmlns") { break }
if ($o -eq "#text") { $Path = $Name }
else { $path = "$($Name)\$($o)" }
write-db -objcount $globalCount -Path $path -Value $Node[$j].$o
}
try {$Definitions = $Node[$j] | get-Member  -MemberType Properties -ErrorAction Stop}
catch{$Definitions = $null}
foreach ($Definition in $Definitions)
{
if ($Definition.Name -notlike "#text")
{
run-recurseinfo -Name ("$($Name)\$($Definition.Name)") -Node $Node[$j].($Definition.Name)
}
}

}
}
else {run-recurseinfo -Name $Name -Node $Node }
}
function write-db
{
param($GPOKey,$objcount,$Path,[string]$Value)
$Value = $Value.replace("'","''")
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name ObjNr -Value $objcount
Add-Member -InputObject $object -MemberType NoteProperty -Name Path -Value $Path
Add-Member -InputObject $object -MemberType NoteProperty -Name Value -Value $Value
$global:output += $object
}
function group-result{
param($Array)
$outputfunc = @()
$lastrunObj = $Null
$count = 0
foreach ($obj in $Array)
{
$spath = ([System.Text.RegularExpressions.Regex]::replace($obj.Path,"^([\w]+)(\\)([\w]+)(\\).+",'${1}${2}${3}'))
if ($obj.ObjNr -ne $lastrunObj -or $lastrunpath -ne $spath )
{$count++}
if ($obj.Value)
{
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name Group -Value $count
Add-Member -InputObject $object -MemberType NoteProperty -Name Path -Value $obj.Path
Add-Member -InputObject $object -MemberType NoteProperty -Name Value -Value $obj.Value
$outputfunc += $object
$lastrunObj = $obj.ObjNr
$lastrunpath = $spath
}
}
return $outputfunc
}
try{ Import-Module GroupPolicy -ErrorAction stop}
catch{"Could not Import Module"
break}
$XML = Get-GPOReport -Guid $guid -ReportType xml
$ComputerGPO = $XML.GPO.Computer.ExtensionData
$ComputerNodes = $ComputerGPO.Name
$UserGPO = $XML.GPO.User.ExtensionData
$UserNodes = $UserGPO.Name
if  ($XML.GPO.Computer.ExtensionData)
{
#Region Prüfen ob XML-Rückgabe ein Array ist #0
#Analysieren ob der zurückgelieferte Wert ein Array ist oder nicht
if (($ComputerGPO.extension | Measure-Object).count -gt 1)
{$multivalue = $true}
else {$multivalue = $false}
#Wenn ein Array dann wird die Variable $ScriptNodes mit allen Werten gefüllt
if ($multivalue -eq $true )
{
$count = 0
$ScriptNodes = @()
foreach ($Node in $ComputerNodes)
{

$obj = $ComputerGPO[$count].extension | Get-Member -MemberType Properties | where {$_.name -notlike "type" -and $_.name -notlike "q*" }
$ScriptNodes += $obj | foreach{"$($_.Name)"}
$count++
}
}
#Wenn kein Array dann wird die Variable $ScriptNodes mit den Werten des Objekt gefüllt
if ($multivalue -eq $false)
{
$ScriptNodes = @()
$obj = $ComputerGPO.extension | Get-Member -MemberType Properties -ErrorAction Stop | where {$_.name -notlike "type" -and $_.name -notlike "q*" }
$ScriptNodes += $obj | foreach{"$($_.Name)"}
}
#EndRegion
#Region Für jeden in #0 gefunden Wert die Überprüfung starten
$ScriptNodes | foreach {checknode -Name "Computer\$($_)" -Node $ComputerGPO.Extension.$_ -direct $true}
#EndRegion
}
if  ($XML.GPO.User.ExtensionData)
{
#Region Prüfen ob XML-Rückgabe ein Array ist #0
#Analysieren ob der zurückgelieferte Wert ein Array ist oder nicht
if (($UserGPO.extension | Measure-Object).count -gt 1)
{$multivalue = $true}
else {$multivalue = $false}
#Wenn ein Array dann wird die Variable $ScriptNodes mit allen Werten gefüllt
if ($multivalue -eq $true )
{
$count = 0
$ScriptNodes = @()
foreach ($Node in $UserNodes)
{
$obj = $UserGPO[$count].extension | Get-Member -MemberType Properties | where {$_.name -notlike "type" -and $_.name -notlike "q*" }
$ScriptNodes += $obj | foreach{"$($_.Name)"}
$count++
}
}
#Wenn kein Array dann wird die Variable $ScriptNodes mit den Werten des Objekt gefüllt
if ($multivalue -eq $false)
{
$ScriptNodes = @()
$obj = $UserGPO.extension | Get-Member -MemberType Properties -ErrorAction Stop | where {$_.name -notlike "type" -and $_.name -notlike "q*" }
$ScriptNodes += $obj | foreach{"$($_.Name)"}
}
#EndRegion
#Region Für jeden in #0 gefunden Wert die Überprüfung starten
$ScriptNodes | foreach {checknode -Name "User\$($_)" -Node $UserGPO.Extension.$_ -direct $true}
#EndRegion

}
group-result $global:output
}
Export-ModuleMember  -Function get-gpoxmldata