PoshCode Archive  Artifact [5fabbcc932]

Artifact 5fabbcc932e08d7effa869bef9c0c0c20e3377193dc1cc8f1c61ca383cd31df8:

  • File Parse-GPO-Settings.ps1 — part of check-in [cf75eea8c9] at 2018-06-10 14:09:35 on branch trunk — The upload is not complete, lines after line 1738 are missing :-( (user: Atamido size: 65875)

# encoding: ascii
# api: powershell
# title: Parse-GPO-Settings
# description: The upload is not complete, lines after line 1738 are missing :-(
# version: 802.1
# type: function
# author: Atamido
# license: CC0
# function: Replace-ExtraLines
# x-poshcode-id: 6173
# x-archived: 2016-01-12T14:44:18
# x-published: 2016-01-11T07:47:00
#
#
#  Reads all of the GPOs out of the domain, parses them, and then sends the output of settings to a GridView

#  Reduces the total lines in a string to 9, because out-gridview doesn't like more than 9 lines in an entry
Function Replace-ExtraLines
{
    Param($Entries)

    ForEach ($Entry in $Entries)
    {
        $Additional = $Entry.Additional
        [String[]]$Lines = @()

        If ((Measure-Object -Line -InputObject $Additional).Lines -gt 9)
        {
            $Count = (Measure-Object -Line -InputObject $Additional).Lines

            $Lines = $Additional.Split("`n")

            [String]$Additional = ''
            $LineNum = 1
            $LineNumRemoved = 0

            ForEach ($Line in $Lines)
            {
                If ($LineNum / ($count / ($count - 9)) - $LineNumRemoved -ge 1.0)
                {
                    If ($Additional -ne '')
                    {
                        $Additional += '; '
                    }
                    $Additional += $Line

                    $LineNumRemoved++
                }
                Else
                {
                    If ($Additional -ne '')
                    {
                        $Additional += "`n"
                    }
                    $Additional += $Line
                }

                $LineNum++
            }
            $Entry.Additional = $Additional
        }
    }
    Return $Entries
}

#  Performs some basic parsing of the ADMX settings that are registry settings
Function Get-XMLTreeSimpleValues 
{
    Param($XmlElement)

    [String]$Additional = ''
    [Bool]$HasChildren = $false
    
    ForEach ($property in $XmlElement.ChildNodes)
    {
        [String]$Desc = ''
        If ($property.GetType().Name -eq 'XmlElement')
        {
            $Desc = Get-XMLTreeSimpleValues $property
            $HasChildren = $true
        }
        ElseIf ($property.GetType().Name -eq 'String')
        {
            $Desc = $property.LocalName + ': ' + $property
        }
        ElseIf ($property.GetType().Name -eq 'XmlText')
        {
            $Desc = $property.value
        }
        Else
        {
            Write-Warning "$($property.LocalName) isn't what was expected $($property.GetType())"
        }
        $Desc = $Desc.Trim()
        If ($Desc -ne '')
        {
            If ($Additional -eq '')
            {
                $Additional = $Desc
            }
            ElseIf ($Additional -match ':$')
            {
                $Additional += " " + $Desc
            }
            ElseIf ($Desc -eq 'Enabled' -or $Desc -eq 'Disabled')
            {
                $Additional += ": " + $Desc
            }
            Else
            {
                $Additional += "`n" + $Desc
            }
        }
    }
    #$Additional = Replace-ExtraLines $Additional
    Return $Additional
}

#  Performs some slightly more complex parsing of the ADMX settings that are registry settings
Function Get-RegistryValues
{
    Param($XmlElement)

    <#  
    Valid values and sub-values appear to be:
        Name
        State
        Explain
        Supported
        Category
        Text
        Comment
        CheckBox
            Name
            State
        DropDownList
            Name
            State
            Value
        EditText
            Name
            State
            Value
        ListBox
            Name
            State
            Value
            Additive
            ExplicitValue
            ValuePrefix
        Numeric
            Name
            State
            Value
    #>

    [String]$Additional = ''
    #  Elements to skip in the "Registry" section
    [String[]]$ignoreList = @('Name', 'State', 'Explain', 'Supported', 'Category', 'Text', 'Comment')
    
    [String[]]$DescList = @()
    ForEach ($property in $XmlElement.ChildNodes)
    {
        [String]$Desc = ''
        [String]$LocalName = $property.LocalName
        If (!$ignoreList.Contains($LocalName))
        {
            If ($property.Name.Trim().Length -eq '0')
            {
                [String]$PropertyName = $LocalName + ': '
            }
            ElseIf ($property.Name.Trim() -match ':$')
            {
                [String]$PropertyName = $property.Name.Trim() + ' '
            }
            ElseIf ($property.Name.Trim() -match '\.$')
            {
                [String]$PropertyName = $property.Name.Trim().Substring(0,$property.Name.Trim().Length - 1) + ': '
            }
            Else
            {
                [String]$PropertyName = $property.Name.Trim() + ': '
            }

            If ($LocalName -eq 'CheckBox')
            {
                $Desc = $PropertyName + $property.State
            }
            ElseIf ($LocalName -eq 'DropDownList')
            {
                $Desc = $PropertyName + $property.Value.Name
            }
            ElseIf ($LocalName -eq 'EditText')
            {
                $Desc = $PropertyName + $property.Value
            }
            ElseIf ($LocalName -eq 'ListBox')
            {
                If ($property.ValuePrefix)
                {
                    [String]$ValuePrefix = '(ValuePrefix: ' + $property.ValuePrefix + ') '
                }
                Else
                {
                    [String]$ValuePrefix = ''
                }

                [String[]]$ValueData = @()
                ForEach ($ValueElement in $property.Value.Element)
                {
                    $ValueData += [String[]]($ValueElement.childnodes.'#text') -join ', '
                }
                #$Desc = $LocalName + ': ' + $PropertyName + 'Expli-' + $property.ExplicitValue + ': ' + 'Addi-' + $property.Additive + ': ' + $ValuePrefix + "`n" + ($ValueData -join "`n")
                $Desc = $PropertyName + $ValuePrefix + "`n" + ($ValueData -join "`n")
            }
            ElseIf ($LocalName -eq 'Numeric')
            {
                $Desc = $PropertyName + $property.Value
            }
            Else
            {
                Write-Warning "$($LocalName): $($property.Name) isn't what was expected $($property.GetType())"
            }
            $DescList += $Desc.Trim()
        }
    }
    #Return (Replace-ExtraLines ($DescList -join "`n"))
    Return ($DescList -join "`n")
}

#  Parses direct registry settings in GPOs
Function Get-XMLTreeRegistryValues
{
    Param($XmlElement)
    
    $updateType = @{'C' = 'Create'; 'R' = 'Replace'; 'U' = 'Update'; 'D' = 'Delete'}
    $hive = @{'HKEY_LOCAL_MACHINE' = 'HKLM'; 'HKEY_CLASSES_ROOT' = 'HKCR'; 'HKEY_CURRENT_USER' = 'HKCU'; 'HKEY_USERS' = 'HKU'; 'HKEY_CURRENT_CONFIG' = 'HKCC'}
    $returnArray = @()
    
    ForEach ($property in $XmlElement.ChildNodes)
    {
        [String]$Desc = ''
        [String]$State = 'Enabled'
        $tempArray = @()
        If ($property.LocalName -eq 'GPOSettingOrder' -or $property.LocalName -eq 'Filters' -or $property.LocalName -eq '#text')
        {
            $tempArray = Get-XMLTreeRegistryValues $property
        }
        ElseIf ($property.LocalName -eq 'Collection')
        {
            $tempArray = Get-XMLTreeRegistryValues $property
        }
        ElseIf ($property.LocalName -eq 'Registry')
        {
            If ($property.disabled -eq '1')
            {
                $State = 'Disabled'
            }
            If ($property.status)
            {
                $name = $property.status
            }
            Else
            {
                $name = $property.name
            }
            $Desc  = $updateType["$($property.Properties.action)"] + ' "'
            $Desc += $name + '" to "'
            $Desc += $property.Properties.value + '"' + "`n"
            $Desc += $hive["$($property.Properties.hive)"] + '\'
            $Desc += $property.Properties.key

            $tempArray += New-Object PSObject -Property @{
                                        State = $state
                                        Additional = $Desc
                                    }
        }
        Else
        {
            Write-Warning "$($property.LocalName) isn't what was expected $($property.GetType()) $($property.value) $($property.Name)"
        }
        If ($tempArray.Count -gt 0)
        {
            $returnArray += $tempArray
        }
    }
    Return $returnArray
}

#  Performs some complex parsing of the ADMX settings that are non-registry settings
Function Get-XMLTreeComplexValues
{
    Param(
        $XmlElement,
        [String]$Parent,
        [String]$Name
    )
    
    [PSObject[]]$returnArray = @()
    $Additional = ''
    
    ForEach ($property in $XmlElement.ChildNodes)
    {
        [String]$Desc = ''
        [PSObject[]]$tempArray = @()
        #  For this to work right, this makes the really bad assumption that the "name" element is first
        If ($property.LocalName -eq 'name' -or $property.LocalName -eq 'SystemAccessPolicyName')
        {
            $Name = $property.'#text'
            #  Not sure if it's meaningful to have an entry every time the name is set
            #$Desc = $Parent + '\' +  $property.LocalName + '\' +  $property.'#text'
        }
        ElseIf ($property.LocalName -eq 'Type')
        {
        }
        ElseIf ($property.GetType().Name -eq 'XmlElement')
        {
            $tempArray = Get-XMLTreeComplexValues $property ($Parent + '\'+ $property.LocalName) $Name
        }
        ElseIf ($property.GetType().Name -eq 'String')
        {
            $Desc = $Parent + '\' +  $property.LocalName + ': ' + $property
        }
        ElseIf ($property.GetType().Name -eq 'XmlText')
        {
            $Desc = $Parent + '\' +  $property.value
        }
        Else
        {
            Write-Warning "$($property.LocalName) isn't what was expected $($property.GetType())"
        }
        $Desc = $Desc.Trim()
        If($Desc -ne '')
        {
            $tempArray += New-Object PSObject -Property @{
                                PolicyName = $Name
                                Additional = $Desc
                            }
        }
        If ($tempArray.Count -gt 0)
        {
            $returnArray += $tempArray
        }
    }
    Return $returnArray
}

#  Parse firewall specific settings
Function Get-XMLFirewallSettings
{
    Param(
        $ChildNodes,
	    [String]$identifier,
	    [String]$GPOName,
        [String]$PolicyType,
        [int32]$LinkCount,
        [Int32]$PermissionCount
    )

    [PSObject[]]$returnArray = @()
    
    [PSObject[]]$registrySettings = @()
    [String]$policyName = 'Windows Firewall'
    ForEach ($policy in $ChildNodes)
    {
        [String]$Additional = ''
        If($policy.LocalName -ne 'InboundFirewallRules' -and $policy.LocalName -ne 'OutboundFirewallRules')
        {
            ForEach ($extValue in $policy.ChildNodes)
            {
                If($extValue -ne '' -and $extValue.LocalName -ne 'PolicyVersion')
                {
                    If($Additional.Length -ne 0)
                    {
                        $Additional += "`n"
                    }
                    $Additional += "$($extValue.LocalName): $($extValue.Value)"
                }
            }
        }
        Else
        {
            ForEach ($extValue in $policy.ChildNodes)
            {
                If($extValue -ne '' -and $extValue.ChildNodes.Count -gt 0 -and $extValue.ChildNodes[0].Value -ne '')
                {
                    If (!$ignoreListFirewall.Contains($extValue.ChildNodes[0].ParentNode.ToString()) -and $extValue.ChildNodes[0].Value -notlike '*@FirewallAPI.dll,-*')
                    {
                        If($Additional.Length -ne 0)
                        {
                            $Additional += "`n"
                        }
                        $Additional += "$($extValue.ChildNodes[0].ParentNode.ToString()): $($extValue.ChildNodes[0].Value)"
                    }
                }
            }
        }

        #$Additional = Replace-ExtraLines $Additional

        If ($Additional -ne '')
        {
            $returnArray += New-Object PSObject -Property @{
	                            Identifier = $identifier
	                            GPOName = $GPOName
                                T = $PolicyType
                                L = $LinkCount
                                PolicyName = "$($policyName): $($policy.LocalName)"
                                State = ''
                                Additional = $Additional
                                P = $PermissionCount
                            }
        }
    }

    Return $returnArray
}

#  Parse Advanced Audit settings
Function Get-XMLAdvancedAuditSettings
{
    Param(
        $ChildNodes,
	    [String]$identifier,
	    [String]$GPOName,
        [String]$PolicyType,
        [int32]$LinkCount,
        [Int32]$PermissionCount
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'Advanced Audit Configuration'
    [String]$Additional = ''
    [String]$TempAdditional = ''
    [String[]]$AuditEvents = @('None', 'Success', 'Failure', 'Success and Failure')

    ForEach ($policy in $ChildNodes)
    {
        $Additional = ''
        $TempAdditional = ''

        If ($policy.LocalName -eq 'AuditSetting')
        {
            $Additional = "$($policy.SubcategoryName): $($AuditEvents[$policy.SettingValue])"
        }
        ElseIf ($policy.LocalName -eq 'GlobalSaclSetting')
        {
            ForEach ($setting in $policy.ChildNodes)
            {
                If ($setting.LocalName -eq 'GlobalSaclType')
                {
                    $Additional += "$($setting.LocalName): $($setting.'#text')"
                }
                ElseIf ($setting.LocalName -eq 'SecurityDescriptor')
                {
                    ForEach ($SecurityDescriptor in $setting.ChildNodes)
                    {
                        If($SecurityDescriptor.LocalName -eq 'Auditing')
                        {
                            ForEach ($Audit in $SecurityDescriptor.ChildNodes)
                            {
                                $Additional += "`n$($Audit.LocalName): $($Audit.'#text')"
                            }
                        }
                        Else
                        {
                            $Additional += "`n$($SecurityDescriptor.LocalName): $($SecurityDescriptor.'#text')"
                        }
                    }
                }
                Else
                {
                    $Additional += "`n$($setting.LocalName): $($setting.'#text')"
                }
            }
        }
        Else
        {
            Write-Warning "Unknown audit setting type '$($policy.LocalName)' in $($GPOName)"
        }

        #$Additional = Replace-ExtraLines $Additional
        If ($Additional -ne '')
        {
            $returnArray += New-Object PSObject -Property @{
	                            Identifier = $identifier
	                            GPOName = $GPOName
                                T = $PolicyType
                                L = $LinkCount
                                PolicyName = "$($policyName)"
                                State = ''
                                Additional = $Additional
                                P = $PermissionCount
                            }
        }
    }

    Return $returnArray
}

#  Parse Security User Rights Assignment settings
Function Get-XMLUserRightsAssignment
{
    Param(
        $UserRightsAssignments
    )

    [PSObject[]]$returnArray = @()

    ForEach ($policy in $UserRightsAssignments)
    {
        [String]$policyName = $policy.Name
        [String]$state = $policy.State

        [String[]]$DescList = @()
        [String]$Desc = ''
        If ($policy.Member -ne $null)
        {
            If ($policy.Member.GetType().Name -eq 'XmlElement')
            {
                If ($policy.Member.SID -ne $null)
                {
                    $Desc = $policy.Member.SID.'#text'
                    If ($policy.Member.Name.'#text' -ne $null)
                    {
                        $Desc += " ($($policy.Member.Name.'#text'))"
                    }
                }
                Else
                {
                    $Desc = $policy.Member.Name.'#text'
                }
                $DescList += $Desc
            }
            Else
            {
                ForEach ($Mem in $policy.Member)
                {
                    If ($Mem.SID -ne $null)
                    {
                        $Desc = $Mem.SID.'#text'
                        If ($Mem.Name.'#text' -ne $null)
                        {
                            $Desc += " ($($Mem.Name.'#text'))"
                        }
                    }
                    Else
                    {
                        $Desc = $Mem.Name.'#text'
                    }
                    $DescList += $Desc
                }
            }
            $Desc = $DescList -join "`n"
        }

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policy.Name
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLSecurityOptions
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = ''
    [String[]]$DescList = @()
    [String]$Desc = ''
    
    $StringValues = @('DisplayNumber', 'DisplayBoolean', 'DisplayString')
    ForEach ($policy in $Policies)
    {
        $DescList = @()

        If ($policy.Display.Name -ne $null)
        {
            $policyName = $policy.Display.Name
            ForEach ($attr in $policy.ChildNodes)
            {
                If ($attr.LocalName -eq 'KeyName' -or $attr.LocalName -eq 'SettingStrings' -or $attr.LocalName -eq 'SettingNumber' -or $attr.LocalName -eq 'SettingString')
                {
                    # Do nothing
                }
                ElseIf ($attr.LocalName -eq 'Display')
                {
                    ForEach ($child in $attr.ChildNodes)
                    {
                        If ($StringValues.Contains($child.LocalName))
                        {
                            If ($child.'#text' -ne '')
                            {
                                If ($Policy.Display.Units -ne '')
                                {
                                    $DescList += "$($Policy.Display.Units): $($child.'#text')"
                                }
                                Else
                                {
                                    $DescList += $child.'#text'
                                }
                            }
                        }
                        ElseIf ($child.LocalName -eq 'DisplayStrings')
                        {
                            #$DescList += 'Strings:'
                            ForEach ($str in $child)
                            {
                                $DescList += $str.Value
                            }
                        }
                        ElseIf ($child.LocalName -eq 'DisplayFields')
                        {
                            ForEach ($Field in $child.Field)
                            {
                                $DescList += "$($Field.Name): $($Field.Value)"
                            }
                        }
                        ElseIf ($child.LocalName -ne 'Name' -and $child.LocalName -ne 'Units')
                        {
                            Write-Warning "Unknown SecurityOption Display type $GPONum $($child.LocalName): $($child.'#text')"
                            $DescList += "$($child.LocalName): $($child.'#text')"
                        }
                    }
                }
                Else
                {
                    Write-Warning "Unknown SecurityOption type $GPONum $($attr.LocalName): $($attr)"
                    $DescList += "$($attr.LocalName): $($attr.'#text')"
                }
            }
        }
        ElseIf ($policy.SystemAccessPolicyName -ne $null)
        {
            [String]$policyName = $policy.SystemAccessPolicyName
                            
            ForEach ($child in $Policy.ChildNodes)
            {
                If ($child.LocalName -ne 'SystemAccessPolicyName')
                {
                    $DescList += $child.'#text'
                }
            }
        }
        Else
        {
            [String]$policyName = 'Security Option'
                            
            ForEach ($child in $Policy.ChildNodes)
            {
                $DescList += $child.'#text'
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLSecurityRegistry
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'SecurityRegistry'
    [String[]]$DescList = @()
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()

        ForEach ($attr in $policy.ChildNodes)
        {
            If ($attr.LocalName -eq 'Path')
            {
                $DescList += $attr.'#text'
            }
            ElseIf ($attr.LocalName -eq 'SecurityDescriptor')
            {
                ForEach ($subAttr in $attr.ChildNodes)
                {
                    If ($subAttr.LocalName -eq 'SDDL')
                    {
                        #  No one wants to see this, right?
                    }
                    ElseIf ($subAttr.LocalName -eq 'Permissions')
                    {
                        $DescList += "InheritsFromParent: $($subAttr.InheritsFromParent)"
                        If ($subAttr.InheritsFromParent -eq 'true')
                        {
                            $DescList += "Explicit Permissions: $(($subAttr.TrusteePermissions | Where-Object {$_.Inherited -eq 'false'} | Measure-Object).Count)"
                        }
                    }
                    Else
                    {
                        $DescList += "$($subAttr.LocalName): $($subAttr.'#text')"
                    }
                }
            }
            Else
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLSecurityFile
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'SecurityFile'
    [String[]]$DescList = @()
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()

        ForEach ($attr in $policy.ChildNodes)
        {
            If ($attr.LocalName -eq 'Path')
            {
                $DescList += $attr.'#text'
            }
            ElseIf ($attr.LocalName -eq 'SecurityDescriptor')
            {
                ForEach ($subAttr in $attr.ChildNodes)
                {
                    If ($subAttr.LocalName -eq 'SDDL')
                    {
                        #  No one wants to see this, right?
                    }
                    ElseIf ($subAttr.LocalName -eq 'Permissions')
                    {
                        $DescList += "InheritsFromParent: $($subAttr.InheritsFromParent)"
                        If ($subAttr.InheritsFromParent -eq 'true')
                        {
                            $DescList += "Explicit Permissions: $(($subAttr.TrusteePermissions | Where-Object {$_.Inherited -eq 'false'} | Measure-Object).Count)"
                        }
                    }
                    Else
                    {
                        $DescList += "$($subAttr.LocalName): $($subAttr.'#text')"
                    }
                }
            }
            Else
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLSecuritySystemServices
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'SecuritySystemServices'
    [String[]]$DescList = @()
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()

        ForEach ($attr in $policy.ChildNodes)
        {
            If ($attr.LocalName -eq 'Name')
            {
                $DescList += $attr.'#text'
            }
            ElseIf ($attr.LocalName -eq 'SecurityDescriptor')
            {
                ForEach ($subAttr in $attr.ChildNodes)
                {
                    If ($subAttr.LocalName -eq 'SDDL' -or $subAttr.LocalName -eq 'Auditing')
                    {
                        #  No one wants to see this, right?
                    }
                    ElseIf ($subAttr.LocalName -eq 'Permissions')
                    {
                        $DescList += "InheritsFromParent: $($subAttr.InheritsFromParent)"
                        If ($subAttr.InheritsFromParent -eq 'true')
                        {
                            $DescList += "Explicit Permissions: $(($subAttr.TrusteePermissions | Where-Object {$_.Inherited -eq 'false'} | Measure-Object).Count)"
                        }
                    }
                    Else
                    {
                        $DescList += "$($subAttr.LocalName): $($subAttr.'#text')"
                    }
                }
            }
            Else
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLSecurityRestrictedGroups
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'SecurityRestrictedGroups'
    [String[]]$DescList = @()
    [String]$Desc = ''
    [String]$Switch = ''
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()
        $Desc = ''

        ForEach ($attr in $policy.ChildNodes)
        {
            If ($attr.LocalName -eq 'GroupName')
            {
                If ($attr.SID -ne $null)
                {
                    $Desc = $attr.SID.'#text'
                    If ($attr.Name.'#text' -ne $null)
                    {
                        $Desc += " ($($attr.Name.'#text'))"
                    }
                }
                Else
                {
                    $Desc = $attr.Name.'#text'
                }
                $DescList += $Desc
            }
            ElseIf ($attr.LocalName -eq 'Member' -or $attr.LocalName -eq 'Memberof')
            {
                If ($Switch -ne $attr.LocalName)
                {
                    $Switch = $attr.LocalName
                    $DescList += "$($attr.LocalName):"
                }
                If ($attr.SID -ne $null)
                {
                    $Desc = $attr.SID.'#text'
                    If ($attr.Name.'#text' -ne $null)
                    {
                        $Desc += " ($($attr.Name.'#text'))"
                    }
                }
                Else
                {
                    $Desc = $attr.Name.'#text'
                }
                $DescList += $Desc
            }
            Else
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLSecurityEventLog
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'SecurityEventLog'
    [String[]]$DescList = @()
    [String]$Desc = ''
    [String]$Switch = ''
    [String[]]$RetentionPeriod = @('Overwrite events as needed', 'Overwrite events by days', 'Do not overwrite events')
    
    ForEach ($policy in $Policies)
    {
        
        If ($Switch -ne $policy.Log)
        {
            If ($DescList.Count -ne 0)
            {
                $Desc = $DescList -join "`n"
                $returnArray += New-Object PSObject -Property @{
                                    PolicyName = $policyName
                                    Additional = $Desc
                                }
                $DescList = @()
            }
            $Switch = $policy.Log
            $DescList += "$($policy.Log) Log"
        }
        
        If ($policy.Name -eq 'RestrictGuestAccess')
        {
            $DescList += "$($policy.Name): $($policy.SettingBoolean)"
        }
        ElseIf ($policy.Name -eq 'AuditLogRetentionPeriod')
        {
            $DescList += "$($policy.Name): $($RetentionPeriod[$policy.SettingNumber])"
        }
        Else
        {
            $DescList += "$($policy.Name): $($policy.SettingNumber)"
        }
    }

    If ($DescList.Count -ne 0)
    {
        $Desc = $DescList -join "`n"
        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
        $DescList = @()
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLWLanSvcNetworks
{
    Param(
        $Policies
    )
    [PSObject[]]$returnArray = @()
    [String]$policyName = 'WLanSvcNetworks'
    [String[]]$DescList = @()
    [String]$Desc = ''
    [String]$Switch = ''
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()
        $Desc = ''

        ForEach ($attr in $policy.ChildNodes)
        {
            If ($attr.LocalName -eq 'networkFilter' -or $attr.LocalName -eq 'profileList')
            {
                #$DescList += "$($attr.LocalName)"
                ForEach ($attr2 in $attr.ChildNodes)
                {
                    If ($attr2.LocalName -eq 'allowList' -or $attr2.LocalName -eq 'blockList')
                    {
                        ForEach ($attr3 in $attr2.ChildNodes)
                        {
                            $DescList += "$($attr2.LocalName)-networkName: $($attr3.networkName); networkType: $($attr3.networkType)"
                        }
                    }
                    ElseIf ($attr2.LocalName -eq 'WLANProfile')
                    {
                        $DescList += "WLANProfile: $($attr2.name); Type: $($attr2.connectionType)"
                        $DescList += "SSID: $($attr2.SSIDConfig.SSID.name); Broadcast: $([Bool]($attr2.SSIDConfig.nonBroadcast) -eq $false)"
                        $DescList += "authentication: $($attr2.MSM.security.authEncryption.Authentication); encryption: $($attr2.MSM.security.authEncryption.encryption); 802.1x: $($attr2.MSM.security.authEncryption.useOneX)"
                        If ($attr2.MSM.security.authEncryption.useOneX -eq 'true')
                        {
                            If ($attr2.MSM.security.OneX.EAPConfig.EapHostConfig.Config.Eap.EapType.Eap.EapType.UseWinLogonCredentials)
                            {
                                $DescList += "authMode: $($attr2.MSM.security.OneX.authMode); UseWinLogonCredentials: $($attr2.MSM.security.OneX.EAPConfig.EapHostConfig.Config.Eap.EapType.Eap.EapType.UseWinLogonCredentials)"
                            }
                            Else
                            {
                                $DescList += "authMode: $($attr2.MSM.security.OneX.authMode)"
                            }
                        }

                        #  I want to skip enough information in these settings that it's easier to just cherry pick
                        <#
                        ForEach ($attr2b in $attr2.ChildNodes)
                        {
                            If ($attr2b.LocalName -eq 'MSM')
                            {
                                ForEach ($attr3 in $attr2b.security.ChildNodes)
                                {
                                    If ($attr3.LocalName -eq 'authEncryption')
                                    {
                                        $DescList += "authentication: $($attr3.Authentication)"
                                        $DescList += "encryption: $($attr3.encryption)"
                                        $DescList += "802.1x: $($attr3.useOneX)"
                                    }
                                    If ($attr3.LocalName -eq 'OneX')
                                    {
                                        $DescList += "authMode: $($attr3.authMode)"
                                        $DescList += "UseWinLogonCredentials: $($attr3.EAPConfig.EapHostConfig.Config.Eap.EapType.Eap.EapType.UseWinLogonCredentials)"
                                        #  Most of the settings under here are best looked at directly in the GPO

                                    }
                                    Else
                                    {
                                        #  I think it's a little excessive to include this information
                                        #$DescList += " $($attr3.LocalName): $($attr3.'#text')"
                                    }
                                }
                            }
                            ElseIf ($attr2b.LocalName -eq 'SSIDConfig')
                            {
                                $DescList += "SSID: $($attr2b.SSID.name)"
                                #$DescList += "Broadcast: $([Bool]($attr2b.nonBroadcast) -eq $false)"
                            }
                            ElseIf ($attr2b.LocalName -eq 'name')
                            {
                                $DescList += "WLANProfile: $($attr2b.'#text')"
                            }
                            ElseIf ($attr2b.LocalName -eq 'connectionType')
                            {
                                $DescList += "connectionType: $($attr2b.'#text')"
                            }
                        }
                        #>
                    }
                    Else
                    {
                        #$DescList += " $($attr2.LocalName): $($attr2.'#text')"
                    }
                }
            }
            ElseIf ($attr.LocalName -eq 'name')
            {
                $DescList += $attr.'#text'
            }
            ElseIf ($attr.LocalName -eq 'globalFlags')
            {
                #  These are more clutter than useful.  Best to look directly at the policies
                #$DescList += "$($attr.LocalName): $($attr.'#text')"
            }
            Else
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLLanSvcNetworks
{
    Param(
        $Policies
    )
    [PSObject[]]$returnArray = @()
    [String]$policyName = 'LanSvcNetworks'
    [String[]]$DescList = @()
    [String]$Desc = ''
    [String]$Switch = ''
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()
        $Desc = ''
        
        $DescList += "$($policy.name)"
        If ($policy.description)
        {
            $DescList += "$($policy.description)"
        }
        $DescList += "enableAutoConfig: $($policy.globalFlags.enableAutoConfig); enableExplicitCreds: $($policy.globalFlags.enableExplicitCreds.'#text')"
        $DescList += "OneXEnabled: $($policy.profileList.LANProfile.MSM.security.OneXEnabled); OneXEnforced: $($policy.profileList.LANProfile.MSM.security.OneXEnforced)"
        $DescList += "authMode: $($policy.profileList.LANProfile.MSM.security.OneX.authMode); maxAuthFailures: $($policy.profileList.LANProfile.MSM.security.OneX.maxAuthFailures)"


        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = $policyName
                            Additional = $Desc
                        }
    }

    Return $returnArray
}

#  Parse Security Options settings
Function Get-XMLPublicKey
{
    Param(
        $Policies
    )
    [PSObject[]]$returnArray = @()
    [String]$policyName = 'PublicKey'
    [String[]]$DescList = @()
    [String]$Desc = ''
    [String]$Switch = ''
    
    ForEach ($policy in $Policies)
    {
        $DescList = @()
        $Desc = ''
        
        If ($policy.LocalName -eq 'AutoEnrollmentSettings')
        {
            $DescList += "EnrollCertificatesAutomatically: $($policy.EnrollCertificatesAutomatically)"
            $DescList += "ExpiryNotification: $($policy.ExpiryNotification)"
            $DescList += "NotifyPercent: $($policy.NotifyPercent.Value)"
            $DescList += "RenewUpdateRevoke: $($policy.Options.RenewUpdateRevoke)"
            $DescList += "UpdateTemplates: $($policy.Options.UpdateTemplates)"
        }
        ElseIf ($policy.LocalName -eq 'RootCertificateSettings' -or $policy.LocalName -eq 'EFSSettings')
        {
            #  Everything in here appears to be set automatically as a consequence of importing a trusted root CA certificate
            #  I need to check on the possibility that this could be set somewhere else
        }
        ElseIf ($policy.LocalName -eq 'RootCertificate' -or $policy.LocalName -eq 'TrustedPublishersCertificate' -or $policy.LocalName -eq 'EnterpriseTrustCertificate' -or
                $policy.LocalName -eq 'IntermediateCertificationAuthorityCertificate' -or $policy.LocalName -eq 'EFSRecoveryAgent')
        {
            $DescList += "IssuedTo: $($policy.IssuedTo)"
            $DescList += "IssuedBy: $($policy.IssuedBy)"
            $DescList += "ExpirationDate: $($policy.ExpirationDate)"
            ForEach ($byte in (New-Object System.Security.Cryptography.SHA256Managed).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policy.Data)))
            {
                $Desc += $byte.ToString('x2')
            }
            $DescList += "CertFileSha256: $($Desc)"
            If ($policy.CertificatePurpose)
            {
                $DescList += "CertificatePurpose: $($policy.CertificatePurpose.Purpose.ToString())"
            }
        }
        Else
        {
            ForEach ($attr in $policy.ChildNodes)
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"
        If ($Desc -ne '')
        {
            $returnArray += New-Object PSObject -Property @{
                                PolicyName = "$($policyName)-$($policy.LocalName)"
                                Additional = $Desc
                            }
        }
    }

    Return $returnArray
}

#  Parse Internet Explorer Maintenance settings
Function Get-XMLInternetExplorerMaintenance
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'InternetExplorerMaint'
    [String]$subName = ''
    [String[]]$DescList = @()
    
    ForEach ($policy in $Policies.ChildNodes)
    {
        $DescList = @()
        $Desc = ''
        $subName = ''
        
        If ($policy.LocalName -eq 'EscOffLocalSites' -or $policy.LocalName -eq 'EscOffTrustedSites' -or $policy.LocalName -eq 'EscOffRestrictedSites' -or $policy.LocalName -eq 'EscOffSecurityZoneAndPrivacy')
        {
            #  Process later
        }
        Else
        {
            If ($policy.ChildNodes.Count -gt 1)
            {
                $subName = "-$($policy.LocalName)"
                ForEach ($attr in $policy.ChildNodes)
                {
                    If ($attr.ChildNodes.Count -gt 1)
                    {
                        ForEach ($subattr in $attr.ChildNodes)
                        {
                            $DescList += "$($attr.LocalName) $($subattr.LocalName): $($subattr.'#text')$($subattr.Value)"
                        }
                    }
                    Else
                    {
                        $DescList += "$($attr.LocalName): $($attr.'#text')"
                    }
                }
            }
            Else
            {
                $DescList += "$($policy.LocalName): $($policy.'#text')$($policy.Value)"
            }
        }

        If ($DescList.Count -gt 0)
        {
            $Desc = $DescList -join "`n"

            $returnArray += New-Object PSObject -Property @{
                                PolicyName = "$($policyName)$($subName)"
                                Additional = $Desc
                            }
        }
    }

    
    If ($Policies.EscOffSecurityZoneAndPrivacy)
    {
        ForEach ($attr in $Policies.EscOffSecurityZoneAndPrivacy.ChildNodes)
        {
            $subName = ''
            $DescList = @()
            $Desc = ''

            If ($attr.LocalName -eq 'Privacy')
            {
                $DescList += "PrivacyLevel: $($attr.PrivacyLevel)"
            }
            ElseIf ($attr.LocalName -eq 'ZoneSettings')
            {
                $subName = "Zone$($attr.Name)"
                $DescList += "SecurityLevel: $($attr.SecurityLevel)"
                $DescList += "RequireVerification: $($attr.RequireVerification)"
                If ($attr.SecurityLevel -eq 'Custom')
                {
                    ForEach ($subAttr in $attr.CustomSetting)
                    {
                        $DescList += "$($subAttr.Name): $($subAttr.Value)"
                    }
                }
            }
            ElseIf ($attr.LocalName -eq 'LocalZoneSettings')
            {
                $subName = "$($attr.LocalName)"
                ForEach ($subAttr in $attr.ChildNodes)
                {
                    $DescList += "$($subAttr.LocalName): $($subAttr.'#text')"
                }
            }
            Else
            {
                Write-Warning "Unknown value in EscOffSecurityZoneAndPrivacy $($attr.LocalName): $($attr.'#text')$($attr.Value)"
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }

            $Desc = $DescList -join "`n"

            $returnArray += New-Object PSObject -Property @{
                                PolicyName = "$($policyName)$($subName)"
                                Additional = $Desc
                            }
        }
    }
    
    If ($Policies.EscOffLocalSites)
    {
        $returnArray += New-Object PSObject -Property @{
                            PolicyName = "$($policyName)-IntranetSites"
                            Additional = ($Policies.EscOffTrustedSites).Value -join "`n"
                        }
    }
    If ($Policies.EscOffTrustedSites)
    {
        $returnArray += New-Object PSObject -Property @{
                            PolicyName = "$($policyName)-TrustedSites"
                            Additional = ($Policies.EscOffTrustedSites).Value -join "`n"
                        }
    }
    If ($Policies.EscOffRestrictedSites)
    {
        $returnArray += New-Object PSObject -Property @{
                            PolicyName = "$($policyName)-RestrictedSites"
                            Additional = ($Policies.EscOffTrustedSites).Value -join "`n"
                        }
    }

    Return $returnArray
}

#  Parse Scripts settings
Function Get-XMLScripts
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'Scripts'
    [String]$subName = ''
    [String[]]$DescList = @()
    
    If (($Policies.GetType()).Name -eq 'XmlElement')
    {
        $DescList += $Policies.Command
        If ($Policies.Parameters)
        {
            $DescList += "Parameters: $($Policies.Parameters)"
        }
        If ($Policies.Order -ne '0')
        {
            $DescList += "Order: $($Policies.Order)"
        }
        If ($Policies.RunOrder -ne 'PSNotConfigured')
        {
            $DescList += "RunOrder: $($Policies.RunOrder)"
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = "$($policyName)-$($Policies.Type)"
                            Additional = $Desc
                        }
    }
    Else
    {
        ForEach ($Policy in $Policies)
        {
            $DescList = @()
            $DescList += $Policy.Command
            If ($Policy.Parameters)
            {
                $DescList += "Parameters: $($Policy.Parameters)"
            }
            If ($Policy.Order -ne '0')
            {
                $DescList += "Order: $($Policy.Order)"
            }
            If ($Policy.RunOrder -ne 'PSNotConfigured')
            {
                $DescList += "RunOrder: $($Policy.RunOrder)"
            }

            $Desc = $DescList -join "`n"

            $returnArray += New-Object PSObject -Property @{
                                PolicyName = "$($policyName)-$($Policy.Type)"
                                Additional = $Desc
                            }
        }
    }

    Return $returnArray
}

#  Parse Services settings
Function Get-XMLServices
{
    Param(
        $Policies
    )

    [PSObject[]]$returnArray = @()
    [String]$policyName = 'NTServices'
    [String]$subName = ''
    [String[]]$DescList = @()

    ForEach ($Policy in $Policies)
    {
        $DescList = @()
        $DescList += $Policy.Properties.serviceName
        ForEach ($attr in $Policy.ChildNodes)
        {
            If ($attr.LocalName -eq 'GPOSettingOrder')
            {
                #  Skip
            }
            ElseIf ($attr.LocalName -eq 'Properties')
            {
                ForEach ($subattr in $attr.Attributes)
                {
                    If ($subattr.LocalName -eq 'serviceName' -or ($subattr.LocalName -eq 'startupType' -and $subattr.'#text' -eq 'NOCHANGE'))
                    {
                        #  skip
                    }
                    Else
                    {
                        $DescList += "$($subattr.LocalName): $($subattr.'#text')"
                    }
                }
            }
            ElseIf ($attr.LocalName -eq 'Filters')
            {
                If ($attr.IsEmpty -eq $false)
                {
                    #  This is hopefully good enough for doing a targetting filter
                    If ($attr.InnerXml -match '(^.+?\s)(.+?)(\sxmlns.*$)')
                    {
                        $DescList += "$($attr.LocalName): $($Matches[2])"
                    }
                }
            }
            Else
            {
                $DescList += "$($attr.LocalName): $($attr.'#text')"
            }
        }

        $Desc = $DescList -join "`n"

        $returnArray += New-Object PSObject -Property @{
                            PolicyName = "$($policyName)"
                            Additional = $Desc
                        }
    }

    Return $returnArray
}


Function Parse-XMLGPOSettings
{
    Param(
        $Reports
    )

    #  XML Elements to skip in the "Windows Firewall" section
    [String[]]$ignoreListFirewall = @('Dir')
    #  Used to reduce the size of the computer/user type of GPO field in the Out-GridView view
    $PolicyTypes = @(@('Computer', 'C'), @('User', 'U'))
    #  Final list of policies.  Will be an array of custom objects
    [PSObject[]]$PolicyList = @()
    [Int32]$GPONum = 0

    #  Iterate through each of the GPOs
    ForEach ($gpo in $Reports.GPOS.GPO)
    {
        #  Remove the curly braces off the GPO's GUID
        [String]$identifier = $gpo.Identifier.Identifier.'#text'.Replace('{','').Replace('}','')
        [String]$GPOName = $gpo.Name
        [Int32]$LinkCount = ($gpo.LinksTo | Where-Object {$_.Enabled -eq 'true'} | Measure-Object).Count
        [Int32]$PermissionCount = 0
        
        If ($gpo.Computer.VersionDirectory -ne $gpo.Computer.VersionSysvol)
        {
            Write-Warning "$($GPONum) '$($GPOName)' version mismatch on Computer side. $($gpo.Computer.VersionDirectory) (AD), $($gpo.Computer.VersionSysvol) (SYSVOL)"
        }

        If ($gpo.User.VersionDirectory -ne $gpo.User.VersionSysvol)
        {
            Write-Warning "$($GPONum) '$($GPOName)' version mismatch on User side. $($gpo.User.VersionDirectory) (AD), $($gpo.User.VersionSysvol) (SYSVOL)"
        }
        
        ForEach ($Permission in $gpo.SecurityDescriptor.Permissions.ChildNodes)
        {
            If ($Permission.LocalName -eq 'TrusteePermissions' -and $Permission.Type.InnerText -eq 'Allow' -and $Permission.Standard.InnerText -like "*Apply Group Policy*")
            {
                $PermissionCount++
            }
        }

        #  Process Computer then User side settings
        ForEach ($PolicyType in $PolicyTypes)
        {
            #  Each individual setting appears to be in its own ExtensionData element
            ForEach ($extension in $gpo.($Policytype[0]).ExtensionData)
            {
                #  GPO settings seem to fall into three categories
                #  1. A registry setting set via some known ADMX template (Registry)
                #  2. A custom registry setting not using an ADMX template (Windows Registry)
                #  3. Non-registry settings parsed by some internal Windows mechanism
                If ([String]($extension.Name) -eq 'Registry')
                {
                    ForEach ($policy in $extension.Extension.Policy)
                    {
                        [String]$policyName = $policy.Name
                        [String]$state = $policy.State
                        [String]$Additional = ''
                        
                        $Additional = Get-RegistryValues $Policy

                        $PolicyList += New-Object PSObject -Property @{
	                                        Identifier = $identifier
	                                        GPOName = $GPOName
                                            T = $PolicyType[1]
                                            L = $LinkCount
                                            PolicyName = $policyName
                                            State = $state
                                            Additional = $Additional
                                            P = $PermissionCount
                                        }
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Windows Registry')
                {
                    [PSObject[]]$registrySettings = @()
                    [String]$policyName = 'RegistrySettings'
                    $registrySettings = Get-XMLTreeRegistryValues $extension.Extension.RegistrySettings
                    ForEach ($registrySetting in $registrySettings)
                    {
                        Add-Member -InputObject $registrySetting -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $registrySetting -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $registrySetting -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $registrySetting -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $registrySetting -MemberType NoteProperty -Name 'PolicyName' -Value $policyName
                        Add-Member -InputObject $registrySetting -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($registrySettings.Count -ne 0)
                    {
                        $PolicyList += $registrySettings
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Windows Firewall')
                {
                    ForEach ($tempEntry in (Get-XMLFirewallSettings $extension.Extension.ChildNodes $identifier $GPOName $PolicyType[1] $LinkCount $PermissionCount))
                    {
                        $PolicyList += $tempEntry
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Advanced Audit Configuration')
                {
                    ForEach ($tempEntry in (Get-XMLAdvancedAuditSettings $extension.Extension.ChildNodes $identifier $GPOName $PolicyType[1] $LinkCount $PermissionCount))
                    {
                        $PolicyList += $tempEntry
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'WLanSvc Networks')
                {
                    $tempEntries = @()
                    $tempEntries += Get-XMLWLanSvcNetworks $extension.Extension.WLanSvcSetting.ChildNodes
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)
                    {
                        $PolicyList += $tempEntries
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'LanSvc Networks')
                {
                    $tempEntries = @()
                    $tempEntries += Get-XMLLanSvcNetworks $extension.Extension.Dot3SvcSetting.ChildNodes
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)
                    {
                        $PolicyList += $tempEntries
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Public Key')
                {
                    $tempEntries = @()
                    $tempEntries += Get-XMLPublicKey $extension.Extension.ChildNodes
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)
                    {
                        $PolicyList += $tempEntries
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Internet Explorer Maintenance')
                {
                    $tempEntries = @()
                    $tempEntries += Get-XMLInternetExplorerMaintenance $extension.Extension
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)
                    {
                        $PolicyList += $tempEntries
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Scripts')
                {
                    $tempEntries = @()
                    $tempEntries += Get-XMLScripts $extension.Extension.Script
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)
                    {
                        $PolicyList += $tempEntries
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Services')
                {
                    $tempEntries = @()
                    If (($extension.Extension.NTServices.NTService.GetType()).Name -eq 'Object[]')
                    {
                        $tempEntries += Get-XMLServices $extension.Extension.NTServices.NTService
                    }
                    Else
                    {
                        $tempEntries += Get-XMLServices @($extension.Extension.NTServices.NTService)
                    }
                    
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)
                    {
                        $PolicyList += $tempEntries
                    }
                }
                ElseIf ([String]($extension.Name) -eq 'Security')
                {
                    $tempEntries = @()
                    $SecurityChildren = @()
                    $SecurityChildren = ($extension.ChildNodes).ChildNodes | Where-Object {$_.LocalName -ne '#text'} | Group-Object LocalName
                    ForEach ($SecurityChild in $SecurityChildren)
                    {
                        $tempEntriesInner = @()
                        If ($SecurityChild.Name -eq 'UserRightsAssignment')
                        {
                            $tempEntries += Get-XMLUserRightsAssignment $SecurityChild.Group
                        }
                        ElseIf ($SecurityChild.Name -eq 'SecurityOptions')
                        {
                            $tempEntries += Get-XMLSecurityOptions $SecurityChild.Group
                        }
                        ElseIf ($SecurityChild.Name -eq 'Audit')
                        {
                            $tempEntries += Get-XMLTreeComplexValues $SecurityChild.Group '' 'Security Audit'
                        }
                        ElseIf ($SecurityChild.Name -eq 'SystemServices')
                        {
                            $tempEntries += Get-XMLSecuritySystemServices $SecurityChild.Group
                        }
                        ElseIf ($SecurityChild.Name -eq 'Account')
                        {
                            $tempEntries += Get-XMLTreeComplexValues $SecurityChild.Group '' 'Security Account'
                        }
                        ElseIf ($SecurityChild.Name -eq 'RestrictedGroups')
                        {
                            $tempEntries += Get-XMLSecurityRestrictedGroups $SecurityChild.Group
                        }
                        ElseIf ($SecurityChild.Name -eq 'Registry')
                        {
                            $tempEntries += Get-XMLSecurityRegistry $SecurityChild.Group
                        }
                        ElseIf ($SecurityChild.Name -eq 'EventLog')
                        {
                            $tempEntries += Get-XMLSecurityEventLog $SecurityChild.Group
                        }
                        ElseIf ($SecurityChild.Name -eq 'File')
                        {
                            $tempEntries += Get-XMLSecurityFile $SecurityChild.Group
                        }
                        Else
                        {
                            Write-Warning "Unknown Security setting type '$($SecurityChild.Name)' in $($GPONum) $($GPOName) in $($Policytype[0]) side"
                            $tempEntries += Get-XMLTreeComplexValues $SecurityChild.Group '' 'Security'
                        }
                    }
                    ForEach ($tempEntry in $tempEntries)
                    {
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'Identifier' -Value $identifier
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'GPOName' -Value $GPOName
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'T' -Value $PolicyType[1]
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'L' -Value $LinkCount
                        Add-Member -InputObject $tempEntry -MemberType NoteProperty -Name 'P' -Value $PermissionCount
                    }
                    If ($tempEntries.Count -ne 0)