Files
GPOZaurr/Private/Get-XMLGPO.ps1
T
Przemyslaw Klys 26ecff620c fix typo
2025-08-16 08:15:09 +02:00

472 lines
22 KiB
PowerShell

function Get-XMLGPO {
<#
.SYNOPSIS
Retrieves information from an XML representation of a Group Policy Object (GPO).
.DESCRIPTION
This function retrieves various details from an XML representation of a GPO, such as GPO name, domain name, links information, etc.
.PARAMETER XMLContent
The XML content representing the GPO.
.PARAMETER GPO
The Microsoft.GroupPolicy.Gpo object representing the GPO.
.PARAMETER PermissionsOnly
Indicates whether to retrieve only permissions information.
.PARAMETER OwnerOnly
Indicates whether to retrieve only owner information.
.PARAMETER ADAdministrativeGroups
A dictionary of Active Directory administrative groups.
.PARAMETER Splitter
The string used to split values in the output.
.PARAMETER ExcludeGroupPolicies
A dictionary of group policies to exclude.
.PARAMETER Type
An array of types to filter the output.
.PARAMETER LinksSummaryCache
A cache of links summary information.
.EXAMPLE
Get-XMLGPO -XMLContent $xml -GPO $gpo
Description:
Retrieves information from the XML content of a specific GPO.
.EXAMPLE
Get-XMLGPO -XMLContent $xml -GPO $gpo -PermissionsOnly
Description:
Retrieves only the permissions information from the XML content of a specific GPO.
#>
[cmdletBinding()]
param(
[XML] $XMLContent,
[Microsoft.GroupPolicy.Gpo] $GPO,
[switch] $PermissionsOnly,
[switch] $OwnerOnly,
[System.Collections.IDictionary] $ADAdministrativeGroups,
[string] $Splitter = [System.Environment]::NewLine,
[System.Collections.IDictionary] $ExcludeGroupPolicies,
[string[]] $Type,
[System.Collections.IDictionary] $LinksSummaryCache
)
$SysvolGpoPath = "\\$($GPO.DomainName)\SYSVOL\$($GPO.DomainName)\Policies\{$($GPO.ID)}"
$DisplayName = $XMLContent.GPO.Name
$DomainName = $XMLContent.GPO.Identifier.Domain.'#text'
if ($LinksSummaryCache) {
$SearchGUID = -join ($XMLContent.GPO.Identifier.Domain.'#text', $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}')
if ($LinksSummaryCache[$SearchGUID]) {
$Linked = $LinksSummaryCache[$SearchGUID].Linked
$LinksEnabledCount = $LinksSummaryCache[$SearchGUID].LinksEnabledCount
$LinksDisabledCount = $LinksSummaryCache[$SearchGUID].LinksDisabledCount
$LinksTotalCount = $LinksSummaryCache[$SearchGUID].LinksCount
$Links = $LinksSummaryCache[$SearchGUID].Links
$LinksObjects = $LinksSummaryCache[$SearchGUID].LinksObjects
} else {
$Linked = $false
$LinksEnabledCount = 0
$LinksDisabledCount = 0
$LinksTotalCount = 0
$Links = $null
$LinksObjects = $null
}
} else {
if ($XMLContent.GPO.LinksTo) {
$LinkSplit = ([Array] $XMLContent.GPO.LinksTo).Where( { $_.Enabled -eq $true }, 'Split')
[Array] $LinksEnabled = $LinkSplit[0]
[Array] $LinksDisabled = $LinkSplit[1]
$LinksEnabledCount = $LinksEnabled.Count
$LinksDisabledCount = $LinksDisabled.Count
$LinksTotalCount = ([Array] $XMLContent.GPO.LinksTo).Count
if ($LinksEnabledCount -eq 0) {
$Linked = $false
} else {
$Linked = $true
}
$Links = @(
$XMLContent.GPO.LinksTo | ForEach-Object -Process {
if ($_) {
$_.SOMPath
}
}
) -join $Splitter
$LinksObjects = $XMLContent.GPO.LinksTo | ForEach-Object -Process {
if ($_) {
[PSCustomObject] @{
CanonicalName = $_.SOMPath
Enabled = $_.Enabled
NoOverride = $_.NoOverride
}
}
}
} else {
$Linked = $false
$LinksEnabledCount = 0
$LinksDisabledCount = 0
$LinksTotalCount = 0
$Links = $null
$LinksObjects = $null
}
}
# Find proper values for enabled/disabled user/computer settings
if ($XMLContent.GPO.Computer.Enabled -eq 'False') {
$ComputerEnabled = $false
} elseif ($XMLContent.GPO.Computer.Enabled -eq 'True') {
$ComputerEnabled = $true
} else {
Write-Warning "Get-XMLGPO - Computer enabled not set to true or false [$DisplayName/$DomainName]. Weird."
$ComputerEnabled = $null
}
if ($XMLContent.GPO.User.Enabled -eq 'False') {
$UserEnabled = $false
} elseif ($XMLContent.GPO.User.Enabled -eq 'True') {
$UserEnabled = $true
} else {
Write-Warning "Get-XMLGPO - User enabled not set to true or false [$DisplayName/$DomainName] . Weird."
$UserEnabled = $null
}
# Translate Enabled to same as GPO GUI
if ($UserEnabled -eq $True -and $ComputerEnabled -eq $true) {
$EnabledBool = $true
$Enabled = 'Enabled'
} elseif ($UserEnabled -eq $false -and $ComputerEnabled -eq $false) {
$EnabledBool = $false
$Enabled = 'All settings disabled'
} elseif ($UserEnabled -eq $true -and $ComputerEnabled -eq $false) {
$EnabledBool = $True
$Enabled = 'Computer configuration settings disabled'
} elseif ($UserEnabled -eq $false -and $ComputerEnabled -eq $true) {
$EnabledBool = $True
$Enabled = 'User configuration settings disabled'
}
# This is kind of old way of doing things, but it's superseded by other way below
[bool] $ComputerSettingsAvailable = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
[bool] $UserSettingsAvailable = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
if ($ComputerSettingsAvailable -eq $false -and $UserSettingsAvailable -eq $false) {
$NoSettings = $true
} else {
$NoSettings = $false
}
# $OutputUser = $XMLContent.GPO.User.ExtensionData.Extension | Where-Object { $_.PSObject.Properties.TypeNameOfValue -in 'System.Xml.XmlElement', 'System.Object[]' }
# $OutputComputer = $XMLContent.GPO.Computer.ExtensionData.Extension | Where-Object { $_.PSObject.Properties.TypeNameOfValue -in 'System.Xml.XmlElement', 'System.Object[]' }
# This is additional check we do for error check to prevent false-positives for EMPTY on non-english language
$PreCheckOutputUser = $false
$PreCheckOutputComputer = $false
foreach ($Extension in $XMLContent.GPO.User.ExtensionData) {
if ($Extension.Error) {
$PreCheckOutputUser = $true
}
}
foreach ($Extension in $XMLContent.GPO.Computer.ExtensionData) {
if ($Extension.Error) {
$PreCheckOutputComputer = $true
}
}
if ($PreCheckOutputComputer -eq $true -or $PreCheckOutputUser -eq $true) {
# in some cases GPResult seems to return an error - this was first noticed by user when using Dutch based system
# I am not sure if it's possible to fix this error for users, but once that happens checking if GPO is empty fails using the method below
# therefore we will use the old method of assuming something is empty or not empty in such case
Write-Warning "Get-XMLGPO - Reading GPO content [$DisplayName/$DomainName] returned an error. This may be because of non-english language. Assessing EMPTY using old method which can report false positives. Be careful please."
$OutputUser = @()
$OutputComputer = @()
} else {
[Array] $OutputUser = foreach ($ExtensionType in $XMLContent.GPO.User.ExtensionData.Extension) {
if ($ExtensionType) {
$GPOSettingTypeSplit = ($ExtensionType.type -split ':')
try {
$KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') }
} catch {
Write-Warning "Get-XMLGPO - things went sideways [$DisplayName/$DomainName]. Error $($_.Exception.Message)"
continue
}
}
$KeysToLoop
}
[Array] $OutputComputer = foreach ($ExtensionType in $XMLContent.GPO.Computer.ExtensionData.Extension) {
if ($ExtensionType) {
$GPOSettingTypeSplit = ($ExtensionType.type -split ':')
try {
$KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') }
} catch {
Write-Warning "Get-XMLGPO - things went sideways [$DisplayName/$DomainName]. Error $($_.Exception.Message)"
continue
}
}
$KeysToLoop
}
[bool] $ComputerSettingsAvailable = if ($OutputComputer.Count -gt 0) { $true } else { $false }
[bool] $UserSettingsAvailable = if ($OutputUser.Count -gt 0) { $true } else { $false }
}
# Check if there are any GPF files in the GPO
# those are special files that are used to store settings for some applications (maily Citrix?)
# if there are any, then we can't say that GPO is empty, and they are not visible in the XML
$GPFFile = $false
$FilesCount = 0
$TotalSize = 0
Get-ChildItem -LiteralPath $SysvolGpoPath -Recurse -ErrorAction SilentlyContinue -File | ForEach-Object {
if ($_.Extension -eq '.gpf') {
#Write-Warning -Message "Get-XMLGPO - GPO [$DisplayName/$DomainName] has no data in XML, but it contains GPF files. Excluding from empty GPO list."
$GPFFile = $true
}
$FilesCount++
$TotalSize += $_.Length
}
if ($ComputerSettingsAvailable -eq $false -and $UserSettingsAvailable -eq $false -and $GPFFile -eq $false) {
$Empty = $true
} else {
$Empty = $false
}
$ComputerProblem = $false
if ($ComputerEnabled -eq $true -and $ComputerSettingsAvailable -eq $true) {
$ComputerOptimized = $true
} elseif ($ComputerEnabled -eq $true -and $ComputerSettingsAvailable -eq $false) {
$ComputerOptimized = $false
} elseif ($ComputerEnabled -eq $false -and $ComputerSettingsAvailable -eq $false) {
$ComputerOptimized = $true
} else {
# Enabled $false, but ComputerData is there.
$ComputerOptimized = $false
$ComputerProblem = $true
}
$UserProblem = $false
if ($UserEnabled -eq $true -and $UserSettingsAvailable -eq $true) {
$UserOptimized = $true
} elseif ($UserEnabled -eq $true -and $UserSettingsAvailable -eq $false) {
$UserOptimized = $false
} elseif ($UserEnabled -eq $false -and $UserSettingsAvailable -eq $false) {
$UserOptimized = $true
} else {
# Enabled $false, but UserData is there.
$UserOptimized = $false
$UserProblem = $true
}
if ($UserProblem -or $ComputerProblem) {
$Problem = $true
} else {
$Problem = $false
}
if ($UserOptimized -and $ComputerOptimized) {
$Optimized = $true
} else {
$Optimized = $false
}
if (-not $PermissionsOnly) {
if ($ADAdministrativeGroups -and $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text') {
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text')"]
$WellKnown = ConvertFrom-SID -SID $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text' -OnlyWellKnown
if ($AdministrativeGroup) {
$OwnerType = 'Administrative'
} elseif ($WellKnown.Name) {
$OwnerType = 'WellKnown'
} else {
$OwnerType = 'NotAdministrative'
}
} elseif ($ADAdministrativeGroups) {
$OwnerType = 'Unknown'
} else {
$OwnerType = 'Unable to assess (local files?)'
}
}
# Mark GPO as excluded
$Exclude = $false
if ($ExcludeGroupPolicies) {
$GUID = $XMLContent.GPO.Identifier.Identifier.'#text'
$GUIDWithOutBrackets = $GUID.Replace('{', '').Replace('}', '')
$PolicyWithDomain = -join ($XMLContent.GPO.Identifier.Domain.'#text', $XMLContent.GPO.Name)
$PolicyWithDomainID = -join ($XMLContent.GPO.Identifier.Domain.'#text', $GUID)
$PolicyWithDomainIDWithoutBrackets = -join ($XMLContent.GPO.Identifier.Domain.'#text', $GUIDWithOutBrackets)
if ($ExcludeGroupPolicies[$XMLContent.GPO.Name] -or
$ExcludeGroupPolicies[$PolicyWithDomain] -or
$ExcludeGroupPolicies[$PolicyWithDomainID] -or
$ExcludeGroupPolicies[$GUID] -or
$ExcludeGroupPolicies[$GUIDWithOutBrackets] -or
$ExcludeGroupPolicies[$PolicyWithDomainIDWithoutBrackets]
) {
$Exclude = $true
}
}
if ($PermissionsOnly) {
$GPOOutput = [PsCustomObject] @{
'DisplayName' = $XMLContent.GPO.Name
'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text'
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}'
'Enabled' = $Enabled
'Name' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text'
'Sid' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text'
#'SidType' = if (($XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text').Length -le 10) { 'WellKnown' } else { 'Other' }
'PermissionType' = 'Allow'
'Inherited' = $false
'Permissions' = 'Owner'
'GPODistinguishedName' = $GPO.Path
'GPOSysvolPath' = $SysvolGpoPath
}
$XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
if ($_) {
[PsCustomObject] @{
'DisplayName' = $XMLContent.GPO.Name
'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text'
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}'
'Enabled' = $Enabled
'Name' = $_.trustee.name.'#Text'
'Sid' = $_.trustee.SID.'#Text'
#'SidType' = if (($XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text').Length -le 10) { 'WellKnown' } else { 'Other' }
'PermissionType' = $_.type.PermissionType
'Inherited' = if ($_.Inherited -eq 'false') { $false } else { $true }
'Permissions' = $_.Standard.GPOGroupedAccessEnum
'GPODistinguishedName' = $GPO.Path
}
}
}
} elseif ($OwnerOnly) {
$GPOOutput = [PsCustomObject] @{
'DisplayName' = $XMLContent.GPO.Name
'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text'
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}'
'Enabled' = $Enabled
'Owner' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text'
'OwnerSID' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text'
'OwnerType' = $OwnerType
'GPODistinguishedName' = $GPO.Path
'GPOSysvolPath' = $SysvolGpoPath
}
} else {
$GPOOutput = [PsCustomObject] @{
'DisplayName' = $XMLContent.GPO.Name
'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text'
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}'
'Days' = (New-TimeSpan -Start ([DateTime] $XMLContent.GPO.ModifiedTime) -End (Get-Date)).Days
'Empty' = $Empty
'Linked' = $Linked
'Enabled' = $EnabledBool
'Optimized' = $Optimized
'Problem' = $Problem
'ApplyPermission' = $null
'Exclude' = $Exclude
'SizeMB' = [Math]::Round($TotalSize / 1MB, 2)
'Size' = $TotalSize
'Description' = $GPO.Description
'ComputerPolicies' = $XMLContent.GPO.Computer.ExtensionData.Name -join ", "
'UserPolicies' = $XMLContent.GPO.User.ExtensionData.Name -join ", "
'FilesCount' = $FilesCount
'LinksCount' = $LinksTotalCount
'LinksEnabledCount' = $LinksEnabledCount
'LinksDisabledCount' = $LinksDisabledCount
'EnabledDetails' = $Enabled
'ComputerProblem' = $ComputerProblem
'ComputerOptimized' = $ComputerOptimized
'UserProblem' = $UserProblem
'UserOptimized' = $UserOptimized
'ComputerSettingsAvailable' = $ComputerSettingsAvailable
'UserSettingsAvailable' = $UserSettingsAvailable
#'ComputerSettingsAvailableReal' = $ComputerSettingsAvailableReal
#'UserSettingsAvailableReal' = $UserSettingsAvailableReal
'ComputerSettingsTypes' = $OutputComputer.Name -join ", "
'UserSettingsTypes' = $OutputUser.Name -join ", "
'ComputerEnabled' = $ComputerEnabled
'UserEnabled' = $UserEnabled
'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
'ComputerSettingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false }
'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension
'UserSettingsStatus' = if ($XMLContent.GPO.User.VersionDirectory -eq 0 -and $XMLContent.GPO.User.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false }
'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension
'NoSettings' = $NoSettings
'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime
'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime
'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime
'WMIFilter' = $GPO.WmiFilter.name
'WMIFilterDescription' = $GPO.WmiFilter.Description
'GPODistinguishedName' = $GPO.Path
'GPOSysvolPath' = $SysvolGpoPath
'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' }
'Owner' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text'
'OwnerSID' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text'
'OwnerType' = $OwnerType
'ACL' = @(
[PsCustomObject] @{
'Name' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text'
'Sid' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text'
'PermissionType' = 'Allow'
'Inherited' = $false
'Permissions' = 'Owner'
}
$XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
if ($_) {
[PsCustomObject] @{
'Name' = $_.trustee.name.'#Text'
'Sid' = $_.trustee.SID.'#Text'
'PermissionType' = $_.type.PermissionType
'Inherited' = if ($_.Inherited -eq 'false') { $false } else { $true }
'Permissions' = $_.Standard.GPOGroupedAccessEnum
}
}
}
)
'Auditing' = if ($XMLContent.GPO.SecurityDescriptor.AuditingPresent.'#text' -eq 'true') { $true } else { $false }
'Links' = $Links
'LinksObjects' = $LinksObjects
'GPOObject' = $GPO
}
if ($GPOOutput.ACL) {
$GPOOutput.ApplyPermission = $false
foreach ($Permission in $GPOOutput.ACL) {
if ($Permission.Permissions -eq 'Apply Group Policy') {
$GPOOutput.ApplyPermission = $true
}
}
}
}
if ($PermissionsOnly -or $OwnerOnly) {
$GPOOutput
} else {
if (-not $Type -or $Type -contains 'All') {
$GPOOutput
} else {
if ($Type -contains 'Empty') {
if ($GPOOutput.Empty -eq $true) {
$GPOOutput
}
}
if ($Type -contains 'Unlinked') {
if ($GPOOutput.Linked -eq $false) {
$GPOOutput
}
}
if ($Type -contains 'Disabled') {
if ($GPOOutput.Enabled -eq $false) {
$GPOOutput
}
}
if ($Type -contains 'NoApplyPermission') {
if ($GPOOutput.ApplyPermission -eq $false) {
$GPOOutput
}
}
}
}
}