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 } } } } }