GPOZaurr.psm1

function Get-WinADForestDetails { 
    [CmdletBinding()]
    param([alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [string[]] $ExcludeDomainControllers,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [alias('DomainControllers', 'ComputerName')][string[]] $IncludeDomainControllers,
        [switch] $SkipRODC,
        [string] $Filter = '*',
        [switch] $TestAvailability,
        [ValidateSet('All', 'Ping', 'WinRM', 'PortOpen', 'Ping+WinRM', 'Ping+PortOpen', 'WinRM+PortOpen')] $Test = 'All',
        [int[]] $Ports = 135,
        [int] $PortsTimeout = 100,
        [int] $PingCount = 1,
        [switch] $Extended,
        [System.Collections.IDictionary] $ExtendedForestInformation)
    if ($Global:ProgressPreference -ne 'SilentlyContinue') {
        $TemporaryProgress = $Global:ProgressPreference
        $Global:ProgressPreference = 'SilentlyContinue'
    }
    if (-not $ExtendedForestInformation) {
        $Findings = [ordered] @{ }
        try { if ($Forest) { $ForestInformation = Get-ADForest -ErrorAction Stop -Identity $Forest } else { $ForestInformation = Get-ADForest -ErrorAction Stop } } catch {
            Write-Warning "Get-WinADForestDetails - Error discovering DC for Forest - $($_.Exception.Message)"
            return
        }
        if (-not $ForestInformation) { return }
        $Findings['Forest'] = $ForestInformation
        $Findings['ForestDomainControllers'] = @()
        $Findings['QueryServers'] = @{ }
        $Findings['DomainDomainControllers'] = @{ }
        [Array] $Findings['Domains'] = foreach ($_ in $ForestInformation.Domains) {
            if ($IncludeDomains) {
                if ($_ -in $IncludeDomains) { $_.ToLower() }
                continue
            }
            if ($_ -notin $ExcludeDomains) { $_.ToLower() }
        }
        [Array] $Findings['ForestDomainControllers'] = foreach ($Domain in $Findings.Domains) {
            try {
                $DC = Get-ADDomainController -DomainName $Domain -Discover -ErrorAction Stop
                $OrderedDC = [ordered] @{Domain = $DC.Domain
                    Forest                      = $DC.Forest
                    HostName                    = [Array] $DC.HostName
                    IPv4Address                 = $DC.IPv4Address
                    IPv6Address                 = $DC.IPv6Address
                    Name                        = $DC.Name
                    Site                        = $DC.Site
                }
            } catch {
                Write-Warning "Get-WinADForestDetails - Error discovering DC for domain $Domain - $($_.Exception.Message)"
                continue
            }
            if ($Domain -eq $Findings['Forest']['Name']) { $Findings['QueryServers']['Forest'] = $OrderedDC }
            $Findings['QueryServers']["$Domain"] = $OrderedDC
            [Array] $AllDC = try {
                try { $DomainControllers = Get-ADDomainController -Filter $Filter -Server $DC.HostName[0] -ErrorAction Stop } catch {
                    Write-Warning "Get-WinADForestDetails - Error listing DCs for domain $Domain - $($_.Exception.Message)"
                    continue
                }
                foreach ($S in $DomainControllers) {
                    if ($IncludeDomainControllers.Count -gt 0) { If (-not $IncludeDomainControllers[0].Contains('.')) { if ($S.Name -notin $IncludeDomainControllers) { continue } } else { if ($S.HostName -notin $IncludeDomainControllers) { continue } } }
                    if ($ExcludeDomainControllers.Count -gt 0) { If (-not $ExcludeDomainControllers[0].Contains('.')) { if ($S.Name -in $ExcludeDomainControllers) { continue } } else { if ($S.HostName -in $ExcludeDomainControllers) { continue } } }
                    $Server = [ordered] @{Domain = $Domain
                        HostName                 = $S.HostName
                        Name                     = $S.Name
                        Forest                   = $ForestInformation.RootDomain
                        Site                     = $S.Site
                        IPV4Address              = $S.IPV4Address
                        IPV6Address              = $S.IPV6Address
                        IsGlobalCatalog          = $S.IsGlobalCatalog
                        IsReadOnly               = $S.IsReadOnly
                        IsSchemaMaster           = ($S.OperationMasterRoles -contains 'SchemaMaster')
                        IsDomainNamingMaster     = ($S.OperationMasterRoles -contains 'DomainNamingMaster')
                        IsPDC                    = ($S.OperationMasterRoles -contains 'PDCEmulator')
                        IsRIDMaster              = ($S.OperationMasterRoles -contains 'RIDMaster')
                        IsInfrastructureMaster   = ($S.OperationMasterRoles -contains 'InfrastructureMaster')
                        OperatingSystem          = $S.OperatingSystem
                        OperatingSystemVersion   = $S.OperatingSystemVersion
                        OperatingSystemLong      = ConvertTo-OperatingSystem -OperatingSystem $S.OperatingSystem -OperatingSystemVersion $S.OperatingSystemVersion
                        LdapPort                 = $S.LdapPort
                        SslPort                  = $S.SslPort
                        DistinguishedName        = $S.ComputerObjectDN
                        Pingable                 = $null
                        WinRM                    = $null
                        PortOpen                 = $null
                        Comment                  = ''
                    }
                    if ($TestAvailability) {
                        if ($Test -eq 'All' -or $Test -like 'Ping*') { $Server.Pingable = Test-Connection -ComputerName $Server.IPV4Address -Quiet -Count $PingCount }
                        if ($Test -eq 'All' -or $Test -like '*WinRM*') { $Server.WinRM = (Test-WinRM -ComputerName $Server.HostName).Status }
                        if ($Test -eq 'All' -or '*PortOpen*') { $Server.PortOpen = (Test-ComputerPort -Server $Server.HostName -PortTCP $Ports -Timeout $PortsTimeout).Status }
                    }
                    [PSCustomObject] $Server
                }
            } catch {
                [PSCustomObject]@{Domain     = $Domain
                    HostName                 = ''
                    Name                     = ''
                    Forest                   = $ForestInformation.RootDomain
                    IPV4Address              = ''
                    IPV6Address              = ''
                    IsGlobalCatalog          = ''
                    IsReadOnly               = ''
                    Site                     = ''
                    SchemaMaster             = $false
                    DomainNamingMasterMaster = $false
                    PDCEmulator              = $false
                    RIDMaster                = $false
                    InfrastructureMaster     = $false
                    LdapPort                 = ''
                    SslPort                  = ''
                    DistinguishedName        = ''
                    Pingable                 = $null
                    WinRM                    = $null
                    PortOpen                 = $null
                    Comment                  = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                }
            }
            if ($SkipRODC) { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC | Where-Object { $_.IsReadOnly -eq $false } } else { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC }
            [Array] $Findings['DomainDomainControllers'][$Domain]
        }
        if ($Extended) {
            $Findings['DomainsExtended'] = @{ }
            foreach ($DomainEx in $Findings['Domains']) {
                try { $Findings['DomainsExtended'][$DomainEx] = Get-ADDomain -Server $Findings['QueryServers'][$DomainEx].HostName[0] } catch {
                    Write-Warning "Get-WinADForestDetails - Error gathering Domain Information for domain $DomainEx - $($_.Exception.Message)"
                    continue
                }
            }
        }
        if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress }
        $Findings
    } else {
        $Findings = Copy-Dictionary -Dictionary $ExtendedForestInformation
        [Array] $Findings['Domains'] = foreach ($_ in $Findings.Domains) {
            if ($IncludeDomains) {
                if ($_ -in $IncludeDomains) { $_.ToLower() }
                continue
            }
            if ($_ -notin $ExcludeDomains) { $_.ToLower() }
        }
        foreach ($_ in [string[]] $Findings.DomainDomainControllers.Keys) { if ($_ -notin $Findings.Domains) { $Findings.DomainDomainControllers.Remove($_) } }
        foreach ($_ in [string[]] $Findings.QueryServers.Keys) { if ($_ -notin $Findings.Domains -and $_ -ne 'Forest') { $Findings.QueryServers.Remove($_) } }
        [Array] $Findings['ForestDomainControllers'] = foreach ($Domain in $Findings.Domains) {
            [Array] $AllDC = foreach ($S in $Findings.DomainDomainControllers["$Domain"]) {
                if ($IncludeDomainControllers.Count -gt 0) { If (-not $IncludeDomainControllers[0].Contains('.')) { if ($S.Name -notin $IncludeDomainControllers) { continue } } else { if ($S.HostName -notin $IncludeDomainControllers) { continue } } }
                if ($ExcludeDomainControllers.Count -gt 0) { If (-not $ExcludeDomainControllers[0].Contains('.')) { if ($S.Name -in $ExcludeDomainControllers) { continue } } else { if ($S.HostName -in $ExcludeDomainControllers) { continue } } }
                $S
            }
            if ($SkipRODC) { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC | Where-Object { $_.IsReadOnly -eq $false } } else { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC }
            [Array] $Findings['DomainDomainControllers'][$Domain]
        }
        $Findings
    }
}
function ConvertTo-OperatingSystem { 
    [CmdletBinding()]
    param([string] $OperatingSystem,
        [string] $OperatingSystemVersion)
    if ($OperatingSystem -like '*Windows 10*') {
        $Systems = @{'10.0 (18363)' = "Windows 10 1909"
            '10.0 (18362)'          = "Windows 10 1903"
            '10.0 (17763)'          = "Windows 10 1809"
            '10.0 (17134)'          = "Windows 10 1803"
            '10.0 (16299)'          = "Windows 10 1709"
            '10.0 (15063)'          = "Windows 10 1703"
            '10.0 (14393)'          = "Windows 10 1607"
            '10.0 (10586)'          = "Windows 10 1511"
            '10.0 (10240)'          = "Windows 10 1507"
            '10.0 (18898)'          = 'Windows 10 Insider Preview'
            '10.0.18363'            = "Windows 10 1909"
            '10.0.18362'            = "Windows 10 1903"
            '10.0.17763'            = "Windows 10 1809"
            '10.0.17134'            = "Windows 10 1803"
            '10.0.16299'            = "Windows 10 1709"
            '10.0.15063'            = "Windows 10 1703"
            '10.0.14393'            = "Windows 10 1607"
            '10.0.10586'            = "Windows 10 1511"
            '10.0.10240'            = "Windows 10 1507"
            '10.0.18898'            = 'Windows 10 Insider Preview'
        }
        $System = $Systems[$OperatingSystemVersion]
        if (-not $System) { $System = $OperatingSystem }
    } elseif ($OperatingSystem -like '*Windows Server*') {
        $Systems = @{'5.2 (3790)' = 'Windows Server 2003'
            '6.1 (7601)'          = 'Windows Server 2008 R2'
            '10.0 (18362)'        = "Windows Server, version 1903 (Semi-Annual Channel) 1903"
            '10.0 (17763)'        = "Windows Server 2019 (Long-Term Servicing Channel) 1809"
            '10.0 (17134)'        = "Windows Server, version 1803 (Semi-Annual Channel) 1803"
            '10.0 (14393)'        = "Windows Server 2016 (Long-Term Servicing Channel) 1607"
            '10.0.18362'          = "Windows Server, version 1903 (Semi-Annual Channel) 1903"
            '10.0.17763'          = "Windows Server 2019 (Long-Term Servicing Channel) 1809"
            '10.0.17134'          = "Windows Server, version 1803 (Semi-Annual Channel) 1803"
            '10.0.14393'          = "Windows Server 2016 (Long-Term Servicing Channel) 1607"
        }
        $System = $Systems[$OperatingSystemVersion]
        if (-not $System) { $System = $OperatingSystem }
    } else { $System = $OperatingSystem }
    if ($System) { $System } else { 'Unknown' }
}
function Test-ComputerPort { 
    [CmdletBinding()]
    param ([alias('Server')][string[]] $ComputerName,
        [int[]] $PortTCP,
        [int[]] $PortUDP,
        [int]$Timeout = 5000)
    begin {
        if ($Global:ProgressPreference -ne 'SilentlyContinue') {
            $TemporaryProgress = $Global:ProgressPreference
            $Global:ProgressPreference = 'SilentlyContinue'
        }
    }
    process {
        foreach ($Computer in $ComputerName) {
            foreach ($P in $PortTCP) {
                $Output = [ordered] @{'ComputerName' = $Computer
                    'Port'                           = $P
                    'Protocol'                       = 'TCP'
                    'Status'                         = $null
                    'Summary'                        = $null
                    'Response'                       = $null
                }
                $TcpClient = Test-NetConnection -ComputerName $Computer -Port $P -InformationLevel Detailed -WarningAction SilentlyContinue
                if ($TcpClient.TcpTestSucceeded) {
                    $Output['Status'] = $TcpClient.TcpTestSucceeded
                    $Output['Summary'] = "TCP $P Successful"
                } else {
                    $Output['Status'] = $false
                    $Output['Summary'] = "TCP $P Failed"
                    $Output['Response'] = $Warnings
                }
                [PSCustomObject]$Output
            }
            foreach ($P in $PortUDP) {
                $Output = [ordered] @{'ComputerName' = $Computer
                    'Port'                           = $P
                    'Protocol'                       = 'UDP'
                    'Status'                         = $null
                    'Summary'                        = $null
                }
                $UdpClient = [System.Net.Sockets.UdpClient]::new($Computer, $P)
                $UdpClient.Client.ReceiveTimeout = $Timeout
                $Encoding = [System.Text.ASCIIEncoding]::new()
                $byte = $Encoding.GetBytes("Evotec")
                [void]$UdpClient.Send($byte, $byte.length)
                $RemoteEndpoint = [System.Net.IPEndPoint]::new([System.Net.IPAddress]::Any, 0)
                try {
                    $Bytes = $UdpClient.Receive([ref]$RemoteEndpoint)
                    [string]$Data = $Encoding.GetString($Bytes)
                    If ($Data) {
                        $Output['Status'] = $true
                        $Output['Summary'] = "UDP $P Successful"
                        $Output['Response'] = $Data
                    }
                } catch {
                    $Output['Status'] = $false
                    $Output['Summary'] = "UDP $P Failed"
                    $Output['Response'] = $_.Exception.Message
                }
                $UdpClient.Close()
                $UdpClient.Dispose()
                [PSCustomObject]$Output
            }
        }
    }
    end { if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress } }
}
function Test-WinRM { 
    [CmdletBinding()]
    param ([alias('Server')][string[]] $ComputerName)
    $Output = foreach ($Computer in $ComputerName) {
        $Test = [PSCustomObject] @{Output = $null
            Status                        = $null
            ComputerName                  = $Computer
        }
        try {
            $Test.Output = Test-WSMan -ComputerName $Computer -ErrorAction Stop
            $Test.Status = $true
        } catch { $Test.Status = $false }
        $Test
    }
    $Output
}
function ConvertTo-TableFormat {
    <#
    .SYNOPSIS
        Rebuild an object based on the Format Data for the object.
    .DESCRIPTION
        Allows an object to be rebuilt based on the view data for the object. Uses Select-Object to create a new PSCustomObject.
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        [Object]$InputObject
    )
    begin {
        $isFirst = $true
    }
    process {
        $format = if ($isFirst) {
            $formatData = Get-FormatData -TypeName $InputObject.PSTypeNames | Select-Object -First 1
            if ($formatData) {
                $viewDefinition = $formatData.FormatViewDefinition | Where-Object Control -match 'TableControl'

                for ($i = 0; $i -lt $viewDefinition.Control.Headers.Count; $i++) {
                    $name = $viewDefinition.Control.Headers[$i].Label

                    $displayEntry = $viewDefinition.Control.Rows.Columns[$i].DisplayEntry
                    if (-not $name) {
                        $name = $displayEntry.Value
                    }

                    $expression = switch ($displayEntry.ValueType) {
                        'Property' { $displayEntry.Value }
                        'ScriptBlock' { [ScriptBlock]::Create($displayEntry.Value) }
                    }

                    @{ Name = $name; Expression = $expression }
                }
            }
        }
        if ($format) {
            $InputObject | Select-Object -Property $format
        } else {
            $InputObject
        }
    }
}
function Get-XMLGPO {
    [cmdletBinding()]
    param(
        [XML] $XMLContent,
        [Microsoft.GroupPolicy.Gpo] $GPO
    )
    if ($XMLContent.GPO.LinksTo) {
        $Linked = $true
        $LinksCount = ([Array] $XMLContent.GPO.LinksTo).Count
    } else {
        $Linked = $false
        $LinksCount = 0
    }

    # 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
    }
    if ($XMLContent.GPO.User.Enabled -eq 'False') {
        $UserEnabled = $false
    } elseif ($XMLContent.GPO.User.Enabled -eq 'True') {
        $UserEnabled = $true
    }
    # Translate Enabled to same as GPO GUI
    if ($UserEnabled -eq $True -and $ComputerEnabled -eq $true) {
        $Enabled = 'Enabled'
    } elseif ($UserEnabled -eq $false -and $ComputerEnabled -eq $false) {
        $Enabled = 'All settings disabled'
    } elseif ($UserEnabled -eq $true -and $ComputerEnabled -eq $false) {
        $Enabled = 'Computer configuration settings disabled'
    } elseif ($UserEnabled -eq $false -and $ComputerEnabled -eq $true) {
        $Enabled = 'User configuration settings disabled'
    }

    [PsCustomObject] @{
        'Name'                              = $XMLContent.GPO.Name
        'Domain'                            = $XMLContent.GPO.Identifier.Domain.'#text'
        'GUID'                              = $XMLContent.GPO.Identifier.Identifier.InnerText
        'Linked'                            = $Linked
        'LinksCount'                        = $LinksCount
        'Enabled'                           = $Enabled
        'ComputerEnabled'                   = $ComputerEnabled
        'UserEnabled'                       = $UserEnabled
        'ComputerSettingsAvailable'         = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
        'UserSettingsAvailable'             = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
        'ComputerSettingsStatus'            = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
        'ComputerSetttingsVersionIdentical' = 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

        'CreationTime'                      = [DateTime] $XMLContent.GPO.CreatedTime
        'ModificationTime'                  = [DateTime] $XMLContent.GPO.ModifiedTime
        'ReadTime'                          = [DateTime] $XMLContent.GPO.ReadTime

        'WMIFilter'                         = $GPO.WmiFilter.name
        'WMIFilterDescription'              = $GPO.WmiFilter.Description
        'Path'                              = $GPO.Path
        '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'
        'ACL'                               = $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
            if ($_) {
                [PsCustomObject] @{
                    'Name'           = $_.trustee.name.'#Text'
                    'Sid'            = $_.trustee.SID.'#Text'
                    'PermissionType' = $_.type.PermissionType
                    'Inherited'      = $_.Inherited
                    'Permissions'    = $_.Standard.GPOGroupedAccessEnum
                }
            }
        }
        'Auditing'                          = if ($XMLContent.GPO.SecurityDescriptor.AuditingPresent.'#text' -eq 'true') { $true } else { $false }
        'Links'                             = $XMLContent.GPO.LinksTo | ForEach-Object -Process {
            if ($_) {
                [PSCustomObject] @{
                    CanonicalName = $_.SOMPath
                    Enabled       = $_.Enabled
                    NoOverride    = $_.NoOverride
                }
            }
        }
        <#
        SOMName SOMPath Enabled NoOverride
        ------- ------- ------- ----------
        ad ad.evotec.xyz true false
        #>


        #| Select-Object -ExpandProperty SOMPath

    }
    #break
}
$Script:GPOPropetiesComputers = [ordered] @{
    'Account'                       = ''
    'Audit'                         = ''
    'AuditSetting'                  = ''
    'AutoEnrollmentSettings'        = ''
    'Blocked'                       = ''
    'certSettingsTrustedPublishers' = ''
    'DataSourcesSettings'           = ''
    'DomainProfile'                 = ''
    'Dot3SvcSetting'                = ''
    'EFSRecoveryAgent'              = ''
    'EFSSettings'                   = ''
    'EnvironmentVariables'          = ''
    'EventLog'                      = ''
    'File'                          = ''
    'FilesSettings'                 = ''
    'Folders'                       = ''
    'General'                       = ''
    'Global'                        = ''
    'GlobalSettings'                = ''
    'InboundFirewallRules'          = ''
    'IntermediateCACertificate'     = ''
    'InternetZoneRule'              = ''
    'LocalUsersAndGroups'           = ''
    'MsiApplication'                = ''
    'NetworkOptions'                = ''
    'NetworkShares'                 = ''
    'NTServices'                    = ''
    'OutboundFirewallRules'         = ''
    'PathRule'                      = ''
    'Policy'                        = ''
    'PowerOptions'                  = ''
    'PrinterConnection'             = ''
    'Printers'                      = ''
    'PrivateProfile'                = ''
    'PublicProfile'                 = ''
    'Registry'                      = ''
    'RegistrySetting'               = ''
    'RegistrySettings'              = ''
    'RestrictedGroups'              = ''
    'RootCertificate'               = ''
    'RootCertificateSettings'       = ''
    'ScheduledTasks'                = ''
    'Script'                        = ''
    'SecurityOptions'               = ''
    'ShortcutSettings'              = ''
    'SystemServices'                = ''
    'TrustedPublishersCertificate'  = ''
    'type'                          = ''
    'UserRightsAssignment'          = ''
    'WLanSvcSetting'                = ''
}
$Script:GPOPropertiesUsers = [ordered] @{
    'AutoDetectConfigSettings'     = ''
    'AutoEnrollmentSettings'       = ''
    'AutomaticConfiguration'       = ''
    'AutoSetupSetting'             = ''
    'Blocked'                      = ''
    'BrowserTitle'                 = ''
    'CustomSetupSetting'           = ''
    'DataSourcesSettings'          = ''
    'DefinesConnectionSettings'    = ''
    'DefinesEscOffSettings'        = ''
    'DefinesEscOnSettings'         = ''
    'DeleteChannels'               = ''
    'DriveMapSettings'             = ''
    'EscOffLocalSites'             = ''
    'EscOffSecurityZoneAndPrivacy' = ''
    'EscOffTrustedSites'           = ''
    'EscOnLocalSites'              = ''
    'EscOnSecurityZoneAndPrivacy'  = ''
    'EscOnTrustedSites'            = ''
    'FavoriteURL'                  = ''
    'FilesSettings'                = ''
    'Folder'                       = ''
    'FolderOptions'                = ''
    'Folders'                      = ''
    'General'                      = ''
    'HomePage'                     = ''
    'ImportedContentRatings'       = ''
    'InternetOptions'              = ''
    'LocalUsersAndGroups'          = ''
    'MsiApplication'               = ''
    'NetworkOptions'               = ''
    'PathRule'                     = ''
    'PlaceFavoritesAtTop'          = ''
    'Policy'                       = ''
    'PowerOptions'                 = ''
    'PreferenceMode'               = ''
    'PrinterConnection'            = ''
    'Printers'                     = ''
    'Programs'                     = ''
    'ProxySettings'                = ''
    'RegionalOptionsSettings'      = ''
    'RegistrySetting'              = ''
    'RegistrySettings'             = ''
    'RestartSetupSetting'          = ''
    'ScheduledTasks'               = ''
    'Script'                       = ''
    'SearchBar'                    = ''
    'ShortcutSettings'             = ''
    'StartMenuSettings'            = ''
    'ToolsSetting'                 = ''
    'TrustedPublisherLockdown'     = ''
    'type'                         = ''
}
function Backup-GPOZaurr {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [int] $LimitProcessing,
        [validateset('All', 'Empty', 'Unlinked', 'EmptyAndUnlinked')][string] $Type = 'All',
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation,
        [string[]] $GPOPath,
        [string] $BackupPath,
        [switch] $BackupDated
    )
    Begin {
        if ($BackupDated) {
            $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))"
        } else {
            $BackupFinalPath = $BackupPath
        }
        Write-Verbose "Backup-GPOZaurr - Backing up to $BackupFinalPath"
        $null = New-Item -ItemType Directory -Path $BackupFinalPath -Force
        $Count = 0
    }
    Process {
        Get-GPOZaurr -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -GPOPath $GPOPath | ForEach-Object {
            #$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
            #$GPOSummary = foreach ($GPO in $GPOs) {
            #$QueryServer = $ForestInformation['QueryServers'][$_.Domain]['HostName'][0]
            if ($Type -eq 'All') {
                Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                $Count++
                try {
                    $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                    $BackupInfo
                } catch {
                    Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                }
                if ($LimitProcessing -eq $Count) {
                    break
                }
            } elseif ($Type -eq 'Empty') {
                if ($_.ComputerSettingsAvailable -eq $false -and $_.UserSettingsAvailable -eq $false) {
                    Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                    $Count++
                    try {
                        $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                        $BackupInfo
                    } catch {
                        Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                    }
                    if ($LimitProcessing -eq $Count) {
                        break
                    }
                }
            } elseif ($Type -eq 'EmptyAndUnlinked') {
                if ($_.ComputerSettingsAvailable -eq $false -and $_.UserSettingsAvailable -eq $false -or $_.Linked -eq $false) {
                    Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                    $Count++
                    try {
                        $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                        $BackupInfo
                    } catch {
                        Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                    }
                    if ($LimitProcessing -eq $Count) {
                        break
                    }
                }
            } elseif ($Type -eq 'Unlinked') {
                if ($_.Linked -eq $false) {
                    Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                    $Count++
                    try {
                        $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                        $BackupInfo
                    } catch {
                        Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                    }
                    if ($LimitProcessing -eq $Count) {
                        break
                    }
                }
            }

            #}
            #$GPOSummary
        }
    }
    End {

    }
}
function Get-GPOZaurr {
    [cmdletBinding()]
    param(
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation,
        [string[]] $GPOPath
    )
    Begin {
        if (-not $GPOPath) {
            $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
        }
    }
    Process {
        if (-not $GPOPath) {
            foreach ($Domain in $ForestInformation.Domains) {
                Get-GPO -All -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain | ForEach-Object {
                    Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
                    $XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain
                    Get-XMLGPO -XMLContent $XMLContent -GPO $_
                }
            }
        } else {
            foreach ($Path in $GPOPath) {
                Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml | ForEach-Object {
                    $XMLContent = [XML]::new()
                    $XMLContent.Load($_.FullName)
                    Get-XMLGPO -XMLContent $XMLContent
                }
            }
        }
    }
    End {

    }
}
function Get-GPOZaurrBackupInformation {
    [cmdletBinding()]
    param(
        [string[]] $BackupFolder
    )
    Begin {

    }
    Process {
        foreach ($Folder in $BackupFolder) {
            if ($Folder) {
                if ((Test-Path -LiteralPath "$Folder\manifest.xml")) {
                    [xml] $Xml = Get-Content -LiteralPath "$Folder\manifest.xml"
                    $Xml.Backups.BackupInst | ForEach-Object {
                        [PSCustomObject] @{
                            DisplayName      = $_.GPODisplayName.'#cdata-section'
                            Domain           = $_.GPODomain.'#cdata-section'
                            Guid             = $_.GPOGUid.'#cdata-section'
                            DomainGuid       = $_.GPODomainGuid.'#cdata-section'
                            DomainController = $_.GPODomainController.'#cdata-section'
                            BackupTime       = $_.BackupTime.'#cdata-section'
                            ID               = $_.ID.'#cdata-section'
                            Comment          = $_.Comment.'#cdata-section'
                        }
                    }
                } else {
                    Write-Warning "Get-GPOZaurrBackupInformation - No backup information available"
                }
            }
        }
    }
    End {

    }
}
function Get-GPOZaurrPassword {
    [cmdletBinding()]
    param(
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation,
        [string[]] $GPOPath
    )
    if (-not $GPOPath) {
        if (-not $ExtendedForestInformation) {
            $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
        } else {
            $ForestInformation = $ExtendedForestInformation
        }

        $GPOPath = foreach ($Domain in $ForestInformation.Domains) {
            -join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies')
        }
    }
    if (-not $GPOPath) {
        return
    }
    foreach ($Path in $GPOPath) {
        #Extract the all XML files in the Folders
        $Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml
        $Output = foreach ($XMLFileName in $Items) {
            #Convert XML in a String file
            [string]$XMLString = Get-Content ($XMLFileName.FullName)
            #Check if Cpassword Exist in the file
            if ($XMLString.Contains("cpassword")) {
                #Take the Cpassword Value from XML String file
                [string]$Cpassword = [regex]::matches($XMLString, '(cpassword=).+?(?=\")')
                $Cpassword = $Cpassword.split('(\")')[1]
                #Check if Cpassword has a value
                if ($Cpassword.Length -gt 20 -and $Cpassword -notlike '*cpassword*') {
                    $Mod = ($Cpassword.length % 4)
                    switch ($Mod) {
                        '1' { $Cpassword = $Cpassword.Substring(0, $Cpassword.Length - 1) }
                        '2' { $Cpassword += ('=' * (4 - $Mod)) }
                        '3' { $Cpassword += ('=' * (4 - $Mod)) }
                    }
                    $Base64Decoded = [Convert]::FromBase64String($Cpassword)
                    $AesObject = [System.Security.Cryptography.AesCryptoServiceProvider]::new()
                    #Use th AES Key
                    [Byte[]] $AesKey = @(0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b)
                    $AesIV = New-Object Byte[]($AesObject.IV.Length)
                    $AesObject.IV = $AesIV
                    $AesObject.Key = $AesKey
                    $DecryptorObject = $AesObject.CreateDecryptor()
                    [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
                    #Convert Hash variable in a String valute
                    $Password = [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
                } else {
                    $Password = ''
                }
                #[string]$GPOguid = [regex]::matches($XMLFileName.DirectoryName, '(?<=\{).+?(?=\})')
                #$GPODetail = Get-GPO -guid $GPOguid
                [xml] $XMLContent = $XMLString

                #if (-not $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword -and -not $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword) {
                #Write-Host ''
                #}
                if ($Password) {
                    $PasswordStatus = $true
                } else {
                    $PasswordStatus = $false
                }

                [PsCustomObject] @{
                    'Name'                              = $XMLContent.GPO.Name
                    'Links'                             = $XMLContent.GPO.LinksTo #| Select-Object -ExpandProperty SOMPath
                    'Enabled'                           = $XMLContent.GPO.GpoStatus
                    'PasswordStatus'                    = $PasswordStatus
                    #'GPO' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups
                    'User'                              = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.name
                    'Cpassword'                         = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword
                    'CpasswordMap'                      = $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword
                    'Password'                          = $Password
                    'GUID'                              = $XMLContent.GPO.Identifier.Identifier.InnerText

                    'Domain'                            = $XMLContent.GPO.Identifier.Domain

                    'ComputerSettingsAvailable'         = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
                    'ComputerSettingsStatus'            = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
                    'ComputerEnabled'                   = [bool] $XMLContent.GPO.Computer.Enabled
                    'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false }
                    'ComputerSettings'                  = $XMLContent.GPO.Computer.ExtensionData.Extension

                    'UserSettingsAvailable'             = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
                    'UserEnabled'                       = [bool] $XMLContent.GPO.User.Enabled
                    '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


                    'CreationTime'                      = [DateTime] $XMLContent.GPO.CreatedTime
                    'ModificationTime'                  = [DateTime] $XMLContent.GPO.ModifiedTime
                    'ReadTime'                          = [DateTime] $XMLContent.GPO.ReadTime

                    'WMIFilter'                         = $GPO.WmiFilter.name
                    'WMIFilterDescription'              = $GPO.WmiFilter.Description
                    'Path'                              = $GPO.Path
                    #'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' }
                    'ACL'                               = $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
                        [PSCustomObject] @{
                            'User'            = $_.trustee.name.'#Text'
                            'Permission Type' = $_.type.PermissionType
                            'Inherited'       = $_.Inherited
                            'Permissions'     = $_.Standard.GPOGroupedAccessEnum
                        }
                    }

                }
                #Write-Host "I find a Password [ " $Password " ] The GPO named:" $GPODetail" and th file is:" $XMLFileName

            } #if($XMLContent.Contains("cpassword")
        }
        $Output
    }
}
function Get-GPOZaurrPermissions {
    [cmdletBinding()]
    param(
        [switch] $SkipWellKnownGroup,
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation
    )
    $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
    foreach ($Domain in $ForestInformation.Domains) {
        $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
        Get-GPO -All -Domain $Domain -Server $QueryServer | ForEach-Object -Process {
            $GPO = $_

            <#
            DisplayName : ALL | Enable RDP
            DomainName : ad.evotec.xyz
            Owner : EVOTEC\Domain Admins
            Id : 051bcddf-cc11-427b-bdf0-684c0a6e3ddb
            GpoStatus : AllSettingsEnabled
            Description :
            CreationTime : 07.08.2018 12:47:44
            ModificationTime : 07.04.2020 22:09:24
            UserVersion : AD Version: 1, SysVol Version: 1
            ComputerVersion : AD Version: 1, SysVol Version: 1
            WmiFilter :
            #>

            Get-GPPermissions -Guid $GPO.ID -DomainName $GPO.DomainName -All -Server $QueryServer | ForEach-Object -Process {
                $GPOPermission = $_
                #$GPOPermissionFormatted = ConvertTo-TableFormat -InputObject $_
                [PSCustomObject] @{
                    DisplayName       = $GPO.DisplayName # : ALL | Enable RDP
                    GUID              = $GPO.ID
                    DomainName        = $GPO.DomainName  # : ad.evotec.xyz
                    Enabled           = $GPO.GpoStatus
                    Description       = $GPO.Description
                    CreationDate      = $GPO.CreationTime
                    ModificationTime  = $GPO.ModificationTime
                    #Owner = $GPO.Owner # : EVOTEC\Domain Admins
                    #Trustee = $GPOPermission.Trustee # : Domain Admins
                    #TrusteeType = $GPOPermissionFormatted.TrusteeType # # : Group
                    Permission        = $GPOPermission.Permission  # : GpoEditDeleteModifySecurity
                    Inherited         = $GPOPermission.Inherited   # : False
                    Domain            = $GPOPermission.Trustee.Domain  #: EVOTEC
                    DistinguishedName = $GPOPermission.Trustee.DSPath  #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz
                    Name              = $GPOPermission.Trustee.Name    #: Domain Admins
                    Sid               = $GPOPermission.Trustee.Sid     #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512
                    SidType           = $GPOPermission.Trustee.SidType #: Group
                }
            }

            if ($GPO.Owner) {
                $SplittedOwner = $GPO.Owner.Split('\')
                $DomainOwner = $SplittedOwner[0]  #: EVOTEC
                $DomainUserName = $SplittedOwner[1]   #: Domain Admins
            } else {
                $DomainOwner = $GPO.Owner
                $DomainUserName = ''
            }
            [PSCustomObject] @{
                DisplayName       = $GPO.DisplayName # : ALL | Enable RDP
                GUID              = $GPO.GUID
                DomainName        = $GPO.DomainName  # : ad.evotec.xyz
                #Owner = $GPO.Owner # : EVOTEC\Domain Admins
                #Trustee = $GPOPermission.Trustee # : Domain Admins
                #TrusteeType = $GPOPermissionFormatted.TrusteeType # # : Group
                Enabled           = $GPO.GpoStatus
                Description       = $GPO.Description
                CreationDate      = $GPO.CreationTime
                ModificationTime  = $GPO.ModificationTime

                Permission        = 'Owner'  # : GpoEditDeleteModifySecurity
                Inherited         = $false  # : False
                Domain            = $DomainOwner
                DistinguishedName = ''  #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz
                Name              = $DomainUserName
                Sid               = ''     #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512
                SidType           = '' #: Group
            }
        }
    }
}
function Get-GPOZaurrWMI {
    [cmdletBinding()]
    Param(
        [Guid[]] $Guid,
        [string[]] $Name,
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation
    )
    $wmiFilterAttr = "msWMI-Name", "msWMI-Parm1", "msWMI-Parm2", "msWMI-Author", "msWMI-ID", 'CanonicalName', 'Created', 'Modified'

    $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
    foreach ($Domain in $ForestInformation.Domains) {
        $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
        if ($Guid -or $Name) {
            foreach ($N in $Name) {
                try {
                    $ldapFilter = "(&(objectClass=msWMI-Som)(msWMI-Name=$N))"
                    Get-ADObject -LDAPFilter $ldapFilter -Properties $wmiFilterAttr -Server $QueryServer | ForEach-Object -Process {
                        $WMI = $_.'msWMI-Parm2' -split ';'
                        [PSCustomObject] @{
                            Name              = $_.'msWMI-Name'
                            Description       = $_.'msWMI-Parm1'
                            Domain            = $Domain
                            NameSpace         = $WMI[5]
                            Query             = $WMI[6]
                            Author            = $_.'msWMI-Author'
                            ID                = $_.'msWMI-ID'
                            Created           = $_.Created
                            Modified          = $_.Modified
                            ObjectGUID        = $_.'ObjectGUID'
                            CanonicalName     = $_.CanonicalName
                            DistinguishedName = $_.'DistinguishedName'
                        }
                    }
                } catch {
                    Write-Warning "Get-GPOZaurrWMI - Error processing WMI for $Domain`: $($_.Error.Exception)"
                }
            }
            foreach ($G in $GUID) {
                $ldapFilter = "(&(objectClass=msWMI-Som)(Name={$G}))"
                try {
                    Get-ADObject -LDAPFilter $ldapFilter -Properties $wmiFilterAttr -Server $QueryServer | ForEach-Object -Process {
                        $WMI = $_.'msWMI-Parm2' -split ';'
                        [PSCustomObject] @{
                            Name              = $_.'msWMI-Name'
                            Description       = $_.'msWMI-Parm1'
                            Domain            = $Domain
                            NameSpace         = $WMI[5]
                            Query             = $WMI[6]
                            Author            = $_.'msWMI-Author'
                            ID                = $_.'msWMI-ID'
                            Created           = $_.Created
                            Modified          = $_.Modified
                            ObjectGUID        = $_.'ObjectGUID'
                            CanonicalName     = $_.CanonicalName
                            DistinguishedName = $_.'DistinguishedName'
                        }
                    }
                } catch {
                    Write-Warning "Get-GPOZaurrWMI - Error processing WMI for $Domain`: $($_.Error.Exception)"
                }
            }
        } else {
            $ldapFilter = "(objectClass=msWMI-Som)"
            try {
                Get-ADObject -LDAPFilter $ldapFilter -Properties $wmiFilterAttr -Server $QueryServer | ForEach-Object -Process {
                    $WMI = $_.'msWMI-Parm2' -split ';'
                    [PSCustomObject] @{
                        Name              = $_.'msWMI-Name'
                        Description       = $_.'msWMI-Parm1'
                        Domain            = $Domain
                        NameSpace         = $WMI[5]
                        Query             = $WMI[6]
                        Author            = $_.'msWMI-Author'
                        ID                = $_.'msWMI-ID'
                        Created           = $_.Created
                        Modified          = $_.Modified
                        ObjectGUID        = $_.'ObjectGUID'
                        CanonicalName     = $_.CanonicalName
                        DistinguishedName = $_.'DistinguishedName'
                    }
                }
            } catch {
                Write-Warning "Get-GPOZaurrWMI - Error processing WMI for $Domain`: $($_.Error.Exception)"
            }
        }
    }
}
<#
CanonicalName : ad.evotec.xyz/System/WMIPolicy/SOM/{E988C890-BDBC-4946-87B5-BF70F39F4686}
CN : {E988C890-BDBC-4946-87B5-BF70F39F4686}
Created : 08.04.2020 19:04:06
createTimeStamp : 08.04.2020 19:04:06
Deleted :
Description :
DisplayName :
DistinguishedName : CN={E988C890-BDBC-4946-87B5-BF70F39F4686},CN=SOM,CN=WMIPolicy,CN=System,DC=ad,DC=evotec,DC=xyz
dSCorePropagationData : {01.01.1601 01:00:00}
instanceType : 4
isDeleted :
LastKnownParent :
Modified : 08.04.2020 19:04:06
modifyTimeStamp : 08.04.2020 19:04:06
msWMI-Author : przemyslaw.klys@evotec.pl
msWMI-ChangeDate : 20200408170406.280000-000
msWMI-CreationDate : 20200408170406.280000-000
msWMI-ID : {E988C890-BDBC-4946-87B5-BF70F39F4686}
msWMI-Name : Virtual Machines
msWMI-Parm1 : Oh my description
msWMI-Parm2 : 1;3;10;66;WQL;root\CIMv2;SELECT * FROM Win32_ComputerSystem WHERE Model = "Virtual Machine";
Name : {E988C890-BDBC-4946-87B5-BF70F39F4686}
nTSecurityDescriptor : System.DirectoryServices.ActiveDirectorySecurity
ObjectCategory : CN=ms-WMI-Som,CN=Schema,CN=Configuration,DC=ad,DC=evotec,DC=xyz
ObjectClass : msWMI-Som
ObjectGUID : c1ee708d-7a67-46e2-b13f-d11a573d2597
ProtectedFromAccidentalDeletion : False
sDRightsEffective : 15
showInAdvancedViewOnly : True
uSNChanged : 12785589
uSNCreated : 12785589
whenChanged : 08.04.2020 19:04:06
whenCreated : 08.04.2020 19:04:06
#>

function New-GPOZaurrWMI {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter(Mandatory)][string] $Name,
        [string] $Description = ' ',
        [parameter(Mandatory)][string] $Query,
        [switch] $SkipQueryCheck,
        [switch] $Force,

        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation
    )
    if (-not $Forest -and -not $ExcludeDomains -and -not $IncludeDomains -and -not $ExtendedForestInformation) {
        $IncludeDomains = $Env:USERDNSDOMAIN
    }

    if (-not $SkipQueryCheck) {
        try {
            $null = Get-CimInstance -Query $Query -ErrorAction Stop -Verbose:$false
        } catch {
            Write-Warning "New-GPOZaurrWMI - Query error $($_.Exception.Message). Terminating."
            return
        }
    }

    $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
    foreach ($Domain in $ForestInformation.Domains) {
        $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
        $DomainInformation = Get-ADDomain -Server $QueryServer
        $defaultNamingContext = $DomainInformation.DistinguishedName
        #$defaultNamingContext = (Get-ADRootDSE).defaultnamingcontext
        [string] $Author = (([ADSI]"LDAP://<SID=$([System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value)>").UserPrincipalName).ToString()
        [string] $GUID = "{" + ([System.Guid]::NewGuid()) + "}"

        [string] $DistinguishedName = -join ("CN=", $GUID, ",CN=SOM,CN=WMIPolicy,CN=System,", $defaultNamingContext)
        $CurrentTime = (Get-Date).ToUniversalTime()
        [string] $CurrentDate = -join (
            ($CurrentTime.Year).ToString("0000"),
            ($CurrentTime.Month).ToString("00"),
            ($CurrentTime.Day).ToString("00"),
            ($CurrentTime.Hour).ToString("00"),
            ($CurrentTime.Minute).ToString("00"),
            ($CurrentTime.Second).ToString("00"),
            ".",
            ($CurrentTime.Millisecond * 1000).ToString("000000"),
            "-000"
        )

        [Array] $ExistingWmiFilter = Get-GPOZaurrWMI -ExtendedForestInformation $ForestInformation -IncludeDomains $Domain -Name $Name
        if ($ExistingWmiFilter.Count -eq 0) {
            [string] $WMIParm2 = -join ("1;3;10;", $Query.Length.ToString(), ";WQL;root\CIMv2;", $Query , ";")
            $OtherAttributes = @{
                "msWMI-Name"             = $Name
                "msWMI-Parm1"            = $Description
                "msWMI-Parm2"            = $WMIParm2
                "msWMI-Author"           = $Author
                "msWMI-ID"               = $GUID
                "instanceType"           = 4
                "showInAdvancedViewOnly" = "TRUE"
                "distinguishedname"      = $DistinguishedName
                "msWMI-ChangeDate"       = $CurrentDate
                "msWMI-CreationDate"     = $CurrentDate
            }
            $WMIPath = -join ("CN=SOM,CN=WMIPolicy,CN=System,", $defaultNamingContext)

            try {
                Write-Verbose "New-GPOZaurrWMI - Creating WMI filter $Name in $Domain"
                New-ADObject -name $GUID -type "msWMI-Som" -Path $WMIPath -OtherAttributes $OtherAttributes -Server $QueryServer
            } catch {
                Write-Warning "New-GPOZaurrWMI - Creating GPO filter error $($_.Exception.Message). Terminating."
                return
            }
        } else {
            foreach ($_ in $ExistingWmiFilter) {
                Write-Warning "New-GPOZaurrWMI - Skipping creation of GPO because name: $($_.Name) guid: $($_.ID) for $($_.Domain) already exists."
            }
        }
    }
}
function Remove-GPOZaurr {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter(Mandatory)][validateset('Empty', 'Unlinked', 'EmptyAndUnlinked')][string] $Type,
        [int] $LimitProcessing,
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation,
        [string[]] $GPOPath,
        [string] $BackupPath,
        [switch] $BackupDated
    )
    Begin {
        if ($BackupPath) {
            $BackupRequired = $true
            if ($BackupDated) {
                $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))"
            } else {
                $BackupFinalPath = $BackupPath
            }
            Write-Verbose "Remove-GPOZaurr - Backing up to $BackupFinalPath"
            $null = New-Item -ItemType Directory -Path $BackupFinalPath -Force
        } else {
            $BackupRequired = $false
        }
        $Count = 0
    }
    Process {
        Get-GPOZaurr -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -GPOPath $GPOPath | ForEach-Object {
            #$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation

            #$GPOSummary = foreach ($GPO in $GPOs) {
            #$QueryServer = $ForestInformation['QueryServers'][$_.Domain]['HostName'][0]
            if ($Type -eq 'Empty') {
                if ($_.ComputerSettingsAvailable -eq $false -and $_.UserSettingsAvailable -eq $false) {
                    if ($BackupRequired) {
                        try {
                            Write-Verbose "Remove-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                            $BackupInfo = Backup-GPO -Guid $_.Guid -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                            $BackupInfo
                        } catch {
                            Write-Warning "Remove-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"

                        }
                    }
                    if (($BackupRequired -and $BackupInfo) -or (-not $BackupRequired)) {
                        try {
                            Write-Verbose "Remove-GPOZaurr - Removing GPO $($_.Name) from $($_.Domain)"
                            Remove-GPO -Domain $_.Domain -Guid $_.Guid -ErrorAction Stop #-Server $QueryServer
                        } catch {
                            Write-Warning "Remove-GPOZaurr - Removing GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                        }
                    }
                    $Count++
                    if ($LimitProcessing -eq $Count) {
                        break
                    }
                }
            } elseif ($Type -eq 'EmptyAndUnlinked') {
                if ($_.ComputerSettingsAvailable -eq $false -and $_.UserSettingsAvailable -eq $false -or $_.Linked -eq $false) {

                    if ($BackupRequired) {
                        try {
                            Write-Verbose "Remove-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                            $BackupInfo = Backup-GPO -Guid $_.Guid -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                            $BackupInfo
                        } catch {
                            Write-Warning "Remove-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                        }
                    }
                    if (($BackupRequired -and $BackupInfo) -or (-not $BackupRequired)) {
                        try {
                            Write-Verbose "Remove-GPOZaurr - Removing GPO $($_.Name) from $($_.Domain)"
                            Remove-GPO -Domain $_.Domain -Guid $_.Guid -ErrorAction Stop #-Server $QueryServer
                        } catch {
                            Write-Warning "Remove-GPOZaurr - Removing GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                        }
                    }
                    $Count++
                    if ($LimitProcessing -eq $Count) {
                        break
                    }
                }
            } elseif ($Type -eq 'Unlinked') {
                if ($_.Linked -eq $false) {
                    if ($BackupRequired) {
                        try {
                            Write-Verbose "Remove-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain)"
                            $BackupInfo = Backup-GPO -Guid $_.Guid -Domain $_.Domain -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer
                            $BackupInfo
                        } catch {
                            Write-Warning "Remove-GPOZaurr - Backing up GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                        }
                    }
                    if (($BackupRequired -and $BackupInfo) -or (-not $BackupRequired)) {
                        try {
                            Write-Verbose "Remove-GPOZaurr - Removing GPO $($_.Name) from $($_.Domain)"
                            Remove-GPO -Domain $_.Domain -Guid $_.Guid -ErrorAction Stop #-Server $QueryServer
                        } catch {
                            Write-Warning "Remove-GPOZaurr - Removing GPO $($_.Name) from $($_.Domain) failed: $($_.Exception.Message)"
                        }
                    }
                    $Count++
                    if ($LimitProcessing -eq $Count) {
                        break
                    }
                }
            }
            #}
            #$GPOSummary
        }
    }
    End {

    }
}
function Remove-GPOZaurrWMI {
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [Guid[]] $Guid,
        [string[]] $Name,

        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation
    )
    if (-not $Forest -and -not $ExcludeDomains -and -not $IncludeDomains -and -not $ExtendedForestInformation) {
        $IncludeDomains = $Env:USERDNSDOMAIN
    }
    $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
    foreach ($Domain in $ForestInformation.Domains) {
        $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
        [Array] $Objects = @(
            if ($Guid) {
                Get-GPOZaurrWMI -Guid $Guid -ExtendedForestInformation $ForestInformation -IncludeDomains $Domain
            }
            if ($Name) {
                Get-GPOZaurrWMI -Name $Name -ExtendedForestInformation $ForestInformation -IncludeDomains $Domain
            }
        )
        $Objects | ForEach-Object -Process {
            if ($_.DistinguishedName) {
                Write-Verbose "Remove-GPOZaurrWMI - Removing WMI Filter $($_.DistinguishedName)"
                Remove-ADObject $_.DistinguishedName -Confirm:$false -Server $QueryServer
            }
        }
    }
}
function Restore-GPOZaurr {
    [cmdletBinding()]
    param(
        [parameter(Mandatory)][string] $BackupFolder,
        [alias('Name')][string] $DisplayName,
        [string] $NewDisplayName,
        [string] $Domain,
        [switch] $SkipBackupSummary
    )
    if ($BackupFolder) {
        if (Test-Path -LiteralPath $BackupFolder) {
            if ($DisplayName) {
                if (-not $SkipBackupSummary) {
                    $BackupSummary = Get-GPOZaurrBackupInformation -BackupFolder $BackupFolder
                    if ($Domain) {
                        [Array] $FoundGPO = $BackupSummary | Where-Object { $_.DisplayName -eq $DisplayName -and $_.Domain -eq $Domain }
                    } else {
                        [Array] $FoundGPO = $BackupSummary | Where-Object { $_.DisplayName -eq $DisplayName }
                    }
                    foreach ($GPO in $FoundGPO) {
                        if ($NewDisplayName) {
                            Import-GPO -Path $BackupFolder -BackupID $GPO.ID -Domain $GPO.Domain -TargetName $NewDisplayName -CreateIfNeeded
                        } else {
                            Write-Verbose "Restore-GPOZaurr - Restoring GPO $($GPO.DisplayName) from $($GPO.Domain) / BackupId: $($GPO.ID)"
                            try {
                                Restore-GPO -Path $BackupFolder -BackupID $GPO.ID -Domain $GPO.Domain
                            } catch {
                                Write-Warning "Restore-GPOZaurr - Restoring GPO $($GPO.DisplayName) from $($GPO.Domain) failed: $($_.Exception.Message)"
                            }
                        }
                    }
                } else {
                    if ($Domain) {
                        Write-Verbose "Restore-GPOZaurr - Restoring GPO $($Name) from $($Domain)"
                        try {
                            Restore-GPO -Path $BackupFolder -Name $Name -Domain $Domain
                        } catch {
                            Write-Warning "Restore-GPOZaurr - Restoring GPO $($Name) from $($Domain) failed: $($_.Exception.Message)"
                        }
                    } else {
                        Write-Verbose "Restore-GPOZaurr - Restoring GPO $($Name)"
                        try {
                            Restore-GPO -Path $BackupFolder -Name $Name
                        } catch {
                            Write-Warning "Restore-GPOZaurr - Restoring GPO $($Name) failed: $($_.Exception.Message)"
                        }
                    }
                }
            } else {
                $BackupSummary = Get-GPOZaurrBackupInformation -BackupFolder $BackupFolder
                foreach ($GPO in $BackupSummary) {
                    Write-Verbose "Restore-GPOZaurr - Restoring GPO $($GPO.DisplayName) from $($GPO.Domain) / BackupId: $($GPO.ID)"
                    try {
                        Restore-GPO -Path $BackupFolder -Domain $GPO.Domain -BackupId $GPO.ID
                    } catch {
                        Write-Warning "Restore-GPOZaurr - Restoring GPO $($GPO.DisplayName) from $($GPO.Domain) failed: $($_.Exception.Message)"
                    }
                }
            }
        } else {
            Write-Warning "Restore-GPOZaurr - BackupFolder incorrect ($BackupFolder)"
        }
    }
}
function Save-GPOZaurrFiles {
    [cmdletBinding()]
    param(
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation,
        [string[]] $GPOPath,
        [switch] $DeleteExisting
    )
    if ($GPOPath) {
        if (-not $ExtendedForestInformation) {
            $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
        } else {
            $ForestInformation = $ExtendedForestInformation
        }

        if ($DeleteExisting) {
            $Test = Test-Path -LiteralPath $GPOPath
            if ($Test) {
                Write-Verbose "Save-GPOZaurrFiles - Removing existing content in $GPOPath"
                Remove-Item -LiteralPath $GPOPath -Recurse
            }
        }

        $null = New-Item -ItemType Directory -Path $GPOPath -Force
        foreach ($Domain in $ForestInformation.Domains) {
            Write-Verbose "Save-GPOZaurrFiles - Processing GPO for $Domain"
            Get-GPO -All -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain | ForEach-Object {
                $XMLContent = Get-GPOReport -ID $_.ID.Guid -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain
                $Path = [io.path]::Combine($GPOPath, "$($_.ID.Guid).xml")

                $XMLContent | Set-Content -LiteralPath $Path -Force -Encoding Unicode
            }
        }
    }
}



Export-ModuleMember -Function @('Backup-GPOZaurr', 'Get-GPOZaurr', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermissions', 'Get-GPOZaurrWMI', 'New-GPOZaurrWMI', 'Remove-GPOZaurr', 'Remove-GPOZaurrWMI', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles') -Alias @()