GPOZaurr.psm1
function ConvertFrom-DistinguishedName { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER DistinguishedName Parameter description .PARAMETER ToOrganizationalUnit Parameter description .PARAMETER ToDC Parameter description .PARAMETER ToDomainCN Parameter description .EXAMPLE $DistinguishedName = 'CN=Przemyslaw Klys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName -ToOrganizationalUnit Output: OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz .EXAMPLE $DistinguishedName = 'CN=Przemyslaw Klys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName Output: Przemyslaw Klys .NOTES General notes #> [CmdletBinding()] param([alias('Identity', 'DN')][Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)][string[]] $DistinguishedName, [switch] $ToOrganizationalUnit, [switch] $ToDC, [switch] $ToDomainCN) process { foreach ($Distinguished in $DistinguishedName) { if ($ToDomainCN) { $DN = $Distinguished -replace '.*?((DC=[^=]+,)+DC=[^=]+)$', '$1' $CN = $DN -replace ',DC=', '.' -replace "DC=" $CN } elseif ($ToOrganizationalUnit) { [Regex]::Match($Distinguished, '(?=OU=)(.*\n?)(?<=.)').Value } elseif ($ToDC) { $Distinguished -replace '.*?((DC=[^=]+,)+DC=[^=]+)$', '$1' } else { $Regex = '^CN=(?<cn>.+?)(?<!\\),(?<ou>(?:(?:OU|CN).+?(?<!\\),)+(?<dc>DC.+?))$' $Output = foreach ($_ in $Distinguished) { $_ -match $Regex $Matches } $Output.cn } } } } function ConvertFrom-SID { <# .SYNOPSIS Small command that can resolve SID values .DESCRIPTION Small command that can resolve SID values .PARAMETER SID Value to resolve .PARAMETER OnlyWellKnown Only resolve SID when it's well know SID. Otherwise return $null .PARAMETER OnlyWellKnownAdministrative Only resolve SID when it's administrative well know SID. Otherwise return $null .PARAMETER DoNotResolve Uses only dicrionary values without querying AD .EXAMPLE ConvertFrom-SID -SID 'S-1-5-8', 'S-1-5-9', 'S-1-5-11', 'S-1-5-18', 'S-1-1-0' -DoNotResolve .NOTES General notes #> [cmdletbinding(DefaultParameterSetName = 'Standard')] param([Parameter(ParameterSetName = 'Standard')] [Parameter(ParameterSetName = 'OnlyWellKnown')] [Parameter(ParameterSetName = 'OnlyWellKnownAdministrative')] [string[]] $SID, [Parameter(ParameterSetName = 'OnlyWellKnown')][switch] $OnlyWellKnown, [Parameter(ParameterSetName = 'OnlyWellKnownAdministrative')][switch] $OnlyWellKnownAdministrative, [Parameter(ParameterSetName = 'Standard')][switch] $DoNotResolve) $WellKnownAdministrative = @{'S-1-5-18' = [PSCustomObject] @{Name = 'NT AUTHORITY\SYSTEM' SID = 'S-1-5-18' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-5-32-544' = [PSCustomObject] @{Name = 'BUILTIN\Administrators' SID = 'S-1-5-32-544' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } } $wellKnownSIDs = @{'S-1-0' = [PSCustomObject] @{Name = 'Null AUTHORITY' SID = 'S-1-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-0-0' = [PSCustomObject] @{Name = 'NULL SID' SID = 'S-1-0-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-1' = [PSCustomObject] @{Name = 'WORLD AUTHORITY' SID = 'S-1-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-1-0' = [PSCustomObject] @{Name = 'Everyone' SID = 'S-1-1-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-2' = [PSCustomObject] @{Name = 'LOCAL AUTHORITY' SID = 'S-1-2' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-2-0' = [PSCustomObject] @{Name = 'LOCAL' SID = 'S-1-2-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-2-1' = [PSCustomObject] @{Name = 'CONSOLE LOGON' SID = 'S-1-2-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3' = [PSCustomObject] @{Name = 'CREATOR AUTHORITY' SID = 'S-1-3' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-0' = [PSCustomObject] @{Name = 'CREATOR OWNER' SID = 'S-1-3-0' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-3-1' = [PSCustomObject] @{Name = 'CREATOR GROUP' SID = 'S-1-3-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-2' = [PSCustomObject] @{Name = 'CREATOR OWNER SERVER' SID = 'S-1-3-2' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-3' = [PSCustomObject] @{Name = 'CREATOR GROUP SERVER' SID = 'S-1-3-3' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-4' = [PSCustomObject] @{Name = 'OWNER RIGHTS' SID = 'S-1-3-4' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-80-0' = [PSCustomObject] @{Name = 'NT SERVICE\ALL SERVICES' SID = 'S-1-5-80-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-4' = [PSCustomObject] @{Name = 'Non-unique Authority' SID = 'S-1-4' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5' = [PSCustomObject] @{Name = 'NT AUTHORITY' SID = 'S-1-5' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-1' = [PSCustomObject] @{Name = 'NT AUTHORITY\DIALUP' SID = 'S-1-5-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-2' = [PSCustomObject] @{Name = 'NT AUTHORITY\NETWORK' SID = 'S-1-5-2' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-3' = [PSCustomObject] @{Name = 'NT AUTHORITY\BATCH' SID = 'S-1-5-3' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-4' = [PSCustomObject] @{Name = 'NT AUTHORITY\INTERACTIVE' SID = 'S-1-5-4' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-6' = [PSCustomObject] @{Name = 'NT AUTHORITY\SERVICE' SID = 'S-1-5-6' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-7' = [PSCustomObject] @{Name = 'NT AUTHORITY\ANONYMOUS LOGON' SID = 'S-1-5-7' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-8' = [PSCustomObject] @{Name = 'NT AUTHORITY\PROXY' SID = 'S-1-5-8' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-9' = [PSCustomObject] @{Name = 'NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS' SID = 'S-1-5-9' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-10' = [PSCustomObject] @{Name = 'NT AUTHORITY\SELF' SID = 'S-1-5-10' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-11' = [PSCustomObject] @{Name = 'NT AUTHORITY\Authenticated Users' SID = 'S-1-5-11' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-12' = [PSCustomObject] @{Name = 'NT AUTHORITY\RESTRICTED' SID = 'S-1-5-12' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-13' = [PSCustomObject] @{Name = 'NT AUTHORITY\TERMINAL SERVER USER' SID = 'S-1-5-13' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-14' = [PSCustomObject] @{Name = 'NT AUTHORITY\REMOTE INTERACTIVE LOGON' SID = 'S-1-5-14' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-15' = [PSCustomObject] @{Name = 'NT AUTHORITY\This Organization' SID = 'S-1-5-15' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-17' = [PSCustomObject] @{Name = 'NT AUTHORITY\IUSR' SID = 'S-1-5-17' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-18' = [PSCustomObject] @{Name = 'NT AUTHORITY\SYSTEM' SID = 'S-1-5-18' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-5-19' = [PSCustomObject] @{Name = 'NT AUTHORITY\NETWORK SERVICE' SID = 'S-1-5-19' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-20' = [PSCustomObject] @{Name = 'NT AUTHORITY\NETWORK SERVICE' SID = 'S-1-5-20' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-544' = [PSCustomObject] @{Name = 'BUILTIN\Administrators' SID = 'S-1-5-32-544' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-5-32-545' = [PSCustomObject] @{Name = 'BUILTIN\Users' SID = 'S-1-5-32-545' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-546' = [PSCustomObject] @{Name = 'BUILTIN\Guests' SID = 'S-1-5-32-546' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-547' = [PSCustomObject] @{Name = 'BUILTIN\Power Users' SID = 'S-1-5-32-547' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-548' = [PSCustomObject] @{Name = 'BUILTIN\Account Operators' SID = 'S-1-5-32-548' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-549' = [PSCustomObject] @{Name = 'BUILTIN\Server Operators' SID = 'S-1-5-32-549' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-550' = [PSCustomObject] @{Name = 'BUILTIN\Print Operators' SID = 'S-1-5-32-550' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-551' = [PSCustomObject] @{Name = 'BUILTIN\Backup Operators' SID = 'S-1-5-32-551' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-552' = [PSCustomObject] @{Name = 'BUILTIN\Replicators' SID = 'S-1-5-32-552' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-64-10' = [PSCustomObject] @{Name = 'NT AUTHORITY\NTLM Authentication' SID = 'S-1-5-64-10' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-64-14' = [PSCustomObject] @{Name = 'NT AUTHORITY\SChannel Authentication' SID = 'S-1-5-64-14' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-64-21' = [PSCustomObject] @{Name = 'NT AUTHORITY\Digest Authentication' SID = 'S-1-5-64-21' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-80' = [PSCustomObject] @{Name = 'NT SERVICE' SID = 'S-1-5-80' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-83-0' = [PSCustomObject] @{Name = 'NT VIRTUAL MACHINE\Virtual Machines' SID = 'S-1-5-83-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-0' = [PSCustomObject] @{Name = 'Untrusted Mandatory Level' SID = 'S-1-16-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-4096' = [PSCustomObject] @{Name = 'Low Mandatory Level' SID = 'S-1-16-4096' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-8192' = [PSCustomObject] @{Name = 'Medium Mandatory Level' SID = 'S-1-16-8192' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-8448' = [PSCustomObject] @{Name = 'Medium Plus Mandatory Level' SID = 'S-1-16-8448' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-12288' = [PSCustomObject] @{Name = 'High Mandatory Level' SID = 'S-1-16-12288' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-16384' = [PSCustomObject] @{Name = 'System Mandatory Level' SID = 'S-1-16-16384' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-20480' = [PSCustomObject] @{Name = 'Protected Process Mandatory Level' SID = 'S-1-16-20480' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-28672' = [PSCustomObject] @{Name = 'Secure Process Mandatory Level' SID = 'S-1-16-28672' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-554' = [PSCustomObject] @{Name = 'BUILTIN\Pre-Windows 2000 Compatible Access' SID = 'S-1-5-32-554' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-555' = [PSCustomObject] @{Name = 'BUILTIN\Remote Desktop Users' SID = 'S-1-5-32-555' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-556' = [PSCustomObject] @{Name = 'BUILTIN\Network Configuration Operators' SID = 'S-1-5-32-556' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-557' = [PSCustomObject] @{Name = 'BUILTIN\Incoming Forest Trust Builders' SID = 'S-1-5-32-557' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-558' = [PSCustomObject] @{Name = 'BUILTIN\Performance Monitor Users' SID = 'S-1-5-32-558' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-559' = [PSCustomObject] @{Name = 'BUILTIN\Performance Log Users' SID = 'S-1-5-32-559' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-560' = [PSCustomObject] @{Name = 'BUILTIN\Windows Authorization Access Group' SID = 'S-1-5-32-560' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-561' = [PSCustomObject] @{Name = 'BUILTIN\Terminal Server License Servers' SID = 'S-1-5-32-561' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-562' = [PSCustomObject] @{Name = 'BUILTIN\Distributed COM Users' SID = 'S-1-5-32-562' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-569' = [PSCustomObject] @{Name = 'BUILTIN\Cryptographic Operators' SID = 'S-1-5-32-569' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-573' = [PSCustomObject] @{Name = 'BUILTIN\Event Log Readers' SID = 'S-1-5-32-573' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-574' = [PSCustomObject] @{Name = 'BUILTIN\Certificate Service DCOM Access' SID = 'S-1-5-32-574' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-575' = [PSCustomObject] @{Name = 'BUILTIN\RDS Remote Access Servers' SID = 'S-1-5-32-575' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-576' = [PSCustomObject] @{Name = 'BUILTIN\RDS Endpoint Servers' SID = 'S-1-5-32-576' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-577' = [PSCustomObject] @{Name = 'BUILTIN\RDS Management Servers' SID = 'S-1-5-32-577' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-578' = [PSCustomObject] @{Name = 'BUILTIN\Hyper-V Administrators' SID = 'S-1-5-32-578' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-579' = [PSCustomObject] @{Name = 'BUILTIN\Access Control Assistance Operators' SID = 'S-1-5-32-579' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-580' = [PSCustomObject] @{Name = 'BUILTIN\Remote Management Users' SID = 'S-1-5-32-580' DomainName = '' Type = 'WellKnownGroup' Error = '' } } foreach ($S in $SID) { if ($OnlyWellKnownAdministrative) { if ($WellKnownAdministrative[$S]) { $WellKnownAdministrative[$S] } } elseif ($OnlyWellKnown) { if ($wellKnownSIDs[$S]) { $wellKnownSIDs[$S] } } else { if ($wellKnownSIDs[$S]) { $wellKnownSIDs[$S] } else { if ($DoNotResolve) { if ($S -like "S-1-5-21-*-519" -or $S -like "S-1-5-21-*-512") { [PSCustomObject] @{Name = $S SID = $S DomainName = '' Type = 'Administrative' Error = '' } } else { [PSCustomObject] @{Name = $S SID = $S DomainName = '' Error = '' Type = 'NotAdministrative' } } } else { try { if ($S -like "S-1-5-21-*-519" -or $S -like "S-1-5-21-*-512") { $Type = 'Administrative' } else { $Type = 'NotAdministrative' } $Name = (([System.Security.Principal.SecurityIdentifier]::new($S)).Translate([System.Security.Principal.NTAccount])).Value [PSCustomObject] @{Name = $Name SID = $S DomainName = (ConvertFrom-NetbiosName -Identity $Name).DomainName Type = $Type Error = '' } } catch { [PSCustomObject] @{Name = $S SID = $S DomainName = '' Error = $_.Exception.Message -replace [environment]::NewLine, ' ' Type = 'Unknown' } } } } } } } function Convert-Identity { <# .SYNOPSIS Small command that tries to resolve any given object .DESCRIPTION Small command that tries to resolve any given object - be it SID, DN, FSP or Netbiosname .PARAMETER Identity Type to resolve in form of Identity, DN, SID .PARAMETER SID Allows to pass SID directly, rather then going thru verification process .PARAMETER Name Allows to pass Name directly, rather then going thru verification process .EXAMPLE $Identity = @( 'S-1-5-4' 'S-1-5-4' 'S-1-5-11' 'S-1-5-32-549' 'S-1-5-32-550' 'S-1-5-32-548' 'S-1-5-64-10' 'S-1-5-64-14' 'S-1-5-64-21' 'S-1-5-18' 'S-1-5-19' 'S-1-5-32-544' 'S-1-5-20-20-10-51' # Wrong SID 'S-1-5-21-853615985-2870445339-3163598659-512' 'S-1-5-21-3661168273-3802070955-2987026695-512' 'S-1-5-21-1928204107-2710010574-1926425344-512' 'CN=Test Test 2,OU=Users,OU=Production,DC=ad,DC=evotec,DC=pl' 'Test Local Group' 'przemyslaw.klys@evotec.pl' 'test2' 'NT AUTHORITY\NETWORK' 'NT AUTHORITY\SYSTEM' 'S-1-5-21-853615985-2870445339-3163598659-519' 'TEST\some' 'EVOTECPL\Domain Admins' 'NT AUTHORITY\INTERACTIVE' 'INTERACTIVE' 'EVOTEC\Domain Admins' 'EVOTECPL\Domain Admins' 'Test\Domain Admins' 'CN=S-1-5-21-1928204107-2710010574-1926425344-512,CN=ForeignSecurityPrincipals,DC=ad,DC=evotec,DC=xyz' # Valid 'CN=S-1-5-21-1928204107-2710010574-512,CN=ForeignSecurityPrincipals,DC=ad,DC=evotec,DC=xyz' # not valid 'CN=S-1-5-21-1928204107-2710010574-1926425344-512,CN=ForeignSecurityPrincipals,DC=ad,DC=evotec,DC=xyz' # cached ) $TestOutput = Convert-Identity -Identity $Identity -Verbose Output: Name SID DomainName Type Error ---- --- ---------- ---- ----- NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup NT AUTHORITY\Authenticated Users S-1-5-11 WellKnownGroup BUILTIN\Server Operators S-1-5-32-549 WellKnownGroup BUILTIN\Print Operators S-1-5-32-550 WellKnownGroup BUILTIN\Account Operators S-1-5-32-548 WellKnownGroup NT AUTHORITY\NTLM Authentication S-1-5-64-10 WellKnownGroup NT AUTHORITY\SChannel Authentication S-1-5-64-14 WellKnownGroup NT AUTHORITY\Digest Authentication S-1-5-64-21 WellKnownGroup NT AUTHORITY\SYSTEM S-1-5-18 WellKnownAdministrative NT AUTHORITY\NETWORK SERVICE S-1-5-19 WellKnownGroup BUILTIN\Administrators S-1-5-32-544 WellKnownAdministrative S-1-5-20-20-10-51 S-1-5-20-20-10-51 Unknown Exception calling "Translate" with "1" argument(s): "Some or all identity references could not be translated." EVOTEC\Domain Admins S-1-5-21-853615985-2870445339-3163598659-512 ad.evotec.xyz Administrative EVOTECPL\Domain Admins S-1-5-21-3661168273-3802070955-2987026695-512 ad.evotec.pl Administrative TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative EVOTECPL\TestingAD S-1-5-21-3661168273-3802070955-2987026695-1111 ad.evotec.pl NotAdministrative EVOTEC\Test Local Group S-1-5-21-853615985-2870445339-3163598659-3610 ad.evotec.xyz NotAdministrative EVOTEC\przemyslaw.klys S-1-5-21-853615985-2870445339-3163598659-1105 ad.evotec.xyz NotAdministrative test2 Unknown Exception calling "Translate" with "1" argument(s): "Some or all identity references could not be translated." NT AUTHORITY\NETWORK S-1-5-2 WellKnownGroup NT AUTHORITY\SYSTEM S-1-5-18 WellKnownAdministrative EVOTEC\Enterprise Admins S-1-5-21-853615985-2870445339-3163598659-519 ad.evotec.xyz Administrative TEST\some S-1-5-21-1928204107-2710010574-1926425344-1106 test.evotec.pl NotAdministrative EVOTECPL\Domain Admins S-1-5-21-3661168273-3802070955-2987026695-512 ad.evotec.pl Administrative NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup EVOTEC\Domain Admins S-1-5-21-853615985-2870445339-3163598659-512 ad.evotec.xyz Administrative EVOTECPL\Domain Admins S-1-5-21-3661168273-3802070955-2987026695-512 ad.evotec.pl Administrative TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative S-1-5-21-1928204107-2710010574-512 S-1-5-21-1928204107-2710010574-512 Unknown Exception calling "Translate" with "1" argument(s): "Some or all identity references could not be translated." TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative .NOTES General notes #> [cmdletBinding(DefaultParameterSetName = 'Identity')] param([parameter(ParameterSetName = 'Identity', Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)][string[]] $Identity, [parameter(ParameterSetName = 'SID', Mandatory)][System.Security.Principal.SecurityIdentifier[]] $SID, [parameter(ParameterSetName = 'Name', Mandatory)][string[]] $Name) Begin { if (-not $Script:GlobalCacheSidConvert) { $Script:GlobalCacheSidConvert = @{} } } Process { if ($Identity) { foreach ($Ident in $Identity) { $MatchRegex = [Regex]::Matches($Ident, "S-\d-\d+-(\d+-|){1,14}\d+") if ($Script:GlobalCacheSidConvert[$Ident]) { Write-Verbose "Convert-Identity - Processing $Ident (Cache)" $Script:GlobalCacheSidConvert[$Ident] } elseif ($MatchRegex.Success) { Write-Verbose "Convert-Identity - Processing $Ident (SID)" if ($MatchRegex.Value -ne $Ident) { $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $MatchRegex.Value } else { $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $Ident } $Script:GlobalCacheSidConvert[$Ident] } elseif ($Ident -like '*DC=*') { Write-Verbose "Convert-Identity - Processing $Ident (DistinguishedName)" try { $Object = [adsi]"LDAP://$($Ident)" $SIDValue = [System.Security.Principal.SecurityIdentifier]::new($Object.objectSid.Value, 0).Value $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $SIDValue } catch { $Script:GlobalCacheSidConvert[$Ident] = [PSCustomObject] @{Name = $Ident SID = $null DomainName = '' Type = 'Unknown' Error = $_.Exception.Message -replace [environment]::NewLine, ' ' } } $Script:GlobalCacheSidConvert[$Ident] } else { Write-Verbose "Convert-Identity - Processing $Ident (Other)" try { $SIDValue = ([System.Security.Principal.NTAccount] $Ident).Translate([System.Security.Principal.SecurityIdentifier]).Value $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $SIDValue } catch { $Script:GlobalCacheSidConvert[$Ident] = [PSCustomObject] @{Name = $Ident SID = $null DomainName = '' Type = 'Unknown' Error = $_.Exception.Message -replace [environment]::NewLine, ' ' } } $Script:GlobalCacheSidConvert[$Ident] } } } else { if ($SID) { foreach ($S in $SID) { if ($Script:GlobalCacheSidConvert[$S]) { $Script:GlobalCacheSidConvert[$S] } else { $Script:GlobalCacheSidConvert[$S] = ConvertFrom-SID -SID $S $Script:GlobalCacheSidConvert[$S] } } } else { foreach ($Ident in $Name) { if ($Script:GlobalCacheSidConvert[$Ident]) { $Script:GlobalCacheSidConvert[$Ident] } else { $Script:GlobalCacheSidConvert[$Ident] = ([System.Security.Principal.NTAccount] $Ident).Translate([System.Security.Principal.SecurityIdentifier]).Value $Script:GlobalCacheSidConvert[$Ident] } } } } } End {} } function Copy-Dictionary { [alias('Copy-Hashtable', 'Copy-OrderedHashtable')] [cmdletbinding()] param([System.Collections.IDictionary] $Dictionary) $ms = [System.IO.MemoryStream]::new() $bf = [System.Runtime.Serialization.Formatters.Binary.BinaryFormatter]::new() $bf.Serialize($ms, $Dictionary) $ms.Position = 0 $clone = $bf.Deserialize($ms) $ms.Close() $clone } function Format-ToTitleCase { <# .SYNOPSIS Formats string or number of strings to Title Case .DESCRIPTION Formats string or number of strings to Title Case allowing for prettty display .PARAMETER Text Sentence or multiple sentences to format .PARAMETER RemoveWhiteSpace Removes spaces after formatting string to Title Case. .PARAMETER RemoveChar Array of characters to remove .EXAMPLE Format-ToTitleCase 'me' Output: Me .EXAMPLE 'me i feel good' | Format-ToTitleCase Output: Me I Feel Good Not Feel .EXAMPLE 'me i feel', 'not feel' | Format-ToTitleCase Output: Me I Feel Good Not Feel .EXAMPLE Format-ToTitleCase -Text 'This is my thing' -RemoveWhiteSpace Output: ThisIsMyThing .EXAMPLE Format-ToTitleCase -Text "This is my thing: That - No I don't want all chars" -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' .NOTES General notes #> [CmdletBinding()] param([Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)][string[]] $Text, [switch] $RemoveWhiteSpace, [string[]] $RemoveChar) Begin {} Process { $Conversion = foreach ($T in $Text) { $Output = (Get-Culture).TextInfo.ToTitleCase($T) foreach ($Char in $RemoveChar) { $Output = $Output -replace $Char } if ($RemoveWhiteSpace) { $Output = $Output -replace ' ', '' } $Output } $Conversion } End {} } function Get-ADACL { [cmdletbinding()] param([Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)][Array] $ADObject, [string] $ForestName, [switch] $Extended, [switch] $ResolveTypes, [switch] $Inherited, [switch] $NotInherited, [switch] $Bundle, [System.Security.AccessControl.AccessControlType] $AccessControlType, [string[]] $IncludeObjectTypeName, [string[]] $IncludeInheritedObjectTypeName, [string[]] $ExcludeObjectTypeName, [string[]] $ExcludeInheritedObjectTypeName, [System.DirectoryServices.ActiveDirectoryRights[]] $IncludeActiveDirectoryRights, [System.DirectoryServices.ActiveDirectoryRights[]] $ExcludeActiveDirectoryRights, [System.DirectoryServices.ActiveDirectorySecurityInheritance[]] $IncludeActiveDirectorySecurityInheritance, [System.DirectoryServices.ActiveDirectorySecurityInheritance[]] $ExcludeActiveDirectorySecurityInheritance, [switch] $ADRightsAsArray) Begin { if (-not $Script:ForestGUIDs) { Write-Verbose "Get-ADACL - Gathering Forest GUIDS" $Script:ForestGUIDs = Get-WinADForestGUIDs } } Process { foreach ($Object in $ADObject) { if ($Object -is [Microsoft.ActiveDirectory.Management.ADOrganizationalUnit] -or $Object -is [Microsoft.ActiveDirectory.Management.ADEntity]) { [string] $DistinguishedName = $Object.DistinguishedName [string] $CanonicalName = $Object.CanonicalName.TrimEnd('/') [string] $ObjectClass = $Object.ObjectClass } elseif ($Object -is [string]) { [string] $DistinguishedName = $Object [string] $CanonicalName = '' [string] $ObjectClass = '' } else { Write-Warning "Get-ADACL - Object not recognized. Skipping..." continue } $DNConverted = (ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName -ToDC) -replace '=' -replace ',' if (-not (Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { Write-Verbose "Get-ADACL - Enabling PSDrives for $DistinguishedName to $DNConverted" New-ADForestDrives -ForestName $ForestName if (-not (Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { Write-Warning "Get-ADACL - Drive $DNConverted not mapped. Terminating..." return } } Write-Verbose "Get-ADACL - Getting ACL from $DistinguishedName" try { $PathACL = "$DNConverted`:\$($DistinguishedName)" $ACLs = Get-Acl -Path $PathACL -ErrorAction Stop } catch { Write-Warning "Get-ADACL - Path $PathACL - Error: $($_.Exception.Message)" } $AccessObjects = foreach ($ACL in $ACLs.Access) { [Array] $ADRights = $ACL.ActiveDirectoryRights -split ', ' if ($AccessControlType) { if ($ACL.AccessControlType -ne $AccessControlType) { continue } } if ($Inherited) { if ($ACL.IsInherited -eq $false) { continue } } if ($NotInherited) { if ($ACL.IsInherited -eq $true) { continue } } if ($IncludeActiveDirectoryRights) { $FoundInclude = $false foreach ($Right in $ADRights) { if ($IncludeActiveDirectoryRights -contains $Right) { $FoundInclude = $true break } } if (-not $FoundInclude) { continue } } if ($ExcludeActiveDirectoryRights) { foreach ($Right in $ADRights) { $FoundExclusion = $false if ($ExcludeActiveDirectoryRights -contains $Right) { $FoundExclusion = $true break } if ($FoundExclusion) { continue } } } if ($IncludeActiveDirectorySecurityInheritance) { if ($IncludeActiveDirectorySecurityInheritance -notcontains $ACL.InheritanceType) { continue } } if ($ExcludeActiveDirectorySecurityInheritance) { if ($ExcludeActiveDirectorySecurityInheritance -contains $ACL.InheritanceType) { continue } } $IdentityReference = $ACL.IdentityReference.Value $ReturnObject = [ordered] @{} $ReturnObject['DistinguishedName' ] = $DistinguishedName if ($CanonicalName) { $ReturnObject['CanonicalName'] = $CanonicalName } if ($ObjectClass) { $ReturnObject['ObjectClass'] = $ObjectClass } $ReturnObject['AccessControlType'] = $ACL.AccessControlType $ReturnObject['Principal'] = $IdentityReference if ($ResolveTypes) { $IdentityResolve = Get-WinADObject -Identity $IdentityReference -AddType if (-not $IdentityResolve) { $ConvertIdentity = Convert-Identity -Identity $IdentityReference $ReturnObject['PrincipalType'] = $ConvertIdentity.Type $ReturnObject['PrincipalObjectType'] = 'foreignSecurityPrincipal' $ReturnObject['PrincipalObjectDomain'] = $ConvertIdentity.DomainName $ReturnObject['PrincipalObjectSid'] = $ConvertIdentity.SID } else { if ($ReturnObject['Principal']) { $ReturnObject['Principal'] = $IdentityResolve.Name } $ReturnObject['PrincipalType'] = $IdentityResolve.Type $ReturnObject['PrincipalObjectType'] = $IdentityResolve.ObjectClass $ReturnObject['PrincipalObjectDomain' ] = $IdentityResolve.DomainName $ReturnObject['PrincipalObjectSid'] = $IdentityResolve.ObjectSID } if (-not $ReturnObject['PrincipalObjectDomain']) { $ReturnObject['PrincipalObjectDomain'] = ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName -ToDomainCN } } $ReturnObject['ObjectTypeName'] = $Script:ForestGUIDs["$($ACL.objectType)"] $ReturnObject['InheritedObjectTypeName'] = $Script:ForestGUIDs["$($ACL.inheritedObjectType)"] if ($IncludeObjectTypeName) { if ($IncludeObjectTypeName -notcontains $ReturnObject['ObjectTypeName']) { continue } } if ($IncludeInheritedObjectTypeName) { if ($IncludeInheritedObjectTypeName -notcontains $ReturnObject['InheritedObjectTypeName']) { continue } } if ($ExcludeObjectTypeName) { if ($ExcludeObjectTypeName -contains $ReturnObject['ObjectTypeName']) { continue } } if ($ExcludeInheritedObjectTypeName) { if ($ExcludeInheritedObjectTypeName -contains $ReturnObject['InheritedObjectTypeName']) { continue } } if ($ADRightsAsArray) { $ReturnObject['ActiveDirectoryRights'] = $ADRights } else { $ReturnObject['ActiveDirectoryRights'] = $ACL.ActiveDirectoryRights } $ReturnObject['InheritanceType'] = $ACL.InheritanceType $ReturnObject['IsInherited'] = $ACL.IsInherited if ($Extended) { $ReturnObject['ObjectType'] = $ACL.ObjectType $ReturnObject['InheritedObjectType'] = $ACL.InheritedObjectType $ReturnObject['ObjectFlags'] = $ACL.ObjectFlags $ReturnObject['InheritanceFlags'] = $ACL.InheritanceFlags $ReturnObject['PropagationFlags'] = $ACL.PropagationFlags } if ($Bundle) { $ReturnObject['Bundle'] = $ACL } [PSCustomObject] $ReturnObject } if ($Bundle) { [PSCustomObject] @{DistinguishedName = $DistinguishedName CanonicalName = $Object.CanonicalName ACL = $ACLs ACLAccessRules = $AccessObjects Path = $PathACL } } else { $AccessObjects } } } End {} } function Get-ADACLOwner { [cmdletBinding()] param([Array] $ADObject, [switch] $Resolve, [System.Collections.IDictionary] $ADAdministrativeGroups, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation) Begin { if (-not $ADAdministrativeGroups -and $Resolve) { $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ForestInformation } } Process { foreach ($Object in $ADObject) { if ($Object -is [Microsoft.ActiveDirectory.Management.ADOrganizationalUnit] -or $Object -is [Microsoft.ActiveDirectory.Management.ADEntity]) { [string] $DistinguishedName = $Object.DistinguishedName [string] $CanonicalName = $Object.CanonicalName [string] $ObjectClass = $Object.ObjectClass } elseif ($Object -is [string]) { [string] $DistinguishedName = $Object [string] $CanonicalName = '' [string] $ObjectClass = '' } else { Write-Warning "Get-ADACLOwner - Object not recognized. Skipping..." continue } $DNConverted = (ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName -ToDC) -replace '=' -replace ',' if (-not (Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { Write-Verbose "Get-ADACLOwner - Enabling PSDrives for $DistinguishedName to $DNConverted" New-ADForestDrives -ForestName $ForestName if (-not (Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { Write-Warning "Set-ADACLOwner - Drive $DNConverted not mapped. Terminating..." return } } $PathACL = "$DNConverted`:\$($DistinguishedName)" try { $ACLs = Get-Acl -Path $PathACL -ErrorAction Stop $Hash = [ordered] @{DistinguishedName = $DistinguishedName Owner = $ACLs.Owner ACLs = $ACLs } $ErrorMessage = '' } catch { $Hash = [ordered] @{DistinguishedName = $DistinguishedName Owner = $null ACLs = $null } $ErrorMessage = $_.Exception.Message } if ($Resolve) { if ($null -eq $Hash.Owner) { $Identity = $null } else { $Identity = Convert-Identity -Identity $Hash.Owner } if ($Identity) { $Hash['OwnerName'] = $Identity.Name $Hash['OwnerSid'] = $Identity.SID $Hash['OwnerType'] = $Identity.Type } else { $Hash['OwnerName'] = '' $Hash['OwnerSid'] = '' $Hash['OwnerType'] = '' } } $Hash['Error'] = $ErrorMessage [PSCustomObject] $Hash } } End {} } function Get-ADADministrativeGroups { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Type Parameter description .PARAMETER Forest Parameter description .PARAMETER ExcludeDomains Parameter description .PARAMETER IncludeDomains Parameter description .PARAMETER ExtendedForestInformation Parameter description .EXAMPLE Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins Output (Where VALUE is Get-ADGroup output): Name Value ---- ----- ByNetBIOS {EVOTEC\Domain Admins, EVOTEC\Enterprise Admins, EVOTECPL\Domain Admins} ad.evotec.xyz {DomainAdmins, EnterpriseAdmins} ad.evotec.pl {DomainAdmins} .NOTES General notes #> [cmdletBinding()] param([parameter(Mandatory)][validateSet('DomainAdmins', 'EnterpriseAdmins')][string[]] $Type, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation) $ADDictionary = [ordered] @{} $ADDictionary['ByNetBIOS'] = [ordered] @{} $ADDictionary['BySID'] = [ordered] @{} $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { $ADDictionary[$Domain] = [ordered] @{} $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] $DomainInformation = Get-ADDomain -Server $QueryServer if ($Type -contains 'DomainAdmins') { Get-ADGroup -Filter "SID -eq '$($DomainInformation.DomainSID)-512'" -Server $QueryServer -ErrorAction SilentlyContinue | ForEach-Object { $ADDictionary['ByNetBIOS']["$($DomainInformation.NetBIOSName)\$($_.Name)"] = $_ $ADDictionary[$Domain]['DomainAdmins'] = "$($DomainInformation.NetBIOSName)\$($_.Name)" $ADDictionary['BySID'][$_.SID.Value] = $_ } } } foreach ($Domain in $ForestInformation.Forest.Domains) { if (-not $ADDictionary[$Domain]) { $ADDictionary[$Domain] = [ordered] @{} } if ($Type -contains 'EnterpriseAdmins') { $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] $DomainInformation = Get-ADDomain -Server $QueryServer Get-ADGroup -Filter "SID -eq '$($DomainInformation.DomainSID)-519'" -Server $QueryServer -ErrorAction SilentlyContinue | ForEach-Object { $ADDictionary['ByNetBIOS']["$($DomainInformation.NetBIOSName)\$($_.Name)"] = $_ $ADDictionary[$Domain]['EnterpriseAdmins'] = "$($DomainInformation.NetBIOSName)\$($_.Name)" $ADDictionary['BySID'][$_.SID.Value] = $_ } } } return $ADDictionary } function Get-FileMetaData { <# .SYNOPSIS Small function that gets metadata information from file providing similar output to what Explorer shows when viewing file .DESCRIPTION Small function that gets metadata information from file providing similar output to what Explorer shows when viewing file .PARAMETER File FileName or FileObject .EXAMPLE Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Get-FileMetaData | Out-HtmlView -ScrollX -Filtering -AllProperties .EXAMPLE Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Where-Object { $_.Attributes -like '*Hidden*' } | Get-FileMetaData | Out-HtmlView -ScrollX -Filtering -AllProperties .NOTES #> [CmdletBinding()] param ([Parameter(Position = 0, ValueFromPipeline)][Object] $File, [ValidateSet('None', 'MACTripleDES', 'MD5', 'RIPEMD160', 'SHA1', 'SHA256', 'SHA384', 'SHA512')][string] $HashAlgorithm = 'None', [switch] $Signature, [switch] $AsHashTable) Process { foreach ($F in $File) { $MetaDataObject = [ordered] @{} if ($F -is [string]) { if ($F -and (Test-Path -LiteralPath $F)) { $FileInformation = Get-ItemProperty -Path $F if ($FileInformation -is [System.IO.DirectoryInfo]) { continue } } else { Write-Warning "Get-FileMetaData - Doesn't exists. Skipping $F." continue } } elseif ($F -is [System.IO.DirectoryInfo]) { continue } elseif ($F -is [System.IO.FileInfo]) { $FileInformation = $F } else { Write-Warning "Get-FileMetaData - Only files are supported. Skipping $F." continue } $ShellApplication = New-Object -ComObject Shell.Application $ShellFolder = $ShellApplication.Namespace($FileInformation.Directory.FullName) $ShellFile = $ShellFolder.ParseName($FileInformation.Name) $MetaDataProperties = [ordered] @{} 0..400 | ForEach-Object -Process { $DataValue = $ShellFolder.GetDetailsOf($null, $_) $PropertyValue = (Get-Culture).TextInfo.ToTitleCase($DataValue.Trim()).Replace(' ', '') if ($PropertyValue -ne '') { $MetaDataProperties["$_"] = $PropertyValue } } foreach ($Key in $MetaDataProperties.Keys) { $Property = $MetaDataProperties[$Key] $Value = $ShellFolder.GetDetailsOf($ShellFile, [int] $Key) if ($Property -in 'Attributes', 'Folder', 'Type', 'SpaceFree', 'TotalSize', 'SpaceUsed') { continue } If (($null -ne $Value) -and ($Value -ne '')) { $MetaDataObject["$Property"] = $Value } } if ($FileInformation.VersionInfo) { $SplitInfo = ([string] $FileInformation.VersionInfo).Split([char]13) foreach ($Item in $SplitInfo) { $Property = $Item.Split(":").Trim() if ($Property[0] -and $Property[1] -ne '') { if ($Property[1] -in 'False', 'True') { $MetaDataObject["$($Property[0])"] = [bool] $Property[1] } else { $MetaDataObject["$($Property[0])"] = $Property[1] } } } } $MetaDataObject["Attributes"] = $FileInformation.Attributes $MetaDataObject['IsReadOnly'] = $FileInformation.IsReadOnly $MetaDataObject['IsHidden'] = $FileInformation.Attributes -like '*Hidden*' $MetaDataObject['IsSystem'] = $FileInformation.Attributes -like '*System*' if ($Signature) { $DigitalSignature = Get-AuthenticodeSignature -FilePath $FileInformation.Fullname $MetaDataObject['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject $MetaDataObject['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer $MetaDataObject['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber $MetaDataObject['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore $MetaDataObject['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter $MetaDataObject['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint $MetaDataObject['SignatureStatus'] = $DigitalSignature.Status $MetaDataObject['IsOSBinary'] = $DigitalSignature.IsOSBinary } if ($HashAlgorithm -ne 'None') { $MetaDataObject[$HashAlgorithm] = (Get-FileHash -LiteralPath $FileInformation.FullName -Algorithm $HashAlgorithm).Hash } if ($AsHashTable) { $MetaDataObject } else { [PSCustomObject] $MetaDataObject } } } } function Get-FileName { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Extension Parameter description .PARAMETER Temporary Parameter description .PARAMETER TemporaryFileOnly Parameter description .EXAMPLE Get-FileName -Temporary Output: 3ymsxvav.tmp .EXAMPLE Get-FileName -Temporary Output: C:\Users\pklys\AppData\Local\Temp\tmpD74C.tmp .EXAMPLE Get-FileName -Temporary -Extension 'xlsx' Output: C:\Users\pklys\AppData\Local\Temp\tmp45B6.xlsx .NOTES General notes #> [CmdletBinding()] param([string] $Extension = 'tmp', [switch] $Temporary, [switch] $TemporaryFileOnly) if ($Temporary) { return "$($([System.IO.Path]::GetTempFileName()).Replace('.tmp','')).$Extension" } if ($TemporaryFileOnly) { return "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).$Extension" } } function Get-FileOwner { [cmdletBinding()] param([Array] $Path, [switch] $Recursive, [switch] $JustPath, [switch] $Resolve, [switch] $AsHashTable) Begin {} Process { foreach ($P in $Path) { if ($P -is [System.IO.FileSystemInfo]) { $FullPath = $P.FullName } elseif ($P -is [string]) { $FullPath = $P } if ($FullPath -and (Test-Path -Path $FullPath)) { if ($JustPath) { $FullPath | ForEach-Object -Process { $ACL = Get-Acl -Path $_ $Object = [ordered]@{FullName = $_ Owner = $ACL.Owner } if ($Resolve) { $Identity = Convert-Identity -Identity $ACL.Owner if ($Identity) { $Object['OwnerName'] = $Identity.Name $Object['OwnerSid'] = $Identity.SID $Object['OwnerType'] = $Identity.Type } else { $Object['OwnerName'] = '' $Object['OwnerSid'] = '' $Object['OwnerType'] = '' } } if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } } else { Get-ChildItem -LiteralPath $FullPath -Recurse:$Recursive -Force | ForEach-Object -Process { $File = $_ $ACL = Get-Acl -Path $File.FullName $Object = [ordered] @{FullName = $_.FullName Extension = $_.Extension CreationTime = $_.CreationTime LastAccessTime = $_.LastAccessTime LastWriteTime = $_.LastWriteTime Attributes = $_.Attributes Owner = $ACL.Owner } if ($Resolve) { $Identity = Convert-Identity -Identity $ACL.Owner if ($Identity) { $Object['OwnerName'] = $Identity.Name $Object['OwnerSid'] = $Identity.SID $Object['OwnerType'] = $Identity.Type } else { $Object['OwnerName'] = '' $Object['OwnerSid'] = '' $Object['OwnerType'] = '' } } if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } } } } } End {} } function Get-FilePermission { [alias('Get-PSPermissions', 'Get-FilePermissions')] [cmdletBinding()] param([Array] $Path, [switch] $Inherited, [switch] $NotInherited, [switch] $ResolveTypes, [switch] $Extended, [switch] $IncludeACLObject, [switch] $AsHashTable, [System.Security.AccessControl.FileSystemSecurity] $ACLS) foreach ($P in $Path) { if ($P -is [System.IO.FileSystemInfo]) { $FullPath = $P.FullName } elseif ($P -is [string]) { $FullPath = $P } $TestPath = Test-Path -Path $FullPath if ($TestPath) { if (-not $ACLS) { try { $ACLS = (Get-Acl -Path $FullPath -ErrorAction Stop) } catch { Write-Warning -Message "Get-FilePermission - Can't access $FullPath. Error $($_.Exception.Message)" continue } } $Output = foreach ($ACL in $ACLS.Access) { if ($Inherited) { if ($ACL.IsInherited -eq $false) { continue } } if ($NotInherited) { if ($ACL.IsInherited -eq $true) { continue } } $TranslateRights = Convert-GenericRightsToFileSystemRights -OriginalRights $ACL.FileSystemRights $ReturnObject = [ordered] @{} $ReturnObject['Path' ] = $FullPath $ReturnObject['AccessControlType'] = $ACL.AccessControlType if ($ResolveTypes) { $Identity = Convert-Identity -Identity $ACL.IdentityReference if ($Identity) { $ReturnObject['Principal'] = $ACL.IdentityReference $ReturnObject['PrincipalName'] = $Identity.Name $ReturnObject['PrincipalSid'] = $Identity.Sid $ReturnObject['PrincipalType'] = $Identity.Type } else { $ReturnObject['Principal'] = $Identity $ReturnObject['PrincipalName'] = '' $ReturnObject['PrincipalSid'] = '' $ReturnObject['PrincipalType'] = '' } } else { $ReturnObject['Principal'] = $ACL.IdentityReference.Value } $ReturnObject['FileSystemRights'] = $TranslateRights $ReturnObject['IsInherited'] = $ACL.IsInherited if ($Extended) { $ReturnObject['InheritanceFlags'] = $ACL.InheritanceFlags $ReturnObject['PropagationFlags'] = $ACL.PropagationFlags } if ($IncludeACLObject) { $ReturnObject['ACL'] = $ACL $ReturnObject['AllACL'] = $ACLS } if ($AsHashTable) { $ReturnObject } else { [PSCustomObject] $ReturnObject } } $Output } else { Write-Warning "Get-PSPermissions - Path $Path doesn't exists. Skipping." } } } function Get-GitHubLatestRelease { [CmdLetBinding()] param([alias('ReleasesUrl')][uri] $Url) $ProgressPreference = 'SilentlyContinue' Try { [Array] $JsonOutput = (Invoke-WebRequest -Uri $Url -ErrorAction Stop | ConvertFrom-Json) foreach ($JsonContent in $JsonOutput) { [PSCustomObject] @{PublishDate = [DateTime] $JsonContent.published_at CreatedDate = [DateTime] $JsonContent.created_at PreRelease = [bool] $JsonContent.prerelease Version = [version] ($JsonContent.name -replace 'v', '') Tag = $JsonContent.tag_name Branch = $JsonContent.target_commitish Errors = '' } } } catch { [PSCustomObject] @{PublishDate = $null CreatedDate = $null PreRelease = $null Version = $null Tag = $null Branch = $null Errors = $_.Exception.Message } } $ProgressPreference = 'Continue' } function Get-PSRegistry { [cmdletbinding()] param([alias('Path')][string[]] $RegistryPath, [string[]] $ComputerName = $Env:COMPUTERNAME) $RootKeyDictionary = @{HKEY_CLASSES_ROOT = 2147483648 HKCR = 2147483648 HKEY_CURRENT_USER = 2147483649 HKCU = 2147483649 HKEY_LOCAL_MACHINE = 2147483650 HKLM = 2147483650 HKEY_USERS = 2147483651 HKU = 2147483651 HKEY_CURRENT_CONFIG = 2147483653 HKCC = 2147483653 HKEY_DYN_DATA = 2147483654 HKDD = 2147483654 } $TypesDictionary = @{'1' = 'GetStringValue' '2' = 'GetExpandedStringValue' '3' = 'GetBinaryValue' '4' = 'GetDWORDValue' '7' = 'GetMultiStringValue' '11' = 'GetQWORDValue' } $Dictionary = @{'HKCR:' = 'HKEY_CLASSES_ROOT' 'HKCU:' = 'HKEY_CURRENT_USER' 'HKLM:' = 'HKEY_LOCAL_MACHINE' 'HKU:' = 'HKEY_USERS' 'HKCC:' = 'HKEY_CURRENT_CONFIG' 'HKDD:' = 'HKEY_DYN_DATA' } [uint32] $RootKey = $null [Array] $Computers = Get-ComputerSplit -ComputerName $ComputerName foreach ($Registry in $RegistryPath) { If ($Registry -like '*:*') { foreach ($Key in $Dictionary.Keys) { if ($Registry.StartsWith($Key, [System.StringComparison]::CurrentCultureIgnoreCase)) { $Registry = $Registry -replace $Key, $Dictionary[$Key] break } } } for ($ComputerSplit = 0; $ComputerSplit -lt $Computers.Count; $ComputerSplit++) { if ($Computers[$ComputerSplit].Count -gt 0) { $Arguments = foreach ($_ in $RootKeyDictionary.Keys) { if ($Registry.StartsWith($_, [System.StringComparison]::CurrentCultureIgnoreCase)) { $RootKey = [uint32] $RootKeyDictionary[$_] @{hDefKey = [uint32] $RootKeyDictionary[$_] sSubKeyName = $Registry.substring($_.Length + 1) } break } } if ($ComputerSplit -eq 0) { $Output2 = Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName EnumValues -Arguments $Arguments -Verbose:$false $OutputKeys = Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName EnumKey -Arguments $Arguments -Verbose:$false } else { $Output2 = Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName EnumValues -Arguments $Arguments -ComputerName $Computers[$ComputerSplit] -Verbose:$false $OutputKeys = Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName EnumKey -ComputerName $Computers[$ComputerSplit] -Arguments $Arguments -Verbose:$false } foreach ($Entry in $Output2) { $RegistryOutput = [ordered] @{} if ($Entry.ReturnValue -ne 0) { $RegistryOutput['PSError'] = $true } else { $RegistryOutput['PSError'] = $false $Types = $Entry.Types $Names = $Entry.sNames for ($i = 0; $i -lt $Names.Count; $i++) { $Arguments['sValueName'] = $Names[$i] $MethodName = $TypesDictionary["$($Types[$i])"] if ($ComputerSplit -eq 0) { $Values = Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName $MethodName -Arguments $Arguments -Verbose:$false } else { $Values = Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName $MethodName -Arguments $Arguments -ComputerName $Entry.PSComputerName -Verbose:$false } if ($null -ne $Values.sValue) { if ($Names[$i]) { $RegistryOutput[$Names[$i]] = $Values.sValue } else { $RegistryOutput['DefaultKey'] = $Values.sValue } } elseif ($null -ne $Values.uValue) { if ($Names[$i]) { $RegistryOutput[$Names[$i]] = $Values.uValue } else { $RegistryOutput['DefaultKey'] = $Values.sValue } } } } if (-not $RegistryOutput['PSComputerName']) { if ($ComputerSplit -eq 0) { $RegistryOutput['PSComputerName'] = $ENV:COMPUTERNAME } else { $RegistryOutput['PSComputerName'] = $Entry.PSComputerName } } else { if ($ComputerSplit -eq 0) { $RegistryOutput['ComputerName'] = $ENV:COMPUTERNAME } else { $RegistryOutput['ComputerName'] = $Entry.PSComputerName } } if (-not $RegistryOutput['PSSubKeys']) { $RegistryOutput['PSSubKeys'] = $OutputKeys.sNames } else { $RegistryOutput['SubKeys'] = $OutputKeys.sNames } $RegistryOutput['PSPath'] = $Registry [PSCustomObject] $RegistryOutput } } } } } function Get-WinADDuplicateObject { [alias('Get-WinADForestObjectsConflict')] [CmdletBinding()] Param([alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [string] $PartialMatchDistinguishedName, [string[]] $IncludeObjectClass, [string[]] $ExcludeObjectClass, [switch] $Extended, [switch] $NoPostProcessing) $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { $DC = $ForestInformation['QueryServers']["$Domain"].HostName[0] $getADObjectSplat = @{LDAPFilter = "(|(cn=*\0ACNF:*)(ou=*CNF:*))" Properties = 'DistinguishedName', 'ObjectClass', 'DisplayName', 'SamAccountName', 'Name', 'ObjectCategory', 'WhenCreated', 'WhenChanged', 'ProtectedFromAccidentalDeletion', 'ObjectGUID' Server = $DC SearchScope = 'Subtree' } $Objects = Get-ADObject @getADObjectSplat foreach ($_ in $Objects) { if ($ExcludeObjectClass) { if ($ExcludeObjectClass -contains $_.ObjectClass) { continue } } if ($IncludeObjectClass) { if ($IncludeObjectClass -notcontains $_.ObjectClass) { continue } } if ($PartialMatchDistinguishedName) { if ($_.DistinguishedName -notlike $PartialMatchDistinguishedName) { continue } } if ($NoPostProcessing) { $_ continue } $DomainName = ConvertFrom-DistinguishedName -DistinguishedName $_.DistinguishedName -ToDomainCN $ConflictObject = [ordered] @{ConflictDN = $_.DistinguishedName ConflictWhenChanged = $_.WhenChanged DomainName = $DomainName ObjectClass = $_.ObjectClass } $LiveObjectData = [ordered] @{LiveDn = "N/A" LiveWhenChanged = "N/A" } $RestData = [ordered] @{DisplayName = $_.DisplayName Name = $_.Name.Replace("`n", ' ') SamAccountName = $_.SamAccountName ObjectCategory = $_.ObjectCategory WhenCreated = $_.WhenCreated WhenChanged = $_.WhenChanged ProtectedFromAccidentalDeletion = $_.ProtectedFromAccidentalDeletion ObjectGUID = $_.ObjectGUID.Guid } if ($Extended) { $LiveObject = $null $ConflictObject = $ConflictObject + $LiveObjectData + $RestData if (Select-String -SimpleMatch "\0ACNF:" -InputObject $ConflictObject.ConflictDn) { $SplitConfDN = $ConflictObject.ConflictDn -split "0ACNF:" try { $LiveObject = Get-ADObject -Identity "$($SplitConfDN[0].TrimEnd("\"))$($SplitConfDN[1].Substring(36))" -Properties WhenChanged -Server $DC -ErrorAction Stop } catch {} if ($LiveObject) { $ConflictObject.LiveDN = $LiveObject.DistinguishedName $ConflictObject.LiveWhenChanged = $LiveObject.WhenChanged } } else { $SplitConfDN = $ConflictObject.ConflictDn -split "CNF:" try { $LiveObject = Get-ADObject -Identity "$($SplitConfDN[0])$($SplitConfDN[1].Substring(36))" -Properties WhenChanged -Server $DC -ErrorAction Stop } catch {} if ($LiveObject) { $ConflictObject.LiveDN = $LiveObject.DistinguishedName $ConflictObject.LiveWhenChanged = $LiveObject.WhenChanged } } } else { $ConflictObject = $ConflictObject + $RestData } [PSCustomObject] $ConflictObject } } } 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() } } foreach ($Domain in $ForestInformation.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] $Findings['ForestDomainControllers'] = foreach ($Domain in $Findings.Domains) { $QueryServer = $Findings['QueryServers'][$Domain]['HostName'][0] [Array] $AllDC = try { try { $DomainControllers = Get-ADDomainController -Filter $Filter -Server $QueryServer -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'] = @{} $Findings['DomainsExtendedNetBIOS'] = @{} foreach ($DomainEx in $Findings['Domains']) { try { $Findings['DomainsExtended'][$DomainEx] = Get-ADDomain -Server $Findings['QueryServers'][$DomainEx].HostName[0] | ForEach-Object { [ordered] @{AllowedDNSSuffixes = $_.AllowedDNSSuffixes | ForEach-Object -Process { $_ } ChildDomains = $_.ChildDomains | ForEach-Object -Process { $_ } ComputersContainer = $_.ComputersContainer DeletedObjectsContainer = $_.DeletedObjectsContainer DistinguishedName = $_.DistinguishedName DNSRoot = $_.DNSRoot DomainControllersContainer = $_.DomainControllersContainer DomainMode = $_.DomainMode DomainSID = $_.DomainSID.Value ForeignSecurityPrincipalsContainer = $_.ForeignSecurityPrincipalsContainer Forest = $_.Forest InfrastructureMaster = $_.InfrastructureMaster LastLogonReplicationInterval = $_.LastLogonReplicationInterval LinkedGroupPolicyObjects = $_.LinkedGroupPolicyObjects | ForEach-Object -Process { $_ } LostAndFoundContainer = $_.LostAndFoundContainer ManagedBy = $_.ManagedBy Name = $_.Name NetBIOSName = $_.NetBIOSName ObjectClass = $_.ObjectClass ObjectGUID = $_.ObjectGUID ParentDomain = $_.ParentDomain PDCEmulator = $_.PDCEmulator PublicKeyRequiredPasswordRolling = $_.PublicKeyRequiredPasswordRolling | ForEach-Object -Process { $_ } QuotasContainer = $_.QuotasContainer ReadOnlyReplicaDirectoryServers = $_.ReadOnlyReplicaDirectoryServers | ForEach-Object -Process { $_ } ReplicaDirectoryServers = $_.ReplicaDirectoryServers | ForEach-Object -Process { $_ } RIDMaster = $_.RIDMaster SubordinateReferences = $_.SubordinateReferences | ForEach-Object -Process { $_ } SystemsContainer = $_.SystemsContainer UsersContainer = $_.UsersContainer } } $NetBios = $Findings['DomainsExtended'][$DomainEx]['NetBIOSName'] $Findings['DomainsExtendedNetBIOS'][$NetBios] = $Findings['DomainsExtended'][$DomainEx] } catch { Write-Warning "Get-WinADForestDetails - Error gathering Domain Information for domain $DomainEx - $($_.Exception.Message)" continue } } } if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress } $Findings } else { $Findings = Copy-DictionaryManual -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.DomainsExtended.Keys) { if ($_ -notin $Findings.Domains) { $Findings.DomainsExtended.Remove($_) $NetBiosName = $Findings.DomainsExtended.$_.'NetBIOSName' if ($NetBiosName) { $Findings.DomainsExtendedNetBIOS.Remove($NetBiosName) } } } [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 Get-WinADObject { <# .SYNOPSIS Gets Active Directory Object .DESCRIPTION Returns Active Directory Object (Computers, Groups, Users or ForeignSecurityPrincipal) using ADSI .PARAMETER Identity Identity of an object. It can be SamAccountName, SID, DistinguishedName or multiple other options .PARAMETER DomainName Choose domain name the objects resides in. This is optional for most objects .PARAMETER Credential Parameter description .PARAMETER IncludeGroupMembership Queries for group members when object is a group .PARAMETER IncludeAllTypes Allows functions to return all objects types and not only Computers, Groups, Users or ForeignSecurityPrincipal .EXAMPLE An example .NOTES General notes #> [cmdletBinding()] param([Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)][Array] $Identity, [string] $DomainName, [pscredential] $Credential, [switch] $IncludeGroupMembership, [switch] $IncludeAllTypes, [switch] $AddType, [switch] $Cache) Begin { if ($Cache -and -not $Script:CacheObjectsWinADObject) { $Script:CacheObjectsWinADObject = @{} } Add-Type -AssemblyName System.DirectoryServices.AccountManagement $GroupTypes = @{'2' = @{Name = 'Distribution Group - Global' Type = 'Distribution' Scope = 'Global' } '4' = @{Name = 'Distribution Group - Domain Local' Type = 'Distribution' Scope = 'Domain local' } '8' = @{Name = 'Distribution Group - Universal' Type = 'Distribution' Scope = 'Universal' } '-2147483640' = @{Name = 'Security Group - Universal' Type = 'Security' Scope = 'Universal' } '-2147483643' = @{Name = 'Security Group - Builtin Local' Type = 'Security' Scope = 'Builtin local' } '-2147483644' = @{Name = 'Security Group - Domain Local' Type = 'Security' Scope = 'Domain local' } '-2147483646' = @{Name = 'Security Group - Global' Type = 'Security' Scope = 'Global' } } } process { foreach ($Ident in $Identity) { $ResolvedIdentity = $null if ($Ident.DistinguishedName) { $Ident = $Ident.DistinguishedName } $TemporaryName = $Ident $TemporaryDomainName = $DomainName if ($Cache -and $Script:CacheObjectsWinADObject[$TemporaryName]) { Write-Verbose "Get-WinADObject - Requesting $TemporaryName from Cache" $Script:CacheObjectsWinADObject[$TemporaryName] continue } if (-not $TemporaryDomainName) { $MatchRegex = [Regex]::Matches($Ident, "S-\d-\d+-(\d+-|){1,14}\d+") if ($MatchRegex.Success) { $ResolvedIdentity = ConvertFrom-SID -SID $MatchRegex.Value $TemporaryDomainName = $ResolvedIdentity.DomainName $Ident = $MatchRegex.Value } elseif ($Ident -like '*\*') { $ResolvedIdentity = Convert-Identity -Identity $Ident if ($ResolvedIdentity.SID) { $TemporaryDomainName = $ResolvedIdentity.DomainName $Ident = $ResolvedIdentity.SID } else { $NetbiosConversion = ConvertFrom-NetbiosName -Identity $Ident if ($NetbiosConversion.DomainName) { $TemporaryDomainName = $NetbiosConversion.DomainName $Ident = $NetbiosConversion.Name } } } elseif ($Ident -like '*@*') { $CNConversion = $Ident -split '@', 2 $TemporaryDomainName = $CNConversion[1] $Ident = $CNConversion[0] } elseif ($Ident -like '*DC=*') { $DNConversion = ConvertFrom-DistinguishedName -DistinguishedName $Ident -ToDomainCN $TemporaryDomainName = $DNConversion } elseif ($Ident -like '*.*') { $ResolvedIdentity = Convert-Identity -Identity $Ident if ($ResolvedIdentity.SID) { $TemporaryDomainName = $ResolvedIdentity.DomainName $Ident = $ResolvedIdentity.SID } else { $CNConversion = $Ident -split '\.', 2 $Ident = $CNConversion[0] $TemporaryDomainName = $CNConversion[1] } } } $Search = [System.DirectoryServices.DirectorySearcher]::new() if ($TemporaryDomainName) { try { $Context = [System.DirectoryServices.AccountManagement.PrincipalContext]::new('Domain', $TemporaryDomainName) } catch { Write-Warning "Get-WinADObject - Building context failed ($TemporaryDomainName), error: $($_.Exception.Message)" } } else { try { $Context = [System.DirectoryServices.AccountManagement.PrincipalContext]::new('Domain') } catch { Write-Warning "Get-WinADObject - Building context failed, error: $($_.Exception.Message)" } } Try { $IdentityGUID = "" ([System.Guid]$Ident).ToByteArray() | ForEach-Object { $IdentityGUID += $("\{0:x2}" -f $_) } } Catch { $IdentityGUID = "null" } $Search.filter = "(|(DistinguishedName=$Ident)(Name=$Ident)(SamAccountName=$Ident)(UserPrincipalName=$Ident)(objectGUID=$IdentityGUID)(objectSid=$Ident))" if ($TemporaryDomainName) { $Search.SearchRoot = "LDAP://$TemporaryDomainName" } if ($PSBoundParameters['Credential']) { $Cred = [System.DirectoryServices.DirectoryEntry]::new("LDAP://$TemporaryDomainName", $($Credential.UserName), $($Credential.GetNetworkCredential().password)) $Search.SearchRoot = $Cred } Write-Verbose "Get-WinADObject - Requesting $Ident ($TemporaryDomainName)" try { $SearchResults = $($Search.FindAll()) } catch { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw "Get-WinADObject - Requesting $Ident ($TemporaryDomainName) failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine,''))" } else { Write-Warning "Get-WinADObject - Requesting $Ident ($TemporaryDomainName) failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine,''))" continue } } if ($SearchResults.Count -lt 1) { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw "Requesting $Ident ($TemporaryDomainName) failed with no results." } } foreach ($Object in $SearchResults) { $UAC = Convert-UserAccountControl -UserAccountControl ($Object.properties.useraccountcontrol -as [string]) $ObjectClass = ($Object.properties.objectclass -as [array])[-1] if ($ObjectClass -notin 'group', 'computer', 'user', 'foreignSecurityPrincipal' -and (-not $IncludeAllTypes)) { Write-Warning "Get-WinADObject - Unsupported object ($Ident) of type $ObjectClass. Only user,computer,group and foreignSecurityPrincipal is supported." continue } $Members = $Object.properties.member -as [array] if ($ObjectClass -eq 'group') { if ($IncludeGroupMembership) { $GroupMembers = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($Context, $Ident).Members [Array] $Members = foreach ($Member in $GroupMembers) { if ($Member.DistinguishedName) { $Member.DistinguishedName } elseif ($Member.DisplayName) { $Member.DisplayName } else { $Member.Sid } } } } $ObjectDomainName = ConvertFrom-DistinguishedName -DistinguishedName ($Object.properties.distinguishedname -as [string]) -ToDomainCN $DisplayName = $Object.properties.displayname -as [string] $SamAccountName = $Object.properties.samaccountname -as [string] $Name = $Object.properties.name -as [string] if ($ObjectClass -eq 'foreignSecurityPrincipal' -and $DisplayName -eq '') { $DisplayName = $ResolvedIdentity.Name if ($DisplayName -like '*\*') { $NetbiosWithName = $DisplayName -split '\\' if ($NetbiosWithName.Count -eq 2) { $NetbiosUser = $NetbiosWithName[1] $Name = $NetbiosUser $SamAccountName = $NetbiosUser } else { $Name = $DisplayName } } else { $Name = $DisplayName } } $GroupType = $Object.properties.grouptype -as [string] if ($Object.Properties.objectsid) { try { $ObjectSID = [System.Security.Principal.SecurityIdentifier]::new($Object.Properties.objectsid[0], 0).Value } catch { Write-Warning "Get-WinADObject - Getting objectsid failed, error: $($_.Exception.Message)" $ObjectSID = $null } } else { $ObjectSID = $null } $ReturnObject = [ordered] @{DisplayName = $DisplayName Name = $Name SamAccountName = $SamAccountName ObjectClass = $ObjectClass Enabled = if ($ObjectClass -eq 'group') { $null } else { $UAC -notcontains 'ACCOUNTDISABLE' } PasswordNeverExpire = if ($ObjectClass -eq 'group') { $null } else { $UAC -contains 'DONT_EXPIRE_PASSWORD' } DomainName = $ObjectDomainName Distinguishedname = $Object.properties.distinguishedname -as [string] WhenCreated = $Object.properties.whencreated -as [string] WhenChanged = $Object.properties.whenchanged -as [string] UserPrincipalName = $Object.properties.userprincipalname -as [string] ObjectSID = $ObjectSID MemberOf = $Object.properties.memberof -as [array] Members = $Members DirectReports = $Object.Properties.directreports GroupScopedType = $GroupTypes[$GroupType].Name GroupScope = $GroupTypes[$GroupType].Scope GroupType = $GroupTypes[$GroupType].Type Description = $Object.properties.description -as [string] } if ($AddType) { if (-not $ResolvedIdentity) { $ResolvedIdentity = ConvertFrom-SID -SID $ReturnObject['ObjectSID'] } $ReturnObject['Type'] = $ResolvedIdentity.Type } if ($ReturnObject['Type'] -eq 'WellKnownAdministrative') { if (-not $TemporaryDomainName) { $ReturnObject['DomainName'] = '' } } if ($Cache) { $Script:CacheObjectsWinADObject[$TemporaryName] = [PSCustomObject] $ReturnObject $Script:CacheObjectsWinADObject[$TemporaryName] } else { [PSCustomObject] $ReturnObject } } } } } function Get-WinADSharePermission { [cmdletBinding(DefaultParameterSetName = 'Path')] param([Parameter(ParameterSetName = 'Path', Mandatory)][string] $Path, [Parameter(ParameterSetName = 'ShareType', Mandatory)][validateset('NetLogon', 'SYSVOL')][string[]] $ShareType, [switch] $Owner, [string[]] $Name, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation) if ($ShareType) { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { $Path = -join ("\\", $Domain, "\$ShareType") @(Get-Item -Path $Path -Force) + @(Get-ChildItem -Path $Path -Recurse:$true -Force -ErrorAction SilentlyContinue -ErrorVariable Err) | ForEach-Object -Process { if ($Owner) { $Output = Get-FileOwner -JustPath -Path $_ -Resolve -AsHashTable $Output['Attributes'] = $_.Attributes [PSCustomObject] $Output } else { $Output = Get-FilePermission -Path $_ -ResolveTypes -Extended -AsHashTable foreach ($O in $Output) { $O['Attributes'] = $_.Attributes [PSCustomObject] $O } } } } } else { if ($Path -and (Test-Path -Path $Path)) { @(Get-Item -Path $Path -Force) + @(Get-ChildItem -Path $Path -Recurse:$true -Force -ErrorAction SilentlyContinue -ErrorVariable Err) | ForEach-Object -Process { if ($Owner) { $Output = Get-FileOwner -JustPath -Path $_ -Resolve -AsHashTable -Verbose $Output['Attributes'] = $_.Attributes [PSCustomObject] $Output } else { $Output = Get-FilePermission -Path $_ -ResolveTypes -Extended -AsHashTable foreach ($O in $Output) { $O['Attributes'] = $_.Attributes [PSCustomObject] $O } } } } } foreach ($e in $err) { Write-Warning "Get-WinADSharePermission - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } } function Remove-ADACL { [cmdletBinding(SupportsShouldProcess)] param([Array] $ACL, [string] $Principal, [System.DirectoryServices.ActiveDirectoryRights] $AccessRule, [System.Security.AccessControl.AccessControlType] $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow) foreach ($SubACL in $ACL) { $OutputRequiresCommit = @(if ($Principal -like '*-*-*-*') { $Identity = [System.Security.Principal.SecurityIdentifier]::new($Principal) } else { [System.Security.Principal.IdentityReference] $Identity = [System.Security.Principal.NTAccount]::new($Principal) } if (-not $AccessRule) { Write-Verbose "Remove-ADACL - Removing access for $($Identity) / All Rights" try { $SubACL.ACL.RemoveAccess($Identity, $AccessControlType) $true } catch { Write-Warning "Remove-ADACL - Removing permissions for $($SubACL.DistinguishedName) failed: $($_.Exception.Message)" $false } } else { foreach ($Rule in $AccessRule) { $AccessRuleToRemove = [System.DirectoryServices.ActiveDirectoryAccessRule]::new($Identity, $Rule, $AccessControlType) Write-Verbose "Remove-ADACL - Removing access for $($AccessRuleToRemove.IdentityReference) / $($AccessRuleToRemove.ActiveDirectoryRights)" $SubACL.ACL.RemoveAccessRule($AccessRuleToRemove) } }) if ($OutputRequiresCommit -notcontains $false -and $OutputRequiresCommit -contains $true) { Write-Verbose "Remove-ADACL - Saving permissions for $($SubACL.DistinguishedName)" Set-Acl -Path $SubACL.Path -AclObject $SubACL.ACL -ErrorAction Stop } elseif ($OutputRequiresCommit -contains $false) { Write-Warning "Remove-ADACL - Skipping saving permissions for $($SubACL.DistinguishedName) due to errors." } } } function Remove-EmptyValue { [alias('Remove-EmptyValues')] [CmdletBinding()] param([alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable, [string[]] $ExcludeParameter, [switch] $Recursive, [int] $Rerun) foreach ($Key in [string[]] $Hashtable.Keys) { if ($Key -notin $ExcludeParameter) { if ($Recursive) { if ($Hashtable[$Key] -is [System.Collections.IDictionary]) { if ($Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if ($null -eq $Hashtable[$Key] -or ($Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') -or ($Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0)) { $Hashtable.Remove($Key) } } } else { if ($null -eq $Hashtable[$Key] -or ($Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') -or ($Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0)) { $Hashtable.Remove($Key) } } } } if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive } } } function Select-Properties { <# .SYNOPSIS Allows for easy selecting property names from one or multiple objects .DESCRIPTION Allows for easy selecting property names from one or multiple objects. This is especially useful with using AllProperties parameter where we want to make sure to get all properties from all objects. .PARAMETER Objects One or more objects .PARAMETER Property Properties to include .PARAMETER ExcludeProperty Properties to exclude .PARAMETER AllProperties All unique properties from all objects .PARAMETER PropertyNameReplacement Default property name when object has no properties .EXAMPLE $Object1 = [PSCustomobject] @{ Name1 = '1' Name2 = '3' Name3 = '5' } $Object2 = [PSCustomobject] @{ Name4 = '2' Name5 = '6' Name6 = '7' } Select-Properties -Objects $Object1, $Object2 -AllProperties #OR: $Object1, $Object2 | Select-Properties -AllProperties -ExcludeProperty Name6 -Property Name3 .EXAMPLE $Object3 = [Ordered] @{ Name1 = '1' Name2 = '3' Name3 = '5' } $Object4 = [Ordered] @{ Name4 = '2' Name5 = '6' Name6 = '7' } Select-Properties -Objects $Object3, $Object4 -AllProperties $Object3, $Object4 | Select-Properties -AllProperties .NOTES General notes #> [CmdLetBinding()] param([Array][Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] $Objects, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $AllProperties, [string] $PropertyNameReplacement = '*') Begin { function Select-Unique { [CmdLetBinding()] param([System.Collections.IList] $Object) $New = $Object.ToLower() | Select-Object -Unique $Selected = foreach ($_ in $New) { $Index = $Object.ToLower().IndexOf($_) if ($Index -ne -1) { $Object[$Index] } } $Selected } $ObjectsList = [System.Collections.Generic.List[Object]]::new() } Process { foreach ($Object in $Objects) { $ObjectsList.Add($Object) } } End { if ($ObjectsList.Count -eq 0) { Write-Warning 'Select-Properties - Unable to process. Objects count equals 0.' return } if ($ObjectsList[0] -is [System.Collections.IDictionary]) { if ($AllProperties) { [Array] $All = foreach ($_ in $ObjectsList) { $_.Keys } $FirstObjectProperties = Select-Unique -Object $All } else { $FirstObjectProperties = $ObjectsList[0].Keys } if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) { $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($Property -contains $_ -and $ExcludeProperty -notcontains $_) { $_ continue } } } elseif ($Property.Count -gt 0) { $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($Property -contains $_) { $_ continue } } } elseif ($ExcludeProperty.Count -gt 0) { $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($ExcludeProperty -notcontains $_) { $_ continue } } } } elseif ($ObjectsList[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') { $FirstObjectProperties = $PropertyNameReplacement } else { if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property $Property -ExcludeProperty $ExcludeProperty } elseif ($Property.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property $Property } elseif ($ExcludeProperty.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property '*' -ExcludeProperty $ExcludeProperty } if ($AllProperties) { [Array] $All = foreach ($_ in $ObjectsList) { $_.PSObject.Properties.Name } $FirstObjectProperties = Select-Unique -Object $All } else { $FirstObjectProperties = $ObjectsList[0].PSObject.Properties.Name } } $FirstObjectProperties } } function Set-ADACLOwner { [cmdletBinding(SupportsShouldProcess)] param([Array] $ADObject, [Parameter(Mandatory)][string] $Principal) Begin { if ($Principal -is [string]) { if ($Principal -like '*/*') { $SplittedName = $Principal -split '/' [System.Security.Principal.IdentityReference] $Identity = [System.Security.Principal.NTAccount]::new($SplittedName[0], $SplittedName[1]) } else { [System.Security.Principal.IdentityReference] $Identity = [System.Security.Principal.NTAccount]::new($Principal) } } else { return } } Process { foreach ($Object in $ADObject) { if ($Object -is [Microsoft.ActiveDirectory.Management.ADOrganizationalUnit] -or $Object -is [Microsoft.ActiveDirectory.Management.ADEntity]) { [string] $DistinguishedName = $Object.DistinguishedName [string] $CanonicalName = $Object.CanonicalName [string] $ObjectClass = $Object.ObjectClass } elseif ($Object -is [string]) { [string] $DistinguishedName = $Object [string] $CanonicalName = '' [string] $ObjectClass = '' } else { Write-Warning "Set-ADACLOwner - Object not recognized. Skipping..." continue } $DNConverted = (ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName -ToDC) -replace '=' -replace ',' if (-not (Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { Write-Verbose "Set-ADACLOwner - Enabling PSDrives for $DistinguishedName to $DNConverted" New-ADForestDrives -ForestName $ForestName if (-not (Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { Write-Warning "Set-ADACLOwner - Drive $DNConverted not mapped. Terminating..." return } } $PathACL = "$DNConverted`:\$($DistinguishedName)" $ACLs = Get-Acl -Path $PathACL -ErrorAction Stop $CurrentOwner = $ACLs.Owner Write-Verbose "Set-ADACLOwner - Changing owner from $($CurrentOwner) to $Identity for $($ACLs.Path)" try { $ACLs.SetOwner($Identity) } catch { Write-Warning "Set-ADACLOwner - Unable to change owner from $($CurrentOwner) to $Identity for $($ACLs.Path): $($_.Exception.Message)" break } try { Set-Acl -Path $PathACL -AclObject $ACLs -ErrorAction Stop } catch { Write-Warning "Set-ADACLOwner - Unable to change owner from $($CurrentOwner) to $Identity for $($ACLs.Path): $($_.Exception.Message)" } } } End {} } function Set-FileOwner { [cmdletBinding(SupportsShouldProcess)] param([Array] $Path, [switch] $Recursive, [string] $Owner, [string[]] $Exlude, [switch] $JustPath) Begin {} Process { foreach ($P in $Path) { if ($P -is [System.IO.FileSystemInfo]) { $FullPath = $P.FullName } elseif ($P -is [string]) { $FullPath = $P } $OwnerTranslated = [System.Security.Principal.NTAccount]::new($Owner) if ($FullPath -and (Test-Path -Path $FullPath)) { if ($JustPath) { $FullPath | ForEach-Object -Process { $File = $_ try { $ACL = Get-Acl -Path $File -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Getting ACL failed with error: $($_.Exception.Message)" } if ($ACL.Owner -notin $Exlude -and $ACL.Owner -ne $OwnerTranslated) { if ($PSCmdlet.ShouldProcess($File, "Replacing owner $($ACL.Owner) to $OwnerTranslated")) { try { $ACL.SetOwner($OwnerTranslated) Set-Acl -Path $File -AclObject $ACL -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Replacing owner $($ACL.Owner) to $OwnerTranslated failed with error: $($_.Exception.Message)" } } } } } else { Get-ChildItem -LiteralPath $FullPath -Recurse:$Recursive -ErrorAction SilentlyContinue -ErrorVariable err | ForEach-Object -Process { $File = $_ try { $ACL = Get-Acl -Path $File.FullName -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Getting ACL failed with error: $($_.Exception.Message)" } if ($ACL.Owner -notin $Exlude -and $ACL.Owner -ne $OwnerTranslated) { if ($PSCmdlet.ShouldProcess($File.FullName, "Replacing owner $($ACL.Owner) to $OwnerTranslated")) { try { $ACL.SetOwner($OwnerTranslated) Set-Acl -Path $File.FullName -AclObject $ACL -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Replacing owner $($ACL.Owner) to $OwnerTranslated failed with error: $($_.Exception.Message)" } } } } foreach ($e in $err) { Write-Warning "Set-FileOwner - Errors processing $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } } } } } End {} } function Start-TimeLog { [CmdletBinding()] param() [System.Diagnostics.Stopwatch]::StartNew() } function Stop-TimeLog { [CmdletBinding()] param ([Parameter(ValueFromPipeline = $true)][System.Diagnostics.Stopwatch] $Time, [ValidateSet('OneLiner', 'Array')][string] $Option = 'OneLiner', [switch] $Continue) Begin {} Process { if ($Option -eq 'Array') { $TimeToExecute = "$($Time.Elapsed.Days) days", "$($Time.Elapsed.Hours) hours", "$($Time.Elapsed.Minutes) minutes", "$($Time.Elapsed.Seconds) seconds", "$($Time.Elapsed.Milliseconds) milliseconds" } else { $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" } } End { if (-not $Continue) { $Time.Stop() } return $TimeToExecute } } function Write-Color { <# .SYNOPSIS Write-Color is a wrapper around Write-Host. It provides: - Easy manipulation of colors, - Logging output to file (log) - Nice formatting options out of the box. .DESCRIPTION Author: przemyslaw.klys at evotec.pl Project website: https://evotec.xyz/hub/scripts/write-color-ps1/ Project support: https://github.com/EvotecIT/PSWriteColor Original idea: Josh (https://stackoverflow.com/users/81769/josh) .EXAMPLE Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow .EXAMPLE Write-Color -Text "This is text in Green ", "followed by red ", "and then we have Magenta... ", "isn't it fun? ", "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan .EXAMPLE Write-Color -Text "This is text in Green ", "followed by red ", "and then we have Magenta... ", "isn't it fun? ", "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1 .EXAMPLE Write-Color "1. ", "Option 1" -Color Yellow, Green Write-Color "2. ", "Option 2" -Color Yellow, Green Write-Color "3. ", "Option 3" -Color Yellow, Green Write-Color "4. ", "Option 4" -Color Yellow, Green Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1 .EXAMPLE Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." ` -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss" Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." ` -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" .EXAMPLE # Added in 0.5 Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow wc -t "my text" -c yellow -b green wc -text "my text" -c red .NOTES Additional Notes: - TimeFormat https://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx #> [alias('Write-Colour')] [CmdletBinding()] param ([alias ('T')] [String[]]$Text, [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White, [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null, [alias ('Indent')][int] $StartTab = 0, [int] $LinesBefore = 0, [int] $LinesAfter = 0, [int] $StartSpaces = 0, [alias ('L')] [string] $LogFile = '', [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss', [alias ('LogTimeStamp')][bool] $LogTime = $true, [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode', [switch] $ShowTime, [switch] $NoNewLine) $DefaultColor = $Color[0] if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) { Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated." return } if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host -Object "`n" -NoNewline } } if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host -Object "`t" -NoNewline } } if ($StartSpaces -ne 0) { for ($i = 0; $i -lt $StartSpaces; $i++) { Write-Host -Object ' ' -NoNewline } } if ($ShowTime) { Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))] " -NoNewline } if ($Text.Count -ne 0) { if ($Color.Count -ge $Text.Count) { if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } } else { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } } } else { if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline } } else { for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline } } } } if ($NoNewLine -eq $true) { Write-Host -NoNewline } else { Write-Host } if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host -Object "`n" -NoNewline } } if ($Text.Count -and $LogFile) { $TextToFile = "" for ($i = 0; $i -lt $Text.Length; $i++) { $TextToFile += $Text[$i] } try { if ($LogTime) { "[$([datetime]::Now.ToString($DateTimeFormat))] $TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop } else { "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop } } catch { $PSCmdlet.WriteError($_) } } } function ConvertFrom-NetbiosName { [cmdletBinding()] param([Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)] [string[]] $Identity) process { foreach ($Ident in $Identity) { if ($Ident -like '*\*') { $NetbiosWithObject = $Ident -split "\\" if ($NetbiosWithObject.Count -eq 2) { $LDAPQuery = ([ADSI]"LDAP://$($NetbiosWithObject[0])") $DomainName = ConvertFrom-DistinguishedName -DistinguishedName $LDAPQuery.distinguishedName -ToDomainCN [PSCustomObject] @{DomainName = $DomainName Name = $NetbiosWithObject[1] } } else { [PSCustomObject] @{DomainName = '' Name = $Ident } } } else { [PSCustomObject] @{DomainName = '' Name = $Ident } } } } } function Convert-GenericRightsToFileSystemRights { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER OriginalRights Parameter description .EXAMPLE An example .NOTES .LINK Improved https://blog.cjwdev.co.uk/2011/06/28/permissions-not-included-in-net-accessrule-filesystemrights-enum/ #> [cmdletBinding()] param([System.Security.AccessControl.FileSystemRights] $OriginalRights) Begin { $FileSystemRights = [System.Security.AccessControl.FileSystemRights] $GenericRights = @{GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE = 0x20000000 GENERIC_ALL = 0x10000000 FILTER_GENERIC = 0x0FFFFFFF } $MappedGenericRights = @{FILE_GENERIC_EXECUTE = $FileSystemRights::ExecuteFile -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::ReadAttributes -bor $FileSystemRights::Synchronize FILE_GENERIC_READ = $FileSystemRights::ReadAttributes -bor $FileSystemRights::ReadData -bor $FileSystemRights::ReadExtendedAttributes -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::Synchronize FILE_GENERIC_WRITE = $FileSystemRights::AppendData -bor $FileSystemRights::WriteAttributes -bor $FileSystemRights::WriteData -bor $FileSystemRights::WriteExtendedAttributes -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::Synchronize FILE_GENERIC_ALL = $FileSystemRights::FullControl } } Process { $MappedRights = [System.Security.AccessControl.FileSystemRights]::new() if ($OriginalRights -band $GenericRights.GENERIC_EXECUTE) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_EXECUTE } if ($OriginalRights -band $GenericRights.GENERIC_READ) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_READ } if ($OriginalRights -band $GenericRights.GENERIC_WRITE) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_WRITE } if ($OriginalRights -band $GenericRights.GENERIC_ALL) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_ALL } (($OriginalRights -bAND $GenericRights.FILTER_GENERIC) -bOR $MappedRights) -as $FileSystemRights } End {} } function ConvertTo-OperatingSystem { <# .SYNOPSIS Allows easy conversion of OperatingSystem, Operating System Version to proper Windows 10 naming based on WMI or AD .DESCRIPTION Allows easy conversion of OperatingSystem, Operating System Version to proper Windows 10 naming based on WMI or AD .PARAMETER OperatingSystem Operating System as returned by Active Directory .PARAMETER OperatingSystemVersion Operating System Version as returned by Active Directory .EXAMPLE $Computers = Get-ADComputer -Filter * -Properties OperatingSystem, OperatingSystemVersion | ForEach-Object { $OPS = ConvertTo-OperatingSystem -OperatingSystem $_.OperatingSystem -OperatingSystemVersion $_.OperatingSystemVersion Add-Member -MemberType NoteProperty -Name 'OperatingSystemTranslated' -Value $OPS -InputObject $_ -Force $_ } $Computers | Select-Object DNS*, Name, SamAccountName, Enabled, OperatingSystem*, DistinguishedName | Format-Table .NOTES General notes #> [CmdletBinding()] param([string] $OperatingSystem, [string] $OperatingSystemVersion) if ($OperatingSystem -like '*Windows 10*') { $Systems = @{'10.0 (19042)' = 'Windows 10 Insider Preview Build 19042.421 (20H2)' '10.0 (19041)' = 'Windows 10 2004' '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.19042' = 'Windows 10 Insider Preview Build 19042.421 (20H2)' '10.0.19041' = 'Windows 10 2004' '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 Convert-UserAccountControl { [cmdletBinding()] param([alias('UAC')][int] $UserAccountControl, [string] $Separator) $UserAccount = [ordered] @{"SCRIPT" = 1 "ACCOUNTDISABLE" = 2 "HOMEDIR_REQUIRED" = 8 "LOCKOUT" = 16 "PASSWD_NOTREQD" = 32 "ENCRYPTED_TEXT_PWD_ALLOWED" = 128 "TEMP_DUPLICATE_ACCOUNT" = 256 "NORMAL_ACCOUNT" = 512 "INTERDOMAIN_TRUST_ACCOUNT" = 2048 "WORKSTATION_TRUST_ACCOUNT" = 4096 "SERVER_TRUST_ACCOUNT" = 8192 "DONT_EXPIRE_PASSWORD" = 65536 "MNS_LOGON_ACCOUNT" = 131072 "SMARTCARD_REQUIRED" = 262144 "TRUSTED_FOR_DELEGATION" = 524288 "NOT_DELEGATED" = 1048576 "USE_DES_KEY_ONLY" = 2097152 "DONT_REQ_PREAUTH" = 4194304 "PASSWORD_EXPIRED" = 8388608 "TRUSTED_TO_AUTH_FOR_DELEGATION" = 16777216 "PARTIAL_SECRETS_ACCOUNT" = 67108864 } $Output = foreach ($_ in $UserAccount.Keys) { $binaryAnd = $UserAccount[$_] -band $UserAccountControl if ($binaryAnd -ne "0") { $_ } } if ($Separator) { $Output -join $Separator } else { $Output } } function Copy-DictionaryManual { [CmdletBinding()] param([System.Collections.IDictionary] $Dictionary) $clone = @{} foreach ($Key in $Dictionary.Keys) { $value = $Dictionary.$Key $clonedValue = switch ($Dictionary.$Key) { { $null -eq $_ } { $null continue } { $_ -is [System.Collections.IDictionary] } { Copy-DictionaryManual -Dictionary $_ continue } { $type = $_.GetType() $type.IsPrimitive -or $type.IsValueType -or $_ -is [string] } { $_ continue } default { $_ | Select-Object -Property * } } if ($value -is [System.Collections.IList]) { $clone[$Key] = @($clonedValue) } else { $clone[$Key] = $clonedValue } } $clone } function Get-ComputerSplit { [CmdletBinding()] param([string[]] $ComputerName) if ($null -eq $ComputerName) { $ComputerName = $Env:COMPUTERNAME } try { $LocalComputerDNSName = [System.Net.Dns]::GetHostByName($Env:COMPUTERNAME).HostName } catch { $LocalComputerDNSName = $Env:COMPUTERNAME } $ComputersLocal = $null [Array] $Computers = foreach ($Computer in $ComputerName) { if ($Computer -eq '' -or $null -eq $Computer) { $Computer = $Env:COMPUTERNAME } if ($Computer -ne $Env:COMPUTERNAME -and $Computer -ne $LocalComputerDNSName) { $Computer } else { $ComputersLocal = $Computer } } , @($ComputersLocal, $Computers) } function Get-WinADForestGUIDs { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Domain Parameter description .PARAMETER RootDSE Parameter description .PARAMETER DisplayNameKey Parameter description .EXAMPLE Get-WinADForestGUIDs .EXAMPLE Get-WinADForestGUIDs -DisplayNameKey .NOTES General notes #> [alias('Get-WinADDomainGUIDs')] [cmdletbinding()] param([string] $Domain = $Env:USERDNSDOMAIN, [Microsoft.ActiveDirectory.Management.ADEntity] $RootDSE, [switch] $DisplayNameKey) if ($null -eq $RootDSE) { $RootDSE = Get-ADRootDSE -Server $Domain } $GUID = @{} $GUID.Add('00000000-0000-0000-0000-000000000000', 'All') $Schema = Get-ADObject -SearchBase $RootDSE.schemaNamingContext -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID foreach ($S in $Schema) { if ($DisplayNameKey) { $GUID["$($S.name)"] = $(([System.GUID]$S.schemaIDGUID).Guid) } else { $GUID["$(([System.GUID]$S.schemaIDGUID).Guid)"] = $S.name } } $Extended = Get-ADObject -SearchBase "CN=Extended-Rights,$($RootDSE.configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID foreach ($S in $Extended) { if ($DisplayNameKey) { $GUID["$($S.name)"] = $(([System.GUID]$S.rightsGUID).Guid) } else { $GUID["$(([System.GUID]$S.rightsGUID).Guid)"] = $S.name } } return $GUID } function New-ADForestDrives { [cmdletbinding()] param([string] $ForestName, [string] $ObjectDN) if (-not $Global:ADDrivesMapped) { if ($ForestName) { $Forest = Get-ADForest -Identity $ForestName } else { $Forest = Get-ADForest } if ($ObjectDN) { $DNConverted = (ConvertFrom-DistinguishedName -DistinguishedName $ObjectDN -ToDC) -replace '=' -replace ',' if (-not(Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { try { if ($Server) { $null = New-PSDrive -Name $DNConverted -Root '' -PSProvider ActiveDirectory -Server $Server.Hostname[0] -Scope Global -WhatIf:$false Write-Verbose "New-ADForestDrives - Mapped drive $Domain / $($Server.Hostname[0])" } else { $null = New-PSDrive -Name $DNConverted -Root '' -PSProvider ActiveDirectory -Server $Domain -Scope Global -WhatIf:$false } } catch { Write-Warning "New-ADForestDrives - Couldn't map new AD psdrive for $Domain / $($Server.Hostname[0])" } } } else { foreach ($Domain in $Forest.Domains) { try { $Server = Get-ADDomainController -Discover -DomainName $Domain $DomainInformation = Get-ADDomain -Server $Server.Hostname[0] } catch { Write-Warning "New-ADForestDrives - Can't process domain $Domain - $($_.Exception.Message)" continue } $ObjectDN = $DomainInformation.DistinguishedName $DNConverted = (ConvertFrom-DistinguishedName -DistinguishedName $ObjectDN -ToDC) -replace '=' -replace ',' if (-not(Get-PSDrive -Name $DNConverted -ErrorAction SilentlyContinue)) { try { if ($Server) { $null = New-PSDrive -Name $DNConverted -Root '' -PSProvider ActiveDirectory -Server $Server.Hostname[0] -Scope Global -WhatIf:$false Write-Verbose "New-ADForestDrives - Mapped drive $Domain / $Server" } else { $null = New-PSDrive -Name $DNConverted -Root '' -PSProvider ActiveDirectory -Server $Domain -Scope Global -WhatIf:$false } } catch { Write-Warning "New-ADForestDrives - Couldn't map new AD psdrive for $Domain / $Server $($_.Exception.Message)" } } } } $Global:ADDrivesMapped = $true } } 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 ConvertFrom-XMLRSOP { [cmdletBinding()] param( [System.Xml.XmlElement]$Content, [string] $ResultsType, [Microsoft.GroupPolicy.GPRsop] $ResultantSetPolicy, [string] $Splitter = [System.Environment]::NewLine ) $GPOPrimary = [ordered] @{ Summary = $null SummaryDetails = [System.Collections.Generic.List[PSCustomObject]]::new() SummaryDownload = $null ResultantSetPolicy = $ResultantSetPolicy GroupPolicies = $null GroupPoliciesLinks = $null GroupPoliciesApplied = $null GroupPoliciesDenied = $null Results = [ordered]@{} } $Object = [ordered] @{ ReadTime = [DateTime] $Content.ReadTime ComputerName = $Content.$ResultsType.Name DomainName = $Content.$ResultsType.Domain OrganizationalUnit = $Content.$ResultsType.SOM Site = $Content.$ResultsType.Site GPOTypes = $Content.$ResultsType.ExtensionData.Name.'#text' -join $Splitter SlowLink = if ($Content.$ResultsType.SlowLink -eq 'true') { $true } else { $false }; } $GPOPrimary['Summary'] = $Object [Array] $GPOPrimary['SecurityGroups'] = foreach ($Group in $Content.$ResultsType.SecurityGroup) { [PSCustomObject] @{ Name = $Group.Name.'#Text' SID = $Group.SID.'#Text' } } [Array] $GPOPrimary['GroupPolicies'] = foreach ($GPO in $Content.$ResultsType.GPO) { <# $EventsReason = @{ 'NOTAPPLIED-EMPTY' = 'Not Applied (Empty)' 'DENIED-WMIFILTER' = 'Denied (WMI Filter)' 'DENIED-SECURITY' = 'Denied (Security)' } #> # Lets translate CSE extensions as some didn't translate automatically $ExtensionName = $GPO.ExtensionName | ForEach-Object { ConvertFrom-CSExtension -CSE $_ -Limited } $GPOObject = [PSCustomObject] @{ Name = $GPO.Name #Path = $GPO.Path GUID = $GPO.Path.Identifier.'#text' DomainName = if ($GPO.Path.Domain.'#text') { $GPO.Path.Domain.'#text' } else { 'Local Policy' }; #VersionDirectory = $GPO.VersionDirectory #VersionSysvol = $GPO.VersionSysvol Revision = -join ('AD (', $GPO.VersionDirectory, '), SYSVOL (', $GPO.VersionSysvol, ')') IsValid = if ($GPO.IsValid -eq 'true') { $true } else { $false }; Status = if ($GPO.FilterAllowed -eq 'true' -and $GPO.AccessDenied -eq 'false') { 'Applied' } else { 'Denied' }; FilterAllowed = if ($GPO.FilterAllowed -eq 'true') { $true } else { $false }; AccessAllowed = if ($GPO.AccessDenied -eq 'true') { $false } else { $true }; FilterName = $GPO.FilterName # : Test ExtensionName = ($ExtensionName | Sort-Object -Unique) -join '; ' # This isn't really pretty for large amount of links but can be useful for assesing things SOMOrder = $GPO.Link.SOMOrder -join '; ' AppliedOrder = $GPO.Link.AppliedOrder -join '; ' LinkOrder = $GPO.Link.LinkOrder -join '; ' Enabled = ($GPO.Link.Enabled | ForEach-Object { if ($_ -eq 'true') { $true } else { $false }; }) -join '; ' Enforced = ($GPO.Link.NoOverride | ForEach-Object { if ($_ -eq 'true') { $true } else { $false }; }) -join '; ' # : true SecurityFilter = $GPO.SecurityFilter -join '; ' # SecurityFilter : {NT AUTHORITY\Authenticated Users, EVOTEC\GDS-TestGroup3} FilterId = $GPO.FilterID # : MSFT_SomFilter.ID="{ff08bc72-dae6-4890-b4cf-85a9c3b00056}",Domain="ad.evotec.xyz" Links = $GPO.Link.SOMPath -join '; ' } $GPOObject } [Array] $GPOPrimary['GroupPoliciesLinks'] = foreach ($GPO in $Content.$ResultsType.GPO) { foreach ($Link in $GPO.Link) { [PSCustomObject] @{ DisplayName = $GPO.Name DomainName = $GPO.Path.Domain.'#text' GUID = $GPO.Path.Identifier.'#text' SOMPath = $Link.SOMPath # : ad.evotec.xyz SOMOrder = $Link.SOMOrder # : 2 AppliedOrder = $Link.AppliedOrder # : 0 LinkOrder = $Link.LinkOrder # : 4 Enabled = if ($Link.Enabled -eq 'true') { $true } else { $false }; # : true Enforced = if ($Link.NoOverride -eq 'true') { $true } else { $false }; # : true } } } [Array] $GPOPrimary['ScopeOfManagement'] = foreach ($SOM in $Content.$ResultsType.SearchedSOM) { [PSCustomObject] @{ Path = $SOM.Path Type = $SOM.Type Order = $SOM.Order BlocksInheritance = if ($SOM.BlocksInheritance -eq 'true') { $true } else { $false }; Blocked = if ($SOM.Blocked -eq 'true') { $true } else { $false }; Reason = if ($SOM.Reason -eq 'true') { $true } else { $false }; } } [Array] $GPOPrimary['ExtensionStatus'] = foreach ($Details in $Content.$ResultsType.ExtensionStatus) { [PSCustomObject] @{ Name = $Details.Name # : Registry Identifier = $Details.Identifier # : {35378EAC-683F-11D2-A89A-00C04FBBCFA2} BeginTime = $Details.BeginTime # : 2020-04-02T12:05:10 EndTime = $Details.EndTime # : 2020-04-02T12:05:10 LoggingStatus = $Details.LoggingStatus # : Complete Error = $Details.Error # : 0 } } [Array] $GPOPrimary['ExtensionData'] = $Content.$ResultsType.ExtensionData.Extension foreach ($Single in $Content.$ResultsType.EventsDetails.SinglePassEventsDetails) { $GPOPrimary['Results']["$($Single.ActivityId)"] = [ordered] @{} $GPOPrimary['Results']["$($Single.ActivityId)"]['SummaryDetails'] = [Ordered] @{ ActivityId = $Single.ActivityId # : {6400d0bf-ac88-4ee6-b2c2-ca2cbbab0695} ProcessingTrigger = $Single.ProcessingTrigger # : Periodic ProcessingAppMode = $Single.ProcessingAppMode # : Background LinkSpeedInKbps = $Single.LinkSpeedInKbps # : 0 SlowLinkThresholdInKbps = $Single.SlowLinkThresholdInKbps # : 500 DomainControllerName = $Single.DomainControllerName # : AD1.ad.evotec.xyz DomainControllerIPAddress = $Single.DomainControllerIPAddress # : 192.168.240.189 PolicyProcessingMode = $Single.PolicyProcessingMode # : None PolicyElapsedTimeInMilliseconds = $Single.PolicyElapsedTimeInMilliseconds # : 1202 ErrorCount = $Single.ErrorCount # : 0 WarningCount = $Single.WarningCount # : 0 } $GPOPrimary['SummaryDetails'].Add([PSCustomObject] $GPOPrimary['Results']["$($Single.ActivityId)"]['SummaryDetails']) [Array] $GPOPrimary['Results']["$($Single.ActivityId)"]['ProcessingTime'] = foreach ($Details in $Single.ExtensionProcessingTime) { [PSCustomObject] @{ ExtensionName = $Details.ExtensionName ExtensionGuid = $Details.ExtensionGuid ElapsedTimeInMilliseconds = $Details.ElapsedTimeInMilliseconds ProcessedTimeStamp = $Details.ProcessedTimeStamp } } $EventsLevel = @{ '5' = 'Verbose' '4' = 'Informational' '3' = 'Warning' '2' = 'Error' '1' = 'Critical' '0' = 'LogAlways' } $EventsReason = @{ 'NOTAPPLIED-EMPTY' = 'Not Applied (Empty)' 'DENIED-WMIFILTER' = 'Denied (WMI Filter)' 'DENIED-SECURITY' = 'Denied (Security)' } [Array] $GPOPrimary['Results']["$($Single.ActivityId)"]['Events'] = foreach ($Event in $Single.EventRecord) { [xml] $EventDetails = $Event.EventXML $EventInformation = [ordered] @{ Description = $Event.EventDescription Provider = $EventDetails.Event.System.Provider.Name # : Provider ProviderGUID = $EventDetails.Event.System.Provider.Guid EventID = $EventDetails.Event.System.EventID # : 4006 Version = $EventDetails.Event.System.Version # : 1 Level = $EventsLevel[$EventDetails.Event.System.Level] # : 4 Task = $EventDetails.Event.System.Task # : 0 Opcode = $EventDetails.Event.System.Opcode # : 1 Keywords = $EventDetails.Event.System.Keywords # : 0x4000000000000000 TimeCreated = [DateTime] $EventDetails.Event.System.TimeCreated.SystemTime # : TimeCreated, 2020-08-09T20:16:44.5668052Z EventRecordID = $EventDetails.Event.System.EventRecordID # : 10641325 Correlation = $EventDetails.Event.System.Correlation.ActivityID # : Correlation Execution = -join ("ProcessID: ", $EventDetails.Event.System.Execution.ProcessID, " ThreadID: ", $EventDetails.Event.System.Execution.ThreadID) # : Execution Channel = $EventDetails.Event.System.Channel # : Microsoft-Windows-GroupPolicy / Operational Computer = $EventDetails.Event.System.Computer # : AD1.ad.evotec.xyz Security = $EventDetails.Event.System.Security.UserID # : Security } foreach ($Entry in $EventDetails.Event.EventData.Data) { $EventInformation["$($Entry.Name)"] = $Entry.'#text' } [PSCustomObject] $EventInformation } # Lets build events by ID, this will be useful for better/easier processing $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID'] = [ordered] @{} $GroupedEvents = $GPOPrimary['Results']["$($Single.ActivityId)"]['Events'] | Group-Object -Property EventId foreach ($Events in $GroupedEvents) { $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID'][$Events.Name] = $Events.Group } $GPOPrimary['Results']["$($Single.ActivityId)"]['GroupPoliciesApplied'] = & { if ($GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5312']) { [xml] $GPODetailsApplied = -join ('<Details>', $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5312'].GPOinfoList, '</Details>') foreach ($GPO in $GPODetailsApplied.Details.GPO) { $ReturnObject = [ordered] @{ GUID = $GPO.ID # : { 4E1F9C70-1DDB-4AB6-BBA3-14A8E07F0B4B } DisplayName = $GPO.Name # : DC | Event Log Settings Version = $GPO.Version # : 851981 Link = $GPO.SOM # : LDAP: / / OU = Domain Controllers, DC = ad, DC = evotec, DC = xyz SysvolPath = $GPO.FSPath # : \\ad.evotec.xyz\SysVol\ad.evotec.xyz\Policies\ { 4E1F9C70-1DDB-4AB6-BBA3-14A8E07F0B4B }\Machine #GPOTypes = $GPO.Extensions -join '; ' # : [ { 35378EAC-683F-11D2-A89A-00C04FBBCFA2 } { D02B1F72 - 3407 - 48AE-BA88-E8213C6761F1 }] } $TranslatedExtensions = foreach ($Extension in $GPO.Extensions) { ConvertFrom-CSExtension -CSE $Extension -Limited } $ReturnObject['GPOTypes'] = $TranslatedExtensions -join '; ' [PSCustomObject] $ReturnObject } } } $GPOPrimary['Results']["$($Single.ActivityId)"]['GroupPoliciesDenied'] = & { if ($GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5312']) { [xml] $GPODetailsDenied = -join ('<Details>', $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5313'].GPOinfoList, '</Details>') foreach ($GPO in $GPODetailsDenied.Details.GPO) { [PSCustomObject] @{ GUID = $GPO.ID #: { 6AC1786C-016F-11D2-945F-00C04fB984F9 } DisplayName = $GPO.Name #: Default Domain Controllers Policy Version = $GPO.Version #: 131074 Link = $GPO.SOM #: LDAP: / / OU = Domain Controllers, DC = ad, DC = evotec, DC = xyz SysvolPath = $GPO.FSPath #: \\ad.evotec.xyz\sysvol\ad.evotec.xyz\Policies\ { 6AC1786C-016F-11D2-945F-00C04fB984F9 }\Machine Reason = $EventsReason["$($GPO.Reason)"] #: DENIED-WMIFILTER } } } } $GPOPrimary['Results']["$($Single.ActivityId)"]['SummaryDownload'] = & { if ($GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5126']) { [PSCustomObject] @{ IsBackgroundProcessing = if ($GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5126'].IsBackgroundProcessing -eq 'true') { $true } else { $false }; # : true IsAsyncProcessing = if ($GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5126'].IsAsyncProcessing -eq 'true') { $true } else { $false }; # : false Downloaded = $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5126'].NumberOfGPOsDownloaded # : 7 Applicable = $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5126'].NumberOfGPOsApplicable # : 6 DownloadTimeMiliseconds = $GPOPrimary['Results']["$($Single.ActivityId)"]['EventsByID']['5126'].GPODownloadTimeElapsedInMilliseconds # : 375 } } } } $GPOPrimary } function ConvertTo-XMLAccountPolicy { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = @( $Settings = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings ClearTextPassword = 'Not Set' LockoutBadCount = 'Not Set' LockoutDuration = 'Not Set' MaximumPasswordAge = 'Not Set' MinimumPasswordAge = 'Not Set' MinimumPasswordLength = 'Not Set' PasswordComplexity = 'Not Set' PasswordHistorySize = 'Not Set' ResetLockoutCount = 'Not Set' MaxClockSkew = 'Not Set' MaxRenewAge = 'Not Set' MaxServiceAge = 'Not Set' MaxTicketAge = 'Not Set' TicketValidateClient = 'Not Set' } foreach ($GPOEntry in $GPO.DataSet) { if ($GPOEntry.SettingBoolean) { $Settings[$($GPOEntry.Name)] = if ($GPOEntry.SettingBoolean -eq 'true') { 'Enabled' } elseif ($GPOEntry.SettingBoolean -eq 'false') { 'Disabled' } else { 'Not set' }; } elseif ($GPOEntry.SettingNumber) { $Settings[$($GPOEntry.Name)] = [int] $GPOEntry.SettingNumber } } [PSCustomObject] $Settings ) $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings ClearTextPassword = 'Not Set' LockoutBadCount = 'Not Set' LockoutDuration = 'Not Set' MaximumPasswordAge = 'Not Set' MinimumPasswordAge = 'Not Set' MinimumPasswordLength = 'Not Set' PasswordComplexity = 'Not Set' PasswordHistorySize = 'Not Set' ResetLockoutCount = 'Not Set' MaxClockSkew = 'Not Set' MaxRenewAge = 'Not Set' MaxServiceAge = 'Not Set' MaxTicketAge = 'Not Set' TicketValidateClient = 'Not Set' } foreach ($GPOEntry in $GPO.DataSet) { if ($GPOEntry.SettingBoolean) { $CreateGPO[$($GPOEntry.Name)] = if ($GPOEntry.SettingBoolean -eq 'true') { 'Enabled' } elseif ($GPOEntry.SettingBoolean -eq 'false') { 'Disabled' } else { 'Not set' }; } elseif ($GPOEntry.SettingNumber) { $CreateGPO[$($GPOEntry.Name)] = [int] $GPOEntry.SettingNumber } } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } function ConvertTo-XMLAudit { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) $SettingType = @{ '0' = 'No Auditing' '1' = 'Success' '2' = 'Failure' '3' = 'Success, Failure' } if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = @( $Settings = [ordered]@{ AuditAccountLogon = 'Not configured' AuditAccountManage = 'Not configured' AuditDSAccess = 'Not configured' AuditLogonEvents = 'Not configured' AuditObjectAccess = 'Not configured' AuditPolicyChange = 'Not configured' AuditPrivilegeUse = 'Not configured' AuditProcessTracking = 'Not configured' AuditSystemEvents = 'Not configured' # Advanced Policies AuditAccountLockout = 'Not configured' AuditApplicationGenerated = 'Not configured' AuditApplicationGroupManagement = 'Not configured' AuditAuditPolicyChange = 'Not configured' AuditAuthenticationPolicyChange = 'Not configured' AuditAuthorizationPolicyChange = 'Not configured' AuditCentralAccessPolicyStaging = 'Not configured' AuditCertificationServices = 'Not configured' AuditComputerAccountManagement = 'Not configured' AuditCredentialValidation = 'Not configured' AuditDetailedDirectoryServiceReplication = 'Not configured' AuditDetailedFileShare = 'Not configured' AuditDirectoryServiceAccess = 'Not configured' AuditDirectoryServiceChanges = 'Not configured' AuditDirectoryServiceReplication = 'Not configured' AuditDistributionGroupManagement = 'Not configured' AuditDPAPIActivity = 'Not configured' AuditFileShare = 'Not configured' AuditFileSystem = 'Not configured' AuditFilteringPlatformConnection = 'Not configured' AuditFilteringPlatformPacketDrop = 'Not configured' AuditFilteringPlatformPolicyChange = 'Not configured' AuditGroupMembership = 'Not configured' AuditHandleManipulation = 'Not configured' AuditIPsecDriver = 'Not configured' AuditIPsecExtendedMode = 'Not configured' AuditIPsecMainMode = 'Not configured' AuditIPsecQuickMode = 'Not configured' AuditKerberosAuthenticationService = 'Not configured' AuditKerberosServiceTicketOperations = 'Not configured' AuditKernelObject = 'Not configured' AuditLogoff = 'Not configured' AuditLogon = 'Not configured' AuditMPSSVCRuleLevelPolicyChange = 'Not configured' AuditNetworkPolicyServer = 'Not configured' AuditNonSensitivePrivilegeUse = 'Not configured' AuditOtherAccountLogonEvents = 'Not configured' AuditOtherAccountManagementEvents = 'Not configured' AuditOtherLogonLogoffEvents = 'Not configured' AuditOtherObjectAccessEvents = 'Not configured' AuditOtherPolicyChangeEvents = 'Not configured' AuditOtherPrivilegeUseEvents = 'Not configured' AuditOtherSystemEvents = 'Not configured' AuditPNPActivity = 'Not configured' AuditProcessCreation = 'Not configured' AuditProcessTermination = 'Not configured' AuditRegistry = 'Not configured' AuditRemovableStorage = 'Not configured' AuditRPCEvents = 'Not configured' AuditSAM = 'Not configured' AuditSecurityGroupManagement = 'Not configured' AuditSecurityStateChange = 'Not configured' AuditSecuritySystemExtension = 'Not configured' AuditSensitivePrivilegeUse = 'Not configured' AuditSpecialLogon = 'Not configured' AuditSystemIntegrity = 'Not configured' AuditUserDeviceClaims = 'Not configured' AuditUserAccountManagement = 'Not configured' } foreach ($GPOEntry in $GPO.DataSet) { if ($GPOEntry.PolicyTarget) { # Category = 'AuditSettings', Settings = 'AuditSetting' $Category = $GPOEntry.SubcategoryName -replace ' ', '' -replace '-', '' -replace '/', '' if ($Settings["$($Category)"]) { $Settings["$($Category)"] = $SettingType["$($GPOEntry.SettingValue)"] } } else { # Category = 'SecuritySettings', Settings = 'Audit' $SuccessAttempts = try { [bool]::Parse($GPOEntry.SuccessAttempts) } catch { $null }; $FailureAttempts = try { [bool]::Parse($GPOEntry.FailureAttempts) } catch { $null }; if ($SuccessAttempts -and $FailureAttempts) { $Setting = 'Success, Failure' } elseif ($SuccessAttempts) { $Setting = 'Success' } elseif ($FailureAttempts) { $Setting = 'Failure' } else { $Setting = 'Not configured' } $Settings["$($GPOEntry.Name)"] = $Setting } } [PSCustomObject] $Settings ) $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings AuditAccountLogon = 'Not configured' AuditAccountManage = 'Not configured' AuditDSAccess = 'Not configured' AuditLogonEvents = 'Not configured' AuditObjectAccess = 'Not configured' AuditPolicyChange = 'Not configured' AuditPrivilegeUse = 'Not configured' AuditProcessTracking = 'Not configured' AuditSystemEvents = 'Not configured' # Advanced Policies AuditAccountLockout = 'Not configured' AuditApplicationGenerated = 'Not configured' AuditApplicationGroupManagement = 'Not configured' AuditAuditPolicyChange = 'Not configured' AuditAuthenticationPolicyChange = 'Not configured' AuditAuthorizationPolicyChange = 'Not configured' AuditCentralAccessPolicyStaging = 'Not configured' AuditCertificationServices = 'Not configured' AuditComputerAccountManagement = 'Not configured' AuditCredentialValidation = 'Not configured' AuditDetailedDirectoryServiceReplication = 'Not configured' AuditDetailedFileShare = 'Not configured' AuditDirectoryServiceAccess = 'Not configured' AuditDirectoryServiceChanges = 'Not configured' AuditDirectoryServiceReplication = 'Not configured' AuditDistributionGroupManagement = 'Not configured' AuditDPAPIActivity = 'Not configured' AuditFileShare = 'Not configured' AuditFileSystem = 'Not configured' AuditFilteringPlatformConnection = 'Not configured' AuditFilteringPlatformPacketDrop = 'Not configured' AuditFilteringPlatformPolicyChange = 'Not configured' AuditGroupMembership = 'Not configured' AuditHandleManipulation = 'Not configured' AuditIPsecDriver = 'Not configured' AuditIPsecExtendedMode = 'Not configured' AuditIPsecMainMode = 'Not configured' AuditIPsecQuickMode = 'Not configured' AuditKerberosAuthenticationService = 'Not configured' AuditKerberosServiceTicketOperations = 'Not configured' AuditKernelObject = 'Not configured' AuditLogoff = 'Not configured' AuditLogon = 'Not configured' AuditMPSSVCRuleLevelPolicyChange = 'Not configured' AuditNetworkPolicyServer = 'Not configured' AuditNonSensitivePrivilegeUse = 'Not configured' AuditOtherAccountLogonEvents = 'Not configured' AuditOtherAccountManagementEvents = 'Not configured' AuditOtherLogonLogoffEvents = 'Not configured' AuditOtherObjectAccessEvents = 'Not configured' AuditOtherPolicyChangeEvents = 'Not configured' AuditOtherPrivilegeUseEvents = 'Not configured' AuditOtherSystemEvents = 'Not configured' AuditPNPActivity = 'Not configured' AuditProcessCreation = 'Not configured' AuditProcessTermination = 'Not configured' AuditRegistry = 'Not configured' AuditRemovableStorage = 'Not configured' AuditRPCEvents = 'Not configured' AuditSAM = 'Not configured' AuditSecurityGroupManagement = 'Not configured' AuditSecurityStateChange = 'Not configured' AuditSecuritySystemExtension = 'Not configured' AuditSensitivePrivilegeUse = 'Not configured' AuditSpecialLogon = 'Not configured' AuditSystemIntegrity = 'Not configured' AuditUserDeviceClaims = 'Not configured' AuditUserAccountManagement = 'Not configured' } foreach ($GPOEntry in $GPO.DataSet) { if ($GPOEntry.PolicyTarget) { # Category = 'AuditSettings', Settings = 'AuditSetting' $Category = $GPOEntry.SubcategoryName -replace ' ', '' -replace '-', '' -replace '/', '' if ($CreateGPO["$($Category)"]) { $CreateGPO["$($Category)"] = $SettingType["$($GPOEntry.SettingValue)"] } } else { # Category = 'SecuritySettings', Settings = 'Audit' $SuccessAttempts = try { [bool]::Parse($GPOEntry.SuccessAttempts) } catch { $null }; $FailureAttempts = try { [bool]::Parse($GPOEntry.FailureAttempts) } catch { $null }; if ($SuccessAttempts -and $FailureAttempts) { $Setting = 'Success, Failure' } elseif ($SuccessAttempts) { $Setting = 'Success' } elseif ($FailureAttempts) { $Setting = 'Failure' } else { $Setting = 'Not configured' } $CreateGPO["$($GPOEntry.Name)"] = $Setting } } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } function ConvertTo-XMLDriveMapSettings { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Entry in $GPO.DataSet.Drive) { [PSCustomObject] @{ Changed = [DateTime] $Entry.changed #uid = $Entry.uid GPOSettingOrder = $Entry.GPOSettingOrder Filter = $Entry.Filter Name = $Entry.Name Status = $Entry.status Action = $Script:Actions["$($Entry.Properties.action)"] ThisDrive = $Entry.Properties.thisDrive AllDrives = $Entry.Properties.allDrives UserName = $Entry.Properties.userName Path = $Entry.Properties.path Label = $Entry.Properties.label Persistent = if ($Entry.Properties.persistent -eq '1') { $true } elseif ($Entry.Properties.persistent -eq '0') { $false } else { $Entry.Properties.persistent }; UseLetter = if ($Entry.Properties.useLetter -eq '1') { $true } elseif ($Entry.Properties.useLetter -eq '0') { $false } else { $Entry.Properties.useLetter }; Letter = $Entry.Properties.letter } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Entry in $GPO.DataSet.Drive) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Changed = [DateTime] $Entry.changed #uid = $Entry.uid GPOSettingOrder = $Entry.GPOSettingOrder Filter = $Entry.Filter Name = $Entry.Name Status = $Entry.status Action = $Script:Actions["$($Entry.Properties.action)"] ThisDrive = $Entry.Properties.thisDrive AllDrives = $Entry.Properties.allDrives UserName = $Entry.Properties.userName Path = $Entry.Properties.path Label = $Entry.Properties.label Persistent = if ($Entry.Properties.persistent -eq '1') { $true } elseif ($Entry.Properties.persistent -eq '0') { $false } else { $Entry.Properties.persistent }; UseLetter = if ($Entry.Properties.useLetter -eq '1') { $true } elseif ($Entry.Properties.useLetter -eq '0') { $false } else { $Entry.Properties.useLetter }; Letter = $Entry.Properties.letter } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLEventLog { [cmdletBinding()] param( [PSCustomObject] $GPO ) $RetionPeriod = @{ '0' = 'Overwrite events as needed' '1' = 'Overwrite events by days' '2' = 'Do not overwrite events (Clear logs manually)' } $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings ApplicationAuditLogRetentionPeriod = $null ApplicationMaximumLogSize = $null ApplicationRestrictGuestAccess = $null ApplicationRetentionDays = $null SystemAuditLogRetentionPeriod = $null SystemMaximumLogSize = $null SystemRestrictGuestAccess = $null SystemRetentionDays = $null SecurityAuditLogRetentionPeriod = $null SecurityMaximumLogSize = $null SecurityRestrictGuestAccess = $null SecurityRetentionDays = $null } foreach ($GPOEntry in $GPO.DataSet) { if ($GPOEntry.SettingBoolean) { $CreateGPO["$($GPOEntry.Log)$($GPOEntry.Name)"] = if ($GPOEntry.SettingBoolean -eq 'true') { 'Enabled' } elseif ($GPOEntry.SettingBoolean -eq 'false') { 'Disabled' } else { 'Not set' }; } elseif ($GPOEntry.SettingNumber) { if ($GPOEntry.Name -eq 'AuditLogRetentionPeriod') { if ($GPOEntry.SettingNumber) { $CreateGPO["$($GPOEntry.Log)$($GPOEntry.Name)"] = $RetionPeriod[$($GPOEntry.SettingNumber)] } else { # Won't happen? $CreateGPO["$($GPOEntry.Log)$($GPOEntry.Name)"] = $GPOEntry.SettingNumber } } else { $CreateGPO["$($GPOEntry.Log)$($GPOEntry.Name)"] = $GPOEntry.SettingNumber } } } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } function ConvertTo-XMLGenericPolicy { [cmdletBinding()] param( [PSCustomObject] $GPO, [string[]] $Category, [switch] $SingleObject ) $UsedNames = [System.Collections.Generic.List[string]]::new() [Array] $Policies = foreach ($Cat in $Category) { $GPO.DataSet | Where-Object { $_.Category -like $Cat } } if ($Policies.Count -gt 0) { if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = @( $Settings = [ordered] @{} foreach ($Policy in $Policies) { #if ($Policy.Category -notlike $Category) { # We check again for Category because one GPO can have multiple categories # First check checks GPO globally, # continue #} $Name = Format-ToTitleCase -Text $Policy.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $Settings[$Name] = $Policy.State foreach ($Setting in @('DropDownList', 'Numeric', 'EditText', 'Text', 'CheckBox', 'ListBox')) { if ($Policy.$Setting) { foreach ($Value in $Policy.$Setting) { if ($Value.Name) { $SubName = Format-ToTitleCase -Text $Value.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $SubName = -join ($Name, $SubName) if ($SubName -notin $UsedNames) { $UsedNames.Add($SubName) } else { $TimesUsed = $UsedNames | Group-Object | Where-Object { $_.Name -eq $SubName } $NumberToUse = $TimesUsed.Count + 1 # We add same name 2nd and 3rd time to make sure we count properly $UsedNames.Add($SubName) # We now build property name based on amnount of times $SubName = -join ($SubName, "$NumberToUse") } if ($Value.Value -is [string]) { $Settings["$SubName"] = $Value.Value } elseif ($Value.Value -is [System.Xml.XmlElement]) { <# if ($null -eq $Value.Value.Name) { # Shouldn't happen but lets see Write-Verbose $Value } else { $CreateGPO["$SubName"] = $Value.Value.Name } #> if ($Value.Value.Element) { $Settings["$SubName"] = $Value.Value.Element.Data -join '; ' } elseif ($null -eq $Value.Value.Name) { # Shouldn't happen but lets see Write-Verbose "Tracking $Value" } else { $Settings["$SubName"] = $Value.Value.Name } } elseif ($Value.State) { $Settings["$SubName"] = $Value.State } elseif ($null -eq $Value.Value) { # This is most likely Setting 'Text # Do nothing, usually it's just a text to display #Write-Verbose "Skipping value for display because it's empty. Name: $($Value.Name)" } else { # shouldn't happen Write-Verbose $Value } } } } } } [PSCustomObject] $Settings ) $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings } foreach ($Policy in $Policies) { #if ($Policy.Category -notlike $Category) { # We check again for Category because one GPO can have multiple categories # First check checks GPO globally, # continue #} $Name = Format-ToTitleCase -Text $Policy.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $CreateGPO[$Name] = $Policy.State foreach ($Setting in @('DropDownList', 'Numeric', 'EditText', 'Text', 'CheckBox', 'ListBox')) { if ($Policy.$Setting) { foreach ($Value in $Policy.$Setting) { if ($Value.Name) { $SubName = Format-ToTitleCase -Text $Value.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $SubName = -join ($Name, $SubName) if ($SubName -notin $UsedNames) { $UsedNames.Add($SubName) } else { $TimesUsed = $UsedNames | Group-Object | Where-Object { $_.Name -eq $SubName } $NumberToUse = $TimesUsed.Count + 1 # We add same name 2nd and 3rd time to make sure we count properly $UsedNames.Add($SubName) # We now build property name based on amnount of times $SubName = -join ($SubName, "$NumberToUse") } if ($Value.Value -is [string]) { $CreateGPO["$SubName"] = $Value.Value } elseif ($Value.Value -is [System.Xml.XmlElement]) { <# if ($null -eq $Value.Value.Name) { # Shouldn't happen but lets see Write-Verbose $Value } else { $CreateGPO["$SubName"] = $Value.Value.Name } #> if ($Value.Value.Element) { $CreateGPO["$SubName"] = $Value.Value.Element.Data -join '; ' } elseif ($null -eq $Value.Value.Name) { # Shouldn't happen but lets see Write-Verbose "Tracking $Value" } else { $CreateGPO["$SubName"] = $Value.Value.Name } } elseif ($Value.State) { $CreateGPO["$SubName"] = $Value.State } elseif ($null -eq $Value.Value) { # This is most likely Setting 'Text # Do nothing, usually it's just a text to display #Write-Verbose "Skipping value for display because it's empty. Name: $($Value.Name)" } else { # shouldn't happen Write-Verbose $Value } } } } } } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO #} } } } function ConvertTo-XMLGenericPublicKey { [cmdletBinding()] param( [PSCustomObject] $GPO, [string[]] $Category, [switch] $SingleObject ) $SkipNames = ('Name', 'LocalName', 'NamespaceURI', 'Prefix', 'NodeType', 'ParentNode', 'OwnerDocument', 'IsEmpty', 'Attributes', 'HasAttributes', 'SchemaInfo', 'InnerXml', 'InnerText', 'NextSibling', 'PreviousSibling', 'ChildNodes', 'FirstChild', 'LastChild', 'HasChildNodes', 'IsReadOnly', 'OuterXml', 'BaseURI', 'PreviousText') if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Setting in $GPO.DataSet) { $SettingName = $Setting.Name -split ":" $MySettings = [ordered] @{ CreatedTime = $GPO.CreatedTime # : 06.06.2020 18:03:36 ModifiedTime = $GPO.ModifiedTime # : 17.06.2020 16:08:10 ReadTime = $GPO.ReadTime # : 13.08.2020 10:15:37 SecurityDescriptor = $GPO.SecurityDescriptor # : SecurityDescriptor FilterDataAvailable = $GPO.FilterDataAvailable # : True } $Name = $SettingName[1] #$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $MySettings['Name'] = $Name # $Setting.Name ConvertTo-XMLNested -CreateGPO $MySettings -Setting $Setting -SkipNames $SkipNames #-Name $Name [PSCustomObject] $MySettings } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Setting in $GPO.DataSet) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings } $SettingName = $Setting.Name -split ":" $CreateGPO['CreatedTime'] = $GPO.CreatedTime # : 06.06.2020 18:03:36 $CreateGPO['ModifiedTime'] = $GPO.ModifiedTime # : 17.06.2020 16:08:10 $CreateGPO['ReadTime'] = $GPO.ReadTime # : 13.08.2020 10:15:37 $CreateGPO['SecurityDescriptor'] = $GPO.SecurityDescriptor # : SecurityDescriptor $CreateGPO['FilterDataAvailable'] = $GPO.FilterDataAvailable # : True $Name = $SettingName[1] #$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $CreateGPO['Name'] = $Name # $Setting.Name #$CreateGPO['GPOSettingOrder'] = $Setting.GPOSettingOrder #foreach ($Property in ($Setting.Properties | Get-Member -MemberType Properties).Name) { ConvertTo-XMLNested -CreateGPO $CreateGPO -Setting $Setting -SkipNames $SkipNames #-Name $Name <# $Properties = $Setting.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames } foreach ($Property in $Properties) { If ($Property -eq 'Value') { if ($Setting.$Property) { #$SubProperties = $Setting.$Property.PSObject.Properties.Name if ($Setting.$Property.Name) { $Name = $Setting.$Property.Name } else { $Name = 'Value' } if ($Setting.$Property.Number) { $CreateGPO[$Name] = $Setting.$Property.Number } elseif ($Setting.$Property.String) { $CreateGPO[$Name] = $Setting.$Property.String } else { throw } } } else { $Name = Format-CamelCaseToDisplayName -Text $Property #-RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' if ($Setting.$Property -is [System.Xml.XmlElement]) { $SubPropeties = $Setting.$Property.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames } } else { $CreateGPO[$Name] = $Setting.$Property } } } #> $CreateGPO['Filters'] = $Setting.Filters $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLGenericSecuritySettings { [cmdletBinding()] param( [PSCustomObject] $GPO, [string[]] $Category ) $SkipNames = ('Name', 'LocalName', 'NamespaceURI', 'Prefix', 'NodeType', 'ParentNode', 'OwnerDocument', 'IsEmpty', 'Attributes', 'HasAttributes', 'SchemaInfo', 'InnerXml', 'InnerText', 'NextSibling', 'PreviousSibling', 'Value', 'ChildNodes', 'FirstChild', 'LastChild', 'HasChildNodes', 'IsReadOnly', 'OuterXml', 'BaseURI', 'PreviousText') #$UsedNames = [System.Collections.Generic.List[string]]::new() [Array] $Settings = foreach ($Cat in $Category) { $GPO.DataSet | Where-Object { $null -ne $_.$Cat } } if ($Settings.Count -gt 0) { foreach ($Cat in $Category) { foreach ($Setting in $Settings.$Cat) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings } #$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $CreateGPO['Name'] = $Setting.Name $CreateGPO['GPOSettingOrder'] = $Setting.GPOSettingOrder #foreach ($Property in ($Setting.Properties | Get-Member -MemberType Properties).Name) { $Properties = $Setting.Properties.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames } foreach ($Property in $Properties) { $Name = Format-CamelCaseToDisplayName -Text $Property #-RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' $CreateGPO[$Name] = $Setting.Properties.$Property } $CreateGPO['Filters'] = $Setting.Filters $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } } function ConvertTo-XMLLocalGroups { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } if (-not $GPO.DataSet.Group) { continue } [Array] $CreateGPO['Settings'] = foreach ($Group in $GPO.DataSet.Group) { # We're mostly interested in Members [Array] $Members = foreach ($Member in $Group.Properties.Members.Member) { [ordered] @{ MemberName = $Member.Name MemberAction = $Member.Action MemberSID = $Member.SID } } # if we have no members we create dummy object to make sure we can use foreach below if ($Members.Count -eq 0) { $Members = @( [ordered] @{ MemberName = $null MemberAction = $null MemberSID = $null } ) } foreach ($Member in $Members) { $GroupObject = [ordered]@{ Changed = [DateTime] $Group.Changed GPOSettingOrder = $Group.GPOSettingOrder Name = $Group.name Action = $Script:Actions["$($Group.Properties.action)"] GroupName = $Group.Properties.groupName #: Administrators (built -in ) NewName = $Group.Properties.newName #: Description = $Group.Properties.description #: DeleteAllUsers = if ($Group.Properties.deleteAllUsers -eq '1') { 'Enabled' } elseif ($Group.Properties.deleteAllUsers -eq '0') { 'Disabled' } else { $Group.Properties.deleteAllUsers }; DeleteAllGroups = if ($Group.Properties.deleteAllGroups -eq '1') { 'Enabled' } elseif ($Group.Properties.deleteAllGroups -eq '0') { 'Disabled' } else { $Group.Properties.deleteAllGroups }; RemoveAccounts = if ($Group.Properties.removeAccounts -eq '1') { 'Enabled' } elseif ($Group.Properties.removeAccounts -eq '0') { 'Disabled' } else { $Group.Properties.removeAccounts }; GroupSid = $Group.Properties.groupSid #: S - 1 - 5 - 32 - 544 } $Last = [ordered] @{ #Uid = $Group.uid #: {8F435B0A-CD15-464E-85F3-B6A55B9E816A}: {8F435B0A-CD15-464E-85F3-B6A55B9E816A} RunInLoggedOnUserSecurityContext = if ($Group.userContext -eq '1') { 'Enabled' } elseif ($Group.userContext -eq '0') { 'Disabled' } else { $Group.userContext }; RemoveThisItemWhenItIsNoLongerApplied = if ($Group.removePolicy -eq '1') { 'Enabled' } elseif ($Group.removePolicy -eq '0') { 'Disabled' } else { $Group.removePolicy }; Filters = $Group.Filters #:: } # Merging GPO with Member $GroupObject = $GroupObject + $Member + $Last [PSCustomObject] $GroupObject } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Group in $GPO.DataSet.Group) { # We're mostly interested in Members [Array] $Members = foreach ($Member in $Group.Properties.Members.Member) { [ordered] @{ MemberName = $Member.Name MemberAction = $Member.Action MemberSID = $Member.SID } } # if we have no members we create dummy object to make sure we can use foreach below if ($Members.Count -eq 0) { $Members = @( [ordered] @{ MemberName = $null MemberAction = $null MemberSID = $null } ) } foreach ($Member in $Members) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPO.GpoCategory #: SecuritySettings #GpoSettings = $GPO.GpoSettings #: SecurityOptions Changed = [DateTime] $Group.Changed GPOSettingOrder = $Group.GPOSettingOrder Name = $Group.name Action = $Script:Actions["$($Group.Properties.action)"] GroupName = $Group.Properties.groupName #: Administrators (built -in ) NewName = $Group.Properties.newName #: Description = $Group.Properties.description #: DeleteAllUsers = if ($Group.Properties.deleteAllUsers -eq '1') { 'Enabled' } elseif ($Group.Properties.deleteAllUsers -eq '0') { 'Disabled' } else { $Group.Properties.deleteAllUsers }; DeleteAllGroups = if ($Group.Properties.deleteAllGroups -eq '1') { 'Enabled' } elseif ($Group.Properties.deleteAllGroups -eq '0') { 'Disabled' } else { $Group.Properties.deleteAllGroups }; RemoveAccounts = if ($Group.Properties.removeAccounts -eq '1') { 'Enabled' } elseif ($Group.Properties.removeAccounts -eq '0') { 'Disabled' } else { $Group.Properties.removeAccounts }; GroupSid = $Group.Properties.groupSid #: S - 1 - 5 - 32 - 544 } $Last = [ordered] @{ # Uid = $Group.uid #: {8F435B0A-CD15-464E-85F3-B6A55B9E816A}: {8F435B0A-CD15-464E-85F3-B6A55B9E816A} RunInLoggedOnUserSecurityContext = if ($Group.userContext -eq '1') { 'Enabled' } elseif ($Group.userContext -eq '0') { 'Disabled' } else { $Group.userContext }; RemoveThisItemWhenItIsNoLongerApplied = if ($Group.removePolicy -eq '1') { 'Enabled' } elseif ($Group.removePolicy -eq '0') { 'Disabled' } else { $Group.removePolicy }; Filters = $Group.Filters #:: } # Merging GPO with Member $CreateGPO = $CreateGPO + $Member + $Last $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } } function ConvertTo-XMLLocalUser { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } if (-not $GPO.DataSet.User) { continue } [Array] $CreateGPO['Settings'] = foreach ($User in $GPO.DataSet.User) { [PSCustomObject] @{ Changed = [DateTime] $User.Changed GPOSettingOrder = $User.GPOSettingOrder Action = $Script:Actions["$($User.Properties.action)"] UserName = $User.Properties.userName NewName = $User.Properties.newName FullName = $User.Properties.fullName Description = $User.Properties.description Password = $User.Properties.cpassword MustChangePasswordAtNextLogon = if ($User.Properties.changeLogon -eq '1') { $true } elseif ($User.Properties.changeLogon -eq '0') { $false } else { $User.Properties.changeLogon }; CannotChangePassword = if ($User.Properties.noChange -eq '1') { $true } elseif ($User.Properties.noChange -eq '0') { $false } else { $User.Properties.noChange }; PasswordNeverExpires = if ($User.Properties.neverExpires -eq '1') { $true } elseif ($User.Properties.neverExpires -eq '0') { $false } else { $User.Properties.neverExpires }; AccountIsDisabled = if ($User.Properties.acctDisabled -eq '1') { $true } elseif ($User.Properties.acctDisabled -eq '0') { $false } else { $User.Properties.acctDisabled }; AccountExpires = try { [DateTime] $User.Properties.expires } catch { $User.Properties.expires }; SubAuthority = $User.Properties.subAuthority } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($User in $GPO.DataSet.User) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPO.GpoCategory #: SecuritySettings #GpoSettings = $GPO.GpoSettings #: SecurityOptions Changed = [DateTime] $User.Changed GPOSettingOrder = $User.GPOSettingOrder Action = $Script:Actions["$($User.Properties.action)"] UserName = $User.Properties.userName NewName = $User.Properties.newName FullName = $User.Properties.fullName Description = $User.Properties.description Password = $User.Properties.cpassword MustChangePasswordAtNextLogon = if ($User.Properties.changeLogon -eq '1') { $true } elseif ($User.Properties.changeLogon -eq '0') { $false } else { $User.Properties.changeLogon }; CannotChangePassword = if ($User.Properties.noChange -eq '1') { $true } elseif ($User.Properties.noChange -eq '0') { $false } else { $User.Properties.noChange }; PasswordNeverExpires = if ($User.Properties.neverExpires -eq '1') { $true } elseif ($User.Properties.neverExpires -eq '0') { $false } else { $User.Properties.neverExpires }; AccountIsDisabled = if ($User.Properties.acctDisabled -eq '1') { $true } elseif ($User.Properties.acctDisabled -eq '0') { $false } else { $User.Properties.acctDisabled }; AccountExpires = try { [DateTime] $User.Properties.expires } catch { $User.Properties.expires }; SubAuthority = $User.Properties.subAuthority } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLNested { [cmdletBinding()] param( [System.Collections.IDictionary] $CreateGPO, [System.Xml.XmlElement] $Setting, [string[]] $SkipNames, [string] $Name ) $Properties = $Setting.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames } $TempName = $Name foreach ($Property in $Properties) { If ($Property -eq 'Value') { if ($Setting.$Property) { #$SubProperties = $Setting.$Property.PSObject.Properties.Name if ($Setting.$Property.Name) { $Name = $Setting.$Property.Name } else { if (-not $Name) { $Name = 'Value' } } if ($Setting.$Property.Number) { $CreateGPO[$Name] = $Setting.$Property.Number } elseif ($Setting.$Property.String) { $CreateGPO[$Name] = $Setting.$Property.String } else { $CreateGPO[$Name] = $Setting.$Property #throw } } } else { $Name = -join ($Name, $Property) $Name = Format-CamelCaseToDisplayName -Text $Name #-RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' if ($Setting.$Property -is [System.Xml.XmlElement]) { #$SubPropeties = $Setting.$Property.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames } ConvertTo-XMLNested -Setting $Setting.$Property -CreateGPO $CreateGPO -Name $Name -SkipNames $SkipNames } else { $CreateGPO[$Name] = $Setting.$Property } } $Name = $TempName } } function ConvertTo-XMLPolicies { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Policy in $GPO.DataSet) { [PSCustomObject] @{ PolicyName = $Policy.Name PolicyState = $Policy.State PolicyCategory = $Policy.Category PolicySupported = $Policy.Supported PolicyExplain = $Policy.Explain PolicyText = $Policy.Text PolicyCheckBox = $Policy.CheckBox PolicyDropDownList = $Policy.DropDownList PolicyEditText = $Policy.EditText } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Policy in $GPO.DataSet) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings PolicyName = $Policy.Name PolicyState = $Policy.State PolicyCategory = $Policy.Category PolicySupported = $Policy.Supported PolicyExplain = $Policy.Explain PolicyText = $Policy.Text PolicyCheckBox = $Policy.CheckBox PolicyDropDownList = $Policy.DropDownList PolicyEditText = $Policy.EditText } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLPrinter { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = @( foreach ($Type in @('SharedPrinter', 'PortPrinter', 'LocalPrinter')) { foreach ($Entry in $GPO.DataSet.$Type) { if ($Entry) { ConvertTo-XMLPrinterInternal -GPO $GPO -Entry $Entry -Type $Type -Limited } } } if ($GPO.GpoCategory -eq 'PrinterConnectionSettings') { foreach ($Entry in $GPO.DataSet) { ConvertTo-XMLPrinterInternal -GPO $GPO -Entry $Entry -Type 'PrinterConnections' -Limited } } ) $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Type in @('SharedPrinter', 'PortPrinter', 'LocalPrinter')) { foreach ($Entry in $GPO.DataSet.$Type) { if ($Entry) { ConvertTo-XMLPrinterInternal -GPO $GPO -Entry $Entry -Type $Type } } } if ($GPO.GpoCategory -eq 'PrinterConnectionSettings') { foreach ($Entry in $GPO.DataSet) { ConvertTo-XMLPrinterInternal -GPO $GPO -Entry $Entry -Type 'PrinterConnections' } } } } function ConvertTo-XMLPrinterInternal { [cmdletBinding()] param( [PSCustomObject] $GPO, $Entry, $Type, [switch] $Limited ) if ($Limited) { $CreateGPO = [ordered]@{ Changed = try { [DateTime] $Entry.changed } catch { $Entry.Changed }; #uid = $Entry.uid BypassErrors = if ($Entry.bypassErrors -eq '1') { $true } else { $false }; GPOSettingOrder = $Entry.GPOSettingOrder Filter = $Entry.Filter type = $Type Action = $null #$Script:Actions["$($Entry.Properties.action)"] Comment = $Entry.Properties.comment Path = if ($Entry.Properties.path) { $Entry.Properties.Path } elseif ($Entry.Path) { $Entry.Path } else { '' } Location = $Entry.Properties.location HostName = $Entry.Properties.ipAddress #: 10.42.20.204 LocalName = $Entry.Properties.localName #: CZ02PRT00017 UseDNS = if ($Entry.Properties.useDNS -eq '1') { $true } elseif ($Entry.Properties.useDNS -eq '0') { $false } else { $Entry.Properties.useDNS }; UseIPv6 = if ($Entry.Properties.useIPv6 -eq '1') { $true } elseif ($Entry.Properties.useIPv6 -eq '0') { $false } else { $Entry.Properties.useIPv6 }; Default = if ($Entry.Properties.default -eq '1') { $true } elseif ($Entry.Properties.default -eq '0') { $false } else { $Entry.Properties.default }; SkipLocal = if ($Entry.Properties.skipLocal -eq '1') { $true } elseif ($Entry.Properties.skipLocal -eq '0') { $false } else { $Entry.Properties.skipLocal }; DeleteAllShared = if ($Entry.Properties.deleteAll -eq '1') { $true } elseif ($Entry.Properties.deleteAll -eq '0') { $false } else { $Entry.Properties.deleteAll }; Persistent = if ($Entry.Properties.persistent -eq '1') { $true } elseif ($Entry.Properties.persistent -eq '0') { $false } else { $Entry.Properties.persistent }; DeleteMaps = if ($Entry.Properties.deleteMaps -eq '1') { $true } elseif ($Entry.Properties.deleteMaps -eq '0') { $false } else { $Entry.Properties.deleteMaps }; LPRSettingsQueueName = $Entry.Properties.lprQueue #: Protocol = $Entry.Properties.protocol #: PROTOCOL_RAWTCP_TYPE PortNumber = if ($Entry.Properties.portNumber) { $Entry.Properties.portNumber } else { $Entry.Properties.port } DoubleSpool = if ($Entry.Properties.doubleSpool -eq '1') { $true } elseif ($Entry.Properties.doubleSpool -eq '0') { $false } else { $Entry.Properties.doubleSpool }; SNMPEnabled = if ($Entry.Properties.snmpEnabled -eq '1') { $true } elseif ($Entry.Properties.snmpEnabled -eq '0') { $false } else { $Entry.Properties.snmpEnabled }; SNMPCommunityName = $Entry.Properties.snmpCommunity #: public SNMPDeviceIndex = $Entry.Properties.snmpDevIndex #: 1 } if ($Entry.Properties.Action) { $CreateGPO['Action'] = $Script:Actions["$($Entry.Properties.action)"] } else { $CreateGPO['Action'] = 'Deploy' } [PSCustomObject] $CreateGPO } else { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType GpoCategory = $GPO.GpoCategory GpoSettings = $GPO.GpoSettings Changed = try { [DateTime] $Entry.changed } catch { $Entry.Changed }; #uid = $Entry.uid BypassErrors = if ($Entry.bypassErrors -eq '1') { $true } else { $false }; GPOSettingOrder = $Entry.GPOSettingOrder Filter = $Entry.Filter type = $Type Action = $null #$Script:Actions["$($Entry.Properties.action)"] Comment = $Entry.Properties.comment Path = if ($Entry.Properties.path) { $Entry.Properties.Path } elseif ($Entry.Path) { $Entry.Path } else { '' } Location = $Entry.Properties.location HostName = $Entry.Properties.ipAddress #: 10.42.20.204 LocalName = $Entry.Properties.localName #: CZ02PRT00017 UseDNS = if ($Entry.Properties.useDNS -eq '1') { $true } elseif ($Entry.Properties.useDNS -eq '0') { $false } else { $Entry.Properties.useDNS }; UseIPv6 = if ($Entry.Properties.useIPv6 -eq '1') { $true } elseif ($Entry.Properties.useIPv6 -eq '0') { $false } else { $Entry.Properties.useIPv6 }; Default = if ($Entry.Properties.default -eq '1') { $true } elseif ($Entry.Properties.default -eq '0') { $false } else { $Entry.Properties.default }; SkipLocal = if ($Entry.Properties.skipLocal -eq '1') { $true } elseif ($Entry.Properties.skipLocal -eq '0') { $false } else { $Entry.Properties.skipLocal }; DeleteAllShared = if ($Entry.Properties.deleteAll -eq '1') { $true } elseif ($Entry.Properties.deleteAll -eq '0') { $false } else { $Entry.Properties.deleteAll }; Persistent = if ($Entry.Properties.persistent -eq '1') { $true } elseif ($Entry.Properties.persistent -eq '0') { $false } else { $Entry.Properties.persistent }; DeleteMaps = if ($Entry.Properties.deleteMaps -eq '1') { $true } elseif ($Entry.Properties.deleteMaps -eq '0') { $false } else { $Entry.Properties.deleteMaps }; LPRSettingsQueueName = $Entry.Properties.lprQueue #: Protocol = $Entry.Properties.protocol #: PROTOCOL_RAWTCP_TYPE PortNumber = if ($Entry.Properties.portNumber) { $Entry.Properties.portNumber } else { $Entry.Properties.port } DoubleSpool = if ($Entry.Properties.doubleSpool -eq '1') { $true } elseif ($Entry.Properties.doubleSpool -eq '0') { $false } else { $Entry.Properties.doubleSpool }; SNMPEnabled = if ($Entry.Properties.snmpEnabled -eq '1') { $true } elseif ($Entry.Properties.snmpEnabled -eq '0') { $false } else { $Entry.Properties.snmpEnabled }; SNMPCommunityName = $Entry.Properties.snmpCommunity #: public SNMPDeviceIndex = $Entry.Properties.snmpDevIndex #: 1 } if ($Entry.Properties.Action) { $CreateGPO['Action'] = $Script:Actions["$($Entry.Properties.action)"] } else { $CreateGPO['Action'] = 'Deploy' } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } function ConvertTo-XMLRegistryAutologon { [cmdletBinding()] param( [PSCustomObject] $GPO ) $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings AutoAdminLogon = $null DefaultDomainName = $null DefaultUserName = $null DefaultPassword = $null DateChangedAutoAdminLogon = $null DateChangedDefaultDomainName = $null DateChangedDefaultUserName = $null DateChangedDefaultPassword = $null } foreach ($Registry in $GPO.DataSet.Registry) { if ($Registry.Properties.Key -eq 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon') { if ($Registry.Properties.Name -eq 'AutoAdminLogon') { $CreateGPO['AutoAdminLogon'] = [bool] $Registry.Properties.value $CreateGPO['DateChangedAutoAdminLogon'] = [DateTime] $Registry.changed } elseif ($Registry.Properties.Name -eq 'DefaultDomainName') { $CreateGPO['DefaultDomainName'] = $Registry.Properties.value $CreateGPO['DateChangedDefaultDomainName'] = [DateTime] $Registry.changed } elseif ($Registry.Properties.Name -eq 'DefaultUserName') { $CreateGPO['DefaultUserName'] = $Registry.Properties.value $CreateGPO['DateChangedDefaultUserName'] = [DateTime] $Registry.changed } elseif ($Registry.Properties.Name -eq 'DefaultPassword') { $CreateGPO['DefaultPassword'] = $Registry.Properties.value $CreateGPO['DateChangedDefaultPassword'] = [DateTime] $Registry.changed } } } if ($null -ne $CreateGPO['AutoAdminLogon'] -or $null -ne $CreateGPO['DefaultDomainName'] -or $null -ne $CreateGPO['DefaultUserName'] -or $null -ne $CreateGPO['DefaultPassword'] ) { $CreateGPO['Linked'] = $GPOEntry.Linked $CreateGPO['LinksCount'] = $GPOEntry.LinksCount $CreateGPO['Links'] = $GPOEntry.Links [PSCustomObject] $CreateGPO } } function ConvertTo-XMLRegistryAutologonOnReport { [cmdletBinding()] param( [PSCustomObject] $GPO ) $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings AutoAdminLogon = $null DefaultDomainName = $null DefaultUserName = $null DefaultPassword = $null DateChangedAutoAdminLogon = $null DateChangedDefaultDomainName = $null DateChangedDefaultUserName = $null DateChangedDefaultPassword = $null } foreach ($Registry in $GPO.Settings) { if ($Registry.Key -eq 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon') { if ($Registry.Name -eq 'AutoAdminLogon') { $CreateGPO['AutoAdminLogon'] = [bool] $Registry.value $CreateGPO['DateChangedAutoAdminLogon'] = [DateTime] $Registry.changed } elseif ($Registry.Name -eq 'DefaultDomainName') { $CreateGPO['DefaultDomainName'] = $Registry.value $CreateGPO['DateChangedDefaultDomainName'] = [DateTime] $Registry.changed } elseif ($Registry.Name -eq 'DefaultUserName') { $CreateGPO['DefaultUserName'] = $Registry.value $CreateGPO['DateChangedDefaultUserName'] = [DateTime] $Registry.changed } elseif ($Registry.Name -eq 'DefaultPassword') { $CreateGPO['DefaultPassword'] = $Registry.value $CreateGPO['DateChangedDefaultPassword'] = [DateTime] $Registry.changed } } } if ($null -ne $CreateGPO['AutoAdminLogon'] -or $null -ne $CreateGPO['DefaultDomainName'] -or $null -ne $CreateGPO['DefaultUserName'] -or $null -ne $CreateGPO['DefaultPassword'] ) { $CreateGPO['Linked'] = $GPO.Linked #: True $CreateGPO['LinksCount'] = $GPO.LinksCount #: 1 $CreateGPO['Links'] = $GPO.Links #: area1.local [PSCustomObject] $CreateGPO } } function ConvertTo-XMLRegistryInternetExplorerZones { [cmdletBinding()] param( [PSCustomObject] $GPO ) foreach ($Registry in $GPO.Settings) { $Keys = @( 'Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\' 'Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\' ) $Found = $false foreach ($Key in $Keys) { if ($Registry.Key -like "$Key*") { $Found = $true } } if ($Found -eq $false) { continue } # https://support.microsoft.com/en-us/help/182569/internet-explorer-security-zones-registry-entries-for-advanced-users if ($Registry.Key -like 'Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\*') { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings } $CreateGPO['Disabled'] = $Registry.Disabled if ($Registry.Key -like '*EscDomains*') { $CreateGPO['Configuration'] = 'Enhanced Security Configuration (ESC)' } else { $CreateGPO['Configuration'] = 'Domains' } if ($Registry.Hive -eq 'HKEY_CURRENT_USER') { $CreateGPO['Type'] = 'User Policy' } elseif ($Registry.Hive -eq 'HKEY_LOCAL_MACHINE') { $CreateGPO['Type'] = 'Computer Policy' } else { $CreateGPO['Type'] = $Registry.Hive } if ($Registry.Value -eq '00000000') { $CreateGPO['Zone'] = 'My Computer (0)' } elseif ($Registry.Value -eq '00000001') { $CreateGPO['Zone'] = 'Local Intranet Zone (1)' } elseif ($Registry.Value -eq '00000002') { $CreateGPO['Zone'] = 'Trusted Sites Zone (2)' } elseif ($Registry.Value -eq '00000003') { $CreateGPO['Zone'] = 'Internet Zone (3)' } elseif ($Registry.Value -eq '00000004') { $CreateGPO['Zone'] = 'Restricted Sites Zone (4)' } else { $CreateGPO['Zone'] = $Registry.Value } [string] $FullKey = foreach ($Key in $Keys) { if ($Registry.Key -like "$Key*") { $Registry.Key.Replace($Key, '') } } $DomainSplit = $FullKey.Split('\') $Reversed = for ($i = $DomainSplit.Count - 1; $i -ge 0; $i--) { $DomainSplit[$i] } if ($Registry.Name -eq '*') { $CreateGPO['DomainZone'] = $Reversed -join '.' } else { $CreateGPO['DomainZone'] = -join ($Registry.Name, '://', ($Reversed -join '.')) } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLRegistrySettings { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = Get-XMLNestedRegistry -GPO $GPO -DataSet $GPO.DataSet $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { Get-XMLNestedRegistry -GPO $GPO -DataSet $GPO.DataSet } } function ConvertTo-XMLScripts { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Script in $GPO.DataSet) { [PSCustomObject] @{ Type = $GPO.DataSet.Type Command = $GPO.DataSet.Command Parameters = $GPO.DataSet.Parameters Order = $GPO.DataSet.Order RunOrder = $GPO.DataSet.RunOrder } } $CreateGPO['DataCount'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Script in $GPO.DataSet) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Type = $Script.Type Command = $Script.Command Parameters = $Script.Parameters Order = $Script.Order RunOrder = $Script.RunOrder } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLSecurityOptions { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Entry in $GPO.DataSet) { $Object = [ordered] @{} $Object['KeyName'] = $Entry.KeyName $Object['KeyDisplayName'] = $Entry.Display.Name $Object['KeyDisplayUnits'] = $Entry.Display.Units $Object['KeyDisplayBoolean'] = try { [bool]::Parse($Entry.Display.DisplayBoolean) } catch { $null }; $Object['KeyDisplayString'] = $Entry.Display.DisplayString $Object['SystemAccessPolicyName'] = $Entry.SystemAccessPolicyName if ($Entry.SettingString) { $Object['KeyValue'] = $Entry.SettingString } else { $Object['KeyValue'] = $Entry.SettingNumber } [PSCustomObject] $Object } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Entry in $GPO.DataSet) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings } $CreateGPO['KeyName'] = $Entry.KeyName $CreateGPO['KeyDisplayName'] = $Entry.Display.Name $CreateGPO['KeyDisplayUnits'] = $Entry.Display.Units $CreateGPO['KeyDisplayBoolean'] = try { [bool]::Parse($Entry.Display.DisplayBoolean) } catch { $null }; $CreateGPO['KeyDisplayString'] = $Entry.Display.DisplayString $CreateGPO['SystemAccessPolicyName'] = $Entry.SystemAccessPolicyName if ($Entry.SettingString) { $CreateGPO['KeyValue'] = $Entry.SettingString } else { $CreateGPO['KeyValue'] = $Entry.SettingNumber } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLSoftwareInstallation { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($MsiInstallerr in $GPO.DataSet) { [PSCustomObject] @{ Identifier = $MsiInstallerr.Identifier #: { 10495e9e-79c1-4a32-b278-a24cd495437f } Name = $MsiInstallerr.Name #: Local Administrator Password Solution (2) Path = $MsiInstallerr.Path #: \\area1.local\SYSVOL\area1.local\Policies\ { 5F5042A0-008F-45E3-8657-79C87BD002E3 }\LAPS.x64.msi MajorVersion = $MsiInstallerr.MajorVersion #: 6 MinorVersion = $MsiInstallerr.MinorVersion #: 2 LanguageId = $MsiInstallerr.LanguageId #: 1033 Architecture = $MsiInstallerr.Architecture #: 9 IgnoreLanguage = if ($MsiInstallerr.IgnoreLanguage -eq 'true') { $true } else { $false } #: false Allowx86Onia64 = if ($MsiInstallerr.Allowx86Onia64 -eq 'true') { $true } else { $false } #: true SupportURL = $MsiInstallerr.SupportURL #: AutoInstall = if ($MsiInstallerr.AutoInstall -eq 'true') { $true } else { $false } #: true DisplayInARP = if ($MsiInstallerr.DisplayInARP -eq 'true') { $true } else { $false } #: true IncludeCOM = if ($MsiInstallerr.IncludeCOM -eq 'true') { $true } else { $false } #: true SecurityDescriptor = $MsiInstallerr.SecurityDescriptor #: SecurityDescriptor DeploymentType = $MsiInstallerr.DeploymentType #: Assign ProductId = $MsiInstallerr.ProductId #: { ea8cb806-c109 - 4700 - 96b4-f1f268e5036c } ScriptPath = $MsiInstallerr.ScriptPath #: \\area1.local\SysVol\area1.local\Policies\ { 5F5042A0-008F-45E3-8657-79C87BD002E3 }\Machine\Applications\ { EAC9B821-FB4D - 457A-806F-E5B528D1E41A }.aas DeploymentCount = $MsiInstallerr.DeploymentCount #: 0 InstallationUILevel = $MsiInstallerr.InstallationUILevel #: Maximum Upgrades = if ($MsiInstallerr.Upgrades.Mandatory -eq 'true') { $true } else { $false } #: Upgrades UninstallUnmanaged = if ($MsiInstallerr.UninstallUnmanaged -eq 'true') { $true } else { $false } #: false LossOfScopeAction = $MsiInstallerr.LossOfScopeAction #: Unmanage } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($MsiInstallerr in $GPO.DataSet) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Identifier = $MsiInstallerr.Identifier #: { 10495e9e-79c1-4a32-b278-a24cd495437f } Name = $MsiInstallerr.Name #: Local Administrator Password Solution (2) Path = $MsiInstallerr.Path #: \\area1.local\SYSVOL\area1.local\Policies\ { 5F5042A0-008F-45E3-8657-79C87BD002E3 }\LAPS.x64.msi MajorVersion = $MsiInstallerr.MajorVersion #: 6 MinorVersion = $MsiInstallerr.MinorVersion #: 2 LanguageId = $MsiInstallerr.LanguageId #: 1033 Architecture = $MsiInstallerr.Architecture #: 9 IgnoreLanguage = if ($MsiInstallerr.IgnoreLanguage -eq 'true') { $true } else { $false } #: false Allowx86Onia64 = if ($MsiInstallerr.Allowx86Onia64 -eq 'true') { $true } else { $false } #: true SupportURL = $MsiInstallerr.SupportURL #: AutoInstall = if ($MsiInstallerr.AutoInstall -eq 'true') { $true } else { $false } #: true DisplayInARP = if ($MsiInstallerr.DisplayInARP -eq 'true') { $true } else { $false } #: true IncludeCOM = if ($MsiInstallerr.IncludeCOM -eq 'true') { $true } else { $false } #: true SecurityDescriptor = $MsiInstallerr.SecurityDescriptor #: SecurityDescriptor DeploymentType = $MsiInstallerr.DeploymentType #: Assign ProductId = $MsiInstallerr.ProductId #: { ea8cb806-c109 - 4700 - 96b4-f1f268e5036c } ScriptPath = $MsiInstallerr.ScriptPath #: \\area1.local\SysVol\area1.local\Policies\ { 5F5042A0-008F-45E3-8657-79C87BD002E3 }\Machine\Applications\ { EAC9B821-FB4D - 457A-806F-E5B528D1E41A }.aas DeploymentCount = $MsiInstallerr.DeploymentCount #: 0 InstallationUILevel = $MsiInstallerr.InstallationUILevel #: Maximum Upgrades = if ($MsiInstallerr.Upgrades.Mandatory -eq 'true') { $true } else { $false } #: Upgrades UninstallUnmanaged = if ($MsiInstallerr.UninstallUnmanaged -eq 'true') { $true } else { $false } #: false LossOfScopeAction = $MsiInstallerr.LossOfScopeAction #: Unmanage } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLSystemServices { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($GPOEntry in $GPO.DataSet) { [PSCustomObject] @{ ServiceName = $GPOEntry.Name ServiceStartUpMode = $GPOEntry.StartUpMode SecurityAuditingPresent = try { [bool]::Parse($GPOEntry.SecurityDescriptor.AuditingPresent.'#text') } catch { $null }; SecurityPermissionsPresent = try { [bool]::Parse($GPOEntry.SecurityDescriptor.PermissionsPresent.'#text') } catch { $null }; SecurityDescriptor = $GPOEntry.SecurityDescriptor } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($GPOEntry in $GPO.DataSet) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings ServiceName = $GPOEntry.Name ServiceStartUpMode = $GPOEntry.StartUpMode SecurityAuditingPresent = try { [bool]::Parse($GPOEntry.SecurityDescriptor.AuditingPresent.'#text') } catch { $null }; SecurityPermissionsPresent = try { [bool]::Parse($GPOEntry.SecurityDescriptor.PermissionsPresent.'#text') } catch { $null }; SecurityDescriptor = $GPOEntry.SecurityDescriptor } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLSystemServicesNT { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Service in $GPO.DataSet.NTService) { [PSCustomObject] @{ GPOSettingOrder = $Service.GPOSettingOrder #ServiceName = $Service.Name ServiceName = $Service.Properties.serviceName #: AppIDSvc: AppIDSvc ServiceStartupType = $Service.Properties.startupType #: NOCHANGE: NOCHANGE ServiceAction = $Service.Properties.serviceAction #: START: START Timeout = $Service.Properties.timeout #: 50: 50 FirstFailure = $Service.Properties.firstFailure #: REBOOT: REBOOT SecondFailure = $Service.Properties.secondFailure #: REBOOT: REBOOT ThirdFailure = $Service.Properties.thirdFailure #: REBOOT: REBOOT ResetFailCountDelay = $Service.Properties.resetFailCountDelay #: 0: 0 RestartComputerDelay = $Service.Properties.restartComputerDelay #: 60000: 60000 Filter = $Service.Filter AccountName = $Service.Properties.accountName AllowServiceToInteractWithTheDesktop = if ($Service.Properties.interact -eq 1) { 'Yes' } elseif ($Service.Properties.interact -eq 0) { 'No' } else { $null } RunThisProgram = $Service.Properties.program CommandLineParameters = $Service.Properties.args AppendFailCountToEndOfCommandLine = if ($Service.Properties.append -eq 1) { 'Yes' } elseif ($Service.Properties.append -eq 0) { 'No' } else { $null } } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Service in $GPO.DataSet.NTService) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings GPOSettingOrder = $Service.GPOSettingOrder #ServiceName = $Service.Name ServiceName = $Service.Properties.serviceName #: AppIDSvc: AppIDSvc ServiceStartupType = $Service.Properties.startupType #: NOCHANGE: NOCHANGE ServiceAction = $Service.Properties.serviceAction #: START: START Timeout = $Service.Properties.timeout #: 50: 50 FirstFailure = $Service.Properties.firstFailure #: REBOOT: REBOOT SecondFailure = $Service.Properties.secondFailure #: REBOOT: REBOOT ThirdFailure = $Service.Properties.thirdFailure #: REBOOT: REBOOT ResetFailCountDelay = $Service.Properties.resetFailCountDelay #: 0: 0 RestartComputerDelay = $Service.Properties.restartComputerDelay #: 60000: 60000 Filter = $Service.Filter AccountName = $Service.Properties.accountName AllowServiceToInteractWithTheDesktop = if ($Service.Properties.interact -eq 1) { 'Yes' } elseif ($Service.Properties.interact -eq 0) { 'No' } else { $null } RunThisProgram = $Service.Properties.program CommandLineParameters = $Service.Properties.args AppendFailCountToEndOfCommandLine = if ($Service.Properties.append -eq 1) { 'Yes' } elseif ($Service.Properties.append -eq 0) { 'No' } else { $null } <#$ startupType : NOCHANGE serviceName : AudioEndpointBuilder timeout : 30 accountName : LocalSystem interact : 1 thirdFailure : RUNCMD resetFailCountDelay : 0 program : fgdfg args : dg append : 1 Service name AudioEndpointBuilder Action No change Startup type: No change Wait timeout if service is locked: 30 seconds Service AccountLog on service as: LocalSystem Allow service to interact with the desktop: Yes First failure: No change Second failure: No change Subsequent failures: Run a program Reset fail count after: 0 days Run this program: fgdfg Command line parameters: dg Append fail count to end of command line: Yes #> } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } function ConvertTo-XMLTaskScheduler { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Entry in $GPO.DataSet.Drive) { [PSCustomObject] @{ Changed = [DateTime] $Entry.changed #uid = $Entry.uid GPOSettingOrder = $Entry.GPOSettingOrder Filter = $Entry.Filter Name = $Entry.Name Action = $Script:Actions["$($Entry.Properties.action)"] ThisDrive = $Entry.Properties.thisDrive AllDrives = $Entry.Properties.allDrives UserName = $Entry.Properties.userName Path = $Entry.Properties.path Label = $Entry.Properties.label Persistent = if ($Entry.Properties.persistent -eq '1') { $true } elseif ($Entry.Properties.persistent -eq '0') { $false } else { $Entry.Properties.persistent }; UseLetter = if ($Entry.Properties.useLetter -eq '1') { $true } elseif ($Entry.Properties.useLetter -eq '0') { $false } else { $Entry.Properties.useLetter }; Letter = $Entry.Properties.letter } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Type in @('TaskV2', 'Task', 'ImmediateTaskV2', 'ImmediateTask')) { foreach ($Entry in $GPO.DataSet.$Type) { $ListActions = foreach ($LoopAction in $Entry.Properties.Task.Actions) { foreach ($InternalAction in $LoopAction.Exec) { $Action = [ordered] @{ ActionType = 'Execute' Command = $InternalAction.Command # : cmd Arguments = $InternalAction.Arguments # : / c wevtutil qe security / rd:true / f:text / c:1 / q:"*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and (EventID=4727 or EventID=4759 or EventID=4754 or EventID=4731)]]" >group-creation.txt WorkingDirectory = $InternalAction.WorkingDirectory# : % windir % \temp Server = $Null Subject = $Null To = $Null From = $Null Body = $Null Attachments = $Null } $Action } foreach ($InternalAction in $LoopAction.SendEmail) { $Action = [ordered] @{ ActionType = 'SendEmail' Command = $null Arguments = $null # : / c wevtutil qe security / rd:true / f:text / c:1 / q:"*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and (EventID=4727 or EventID=4759 or EventID=4754 or EventID=4731)]]" >group-creation.txt WorkingDirectory = $null # : % windir % \temp Server = $InternalAction.Server # : smtp-de Subject = $InternalAction.Subject # : AD Group creation To = $InternalAction.To # : gm6b@eurofins.de,RalphThomasAussem@eurofins.de,karlthomaseggert@eurofins.de From = $InternalAction.From # : %computername%@eurofins.local Body = $InternalAction.Body # : A new security group has been created. Check attachment for further details. Attachments = $InternalAction.Attachments.File -join '; ' # : Attachments } $Action } } <# [DBG]: PS C:\Support\GitHub\GpoZaurr> $Entry.Properties.Task.Triggers.EventTrigger Enabled Subscription ------- ------------ true <QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4727]]</Select></Query></QueryList> true <QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4731]]</Select></Query></QueryList> true <QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4754]]</Select></Query></QueryList> false <QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4759]]</Select></Query></QueryList> #> if ($ListActions.Count -eq 0) { $ListActions = @( if ($Entry.Properties.appName) { # This supports Scheduled Task (legacy) $Action = [ordered] @{ ActionType = $Script:Actions["$($Entry.Properties.action)"] Command = $Entry.Properties.appName Arguments = $Entry.Properties.args # WorkingDirectory = $Entry.Properties.startIn # : % windir % \temp Server = $null # : smtp-de Subject = $null # : AD Group creation To = $null From = $null Body = $null Attachments = $null } $Action } else { $Action = [ordered] @{ ActionType = $Script:Actions["$($Entry.Properties.action)"] Command = $null Arguments = $null # WorkingDirectory = $null # : % windir % \temp Server = $null # : smtp-de Subject = $null # : AD Group creation To = $null From = $null Body = $null Attachments = $null } $Action } ) } foreach ($Action in $ListActions) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Type = $Type Changed = [DateTime] $Entry.changed GPOSettingOrder = $Entry.GPOSettingOrder userContext = '' Name = $Entry.Name Status = $Entry.status Action = $Script:Actions["$($Entry.Properties.action)"] runAs = $Entry.Properties.runAs #: NT AUTHORITY\System #logonType = $Entry.Properties.logonType #: InteractiveToken #Task = $Entry.Properties.Task #: Task Comment = $Entry.Properties.comment } if ($Entry.Properties.startOnlyIfIdle) { # Old legacy task $Middle = [ordered] @{ AllowStartOnDemand = $null #: true DisallowStartIfOnBatteries = $Entry.Properties.noStartIfOnBatteries #: false StopIfGoingOnBatteries = $Entry.Properties.stopIfGoingOnBatteries #: false AllowHardTerminate = $null #: true Enabled = $Entry.Properties.enabled #: true Hidden = $null #: false MultipleInstancesPolicy = $null #: IgnoreNew Priority = $null #: 7 ExecutionTimeLimit = $null #: PT1H #IdleSettings = $Entry.Properties.Task.Settings.IdleSettings #: IdleSettings IdleDuration = $Entry.Properties.deadlineMinutes # : PT5M IdleWaitTimeout = $null # : PT1H IdleStopOnIdleEnd = $Entry.Properties.stopOnIdleEnd # : false IdleRestartOnIdle = $Entry.Properties.startOnlyIfIdle # : false RegistrationInfoAuthor = $null RegistrationInfoDescription = $null deleteWhenDone = $Entry.Properties.deleteWhenDone <# action : U name : Task Name appName : Run command args : args for command startIn : start me in comment : Oops i did it again enabled : 1 deleteWhenDone : 1 maxRunTime : 259200000 startOnlyIfIdle : 1 idleMinutes : 10 deadlineMinutes : 60 stopOnIdleEnd : 1 noStartIfOnBatteries : 1 stopIfGoingOnBatteries : 1 systemRequired : 0 Triggers : Triggers #> } } else { $Middle = [ordered] @{ AllowStartOnDemand = $Entry.Properties.Task.Settings.AllowStartOnDemand #: true DisallowStartIfOnBatteries = $Entry.Properties.Task.Settings.DisallowStartIfOnBatteries #: false StopIfGoingOnBatteries = $Entry.Properties.Task.Settings.StopIfGoingOnBatteries #: false AllowHardTerminate = $Entry.Properties.Task.Settings.AllowHardTerminate #: true Enabled = $Entry.Properties.Task.Settings.Enabled #: true Hidden = $Entry.Properties.Task.Settings.Hidden #: false MultipleInstancesPolicy = $Entry.Properties.Task.Settings.MultipleInstancesPolicy #: IgnoreNew Priority = $Entry.Properties.Task.Settings.Priority #: 7 ExecutionTimeLimit = $Entry.Properties.Task.Settings.ExecutionTimeLimit #: PT1H #IdleSettings = $Entry.Properties.Task.Settings.IdleSettings #: IdleSettings IdleDuration = $Entry.Properties.Task.Settings.IdleSettings.Duration # : PT5M IdleWaitTimeout = $Entry.Properties.Task.Settings.IdleSettings.WaitTimeout # : PT1H IdleStopOnIdleEnd = $Entry.Properties.Task.Settings.IdleSettings.StopOnIdleEnd # : false IdleRestartOnIdle = $Entry.Properties.Task.Settings.IdleSettings.RestartOnIdle # : false RegistrationInfoAuthor = $Entry.Properties.Task.RegistrationInfo.Author RegistrationInfoDescription = $Entry.Properties.Task.RegistrationInfo.Description deleteWhenDone = $Entry.Properties.deleteWhenDone } } $End = [ordered] @{ id = $Entry.Properties.Principals.Principal.id # : Author UserId = $Entry.Properties.Principals.Principal.UserId # : NT AUTHORITY\System LogonType = $Entry.Properties.Principals.Principal.LogonType # : InteractiveToken RunLevel = $Entry.Properties.Principals.Principal.RunLevel # : HighestAvailable #Persistent = if ($Entry.Properties.persistent -eq '1') { $true } elseif ($Entry.Properties.persistent -eq '0') { $false } else { $Entry.Properties.persistent }; #UseLetter = if ($Entry.Properties.useLetter -eq '1') { $true } elseif ($Entry.Properties.useLetter -eq '0') { $false } else { $Entry.Properties.useLetter }; #Letter = $Entry.Properties.letter } $CreateGPO = $CreateGPO + $Middle + $End + $Action $Last = [ordered] @{ #Uid = $Group.uid #: {8F435B0A-CD15-464E-85F3-B6A55B9E816A}: {8F435B0A-CD15-464E-85F3-B6A55B9E816A} RunInLoggedOnUserSecurityContext = if ($Entry.userContext -eq '1') { 'Enabled' } elseif ($Entry.userContext -eq '0') { 'Disabled' } else { $Entry.userContext }; RemoveThisItemWhenItIsNoLongerApplied = if ($Entry.removePolicy -eq '1') { 'Enabled' } elseif ($Entry.removePolicy -eq '0') { 'Disabled' } else { $Entry.removePolicy }; Filters = $Group.Filters #:: } $CreateGPO = $CreateGPO + $Last $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } } } function ConvertTo-XMLUserRightsAssignment { [cmdletBinding()] param( [PSCustomObject] $GPO, [switch] $SingleObject ) $UserRightsTranslation = @{ SeNetworkLogonRight = 'Access this computer from the network' SeMachineAccountPrivilege = 'Add workstations to domain' SeIncreaseQuotaPrivilege = 'Adjust memory quotas for a process' SeInteractiveLogonRight = 'Allow log on locally' SeBackupPrivilege = 'Back up files and directories' SeChangeNotifyPrivilege = 'Bypass traverse checking Everyone' SeSystemTimePrivilege = 'Change the system time' SeCreatePagefilePrivilege = 'Create a pagefile' SeDebugPrivilege = 'Debug programs' SeEnableDelegationPrivilege = 'Enable computer and user accounts to be trusted for delegation' SeRemoteShutdownPrivilege = 'Force shutdown from a remote system' SeAuditPrivilege = 'Generate security audits' SeIncreaseBasePriorityPrivilege = 'Increase scheduling priority' SeLoadDriverPrivilege = 'Load and unload device drivers' SeBatchLogonRight = 'Log on as a batch job' SeSecurityPrivilege = 'Manage auditing and security log' SeSystemEnvironmentPrivilege = 'Modify firmware environment values' SeProfileSingleProcessPrivilege = 'Profile single process' SeSystemProfilePrivilege = 'Profile system performance' SeUndockPrivilege = 'Remove computer from docking station' SeAssignPrimaryTokenPrivilege = 'Replace a process level token' SeRestorePrivilege = 'Restore files and directories' SeShutdownPrivilege = 'Shut down the system' SeTakeOwnershipPrivilege = 'Take ownership of files or other objects' } if ($SingleObject) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Count = 0 Settings = $null } [Array] $CreateGPO['Settings'] = foreach ($Entry in $GPO.DataSet) { foreach ($Member in $Entry.Member) { [PSCustomObject]@{ 'UserRightsAssignment' = $Entry.Name 'UserRightsAssignmentDescription' = $UserRightsTranslation[$Entry.Name] 'Name' = $Member.Name.'#text' 'Sid' = $Member.SID.'#text' } } } $CreateGPO['Count'] = $CreateGPO['Settings'].Count $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } else { foreach ($Entry in $GPO.DataSet) { foreach ($Member in $Entry.Member) { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings } $CreateGPO['UserRightsAssignment'] = $Entry.Name $CreateGPO['UserRightsAssignmentDescription'] = $UserRightsTranslation[$Entry.Name] $CreateGPO['Name'] = $Member.Name.'#text' $CreateGPO['Sid'] = $Member.SID.'#text' #$CreateGPO['CreatedTime'] = $GPO.CreatedTime # : 06.06.2020 18:03:36 #$CreateGPO['ModifiedTime'] = $GPO.ModifiedTime # : 17.06.2020 16:08:10 #$CreateGPO['ReadTime'] = $GPO.ReadTime # : 13.08.2020 10:15:37 $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } } function Find-GPOPassword { [cmdletBinding()] param( [string] $Path ) #Convert XML in a String file [string]$XMLString = Get-Content -LiteralPath $Path #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 = '' } } $Password } function Format-CamelCaseToDisplayName { [cmdletBinding()] param( [string[]] $Text, [string] $AddChar ) foreach ($string in $Text) { $newString = '' $stringChars = $string.GetEnumerator() $charIndex = 0 foreach ($char in $stringChars) { # If upper and not first character, add a space if ([char]::IsUpper($char) -eq 'True' -and $charIndex -gt 0) { $newString = $newString + $AddChar + $char.ToString() } elseif ($charIndex -eq 0) { # If the first character, make it a capital always $newString = $newString + $char.ToString().ToUpper() } else { $newString = $newString + $char.ToString() } $charIndex++ } $newString } } #Format-CamelCaseToDisplayName -Text 'Test1', 'TestingMyAss', 'OtherTest', 'otherTEst' function Get-GitHubVersion { [cmdletBinding()] param( [Parameter(Mandatory)][string] $Cmdlet, [Parameter(Mandatory)][string] $RepositoryOwner, [Parameter(Mandatory)][string] $RepositoryName ) $App = Get-Command -Name $Cmdlet -ErrorAction SilentlyContinue if ($App) { [Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/$RepositoryOwner/$RepositoryName/releases" -Verbose:$false) $LatestVersion = $GitHubReleases[0] if (-not $LatestVersion.Errors) { if ($App.Version -eq $LatestVersion.Version) { "Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)" } elseif ($App.Version -lt $LatestVersion.Version) { "Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?" } elseif ($App.Version -gt $LatestVersion.Version) { "Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!" } } else { "Current: $($App.Version)" } } } function Get-GPOCategories { [cmdletBinding()] param( [PSCustomObject] $GPO, [System.Xml.XmlElement[]] $GPOOutput, [string] $Splitter, [switch] $FullObjects, [System.Collections.IDictionary] $CachedCategories ) if (-not $CachedCategories) { $CachedCategories = [ordered] @{} } $LinksInformation = Get-LinksFromXML -GPOOutput $GPOOutput -Splitter $Splitter -FullObjects:$FullObjects foreach ($GpoType in @('User', 'Computer')) { if ($GPOOutput.$GpoType.ExtensionData.Extension) { foreach ($ExtensionType in $GPOOutput.$GpoType.ExtensionData.Extension) { # It's possible that one of the ExtensionType records has value null. Weird but happend. if ($ExtensionType) { $GPOSettingTypeSplit = ($ExtensionType.type -split ':') try { $KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') } } catch { Write-Warning "Get-XMLStandard - things went sideways $($_.Exception.Message)" continue } foreach ($GpoSettings in $KeysToLoop.Name) { $Template = [ordered] @{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.Guid GpoType = $GpoType GpoCategory = $GPOSettingTypeSplit[1] GpoSettings = $GpoSettings } $Template['Linked'] = $LinksInformation.Linked $Template['LinksCount'] = $LinksInformation.LinksCount $Template['Links'] = $LinksInformation.Links $Template['IncludeComments'] = [bool]::Parse($GPOOutput.IncludeComments) $Template['CreatedTime'] = [DateTime] $GPOOutput.CreatedTime $Template['ModifiedTime'] = [DateTime] $GPOOutput.ModifiedTime $Template['ReadTime'] = [DateTime] $GPOOutput.ReadTime $Template['SecurityDescriptor'] = $GPOOutput.SecurityDescriptor $Template['FilterDataAvailable'] = [bool]::Parse($GPOOutput.FilterDataAvailable) $Template['DataSet'] = $ExtensionType.$GpoSettings $ConvertedObject = [PSCustomObject] $Template if (-not $CachedCategories["$($Template.GpoCategory)"]) { $CachedCategories["$($Template.GpoCategory)"] = [ordered] @{} } if (-not $CachedCategories["$($Template.GpoCategory)"]["$($Template.GpoSettings)"]) { $CachedCategories["$($Template.GpoCategory)"]["$($Template.GpoSettings)"] = [System.Collections.Generic.List[PSCustomObject]]::new() } $CachedCategories["$($Template.GpoCategory)"]["$($Template.GpoSettings)"].Add($ConvertedObject) # return GPOCategory $ConvertedObject } } } } } } function Get-LinksFromXML { [cmdletBinding()] param( [System.Xml.XmlElement[]] $GPOOutput, [string] $Splitter, [switch] $FullObjects ) $Links = [ordered] @{ Linked = $null LinksCount = $null Links = $null } if ($GPOOutput.LinksTo) { $Links.Linked = $true $Links.LinksCount = ([Array] $GPOOutput.LinksTo).Count $Links.Links = foreach ($Link in $GPOOutput.LinksTo) { if ($FullObjects) { [PSCustomObject] @{ Path = $Link.SOMPath Enabled = if ($Link.Enabled -eq 'true') { $true } else { $false } NoOverride = if ($Link.NoOverride -eq 'true') { $true } else { $false } } } else { if ($Link.Enabled) { $Link.SOMPath } } } if ($Splitter) { $Links.Links = $Links.Links -join $Splitter } } else { $Links.Linked = $false $Links.LinksCount = 0 $Links.Links = $null } [PSCustomObject] $Links } function Get-PermissionsAnalysis { [cmdletBinding()] param( [PSCustomObject] $GPOPermissions, [validateset('WellKnownAdministrative', 'Administrative', 'AuthenticatedUsers', 'Default')][string] $Type = 'Default', [Parameter(Mandatory)][alias('IncludePermissionType')][Microsoft.GroupPolicy.GPPermissionType] $PermissionType, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [System.Collections.IDictionary] $ADAdministrativeGroups ) if (-not $ADAdministrativeGroups) { $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } $AdministrativeExists = @{ Skip = $false DomainAdmins = $false EnterpriseAdmins = $false } #$GPOPermissions = $_ # Verification Phase # When it has GPOSecurityPermissionItem property it means it has permissions, if it doesn't it means we have clean object to process if ($GPOPermissions.GPOSecurityPermissionItem) { # Permission exists, but may be incomplete foreach ($GPOPermission in $GPOPermissions) { if ($Type -eq 'Default') { # We were looking for specific principal and we got it. nothing to do # this is for standard users such as przemyslaw.klys / adam.gonzales $AdministrativeExists['Skip'] = $true break } elseif ($Type -eq 'Administrative') { # We are looking for administrative but we need to make sure we got correct administrative if ($GPOPermission.Permission -eq $PermissionType) { $AdministrativeGroup = $ADAdministrativeGroups['BySID'][$GPOPermission.PrincipalSid] if ($AdministrativeGroup.SID -like '*-519') { $AdministrativeExists['EnterpriseAdmins'] = $true } elseif ($AdministrativeGroup.SID -like '*-512') { $AdministrativeExists['DomainAdmins'] = $true } } if ($AdministrativeExists['DomainAdmins'] -and $AdministrativeExists['EnterpriseAdmins']) { $AdministrativeExists['Skip'] = $true break } } elseif ($Type -eq 'WellKnownAdministrative') { # this is for SYSTEM account $AdministrativeExists['Skip'] = $true break } elseif ($Type -eq 'AuthenticatedUsers') { # this is for Authenticated Users $AdministrativeExists['Skip'] = $true break } } } $AdministrativeExists } function Get-PrivGPOZaurrLink { [cmdletBinding()] param( [Microsoft.ActiveDirectory.Management.ADObject] $Object, [switch] $Limited, [System.Collections.IDictionary] $GPOCache ) if ($Object.GpLink -and $Object.GpLink.Trim() -ne '') { #$Object.GpLink -split { $_ -eq '[' -or $_ -eq ']' } -replace ';0' -replace 'LDAP://' $Object.GpLink -split '\[LDAP://' -split ';' | ForEach-Object -Process { #Write-Verbose $_ if ($_.Length -gt 10) { $DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDomainCN $Output = [ordered] @{ DistinguishedName = $Object.DistinguishedName CanonicalName = if ($Object.CanonicalName) { $Object.CanonicalName.TrimEnd('/') } else { $Object.CanonicalName } Guid = [Regex]::Match( $_, '(?={)(.*)(?<=})').Value -replace '{' -replace '}' } $Search = -join ($DomainCN, $Output['Guid']) if ($GPOCache -and -not $Limited) { if ($GPOCache[$Search]) { $Output['DisplayName'] = $GPOCache[$Search].DisplayName $Output['DomainName'] = $GPOCache[$Search].DomainName $Output['Owner'] = $GPOCache[$Search].Owner $Output['GpoStatus'] = $GPOCache[$Search].GpoStatus $Output['Description'] = $GPOCache[$Search].Description $Output['CreationTime'] = $GPOCache[$Search].CreationTime $Output['ModificationTime'] = $GPOCache[$Search].ModificationTime $Output['GPODomainDistinguishedName'] = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDC $Output['GPODistinguishedName'] = $_ [PSCustomObject] $Output } else { Write-Warning "Get-PrivGPOZaurrLink - Couldn't find link $Search in a GPO Cache. Lack of permissions for given GPO? Are you running as admin? Skipping." } } else { $Output['GPODomainDistinguishedName'] = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDC $Output['GPODistinguishedName'] = $_ [PSCustomObject] $Output } } } } elseif ($Object.LinkedGroupPolicyObjects -and $Object.LinkedGroupPolicyObjects.Trim() -ne '') { $Object.LinkedGroupPolicyObjects -split '\[LDAP://' -split ';' | ForEach-Object -Process { if ($_.Length -gt 10) { $DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDomainCN $Output = [ordered] @{ DistinguishedName = $Object.DistinguishedName CanonicalName = if ($Object.CanonicalName) { $Object.CanonicalName.TrimEnd('/') } else { $Object.CanonicalName } Guid = [Regex]::Match( $_, '(?={)(.*)(?<=})').Value -replace '{' -replace '}' } $Search = -join ($DomainCN, $Output['Guid']) if ($GPOCache -and -not $Limited) { if ($GPOCache[$Search]) { $Output['Name'] = $GPOCache[$Search].DisplayName $Output['DomainName'] = $GPOCache[$Search].DomainName $Output['Owner'] = $GPOCache[$Search].Owner $Output['GpoStatus'] = $GPOCache[$Search].GpoStatus $Output['Description'] = $GPOCache[$Search].Description $Output['CreationTime'] = $GPOCache[$Search].CreationTime $Output['ModificationTime'] = $GPOCache[$Search].ModificationTime $Output['GPODomainDistinguishedName'] = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDC $Output['GPODistinguishedName'] = $_ [PSCustomObject] $Output } else { Write-Warning "Get-PrivGPOZaurrLink - Couldn't find link $Search in a GPO Cache. Lack of permissions for given GPO? Are you running as admin? Skipping." } } else { $Output['GPODomainDistinguishedName'] = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDC $Output['GPODistinguishedName'] = $_ [PSCustomObject] $Output } } } } } function Get-PrivPermission { [cmdletBinding()] param( [Microsoft.GroupPolicy.Gpo] $GPO, [Object] $SecurityRights, [string[]] $Principal, [validateset('DistinguishedName', 'Name', 'NetbiosName', 'Sid')][string] $PrincipalType = 'Sid', [switch] $SkipWellKnown, [switch] $SkipAdministrative, [switch] $IncludeOwner, [Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType, [Microsoft.GroupPolicy.GPPermissionType[]] $ExcludePermissionType, [validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'All', [string[]] $ExcludePrincipal, [validateset('DistinguishedName', 'Name', 'Sid')][string] $ExcludePrincipalType = 'Sid', [switch] $IncludeGPOObject, [System.Collections.IDictionary] $ADAdministrativeGroups, [validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All', #[System.Collections.IDictionary] $Accounts, [System.Collections.IDictionary] $ExtendedForestInformation ) Begin { Write-Verbose "Get-PrivPermission - Processing $($GPO.DisplayName) from $($GPO.DomainName)" } Process { $SecurityRights | ForEach-Object -Process { $GPOPermission = $_ if ($PermitType -ne 'All') { if ($PermitType -eq 'Deny') { if ($GPOPermission.Denied -eq $false) { return } } else { if ($GPOPermission.Denied -eq $true) { return } } } if ($ExcludePermissionType -contains $GPOPermission.Permission) { return } if ($IncludePermissionType) { if ($IncludePermissionType -notcontains $GPOPermission.Permission) { if ($IncludePermissionType -eq 'GpoRead' -and $GPOPermission.Permission -eq 'GpoApply') { # We treat GpoApply as GpoRead as well. This is because when GpoApply is set it becomes GpoRead as well but of course not vice versa } else { return } } } if ($SkipWellKnown.IsPresent -or $Type -contains 'NotWellKnown') { if ($GPOPermission.Trustee.SidType -eq 'WellKnownGroup') { return } } if ($SkipAdministrative.IsPresent -or $Type -contains 'NotAdministrative') { $IsAdministrative = $ADAdministrativeGroups['BySID'][$GPOPermission.Trustee.Sid.Value] if ($IsAdministrative) { return } } if ($Type -contains 'Administrative' -and $Type -notcontains 'All') { $IsAdministrative = $ADAdministrativeGroups['BySID'][$GPOPermission.Trustee.Sid.Value] if (-not $IsAdministrative) { return } } if ($Type -contains 'NotWellKnownAdministrative' -and $Type -notcontains 'All') { # We check for SYSTEM account # Maybe we should make it a function and provide more if ($GPOPermission.Trustee.Sid -eq 'S-1-5-18') { return } } if ($Type -contains 'WellKnownAdministrative' -and $Type -notcontains 'All') { # We check for SYSTEM account # Maybe we should make it a function and provide more if ($GPOPermission.Trustee.Sid -ne 'S-1-5-18') { return } } if ($Type -contains 'Unknown' -and $Type -notcontains 'All') { # May need updates if there's more types if ($GPOPermission.Trustee.SidType -ne 'Unknown') { return } } if ($Type -contains 'AuthenticatedUsers' -and $Type -notcontains 'All') { if ($GPOPermission.Trustee.Sid -ne 'S-1-5-11') { return } } if ($Type -contains 'DomainComputers' -and $Type -notcontains 'All') { $DomainComputersSID = -join ($ExtendedForestInformation['DomainsExtended'][$GPO.DomainName].DomainSID, '-515') if ($GPOPermission.Trustee.Sid -ne $DomainComputersSID) { return } } if ($GPOPermission.Trustee.Domain) { $UserMerge = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name) } else { $UserMerge = $null } if ($Principal) { if ($PrincipalType -eq 'Sid') { if ($Principal -notcontains $GPOPermission.Trustee.Sid.Value) { return } } elseif ($PrincipalType -eq 'DistinguishedName') { if ($Principal -notcontains $GPOPermission.Trustee.DSPath) { return } } elseif ($PrincipalType -eq 'Name') { if ($Principal -notcontains $GPOPermission.Trustee.Name) { return } } elseif ($PrincipalType -eq 'NetbiosName') { if ($Principal -notcontains $UserMerge) { return } } } if ($ExcludePrincipal) { if ($ExcludePrincipalType -eq 'Sid') { if ($ExcludePrincipal -contains $GPOPermission.Trustee.Sid.Value) { return } } elseif ($ExcludePrincipalType -eq 'DistinguishedName') { if ($ExcludePrincipal -contains $GPOPermission.Trustee.DSPath) { return } } elseif ($ExcludePrincipalType -eq 'Name') { if ($ExcludePrincipal -contains $GPOPermission.Trustee.Name) { return } } elseif ($ExcludePrincipalType -eq 'NetbiosName') { if ($ExcludePrincipal -contains $UserMerge) { return } } } <# # Sets permissions name, domain, distinguishedname to proper values if ($GPOPermission.Trustee.Name) { $DomainPlusName = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name) if ($GPOPermission.Trustee.DSPath) { $NetbiosConversion = ConvertFrom-NetbiosName -Identity $DomainPlusName if ($NetbiosConversion.DomainName) { $UserNameDomain = $NetbiosConversion.DomainName $UserName = $NetbiosConversion.Name } } else { $UserNameDomain = '' $Username = $DomainPlusName } } else { $DomainPlusName = '' $UserNameDomain = '' $Username = '' } #> # I don't trust the returned data, some stuff like 'alias' shows up for groups. To unify it with everything else... using my own function $PermissionAccount = Get-WinADObject -Identity $GPOPermission.Trustee.Sid.Value -AddType -Cache -Verbose:$false if ($PermissionAccount) { $UserNameDomain = $PermissionAccount.DomainName $UserName = $PermissionAccount.Name $SidType = $PermissionAccount.Type $ObjectClass = $PermissionAccount.ObjectClass } else { $ConvertFromSID = ConvertFrom-SID -SID $GPOPermission.Trustee.Sid.Value $UserNameDomain = '' $Username = $ConvertFromSID.Name $SidType = $ConvertFromSID.Type if ($SidType -eq 'Unknown') { $ObjectClass = 'unknown' } else { $ObjectClass = 'foreignSecurityPrincipal' } } $ReturnObject = [ordered] @{ 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 PermissionType = if ($GPOPermission.Denied -eq $true) { 'Deny' } else { 'Allow' } Permission = $GPOPermission.Permission # : GpoEditDeleteModifySecurity Inherited = $GPOPermission.Inherited # : False PrincipalNetBiosName = $UserMerge PrincipalDistinguishedName = $GPOPermission.Trustee.DSPath #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz PrincipalDomainName = $UserNameDomain #: EVOTEC PrincipalName = $UserName #: Domain Admins PrincipalSid = $GPOPermission.Trustee.Sid.Value #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512 PrincipalSidType = $SidType #$GPOPermission.Trustee.SidType #: Group PrincipalObjectClass = $ObjectClass } if ($IncludeGPOObject) { $ReturnObject['GPOObject'] = $GPO $ReturnObject['GPOSecurity'] = $SecurityRights $ReturnObject['GPOSecurityPermissionItem'] = $GPOPermission } [PSCustomObject] $ReturnObject } if ($IncludeOwner) { if ($GPO.Owner) { # I don't trust the returned data, some stuff like 'alias' shows up for groups. To unify it with everything else... using my own function $OwnerAccount = Get-WinADObject -Identity $GPO.Owner -AddType -Cache -Verbose:$false if ($OwnerAccount) { $UserNameDomain = $OwnerAccount.DomainName $UserName = $OwnerAccount.Name $SidType = $OwnerAccount.Type $OwnerObjectClass = $OwnerAccount.ObjectClass $SID = $OwnerAccount.ObjectSID } else { $ConvertFromSID = ConvertFrom-SID -SID $GPO.Owner $UserNameDomain = '' $Username = $ConvertFromSID.Name $SidType = $ConvertFromSID.Type if ($SidType -eq 'Unknown') { $OwnerObjectClass = 'unknown' } else { $OwnerObjectClass = 'foreignSecurityPrincipal' } $SID = $ConvertFromSID.SID } } else { $UserName = '' $UserNameDomain = '' $SID = '' $SIDType = 'Unknown' $DistinguishedName = '' $OwnerObjectClass = 'unknown' } # We have to process it for owners after querying user because $Owners are not as established as standard permissions so we don't know a lot if ($Type -contains 'Administrative' -and $Type -notcontains 'All') { if ($SID) { $IsAdministrative = $ADAdministrativeGroups['BySID'][$SID] if (-not $IsAdministrative) { return } } else { # if there is no SID, it's not administrative return } } if ($Type -contains 'NotWellKnownAdministrative' -and $Type -notcontains 'All') { # We check for SYSTEM account # Maybe we should make it a function and provide more if ($SID -eq 'S-1-5-18') { return } } if ($Type -contains 'WellKnownAdministrative' -and $Type -notcontains 'All') { # We check for SYSTEM account # Maybe we should make it a function and provide more if ($SID -ne 'S-1-5-18') { return } } if ($Type -contains 'Unknown' -and $Type -notcontains 'All') { # May need updates if there's more types if ($SidType -ne 'Unknown') { return } } if ($Type -contains 'AuthenticatedUsers' -and $Type -notcontains 'All') { if ($SID -ne 'S-1-5-11') { return } } if ($Type -contains 'DomainComputers' -and $Type -notcontains 'All') { $DomainComputersSID = -join ($ExtendedForestInformation['DomainsExtended'][$GPO.DomainName].DomainSID, '-515') if ($SID -ne $DomainComputersSID) { return } } if ($Principal) { if ($PrincipalType -eq 'Sid') { if ($Principal -notcontains $SID) { return } } elseif ($PrincipalType -eq 'DistinguishedName') { if ($Principal -notcontains $DistinguishedName) { return } } elseif ($PrincipalType -eq 'Name') { if ($Principal -notcontains $UserName) { return } } elseif ($PrincipalType -eq 'NetbiosName') { if ($Principal -notcontains $GPO.Owner) { return } } } if ($ExcludePrincipal) { if ($ExcludePrincipalType -eq 'Sid') { if ($ExcludePrincipal -contains $SID) { return } } elseif ($ExcludePrincipalType -eq 'DistinguishedName') { if ($ExcludePrincipal -contains $DistinguishedName) { return } } elseif ($ExcludePrincipalType -eq 'Name') { if ($ExcludePrincipal -contains $UserName) { return } } elseif ($ExcludePrincipalType -eq 'NetbiosName') { if ($ExcludePrincipal -contains $GPO.Owner) { return } } } $ReturnObject = [ordered] @{ 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 PermissionType = 'Allow' Permission = 'GpoOwner' # : GpoEditDeleteModifySecurity Inherited = $false # : False PrincipalNetBiosName = $GPO.Owner PrincipalDistinguishedName = $DistinguishedName #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz PrincipalDomainName = $UserNameDomain PrincipalName = $UserName PrincipalSid = $SID #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512 PrincipalSidType = $SIDType # #: Group PrincipalObjectClass = $OwnerObjectClass } if ($IncludeGPOObject) { $ReturnObject['GPOObject'] = $GPO $ReturnObject['GPOSecurity'] = $SecurityRights $ReturnObject['GPOSecurityPermissionItem'] = $null } [PSCustomObject] $ReturnObject } } End { } } function Get-XMLGPO { [cmdletBinding()] param( [XML] $XMLContent, [Microsoft.GroupPolicy.Gpo] $GPO, [switch] $PermissionsOnly, [switch] $OwnerOnly, [System.Collections.IDictionary] $ADAdministrativeGroups, [string] $Splitter = [System.Environment]::NewLine, [switch] $ReturnObject ) if ($XMLContent.GPO.LinksTo) { $LinkSplit = ([Array] $XMLContent.GPO.LinksTo).Where( { $_.Enabled -eq $true }, 'Split') [Array] $LinksEnabled = $LinkSplit[0] [Array] $LinksDisabled = $LinkSplit[1] $LinksEnabledCount = $LinksEnabled.Count $LinksDisabledCount = $LinksDisabled.Count $LinksTotalCount = ([Array] $XMLContent.GPO.LinksTo).Count if ($LinksEnabledCount -eq 0) { $Linked = $false } else { $Linked = $true } } else { $Linked = $false $LinksEnabledCount = 0 $LinksDisabledCount = 0 $LinksTotalCount = 0 } #if ($null -eq $XMLContent.GPO.Computer.ExtensionData -and $null -eq $XMLContent.GPO.User.ExtensionData) { # $Empty = $true #} else { # $Empty = $false #} # 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' } $ComputerSettingsAvailable = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true } $UserSettingsAvailable = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true } if ($ComputerSettingsAvailable -eq $false -and $UserSettingsAvailable -eq $false) { $Empty = $true } else { $Empty = $false } # $OutputUser = $XMLContent.GPO.User.ExtensionData.Extension | Where-Object { $_.PSObject.Properties.TypeNameOfValue -in 'System.Xml.XmlElement', 'System.Object[]' } # $OutputComputer = $XMLContent.GPO.Computer.ExtensionData.Extension | Where-Object { $_.PSObject.Properties.TypeNameOfValue -in 'System.Xml.XmlElement', 'System.Object[]' } $OutputUser = foreach ($ExtensionType in $XMLContent.GPO.User.ExtensionData.Extension) { if ($ExtensionType) { $GPOSettingTypeSplit = ($ExtensionType.type -split ':') try { $KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') } } catch { Write-Warning "Get-XMLStandard - things went sideways $($_.Exception.Message)" continue } } $KeysToLoop } $OutputComputer = foreach ($ExtensionType in $XMLContent.GPO.Computer.ExtensionData.Extension) { if ($ExtensionType) { $GPOSettingTypeSplit = ($ExtensionType.type -split ':') try { $KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') } } catch { Write-Warning "Get-XMLStandard - things went sideways $($_.Exception.Message)" continue } } $KeysToLoop } $ComputerSettingsAvailableReal = if ($OutputComputer) { $true } else { $false } $UserSettingsAvailableReal = if ($OutputUser) { $true } else { $false } if (-not $ComputerSettingsAvailableReal -and -not $UserSettingsAvailableReal) { $EmptyMaybe = $true } else { $EmptyMaybe = $false } $ComputerProblem = $false if ($ComputerEnabled -eq $true -and $ComputerSettingsAvailableReal -eq $true) { $ComputerOptimized = $true } elseif ($ComputerEnabled -eq $true -and $ComputerSettingsAvailableReal -eq $false) { $ComputerOptimized = $false } elseif ($ComputerEnabled -eq $false -and $ComputerSettingsAvailableReal -eq $false) { $ComputerOptimized = $true } else { # Enabled $false, but ComputerData is there. $ComputerOptimized = $false $ComputerProblem = $true } $UserProblem = $false if ($UserEnabled -eq $true -and $UserSettingsAvailableReal -eq $true) { $UserOptimized = $true } elseif ($UserEnabled -eq $true -and $UserSettingsAvailableReal -eq $false) { $UserOptimized = $false } elseif ($UserEnabled -eq $false -and $UserSettingsAvailableReal -eq $false) { $UserOptimized = $true } else { # Enabled $false, but UserData is there. $UserOptimized = $false $UserProblem = $true } if (-not $PermissionsOnly) { if ($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text') { $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text')"] $WellKnown = ConvertFrom-SID -SID $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text' -OnlyWellKnown if ($AdministrativeGroup) { $OwnerType = 'Administrative' } elseif ($WellKnown.Name) { $OwnerType = 'WellKnown' } else { $OwnerType = 'NotAdministrative' } } else { $OwnerType = 'Unknown' } } if ($PermissionsOnly) { [PsCustomObject] @{ 'DisplayName' = $XMLContent.GPO.Name 'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text' 'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}' 'Enabled' = $Enabled 'Name' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text' 'Sid' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text' #'SidType' = if (($XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text').Length -le 10) { 'WellKnown' } else { 'Other' } 'PermissionType' = 'Allow' 'Inherited' = $false 'Permissions' = 'Owner' 'GPODistinguishedName' = $GPO.Path } $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process { if ($_) { [PsCustomObject] @{ 'DisplayName' = $XMLContent.GPO.Name 'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text' 'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}' 'Enabled' = $Enabled 'Name' = $_.trustee.name.'#Text' 'Sid' = $_.trustee.SID.'#Text' #'SidType' = if (($XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text').Length -le 10) { 'WellKnown' } else { 'Other' } 'PermissionType' = $_.type.PermissionType 'Inherited' = if ($_.Inherited -eq 'false') { $false } else { $true } 'Permissions' = $_.Standard.GPOGroupedAccessEnum 'GPODistinguishedName' = $GPO.Path } } } } elseif ($OwnerOnly) { [PsCustomObject] @{ 'DisplayName' = $XMLContent.GPO.Name 'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text' 'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}' 'Enabled' = $Enabled 'Owner' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text' 'OwnerSID' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text' 'OwnerType' = $OwnerType 'GPODistinguishedName' = $GPO.Path } } else { [PsCustomObject] @{ 'DisplayName' = $XMLContent.GPO.Name 'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text' 'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}' 'Empty' = $Empty 'EmptyMaybe' = $EmptyMaybe 'Linked' = $Linked 'LinksCount' = $LinksTotalCount 'LinksEnabledCount' = $LinksEnabledCount 'LinksDisabledCount' = $LinksDisabledCount 'Enabled' = $Enabled 'ComputerEnabled' = $ComputerEnabled 'ComputerOptimized' = $ComputerOptimized 'ComputerProblem' = $ComputerProblem 'UserEnabled' = $UserEnabled 'UserOptimized' = $UserOptimized 'UserProblem' = $UserProblem 'ComputerSettingsAvailable' = $ComputerSettingsAvailable 'UserSettingsAvailable' = $UserSettingsAvailable 'ComputerSettingsAvailableReal' = $ComputerSettingsAvailableReal 'UserSettingsAvailableReal' = $UserSettingsAvailableReal 'ComputerSettingsTypes' = $OutputComputer.Name 'UserSettingsTypes' = $OutputUser.Name '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 'ComputerPolicies' = $XMLContent.GPO.Computer.ExtensionData.Name -join ", " 'UserPolicies' = $XMLContent.GPO.User.ExtensionData.Name -join ", " 'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime 'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime 'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime 'WMIFilter' = $GPO.WmiFilter.name 'WMIFilterDescription' = $GPO.WmiFilter.Description 'GPODistinguishedName' = $GPO.Path 'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' } 'Owner' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text' 'OwnerSID' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text' 'OwnerType' = $OwnerType 'ACL' = @( [PsCustomObject] @{ 'Name' = $XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text' 'Sid' = $XMLContent.GPO.SecurityDescriptor.Owner.SID.'#text' 'PermissionType' = 'Allow' 'Inherited' = $false 'Permissions' = 'Owner' } $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process { if ($_) { [PsCustomObject] @{ 'Name' = $_.trustee.name.'#Text' 'Sid' = $_.trustee.SID.'#Text' 'PermissionType' = $_.type.PermissionType 'Inherited' = if ($_.Inherited -eq 'false') { $false } else { $true } 'Permissions' = $_.Standard.GPOGroupedAccessEnum } } } ) 'Auditing' = if ($XMLContent.GPO.SecurityDescriptor.AuditingPresent.'#text' -eq 'true') { $true } else { $false } 'Links' = @( $XMLContent.GPO.LinksTo | ForEach-Object -Process { if ($_) { $_.SOMPath } } ) -join $Splitter 'LinksObjects' = $XMLContent.GPO.LinksTo | ForEach-Object -Process { if ($_) { [PSCustomObject] @{ CanonicalName = $_.SOMPath Enabled = $_.Enabled NoOverride = $_.NoOverride } } } 'GPOObject' = $GPO } } } function Get-XMLNestedRegistry { [cmdletBinding()] param( [PSCustomObject] $GPO, [System.Xml.XmlElement[]] $DataSet, [string] $Collection, [switch] $Limited ) if ($DataSet.Properties) { $Registry = $DataSet foreach ($Registry in $DataSet) { if ($Registry.Properties) { if ($Limited) { [PSCustomObject] @{ Collection = $Collection Description = $Registry.descr Changed = try { [DateTime] $Registry.changed } catch { $Registry.changed }; Disabled = if ($Registry.disabled -eq '1') { $true } else { $false }; GPOSettingOrder = [int] $Registry.GPOSettingOrder Action = $Script:Actions[$Registry.Properties.action] DisplayDecimal = if ($Registry.Properties.displayDecimal -eq '1') { $true } else { $false }; Default = if ($Registry.Properties.default -eq '1') { $true } else { $false }; Hive = $Registry.Properties.hive #: HKEY_LOCAL_MACHINE Key = $Registry.Properties.key #: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon Name = $Registry.Properties.name #: AutoAdminLogon Type = $Registry.Properties.type #: REG_SZ Value = $Registry.Properties.value # Filters = $Registry.Filters BypassErrors = if ($Registry.bypassErrors -eq '1') { $true } else { $false }; } } else { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Collection = $Collection Description = $Registry.descr Changed = try { [DateTime] $Registry.changed } catch { $Registry.changed }; Disabled = if ($Registry.disabled -eq '1') { $true } else { $false }; GPOSettingOrder = [int] $Registry.GPOSettingOrder Action = $Script:Actions[$Registry.Properties.action] DisplayDecimal = if ($Registry.Properties.displayDecimal -eq '1') { $true } else { $false }; Default = if ($Registry.Properties.default -eq '1') { $true } else { $false }; Hive = $Registry.Properties.hive #: HKEY_LOCAL_MACHINE Key = $Registry.Properties.key #: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon Name = $Registry.Properties.name #: AutoAdminLogon Type = $Registry.Properties.type #: REG_SZ Value = $Registry.Properties.value # Filters = $Registry.Filters BypassErrors = if ($Registry.bypassErrors -eq '1') { $true } else { $false }; } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } } } foreach ($Name in @('Registry', 'Collection')) { foreach ($Registry in $DataSet.$Name) { if ($Registry.Properties) { if ($Limited) { [PSCustomObject] @{ Collection = $Collection Description = $Registry.descr Changed = try { [DateTime] $Registry.changed } catch { $Registry.changed }; Disabled = if ($Registry.disabled -eq '1') { $true } else { $false }; GPOSettingOrder = [int] $Registry.GPOSettingOrder Action = $Script:Actions[$Registry.Properties.action] DisplayDecimal = if ($Registry.Properties.displayDecimal -eq '1') { $true } else { $false }; Default = if ($Registry.Properties.default -eq '1') { $true } else { $false }; Hive = $Registry.Properties.hive #: HKEY_LOCAL_MACHINE Key = $Registry.Properties.key #: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon Name = $Registry.Properties.name #: AutoAdminLogon Type = $Registry.Properties.type #: REG_SZ Value = $Registry.Properties.value # Filters = $Registry.Filters BypassErrors = if ($Registry.bypassErrors -eq '1') { $true } else { $false }; } } else { $CreateGPO = [ordered]@{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID GpoType = $GPO.GpoType #GpoCategory = $GPOEntry.GpoCategory #GpoSettings = $GPOEntry.GpoSettings Collection = $Collection Description = $Registry.descr Changed = try { [DateTime] $Registry.changed } catch { $Registry.changed }; Disabled = if ($Registry.disabled -eq '1') { $true } else { $false }; GPOSettingOrder = [int] $Registry.GPOSettingOrder Action = $Script:Actions[$Registry.Properties.action] DisplayDecimal = if ($Registry.Properties.displayDecimal -eq '1') { $true } else { $false }; ; Default = if ($Registry.Properties.default -eq '1') { $true } else { $false }; Hive = $Registry.Properties.hive #: HKEY_LOCAL_MACHINE Key = $Registry.Properties.key #: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon Name = $Registry.Properties.name #: AutoAdminLogon Type = $Registry.Properties.type #: REG_SZ Value = $Registry.Properties.value # Filters = $Registry.Filters BypassErrors = if ($Registry.bypassErrors -eq '1') { $true } else { $false }; } $CreateGPO['Linked'] = $GPO.Linked $CreateGPO['LinksCount'] = $GPO.LinksCount $CreateGPO['Links'] = $GPO.Links [PSCustomObject] $CreateGPO } } else { if ($Registry.Registry) { #if ($Registry.Name.Count -gt 1) { #Write-Verbose "Registry Name count more than 1" #} $TempCollection = $Collection if ($Collection) { $Collection = "$Collection/$($Registry.name)" } else { $Collection = $Registry.name } Get-XMLNestedRegistry -GPO $GPO -DataSet $Registry.Registry -Collection $Collection $Collection = $TempCollection } if ($Registry.Collection) { $TempCollection = $Collection #if ($Registry.Collection.Count -gt 1) { # Write-Verbose "Registry collection count more than 1" #} foreach ($MyCollection in $Registry.Collection) { if ($Collection) { #Write-Verbose "Collection1: $Collection - $($Registry.name) - $($MyCollection.name) - $($($MyCollection.name).Count)" $Collection = "$Collection/$($Registry.name)/$($MyCollection.name)" #Write-Verbose "Collection2: $Collection" } else { #Write-Verbose "Collection3: $Collection - $($Registry.name) - $($MyCollection.name)" $Collection = "$($Registry.name)/$($MyCollection.name)" #Write-Verbose "Collection4: $Collection" } Get-XMLNestedRegistry -GPO $GPO -DataSet $MyCollection -Collection $Collection $Collection = $TempCollection } } } } } } $GPOZaurrAnalysis = [ordered] @{ Name = 'Group Policy Content' Enabled = $true ActionRequired = $null Data = $null Execute = { Invoke-GPOZaurrContent -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { foreach ($Key in $Script:Reporting['GPOAnalysis']['Data'].Keys) { New-HTMLTab -Name $Key { New-HTMLTable -DataTable $Script:Reporting['GPOAnalysis']['Data'][$Key] -Filtering -Title $Key } } if ($Script:Reporting['GPOAnalysis']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOAnalysis']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrBlockedInheritance = [ordered] @{ Name = 'Group Policy Blocked Inhertiance' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrInheritance -IncludeBlockedObjects -OnlyBlockedInheritance -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['Data'] -Filtering if ($Script:Reporting['GPOBlockedInheritance']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrConsistency = [ordered] @{ Name = 'GPO Permissions Consistency' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrPermissionConsistency -Type All -VerifyInheritance -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { foreach ($GPO in $Script:Reporting['GPOConsistency']['Data']) { if ($GPO.ACLConsistent -eq $true) { $Script:Reporting['GPOConsistency']['Variables']['Consistent']++ } else { $Script:Reporting['GPOConsistency']['Variables']['Inconsistent']++ } if ($GPO.ACLConsistentInside -eq $true) { $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside']++ } else { $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']++ } } if ($Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -gt 0 -or $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -gt 0 ) { $Script:Reporting['GPOConsistency']['ActionRequired'] = $true } else { $Script:Reporting['GPOConsistency']['ActionRequired'] = $false } } Variables = @{ Consistent = 0 Inconsistent = 0 ConsistentInside = 0 InconsistentInside = 0 } Overview = { New-HTMLPanel { New-HTMLText -Text 'Following chart presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Top level permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['Consistent'] -FontWeight normal, bold New-HTMLListItem -Text 'Inherited permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'] -FontWeight normal, bold New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -FontWeight normal, bold New-HTMLListItem -Text "Inconsistent inherited permissions: ", $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them.' New-HTMLChart { New-ChartLegend -Names 'Bad', 'Good' -Color PaleGreen, Salmon New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Consistent', 'Inconsistent' New-ChartBar -Name 'TopLevel' -Value $Script:Reporting['GPOConsistency']['Variables']['Consistent'], $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] New-ChartBar -Name 'Inherited' -Value $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'], $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] } -Title 'Permissions Consistency' -TitleAlignment center } } Summary = { New-HTMLText -FontSize 10pt -TextBlock { "When GPO is created it creates an entry in Active Directory (metadata) and SYSVOL (content). " "Two different places meens two different sets of permissions. Group Policy module is making sure the data in both places is correct. " "However, for different reasons it's not nessecary the case and often permissions go out of sync between AD and SYSVOL. " "This test verifies consistency of policies between AD and SYSVOL in two ways. " "It checks top level permissions for a GPO, and then checks if all files within said GPO are inheriting permissions or have different permissions in place. " } New-HTMLText -Text 'Following list presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Top level permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['Consistent'] -FontWeight normal, bold New-HTMLListItem -Text 'Inherited permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'] -FontWeight normal, bold New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -FontWeight normal, bold New-HTMLListItem -Text "Inconsistent inherited permissions: ", $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them. Please notice that if ', ` ' Not available ', 'is visible in the table you should first fix related, more pressing issue, before fixing permissions inconsistency.' -FontWeight normal, bold, normal } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOConsistency']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Consistent', 'Inconsistent' -Color PaleGreen, Salmon New-ChartBar -Name 'TopLevel' -Value $Script:Reporting['GPOConsistency']['Variables']['Consistent'], $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] New-ChartBar -Name 'Inherited' -Value $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'], $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] } -Title 'Permissions Consistency' -TitleAlignment center } } New-HTMLSection -Name 'Group Policy Permissions Consistency' { New-HTMLTable -DataTable $Script:Reporting['GPOConsistency']['Data'] -Filtering { New-HTMLTableCondition -Name 'ACLConsistent' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string New-HTMLTableCondition -Name 'ACLConsistent' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string New-HTMLTableCondition -Name 'ACLConsistent' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string New-HTMLTableCondition -Name 'ACLConsistentInside' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string } -PagingOptions 10, 20, 30, 40, 50 } if ($Script:Reporting['GPOConsistency']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOConsistency']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } New-HTMLSection -Name 'Steps to fix - Permissions Consistency' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLText -Text 'Following steps will guide you how to fix permissions consistency' New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing permissions inconsistencies. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentBefore.html -Verbose -Type GPOConsistency } New-HTMLText -Text { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $GPOOutput = Get-GPOZaurrPermissionConsistency $GPOOutput | Format-Table # do your actions as desired } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Fix inconsistent permissions' { New-HTMLText -Text "Following command when executed fixes inconsistent permissions." New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black New-HTMLText -Text "Make sure to fill in TargetDomain to match your Domain Admin permission account" New-HTMLCodeBlock -Code { Repair-GPOZaurrPermissionConsistency -IncludeDomains "TargetDomain" -Verbose -WhatIf } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: " } New-HTMLCodeBlock -Code { Repair-GPOZaurrPermissionConsistency -LimitProcessing 2 -IncludeDomains "TargetDomain" } New-HTMLText -TextBlock { "This command when executed repairs only first X inconsistent permissions. Use LimitProcessing parameter to prevent mass fixing and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly." } New-HTMLText -Text "If there's nothing else to be fixed, we can skip to next step step" } New-HTMLWizardStep -Name 'Fix inconsistent downlevel permissions' { New-HTMLText -Text "Unfortunetly this step is manual until automation is developed. " New-HTMLText -Text "If there are inconsistent permissions found inside GPO one has to fix them manually by going into SYSVOL and making sure inheritance is enabled, and that permissions are consistent across all files." } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentAfter.html -Verbose -Type GPOConsistency } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } } } $GPOZaurrDuplicates = [ordered] @{ Name = 'Duplicate (CNF) Group Policies' Enabled = $true Action = $null Data = $null Execute = { Get-GPOZaurrDuplicateObject -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { $Script:Reporting['GPODuplicates']['Variables']['RequireDeletion'] = $Script:Reporting['GPODuplicates']['Data'].Count if ($Script:Reporting['GPODuplicates']['Data'].Count -gt 0) { $Script:Reporting['GPODuplicates']['ActionRequired'] = $true } else { $Script:Reporting['GPODuplicates']['ActionRequired'] = $false } } Variables = @{ RequireDeletion = 0 } Overview = { } Resources = @( 'https://social.technet.microsoft.com/wiki/contents/articles/15435.active-directory-duplicate-object-name-resolution.aspx' 'https://kickthatcomputer.wordpress.com/2014/11/22/seek-and-destroy-duplicate-ad-objects-with-cnf-in-the-name/' ) Summary = { New-HTMLText -FontSize 10pt -TextBlock { "CNF objects, Conflict objects or Duplicate Objects are created in Active Directory when there is simultaneous creation of an AD object under the same container " "on two separate Domain Controllers near about the same time or before the replication occurs. " "This results in a conflict and the same is exhibited by a CNF (Duplicate) object. " "While it doesn't nessecary has a huge impact on Active Directory it's important to keep Active Directory in proper, healthy state. " } New-HTMLText -Text 'As it stands currently there are ', $Script:Reporting['GPODuplicates']['Data'].Count, ' CNF (Duplicate) Group Policy objects to be deleted.' -FontSize 10pt -FontWeight normal, bold, normal } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPODuplicates']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartLegend -Names 'Bad' -Color Salmon New-ChartBar -Name 'Duplicate (CNF) object' -Value $Script:Reporting['GPODuplicates']['Data'].Count } -Title 'Duplicate (CNF) Objects' -TitleAlignment center } } New-HTMLSection -Name 'Group Policy CNF (Duplicate) Objects' { New-HTMLTable -DataTable $Script:Reporting['GPODuplicates']['Data'] -Filtering { } -PagingOptions 10, 20, 30, 40, 50 } if ($Script:Reporting['GPODuplicates']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPODuplicates']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } New-HTMLSection -Name 'Steps to fix - Remove duplicate (CNF) objects' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing duplicate GPO objects. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsBefore.html -Verbose -Type GPODuplicates } New-HTMLText -Text { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $GPOOutput = Get-GPOZaurrDuplicateObject $GPOOutput | Format-Table # do your actions as desired } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Remove CNF objects' { New-HTMLText -Text "Following command when executed, runs internally command that lists all duplicate objects." New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black New-HTMLCodeBlock -Code { Remove-GPOZaurrDuplicateObject -WhatIf -Verbose } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrDuplicateObject -Verbose -LimitProcessing 2 } New-HTMLText -TextBlock { "This command when executed removes only first X duplicate objects. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly." } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsAfter.html -Verbose -Type GPODuplicates } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } } } $GPOZaurrFiles = [ordered] @{ Name = 'SYSVOL (NetLogon) Files List' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrFiles -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { New-HTMLTable -DataTable $Script:Reporting['GPOFiles']['Data'] -Filtering if ($Script:Reporting['GPOFiles']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOFiles']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrList = [ordered] @{ Name = 'Group Policy Summary' Enabled = $true Action = $null Data = $null Execute = { Get-GPOZaurr -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { foreach ($GPO in $Script:Reporting['GPOList']['Data']) { if ($GPO.Linked -eq $false -and $GPO.Empty -eq $true) { # Not linked, Empty $Script:Reporting['GPOList']['Variables']['GPOEmptyAndUnlinked']++ $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++ $Script:Reporting['GPOList']['Variables']['GPONotLinked']++ $Script:Reporting['GPOList']['Variables']['GPOEmpty']++ } elseif ($GPO.Linked -eq $true -and $GPO.Empty -eq $true) { # Linked, But EMPTY $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty']++ $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++ $Script:Reporting['GPOList']['Variables']['GPOEmpty']++ $Script:Reporting['GPOList']['Variables']['GPOLinked']++ } elseif ($GPO.Linked -eq $false) { # Not linked, but not EMPTY $Script:Reporting['GPOList']['Variables']['GPONotLinked']++ $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++ $Script:Reporting['GPOList']['Variables']['GPONotEmpty']++ } elseif ($GPO.Empty -eq $true) { # Linked, But EMPTY $Script:Reporting['GPOList']['Variables']['GPOEmpty']++ $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++ $Script:Reporting['GPOList']['Variables']['GPOLinked']++ } else { # Linked, not EMPTY $Script:Reporting['GPOList']['Variables']['GPOValid']++ $Script:Reporting['GPOList']['Variables']['GPOLinked']++ $Script:Reporting['GPOList']['Variables']['GPONotEmpty']++ } if ($GPO.LinksDisabledCount -eq $GPO.LinksCount -and $GPO.LinksCount -gt 0) { $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled']++ } if ($GPO.ComputerOptimized -eq $true) { $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes']++ } else { $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo']++ } if ($GPO.ComputerProblem -eq $true) { $Script:Reporting['GPOList']['Variables']['ComputerProblemYes']++ } else { $Script:Reporting['GPOList']['Variables']['ComputerProblemNo']++ } if ($GPO.UserOptimized -eq $true) { $Script:Reporting['GPOList']['Variables']['UserOptimizedYes']++ } else { $Script:Reporting['GPOList']['Variables']['UserOptimizedNo']++ } if ($GPO.UserProblem -eq $true) { $Script:Reporting['GPOList']['Variables']['UserProblemYes']++ } else { $Script:Reporting['GPOList']['Variables']['UserProblemNo']++ } if ($GPO.UserProblem -or $GPO.ComputerProblem) { $Script:Reporting['GPOList']['Variables']['GPOWithProblems']++ } } $Script:Reporting['GPOList']['Variables']['GPOTotal'] = $Script:Reporting['GPOList']['Data'].Count if ($Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -gt 0) { $Script:Reporting['GPOList']['ActionRequired'] = $true } else { $Script:Reporting['GPOList']['ActionRequired'] = $false } } Variables = @{ GPOWithProblems = 0 ComputerOptimizedYes = 0 ComputerOptimizedNo = 0 ComputerProblemYes = 0 ComputerProblemNo = 0 UserOptimizedYes = 0 UserOptimizedNo = 0 UserProblemYes = 0 UserProblemNo = 0 GPONotLinked = 0 GPOLinked = 0 GPOEmpty = 0 GPONotEmpty = 0 GPOEmptyAndUnlinked = 0 GPOEmptyOrUnlinked = 0 GPOLinkedButEmpty = 0 GPOValid = 0 GPOLinkedButLinkDisabled = 0 GPOTotal = 0 } Overview = { New-HTMLPanel { New-HTMLText -Text 'Following chart presents ', 'Linked / Empty and Unlinked Group Policies' -FontSize 10pt -FontWeight normal, bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies total: ', $Script:Reporting['GPOList']['Variables']['GPOTotal'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies valid: ", $Script:Reporting['GPOList']['Variables']['GPOValid'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies to delete: ", $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $Script:Reporting['GPOList']['Variables']['GPONotLinked'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $Script:Reporting['GPOList']['Variables']['GPOEmpty'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are linked, but empty: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are linked, but link disabled: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled'] -FontWeight normal, bold } } } New-HTMLText -FontSize 10pt -Text 'Usually empty or unlinked Group Policies are safe to delete.' New-HTMLChart -Title 'Group Policies Summary' { New-ChartBarOptions -Type barStacked #New-ChartLegend -Names 'Unlinked', 'Linked', 'Empty', 'Total' -Color Salmon, PaleGreen, PaleVioletRed, PaleTurquoise New-ChartLegend -Names 'Good', 'Bad' -Color PaleGreen, Salmon #New-ChartBar -Name 'Group Policies' -Value $Script:Reporting['GPOList']['Variables']['GPONotLinked'], $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPOEmpty'], $Script:Reporting['GPOList']['Variables']['GPOTotal'] New-ChartBar -Name 'Linked' -Value $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPONotLinked'] New-ChartBar -Name 'Empty' -Value $Script:Reporting['GPOList']['Variables']['GPONotEmpty'], $Script:Reporting['GPOList']['Variables']['GPOEmpty'] New-ChartBar -Name 'Valid' -Value $Script:Reporting['GPOList']['Variables']['GPOValid'], $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] } -TitleAlignment center } } Summary = { New-HTMLText -TextBlock { "Over time Administrators add more and more group policies, as business requirements change. " "Due to neglection or thinking it may serve it's purpose later on a lot of Group Policies often have no value at all. " "Either the Group Policy is not linked to anything and just stays unlinked forever, or GPO is linked, but the link (links) are disabled. " "Additionally sometimes new GPO is created without any settings or the settings are removed over time, but GPO stays in place. " } -FontSize 10pt New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies total: ', $Script:Reporting['GPOList']['Variables']['GPOTotal'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies valid: ", $Script:Reporting['GPOList']['Variables']['GPOValid'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies to delete: ", $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $Script:Reporting['GPOList']['Variables']['GPONotLinked'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $Script:Reporting['GPOList']['Variables']['GPOEmpty'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are linked, but empty: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are linked, but link disabled: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled'] -FontWeight normal, bold } } } -FontSize 10pt New-HTMLText -Text "Additionally, we're reviewing Group Policies that have their section disabled, but contain data. Please review them and make sure this configuration is as expected!" -FontSize 10pt New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies with problems: ', $Script:Reporting['GPOList']['Variables']['GPOWithProblems'] -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies that have content (computer), but are disabled: ', $Script:Reporting['GPOList']['Variables']['ComputerProblemYes'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that have content (user), but are disabled: ", $Script:Reporting['GPOList']['Variables']['UserProblemYes'] -FontWeight normal, bold } } } -FontSize 10pt New-HTMLText -Text "For best performance it's recommended that if there are no settings of certain kind (Computer or User settings) it's best to disable them. " -FontSize 10pt New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies with optimization: ' -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies that are optimized (computer) ', $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are optimized (user): ", $Script:Reporting['GPOList']['Variables']['UserOptimizedYes'] -FontWeight normal, bold } } New-HTMLListItem -Text 'Group Policies without optimization: ' -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies that are not optimized (computer): ', $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies that are not optimized (user): ", $Script:Reporting['GPOList']['Variables']['UserOptimizedNo'] -FontWeight normal, bold } } } -FontSize 10pt New-HTMLText -TextBlock { 'All empty or unlinked Group Policies can be automatically deleted. Please review output in the table and follow steps below table to cleanup Group Policies. ' 'GPOs that have content, but are disabled require manual intervention. ' "If performance is an issue you should consider disabling user or computer sections of GPO when those are not used. " } -FontSize 10pt } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOList']['Summary'] } New-HTMLPanel { New-HTMLChart -Title 'Group Policies Empty & Unlinked' { New-ChartBarOptions -Type barStacked New-ChartLegend -Names 'Yes', 'No' -Color SpringGreen, Salmon New-ChartBar -Name 'Linked' -Value $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPONotLinked'] New-ChartBar -Name 'Empty' -Value $Script:Reporting['GPOList']['Variables']['GPONotEmpty'], $Script:Reporting['GPOList']['Variables']['GPOEmpty'] New-ChartBar -Name 'Valid' -Value $Script:Reporting['GPOList']['Variables']['GPOValid'], $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] New-ChartBar -Name 'With problem (computers)' -Value $Script:Reporting['GPOList']['Variables']['ComputerProblemNo'], $Script:Reporting['GPOList']['Variables']['ComputerProblemYes'] New-ChartBar -Name 'With problem (users)' -Value $Script:Reporting['GPOList']['Variables']['UserProblemNo'], $Script:Reporting['GPOList']['Variables']['UserProblemYes'] New-ChartBar -Name 'Optimized Computers' -Value $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes'], $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo'] New-ChartBar -Name 'Optimized Users' -Value $Script:Reporting['GPOList']['Variables']['UserOptimizedYes'], $Script:Reporting['GPOList']['Variables']['UserOptimizedNo'] } -TitleAlignment center } } New-HTMLSection -Name 'Group Policies List' { New-HTMLTable -DataTable $Script:Reporting['GPOList']['Data'] -Filtering { New-HTMLTableCondition -Name 'Empty' -Value $true -BackgroundColor Salmon -ComparisonType string New-HTMLTableCondition -Name 'Linked' -Value $false -BackgroundColor Salmon -ComparisonType string New-HTMLTableCondition -Name 'ComputerProblem' -Value $true -BackgroundColor Salmon -ComparisonType string New-HTMLTableCondition -Name 'UserProblem' -Value $true -BackgroundColor Salmon -ComparisonType string New-HTMLTableCondition -Name 'ComputerOptimized' -Value $false -BackgroundColor Salmon -ComparisonType string New-HTMLTableCondition -Name 'UserOptimized' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string # reverse New-HTMLTableCondition -Name 'Empty' -Value $false -BackgroundColor SpringGreen -ComparisonType string New-HTMLTableCondition -Name 'Linked' -Value $true -BackgroundColor SpringGreen -ComparisonType string New-HTMLTableCondition -Name 'ComputerProblem' -Value $false -BackgroundColor SpringGreen -ComparisonType string New-HTMLTableCondition -Name 'UserProblem' -Value $false -BackgroundColor SpringGreen -ComparisonType string New-HTMLTableCondition -Name 'ComputerOptimized' -Value $true -BackgroundColor SpringGreen -ComparisonType string New-HTMLTableCondition -Name 'UserOptimized' -Value $true -BackgroundColor SpringGreen -TextTransform capitalize -ComparisonType string } -PagingOptions 10, 20, 30, 40, 50 } if ($Script:Reporting['GPOList']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOList']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } New-HTMLSection -Name 'Steps to fix - Empty & Unlinked Group Policies' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLText -Text 'Following steps will guide you how to remove empty or unlinked group policies' New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrEmptyUnlinked.html -Verbose -Type GPOList } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $GPOOutput = Get-GPOZaurr $GPOOutput | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Remove GPOs that are EMPTY or UNLINKED' { New-HTMLText -Text @( "Following command when executed removes every ", "EMPTY" " or " "NOT LINKED" " Group Policy. Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal.", "Make sure to use BackupPath which will make sure that for each GPO that is about to be deleted a backup is made to folder on a desktop." ) -FontWeight normal, bold, normal, bold, normal, bold, normal, normal -Color Black, Red, Black, Red, Black New-HTMLCodeBlock -Code { Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose } New-HTMLText -TextBlock { "This command when executed deletes only first empty or unlinked GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly." "Please make sure to check if backup is made as well before going all in." } New-HTMLText -Text "If there's nothing else to be deleted on SYSVOL side, we can skip to next step step" } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrEmptyUnlinkedAfter.html -Verbose -Type GPOList } New-HTMLText -Text "If there are no more empty or unlinked GPOs in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } } } $GPOZaurrNetLogonOwners = [ordered] @{ Name = 'NetLogon Owners' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrNetLogon -OwnerOnly -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { foreach ($File in $Script:Reporting['NetLogonOwners']['Data']) { # if ($File.FileSystemRights -eq 'Owner') { # Process Owner part of the report $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwners']++ if ($File.OwnerType -eq 'WellKnownAdministrative') { $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrative']++ } elseif ($File.OwnerType -eq 'Administrative') { $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrative']++ } else { $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersNotAdministrative']++ } if ($File.OwnerSid -eq 'S-1-5-32-544') { $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrators']++ } elseif ($File.OwnerType -in 'WellKnownAdministrative', 'Administrative') { $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrativeNotAdministrators']++ $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersToFix']++ } else { $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersToFix']++ } #$Script:Reporting['NetLogonPermissions']['Variables']['Owner'].Add($File) #} else { # Process all other part of the report # $Script:Reporting['NetLogonPermissions']['Variables']['NonOwner'].Add($File) # if ($File.Status -eq 'Review permission required') { # $Script:Reporting['NetLogonPermissions']['Variables']['PermissionReviewRequired']++ # } elseif ($File.Status -eq 'Removal permission required') { # $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequired']++ # } elseif ($File.Status -eq 'Not assesed') { # $Script:Reporting['NetLogonPermissions']['Variables']['PermissionNotAssesed']++ # } else { # $Script:Reporting['NetLogonPermissions']['Variables']['PermissionOK']++ # } # } } if ($Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersToFix'] -gt 0) { $Script:Reporting['NetLogonOwners']['ActionRequired'] = $true } else { $Script:Reporting['NetLogonOwners']['ActionRequired'] = $false } # if (-not $Script:Reporting['NetLogonPermissions']['ActionRequired']) { # # if owners require fixing, we don't need to check those as we get this anyways # # if owners don't require fixing we check permissions anyways # if ($Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequired'] -gt 0 -or $Script:Reporting['NetLogonPermissions']['Variables']['PermissionReviewRequired'] -gt 0) { # $Script:Reporting['NetLogonPermissions']['ActionRequired'] = $true # } else { # $Script:Reporting['NetLogonPermissions']['ActionRequired'] = $false # } # } } Variables = @{ NetLogonOwners = 0 NetLogonOwnersAdministrators = 0 NetLogonOwnersNotAdministrative = 0 NetLogonOwnersAdministrative = 0 NetLogonOwnersAdministrativeNotAdministrators = 0 NetLogonOwnersToFix = 0 #Owner = [System.Collections.Generic.List[PSCustomObject]]::new() #NonOwner = [System.Collections.Generic.List[PSCustomObject]]::new() # PermissionReviewRequired = 0 # PermissionRemovalRequired = 0 # PermissionOK = 0 # PermissionNotAssesed = 0 } Overview = { # New-HTMLPanel { # New-HTMLText -Text 'Following chart presents ', 'NetLogon Summary' -FontSize 10pt -FontWeight normal, bold # New-HTMLList -Type Unordered { # New-HTMLListItem -Text 'NetLogon Files in Total: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners'] -FontWeight normal, bold # New-HTMLListItem -Text 'NetLogon BUILTIN\Administrators as Owner: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -FontWeight normal, bold # New-HTMLListItem -Text "NetLogon Owners requiring change: ", $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -FontWeight normal, bold { # New-HTMLList -Type Unordered { # New-HTMLListItem -Text 'Not Administrative: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative'] -FontWeight normal, bold # New-HTMLListItem -Text 'Administrative, but not BUILTIN\Administrators: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators'] -FontWeight normal, bold # } # } # } -FontSize 10pt # #New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.' # New-HTMLChart { # New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen # New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson # } -Title 'NetLogon Owners' -TitleAlignment center # } # New-HTMLPanel { # } } Summary = { New-HTMLText -TextBlock { "NetLogon is crucial part of Active Directory. Files stored there are available on each and every computer or server in the company. " "Keeping those files clean and secure is very important task. " "It's important that NetLogon file owners are set to BUILTIN\Administrators (SID: S-1-5-32-544). " "Owners have full control over the file object. Current owner of the file may be an Administrator but it doesn't guarentee that he/she will be in the future. " "That's why as a best-practice it's recommended to change any non-administrative owners to BUILTIN\Administrators, and even Administrative accounts should be replaced with it. " } -FontSize 10pt New-HTMLList -Type Unordered { New-HTMLListItem -Text 'NetLogon Files in Total: ', $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwners'] -FontWeight normal, bold New-HTMLListItem -Text 'NetLogon BUILTIN\Administrators as Owner: ', $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrators'] -FontWeight normal, bold New-HTMLListItem -Text "NetLogon Owners requiring change: ", $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersToFix'] -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Not Administrative: ', $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersNotAdministrative'] -FontWeight normal, bold New-HTMLListItem -Text 'Administrative, but not BUILTIN\Administrators: ', $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrativeNotAdministrators'] -FontWeight normal, bold } } } -FontSize 10pt New-HTMLText -Text "Follow the steps below table to get NetLogon Owners into compliant state." -FontSize 10pt } Solution = { #New-HTMLTab -Name 'NetLogon Owners' { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['NetLogonOwners']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonOwners']['Variables']['NetLogonOwnersToFix'] -Color Crimson } -Title 'NetLogon Owners' -TitleAlignment center } } New-HTMLSection -Name 'NetLogon File Owners' { New-HTMLTable -DataTable $Script:Reporting['NetLogonOwners']['Data'] -Filtering { New-HTMLTableCondition -Name 'OwnerSid' -Value "S-1-5-32-544" -BackgroundColor LightGreen -ComparisonType string New-HTMLTableCondition -Name 'OwnerSid' -Value "S-1-5-32-544" -BackgroundColor Salmon -ComparisonType string -Operator ne New-HTMLTableCondition -Name 'OwnerType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq New-HTMLTableCondition -Name 'Status' -Value "OK" -BackgroundColor LightGreen -ComparisonType string -Operator eq New-HTMLTableCondition -Name 'Status' -Value "OK" -BackgroundColor Salmon -ComparisonType string -Operator ne } } New-HTMLSection -Name 'Steps to fix NetLogon Owners ' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonBefore.html -Verbose -Type NetLogonOwners } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $NetLogonOutput = Get-GPOZaurrNetLogon -OwnerOnly -Verbose $NetLogonOutput | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Set non-compliant file owners to BUILTIN\Administrators' { New-HTMLText -Text "Following command when executed runs internally command that lists all file owners and if it doesn't match changes it BUILTIN\Administrators. It doesn't change compliant owners." New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black New-HTMLCodeBlock -Code { Repair-GPOZaurrNetLogonOwner -Verbose -WhatIf } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Repair-GPOZaurrNetLogonOwner -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. " } -LineBreak New-HTMLText -Text "Once happy with results please follow with command (this will start replacement of owners process): " -LineBreak -FontWeight bold New-HTMLText -TextBlock { "This command when executed sets new owner only on first X non-compliant NetLogon files. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly." } New-HTMLCodeBlock -Code { Repair-GPOZaurrNetLogonOwner -Verbose -LimitProcessing 2 } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Repair-GPOZaurrNetLogonOwner -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonAfter.html -Verbose -Type NetLogonOwners } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['NetLogonOwners']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['NetLogonOwners']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } #} #New-HTMLTab -Name 'NetLogon Permissions' { # New-HTMLSection -Invisible { # New-HTMLPanel { # #& $Script:GPOConfiguration['NetLogonPermissions']['Summary'] # } # New-HTMLPanel { # #New-HTMLChart { # # New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen # # New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson # #} -Title 'NetLogon Owners' -TitleAlignment center # } # } # # New-HTMLSection -Name 'NetLogon Files List' { # # New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['Owner'] -Filtering { # # New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor LightGreen -ComparisonType string # # New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor Salmon -ComparisonType string -Operator ne # # New-HTMLTableCondition -Name 'PrincipalType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq # # } # # } # New-HTMLSection -Name 'NetLogon Files List' { # New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['NonOwner'] -Filtering { # New-HTMLTableCondition -Name 'PrincipalType' -Value "Unknown" -BackgroundColor Salmon -ComparisonType string -Operator eq -Row # New-HTMLTableCondition -Name 'PrincipalType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq -Row # New-HTMLTableCondition -Name 'Status' -Value "Review permission required" -BackgroundColor PaleGoldenrod -ComparisonType string -Operator eq -Row # New-HTMLTableCondition -Name 'Status' -Value "Removal permission required" -BackgroundColor Salmon -ComparisonType string -Operator eq -Row # New-HTMLTableCondition -Name 'Status' -Value "OK" -BackgroundColor LightGreen -ComparisonType string -Operator eq # } # } # if ($Script:Reporting['NetLogonPermissions']['WarningsAndErrors']) { # New-HTMLSection -Name 'Warnings & Errors to Review' { # New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['WarningsAndErrors'] -Filtering { # New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row # New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row # } # } # } # } } } $GPOZaurrNetLogonPermissions = [ordered] @{ Name = 'NetLogon Permissions' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrNetLogon -SkipOwner -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReviewPerDomain'] = @{} $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReviewPerDomain'] = @{} $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReviewPerDomain'] = @{} $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredPerDomain'] = @{} foreach ($File in $Script:Reporting['NetLogonPermissions']['Data']) { if (-not $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReviewPerDomain'][$File.DomainName]) { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReviewPerDomain'][$File.DomainName] = 0 } if (-not $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReviewPerDomain'][$File.DomainName]) { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReviewPerDomain'][$File.DomainName] = 0 } if (-not $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReviewPerDomain'][$File.DomainName]) { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReviewPerDomain'][$File.DomainName] = 0 } if (-not $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredPerDomain'][$File.DomainName]) { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredPerDomain'][$File.DomainName] = 0 } if ($File.Status -eq 'Review permission required') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionReviewRequired']++ if ($File.FileSystemRights -like '*Modify*') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReview']++ $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReviewPerDomain'][$File.DomainName]++ } elseif ($File.FileSystemRights -like '*Write*') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReview']++ $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReviewPerDomain'][$File.DomainName]++ } elseif ($File.FileSystemRights -like '*FullControl*') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReview']++ $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReviewPerDomain'][$File.DomainName]++ } else { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionOtherReview']++ } } elseif ($File.Status -eq 'Removal permission required') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequired']++ $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredPerDomain'][$File.DomainName]++ if ($File.PrincipalObjectClass -in 'user', 'computer') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredBecauseObject']++ } else { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredBecauseUnknown']++ } } elseif ($File.Status -eq 'Not assesed') { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionNotAssesed']++ } else { $Script:Reporting['NetLogonPermissions']['Variables']['PermissionOK']++ } } if ($Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequired'] -gt 0 -or $Script:Reporting['NetLogonPermissions']['Variables']['PermissionReviewRequired'] -gt 0) { $Script:Reporting['NetLogonPermissions']['ActionRequired'] = $true } else { $Script:Reporting['NetLogonPermissions']['ActionRequired'] = $false } } Variables = @{ PermissionReviewRequired = 0 PermissionRemovalRequired = 0 PermissionOK = 0 PermissionNotAssesed = 0 PermissionWriteReview = 0 PermissionFullControlReview = 0 PermissionModifyReview = 0 PermissionOtherReview = 0 PermissionRemovalRequiredBecauseObject = 0 PermissionRemovalRequiredBecauseUnknown = 0 PermissionWriteReviewPerDomain = $null PermissionFullControlReviewPerDomain = $null PermissionModifyReviewPerDomain = $null PermissionRemovalRequiredPerDomain = $null } Overview = { } Summary = { New-HTMLText -TextBlock { "NetLogon is crucial part of Active Directory. Files stored there are available on each and every computer or server in the company. " "Keeping those files clean and secure is very important task. " "Each file stored on NETLOGON has it's own permissions. " "It's important that crucial permissions such as FullControl, Modify or Write permissions are only applied to proper, trusted groups of users. " "Additionally permissions for FullControl, Modify or Write should not be granted to direct users or computers. Only groups are allowed! " "" } -FontSize 10pt New-HTMLText -Text 'Assesment overall: ' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Permissions that look ok: ' { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Assesed and as expected ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionOK'] -FontWeight normal, bold New-HTMLListItem -Text 'Not assesed, but not critical (read/execute only) ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionNotAssesed'] -FontWeight normal, bold } } New-HTMLListItem -Text 'Permissions requiring review:' { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Full control permissions ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReview'] -FontWeight normal, bold New-HTMLListItem -Text 'Modify permissions ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReview'] -FontWeight normal, bold New-HTMLListItem -Text 'Write permissions ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReview'] -FontWeight normal, bold } } New-HTMLListItem -Text 'Permissions requiring removal: ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequired'] -FontWeight normal, bold { New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Because of object type (user/computer) ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredBecauseObject'] -FontWeight normal, bold New-HTMLListItem -Text 'Because of unknown permissions ', $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredBecauseUnknown'] -FontWeight normal, bold } } } -FontSize 10pt -LineBreak New-HTMLText -Text 'Assesment split per domain (will require permissions to fix): ' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReviewPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires review of ", $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReviewPerDomain'][$Domain], " full control" -FontWeight normal, bold, normal New-HTMLListItem -Text "$Domain requires review of ", $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReviewPerDomain'][$Domain], " modify permission" -FontWeight normal, bold, normal New-HTMLListItem -Text "$Domain requires review of ", $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReviewPerDomain'][$Domain], " write permission" -FontWeight normal, bold, normal New-HTMLListItem -Text "$Domain requires removal of ", $Script:Reporting['NetLogonPermissions']['Variables']['PermissionRemovalRequiredPerDomain'][$Domain], " permissions" -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -Text "Please review output in table and follow the steps below table to get NetLogon permissions in order." -FontSize 10pt } Solution = { # New-HTMLTab -Name 'NetLogon Owners' { # New-HTMLSection -Invisible { # New-HTMLPanel { # & $Script:GPOConfiguration['NetLogonPermissions']['Summary'] # } # New-HTMLPanel { # New-HTMLChart { # New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen # New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson # } -Title 'NetLogon Owners' -TitleAlignment center # } # } # New-HTMLSection -Name 'NetLogon File Owners' { # New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['Owner'] -Filtering { # New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor LightGreen -ComparisonType string # New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor Salmon -ComparisonType string -Operator ne # New-HTMLTableCondition -Name 'PrincipalType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq # New-HTMLTableCondition -Name 'Status' -Value "OK" -BackgroundColor LightGreen -ComparisonType string -Operator eq # New-HTMLTableCondition -Name 'Status' -Value "OK" -BackgroundColor Salmon -ComparisonType string -Operator ne # } # } # New-HTMLSection -Name 'Steps to fix NetLogon Owners ' { # New-HTMLContainer { # New-HTMLSpanStyle -FontSize 10pt { # New-HTMLText -Text 'Following steps will guide you how to fix NetLogon Owners and make them compliant.' # New-HTMLWizard { # New-HTMLWizardStep -Name 'Prepare environment' { # New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." # New-HTMLCodeBlock -Code { # Install-Module GPOZaurr -Force # Import-Module GPOZaurr -Force # } -Style powershell # New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." # } # New-HTMLWizardStep -Name 'Prepare report' { # New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:" # New-HTMLCodeBlock -Code { # Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonBefore.html -Verbose -Type NetLogon # } # New-HTMLText -TextBlock { # "When executed it will take a while to generate all data and provide you with new report depending on size of environment." # "Once confirmed that data is still showing issues and requires fixing please proceed with next step." # } # New-HTMLText -Text "Alternatively if you prefer working with console you can run: " # New-HTMLCodeBlock -Code { # $NetLogonOutput = Get-GPOZaurrNetLogon -OwnerOnly -Verbose # $NetLogonOutput | Format-Table # } # New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." # } # New-HTMLWizardStep -Name 'Set non-compliant file owners to BUILTIN\Administrators' { # New-HTMLText -Text "Following command when executed runs internally command that lists all file owners and if it doesn't match changes it BUILTIN\Administrators. It doesn't change compliant owners." # New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black # New-HTMLCodeBlock -Code { # Repair-GPOZaurrNetLogonOwner -Verbose -WhatIf # } # New-HTMLText -TextBlock { # "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: " # } # New-HTMLCodeBlock -Code { # Repair-GPOZaurrNetLogonOwner -Verbose -LimitProcessing 2 # } # New-HTMLText -TextBlock { # "This command when executed sets new owner only on first X non-compliant NetLogon files. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur." # "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly." # } # } # New-HTMLWizardStep -Name 'Verification report' { # New-HTMLText -TextBlock { # "Once cleanup task was executed properly, we need to verify that report now shows no problems." # } # New-HTMLCodeBlock -Code { # Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonAfter.html -Verbose -Type NetLogon # } # New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond # } # } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center # } # } # } # if ($Script:Reporting['NetLogonPermissions']['WarningsAndErrors']) { # New-HTMLSection -Name 'Warnings & Errors to Review' { # New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['WarningsAndErrors'] -Filtering { # New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row # New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row # } # } # } # } #New-HTMLTab -Name 'NetLogon Permissions' { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['NetLogonPermissions']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartPie -Name 'Full Control requiring review' -Value $Script:Reporting['NetLogonPermissions']['Variables']['PermissionFullControlReview'] -Color Crimson New-ChartPie -Name 'Modify requiring review' -Value $Script:Reporting['NetLogonPermissions']['Variables']['PermissionModifyReview'] -Color Plum New-ChartPie -Name 'Write requiring review' -Value $Script:Reporting['NetLogonPermissions']['Variables']['PermissionWriteReview'] -Color LightCoral New-ChartPie -Name 'Permissions OK' -Value $Script:Reporting['NetLogonPermissions']['Variables']['PermissionOK'] -Color LightGreen New-ChartPie -Name 'Permissions ReadOnly/Execute' -Value $Script:Reporting['NetLogonPermissions']['Variables']['PermissionNotAssesed'] -Color Aqua } -Title 'NetLogon Permissions' -TitleAlignment center } } New-HTMLSection -Name 'NetLogon Files List' { New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Data'] -Filtering { New-HTMLTableCondition -Name 'PrincipalType' -Value "Unknown" -BackgroundColor Salmon -ComparisonType string -Operator eq -Row New-HTMLTableCondition -Name 'PrincipalType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq -Row New-HTMLTableCondition -Name 'Status' -Value "Review permission required" -BackgroundColor PaleGoldenrod -ComparisonType string -Operator eq New-HTMLTableCondition -Name 'Status' -Value "Removal permission required" -BackgroundColor Salmon -ComparisonType string -Operator eq -Row New-HTMLTableCondition -Name 'Status' -Value "OK" -BackgroundColor LightGreen -ComparisonType string -Operator eq } } New-HTMLSection -Name 'Steps to fix NetLogon Permissions ' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonBefore.html -Verbose -Type NetLogonPermissions } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $NetLogonOutput = Get-GPOZaurrNetLogon -SkipOwner -Verbose $NetLogonOutput | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Remove permissions manually for non-compliant users/groups' { New-HTMLText -Text @( "In case of NETLOGON permissions it's impossible to tell what in a given moment for given domain should be automatically removed except for the very obvious ", "unknown ", 'permissions. Domain Admins have to make their assesment on and remove permissions from users or groups that ' ) -FontWeight normal, bold, normal } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonAfter.html -Verbose -Type NetLogonPermissions } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['NetLogonPermissions']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrOrphans = [ordered] @{ Name = 'Orphaned Group Policies' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrBroken -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'] = @{} $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'] = @{} foreach ($GPO in $Script:Reporting['GPOOrphans']['Data']) { if (-not $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName] = 0 } if (-not $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName] = 0 } if ($GPO.Status -eq 'Not available in AD') { $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD']++ $Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++ $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++ } elseif ($GPO.Status -eq 'Not available on SYSVOL') { $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol']++ $Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++ $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++ } elseif ($GPO.Status -eq 'Permissions issue') { $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']++ $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]++ } } if ($Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted'] -gt 0) { $Script:Reporting['GPOOrphans']['ActionRequired'] = $true } else { $Script:Reporting['GPOOrphans']['ActionRequired'] = $false } } Variables = @{ NotAvailableInAD = 0 NotAvailableOnSysvol = 0 NotAvailablePermissionIssue = 0 NotAvailablePermissionIssuePerDomain = $null ToBeDeleted = 0 ToBeDeletedPerDomain = $null } Overview = { <# New-HTMLPanel { New-HTMLText -TextBlock { "Group Policies are stored in two places - Active Directory (metadata) and SYSVOL (content)." "Since those are managed in different ways, replicated in different ways it's possible because of different issues they get out of sync." } -LineBreak New-HTMLText -Text "For example:" New-HTMLList -Type Unordered { New-HTMLListItem -Text 'USN Rollback in AD could cause group policies to reappar in Active Directory, yet SYSVOL data would be unavailable' New-HTMLListItem -Text 'Group Policy deletion failing to delete GPO content' New-HTMLListItem -Text 'DFSR replication failing between DCs' } New-HTMLText -Text 'Following chart presents ', 'Broken / Orphaned Group Policies' -FontSize 10pt -FontWeight normal, bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'] -FontWeight normal, bold New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.' New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] } -Title 'Broken / Orphaned Group Policies' -TitleAlignment center } #> } Summary = { New-HTMLText -TextBlock { "Group Policies are stored in two places - Active Directory (metadata) and SYSVOL (content)." "Since those are managed in different ways, replicated in different ways it's possible because of different issues they get out of sync." } -FontSize 10pt -LineBreak New-HTMLText -Text "For example:" -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'USN Rollback in AD could cause already deleted Group Policies to reapper in Active Directory, yet SYSVOL data would be unavailable' New-HTMLListItem -Text 'Group Policy deletion failing to delete GPO content' New-HTMLListItem -Text 'Permission issue preventing deletion of GPO content' New-HTMLListItem -Text 'Failing DFSR replication between DCs' } -FontSize 10pt New-HTMLText -Text 'Following problems were detected:' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'] -FontWeight normal, bold New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -Text "Please review output in table and follow the steps below table to get Active Directory Group Policies in healthy state." -FontSize 10pt } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOOrphans']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] } -Title 'Broken / Orphaned Group Policies' -TitleAlignment center } } New-HTMLSection -Name 'Health State of Group Policies' { New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['Data'] -Filtering { New-HTMLTableCondition -Name 'Status' -Value "Not available in AD" -BackgroundColor Salmon -ComparisonType string New-HTMLTableCondition -Name 'Status' -Value "Not available on SYSVOL" -BackgroundColor LightCoral -ComparisonType string New-HTMLTableCondition -Name 'Status' -Value "Permissions issue" -BackgroundColor MediumVioletRed -ComparisonType string -Color White } -PagingOptions 10, 20, 30, 40, 50 } New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoBefore.html -Verbose -Type GPOOrphans } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $GPOOutput = Get-GPOZaurrBroken -Verbose $GPOOutput | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Make a backup (optional)' { New-HTMLText -TextBlock { "The process fixing broken GPOs will delete AD or SYSVOL content depending on type of a problem. " "While it's always useful to have a backup, this backup won't actually backup those broken group policies" " for a simple reason that those are not backupable. You can't back up GPO if there is no SYSVOL content" " and you can't backup GPO if there's only SYSVOL content. " "However, since the script does make changes to GPOs it's advised to have a backup anyways! " } New-HTMLCodeBlock -Code { $GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All $GPOSummary | Format-Table # only if you want to display output of backup } New-HTMLText -TextBlock { "Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. " } } New-HTMLWizardStep -Name 'Fix GPOs not available in AD' { New-HTMLText -Text @( "Following command when executed runs cleanup procedure that removes all broken GPOs on SYSVOL side. ", "Make sure when running it for the first time to run it with ", "WhatIf ", "parameter as shown below to prevent accidental removal. ", 'When run it will remove any GPO remains from SYSVOL, that should not be there, as AD metadata is already gone.' "Please notice I'm using SYSVOL as a type, because the removal will happen on SYSVOL. " ) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type SYSVOL -WhatIf -Verbose } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type SYSVOL -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. " "Keep in mind that what backup command does is simply copy SYSVOL content to given place. " "Since there is no GPO metadata in AD there's no real restore process for this step. " "It's there to make sure if someone kept some data in there and wants to get access to it, he/she can. " } -LineBreak New-HTMLText -Text "Once happy with results please follow with command (this will start deletion process): " -LineBreak -FontWeight bold New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type SYSVOL -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup -Verbose } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type SYSVOL -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose } New-HTMLText -TextBlock { "This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur. " "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly. " "If there's nothing else to be deleted on SYSVOL side, we can skip to next step step. " } } New-HTMLWizardStep -Name 'Fix GPOs not available on SYSVOL' { New-HTMLText -Text @( "Following command when executed runs cleanup procedure that removes all broken GPOs on Active Directory side." "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." 'When run it will remove any GPO remains from AD, that should not be there, as SYSVOL content is already gone.' "Please notice I'm using AD as a type, because the removal will happen on AD side. " ) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type AD -WhatIf -Verbose } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type AD -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. " "Keep in mind that there is no backup for this. " "Since there is no SYSVOL data, and only AD object is there there's no real restore process for this step. " "Once you delete it, it's gone. " } -LineBreak New-HTMLText -Text 'Once happy with results please follow with command (this will start deletion process): ' -LineBreak -FontWeight bold New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type AD -LimitProcessing 2 -Verbose } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrBroken -Type AD -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose } New-HTMLText -TextBlock { "This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur. " "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly. " "If there's nothing else to be deleted on AD side, we can skip to next step step. " } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoAfter.html -Verbose -Type GPOOrphans } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['GPOOrphans']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrOwners = [ordered] @{ Name = 'Group Policy Owners' Enabled = $true ActionRequired = $null Data = $null Execute = { Get-GPOZaurrOwner -IncludeSysvol -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { # Create Per Domain Variables $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'] = @{} $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'] = @{} foreach ($GPO in $Script:Reporting['GPOOwners']['Data']) { # Create Per Domain Variables if (-not $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$GPO.DomainName] = 0 } if (-not $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$GPO.DomainName] = 0 } # Checks if ($GPO.IsOwnerConsistent) { $Script:Reporting['GPOOwners']['Variables']['IsConsistent']++ } else { $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']++ } if ($GPO.IsOwnerAdministrative) { $Script:Reporting['GPOOwners']['Variables']['IsAdministrative']++ } else { $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']++ } if (($GPO.IsOwnerAdministrative -eq $false -or $GPO.IsOwnerConsistent -eq $false) -and $GPO.SysvolExists -eq $true) { $Script:Reporting['GPOOwners']['Variables']['WillFix']++ $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$GPO.DomainName]++ } elseif ($GPO.SysvolExists -eq $false) { $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFix']++ $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$GPO.DomainName]++ } else { $Script:Reporting['GPOOwners']['Variables']['WillNotTouch']++ } } if ($Script:Reporting['GPOOwners']['Variables']['WillFix'] -gt 0) { $Script:Reporting['GPOOwners']['ActionRequired'] = $true } else { $Script:Reporting['GPOOwners']['ActionRequired'] = $false } } Variables = @{ IsAdministrative = 0 IsNotAdministrative = 0 IsConsistent = 0 IsNotConsistent = 0 WillFix = 0 RequiresDiffFix = 0 WillNotTouch = 0 RequiresDiffFixPerDomain = $null WillFixPerDomain = $null } Overview = { <# New-HTMLPanel { New-HTMLText -Text 'Following chart presents Group Policy owners and whether they are administrative and consistent. By design an owner of Group Policy should be Domain Admins or Enterprise Admins group only to prevent malicious takeover. ', ` "It's also important that owner in Active Directory matches owner on SYSVOL (file system)." -FontSize 10pt New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'] -FontWeight normal, bold New-HTMLListItem -Text 'Non-Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] -FontWeight normal, bold New-HTMLListItem -Text "Owners consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsConsistent'] -FontWeight normal, bold New-HTMLListItem -Text "Owners not-consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] -FontWeight normal, bold } -FontSize 10pt New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Yes', 'No' -Color PaleGreen, Orchid New-ChartBar -Name 'Is administrative' -Value $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'], $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] New-ChartBar -Name 'Is consistent' -Value $Script:Reporting['GPOOwners']['Variables']['IsConsistent'], $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] } -Title 'Group Policy Owners' -TitleAlignment center } #> } Summary = { New-HTMLText -FontSize 10pt -TextBlock { "By default GPO creation is usually maintained by Domain Admins or Enterprise Admins. " "When GPO is created by member of Domain Admins or Enterprise Admins group the GPO Owner is set to Domain Admins. " "When GPO is created by member of Group Policy Creator Owners or other group has delegated rights to create a GPO the owner of said GPO is not Domain Admins group but is assigned to relevant user. " "GPO Owners should be Domain Admins or Enterprise Admins to prevent abuse. If that isn't so it means owner is able to fully control GPO and potentially change it's settings in uncontrolled way. " "While at the moment of creation of new GPO it's not a problem, in long term it's possible such person may no longer be admin, yet keep their rights over GPO. " } New-HTMLText -FontSize 10pt -TextBlock { "As you're aware Group Policies are stored in 2 places. In Active Directory (metadata) and SYSVOL (settings). This means that there are 2 places where GPO Owners exists. " "This also means that for multiple reasons AD and SYSVOL can be out of sync when it comes to their permissions which can lead to uncontrolled ability to modify them. " "Ownership in Active Directory and Ownership of SYSVOL for said GPO are required to be the same. " } New-HTMLText -Text "Here's a short summary of ", "Group Policy Owners", ": " -FontSize 10pt -FontWeight normal, bold, normal New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'] -FontWeight normal, bold New-HTMLListItem -Text 'Non-Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] -FontWeight normal, bold New-HTMLListItem -Text "Owners consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsConsistent'] -FontWeight normal, bold New-HTMLListItem -Text "Owners not-consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -FontSize 10pt -Text "Following will need to happen: " -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies requiring owner change: ', $Script:Reporting['GPOOwners']['Variables']['WillFix'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies which can't be fixed (no SYSVOL?): ", $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFix'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies unaffected: ", $Script:Reporting['GPOOwners']['Variables']['WillNotTouch'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -Text 'Following domains require fixing using, ', 'different methods:' -FontSize 10pt -FontWeight bold, bold -Color Black, RedRobin -TextDecoration none, underline New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal } } -FontSize 10pt } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOOwners']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Yes', 'No' -Color LightGreen, Salmon New-ChartBar -Name 'Is administrative' -Value $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'], $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] New-ChartBar -Name 'Is consistent' -Value $Script:Reporting['GPOOwners']['Variables']['IsConsistent'], $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] } -Title 'Group Policy Owners' -TitleAlignment center } } New-HTMLSection -Name 'Group Policy Owners' { New-HTMLTable -DataTable $Script:Reporting['GPOOwners']['Data'] -Filtering { New-HTMLTableCondition -Name 'IsOwnerConsistent' -Value $false -BackgroundColor Salmon -ComparisonType string -Row New-HTMLTableCondition -Name 'IsOwnerAdministrative' -Value $false -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 10, 20, 30, 40, 50 } New-HTMLSection -Name 'Steps to fix Group Policy Owners' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { #New-HTMLText -Text 'Following steps will guide you how to fix group policy owners' New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with fixing Group Policy Owners. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOwnersBefore.html -Verbose -Type GPOOwners } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment." "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $OwnersGPO = Get-GPOZaurrOwner -IncludeSysvol -Verbose $OwnersGPO | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Make a backup (optional)' { New-HTMLText -TextBlock { "The process of fixing GPO Owner does NOT touch GPO content. It simply changes owners on AD and SYSVOL at the same time. " "However, it's always good to have a backup before executing changes that may impact Active Directory. " } New-HTMLCodeBlock -Code { $GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All $GPOSummary | Format-Table # only if you want to display output of backup } New-HTMLText -TextBlock { "Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. " } } New-HTMLWizardStep -Name 'Set GPO Owners to Administrative (Domain Admins)' { New-HTMLText -Text "Following command will find any GPO which doesn't have proper GPO Owner (be it due to inconsistency or not being Domain Admin) and will enforce new GPO Owner. " New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black New-HTMLCodeBlock -Code { Set-GPOZaurrOwner -Type All -Verbose -WhatIf } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Set-GPOZaurrOwner -Type All -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data." } -LineBreak New-HTMLText -Text "Once happy with results please follow with command (this will start fixing process): " -LineBreak -FontWeight bold New-HTMLCodeBlock -Code { Set-GPOZaurrOwner -Type All -Verbose -LimitProcessing 2 } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Set-GPOZaurrOwner -Type All -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "This command when executed sets new owner only on first X non-compliant GPO Owners for AD/SYSVOL. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly." } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOwnersAfter.html -Verbose -Type GPOOwners } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['GPOOwners']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOOwners']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 10, 20, 30, 40, 50 } } } } $GPOZaurrPassword = [ordered] @{ Name = 'Group Policy Passwords' Enabled = $true Action = $null Data = $null Execute = { Get-GPOZaurrPassword -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { New-HTMLTable -DataTable $Script:Reporting['GPOPassword']['Data'] -Filtering if ($Script:Reporting['GPOPassword']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOPassword']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrPermissions = [ordered] @{ Name = 'Group Policy Permissions' Enabled = $true Action = $null Data = $null Execute = { Get-GPOZaurrPermission -Type All -IncludePermissionType GpoEditDeleteModifySecurity, GpoEdit, GpoCustom -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['Data'] -Filtering if ($Script:Reporting['GPOPermissions']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrPermissionsAdministrative = [ordered] @{ Name = 'Group Policy Administrative Permissions' Enabled = $true Action = $null Data = $null Execute = { $Object = [ordered] @{ Permissions = Get-GPOZaurrPermission -Type Administrative -IncludePermissionType GpoEditDeleteModifySecurity -ReturnSecurityWhenNoData -IncludeGPOObject -ReturnSingleObject -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } $Object['PermissionsPerRow'] = $Object['Permissions'] | ForEach-Object { $_ } $Object } Processing = { # Create Per Domain Variables $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'] = @{} $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouchPerDomain'] = @{} foreach ($GPO in $Script:Reporting['GPOPermissionsAdministrative']['Data'].Permissions) { # Create Per Domain Variables if (-not $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'][$GPO[0].DomainName]) { $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'][$GPO[0].DomainName] = 0 } if (-not $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouchPerDomain'][$GPO[0].DomainName]) { $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouchPerDomain'][$GPO[0].DomainName] = 0 } # Checks $Analysis = Get-PermissionsAnalysis -GPOPermissions $GPO -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -Type Administrative -PermissionType GpoEditDeleteModifySecurity if ($Analysis.Skip) { $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouch']++ $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouchPerDomain'][$GPO[0].DomainName]++ } else { $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFix']++ $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'][$GPO[0].DomainName]++ } } if ($Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFix'] -gt 0) { $Script:Reporting['GPOPermissionsAdministrative']['ActionRequired'] = $true } else { $Script:Reporting['GPOPermissionsAdministrative']['ActionRequired'] = $false } } Variables = @{ WillFix = 0 WillNotTouch = 0 WillFixPerDomain = $null WillNotTouchPerDomain = $null } Overview = { } Summary = { New-HTMLText -FontSize 10pt -TextBlock { "When GPO is created by default it gets Domain Admins and Enterprise Admins with Edit/Delete/Modify Security permissions. " "For some reason, some Administrators remove those permissions or modify them when they shouldn't touch those at all. " "Since having Edit/Delete/Modify Security permissions doesn't affect GPOApply permissions there's no reason to remove Domain Admins or Enterprise Admins from permissions, or limit their rights. " } -LineBreak New-HTMLText -FontSize 10pt -Text "Assesment results: " -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies requiring adding Domain Admins or Enterprise Admins: ', $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFix'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouch'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -Text @( "That means we need to fix permissions on: " $($Script:Reporting['GPOPermissionsAdministrative']['Variables'].WillFix) " out of " $($Script:Reporting['GPOPermissionsAdministrative']['Data'].Permissions).Count " Group Policies. " ) -FontSize 10pt -FontWeight bold, bold, normal, bold, normal -Color Black, FreeSpeechRed, Black, Black -LineBreak -TextDecoration none, underline, underline, underline, none } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOPermissionsAdministrative']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Yes', 'No' -Color LightGreen, Salmon New-ChartBar -Name 'Administrative Users Present' -Value $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillNotTouch'], $Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFix'] #New-ChartBar -Name 'Accessible Group Policies' -Value $Script:Reporting['GPOPermissionsAdministrative']['Variables']['Read'], $Script:Reporting['GPOPermissionsAdministrative']['Variables']['CouldNotRead'] } -Title 'Group Policy Permissions' -TitleAlignment center } } New-HTMLSection -Name 'Group Policy Administrative Users Analysis' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsAdministrative']['Data'].PermissionsPerRow -Filtering { New-HTMLTableCondition -Name 'Permission' -Value '' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 7, 15, 30, 45, 60 } New-HTMLSection -Name 'Steps to fix Group Policy Administrative Users' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with fixing Group Policy Authenticated Users. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsAdministrativeBefore.html -Verbose -Type GPOPermissionsAdministrative } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment. " "GPOs with problems will be those not having any value for Permission/PermissionType columns. " "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $AdministrativeUsers = Get-GPOZaurrPermission -Type Administrative -IncludePermissionType GpoEditDeleteModifySecurity -ReturnSecurityWhenNoData $AdministrativeUsers | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Make a backup (optional)' { New-HTMLText -TextBlock { "The process of fixing GPO Permissions does NOT touch GPO content. It simply adds permissionss on AD and SYSVOL at the same time for given GPO. " "However, it's always good to have a backup before executing changes that may impact Active Directory. " } New-HTMLCodeBlock -Code { $GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All $GPOSummary | Format-Table # only if you want to display output of backup } New-HTMLText -TextBlock { "Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. " } } New-HTMLWizardStep -Name 'Add Administrative Groups proper permissions GPO' { New-HTMLText -Text @( "Following command will find any GPO which doesn't have Domain Admins and Enterprise Admins added with GpoEditDeleteModifySecurity and will add it as GpoEditDeleteModifySecurity. ", "This change doesn't change GpoApply permission, therefore it won't change to whom the GPO applies to. ", "It ensures that Domain Admins and Enterprise Admins can manage GPO. ", "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental adding of permissions." ) -FontWeight normal, normal, normal, normal, bold, normal -Color Black, Black, Black, Black, Red, Black New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -All -WhatIf -Verbose } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -All -WhatIf -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data." } -LineBreak New-HTMLText -Text "Once happy with results please follow with command (this will start fixing process): " -LineBreak -FontWeight bold New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -All -Verbose -LimitProcessing 2 } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -All -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "This command when executed adds Enterprise Admins or/and Domain Admins (GpoEditDeleteModifySecurity permission) only on first X non-compliant Group Policies. " "Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. " "In case of any issues please review and action accordingly." } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsAdministrativeAfter.html -Verbose -Type GPOPermissionsAdministrative } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['GPOPermissionsAdministrative']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsAdministrative']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 10, 20, 30, 40, 50 } } } } $GPOZaurrPermissionsRead = [ordered] @{ Name = 'Group Policy Authenticated Users Permissions' Enabled = $true Action = $null Data = $null Execute = { [ordered] @{ Permissions = Get-GPOZaurrPermission -Type AuthenticatedUsers -ReturnSecurityWhenNoData -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains Issues = Get-GPOZaurrPermissionIssue } } Processing = { # This is a workaround - we need to use it since we have 0 permissions # Create Per Domain Variables $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'] = @{} $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'] = @{} $Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'] = @{} $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'] = @{} $Script:Reporting['GPOPermissionsRead']['Variables']['TotalPerDomain'] = @{} foreach ($GPO in $Script:Reporting['GPOPermissionsRead']['Data'].Issues) { # Create Per Domain Variables if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName] = 0 } if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'][$GPO.DomainName] = 0 } if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['TotalPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOPermissionsRead']['Variables']['TotalPerDomain'][$GPO.DomainName] = 0 } if ($GPO.PermissionIssue) { $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead']++ $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]++ } else { $Script:Reporting['GPOPermissionsRead']['Variables']['Read']++ $Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'][$GPO.DomainName]++ } $Script:Reporting['GPOPermissionsRead']['Variables']['TotalPerDomain'][$GPO.DomainName]++ } foreach ($GPO in $Script:Reporting['GPOPermissionsRead']['Data'].Permissions) { # Create Per Domain Variables if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$GPO.DomainName] = 0 } if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'][$GPO.DomainName]) { $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'][$GPO.DomainName] = 0 } # Checks if ($GPO.Permission -in 'GpoApply', 'GpoRead') { $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouch']++ $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'][$GPO.DomainName]++ } else { $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix']++ $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$GPO.DomainName]++ } } if ($Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] -gt 0 -or $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead'] -gt 0) { $Script:Reporting['GPOPermissionsRead']['ActionRequired'] = $true } else { $Script:Reporting['GPOPermissionsRead']['ActionRequired'] = $false } # Summary from 2 reports $Script:Reporting['GPOPermissionsRead']['Variables']['TotalToFix'] = $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] + $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead'] } Variables = @{ WillFix = 0 WillNotTouch = 0 WillFixPerDomain = $null WillNotTouchPerDomain = $null CouldNotRead = 0 CouldNotReadPerDomain = $null Read = 0 ReadPerDomain = $null TotalToFix = 0 TotalPerDomain = $null } Overview = { } Summary = { New-HTMLText -FontSize 10pt -TextBlock { "When GPO is created one of the permissions that are required for proper functioning of Group Policies is NT AUTHORITY\Authenticated Users. " "Some Administrators don't follow best practices and trying to remove GpoApply permission, remove also GpoRead permission from a GPO which can have consequences. " } -LineBreak New-HTMLText -Text "On June 14th, 2016 Microsoft released [HotFix](https://support.microsoft.com/en-gb/help/3159398/ms16-072-description-of-the-security-update-for-group-policy-june-14-2) that requires Authenticated Users to be present on all Group Policies to function properly: " -FontSize 10pt New-HTMLText -TextBlock { "MS16-072 changes the security context with which user group policies are retrieved. " "This by-design behavior change protects customers’ computers from a security vulnerability. " "Before MS16-072 is installed, user group policies were retrieved by using the user’s security context. " "After MS16-072 is installed, user group policies are retrieved by using the computer's security context." } -FontStyle italic -FontSize 10pt -FontWeight bold -LineBreak New-HTMLText -FontSize 10pt -Text @( "There are two parts to this assesment. Reading all Group Policies Permissions that account ", $($Env:USERNAME.ToUpper()), " has permissions to read and provide detailed assesment about permissions. ", "Second assesment checks for permissions that this account is not able to read at all, and therefore it has no visibility about permissions set on it. " "We just were able to detect the problem, but hopefully higher level account (Domain Admin) should be able to provide full assesment. " ) -FontWeight normal, bold, normal New-HTMLText -FontSize 10pt -Text "First assesment results: " -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies requiring Authenticated Users with GpoRead permission: ', $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouch'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -FontSize 10pt -Text "Secondary assesment results: " -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text "Group Policies couldn't read at all: ", $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead'] -FontWeight normal, bold New-HTMLListItem -Text "Group Policies with permissions allowing read: ", $Script:Reporting['GPOPermissionsRead']['Variables']['Read'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -Text 'With split per domain (permissions required):' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'].Keys) { New-HTMLListItem -Text @( "$Domain requires ", $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$Domain], " changes out of ", $Script:Reporting['GPOPermissionsRead']['Variables']['TotalPerDomain'][$Domain], "." ) -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -Text @( "That means we need to fix permissions on: " $($Script:Reporting['GPOPermissionsRead']['Variables']['TotalToFix']) " out of " $($Script:Reporting['GPOPermissionsRead']['Data'].Issues).Count " Group Policies. " ) -FontSize 10pt -FontWeight bold, bold, normal, bold, normal -Color Black, FreeSpeechRed, Black, Black -LineBreak -TextDecoration none, underline, underline, underline, none } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOPermissionsRead']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Yes', 'No' -Color LightGreen, Salmon New-ChartBar -Name 'Authenticated Users Available' -Value $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouch'], $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] New-ChartBar -Name 'Accessible Group Policies' -Value $Script:Reporting['GPOPermissionsRead']['Variables']['Read'], $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead'] } -Title 'Group Policy Permissions' -TitleAlignment center } } New-HTMLSection -Name 'Group Policy Authenticated Users Analysis' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRead']['Data'].Permissions -Filtering { New-HTMLTableCondition -Name 'Permission' -Value '' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 7, 15, 30, 45, 60 } New-HTMLSection -Name 'Group Policy Issues Assesment' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRead']['Data'].Issues -Filtering { New-HTMLTableCondition -Name 'PermissionIssue' -Value $true -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 7, 15, 30, 45, 60 -DefaultSortColumn PermissionIssue -DefaultSortOrder Descending } New-HTMLSection -Name 'Steps to fix Group Policy Authenticated Users' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with fixing Group Policy Authenticated Users. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsReadBefore.html -Verbose -Type GPOPermissionsRead } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment. " "GPOs with problems will be those not having any value for Permission/PermissionType columns. " "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $AuthenticatedUsers = Get-GPOZaurrPermission -Type AuthenticatedUsers -ReturnSecurityWhenNoData $AuthenticatedUsers | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Make a backup (optional)' { New-HTMLText -TextBlock { "The process of fixing GPO Permissions does NOT touch GPO content. It simply adds permissionss on AD and SYSVOL at the same time for given GPO. " "However, it's always good to have a backup before executing changes that may impact Active Directory. " } New-HTMLCodeBlock -Code { $GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All $GPOSummary | Format-Table # only if you want to display output of backup } New-HTMLText -TextBlock { "Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. " } } New-HTMLWizardStep -Name 'Add Authenticated Users ability to read all GPO' { New-HTMLText -Text @( "Following command will find any GPO which doesn't have Authenticated User as GpoRead or GpoApply and will add it as GpoRead. ", "This change doesn't change GpoApply permission, therefore it won't change to whom the GPO applies to. ", "It ensures that COMPUTERS can read GPO properly to be able to Apply it. ", "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental adding of permissions." ) -FontWeight normal, normal, normal, normal, bold, normal -Color Black, Black, Black, Black, Red, Black New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -WhatIf -Verbose } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -WhatIf -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data." } -LineBreak New-HTMLText -Text "Once happy with results please follow with command (this will start fixing process): " -LineBreak -FontWeight bold New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -Verbose -LimitProcessing 2 } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "This command when executed adds Authenticated Users (GpoRead permission) only on first X non-compliant Group Policies. " "Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. " "In case of any issues please review and action accordingly." } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsReadAfter.html -Verbose -Type GPOPermissionsRead } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['GPOPermissionsRead']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRead']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 10, 20, 30, 40, 50 } } } } $GPOZaurrPermissionsRoot = [ordered] @{ Name = 'Group Policies Root Permissions' Enabled = $true Action = $null Data = $null Execute = { Get-GPOZaurrPermissionRoot -SkipNames -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRoot']['Data'] -Filtering if ($Script:Reporting['GPOPermissionsRoot']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRoot']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } $GPOZaurrPermissionsUnknown = [ordered] @{ Name = 'Group Policy Unknown Permissions' Enabled = $true Action = $null Data = $null Execute = { Get-GPOZaurrPermission -Type Unknown -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { # Create Per Domain Variables $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFixPerDomain'] = @{} foreach ($GPO in $Script:Reporting['GPOPermissionsUnknown']['Data']) { # Create Per Domain Variables if (-not $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFixPerDomain'][$GPO[0].DomainName]) { $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFixPerDomain'][$GPO[0].DomainName] = 0 } # Checks $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFix']++ $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFixPerDomain'][$GPO[0].DomainName]++ } if ($Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFix'] -gt 0) { $Script:Reporting['GPOPermissionsUnknown']['ActionRequired'] = $true } else { $Script:Reporting['GPOPermissionsUnknown']['ActionRequired'] = $false } } Variables = @{ WillFix = 0 WillFixPerDomain = $null } Overview = { } Summary = { New-HTMLText -FontSize 10pt -TextBlock { "Group Policies contain multiple permissions for different level of access. " "Be it adminstrative permissions, read permissions or apply permissions. " "Over time some users or groups get deleted for different reasons and such permission in Group Policies leave a trace in form of Unknown SID. " "Unknown SIDs can also be remains of Active Directory Trusts, that have been deleted or are otherwise unavailable. " "Following assesment detects all unknown permissions and provides them for review & deletion. " } -LineBreak New-HTMLText -FontSize 10pt -Text "Assesment results: " -FontWeight bold New-HTMLList -Type Unordered { New-HTMLListItem -Text 'Group Policies requiring removal of unknown SIDs: ', $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFix'] -FontWeight normal, bold } -FontSize 10pt New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold New-HTMLList -Type Unordered { foreach ($Domain in $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFixPerDomain'].Keys) { New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal } } -FontSize 10pt New-HTMLText -Text @( "That means we need to fix permissions on: " $($Script:Reporting['GPOPermissionsUnknown']['Variables'].WillFix) " out of " ($Script:Reporting['GPOPermissionsUnknown']['Data']).Count " Group Policies. " ) -FontSize 10pt -FontWeight bold, bold, normal, bold, normal -Color Black, FreeSpeechRed, Black, Black -LineBreak -TextDecoration none, underline, underline, underline, none } Solution = { New-HTMLSection -Invisible { New-HTMLPanel { & $Script:GPOConfiguration['GPOPermissionsUnknown']['Summary'] } New-HTMLPanel { New-HTMLChart { New-ChartBarOptions -Type barStacked New-ChartLegend -Name 'Yes' -Color Salmon New-ChartBar -Name 'Unknown Permissions Present' -Value $Script:Reporting['GPOPermissionsUnknown']['Variables']['WillFix'] } -Title 'Group Policy Permissions' -TitleAlignment center } } New-HTMLSection -Name 'Group Policy Unknown Permissions Analysis' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsUnknown']['Data'] -Filtering { New-HTMLTableCondition -Name 'Permission' -Value '' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 7, 15, 30, 45, 60 } New-HTMLSection -Name 'Steps to fix Group Policy Unknown Permissions' { New-HTMLContainer { New-HTMLSpanStyle -FontSize 10pt { New-HTMLWizard { New-HTMLWizardStep -Name 'Prepare environment' { New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery." New-HTMLCodeBlock -Code { Install-Module GPOZaurr -Force Import-Module GPOZaurr -Force } -Style powershell New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step." } New-HTMLWizardStep -Name 'Prepare report' { New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removing unknown permissions. To generate new report please use:" New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsUnknownBefore.html -Verbose -Type GPOPermissionsUnknown } New-HTMLText -TextBlock { "When executed it will take a while to generate all data and provide you with new report depending on size of environment. " "The table only shows GPO and their unknown permissions. " "It doesn't show permissions that are not subject of this investigation. " "Once confirmed that data is still showing issues and requires fixing please proceed with next step." } New-HTMLText -Text "Alternatively if you prefer working with console you can run: " New-HTMLCodeBlock -Code { $UnknownPermissions = Get-GPOZaurrPermission -Type Unknown $UnknownPermissions | Format-Table } New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you." } New-HTMLWizardStep -Name 'Make a backup (optional)' { New-HTMLText -TextBlock { "The process of fixing GPO Permissions does NOT touch GPO content. It simply removes permissionss on AD and SYSVOL at the same time for given GPO. " "However, it's always good to have a backup before executing changes that may impact Active Directory. " } New-HTMLCodeBlock -Code { $GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All $GPOSummary | Format-Table # only if you want to display output of backup } New-HTMLText -TextBlock { "Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. " } } New-HTMLWizardStep -Name 'Add Administrative Groups proper permissions GPO' { New-HTMLText -Text @( "Following command will find any GPO which has an unknown SID and will remove it. ", "This change doesn't change any other permissions. ", "It ensures that GPOs have no unknown permissions present. ", "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental adding of permissions." ) -FontWeight normal, normal, normal, normal, bold, normal -Color Black, Black, Black, Black, Red, Black New-HTMLCodeBlock -Code { Remove-GPOZaurrPermission -Verbose -Type Unknown -WhatIf } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrPermission -Verbose -Type Unknown -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data." } -LineBreak New-HTMLText -Text "Once happy with results please follow with command (this will start fixing process): " -LineBreak -FontWeight bold New-HTMLCodeBlock -Code { Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 2 } New-HTMLText -TextBlock { "Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: " } New-HTMLCodeBlock -Code { Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' } New-HTMLText -TextBlock { "This command when executed removes only first X unknwon permissions from Group Policies. " "Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur." "Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. " "In case of any issues please review and action accordingly." } } New-HTMLWizardStep -Name 'Verification report' { New-HTMLText -TextBlock { "Once cleanup task was executed properly, we need to verify that report now shows no problems." } New-HTMLCodeBlock -Code { Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsUnknownAfter.html -Verbose -Type GPOPermissionsUnknown } New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond } } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center } } } if ($Script:Reporting['GPOPermissionsUnknown']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsUnknown']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } -PagingOptions 10, 20, 30, 40, 50 } } } } $GPOZaurrSysVolLegacyFiles = [ordered] @{ Name = 'SYSVOL Legacy ADM Files' Enabled = $false Action = $null Data = $null Execute = { Get-GPOZaurrLegacyFiles -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains } Processing = { } Variables = @{ } Overview = { } Solution = { New-HTMLTable -DataTable $Script:Reporting['SysVolLegacyFiles']['Data'] -Filtering if ($Script:Reporting['SysVolLegacyFiles']['WarningsAndErrors']) { New-HTMLSection -Name 'Warnings & Errors to Review' { New-HTMLTable -DataTable $Script:Reporting['SysVolLegacyFiles']['WarningsAndErrors'] -Filtering { New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row } } } } } function New-GPOZaurrReportConsole { [cmdletBinding()] param( [System.Collections.IDictionary] $Results ) Begin { $GPODeny = @{ Color = 'Yellow', 'Red', 'Yellow', 'Red' StartSpaces = 6 } $GPOSuccess = @{ Color = 'Yellow', 'Green', 'Yellow', 'Green' StartSpaces = 6 } $WriteSummary = @{ Color = 'Yellow', 'Blue' StartSpaces = 3 } $ComputerWhereApplied = ($Results.ComputerResults.GroupPolicies | Sort-Object -Property DomainName, Name).Where( { $_.Status -eq 'Applied' }, 'split') $UserWhereApplied = ($Results.UserResults.GroupPolicies | Sort-Object -Property Name).Where( { $_.Status -eq 'Applied' }, 'split') } Process { Write-Color -Text 'Computer Settings' -Color White -LinesBefore 1 Write-Color -Text '[>] Last time Group Policy was applied: ', $Results.ComputerResults.Summary.ReadTime @WriteSummary Write-Color -Text '[>] Computer Name: ', $Results.ComputerResults.Summary.ComputerName @WriteSummary Write-Color -Text '[>] Domain Name: ', $Results.ComputerResults.Summary.DomainName @WriteSummary Write-Color -Text '[>] Organizational Unit: ', $Results.ComputerResults.Summary.OrganizationalUnit @WriteSummary Write-Color -Text '[>] Site: ', $Results.ComputerResults.Summary.Site @WriteSummary Write-Color -Text '[>] GPO Types: ', ($Results.ComputerResults.Summary.GPOTypes -replace [System.Environment]::NewLine, ', ') @WriteSummary Write-Color -Text '[>] Slow link: ', ($Results.ComputerResults.Summary.SlowLink) @WriteSummary Write-Color -Text 'Applied Group Policy Objects' -StartSpaces 3 -LinesBefore 1 foreach ($GPO in $ComputerWhereApplied[0]) { Write-Color -Text '[+] [', $GPO.DomainName, '] ', $GPO.Name @GPOSuccess } Write-Color -Text 'Denied Group Policy Objects' -StartSpaces 3 foreach ($GPO in $ComputerWhereApplied[1]) { Write-Color -Text '[-] [', $GPO.DomainName, '] ', $GPO.Name @GPODeny } Write-Color -Text 'User Settings' -Color Yellow -LinesBefore 1 Write-Color -Text 'Applied Group Policy Objects' -StartSpaces 3 foreach ($GPO in $UserWhereApplied[0] ) { Write-Color -Text '[+] [', $GPO.DomainName, '] ', $GPO.Name @GPOSuccess } Write-Color -Text 'Denied Group Policy Objects' -StartSpaces 3 foreach ($GPO in $UserWhereApplied[1]) { Write-Color -Text '[-] [', $GPO.DomainName, '] ', $GPO.Name @GPODeny } } } function New-GPOZaurrReportHTML { [cmdletBinding()] param( [System.Collections.IDictionary] $Support, [string] $Path, [switch] $Offline, [switch] $Open ) $PSDefaultParameterValues = @{ "New-HTMLTable:WarningAction" = 'SilentlyContinue' } if (-not $Path) { $Path = [io.path]::GetTempFileName().Replace('.tmp', ".html") } $ComputerName = $($Support.ResultantSetPolicy.LoggingComputer) #$UserName = $($Support.ResultantSetPolicy.UserName) #$LoggingMode = $($Support.ResultantSetPolicy.LoggingMode) New-HTML -TitleText "Group Policy Report - $ComputerName" { #New-HTMLTabOptions -SlimTabs -Transition -LinearGradient -SelectorColor Akaroa New-HTMLTabOptions -SlimTabs ` -BorderBottomStyleActive solid -BorderBottomColorActive LightSkyBlue -BackgroundColorActive none ` -TextColorActive Black -Align left -BorderRadius 0px -RemoveShadow -TextColor Grey -TextTransform capitalize New-HTMLTab -Name 'Information' { New-HTMLTable -DataTable $Support.ResultantSetPolicy -HideFooter } foreach ($Key in $Support.Keys) { if ($Key -eq 'ResultantSetPolicy') { continue } New-HTMLTab -Name $Key { New-HTMLTab -Name 'Summary' { New-HTMLSection -Invisible { New-HTMLSection -HeaderText 'Summary' { New-HTMLTable -DataTable $Support.$Key.Summary -Filtering -PagingOptions @(7, 14 ) New-HTMLTable -DataTable $Support.$Key.SummaryDetails -Filtering -PagingOptions @(7, 14) } New-HTMLSection -HeaderText 'Part of Security Groups' { New-HTMLTable -DataTable $Support.$Key.SecurityGroups -Filtering -PagingOptions @(7, 14) } } <# New-HTMLSection -HeaderText 'Summary Downloads' { New-HTMLTable -DataTable $Support.$Key.SummaryDownload -HideFooter } #> New-HTMLSection -HeaderText 'Resultant Set Policy' { New-HTMLTable -DataTable $Support.$Key.ResultantSetPolicy -HideFooter } } New-HTMLTab -Name 'Group Policies' { New-HTMLSection -Invisible { <# New-HTMLSection -HeaderText 'Processing Time' { New-HTMLTable -DataTable $Support.$Key.ProcessingTime -Filtering } #> New-HTMLSection -HeaderText 'ExtensionStatus' { New-HTMLTable -DataTable $Support.$Key.ExtensionStatus -Filtering } } New-HTMLSection -HeaderText 'Group Policies' { New-HTMLTable -DataTable $Support.$Key.GroupPolicies -Filtering } New-HTMLSection -HeaderText 'Group Policies Links' { New-HTMLTable -DataTable $Support.$Key.GroupPoliciesLinks -Filtering } <# New-HTMLSection -HeaderText 'Group Policies Applied' { New-HTMLTable -DataTable $Support.$Key.GroupPoliciesApplied -Filtering } New-HTMLSection -HeaderText 'Group Policies Denied' { New-HTMLTable -DataTable $Support.$Key.GroupPoliciesDenied -Filtering } #> } New-HTMLTab -Name 'Extension Data' { New-HTMLSection -HeaderText 'Extension Data' { New-HTMLTable -DataTable $Support.$Key.ExtensionData -Filtering } } New-HTMLTab -Name 'Scope of Management' { New-HTMLSection -HeaderText 'Scope of Management' { New-HTMLTable -DataTable $Support.$Key.ScopeOfManagement -Filtering } } <# New-HTMLTab -Name 'Events By ID' { foreach ($ID in $Support.$Key.EventsByID.Keys) { New-HTMLSection -HeaderText "Event ID $ID" { New-HTMLTable -DataTable $Support.$Key.EventsByID[$ID] -Filtering -AllProperties } } } New-HTMLTab -Name 'Events' { New-HTMLSection -HeaderText 'Events' { New-HTMLTable -DataTable $Support.$Key.Events -Filtering -AllProperties } } #> } } if ($Support.ComputerResults.Results) { New-HTMLTab -Name 'Details' { foreach ($Detail in $Support.ComputerResults.Results.Keys) { $ShortDetails = $Support.ComputerResults.Results[$Detail] New-HTMLTab -Name $Detail { New-HTMLTab -Name 'Test' { New-HTMLSection -HeaderText 'Summary Downloads' { New-HTMLTable -DataTable $ShortDetails.SummaryDownload -HideFooter } New-HTMLSection -HeaderText 'Processing Time' { New-HTMLTable -DataTable $ShortDetails.ProcessingTime -Filtering } New-HTMLSection -HeaderText 'Group Policies Applied' { New-HTMLTable -DataTable $ShortDetails.GroupPoliciesApplied -Filtering } New-HTMLSection -HeaderText 'Group Policies Denied' { New-HTMLTable -DataTable $ShortDetails.GroupPoliciesDenied -Filtering } } New-HTMLTab -Name 'Events By ID' { foreach ($ID in $ShortDetails.EventsByID.Keys) { New-HTMLSection -HeaderText "Event ID $ID" { New-HTMLTable -DataTable $ShortDetails.EventsByID[$ID] -Filtering -AllProperties } } } New-HTMLTab -Name 'Events' { New-HTMLSection -HeaderText 'Events' { New-HTMLTable -DataTable $ShortDetails.Events -Filtering -AllProperties } } } } } } } -Online:(-not $Offline.IsPresent) -Open:$Open.IsPresent -FilePath $Path } function Remove-PrivPermission { [cmdletBinding(SupportsShouldProcess)] param( [string] $Principal, [validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'DistinguishedName', [PSCustomObject] $GPOPermission, [alias('PermissionType')][Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType ) if ($GPOPermission.PrincipalName) { $Text = "Removing SID: $($GPOPermission.PrincipalSid), Name: $($GPOPermission.PrincipalDomainName)\$($GPOPermission.PrincipalName), SidType: $($GPOPermission.PrincipalSidType) from domain $($GPOPermission.DomainName)" } else { $Text = "Removing SID: $($GPOPermission.PrincipalSid), Name: EMPTY, SidType: $($GPOPermission.PrincipalSidType) from domain $($GPOPermission.DomainName)" } if ($PrincipalType -eq 'DistinguishedName') { if ($GPOPermission.DistinguishedName -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) { if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, $Text)) { try { Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.PrincipalName) / Type: $($GPOPermission.PermissionType)" $GPOPermission.GPOSecurity.Remove($GPOPermission.GPOSecurityPermissionItem) $GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity) } catch { Write-Warning "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } } elseif ($PrincipalType -eq 'Sid') { if ($GPOPermission.PrincipalSid -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) { if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, $Text)) { try { Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.PrincipalName) / Type: $($GPOPermission.PermissionType)" $GPOPermission.GPOSecurity.Remove($GPOPermission.GPOSecurityPermissionItem) $GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity) } catch { if ($_.Exception.Message -like '*The request is not supported. (Exception from HRESULT: 0x80070032)*') { Write-Warning "Remove-GPOZaurrPermission - Bummer! The request is not supported, but lets fix it differently." # This is basically atomic approach. We will totally remove any permissions for that user on ACL level to get rid of this situation. # This situation should only happen if DENY is on FULL Control $ACL = Get-ADACL -ADObject $GPOPermission.GPOObject.Path -Bundle #-Verbose:$VerbosePreference if ($ACL) { Remove-ADACL -ACL $ACL -Principal $Principal -AccessControlType Deny -Verbose:$VerbosePreference } # I've noticed that in situation where Edit settings, delete, modify security is set and then set to Deny we need to fix it once more $ACL = Get-ADACL -ADObject $GPOPermission.GPOObject.Path -Bundle if ($ACL) { Remove-ADACL -ACL $ACL -Principal $Principal -AccessControlType Allow -Verbose:$VerbosePreference } } else { Write-Warning "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } } } elseif ($PrincipalType -eq 'Name') { if ($GPOPermission.PrincipalName -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) { if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, $Text)) { try { Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.PrincipalName) / Type: $($GPOPermission.PermissionType)" $GPOPermission.GPOSecurity.Remove($GPOPermission.GPOSecurityPermissionItem) $GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity) } catch { Write-Warning "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } } } function Reset-GPOZaurrStatus { param( ) #if (-not $Script:GPOConfigurationClean) { # $Script:GPOConfigurationClean = Copy-Dictionary -Dictionary $Script:GPOConfiguration #} else { # $Script:GPOConfiguration = Copy-Dictionary -Dictionary $Script:GPOConfigurationClean #} if (-not $Script:DefaultTypes) { $Script:DefaultTypes = foreach ($T in $Script:GPOConfiguration.Keys) { if ($Script:GPOConfiguration[$T].Enabled) { $T } } } else { foreach ($T in $Script:GPOConfiguration.Keys) { $Script:GPOConfiguration[$T]['Enabled'] = $false } foreach ($T in $Script:DefaultTypes) { $Script:GPOConfiguration[$T]['Enabled'] = $true } } } $Script:Actions = @{ C = 'Create' D = 'Delete' U = 'Update' R = 'Replace' } $Script:GPOConfiguration = [ordered] @{ GPOOrphans = $GPOZaurrOrphans GPOOwners = $GPOZaurrOwners GPOConsistency = $GPOZaurrConsistency GPODuplicates = $GPOZaurrDuplicates GPOList = $GPOZaurrList GPOPassword = $GPOZaurrPassword GPOPermissions = $GPOZaurrPermissions GPOPermissionsAdministrative = $GPOZaurrPermissionsAdministrative GPOPermissionsRead = $GPOZaurrPermissionsRead GPOPermissionsRoot = $GPOZaurrPermissionsRoot GPOPermissionsUnknown = $GPOZaurrPermissionsUnknown GPOFiles = $GPOZaurrFiles GPOBlockedInheritance = $GPOZaurrBlockedInheritance GPOAnalysis = $GPOZaurrAnalysis NetLogonOwners = $GPOZaurrNetLogonOwners NetLogonPermissions = $GPOZaurrNetLogonPermissions SysVolLegacyFiles = $GPOZaurrSysVolLegacyFiles } $Script:GPODitionary = [ordered] @{ AccountPolicies = [ordered] @{ Types = @( @{ Category = 'SecuritySettings' Settings = 'Account' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Account Policies' Code = { ConvertTo-XMLAccountPolicy -GPO $GPO } CodeSingle = { ConvertTo-XMLAccountPolicy -GPO $GPO -SingleObject } } Audit = [ordered] @{ Types = @( @{ Category = 'SecuritySettings' Settings = 'Audit' } @{ Category = 'AuditSettings' Settings = 'AuditSetting' } ) GPOPath = @( 'Policies -> Windows Settings -> Security Settings -> Advanced Audit Policy Configuration -> Audit Policies' 'Policies -> Windows Settings -> Security Settings -> Local Policies -> Audit Policy' ) Code = { ConvertTo-XMLAudit -GPO $GPO } CodeSingle = { ConvertTo-XMLAudit -GPO $GPO -SingleObject } } Autologon = [ordered] @{ # We want to process this based on other report called RegistrySettings # This is because registry settings can be stored in Collections or nested within other registry settings # The original function ConvertTo-XMLRegistryAutologon was processing it in limited ordered and potentially would skip some entries. ByReports = @( @{ Report = 'RegistrySettings' } ) GPOPath = 'Preferences -> Windows Settings -> Registry' CodeReport = { ConvertTo-XMLRegistryAutologonOnReport -GPO $GPO } } AutoPlay = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/AutoPlay Policies' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/AutoPlay Policies*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/AutoPlay Policies*' -SingleObject } } Biometrics = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Biometrics' Code = { #ConvertTo-XMLBitlocker -GPO $GPO ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Biometrics*' } } Bitlocker = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/BitLocker Drive Encryption' Code = { #ConvertTo-XMLBitlocker -GPO $GPO ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/BitLocker Drive Encryption*' } CodeSingle = { #ConvertTo-XMLBitlocker -GPO $GPO ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/BitLocker Drive Encryption*' -SingleObject } } ControlPanel = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Controol Panel' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel' -SingleObject } } ControlPanelAddRemove = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Control Panel/Add or Remove Programs' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Add or Remove Programs' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Add or Remove Programs' -SingleObject } } ControlPanelDisplay = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Control Panel/Display' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Display' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Display' -SingleObject } } ControlPanelPersonalization = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Control Panel/Personalization' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Personalization' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Personalization' -SingleObject } } ControlPanelPrinters = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Control Panel/Printers' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Printers' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Printers' -SingleObject } } ControlPanelPrograms = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Control Panel/Programs' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Programs' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Programs' -SingleObject } } ControlPanelRegional = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Control Panel/Regional and Language Options' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Regional and Language Options' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Control Panel/Regional and Language Options' -SingleObject } } CredentialsDelegation = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/Credentials Delegation' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Credentials Delegation*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Credentials Delegation*' -SingleObject } } CustomInternationalSettings = [ordered]@{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Custom International Settings' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Custom International Settings*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Custom International Settings*' -SingleObject } } Desktop = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Desktop' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Desktop*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Desktop*' -SingleObject } } DnsClient = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Network/DNS Client' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Network/DNS Client*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Network/DNS Client*' -SingleObject } } DriveMapping = [ordered] @{ Types = @( @{ Category = 'DriveMapSettings' Settings = 'DriveMapSettings' } ) GPOPath = 'Preferences -> Windows Settings -> Drive Maps' Code = { ConvertTo-XMLDriveMapSettings -GPO $GPO } CodeSingle = { ConvertTo-XMLDriveMapSettings -GPO $GPO -SingleObject } } EventLog = [ordered] @{ Types = @( @{ Category = 'SecuritySettings' Settings = 'EventLog' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Event Log' Code = { ConvertTo-XMLEventLog -GPO $GPO } CodeSingle = { ConvertTo-XMLEventLog -GPO $GPO } } EventForwarding = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Event Forwarding' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Event Forwarding*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Event Forwarding*' -SingleObject } } EventLogService = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Event Log Service' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Event Log Service*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Event Log Service*' -SingleObject } } FileExplorer = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/File Explorer' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/File Explorer*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/File Explorer*' -SingleObject } } FolderRedirection = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/Folder Redirection' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Folder Redirection' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Folder Redirection' -SingleObject } } FSLogix = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> FSLogix' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'FSLogix' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'FSLogix' -SingleObject } } GoogleChrome = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = @( 'Policies -> Administrative Templates -> Google Chrome' 'Policies -> Administrative Templates -> Google/Google Chrome' 'Policies -> Administrative Templates -> Google Chrome - Default Settings (users can override)' ) Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Google Chrome', 'Google/Google Chrome', 'Google Chrome - Default Settings (users can override)' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Google Chrome', 'Google/Google Chrome', 'Google Chrome - Default Settings (users can override)' -SingleObject } } GroupPolicy = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/Group Policy' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Group Policy*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Group Policy*' -SingleObject } } InternetCommunicationManagement = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/Internet Communication Management' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Internet Communication Management*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Internet Communication Management*' -SingleObject } } InternetExplorer = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Internet Explorer' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Internet Explorer*', 'Composants Windows/Celle Internet Explorer' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Internet Explorer*', 'Composants Windows/Celle Internet Explorer' -SingleObject } } InternetExplorerZones = [ordered] @{ ByReports = @( @{ Report = 'RegistrySettings' } ) GPOPath = 'Preferences -> Windows Settings -> Registry' CodeReport = { ConvertTo-XMLRegistryInternetExplorerZones -GPO $GPO } } KDC = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/KDC' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/KDC' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/KDC' -SingleObject } } LAPS = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> LAPS' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'LAPS' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'LAPS' -SingleObject } } Lithnet = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Lithnet/Password Protection for Active Directory' Code = { #ConvertTo-XMLLithnetFilter -GPO $GPO ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Lithnet/Password Protection for Active Directory*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Lithnet/Password Protection for Active Directory*' -SingleObject } } LocalUsers = [ordered] @{ Types = @( @{ Category = 'LugsSettings' Settings = 'LocalUsersAndGroups' } ) GPOPath = 'Preferences -> Control Panel Settings -> Local Users and Groups' Code = { ConvertTo-XMLLocalUser -GPO $GPO } CodeSingle = { ConvertTo-XMLLocalUser -GPO $GPO -SingleObject } } LocalGroups = [ordered] @{ Types = @( @{ Category = 'LugsSettings' Settings = 'LocalUsersAndGroups' } ) GPOPath = 'Preferences -> Control Panel Settings -> Local Users and Groups' Code = { ConvertTo-XMLLocalGroups -GPO $GPO } CodeSingle = { ConvertTo-XMLLocalGroups -GPO $GPO -SingleObject } } Logon = @{ Types = @( @{ Category = 'RegistrySettings'; Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/Logon' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Logon*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Logon*' -SingleObject } } MicrosoftOutlook2002 = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Microsoft Outlook 2002' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2002*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2002*' -SingleObject } } MicrosoftEdge = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = @( 'Policies -> Administrative Templates -> Microsoft Edge' 'Policies -> Administrative Templates -> Windows Components/Edge UI' 'Policies -> Administrative Templates -> Windows Components/Microsoft Edge' ) Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Edge*', 'Windows Components/Microsoft Edge', 'Windows Components/Edge UI' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Edge*', 'Windows Components/Microsoft Edge', 'Windows Components/Edge UI' -SingleObject } } MicrosoftOutlook2003 = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = @( 'Policies -> Administrative Templates -> Microsoft Office Outlook 2003' 'Policies -> Administrative Templates -> Outlook 2003 RPC Encryption' ) Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Office Outlook 2003*', 'Outlook 2003 RPC Encryption' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Office Outlook 2003*', 'Outlook 2003 RPC Encryption' -SingleObject } } MicrosoftOutlook2010 = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Microsoft Outlook 2010' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2010*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2010*' -SingleObject } } MicrosoftOutlook2013 = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Microsoft Outlook 2013' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2013*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2013*' -SingleObject } } MicrosoftOutlook2016 = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Microsoft Outlook 2016' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2016*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Microsoft Outlook 2016*' -SingleObject } } MicrosoftManagementConsole = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Microsoft Management Console' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Microsoft Management Console*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Microsoft Management Console*' -SingleObject } } NetMeeting = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/NetMeeting' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/NetMeeting*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/NetMeeting*' -SingleObject } } MSSLegacy = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> MSS (Legacy)' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'MSS (Legacy)' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'MSS (Legacy)' -SingleObject } } MSSecurityGuide = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> MS Security Guide' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'MS Security Guide' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'MS Security Guide' -SingleObject } } OneDrive = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/OneDrive' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/OneDrive*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/OneDrive*' -SingleObject } } Policies = @{ Comment = "This isn't really translated" Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates' Code = { ConvertTo-XMLPolicies -GPO $GPO } CodeSingle = { ConvertTo-XMLPolicies -GPO $GPO -SingleObject } } Printers = @{ Types = @( @{ Category = 'PrintersSettings' Settings = 'Printers' } @{ Category = 'PrinterConnectionSettings' Settings = 'PrinterConnection' } ) GPOPath = 'Preferences -> Control Panel Settings -> Printers' Code = { ConvertTo-XMLPrinter -GPO $GPO } CodeSingle = { ConvertTo-XMLPrinter -GPO $GPO -SingleObject } } PrintersPolicies = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = @( 'Policies -> Administrative Templates -> Printers' 'Policies -> Administrative Templates -> Control Panel/Printers' ) Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Printers*', 'Control Panel/Printers*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Printers*', 'Control Panel/Printers*' -SingleObject } } PublicKeyPoliciesCertificates = [ordered] @{ Types = @( @{ Category = 'PublicKeySettings' Settings = 'RootCertificate' } @{ Category = 'PublicKeySettings' Settings = 'IntermediateCACertificate' } @{ Category = 'PublicKeySettings' Settings = 'TrustedPeopleCertificate' } @{ Category = 'PublicKeySettings' Settings = 'UntrustedCertificate' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Public Key Policies' Code = { ConvertTo-XMLGenericPublicKey -GPO $GPO } CodeSingle = { ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject } } <# PublicKeyPoliciesAll = [ordered] @{ Types = @( @{ Category = 'PublicKeySettings' Settings = 'AutoEnrollmentSettings' } @{ Category = 'PublicKeySettings' Settings = 'EFSSettings' } @{ Category = 'PublicKeySettings' Settings = 'RootCertificateSettings' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Public Key Policies' Code = { ConvertTo-XMLGenericPublicKey -GPO $GPO } CodeSingle = { ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject } } #> PublicKeyPoliciesAutoEnrollment = [ordered] @{ Types = @( @{ Category = 'PublicKeySettings' Settings = 'AutoEnrollmentSettings' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Public Key Policies' Code = { ConvertTo-XMLGenericPublicKey -GPO $GPO } CodeSingle = { ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject } } PublicKeyPoliciesEFS = [ordered] @{ Types = @( @{ Category = 'PublicKeySettings' Settings = 'EFSSettings' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Public Key Policies' Code = { ConvertTo-XMLGenericPublicKey -GPO $GPO } CodeSingle = { ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject } } PublicKeyPoliciesRootCA = [ordered] @{ Types = @( @{ Category = 'PublicKeySettings' Settings = 'RootCertificateSettings' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Public Key Policies' Code = { ConvertTo-XMLGenericPublicKey -GPO $GPO } CodeSingle = { ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject } } PublicKeyPoliciesEnrollmentPolicy = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = @( 'Policies -> Windows Settings -> Security Settings -> Public Key Policies' ) Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Internet Communication Management*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Internet Communication Management*' -SingleObject } } RegistrySetting = [ordered] @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'RegistrySetting' } ) GPOPath = "Mixed - missing ADMX?" Code = { ConvertTo-XMLGenericPublicKey -GPO $GPO } CodeSingle = { ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject } } RegistrySettings = [ordered] @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'RegistrySettings' } ) GPOPath = 'Preferences -> Windows Settings -> Registry' Code = { ConvertTo-XMLRegistrySettings -GPO $GPO } CodeSingle = { ConvertTo-XMLRegistrySettings -GPO $GPO -SingleObject } } OnlineAssistance = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Online Assistance' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Online Assistance*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Online Assistance*' -SingleObject } } RemoteAssistance = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> System/Remote Assistance' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Remote Assistance*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Remote Assistance*' -SingleObject } } RemoteDesktopServices = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Remote Desktop Services' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Remote Desktop Services*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Remote Desktop Services*' -SingleObject } } RSSFeeds = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/RSS Feeds' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/RSS Feeds*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/RSS Feeds*' -SingleObject } } Scripts = [ordered] @{ Types = @( @{ Category = 'Scripts' Settings = 'Script' } ) GPOPath = 'Policies -> Windows Settings -> Scripts' Code = { ConvertTo-XMLScripts -GPO $GPO } CodeSingle = { ConvertTo-XMLScripts -GPO $GPO -SingleObject } } SecurityOptions = [ordered] @{ Types = @( @{ Category = 'SecuritySettings' Settings = 'SecurityOptions' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Local Policies -> Security Options' Code = { ConvertTo-XMLSecurityOptions -GPO $GPO } CodeSingle = { ConvertTo-XMLSecurityOptions -GPO $GPO -SingleObject } } SoftwareInstallation = [ordered] @{ Types = @( @{ Category = 'SoftwareInstallationSettings' Settings = 'MsiApplication' } ) GPOPath = 'Policies -> Software Settings -> Software Installation' Code = { ConvertTo-XMLSoftwareInstallation -GPO $GPO } CodeSingle = { ConvertTo-XMLSoftwareInstallation -GPO $GPO -SingleObject } } SystemServices = [ordered] @{ Types = @( @{ Category = 'SecuritySettings' Settings = 'SystemServices' } ) Description = '' GPOPath = 'Policies -> Windows Settings -> Security Settings -> System Services' Code = { ConvertTo-XMLSystemServices -GPO $GPO } CodeSingle = { ConvertTo-XMLSystemServices -GPO $GPO -SingleObject } } SystemServicesNT = [ordered] @{ Types = @( @{ Category = 'ServiceSettings' Settings = 'NTServices' } ) Description = '' GPOPath = 'Preferences -> Control Pannel Settings -> Services' Code = { ConvertTo-XMLSystemServicesNT -GPO $GPO } CodeSingle = { ConvertTo-XMLSystemServicesNT -GPO $GPO -SingleObject } } <# SystemServicesNT1 = [ordered] @{ Types = @( @{ Category = 'ServiceSettings' Settings = 'NTServices' } ) Description = '' GPOPath = 'Preferences -> Control Pannel Settings -> Services' Code = { ConvertTo-XMLSecuritySettings -GPO $GPO -Category 'NTService' } CodeSingle = { ConvertTo-XMLSecuritySettings -GPO $GPO -SingleObject } } #> TaskScheduler = [ordered] @{ Types = @( @{ Category = 'ScheduledTasksSettings' Settings = 'ScheduledTasks' } ) Description = '' GPOPath = 'Preferences -> Control Pannel Settings -> Scheduled Tasks' Code = { ConvertTo-XMLTaskScheduler -GPO $GPO } CodeSingle = { ConvertTo-XMLTaskScheduler -GPO $GPO -SingleObject } } TaskSchedulerPolicies = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Task Scheduler' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Task Scheduler*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Task Scheduler*' -SingleObject } } <# TaskScheduler1 = [ordered] @{ Types = @( @{ Category = 'ScheduledTasksSettings' Settings = 'ScheduledTasks' } ) Description = '' GPOPath = '' Code = { ConvertTo-XMLSecuritySettings -GPO $GPO -Category 'TaskV2', 'Task', 'ImmediateTaskV2', 'ImmediateTask' } CodeSingle = { ConvertTo-XMLTaskScheduler -GPO $GPO -SingleObject } } #> UserRightsAssignment = [ordered] @{ Types = @( @{ Category = 'SecuritySettings' Settings = 'UserRightsAssignment' } ) GPOPath = 'Policies -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment' Code = { ConvertTo-XMLUserRightsAssignment -GPO $GPO } CodeSingle = { ConvertTo-XMLUserRightsAssignment -GPO $GPO -SingleObject } } WindowsDefender = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Defender' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Defender*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Defender*' -SingleObject } } WindowsDefenderExploitGuard = @{ # this needs improvements because of DropDownList Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Microsoft Defender Antivirus/Microsoft Defender Exploit Guard' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Microsoft Defender Antivirus/Microsoft Defender Exploit Guard*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Microsoft Defender Antivirus/Microsoft Defender Exploit Guard*' -SingleObject } } WindowsHelloForBusiness = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Hello For Business' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Hello For Business*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Hello For Business*' -SingleObject } } WindowsInstaller = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Installer' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Installer*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Installer*' -SingleObject } } WindowsLogon = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Logon Options' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Logon Options*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Logon Options*' -SingleObject } } WindowsMediaPlayer = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Media Player' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Media Player*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Media Player*' -SingleObject } } WindowsMessenger = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Messenger' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Messenger*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Messenger*' -SingleObject } } WindowsPowerShell = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows PowerShell' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows PowerShell*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows PowerShell*' -SingleObject } } WindowsRemoteManagement = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = 'Policies -> Administrative Templates -> Windows Components/Windows Remote Management (WinRM)' Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Remote Management (WinRM)*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Remote Management (WinRM)*' -SingleObject } } WindowsUpdate = @{ Types = @( @{ Category = 'RegistrySettings' Settings = 'Policy' } ) GPOPath = @( 'Policies -> Administrative Templates -> Windows Components/Windows Update' #'Policies -> Administrative Templates -> Windows Components/Delivery Optimization' ) Code = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Update*', 'Windows Components/Delivery Optimization*' } CodeSingle = { ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Update*', 'Windows Components/Delivery Optimization*' -SingleObject } } } function Test-SysVolFolders { [cmdletBinding()] param( [Array] $GPOs, [string] $Server, [string] $Domain, [System.Collections.IDictionary] $PoliciesAD, [string] $PoliciesSearchBase ) $Differences = @{ } $SysvolHash = @{ } $GPOGUIDS = $GPOs.ID.GUID $SysVolPath = "\\$($Server)\SYSVOL\$Domain\Policies" Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL from \\$($Server)\SYSVOL\$Domain\Policies" try { $SYSVOL = Get-ChildItem -Path "\\$($Server)\SYSVOL\$Domain\Policies" -Exclude 'PolicyDefinitions' -ErrorAction Stop -Verbose:$false } catch { $Sysvol = $Null } foreach ($_ in $SYSVOL) { $GUID = $_.Name -replace '{' -replace '}' $SysvolHash[$GUID] = $_ } $Files = $SYSVOL.Name -replace '{' -replace '}' if ($Files) { $Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual foreach ($_ in $Comparing) { if ($_.InputObject -eq 'PolicyDefinitions') { # we skip policy definitions continue } if ($_.SideIndicator -eq '==') { $Found = 'Exists' } elseif ($_.SideIndicator -eq '<=') { $Found = 'Not available on SYSVOL' } elseif ($_.SideIndicator -eq '=>') { if ($PoliciesAD[$_.InputObject]) { $Found = $PoliciesAD[$_.InputObject] } else { $Found = 'Not available in AD' } } else { $Found = 'Orphaned GPO' } $Differences[$_.InputObject] = $Found } } $GPOSummary = @( $Count = 0 foreach ($GPO in $GPOS) { $Count++ Write-Verbose "Get-GPOZaurrBroken - Processing [$($GPO.DomainName)]($Count/$($GPOS.Count)) $($GPO.DisplayName)" if ($null -ne $SysvolHash[$GPO.Id.GUID].FullName) { $FullPath = $SysvolHash[$GPO.Id.GUID].FullName try { $ACL = Get-Acl -Path $SysvolHash[$GPO.Id.GUID].FullName -ErrorAction Stop -Verbose:$false $Owner = $ACL.Owner $ErrorMessage = '' } catch { Write-Warning "Get-GPOZaurrBroken - ACL reading (1) failed for $FullPath with error: $($_.Exception.Message)" $ACL = $null $Owner = '' $ErrorMessage = $_.Exception.Message } } else { $FullPath = -join ($SysVolPath, "\{$($GPO.Id.Guid)}") $ACL = $null $Owner = '' $ErrorMessage = 'Not found on SYSVOL' } if ($null -eq $Differences[$GPO.Id.Guid]) { $SysVolStatus = 'Unknown Issue' } else { $SysVolStatus = $Differences[$GPO.Id.Guid] } [PSCustomObject] @{ DisplayName = $GPO.DisplayName Status = $SysVolStatus DomainName = $GPO.DomainName SysvolServer = $Server SysvolStatus = $SysVolStatus GpoStatus = $GPO.GpoStatus Owner = $GPO.Owner FileOwner = $Owner Id = $GPO.Id.Guid Path = $FullPath DistinguishedName = -join ("CN={", $GPO.Id.Guid, "},", $PoliciesSearchBase) Description = $GPO.Description CreationTime = $GPO.CreationTime ModificationTime = $GPO.ModificationTime UserVersion = $GPO.UserVersion ComputerVersion = $GPO.ComputerVersion WmiFilter = $GPO.WmiFilter Error = $ErrorMessage } } # Now we need to list thru Sysvol files and fine those that do not exists as GPO and create dummy GPO objects to show orphaned gpos Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL differences" foreach ($_ in $Differences.Keys) { if ($Differences[$_] -in 'Not available in AD', 'Permissions issue') { $FullPath = $SysvolHash[$_].FullName try { $ACL = Get-Acl -Path $FullPath -ErrorAction Stop $Owner = $ACL.Owner $ErrorMessage = '' } catch { Write-Warning "Get-GPOZaurrBroken - ACL reading (2) failed for $FullPath with error: $($_.Exception.Message)" $ACL = $null $Owner = $null $ErrorMessage = $_.Exception.Message } [PSCustomObject] @{ DisplayName = $SysvolHash[$_].BaseName Status = $Differences[$_] DomainName = $Domain SysvolServer = $Server SysvolStatus = 'Exists' #$Differences[$GPO.Id.Guid] GpoStatus = $Differences[$_] Owner = '' FileOwner = $Owner Id = $_ Path = $FullPath DistinguishedName = -join ("CN={", $_, "},", $PoliciesSearchBase) Description = $null CreationTime = $SysvolHash[$_].CreationTime ModificationTime = $SysvolHash[$_].LastWriteTime UserVersion = $null ComputerVersion = $null WmiFilter = $null Error = $ErrorMessage } } } ) $GPOSummary | Sort-Object -Property DisplayName } function Add-GPOPermission { [cmdletBinding()] param( [validateset('WellKnownAdministrative', 'Administrative', 'AuthenticatedUsers', 'Default')][string] $Type = 'Default', [Microsoft.GroupPolicy.GPPermissionType] $IncludePermissionType, [alias('Trustee')][string] $Principal, [alias('TrusteeType')][validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'DistinguishedName', [validateSet('Allow', 'Deny')][string] $PermitType = 'Allow' ) if ($Type -eq 'Default') { @{ Action = 'Add' Type = 'Default' Principal = $Principal IncludePermissionType = $IncludePermissionType PrincipalType = $PrincipalType PermitType = $PermitType } } elseif ($Type -eq 'AuthenticatedUsers') { @{ Action = 'Add' Type = 'AuthenticatedUsers' IncludePermissionType = $IncludePermissionType PermitType = $PermitType } } elseif ($Type -eq 'Administrative') { @{ Action = 'Add' Type = 'Administrative' IncludePermissionType = $IncludePermissionType PermitType = $PermitType } } elseif ($Type -eq 'WellKnownAdministrative') { @{ Action = 'Add' Type = 'WellKnownAdministrative' IncludePermissionType = $IncludePermissionType PermitType = $PermitType } } } function Add-GPOZaurrPermission { [cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'GPOName')] param( [Parameter(ParameterSetName = 'GPOName', Mandatory)][string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID', Mandatory)][alias('GUID', 'GPOID')][string] $GPOGuid, [Parameter(ParameterSetName = 'All', Mandatory)][switch] $All, [Parameter(ParameterSetName = 'ADObject', Mandatory)] [alias('OrganizationalUnit', 'DistinguishedName')][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject, [validateset('WellKnownAdministrative', 'Administrative', 'AuthenticatedUsers', 'Default')][string] $Type = 'Default', [alias('Trustee')][string] $Principal, [alias('TrusteeType')][validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'DistinguishedName', [Parameter(Mandatory)][alias('IncludePermissionType')][Microsoft.GroupPolicy.GPPermissionType] $PermissionType, [switch] $Inheritable, [validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'All', [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [System.Collections.IDictionary] $ADAdministrativeGroups, [int] $LimitProcessing = [int32]::MaxValue ) $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Extended if (-not $ADAdministrativeGroups) { $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } if ($GPOName) { $Splat = @{ GPOName = $GPOName } } elseif ($GPOGUID) { $Splat = @{ GPOGUID = $GPOGUID } } else { $Splat = @{} } $Splat['IncludeGPOObject'] = $true $Splat['Forest'] = $Forest $Splat['IncludeDomains'] = $IncludeDomains if ($Type -ne 'Default') { $Splat['Type'] = $Type } $Splat['PermitType'] = $PermitType $Splat['Principal'] = $Principal if ($PrincipalType) { $Splat.PrincipalType = $PrincipalType } $Splat['ExcludeDomains'] = $ExcludeDomains $Splat['ExtendedForestInformation'] = $ExtendedForestInformation $Splat['IncludePermissionType'] = $PermissionType $Splat['SkipWellKnown'] = $SkipWellKnown.IsPresent $Splat['SkipAdministrative'] = $SkipAdministrative.IsPresent $CountFixed = 0 Do { # This should always return results. When no data is found it should return basic information that will allow us to add credentials. Get-GPOZaurrPermission @Splat -ReturnSecurityWhenNoData -ReturnSingleObject | ForEach-Object { $GPOPermissions = $_ $PermissionsAnalysis = Get-PermissionsAnalysis -GPOPermissions $GPOPermissions -ADAdministrativeGroups $ADAdministrativeGroups -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Type $Type -PermissionType $PermissionType <# # Prepare data to clean $Skip = $false $AdministrativeExists = @{ DomainAdmins = $false EnterpriseAdmins = $false } $GPOPermissions = $_ # Verification Phase # When it has GPOSecurityPermissionItem property it means it has permissions, if it doesn't it means we have clean object to process if ($GPOPermissions.GPOSecurityPermissionItem) { # Permission exists, but may be incomplete foreach ($GPOPermission in $GPOPermissions) { if ($Type -eq 'Default') { # We were looking for specific principal and we got it. nothing to do # this is for standard users such as przemyslaw.klys / adam.gonzales $Skip = $true break } elseif ($Type -eq 'Administrative') { # We are looking for administrative but we need to make sure we got correct administrative if ($GPOPermission.Permission -eq $PermissionType) { $AdministrativeGroup = $ADAdministrativeGroups['BySID'][$GPOPermission.PrincipalSid] if ($AdministrativeGroup.SID -like '*-519') { $AdministrativeExists['EnterpriseAdmins'] = $true } elseif ($AdministrativeGroup.SID -like '*-512') { $AdministrativeExists['DomainAdmins'] = $true } } if ($AdministrativeExists['DomainAdmins'] -and $AdministrativeExists['EnterpriseAdmins']) { $Skip = $true break } } elseif ($Type -eq 'WellKnownAdministrative') { # this is for SYSTEM account $Skip = $true break } elseif ($Type -eq 'AuthenticatedUsers') { # this is for Authenticated Users $Skip = $true break } } } #> if (-not $PermissionsAnalysis['Skip']) { if (-not $GPOPermissions) { # This is bad - things went wrong Write-Warning "Add-GPOZaurrPermission - Couldn't get permissions for GPO. Things aren't what they should be. Skipping!" } else { $GPO = $GPOPermissions[0] if ($GPOPermissions.GPOSecurityPermissionItem) { # We asked, we got response, now we need to check if maybe we're missing one of the two administrative groups if ($Type -eq 'Administrative') { # this is a case where something was returned. Be it Domain Admins or Enterprise Admins or both. But we still need to check because it may have been Domain Admins from other domain or just one of the two required groups if ($PermissionsAnalysis['DomainAdmins'] -eq $false) { $Principal = $ADAdministrativeGroups[$GPO.DomainName]['DomainAdmins'] Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } if ($PermissionsAnalysis['EnterpriseAdmins'] -eq $false) { $Principal = $ADAdministrativeGroups[$ForestInformation.Forest.RootDomain]['EnterpriseAdmins'] Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } } elseif ($Type -eq 'Default') { # This shouldn't really happen, as if we got response, and it didn't exists it wouldn't be here Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType skipped for $($Principal). This shouldn't even happen!" } } else { # We got no response. That means we either asked incorrectly or we need to fix permission. Trying to do so if ($Type -eq 'Default') { Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PrincipalType -eq 'DistinguishedName') { $ADIdentity = Get-WinADObject -Identity $Principal if ($ADIdentity) { Write-Verbose "Add-GPOZaurrPermission - Need to convert DN $Principal to SID $($ADIdentity.ObjectSID) to $($GPO.DisplayName) at $($GPO.DomainName)" $Principal = $ADIdentity.ObjectSID } } if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal)" $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } elseif ($Type -eq 'Administrative') { # this is a case where both Domain Admins/Enterprise Admins were missing $Principal = $ADAdministrativeGroups[$GPO.DomainName]['DomainAdmins'] Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } $Principal = $ADAdministrativeGroups[$ForestInformation.Forest.RootDomain]['EnterpriseAdmins'] Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)" } } } elseif ($Type -eq 'WellKnownAdministrative') { $Principal = 'S-1-5-18' Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal (SYSTEM) / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) (SYSTEM) with error: $($_.Exception.Message)" } } } elseif ($Type -eq 'AuthenticatedUsers') { $Principal = 'S-1-5-11' Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)" $CountFixed++ if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal (Authenticated Users) / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) { try { $AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent) $GPO.GPOSecurity.Add($AddPermission) $GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity) } catch { Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) (Authenticated Users) with error: $($_.Exception.Message)" } } } } } } if ($CountFixed -ge $LimitProcessing) { # We want to exit foreach-object, but ForEach-Object doesn't really allow that # that's why there is Do/While which will make sure that breaks doesn't break what it's not supposed to break } } } while ($false) } function Backup-GPOZaurr { [cmdletBinding(SupportsShouldProcess)] param( [int] $LimitProcessing, [validateset('All', 'Empty', 'Unlinked')][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 { if ($Type -contains 'All') { Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName)" $Count++ try { $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.DomainName -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer $BackupInfo } catch { Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" } if ($LimitProcessing -eq $Count) { break } } if ($Type -notcontains 'All' -and $Type -contains 'Empty') { if ($_.ComputerSettingsAvailable -eq $false -and $_.UserSettingsAvailable -eq $false) { Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName)" $Count++ try { $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.DomainName -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer $BackupInfo } catch { Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" } if ($LimitProcessing -eq $Count) { break } } } if ($Type -notcontains 'All' -and $Type -contains 'Unlinked') { if ($_.Linked -eq $false) { Write-Verbose "Backup-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName)" $Count++ try { $BackupInfo = Backup-GPO -Guid $_.GUID -Domain $_.DomainName -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer $BackupInfo } catch { Write-Warning "Backup-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" } if ($LimitProcessing -eq $Count) { break } } } } } End { } } function Clear-GPOZaurrSysvolDFSR { [cmdletBinding(SupportsShouldProcess)] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [string[]] $ExcludeDomainControllers, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [alias('DomainControllers')][string[]] $IncludeDomainControllers, [switch] $SkipRODC, [System.Collections.IDictionary] $ExtendedForestInformation, [int] $LimitProcessing = [int32]::MaxValue ) # Based on https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/manually-clearing-the-conflictanddeleted-folder-in-dfsr/ba-p/395711 $StatusCodes = @{ '0' = 'Success' # MONITOR_STATUS_SUCCESS '1' = 'Generic database error' #MONITOR_STATUS_GENERIC_DB_ERROR '2' = 'ID record not found' # MONITOR_STATUS_IDRECORD_NOT_FOUND '3' = 'Volume not found' # MONITOR_STATUS_VOLUME_NOT_FOUND '4' = 'Access denied' #MONITOR_STATUS_ACCESS_DENIED '5' = 'Generic error' #MONITOR_STATUS_GENERIC_ERROR } #WMIC.EXE /namespace:\\root\microsoftdfs path dfsrreplicatedfolderconfig get replicatedfolderguid, replicatedfoldername #WMIC.EXE /namespace:\\root\microsoftdfs path dfsrreplicatedfolderinfo where "replicatedfolderguid='<RF GUID>'" call cleanupconflictdirectory #WMIC.EXE /namespace:\\root\microsoftdfs path dfsrreplicatedfolderinfo where "replicatedfolderguid='70bebd41-d5ae-4524-b7df-4eadb89e511e'" call cleanupconflictdirectory # https://docs.microsoft.com/en-us/previous-versions/windows/desktop/dfsr/dfsrreplicatedfolderinfo $getGPOZaurrSysvolDFSRSplat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation ExcludeDomainControllers = $ExcludeDomainControllers IncludeDomainControllers = $IncludeDomainControllers SkipRODC = $SkipRODC } Get-GPOZaurrSysvolDFSR @getGPOZaurrSysvolDFSRSplat | Select-Object -First $LimitProcessing | ForEach-Object { $Executed = Invoke-CimMethod -InputObject $_.DFSR -MethodName 'cleanupconflictdirectory' -CimSession $_.ComputerName if ($Executed) { [PSCustomObject] @{ Status = $StatusCodes["$($Executed.ReturnValue)"] ComputerName = $Executed.PSComputerName } } } } function ConvertFrom-CSExtension { [cmdletBinding()] param( [string[]] $CSE, [switch] $Limited ) $GUIDs = @{ # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpreg/f0dba6b8-704f-45d5-999f-1a0a694a6df9 '{35378EAC-683F-11D2-A89A-00C04FBBCFA2}' = 'Client-side extension GUID (CSE GUID)' '{0F6B957E-509E-11D1-A7CC-0000F87571E3}' = 'Tool Extension GUID (User Policy Settings)' '{D02B1F73-3407-48AE-BA88-E8213C6761F1}' = 'Tool Extension GUID (User Policy Settings)' '{0F6B957D-509E-11D1-A7CC-0000F87571E3}' = 'Tool Extension GUID (Computer Policy Settings)' '{D02B1F72-3407-48AE-BA88-E8213C6761F1}' = 'Tool Extension GUID (Computer Policy Settings)' '{0ACDD40C-75AC-47ab-BAA0-BF6DE7E7FE63}' = 'Wireless Group Policy' '{0E28E245-9368-4853-AD84-6DA3BA35BB75}' = 'Group Policy Environment' '{16be69fa-4209-4250-88cb-716cf41954e0}' = 'Central Access Policy Configuration' '{17D89FEC-5C44-4972-B12D-241CAEF74509}' = 'Group Policy Local Users and Groups' '{1A6364EB-776B-4120-ADE1-B63A406A76B5}' = 'Group Policy Device Settings' '{25537BA6-77A8-11D2-9B6C-0000F8080861}' = 'Folder Redirection' '{2A8FDC61-2347-4C87-92F6-B05EB91A201A}' = 'MitigationOptions' '{346193F5-F2FD-4DBD-860C-B88843475FD3}' = 'ConfigMgr User State Management Extension.' '{3610eda5-77ef-11d2-8dc5-00c04fa31a66}' = 'Microsoft Disk Quota' '{3A0DBA37-F8B2-4356-83DE-3E90BD5C261F}' = 'Group Policy Network Options' '{426031c0-0b47-4852-b0ca-ac3d37bfcb39}' = 'QoS Packet Scheduler' '{42B5FAAE-6536-11d2-AE5A-0000F87571E3}' = 'Scripts' '{4bcd6cde-777b-48b6-9804-43568e23545d}' = 'Remote Desktop USB Redirection' '{4CFB60C1-FAA6-47f1-89AA-0B18730C9FD3}' = 'Internet Explorer Zonemapping' '{4D2F9B6F-1E52-4711-A382-6A8B1A003DE6}' = 'RADCProcessGroupPolicyEx' '{4d968b55-cac2-4ff5-983f-0a54603781a3}' = 'Work Folders' '{5794DAFD-BE60-433f-88A2-1A31939AC01F}' = 'Group Policy Drive Maps' '{6232C319-91AC-4931-9385-E70C2B099F0E}' = 'Group Policy Folders' '{6A4C88C6-C502-4f74-8F60-2CB23EDC24E2}' = 'Group Policy Network Shares' '{7150F9BF-48AD-4da4-A49C-29EF4A8369BA}' = 'Group Policy Files' '{728EE579-943C-4519-9EF7-AB56765798ED}' = 'Group Policy Data Sources' '{74EE6C03-5363-4554-B161-627540339CAB}' = 'Group Policy Ini Files' '{7933F41E-56F8-41d6-A31C-4148A711EE93}' = 'Windows Search Group Policy Extension' '{7B849a69-220F-451E-B3FE-2CB811AF94AE}' = 'Internet Explorer User Accelerators' '{827D319E-6EAC-11D2-A4EA-00C04F79F83A}' = 'Security' '{8A28E2C5-8D06-49A4-A08C-632DAA493E17}' = 'Deployed Printer Connections' '{91FBB303-0CD5-4055-BF42-E512A681B325}' = 'Group Policy Services' '{A3F3E39B-5D83-4940-B954-28315B82F0A8}' = 'Group Policy Folder Options' '{AADCED64-746C-4633-A97C-D61349046527}' = 'Group Policy Scheduled Tasks' '{B087BE9D-ED37-454f-AF9C-04291E351182}' = 'Group Policy Registry' '{B587E2B1-4D59-4e7e-AED9-22B9DF11D053}' = '802.3 Group Policy' '{BA649533-0AAC-4E04-B9BC-4DBAE0325B12}' = 'Windows To Go Startup Options' '{BC75B1ED-5833-4858-9BB8-CBF0B166DF9D}' = 'Group Policy Printers' '{C34B2751-1CF4-44F5-9262-C3FC39666591}' = 'Windows To Go Hibernate Options' '{C418DD9D-0D14-4efb-8FBF-CFE535C8FAC7}' = 'Group Policy Shortcuts' '{C631DF4C-088F-4156-B058-4375F0853CD8}' = 'Microsoft Offline Files' '{c6dc5466-785a-11d2-84d0-00c04fb169f7}' = 'Software Installation' '{cdeafc3d-948d-49dd-ab12-e578ba4af7aa}' = 'TCPIP' '{CF7639F3-ABA2-41DB-97F2-81E2C5DBFC5D}' = 'Internet Explorer Machine Accelerators' '{e437bc1c-aa7d-11d2-a382-00c04f991e27}' = 'IP Security' '{E47248BA-94CC-49c4-BBB5-9EB7F05183D0}' = 'Group Policy Internet Settings' '{E4F48E54-F38D-4884-BFB9-D4D2E5729C18}' = 'Group Policy Start Menu Settings' '{E5094040-C46C-4115-B030-04FB2E545B00}' = 'Group Policy Regional Options' '{E62688F0-25FD-4c90-BFF5-F508B9D2E31F}' = 'Group Policy Power Options' '{F312195E-3D9D-447A-A3F5-08DFFA24735E}' = 'ProcessVirtualizationBasedSecurityGroupPolicy' '{f3ccc681-b74c-4060-9f26-cd84525dca2a}' = 'Audit Policy Configuration' '{F9C77450-3A41-477E-9310-9ACD617BD9E3}' = 'Group Policy Applications' '{FB2CA36D-0B40-4307-821B-A13B252DE56C}' = 'Enterprise QoS' '{fbf687e6-f063-4d9f-9f4f-fd9a26acdd5f}' = 'CP' '{FC491EF1-C4AA-4CE1-B329-414B101DB823}' = 'ProcessConfigCIPolicyGroupPolicy' '{169EBF44-942F-4C43-87CE-13C93996EBBE}' = 'UEV Policy' '{2BFCC077-22D2-48DE-BDE1-2F618D9B476D}' = 'AppV Policy' '{4B7C3B0F-E993-4E06-A241-3FBE06943684}' = 'Per-process Mitigation Options' '{7909AD9E-09EE-4247-BAB9-7029D5F0A278}' = 'MDM Policy' '{CFF649BD-601D-4361-AD3D-0FC365DB4DB7}' = 'Delivery Optimization GP extension' '{D76B9641-3288-4f75-942D-087DE603E3EA}' = 'AdmPwd' '{9650FDBC-053A-4715-AD14-FC2DC65E8330}' = 'Unknown' '{B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A}' = 'EFS Recovery' '{A2E30F80-D7DE-11d2-BBDE-00C04F86AE3B}' = 'Internet Explorer Maintenance Policy Processing' '{FC715823-C5FB-11D1-9EEF-00A0C90347FF}' = 'Internet Explorer Maintenance Extension Protocol' # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpie/f566a58a-4114-4981-b1e2-30b9d1a3c0e6 } foreach ($C in $CSE) { if (-not $Limited) { if ($GUIDs[$C]) { [PSCustomObject] @{ Name = $C; Description = $GUIDs[$C] } } else { [PSCustomObject] @{ Name = $C; Description = $C } } } else { if ($GUIDs[$C]) { $GUIDs[$C] } else { $CSE } } } } function Find-CSExtension { [cmdletBinding()] param( [string[]] $CSE, [string] $ComputerName ) #List Group Policy Client Side Extensions, CSEs, from Windows 10 $Keys = Get-PSRegistry -RegistryPath "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions" -ComputerName $ComputerName foreach ($Key in $Keys.PSSubKeys) { $RegistryKey = Get-PSRegistry -RegistryPath "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\$Key" -ComputerName $ComputerName if ($CSE) { foreach ($C in $CSE) { if ($RegistryKey.DefaultKey -eq $Key) { [PSCustomObject] @{ Name = $Key; Description = $RegistryKey.DefaultKey } } } } else { [PSCustomObject] @{ CSE = $Key; Description = $RegistryKey.DefaultKey } } } } function Get-GPOZaurr { [cmdletBinding()] param( [string] $GPOName, [alias('GUID', 'GPOID')][string] $GPOGuid, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [string[]] $GPOPath, [switch] $PermissionsOnly, [switch] $OwnerOnly, [switch] $Limited, [switch] $ReturnObject, [System.Collections.IDictionary] $ADAdministrativeGroups ) Begin { if (-not $ADAdministrativeGroups) { Write-Verbose "Get-GPOZaurr - Getting ADAdministrativeGroups" $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } if (-not $GPOPath) { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } } Process { if (-not $GPOPath) { foreach ($Domain in $ForestInformation.Domains) { $QueryServer = $ForestInformation.QueryServers[$Domain]['HostName'][0] $Count = 0 if ($GPOName) { $GroupPolicies = Get-GPO -Name $GPOName -Domain $Domain -Server $QueryServer -ErrorAction SilentlyContinue $GroupPolicies | ForEach-Object { $Count++ #Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain" Write-Verbose "Get-GPOZaurr - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)" if (-not $Limited) { try { $XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain -ErrorAction Stop } catch { Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping." continue } Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent } else { $_ } } } elseif ($GPOGuid) { $GroupPolicies = Get-GPO -Guid $GPOGuid -Domain $Domain -Server $QueryServer -ErrorAction SilentlyContinue $GroupPolicies | ForEach-Object { $Count++ #Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain" Write-Verbose "Get-GPOZaurr - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)" if (-not $Limited) { try { $XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain -ErrorAction Stop } catch { Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping." continue } Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent } else { $_ } } } else { $GroupPolicies = Get-GPO -All -Server $QueryServer -Domain $Domain -ErrorAction SilentlyContinue $GroupPolicies | ForEach-Object { $Count++ #Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain" Write-Verbose "Get-GPOZaurr - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)" if (-not $Limited) { try { $XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain -ErrorAction Stop } catch { Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping." continue } Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent } else { $_ } } } } } else { foreach ($Path in $GPOPath) { Write-Verbose "Get-GPOZaurr - Getting GPO content from XML files" Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml | ForEach-Object { $XMLContent = [XML]::new() $XMLContent.Load($_.FullName) Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -PermissionsOnly:$PermissionsOnly.IsPresent } Write-Verbose "Get-GPOZaurr - Finished GPO content from XML files" } } } End { } } function Get-GPOZaurrAD { [cmdletbinding(DefaultParameterSetName = 'Default')] param( [Parameter(ParameterSetName = 'GPOName')] [string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID')] [alias('GUID', 'GPOID')][string] $GPOGuid, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) Begin { $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } Process { foreach ($Domain in $ForestInformation.Domains) { if ($PSCmdlet.ParameterSetName -eq 'GPOGUID') { if ($GPOGuid) { if ($GPOGUID -notlike '*{*') { $GUID = -join ("{", $GPOGUID, '}') } else { $GUID = $GPOGUID } $Splat = @{ Filter = "(objectClass -eq 'groupPolicyContainer') -and (Name -eq '$GUID')" Server = $ForestInformation['QueryServers'][$Domain]['HostName'][0] } } else { Write-Warning "Get-GPOZaurrAD - GPOGUID parameter is empty. Provide name and try again." continue } } elseif ($PSCmdlet.ParameterSetName -eq 'GPOName') { if ($GPOName) { $Splat = @{ Filter = "(objectClass -eq 'groupPolicyContainer') -and (DisplayName -eq '$GPOName')" Server = $ForestInformation['QueryServers'][$Domain]['HostName'][0] } } else { Write-Warning "Get-GPOZaurrAD - GPOName parameter is empty. Provide name and try again." continue } } else { $Splat = @{ Filter = "(objectClass -eq 'groupPolicyContainer')" Server = $ForestInformation['QueryServers'][$Domain]['HostName'][0] } } Get-ADObject @Splat -Properties DisplayName, Name, Created, Modified, gPCFileSysPath, gPCFunctionalityVersion, gPCWQLFilter, gPCMachineExtensionNames, Description, CanonicalName, DistinguishedName | ForEach-Object -Process { $DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $_.DistinguishedName -ToDomainCN $GUID = $_.Name -replace '{' -replace '}' if (($GUID).Length -ne 36) { Write-Warning "Get-GPOZaurrAD - GPO GUID ($($($GUID.Replace("`n",' ')))) is incorrect. Skipping $($_.DisplayName) / Domain: $($DomainCN)" } else { $Output = [ordered]@{ } $Output['DisplayName'] = $_.DisplayName $Output['DomainName'] = $DomainCN $Output['Description'] = $_.Description $Output['GUID'] = $GUID $Output['Path'] = $_.gPCFileSysPath $Output['FunctionalityVersion'] = $_.gPCFunctionalityVersion $Output['Created'] = $_.Created $Output['Modified'] = $_.Modified $Output['GPOCanonicalName'] = $_.CanonicalName $Output['GPODomainDistinguishedName'] = ConvertFrom-DistinguishedName -DistinguishedName $_.DistinguishedName -ToDC $Output['GPODistinguishedName'] = $_.DistinguishedName [PSCustomObject] $Output } } } } 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 -Process { [PSCustomObject] @{ DisplayName = $_.GPODisplayName.'#cdata-section' DomainName = $_.GPODomain.'#cdata-section' Guid = $_.GPOGUid.'#cdata-section' -replace '{' -replace '}' DomainGuid = $_.GPODomainGuid.'#cdata-section' -replace '{' -replace '}' DomainController = $_.GPODomainController.'#cdata-section' BackupTime = [DateTime]::Parse($_.BackupTime.'#cdata-section') ID = $_.ID.'#cdata-section' -replace '{' -replace '}' Comment = $_.Comment.'#cdata-section' } } } else { Write-Warning "Get-GPOZaurrBackupInformation - No backup information available" } } } } End { } } function Get-GPOZaurrBroken { [alias('Get-GPOZaurrSysvol')] [cmdletBinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [string[]] $ExcludeDomainControllers, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [alias('DomainControllers')][string[]] $IncludeDomainControllers, [switch] $SkipRODC, [Array] $GPOs, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $VerifyDomainControllers ) $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation -Extended foreach ($Domain in $ForestInformation.Domains) { $TimeLog = Start-TimeLog Write-Verbose "Get-GPOZaurrBroken - Starting process for $Domain" $QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0] $SystemsContainer = $ForestInformation['DomainsExtended'][$Domain].SystemsContainer $PoliciesAD = @{} if ($SystemsContainer) { $PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer) $PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer foreach ($Policy in $PoliciesInAD) { $GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName $GUIDFromDN = $GUIDFromDN -replace '{' -replace '}' $GUID = $Policy.Name -replace '{' -replace '}' if ($GUID -and $GUIDFromDN) { $PoliciesAD[$GUIDFromDN] = 'Exists' } else { $PoliciesAD[$GUIDFromDN] = 'Permissions issue' } } } Try { [Array]$GPOs = Get-GPO -All -Domain $Domain -Server $QueryServer } catch { Write-Warning "Get-GPOZaurrBroken - Couldn't get GPOs from $Domain. Error: $($_.Exception.Message)" continue } if ($GPOs.Count -ge 2) { if (-not $VerifyDomainControllers) { Test-SysVolFolders -GPOs $GPOs -Server $Domain -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase } else { foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) { Write-Verbose "Get-GPOZaurrBroken - Processing $Domain \ $($Server.HostName.Trim())" Test-SysVolFolders -GPOs $GPOs -Server $Server.Hostname -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase } } } else { Write-Warning "Get-GPOZaurrBroken - GPO count for $Domain is less then 2. This is not expected for fully functioning domain. Skipping processing SYSVOL folder." } $TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner Write-Verbose "Get-GPOZaurrBroken - Finishing process for $Domain (Time to process: $TimeEnd)" } } function Get-GPOZaurrDictionary { [cmdletBinding()] param( [string] $Splitter = [System.Environment]::NewLine ) foreach ($Policy in $Script:GPODitionary.Keys) { if ($Script:GPODitionary[$Policy].ByReports) { [Array] $Type = foreach ($T in $Script:GPODitionary[$Policy].ByReports ) { $T.Report } } else { [Array]$Type = foreach ($T in $Script:GPODitionary[$Policy].Types) { ( -join ($T.Category, '/', $T.Settings)) } } [PSCustomObject] @{ Name = $Policy Types = $Type -join $Splitter Path = $Script:GPODitionary[$Policy].GPOPath -join $Splitter #Details = $Script:GPODitionary[$Policy] } } } function Get-GPOZaurrDuplicateObject { [cmdletBinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $getWinADDuplicateObjectSplat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation PartialMatchDistinguishedName = "*,CN=Policies,CN=System,DC=*" Extended = $true } $DuplicateObjects = Get-WinADDuplicateObject @getWinADDuplicateObjectSplat $DuplicateObjects } function Get-GPOZaurrFiles { [cmdletbinding()] param( [ValidateSet('All', 'Netlogon', 'Sysvol')][string[]] $Type = 'All', [ValidateSet('None', 'MACTripleDES', 'MD5', 'RIPEMD160', 'SHA1', 'SHA256', 'SHA384', 'SHA512')][string] $HashAlgorithm = 'None', [switch] $Signature, [switch] $AsHashTable, [switch] $Extended, [switch] $ExtendedMetaData, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $GPOCache = @{} $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation $GPOList = Get-GPOZaurrAD -ExtendedForestInformation $ForestInformation foreach ($GPO in $GPOList) { if (-not $GPOCache[$GPO.DomainName]) { $GPOCache[$GPO.DomainName] = @{} } $GPOCache[$($GPO.DomainName)][($GPO.GUID)] = $GPO } foreach ($Domain in $ForestInformation.Domains) { $Path = @( if ($Type -contains 'All') { "\\$Domain\SYSVOL\$Domain" } if ($Type -contains 'Sysvol') { "\\$Domain\SYSVOL\$Domain\policies" } if ($Type -contains 'NetLogon') { "\\$Domain\NETLOGON" } ) # Order does matter $Folders = [ordered] @{ "\\$Domain\SYSVOL\$Domain\policies\PolicyDefinitions" = @{ Name = 'SYSVOL PolicyDefinitions' } "\\$Domain\SYSVOL\$Domain\policies" = @{ Name = 'SYSVOL Policies' } "\\$Domain\SYSVOL\$Domain\scripts" = @{ Name = 'NETLOGON Scripts' } "\\$Domain\SYSVOL\$Domain\StarterGPOs" = @{ Name = 'SYSVOL GPO Starters' } "\\$Domain\NETLOGON" = @{ Name = 'NETLOGON Scripts' } "\\$Domain\SYSVOL\$Domain\DfsrPrivate" = @{ Name = 'DfsrPrivate' } "\\$Domain\SYSVOL\$Domain" = @{ Name = 'SYSVOL Root' } } Get-ChildItem -Path $Path -ErrorAction SilentlyContinue -Recurse -ErrorVariable err -File -Force | ForEach-Object { # Lets reset values just to be sure those are empty $GPO = $null $BelongsToGPO = $false $GPODisplayName = $null $SuggestedAction = $null $SuggestedActionComment = $null $FileType = foreach ($Key in $Folders.Keys) { if ($_.FullName -like "$Key*") { $Folders[$Key] break } } if ($FileType.Name -eq 'SYSVOL Policies') { $FoundGUID = $_.FullName -match '[\da-zA-Z]{8}-([\da-zA-Z]{4}-){3}[\da-zA-Z]{12}' if ($FoundGUID) { $GPO = $GPOCache[$Domain][$matches[0]] if ($GPO) { $BelongsToGPO = $true $GPODisplayName = $GPO.DisplayName } } $Correct = @( [System.IO.Path]::Combine($GPO.Path, 'GPT.INI') [System.IO.Path]::Combine($GPO.Path, 'GPO.cmt') [System.IO.Path]::Combine($GPO.Path, 'Group Policy', 'GPE.ini') foreach ($TypeM in @('Machine', 'User')) { [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Registry.pol') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'comment.cmtx') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Registry\Registry.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Printers\Printers.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\ScheduledTasks\ScheduledTasks.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Services\Services.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Groups\Groups.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\RegionalOptions\RegionalOptions.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\FolderOptions\FolderOptions.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Drives\Drives.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\InternetSettings\InternetSettings.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Folders\Folders.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\PowerOptions\PowerOptions.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Shortcuts\Shortcuts.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\Files\Files.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\EnvironmentVariables\EnvironmentVariables.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\NetworkOptions\NetworkOptions.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\DataSources\DataSources.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\NetworkShares\NetworkShares.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Preferences\StartMenuTaskbar\StartMenuTaskbar.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Applications\Microsoft\TBLayout.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Applications\Microsoft\DefaultApps.xml') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Applications\ADE.CFG') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Scripts\scripts.ini') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Scripts\psscripts.ini') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Documents & Settings\fdeploy.ini') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Documents & Settings\fdeploy1.ini') [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Documents & Settings\fdeploy2.ini') if ($_.Extension -eq '.aas') { [System.IO.Path]::Combine($GPO.Path, $TypeM, 'Applications', $_.Name) } } [System.IO.Path]::Combine($GPO.Path, 'Machine\Microsoft\Windows NT\SecEdit\GptTmpl.inf') [System.IO.Path]::Combine($GPO.Path, 'Machine\Microsoft\Windows NT\Audit\audit.csv') ) if ($GPO) { if ($_.FullName -in $Correct) { $SuggestedAction = 'Skip assesment' $SuggestedActionComment = 'Correctly placed in SYSVOL' } elseif ($_.FullName -like '*_NTFRS_*') { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely replication error' } elseif ($_.Extension -eq '.adm') { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely legacy ADM files' } elseif ($_.Name -eq 'Thumbs.db') { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely database files to store image thumbnails on Windows systems.' } if (-not $SuggestedAction) { $FullPathAdmFiles = [System.IO.Path]::Combine($GPO.Path, 'Adm\admfiles.ini') if ($_.FullName -eq $FullPathAdmFiles) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely legacy ADM files settings file' } } if (-not $SuggestedAction) { foreach ($Ext in @('*old*', '*bak*', '*bck', '.new')) { if ($_.Extension -like $Ext) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely backup files' break } } } if (-not $SuggestedAction) { <# $IEAK = @( 'microsoft\IEAK\install.ins' 'MICROSOFT\IEAK\BRANDING\cs\connect.ras' 'microsoft\IEAK\BRANDING\cs\connect.set' 'microsoft\IEAK\BRANDING\cs\cs.dat' 'microsoft\IEAK\BRANDING\ADM\inetcorp.iem' 'microsoft\IEAK\BRANDING\ADM\inetcorp.inf' 'microsoft\IEAK\install.ins' 'microsoft\IEAK\BRANDING\favs\Outlook.ico' 'microsoft\IEAK\BRANDING\favs\Bio.ico' 'MICROSOFT\IEAK\BRANDING\favs\$fi380.ico' 'microsoft\IEAK\BRANDING\PROGRAMS\programs.inf' 'MICROSOFT\IEAK\BRANDING\RATINGS\ratings.inf' 'MICROSOFT\IEAK\BRANDING\RATINGS\ratrsop.inf' 'microsoft\IEAK\BRANDING\ZONES\seczones.inf' 'microsoft\IEAK\BRANDING\ZONES\seczrsop.inf' 'microsoft\IEAK\BRANDING\ZONES\seczrsop.inf' ) #> if ($_.FullName -like '*microsoft\IEAK*') { # https://docs.microsoft.com/en-us/internet-explorer/ie11-deploy-guide/missing-internet-explorer-maintenance-settings-for-ie11#:~:text=The%20Internet%20Explorer%20Maintenance%20(IEM,Internet%20Explorer%2010%20or%20newer. $SuggestedAction = 'GPO requires cleanup' $SuggestedActionComment = 'Internet Explorer Maintenance (IEM) is deprecated for IE 11' } } } else { <# $FullPathAdmFiles = [System.IO.Path]::Combine($GPO.Path, 'Adm\admfiles.ini') if ($_.FullName -in $Correct) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely orphaned SYSVOL GPO' } elseif ($_.Extension -eq '.adm') { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely orphaned SYSVOL GPO (legacy ADM files)' } elseif ($_.FullName -eq $FullPathAdmFiles) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely orphaned SYSVOL GPO (legacy ADM files)' } #> $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely orphaned SYSVOL GPO' } } elseif ($FileType.Name -eq 'NETLOGON Scripts') { foreach ($Ext in @('*old*', '*bak*', '*bck', '.new')) { if ($_.Extension -like $Ext) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely backup files' break } } if (-not $SuggestedAction) { # We didn't find it in earlier check, lets go deeper if ($_.Extension.Length -gt 6 -and $_.Extension -notin @('.config', '.sites', '.ipsec')) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Extension longer then 5 chars' } elseif ($_.Extension -eq '') { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'No extension' } } if (-not $SuggestedAction) { foreach ($Name in @('*old*', '*bak*', '*bck*', '*Copy', '*backup*')) { if ($_.BaseName -like $Name) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = "FileName contains backup related names ($Name)" break } } } if (-not $SuggestedAction) { if ($_.Name -eq 'Thumbs.db') { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'Most likely database files to store image thumbnails on Windows systems.' } } if (-not $SuggestedAction) { foreach ($FullName in @('*backup*', '*Delete*', '*Obsoleet*', '*Obsolete*', '*Archive*')) { if ($_.FullName -like $FullName) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = "Fullname contains backup related names ($FullName)" break } } } if (-not $SuggestedAction) { # We replace all letters leaving only numbers # We want to find if there is a date possibly $StrippedNumbers = $_.Name -replace "[^0-9]" , '' if ($StrippedNumbers.Length -gt 5) { $SuggestedAction = 'Consider deleting' $SuggestedActionComment = 'FileName contains over 5 numbers (date?)' } } } elseif ($FileType.Name -eq 'SYSVOL PolicyDefinitions') { if ($_.Extension -in @('.admx', '.adml')) { $SuggestedAction = 'Skip assesment' $SuggestedActionComment = 'Most likely ADMX templates' } } elseif ($FileType.Name -eq 'SYSVOL GPO Starters') { $FoundGUID = $_.FullName -match '[\da-zA-Z]{8}-([\da-zA-Z]{4}-){3}[\da-zA-Z]{12}' if ($FoundGUID) { $GUID = $matches[0] $TemporaryStarterPath = "\\$Domain\SYSVOL\$Domain\StarterGPOs\{$GUID}" $Correct = @( [System.IO.Path]::Combine($TemporaryStarterPath, 'StarterGPO.tmplx') [System.IO.Path]::Combine($TemporaryStarterPath, 'en-US', 'StarterGPO.tmpll') foreach ($TypeM in @('Machine', 'User')) { [System.IO.Path]::Combine($TemporaryStarterPath, $TypeM, 'Registry.pol') [System.IO.Path]::Combine($TemporaryStarterPath, $TypeM, 'comment.cmtx') } ) if ($_.FullName -in $Correct) { $SuggestedAction = 'Skip assesment' $SuggestedActionComment = 'Correctly placed in SYSVOL' } } } else { } if (-not $SuggestedAction) { $SuggestedAction = 'Requires verification' $SuggestedActionComment = 'Not able to auto asses' } if (-not $ExtendedMetaData) { $MetaData = [ordered] @{ LocationType = $FileType.Name FullName = $_.FullName #Name = $_.Name Extension = $_.Extension SuggestedAction = $SuggestedAction SuggestedActionComment = $SuggestedActionComment BelongsToGPO = $BelongsToGPO GPODisplayName = $GPODisplayName Attributes = $_.Attributes CreationTime = $_.CreationTime LastAccessTime = $_.LastAccessTime LastWriteTime = $_.LastWriteTime } } else { $MetaData = Get-FileMetaData -File $_ -AsHashTable $MetaData['SuggestedAction'] = $SuggestedAction $MetaData['SuggestedActionComment'] = $SuggestedActionComment $MetaData['BelongsToGPO'] = $BelongsToGPO $MetaData['GPODisplayName'] = $GPODisplayName } if ($Signature) { try { $DigitalSignature = Get-AuthenticodeSignature -FilePath $_.Fullname -ErrorAction Stop } catch { Write-Warning "Get-GPOZaurrFiles - Error when reading signature: $($_.Exception.Message)" } if ($DigitalSignature) { $MetaData['SignatureStatus'] = $DigitalSignature.Status $MetaData['IsOSBinary'] = $DigitalSignature.IsOSBinary $MetaData['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject if ($Extended) { $MetaData['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer $MetaData['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber $MetaData['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore $MetaData['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter $MetaData['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint } } else { $MetaData['SignatureStatus'] = 'Not available' $MetaData['IsOSBinary'] = $null $MetaData['SignatureCertificateSubject'] = $null if ($Extended) { $MetaData['SignatureCertificateIssuer'] = $null $MetaData['SignatureCertificateSerialNumber'] = $null $MetaData['SignatureCertificateNotBefore'] = $null $MetaData['SignatureCertificateNotAfter'] = $null $MetaData['SignatureCertificateThumbprint'] = $null } } } if ($HashAlgorithm -ne 'None') { $MetaData['ChecksumSHA256'] = (Get-FileHash -LiteralPath $_.FullName -Algorithm $HashAlgorithm).Hash } if ($AsHashTable) { $MetaData } else { [PSCustomObject] $MetaData } } foreach ($e in $err) { Write-Warning "Get-GPOZaurrFiles - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } } } function Get-GPOZaurrFilesPolicyDefinition { [alias('Get-GPOZaurrFilesPolicyDefinitions')] [cmdletbinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $Signature ) $Output = [ordered] @{ FilesToDelete = [System.Collections.Generic.List[Object]]::new() } $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation $FilesCache = @{} foreach ($Domain in $ForestInformation.Domains) { $Output[$Domain] = [ordered] @{} $FilesCache[$Domain] = [ordered] @{} $Directories = Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies\PolicyDefinitions" -Directory -ErrorAction SilentlyContinue -ErrorVariable err [Array] $Languages = foreach ($Directory in $Directories) { if ($Directory.BaseName.Length -eq 5) { $Directory.BaseName } } $Files = Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies\PolicyDefinitions" -ErrorAction SilentlyContinue -ErrorVariable +err -File #| Select-Object Name, FullName, CreationTime, LastWriteTime, Attributes foreach ($File in $Files) { $FilesCache[$Domain][$($File.BaseName)] = [ordered] @{ Name = $File.BaseName FullName = $File.FullName IsReadOnly = $File.IsReadOnly CreationTime = $File.CreationTime LastAccessTime = $File.LastAccessTime LastWriteTime = $File.LastWriteTime IsConsistent = $false } foreach ($Language in $Languages) { $FilesCache[$Domain][$($File.BaseName)][$Language] = $false } if ($Signature) { $DigitalSignature = Get-AuthenticodeSignature -FilePath $File.FullName $FilesCache[$Domain][$($File.BaseName)]['SignatureStatus'] = $DigitalSignature.Status $FilesCache[$Domain][$($File.BaseName)]['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject $FilesCache[$Domain][$($File.BaseName)]['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer $FilesCache[$Domain][$($File.BaseName)]['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber $FilesCache[$Domain][$($File.BaseName)]['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore $FilesCache[$Domain][$($File.BaseName)]['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter $FilesCache[$Domain][$($File.BaseName)]['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint $FilesCache[$Domain][$($File.BaseName)]['IsOSBinary'] = $DigitalSignature.IsOSBinary } } foreach ($Directory in $Directories) { $FilesLanguage = Get-ChildItem -Path $Directory.FullName -ErrorAction SilentlyContinue -ErrorVariable +err foreach ($FileLanguage in $FilesLanguage) { if ($FileLanguage.Extension -eq '.adml') { if ($FilesCache[$Domain][$FileLanguage.BaseName]) { $FilesCache[$Domain][$FileLanguage.BaseName][$Directory.Name] = $true } else { #Write-Warning "Get-GPOZaurrFilesPolicyDefinition - File $($FileLanguage.FullName) doesn't have a match." $Output.FilesToDelete.Add( [PSCustomobject] @{ Name = $FileLanguage.BaseName FullName = $FileLanguage.FullName IsReadOnly = $FileLanguage.IsReadOnly CreationTime = $FileLanguage.CreationTime LastAccessTime = $FileLanguage.LastAccessTime LastWriteTime = $FileLanguage.LastWriteTime } ) } } else { } } } foreach ($e in $err) { Write-Warning "Get-GPOZaurrLegacy - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } $ExcludeProperty = @( 'Name', 'FullName', 'IsReadOnly', 'CreationTime', 'LastAccessTime', 'LastWriteTime', 'IsConsistent' 'SignatureCertificateSubject', 'SignatureCertificateIssuer', 'SignatureCertificateSerialNumber', 'SignatureCertificateNotBefore' 'SignatureCertificateNotAfter', 'SignatureCertificateThumbprint', 'SignatureStatus', 'IsOSBinary' ) $Properties = Select-Properties -Objects $FilesCache[$Domain][0] -ExcludeProperty $ExcludeProperty $Output[$Domain] = foreach ($File in $FilesCache[$Domain].Keys) { $Values = foreach ($Property in $Properties) { $FilesCache[$Domain][$File][$Property] } if ($Values -notcontains $false) { $FilesCache[$Domain][$File]['IsConsistent'] = $true } [PSCustomObject] $FilesCache[$Domain][$File] } } $Output } function Get-GPOZaurrFolders { [cmdletBinding()] param( [ValidateSet('All', 'Netlogon', 'Sysvol')][string[]] $Type = 'All', [ValidateSet('All', 'NTFRS', 'Empty')][string] $FolderType = 'All', [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $AsHashTable ) $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { $Path = @( if ($Type -contains 'All') { "\\$Domain\SYSVOL\$Domain" } if ($Type -contains 'Sysvol') { "\\$Domain\SYSVOL\$Domain\policies" } if ($Type -contains 'NetLogon') { "\\$Domain\NETLOGON" } ) # Order does matter $Folders = [ordered] @{ "\\$Domain\SYSVOL\$Domain\policies\PolicyDefinitions" = @{ Name = 'SYSVOL PolicyDefinitions' } "\\$Domain\SYSVOL\$Domain\policies" = @{ Name = 'SYSVOL Policies' } "\\$Domain\SYSVOL\$Domain\scripts" = @{ Name = 'NETLOGON Scripts' } "\\$Domain\SYSVOL\$Domain\StarterGPOs" = @{ Name = 'SYSVOL GPO Starters' } "\\$Domain\NETLOGON" = @{ Name = 'NETLOGON Scripts' } "\\$Domain\SYSVOL\$Domain\DfsrPrivate" = @{ Name = 'DfsrPrivate' } "\\$Domain\SYSVOL\$Domain" = @{ Name = 'SYSVOL Root' } } $Exclusions = @{ DfsrPrivate = @{ ConflictAndDeleted = $true Deleted = $true Installing = $true } 'SYSVOL Policies' = @{ User = $true Machine = $true } 'NETLOGON Scripts' = @{ } 'SYSVOL Root' = @{ } 'SYSVOL GPO Starters' = @{ } 'SYSVOL PolicyDefinitions' = @{ } } Get-ChildItem -Path $Path -ErrorAction SilentlyContinue -Recurse -ErrorVariable +err -Force -Directory | ForEach-Object { $FileType = foreach ($Key in $Folders.Keys) { if ($_.FullName -like "$Key*") { $Folders[$Key] break } } $RootFolder = $Folders["$($_.FullName)"] if ($RootFolder) { $IsRootFolder = $true } else { $IsRootFolder = $false } $IsExcluded = $Exclusions["$($FileType.Name)"]["$($_.Name)"] -is [bool] if ($IsRootFolder -and $IsExcluded -eq $false) { $IsExcluded = $true } $FullFolder = Test-Path -Path "$($_.FullName)\*" $BrokenReplicationRoot = $_.Name -like '*_NTFRS_*' $BrokenReplicationChild = $_.FullName -like '*_NTFRS_*' -and $_.Name -notlike '*_NTFRS_*' $BrokenReplication = $_.FullName -like '*_NTFRS_*' $Object = [ordered] @{ FolderType = $FileType.Name FullName = $_.FullName IsEmptyFolder = -not $FullFolder IsBrokenReplication = $BrokenReplication IsBrokenReplicationRoot = $BrokenReplicationRoot IsBrokenReplicationChild = $BrokenReplicationChild IsRootFolder = $IsRootFolder IsExcluded = $IsExcluded Name = $_.Name Root = $_.Root Parent = $_.Parent CreationTime = $_.CreationTime LastWriteTime = $_.LastWriteTime Attributes = $_.Attributes DomainName = $Domain } if (-not $Object.IsExcluded) { if ($FolderType -eq 'Empty' -and $Object.IsEmptyFolder -eq $true) { if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } elseif ($FolderType -eq 'NTFRS' -and $Object.IsBrokenReplicationRoot -eq $true) { if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } elseif ($FolderType -eq 'All') { if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } } } } foreach ($e in $err) { Write-Warning "Get-GPOZaurrFolders - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } } function Get-GPOZaurrInheritance { [cmdletBinding()] param( [switch] $IncludeBlockedObjects, [switch] $OnlyBlockedInheritance, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) Begin { $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } Process { foreach ($Domain in $ForestInformation.Domains) { $OrganizationalUnits = Get-ADOrganizationalUnit -Filter * -Properties gpOptions, canonicalName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] foreach ($OU in $OrganizationalUnits) { $InheritanceInformation = [Ordered] @{ CanonicalName = $OU.canonicalName BlockedInheritance = if ($OU.gpOptions -eq 1) { $true } else { $false } } if (-not $IncludeBlockedObjects) { if ($OnlyBlockedInheritance) { if ($InheritanceInformation.BlockedInheritance -eq $true) { [PSCustomObject] $InheritanceInformation } } else { [PSCustomObject] $InheritanceInformation } } else { if ($InheritanceInformation) { if ($InheritanceInformation.BlockedInheritance -eq $true) { $InheritanceInformation['UsersCount'] = $null $InheritanceInformation['ComputersCount'] = $null [Array] $InheritanceInformation['Users'] = (Get-ADUser -SearchBase $OU.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] -Filter *).SamAccountName [Array] $InheritanceInformation['Computers'] = (Get-ADComputer -SearchBase $OU.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] -Filter *).SamAccountName $InheritanceInformation['UsersCount'] = $InheritanceInformation['Users'].Count $InheritanceInformation['ComputersCount'] = $InheritanceInformation['Computers'].Count } else { $InheritanceInformation['UsersCount'] = $null $InheritanceInformation['ComputersCount'] = $null $InheritanceInformation['Users'] = $null $InheritanceInformation['Computers'] = $null } } if ($OnlyBlockedInheritance) { if ($InheritanceInformation.BlockedInheritance -eq $true) { [PSCustomObject] $InheritanceInformation } } else { [PSCustomObject] $InheritanceInformation } } $InheritanceInformation['DistinguishedName'] = $OU.DistinguishedName } } } } <# $OrganizationalUnits = Get-ADOrganizationalUnit -Filter * $Output = foreach ($OU in $OrganizationalUnits) { Get-GPInheritance -Target $OU.DistinguishedName } $Output | Format-Table #> function Get-GPOZaurrLegacyFiles { [cmdletbinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies" -ErrorAction SilentlyContinue -Recurse -Include '*.adm', 'admfiles.ini' -ErrorVariable err -Force | ForEach-Object { [PSCustomObject] @{ Name = $_.Name FullName = $_.FullName CreationTime = $_.CreationTime LastWriteTime = $_.LastWriteTime Attributes = $_.Attributes DomainName = $Domain DirectoryName = $_.DirectoryName } } foreach ($e in $err) { Write-Warning "Get-GPOZaurrLegacyFiles - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } } } function Get-GPOZaurrLink { [cmdletbinding()] param( [parameter(ParameterSetName = 'ADObject', ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject, # weirdly enough site doesn't really work this way unless you give it 'CN=Configuration,DC=ad,DC=evotec,DC=xyz' as SearchBase [parameter(ParameterSetName = 'Filter')][string] $Filter = "(objectClass -eq 'organizationalUnit' -or objectClass -eq 'domainDNS' -or objectClass -eq 'site')", [parameter(ParameterSetName = 'Filter')][string] $SearchBase, [parameter(ParameterSetName = 'Filter')][Microsoft.ActiveDirectory.Management.ADSearchScope] $SearchScope, [parameter(ParameterSetName = 'Linked', Mandatory)][validateset('Root', 'DomainControllers', 'Site', 'Other')][string] $Linked, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [switch] $Limited, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [switch] $SkipDuplicates, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [System.Collections.IDictionary] $GPOCache, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [alias('ForestName')][string] $Forest, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [string[]] $ExcludeDomains, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [alias('Domain', 'Domains')][string[]] $IncludeDomains, [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [System.Collections.IDictionary] $ExtendedForestInformation ) Begin { $CacheReturnedGPOs = [ordered] @{} $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation if (-not $GPOCache -and -not $Limited) { $GPOCache = @{ } # While initially we used $ForestInformation.Domains but the thing is GPOs can be linked to other domains so we need to get them all so we can use cache of it later on even if we're processing just one domain # That's why we use $ForestInformation.Forest.Domains instead foreach ($Domain in $ForestInformation.Forest.Domains) { $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] Get-GPO -All -DomainName $Domain -Server $QueryServer | ForEach-Object { $GPOCache["$Domain$($_.ID.Guid)"] = $_ } } } } Process { if (-not $ADObject) { if ($Linked) { foreach ($Domain in $ForestInformation.Domains) { $Splat = @{ #Filter = $Filter Properties = 'distinguishedName', 'gplink', 'CanonicalName' # Filter = "(objectClass -eq 'organizationalUnit' -or objectClass -eq 'domainDNS' -or objectClass -eq 'site')" Server = $ForestInformation['QueryServers'][$Domain]['HostName'][0] } if ($Linked -contains 'DomainControllers') { $SearchBase = $ForestInformation['DomainsExtended'][$Domain]['DomainControllersContainer'] #if ($SearchBase -notlike "*$DomainDistinguishedName") { # we check if SearchBase is part of domain distinugishname. If it isn't we skip # continue #} $Splat['Filter'] = "(objectClass -eq 'organizationalUnit')" $Splat['SearchBase'] = $SearchBase try { $ADObjectGPO = Get-ADObject @Splat } catch { Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)" } foreach ($_ in $ADObjectGPO) { $OutputGPOs = Get-PrivGPOZaurrLink -Object $_ -Limited:$Limited.IsPresent -GPOCache $GPOCache foreach ($OutputGPO in $OutputGPOs) { if (-not $SkipDuplicates) { $OutputGPO } else { $UniqueGuid = -join ($OutputGPO.DomainName, $OutputGPO.Guid) if (-not $CacheReturnedGPOs[$UniqueGuid]) { $CacheReturnedGPOs[$UniqueGuid] = $OutputGPO $OutputGPO } } } } } if ($Linked -contains 'Root') { $SearchBase = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName'] #if ($SearchBase -notlike "*$DomainDistinguishedName") { # we check if SearchBase is part of domain distinugishname. If it isn't we skip # continue # } $Splat['Filter'] = "objectClass -eq 'domainDNS'" $Splat['SearchBase'] = $SearchBase try { $ADObjectGPO = Get-ADObject @Splat } catch { Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)" } foreach ($_ in $ADObjectGPO) { $OutputGPOs = Get-PrivGPOZaurrLink -Object $_ -Limited:$Limited.IsPresent -GPOCache $GPOCache foreach ($OutputGPO in $OutputGPOs) { if (-not $SkipDuplicates) { $OutputGPO } else { $UniqueGuid = -join ($OutputGPO.DomainName, $OutputGPO.Guid) if (-not $CacheReturnedGPOs[$UniqueGuid]) { $CacheReturnedGPOs[$UniqueGuid] = $OutputGPO $OutputGPO } } } } } if ($Linked -contains 'Site') { # Sites are defined only in primary domain if ($ForestInformation['DomainsExtended'][$Domain]['DNSRoot'] -eq $ForestInformation['DomainsExtended'][$Domain]['Forest']) { $SearchBase = -join ("CN=Configuration,", $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']) # if ($SearchBase -notlike "*$DomainDistinguishedName") { # we check if SearchBase is part of domain distinugishname. If it isn't we skip #continue #} $Splat['Filter'] = "(objectClass -eq 'site')" $Splat['SearchBase'] = $SearchBase try { $ADObjectGPO = Get-ADObject @Splat } catch { Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)" } foreach ($_ in $ADObjectGPO) { Get-PrivGPOZaurrLink -Object $_ -Limited:$Limited.IsPresent -GPOCache $GPOCache } } } if ($Linked -contains 'Other') { $SearchBase = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName'] #if ($SearchBase -notlike "*$DomainDistinguishedName") { # we check if SearchBase is part of domain distinugishname. If it isn't we skip # continue #} $Splat['Filter'] = "(objectClass -eq 'organizationalUnit')" $Splat['SearchBase'] = $SearchBase try { $ADObjectGPO = Get-ADObject @Splat } catch { Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)" } foreach ($_ in $ADObjectGPO) { if ($_.DistinguishedName -eq $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']) { # other skips Domain Root } elseif ($_.DistinguishedName -eq $ForestInformation['DomainsExtended'][$Domain]['DomainControllersContainer']) { # other skips Domain Controllers } else { $OutputGPOs = Get-PrivGPOZaurrLink -Object $_ -Limited:$Limited.IsPresent -GPOCache $GPOCache foreach ($OutputGPO in $OutputGPOs) { if (-not $SkipDuplicates) { $OutputGPO } else { $UniqueGuid = -join ($OutputGPO.DomainName, $OutputGPO.Guid) if (-not $CacheReturnedGPOs[$UniqueGuid]) { $CacheReturnedGPOs[$UniqueGuid] = $OutputGPO $OutputGPO } } } } } } } } else { foreach ($Domain in $ForestInformation.Domains) { $Splat = @{ Filter = $Filter Properties = 'distinguishedName', 'gplink', 'CanonicalName' Server = $ForestInformation['QueryServers'][$Domain]['HostName'][0] } if ($PSBoundParameters.ContainsKey('SearchBase')) { $DomainDistinguishedName = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName'] $SearchBaseDC = ConvertFrom-DistinguishedName -DistinguishedName $SearchBase -ToDC if ($SearchBaseDC -ne $DomainDistinguishedName) { # we check if SearchBase is part of domain distinugishname. If it isn't we skip continue } $Splat['SearchBase'] = $SearchBase } if ($PSBoundParameters.ContainsKey('SearchScope')) { $Splat['SearchScope'] = $SearchScope } try { $ADObjectGPO = Get-ADObject @Splat } catch { Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)" } foreach ($_ in $ADObjectGPO) { $OutputGPOs = Get-PrivGPOZaurrLink -Object $_ -Limited:$Limited.IsPresent -GPOCache $GPOCache foreach ($OutputGPO in $OutputGPOs) { if (-not $SkipDuplicates) { $OutputGPO } else { $UniqueGuid = -join ($OutputGPO.DomainName, $OutputGPO.Guid) if (-not $CacheReturnedGPOs[$UniqueGuid]) { $CacheReturnedGPOs[$UniqueGuid] = $OutputGPO $OutputGPO } } } } } } } else { foreach ($Object in $ADObject) { Get-PrivGPOZaurrLink -Object $Object -Limited:$Limited.IsPresent -GPOCache $GPOCache } } } End { } } function Get-GPOZaurrLinkSummary { [cmdletBinding()] param( [ValidateSet('All', 'MultipleLinks', 'OneLink', 'LinksSummary')][string[]] $Report = 'All', [switch] $UnlimitedProperties, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $HighestCount = 0 # to keep number of depth $CacheSummaryLinks = [ordered] @{} # cache # Get all links $Links = Get-GPOZaurrLink -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Link in $Links) { if (-not $CacheSummaryLinks["$($Link.DomainName)$($Link.Guid)"]) { $CacheSummaryLinks["$($Link.DomainName)$($Link.Guid)"] = [System.Collections.Generic.List[System.Object]]::new() } $CacheSummaryLinks["$($Link.DomainName)$($Link.Guid)"].Add($Link) } $ReturnObject = [ordered] @{ MultipleLinks = [System.Collections.Generic.List[System.Object]]::new() OneLink = [System.Collections.Generic.List[System.Object]]::new() LinksSummary = [System.Collections.Generic.List[System.Object]]::new() } foreach ($Key in $CacheSummaryLinks.Keys) { $GPOs = $CacheSummaryLinks[$Key] [Array] $LinkingSummary = foreach ($GPO in $GPOs) { $SplitttedOU = ($GPO.DistinguishedName -split ',') [Array] $Clean = foreach ($_ in $SplitttedOU) { if ($_ -notlike 'DC=*') { $_ -replace 'OU=' } } if ($Clean.Count -gt $HighestCount) { $HighestCount = $Clean.Count } if ($Clean) { $Test = [ordered] @{ DisplayName = $GPO.DisplayName Guid = $GPO.Guid DomainName = $GPO.DomainName Level0 = ConvertFrom-DistinguishedName -DistinguishedName $GPO.DistinguishedName -ToDomainCN } for ($i = 1; $i -le 10; $i++) { $Test["Level$i"] = $Clean[ - $i] } [PSCustomobject] $Test } else { $Test = [ordered] @{ DisplayName = $GPO.DisplayName Guid = $GPO.Guid DomainName = $GPO.DomainName Level0 = $GPO.CanonicalName } for ($i = 1; $i -le 10; $i++) { $Test["Level$i"] = $null } [PSCustomobject] $Test } } if ($Report -contains 'MultipleLinks' -or $Report -contains 'All') { foreach ($Link in $LinkingSummary) { $ReturnObject.MultipleLinks.Add($Link) } #continue } if ($Report -eq 'OneLink' -or $Report -contains 'All') { $List = [ordered] @{ DisplayName = $GPOs[0].DisplayName Guid = $GPOs[0].Guid DomainName = $GPOs[0].DomainName LinksCount = $GPOs.Count } for ($i = 0; $i -le 10; $i++) { $List["Level$i"] = ($LinkingSummary."Level$i" | Select-Object -Unique).Count $List["Level$($i)List"] = ($LinkingSummary."Level$i" | Select-Object -Unique) } $List.LinksDistinguishedName = $GPOs.DistinguishedName # = Computers, OU = ITR02, DC = ad, DC = evotec, DC = xyz $List.LinksCanonicalName = $GPOs.CanonicalName $List.Owner = $GPOs[0].Owner #: EVOTEC\Domain Admins $List.GpoStatus = $GPOs[0].GpoStatus #: AllSettingsEnabled $List.Description = $GPOs[0].Description #: $List.CreationTime = $GPOs[0].CreationTime #: 16.12.2019 21:25:32 $List.ModificationTime = $GPOs[0].ModificationTime #: 30.05.2020 19:12:58 $List.GPODomainDistinguishedName = $GPOs[0].GPODomainDistinguishedName #: DC = ad, DC = evotec, DC = xyz $List.GPODistinguishedName = $GPOs[0].GPODistinguishedName #: cn = { AA782787 - 002B-4B8C-886F-05873F2DC0CA }, cn = policies, cn = system, DC = ad, DC = evotec, DC = xy $ReturnObject.OneLink.Add( [PSCustomObject] $List) } if ($Report -eq 'LinksSummary' -or $Report -contains 'All') { $Output = [PSCustomObject] @{ DisplayName = $GPOs[0].DisplayName #: COMPUTERS | LAPS Guid = $GPOs[0].Guid #: AA782787 - 002B-4B8C-886F-05873F2DC0CA DomainName = $GPOs[0].DomainName #: ad.evotec.xyz LinksCount = $GPOs.Count LinksDistinguishedName = $GPOs.DistinguishedName # = Computers, OU = ITR02, DC = ad, DC = evotec, DC = xyz LinksCanonicalName = $GPOs.CanonicalName #: ad.evotec.xyz / ITR02 / Computers Owner = $GPOs[0].Owner #: EVOTEC\Domain Admins GpoStatus = $GPOs[0].GpoStatus #: AllSettingsEnabled Description = $GPOs[0].Description #: CreationTime = $GPOs[0].CreationTime #: 16.12.2019 21:25:32 ModificationTime = $GPOs[0].ModificationTime #: 30.05.2020 19:12:58 GPODomainDistinguishedName = $GPOs[0].GPODomainDistinguishedName #: DC = ad, DC = evotec, DC = xyz GPODistinguishedName = $GPOs[0].GPODistinguishedName #: cn = { AA782787 - 002B-4B8C-886F-05873F2DC0CA }, cn = policies, cn = system, DC = ad, DC = evotec, DC = xy } $ReturnObject.LinksSummary.Add($Output) } } # Processing output if (-not $UnlimitedProperties) { if ($Report -contains 'MultipleLinks' -or $Report -contains 'All') { $Properties = @( 'DisplayName' 'DomainName' 'GUID' for ($i = 0; $i -le $HighestCount; $i++) { "Level$i" } 'Owner' 'GpoStatus' 'Description' 'CreationTime' 'ModificationTime' 'GPODomainDistinguishedName' 'GPODistinguishedName' ) $ReturnObject.MultipleLinks = $ReturnObject.MultipleLinks | Select-Object -Property $Properties } if ($Report -contains 'OneLink' -or $Report -contains 'All') { $Properties = @( 'DisplayName' 'DomainName' 'GUID' for ($i = 0; $i -le $HighestCount; $i++) { "Level$i" "Level$($i)List" } 'LinksDistinguishedName' 'LinksCanonicalName' 'Owner' 'GpoStatus' 'Description' 'CreationTime' 'ModificationTime' 'GPODomainDistinguishedName' 'GPODistinguishedName' ) $ReturnObject.OneLink = $ReturnObject.OneLink | Select-Object -Property $Properties } #if ($Report -contains 'LinksSummary' -or $Report -contains 'All') { # Not needed because there's no dynamic properties, but if there would be we need to uncomment and fix it #} } if ($Report.Count -eq 1 -and $Report -notcontains 'All') { $ReturnObject["$Report"] } else { $ReturnObject } } function Get-GPOZaurrNetLogon { [cmdletBinding(DefaultParameterSetName = 'Default')] param( [parameter(ParameterSetName = 'OwnerOnly')][switch] $OwnerOnly, [parameter(ParameterSetName = 'SkipOwner')][switch] $SkipOwner, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation $FilesAll = foreach ($Domain in $ForestInformation.Domains) { $Path = -join ("\\", $Domain, '\Netlogon') $PathOnSysvol = -join ("\\", $Domain, "\SYSVOL\", $Domain, "\Scripts") [Array] $Files = Get-ChildItem -LiteralPath $Path -Recurse -Force -ErrorVariable Err -ErrorAction SilentlyContinue foreach ($e in $err) { Write-Warning "Get-GPOZaurrNetLogon - Listing file failed with error $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } $Count = 0 foreach ($File in $Files) { $Count++ Write-Verbose "GPOZaurrNetLogon - Processing [$($Domain)]($Count/$($Files.Count)) $($File.FullName)" try { $ACL = Get-Acl -Path $File.FullName -ErrorAction Stop } catch { Write-Warning "Get-GPOZaurrNetLogon - ACL reading failed for $($File.FullName) with error $($_.Exception.Message) ($($_.CategoryInfo.Reason))" } #if ($ACL.Owner) { $IdentityOwner = Convert-Identity -Identity $ACL.Owner -Verbose:$false $IdentityOwnerAdvanced = Get-WinADObject -Identity $ACL.Owner -Cache -Verbose:$false #} else { # $IdentityOwner = [PSCustomObject] @{ SID = ''; Type = 'Unknown' } # $IdentityOwnerAdvanced = [PSCustomObject] @{ ObjectClass = '' } #} if (-not $OwnerOnly) { if (-not $SkipOwner) { if ($IdentityOwner.SID -eq "S-1-5-32-544") { $Status = 'OK' } else { $Status = 'Replace owner required' } [PSCustomObject] @{ FullName = $File.FullName Status = $Status DomainName = $Domain Extension = $File.Extension CreationTime = $File.CreationTime LastAccessTime = $File.LastAccessTime LastWriteTime = $File.LastWriteTime Attributes = $File.Attributes AccessControlType = 'Allow' # : Allow Principal = $IdentityOwner.Name # : BUILTIN\Administrators PrincipalSid = $IdentityOwner.SID PrincipalType = $IdentityOwner.Type PrincipalObjectClass = $IdentityOwnerAdvanced.ObjectClass FileSystemRights = 'Owner' # : FullControl IsInherited = $false FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol) } } $FilePermission = Get-FilePermissions -Path $File.FullName -ACLS $ACL -Verbose:$false foreach ($Perm in $FilePermission) { $Identity = Convert-Identity -Identity $Perm.Principal -Verbose:$false $AdvancedIdentity = Get-WinADObject -Identity $Perm.Principal -Cache -Verbose:$false $Status = 'Not assesed' if ($Perm.FileSystemRights -eq [System.Security.AccessControl.FileSystemRights]::FullControl) { if ($Identity.Type -eq 'WellKnownAdministrative') { $Status = 'OK' } else { if ($AdvancedIdentity.ObjectClass -in 'user', 'computer') { $Status = 'Removal permission required' } else { $Status = 'Review permission required' } } } elseif ($Perm.FileSystemRights -like "*Modify*") { if ($AdvancedIdentity.ObjectClass -in 'user', 'computer') { $Status = 'Removal permission required' } else { $Status = 'Review permission required' } } elseif ($Perm.FileSystemRights -like "*Write*") { if ($AdvancedIdentity.ObjectClass -in 'user', 'computer') { $Status = 'Removal permission required' } else { $Status = 'Review permission required' } } if ($Identity.Type -eq 'Unknown') { $Status = 'Removal permission required' } [PSCustomObject] @{ FullName = $File.FullName Status = $Status DomainName = $Domain Extension = $File.Extension CreationTime = $File.CreationTime LastAccessTime = $File.LastAccessTime LastWriteTime = $File.LastWriteTime Attributes = $File.Attributes AccessControlType = $Perm.AccessControlType # : Allow Principal = $Identity.Name # : BUILTIN\Administrators PrincipalSid = $Identity.SID PrincipalType = $Identity.Type PrincipalObjectClass = $AdvancedIdentity.ObjectClass FileSystemRights = $Perm.FileSystemRights # : FullControl IsInherited = $Perm.IsInherited # : True FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol) } } } else { if ($IdentityOwner.SID -eq "S-1-5-32-544") { $Status = 'OK' } else { $Status = 'Replace owner required' } [PSCustomObject] @{ FullName = $File.FullName Status = $Status DomainName = $Domain Extension = $File.Extension CreationTime = $File.CreationTime LastAccessTime = $File.LastAccessTime LastWriteTime = $File.LastWriteTime Attributes = $File.Attributes Owner = $IdentityOwner.Name OwnerSid = $IdentityOwner.SID OwnerType = $IdentityOwner.Type FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol) } } } } $FilesAll } function Get-GPOZaurrOwner { <# .SYNOPSIS Gets owners of GPOs from Active Directory and SYSVOL .DESCRIPTION Gets owners of GPOs from Active Directory and SYSVOL .PARAMETER GPOName Name of GPO. By default all GPOs are returned .PARAMETER GPOGuid GUID of GPO. By default all GPOs are returned .PARAMETER IncludeSysvol Includes Owner from SYSVOL as well .PARAMETER SkipBroken Doesn't display GPOs that have no SYSVOL content (orphaned GPOs) .PARAMETER Forest Target different Forest, by default current forest is used .PARAMETER ExcludeDomains Exclude domain from search, by default whole forest is scanned .PARAMETER IncludeDomains Include only specific domains, by default whole forest is scanned .PARAMETER ExtendedForestInformation Ability to provide Forest Information from another command to speed up processing .PARAMETER ADAdministrativeGroups Ability to provide AD Administrative Groups from another command to speed up processing .EXAMPLE Get-GPOZaurrOwner -Verbose -IncludeSysvol .EXAMPLE Get-GPOZaurrOwner -Verbose -IncludeSysvol -SkipBroken .NOTES General notes #> [cmdletbinding(DefaultParameterSetName = 'Default')] param( [Parameter(ParameterSetName = 'GPOName')][string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID')][alias('GUID', 'GPOID')][string] $GPOGuid, [switch] $IncludeSysvol, [switch] $SkipBroken, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [System.Collections.IDictionary] $ADAdministrativeGroups ) Begin { $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation if (-not $ADAdministrativeGroups) { $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } $Count = 0 } Process { $getGPOZaurrADSplat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ForestInformation } if ($GPOName) { $getGPOZaurrADSplat['GPOName'] = $GPOName } elseif ($GPOGuid) { $getGPOZaurrADSplat['GPOGUID'] = $GPOGuid } $Objects = Get-GPOZaurrAD @getGPOZaurrADSplat foreach ($_ in $Objects) { $Count++ Write-Verbose "Get-GPOZaurrOwner - Processing GPO [$Count/$($Objects.Count)]: $($_.DisplayName) from domain: $($_.DomainName)" $ACL = Get-ADACLOwner -ADObject $_.GPODistinguishedName -Resolve -ADAdministrativeGroups $ADAdministrativeGroups -Verbose:$false $Object = [ordered] @{ DisplayName = $_.DisplayName DomainName = $_.DomainName GUID = $_.GUID Owner = $ACL.OwnerName OwnerSid = $ACL.OwnerSid OwnerType = $ACL.OwnerType } if ($IncludeSysvol) { $FileOwner = Get-FileOwner -JustPath -Path $_.Path -Resolve -Verbose:$false $Object['SysvolOwner'] = $FileOwner.OwnerName $Object['SysvolSid'] = $FileOwner.OwnerSid $Object['SysvolType'] = $FileOwner.OwnerType $Object['SysvolPath'] = $_.Path $Object['IsOwnerConsistent'] = if ($ACL.OwnerName -eq $FileOwner.OwnerName) { $true } else { $false } $Object['IsOwnerAdministrative'] = if ($Object['SysvolType'] -eq 'Administrative' -and $Object['OwnerType'] -eq 'Administrative') { $true } else { $false } if (Test-Path -LiteralPath $Object['SysvolPath']) { $Object['SysvolExists'] = $true } else { $Object['SysvolExists'] = $false } } else { $Object['IsOwnerAdministrative'] = if ($Object['OwnerType'] -eq 'Administrative') { $true } else { $false } } if ($SkipBroken -and $Object['SysvolExists'] -eq $false) { continue } $Object['DistinguishedName'] = $_.GPODistinguishedName [PSCUstomObject] $Object } } End { } } function Get-GPOZaurrPassword { <# .SYNOPSIS Tries to find CPassword in Group Policies or given path and translate it to readable value .DESCRIPTION Tries to find CPassword in Group Policies or given path and translate it to readable value .PARAMETER Forest Target different Forest, by default current forest is used .PARAMETER ExcludeDomains Exclude domain from search, by default whole forest is scanned .PARAMETER IncludeDomains Include only specific domains, by default whole forest is scanned .PARAMETER ExtendedForestInformation Ability to provide Forest Information from another command to speed up processing .PARAMETER GPOPath Path where Group Policy content is located or where backup is located .EXAMPLE Get-GPOZaurrPassword -GPOPath 'C:\Users\przemyslaw.klys\Desktop\GPOExport_2020.10.12' .EXAMPLE Get-GPOZaurrPassword .NOTES General notes #> [cmdletBinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [string[]] $GPOPath ) if ($GPOPath) { foreach ($Path in $GPOPath) { $Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml -ErrorAction SilentlyContinue -ErrorVariable err $Output = foreach ($XMLFileName in $Items) { $Password = Find-GPOPassword -Path $XMLFileName.FullName if ($Password) { if ($XMLFileName.FullName -match '{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}') { $GPOGUID = $matches[0] } [PSCustomObject] @{ RootPath = $Path PasswordFile = $XMLFileName.FullName GUID = $GPOGUID Password = $Password } } } } } else { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { $Path = -join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies') #Extract the all XML files in the Folder $Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml -ErrorAction SilentlyContinue -ErrorVariable err $Output = foreach ($XMLFileName in $Items) { $Password = Find-GPOPassword -Path $XMLFileName.FullName if ($Password) { # match regex if ($XMLFileName.FullName -match '{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}') { $GPOGUID = $matches[0] $GPO = Get-GPOZaurrAD -GPOGuid $GPOGUID -IncludeDomains $Domain [PSCustomObject] @{ DisplayName = $GPO.DisplayName DomainName = $GPO.DomainName GUID = $GPO.GUID PasswordFile = $XMLFileName.FullName Password = $Password Created = $GPO.Created Modified = $GPO.Modified Description = $GPO.Description } } else { [PSCustomObject] @{ DisplayName = '' DomainName = '' GUID = '' PasswordFile = $XMLFileName.FullName Password = $Password Created = '' Modified = '' Description = '' } } } } foreach ($e in $err) { Write-Warning "Get-GPOZaurrPassword - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } $Output } } } function Get-GPOZaurrPermission { [cmdletBinding(DefaultParameterSetName = 'GPO' )] param( [Parameter(ParameterSetName = 'GPOName')] [string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID')] [alias('GUID', 'GPOID')][string] $GPOGuid, [string[]] $Principal, [validateset('DistinguishedName', 'Name', 'NetbiosName', 'Sid')][string] $PrincipalType = 'Sid', [validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All', [switch] $SkipWellKnown, [switch] $SkipAdministrative, #[switch] $ResolveAccounts, [switch] $IncludeOwner, [Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType, [Microsoft.GroupPolicy.GPPermissionType[]] $ExcludePermissionType, [validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'All', [string[]] $ExcludePrincipal, [validateset('DistinguishedName', 'Name', 'Sid')][string] $ExcludePrincipalType = 'Sid', [switch] $IncludeGPOObject, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [System.Collections.IDictionary] $ADAdministrativeGroups, [switch] $ReturnSecurityWhenNoData, # if no data return all data [switch] $ReturnSingleObject # forces return of single object per GPO as one for ForEach-Object processing ) Begin { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Extended if (-not $ADAdministrativeGroups) { $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } if ($Type -eq 'Unknown') { if ($SkipAdministrative -or $SkipWellKnown) { Write-Warning "Get-GPOZaurrPermission - Using SkipAdministrative or SkipWellKnown while looking for Unknown doesn't make sense as only Unknown will be displayed." } } <# if ($ResolveAccounts) { $Accounts = @{ } foreach ($Domain in $ForestInformation.Domains) { $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] $DomainInformation = Get-ADDomain -Server $QueryServer $Users = Get-ADUser -Filter * -Server $QueryServer -Properties PasswordLastSet, LastLogonDate, UserPrincipalName foreach ($User in $Users) { $U = -join ($DomainInformation.NetBIOSName, '\', $User.SamAccountName) $Accounts[$U] = $User } $Groups = Get-ADGroup -Filter * -Server $QueryServer foreach ($Group in $Groups) { $G = -join ($DomainInformation.NetBIOSName, '\', $Group.SamAccountName) $Accounts[$G] = $Group } } } #> } Process { foreach ($Domain in $ForestInformation.Domains) { $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] if ($GPOName) { $getGPOSplat = @{ Name = $GPOName Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } $TextForError = "Error running Get-GPO (QueryServer: $QueryServer / Domain: $Domain / Name: $GPOName) with:" } elseif ($GPOGuid) { $getGPOSplat = @{ Guid = $GPOGuid Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } $TextForError = "Error running Get-GPO (QueryServer: $QueryServer / Domain: $Domain / GUID: $GPOGuid) with:" } else { $getGPOSplat = @{ All = $true Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } $TextForError = "Error running Get-GPO (QueryServer: $QueryServer / Domain: $Domain / All: $True) with:" } Try { Get-GPO @getGPOSplat | ForEach-Object -Process { $GPOSecurity = $_.GetSecurityInfo() $getPrivPermissionSplat = @{ Principal = $Principal PrincipalType = $PrincipalType PermitType = $PermitType #Accounts = $Accounts Type = $Type GPO = $_ SkipWellKnown = $SkipWellKnown.IsPresent SkipAdministrative = $SkipAdministrative.IsPresent IncludeOwner = $IncludeOwner.IsPresent IncludeGPOObject = $IncludeGPOObject.IsPresent IncludePermissionType = $IncludePermissionType ExcludePermissionType = $ExcludePermissionType ExcludePrincipal = $ExcludePrincipal ExcludePrincipalType = $ExcludePrincipalType ADAdministrativeGroups = $ADAdministrativeGroups ExtendedForestInformation = $ForestInformation SecurityRights = $GPOSecurity } try { $Output = Get-PrivPermission @getPrivPermissionSplat } catch { $Output = $null Write-Warning "Get-GPOZaurrPermission - Error running Get-PrivPermission: $($_.Exception.Message)" } if (-not $Output) { if ($ReturnSecurityWhenNoData) { # there is no data to return, but we need to have GPO information to process ADD permissions. $ReturnObject = [PSCustomObject] @{ DisplayName = $_.DisplayName # : ALL | Enable RDP GUID = $_.ID DomainName = $_.DomainName # : ad.evotec.xyz Enabled = $_.GpoStatus Description = $_.Description CreationDate = $_.CreationTime ModificationTime = $_.ModificationTime GPOObject = $_ GPOSecurity = $GPOSecurity } $ReturnObject } } else { if ($ReturnSingleObject) { , $Output } else { $Output } } } } catch { Write-Warning "Get-GPOZaurrPermission - $TextForError $($_.Exception.Message)" } } } End { } } function Get-GPOZaurrPermissionConsistency { [cmdletBinding(DefaultParameterSetName = 'Type')] param( [Parameter(ParameterSetName = 'GPOName')][string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID')][alias('GUID', 'GPOID')][string] $GPOGuid, [Parameter(ParameterSetName = 'Type')][validateSet('Consistent', 'Inconsistent', 'All')][string[]] $Type = 'All', [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $IncludeGPOObject, [switch] $VerifyInheritance ) Begin { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } Process { foreach ($Domain in $ForestInformation.Domains) { $TimeLog = Start-TimeLog Write-Verbose "Get-GPOZaurrPermissionConsistency - Starting process for $Domain" $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] if ($GPOName) { $getGPOSplat = @{ Name = $GPOName Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } } elseif ($GPOGuid) { $getGPOSplat = @{ Guid = $GPOGuid Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } } else { $getGPOSplat = @{ All = $true Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } } $GroupPolicies = Get-GPO @getGPOSplat $Count = 0 $GroupPolicies | ForEach-Object -Process { $GPO = $_ $Count++ Write-Verbose "Get-GPOZaurrPermissionConsistency - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)" $SysVolpath = -join ('\\', $Domain, '\sysvol\', $Domain, '\Policies\{', $GPO.ID.GUID, '}') if (Test-Path -LiteralPath $SysVolpath) { try { $IsConsistent = $GPO.IsAclConsistent() $ErrorMessage = '' } catch { $ErrorMessage = $_.Exception.Message Write-Warning "Get-GPOZaurrPermissionConsistency - Processing $($GPO.DisplayName) / $($GPO.DomainName) failed to get consistency with error: $($_.Exception.Message)." $IsConsistent = 'Not available' } } else { Write-Warning "Get-GPOZaurrPermissionConsistency - Processing $($GPO.DisplayName) / $($GPO.DomainName) failed as path $SysvolPath doesn't exists!" $IsConsistent = $false } if ($VerifyInheritance) { if ($IsConsistent -eq $true) { $FolderPermissions = Get-WinADSharePermission -Path $SysVolpath -Verbose:$false if ($FolderPermissions) { [Array] $NotInheritedPermissions = foreach ($File in $FolderPermissions) { if ($File.Path -ne $SysVolpath -and $File.IsInherited -eq $false) { $File } } if ($NotInheritedPermissions.Count -eq 0) { $ACLConsistentInside = $true } else { $ACLConsistentInside = $false } } else { $ACLConsistentInside = 'Not available' $NotInheritedPermissions = $null } } else { # Since top level permissions are inconsistent we don't even try to asses inside permissions $ACLConsistentInside = $IsConsistent $NotInheritedPermissions = $null } } $Object = [ordered] @{ DisplayName = $_.DisplayName # : New Group Policy Object DomainName = $_.DomainName # : ad.evotec.xyz ACLConsistent = $IsConsistent } if ($VerifyInheritance) { $Object['ACLConsistentInside'] = $ACLConsistentInside } $Object['Owner'] = $_.Owner # : EVOTEC\Enterprise Admins $Object['Path'] = $_.Path $Object['SysVolPath '] = $SysvolPath $Object['Id '] = $_.Id # : 8a7bc515-d7fd-4d1f-90b8-e47c15f89295 $Object['GpoStatus'] = $_.GpoStatus # : AllSettingsEnabled $Object['Description'] = $_.Description # : $Object['CreationTime'] = $_.CreationTime # : 04.03.2020 17:19:42 $Object['ModificationTime'] = $_.ModificationTime# : 06.05.2020 10:30:36 $Object['UserVersion'] = $_.UserVersion # : AD Version: 0, SysVol Version: 0 $Object['ComputerVersion'] = $_.ComputerVersion # : AD Version: 1, SysVol Version: 1 $Object['WmiFilter'] = $_.WmiFilter # : $Object['Error'] = $ErrorMessage if ($IncludeGPOObject) { $Object['IncludeGPOObject'] = $_ } if ($VerifyInheritance) { $Object['ACLConsistentInsideDetails'] = $NotInheritedPermissions } if ($Type -eq 'All') { [PSCustomObject] $Object } elseif ($Type -eq 'Inconsistent') { if ($VerifyInheritance) { if (-not ($IsConsistent -eq $true) -or (-not $ACLConsistentInside -eq $true)) { [PSCustomObject] $Object } } else { if (-not ($IsConsistent -eq $true)) { [PSCustomObject] $Object } } } elseif ($Type -eq 'Consistent') { if ($VerifyInheritance) { if ($IsConsistent -eq $true -and $ACLConsistentInside -eq $true) { [PSCustomObject] $Object } } else { if ($IsConsistent -eq $true) { [PSCustomObject] $Object } } } } $TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner Write-Verbose "Get-GPOZaurrPermissionConsistency - Finishing process for $Domain (Time to process: $TimeEnd)" } } End { } } function Get-GPOZaurrPermissionIssue { <# .SYNOPSIS Detects Group Policy missing Authenticated Users permission while not having higher permissions. .DESCRIPTION Detects Group Policy missing Authenticated Users permission while not having higher permissions. .PARAMETER Forest Target different Forest, by default current forest is used .PARAMETER ExcludeDomains Exclude domain from search, by default whole forest is scanned .PARAMETER IncludeDomains Include only specific domains, by default whole forest is scanned .PARAMETER ExtendedForestInformation Ability to provide Forest Information from another command to speed up processing .EXAMPLE $Issues = Get-GPOZaurrPermissionIssue $Issues | Format-Table .NOTES General notes #> [cmdletBinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation -Extended foreach ($Domain in $ForestInformation.Domains) { $TimeLog = Start-TimeLog Write-Verbose "Get-GPOZaurrPermissionIssue - Starting process for $Domain" $QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0] $SystemsContainer = $ForestInformation['DomainsExtended'][$Domain].SystemsContainer if ($SystemsContainer) { $PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer) $Properties = 'DisplayName', 'Name', 'DistinguishedName', 'ObjectClass', 'WhenCreated', 'WhenChanged' $PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer -Properties $Properties foreach ($Policy in $PoliciesInAD) { $GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName $GUIDFromDN = $GUIDFromDN -replace '{' -replace '}' $GUID = $Policy.Name -replace '{' -replace '}' [PSCustomObject] @{ DisplayName = $Policy.DisplayName DomainName = $Domain PermissionIssue = -not ($GUID -and $GUIDFromDN) ObjectClass = $Policy.ObjectClass Name = $Policy.Name DistinguishedName = $Policy.DistinguishedName GUID = $GUIDFromDN WhenCreated = $Policy.WhenCreated WhenChanged = $Policy.WhenChanged } } } $TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner Write-Verbose "Get-GPOZaurrPermissionIssue - Finishing process for $Domain (Time to process: $TimeEnd)" } } function Get-GPOZaurrPermissionRoot { [cmdletBinding()] param( [ValidateSet('GpoRootCreate', 'GpoRootOwner')][string[]] $IncludePermissionType, [ValidateSet('GpoRootCreate', 'GpoRootOwner')][string[]] $ExcludePermissionType, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $SkipNames ) Begin { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Extended } Process { foreach ($Domain in $ForestInformation.Domains) { $DomainDistinguishedName = $ForestInformation['DomainsExtended'][$Domain].DistinguishedName $QueryServer = $ForestInformation['QueryServers'][$Domain].HostName[0] $getADACLSplat = @{ ADObject = "CN=Policies,CN=System,$DomainDistinguishedName" IncludeActiveDirectoryRights = 'GenericAll', 'CreateChild', 'WriteOwner', 'WriteDACL' IncludeObjectTypeName = 'All', 'Group-Policy-Container' IncludeInheritedObjectTypeName = 'All', 'Group-Policy-Container' ADRightsAsArray = $true ResolveTypes = $true } $GPOPermissionsGlobal = Get-ADACL @getADACLSplat -Verbose:$false $GPOs = Get-ADObject -SearchBase "CN=Policies,CN=System,$DomainDistinguishedName" -SearchScope OneLevel -Filter * -Properties DisplayName -Server $QueryServer -Verbose:$false foreach ($Permission in $GPOPermissionsGlobal) { $CustomPermission = foreach ($_ in $Permission.ActiveDirectoryRights) { if ($_ -in 'WriteDACL', 'WriteOwner', 'GenericAll' ) { 'GpoRootOwner' } if ($_ -in 'CreateChild', 'GenericAll') { 'GpoRootCreate' } } $CustomPermission = $CustomPermission | Sort-Object -Unique foreach ($SinglePermission in $CustomPermission) { if ($SinglePermission -in $ExcludePermissionType) { continue } if ($IncludePermissionType.Count -gt 0 -and $SinglePermission -notin $IncludePermissionType) { continue } $OutputEntry = [ordered] @{ PrincipalName = $Permission.Principal Permission = $SinglePermission PermissionType = $Permission.AccessControlType PrincipalSidType = $Permission.PrincipalType PrincipalObjectClass = $Permission.PrincipalObjectType PrincipalDomainName = $Permission.PrincipalObjectDomain PrincipalSid = $Permission.PrincipalObjectSid DomainName = $Domain GPOCount = $GPOs.Count } if (-not $SkipNames) { $OutputEntry['GPONames'] = $GPOs.DisplayName } [PSCustomObject] $OutputEntry } } } } End {} } function Get-GPOZaurrPermissionSummary { [cmdletBinding()] param( [validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All', [validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'All', [ValidateSet('GpoApply', 'GpoEdit', 'GpoCustom', 'GpoEditDeleteModifySecurity', 'GpoRead', 'GpoOwner', 'GpoRootCreate', 'GpoRootOwner')][string[]] $IncludePermissionType, [ValidateSet('GpoApply', 'GpoEdit', 'GpoCustom', 'GpoEditDeleteModifySecurity', 'GpoRead', 'GpoOwner', 'GpoRootCreate', 'GpoRootOwner')][string[]] $ExcludePermissionType, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [string] $Separator ) $IncludePermTypes = [System.Collections.Generic.List[Microsoft.GroupPolicy.GPPermissionType]]::new() $ExcludePermTypes = [System.Collections.Generic.List[Microsoft.GroupPolicy.GPPermissionType]]::new() $CustomPermissions = [System.Collections.Generic.List[string]]::new() foreach ($PermType in $IncludePermissionType) { if ($PermType -in 'GpoApply', 'GpoEdit', 'GPOCustom', 'GpoEditDeleteModifySecurity', 'GPORead') { $IncludePermTypes.Add([Microsoft.GroupPolicy.GPPermissionType]::$PermType) } elseif ($PermType -in 'GpoOwner') { $IncludeOwner = $true } elseif ($PermType -in 'GpoRootCreate', 'GpoRootOwner') { $CustomPermissions.Add($PermType) } } foreach ($PermType in $ExcludePermissionType) { if ($PermType -in 'GpoApply', 'GpoEdit', 'GPOCustom', 'GpoEditDeleteModifySecurity', 'GPORead') { $ExcludePermTypes.Add([Microsoft.GroupPolicy.GPPermissionType]::$PermType) } elseif ($PermType -in 'GpoOwner') { $IncludeOwner = $false } elseif ($PermType -in 'GpoRootCreate', 'GpoRootOwner') { $CustomPermissions.Add($PermType) } } $RootPermissions = Get-GPOZaurrPermissionRoot -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation $Permissions = Get-GPOZaurrPermission -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -IncludePermissionType $IncludePermTypes -ExcludePermissionType $ExcludePermissionType -Type $Type -PermitType $PermitType -IncludeOwner:$IncludeOwner $Entries = @( foreach ($Permission in $Permissions) { [PSCustomObject] @{ PrincipalName = $Permission.PrincipalName PrincipalDomainName = $Permission.PrincipalDomainName Permission = $Permission.Permission PermissionType = $Permission.PermissionType PrincipalSid = $Permission.PrincipalSid PrincipalSidType = $Permission.PrincipalSidType PrincipalObjectClass = $Permission.PrincipalObjectClass DisplayName = $Permission.DisplayName DomainName = $Permission.DomainName } } foreach ($RootPermission in $RootPermissions) { [PSCustomObject] @{ PrincipalName = $RootPermission.PrincipalName PrincipalDomainName = $RootPermission.PrincipalDomainName Permission = $RootPermission.Permission PermissionType = $RootPermission.PermissionType PrincipalSid = $RootPermission.PrincipalSid PrincipalSidType = $RootPermission.PrincipalSidType PrincipalObjectClass = $RootPermission.PrincipalObjectClass DisplayName = $RootPermission.GPONames DomainName = $RootPermission.DomainName } } ) $PermissionsData = [ordered] @{} foreach ($Entry in $Entries) { $Key = -join ($Entry.Permission, $Entry.PrincipalName, $Entry.PrincipalDomainName) if (-not $PermissionsData[$Key]) { $PermissionsData[$Key] = [PSCustomObject] @{ Permission = $Entry.Permission PrincipalName = $Entry.PrincipalName PrincipalDomainName = $Entry.PrincipalDomainName PrincipalSidType = $Entry.PrincipalSidType DomainName = $Entry.DomainName PermissionType = $Entry.PermissionType GPOCOunt = 0 GPONames = [System.Collections.Generic.List[string]]::new() } } #if ($IncludeNames) { $PermissionsData[$Key].GPONames.Add($Entry.DisplayName) #} $PermissionsData[$Key].GPOCOunt = $PermissionsData[$Key].GPOCOunt + ($Entry.DisplayName).Count } $PermissionsData.Values <# ($Entries | Group-Object -Property Permission, PrincipalSidType, PrincipalName, PrincipalDomainName, DomainName, PermissionType) | ForEach-Object { $Property = $_.Name -split ', ' Write-Verbose "$Property - $($Property.Count)" if ($Property[0] -eq 'GpoOwner') { [PSCustomObject] @{ Permission = $Property[0] PrincipalSidType = $Property[1] PrincipalName = $Property[2] PrincipalDomainName = $Property[3] DomainName = $Property[4] PermissionType = 'Allow' GPOCount = $_.Count GPONames = if ($Separator) { $_.Group.DisplayName -join $Separator } else { $_.Group.DisplayName } } } elseif ($Property.Count -eq 5) { [PSCustomObject] @{ Permission = $Property[0] PrincipalSidType = $Property[1] PrincipalName = $Property[2] PrincipalDomainName = $Property[3] DomainName = $Property[4] PermissionType = if ($Property[5]) { $Property[5] } else { 'Owner' } GPOCount = $_.Count GPONames = if ($Separator) { $_.Group.DisplayName -join $Separator } else { $_.Group.DisplayName } } } else { [PSCustomObject] @{ Permission = $Property[0] PrincipalSidType = $Property[1] PrincipalName = '' PrincipalDomainName = '' DomainName = $Property[2] PermissionType = if ($Property[3]) { $Property[3] } else { 'Owner' } GPOCount = $_.Count GPONames = if ($Separator) { $_.Group.DisplayName -join $Separator } else { $_.Group.DisplayName } } } } #> } function Get-GPOZaurrSysvolDFSR { [cmdletBinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [string[]] $ExcludeDomainControllers, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [alias('DomainControllers')][string[]] $IncludeDomainControllers, [switch] $SkipRODC, [System.Collections.IDictionary] $ExtendedForestInformation, [string] $SearchDFSR = 'SYSVOL Share' ) $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { Write-Verbose "Get-GPOZaurrSysvolDFSR - Processing $Domain" foreach ($DC in $ForestInformation.DomainDomainControllers[$Domain]) { Write-Verbose "Get-GPOZaurrSysvolDFSR - Processing $Domain \ $($DC.HostName)" #$QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0] $DFSRConfig = Get-CimInstance -Namespace 'root\microsoftdfs' -Class 'dfsrreplicatedfolderconfig' -ComputerName $($DC.HostName) | Where-Object { $_.ReplicatedFolderName -eq $SearchDFSR } $DFSR = Get-CimInstance -Namespace 'root\microsoftdfs' -Class 'dfsrreplicatedfolderinfo' -ComputerName $($DC.HostName) | Where-Object { $_.ReplicatedFolderName -eq $SearchDFSR } if ($DFSR -and $DFSRConfig -and ($DFSR.ReplicatedFolderGuid -eq $DFSRConfig.ReplicatedFolderGuid)) { [PSCustomObject] @{ ComputerName = $DFSR.PSComputerName DomainName = $Domain ConflictPath = $DFSRConfig.ConflictPath LastConflictCleanupTime = $DFSR.LastConflictCleanupTime CurrentConflictSizeInMb = $DFSR.CurrentConflictSizeInMb MaximumConflictSizeInMb = $DFSRConfig.ConflictSizeInMb LastErrorCode = $DFSR.LastErrorCode LastErrorMessageId = $DFSR.LastErrorMessageId LastTombstoneCleanupTime = $DFSR.LastTombstoneCleanupTime ReplicatedFolderGuid = $DFSR.ReplicatedFolderGuid DFSRConfig = $DFSRConfig DFSR = $DFSR } } else { Write-Warning "Get-GPOZaurrSysvolDFSR - Couldn't process $($DC.HostName). Conditions not met." } } } } function Get-GPOZaurrWMI { [cmdletBinding()] Param( [Guid[]] $Guid, [string[]] $Name, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $AsHashtable ) $Dictionary = [ordered] @{} $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] $Objects = @( if ($Name) { foreach ($N in $Name) { try { $ldapFilter = "(&(objectClass=msWMI-Som)(msWMI-Name=$N))" Get-ADObject -LDAPFilter $ldapFilter -Properties $wmiFilterAttr -Server $QueryServer } catch { Write-Warning "Get-GPOZaurrWMI - Error processing WMI for $Domain`: $($_.Error.Exception)" } } } elseif ($GUID) { foreach ($G in $GUID) { try { $ldapFilter = "(&(objectClass=msWMI-Som)(Name={$G}))" Get-ADObject -LDAPFilter $ldapFilter -Properties $wmiFilterAttr -Server $QueryServer } catch { Write-Warning "Get-GPOZaurrWMI - Error processing WMI for $Domain`: $($_.Error.Exception)" } } } else { try { $ldapFilter = '(objectClass=msWMI-Som)' Get-ADObject -LDAPFilter $ldapFilter -Properties $wmiFilterAttr -Server $QueryServer } catch { Write-Warning "Get-GPOZaurrWMI - Error processing WMI for $Domain`: $($_.Error.Exception)" } } ) foreach ($_ in $Objects) { $WMI = $_.'msWMI-Parm2' -split ';' #$WMI = $_.'msWMI-Parm2'.Split(';',8) [Array] $Data = for ($i = 0; $i -lt $WMI.length; $i += 6) { if ($WMI[$i + 5]) { #[PSCustomObject] @{ # NameSpace = $WMI[$i + 5] # Query = $WMI[$i + 6] #} -join ($WMI[$i + 5], ';' , $WMI[$i + 6]) } } $WMIObject = [PSCustomObject] @{ DisplayName = $_.'msWMI-Name' Description = $_.'msWMI-Parm1' DomainName = $Domain #NameSpace = $WMI[$i + 5] #Query = $WMI[$i + 6] QueryCount = $Data.Count Query = $Data -join ',' Author = $_.'msWMI-Author' ID = $_.'msWMI-ID' Created = $_.Created Modified = $_.Modified ObjectGUID = $_.'ObjectGUID' CanonicalName = $_.CanonicalName DistinguishedName = $_.'DistinguishedName' } if (-not $AsHashtable) { $WMIObject } else { $Dictionary[$WMIObject.ID] = $WMIObject } } } if ($AsHashtable) { $Dictionary } } <# 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 Invoke-GPOZaurr { [alias('Show-GPOZaurr', 'Show-GPO')] [cmdletBinding()] param( [string] $FilePath, [string[]] $Type, [switch] $PassThru, [switch] $HideHTML, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains ) Reset-GPOZaurrStatus # This makes sure types are at it's proper status $Script:Reporting = [ordered] @{} $Script:Reporting['Version'] = Get-GitHubVersion -Cmdlet 'Invoke-GPOZaurr' -RepositoryOwner 'evotecit' -RepositoryName 'GPOZaurr' Write-Color '[i]', "[GPOZaurr] ", 'Version', ' [Informative] ', $Script:Reporting['Version'] -Color Yellow, DarkGray, Yellow, DarkGray, Magenta # Verify requested types are supported $Supported = [System.Collections.Generic.List[string]]::new() [Array] $NotSupported = foreach ($T in $Type) { if ($T -notin $Script:GPOConfiguration.Keys ) { $T } else { $Supported.Add($T) } } if ($Supported) { Write-Color '[i]', "[GPOZaurr] ", 'Supported types', ' [Informative] ', "Chosen by user: ", ($Supported -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta } if ($NotSupported) { Write-Color '[i]', "[GPOZaurr] ", 'Not supported types', ' [Informative] ', "Following types are not supported: ", ($NotSupported -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta Write-Color '[i]', "[GPOZaurr] ", 'Not supported types', ' [Informative] ', "Please use one/multiple from the list: ", ($Script:GPOConfiguration.Keys -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta return } $DisplayForest = if ($Forest) { $Forest } else { 'Not defined. Using current one' } $DisplayIncludedDomains = if ($IncludeDomains) { $IncludeDomains -join "," } else { 'Not defined. Using all domains of forest' } $DisplayExcludedDomains = if ($ExcludeDomains) { $ExcludeDomains -join ',' } else { 'No exclusions provided' } Write-Color '[i]', "[GPOZaurr] ", 'Domain Information', ' [Informative] ', "Forest: ", $DisplayForest -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta Write-Color '[i]', "[GPOZaurr] ", 'Domain Information', ' [Informative] ', "Included Domains: ", $DisplayIncludedDomains -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta Write-Color '[i]', "[GPOZaurr] ", 'Domain Information', ' [Informative] ', "Excluded Domains: ", $DisplayExcludedDomains -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta # Lets make sure we only enable those types which are requestd by user if ($Type) { foreach ($T in $Script:GPOConfiguration.Keys) { $Script:GPOConfiguration[$T].Enabled = $false } # Lets enable all requested ones foreach ($T in $Type) { $Script:GPOConfiguration[$T].Enabled = $true } } # Build data foreach ($T in $Script:GPOConfiguration.Keys) { if ($Script:GPOConfiguration[$T].Enabled -eq $true) { $Script:Reporting[$T] = [ordered] @{ Name = $Script:GPOConfiguration[$T].Name ActionRequired = $null Data = $null WarningsAndErrors = $null Time = $null Summary = $null Variables = Copy-Dictionary -Dictionary $Script:GPOConfiguration[$T]['Variables'] } $TimeLogGPOList = Start-TimeLog Write-Color -Text '[i]', '[Start] ', $($Script:GPOConfiguration[$T]['Name']) -Color Yellow, DarkGray, Yellow $OutputCommand = Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Execute'] -WarningVariable CommandWarnings -ErrorVariable CommandErrors -ArgumentList $Forest, $ExcludeDomains, $IncludeDomains if ($OutputCommand -is [System.Collections.IDictionary]) { # in some cases the return will be wrapped in Hashtable/orderedDictionary and we need to handle this without an array $Script:Reporting[$T]['Data'] = $OutputCommand } else { # since sometimes it can be 0 or 1 objects being returned we force it being an array $Script:Reporting[$T]['Data'] = [Array] $OutputCommand } Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Processing'] $Script:Reporting[$T]['WarningsAndErrors'] = @( foreach ($War in $CommandWarnings) { [PSCustomObject] @{ Type = 'Warning' Comment = $War Reason = '' TargetName = '' } } foreach ($Err in $CommandErrors) { [PSCustomObject] @{ Type = 'Error' Comment = $Err Reason = $Err.CategoryInfo.Reason TargetName = $Err.CategoryInfo.TargetName } } ) if ($Script:GPOConfiguration[$T]['Summary']) { $Script:Reporting[$T]['Summary'] = Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Summary'] } $TimeEndGPOList = Stop-TimeLog -Time $TimeLogGPOList -Option OneLiner $Script:Reporting[$T]['Time'] = $TimeEndGPOList Write-Color -Text '[i]', '[End ] ', $($Script:GPOConfiguration[$T]['Name']), " [Time to execute: $TimeEndGPOList]" -Color Yellow, DarkGray, Yellow, DarkGray } } # Generate pretty HTML $TimeLogHTML = Start-TimeLog Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report' -Color Yellow, DarkGray, Yellow New-HTML -Author 'Przemysław Kłys' -TitleText 'GPOZaurr Report' { New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow New-HTMLPanelStyle -BorderRadius 0px New-HTMLTableOption -DataStore JavaScript -BoolAsString New-HTMLHeader { New-HTMLSection -Invisible { New-HTMLSection { New-HTMLText -Text "Report generated on $(Get-Date)" -Color Blue } -JustifyContent flex-start -Invisible New-HTMLSection { New-HTMLText -Text "GPOZaurr - $($Script:Reporting['Version'])" -Color Blue } -JustifyContent flex-end -Invisible } } if ($Type.Count -eq 1) { foreach ($T in $Script:GPOConfiguration.Keys) { if ($Script:GPOConfiguration[$T].Enabled -eq $true) { & $Script:GPOConfiguration[$T]['Solution'] } } } else { foreach ($T in $Script:GPOConfiguration.Keys) { if ($Script:GPOConfiguration[$T].Enabled -eq $true) { New-HTMLTab -Name $Script:GPOConfiguration[$T]['Name'] { & $Script:GPOConfiguration[$T]['Solution'] } } } } } -Online -ShowHTML:(-not $HideHTML) -FilePath $FilePath $TimeLogEndHTML = Stop-TimeLog -Time $TimeLogHTML -Option OneLiner Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report', " [Time to execute: $TimeLogEndHTML]" -Color Yellow, DarkGray, Yellow, DarkGray if ($PassThru) { $Script:Reporting } Reset-GPOZaurrStatus # This makes sure types are at it's proper status } [scriptblock] $SourcesAutoCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $Script:GPOConfiguration.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" } } Register-ArgumentCompleter -CommandName Invoke-GPOZaurr -ParameterName Type -ScriptBlock $SourcesAutoCompleter function Invoke-GPOZaurrContent { [alias('Find-GPO')] [cmdletBinding(DefaultParameterSetName = 'Default')] param( [Parameter(ParameterSetName = 'Default')][alias('ForestName')][string] $Forest, [Parameter(ParameterSetName = 'Default')][string[]] $ExcludeDomains, [Parameter(ParameterSetName = 'Default')][alias('Domain', 'Domains')][string[]] $IncludeDomains, [Parameter(ParameterSetName = 'Default')][System.Collections.IDictionary] $ExtendedForestInformation, [Parameter(ParameterSetName = 'Local')][string] $GPOPath, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [string[]] $Type, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [string] $Splitter = [System.Environment]::NewLine, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $FullObjects, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [ValidateSet('HTML', 'Object')][string[]] $OutputType = 'Object', [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [string] $OutputPath, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $Open, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $Online, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $CategoriesOnly, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $SingleObject, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $SkipNormalize, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Local')] [switch] $SkipCleanup, [switch] $Extended ) if ($Type.Count -eq 0) { $Type = $Script:GPODitionary.Keys } if ($GPOPath) { Write-Verbose "Invoke-GPOZaurrContent - Reading GPOs from $GPOPath" if (Test-Path -LiteralPath $GPOPath) { $GPOFiles = Get-ChildItem -LiteralPath $GPOPath -Recurse -File -Filter *.xml [Array] $GPOs = foreach ($File in $GPOFiles) { if ($File.Name -ne 'GPOList.xml') { try { [xml] $GPORead = Get-Content -LiteralPath $File.FullName } catch { Write-Warning "Invoke-GPOZaurrContent - Couldn't process $($File.FullName) error: $($_.Exception.message)" continue } [PSCustomObject] @{ DisplayName = $GPORead.GPO.Name DomainName = $GPORead.GPO.Identifier.Domain.'#text' GUID = $GPORead.GPO.Identifier.Identifier.'#text' -replace '{' -replace '}' GPOOutput = $GPORead } } } } else { Write-Warning "Invoke-GPOZaurrContent - $GPOPath doesn't exists." return } } else { Write-Verbose "Invoke-GPOZaurrContent - Query AD for GPOs" [Array] $GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } # This caches single reports. $TemporaryCachedSingleReports = [ordered] @{} $TemporaryCachedSingleReports['ReportsSingle'] = [ordered] @{} # This will be returned $Output = [ordered] @{} $Output['Reports'] = [ordered] @{} $Output['CategoriesFull'] = [ordered] @{} Write-Verbose "Invoke-GPOZaurrContent - Loading GPO Report to Categories" [Array] $GPOCategories = foreach ($GPO in $GPOs) { if ($GPOPath) { $GPOOutput = $GPO.GPOOutput } else { [xml] $GPOOutput = Get-GPOReport -Guid $GPO.GUID -Domain $GPO.DomainName -ReportType Xml } Get-GPOCategories -GPO $GPO -GPOOutput $GPOOutput.GPO -Splitter $Splitter -FullObjects:$FullObjects -CachedCategories $Output['CategoriesFull'] } $Output['Categories'] = $GPOCategories | Select-Object -Property * -ExcludeProperty DataSet if ($CategoriesOnly) { # Return Categories only return $Output['Categories'] } # We check our dictionary for reports that are based on reports to make sure we run CodeSingle separatly [Array] $FindRequiredSingle = foreach ($Key in $Script:GPODitionary.Keys) { $Script:GPODitionary[$Key].ByReports.Report } # Build reports based on categories if ($Output['CategoriesFull'].Count -gt 0) { foreach ($Report in $Type) { Write-Verbose "Invoke-GPOZaurrContent - Processing report type $Report" foreach ($CategoryType in $Script:GPODitionary[$Report].Types) { $Category = $CategoryType.Category $Settings = $CategoryType.Settings # Those are checks for making sure we have data to be even able to process it if (-not $Output['CategoriesFull'][$Category]) { continue } if (-not $Output['CategoriesFull'][$Category][$Settings]) { continue } # Translation $CategorizedGPO = $Output['CategoriesFull'][$Category][$Settings] foreach ($GPO in $CategorizedGPO) { if (-not $Output['Reports'][$Report]) { $Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new() } # Create temporary storage for "single gpo" reports # it's required if we want to base reports on other reports later on if (-not $TemporaryCachedSingleReports['ReportsSingle'][$Report]) { $TemporaryCachedSingleReports['ReportsSingle'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new() } # Make sure translated gpo is null $TranslatedGpo = $null if ($SingleObject -or ($Report -in $FindRequiredSingle)) { # We either create 1 GPO with multiple settings to return it as user requested it # Or we process it only because we need to base it for reports based on other reports if (-not $Script:GPODitionary[$Report]['CodeSingle']) { # sometimes code and code single are identical. To not define things two times, one can just skip it If ($Script:GPODitionary[$Report]['Code']) { $Script:GPODitionary[$Report]['CodeSingle'] = $Script:GPODitionary[$Report]['Code'] } } if ($Script:GPODitionary[$Report]['CodeSingle']) { #Write-Verbose "Invoke-GPOZaurrContent - Processing $Report single entry mode" $TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeSingle'] if ($Report -in $FindRequiredSingle) { foreach ($T in $TranslatedGpo) { $TemporaryCachedSingleReports['ReportsSingle'][$Report].Add($T) } } if ($SingleObject) { foreach ($T in $TranslatedGpo) { $Output['Reports'][$Report].Add($T) } } } } if (-not $SingleObject) { # We want each GPO to be listed multiple times if it makes sense for reporting # think drive mapping - showing 1 mapping of a drive per object even if there are 50 drive mappings within 1 gpo # this would result in 50 objects created if ($Script:GPODitionary[$Report]['Code']) { #Write-Verbose "Invoke-GPOZaurrContent - Processing $Report multi entry mode" $TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['Code'] foreach ($T in $TranslatedGpo) { $Output['Reports'][$Report].Add($T) } } } } } } } # Those reports are based on other reports (for example already processed registry settings) # This is useful where going thru registry collections may not be efficient enough to try and read it directly again foreach ($Report in $Type) { foreach ($ReportType in $Script:GPODitionary[$Report].ByReports) { if (-not $Output['Reports'][$Report]) { $Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new() } $FindReport = $ReportType.Report Write-Verbose "Invoke-GPOZaurrContent - Processing reports based on other report $Report ($FindReport)" foreach ($GPO in $TemporaryCachedSingleReports['ReportsSingle'][$FindReport]) { $TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeReport'] foreach ($T in $TranslatedGpo) { $Output['Reports'][$Report].Add($T) } } } } # Normalize - meaning that before we return each GPO report we make sure that each entry has the same column names regardless which one is first. # Normally if you would have a GPO with just 2 entries for given subject (say LAPS), and then another GPO having 5 settings for the same type # and you would display them one after another - all entries would be shown using first object which has less properties then 2nd or 3rd object # to make sure all objects are having same (even empty) properties we "normalize" it if (-not $SkipNormalize) { foreach ($Report in [string[]] $Output['Reports'].Keys) { $FirstProperties = 'DisplayName', 'DomainName', 'GUID', 'GpoType' #$EndProperties = 'CreatedTime', 'ModifiedTime', 'ReadTime', 'Filters', 'Linked', 'LinksCount', 'Links' $EndProperties = 'Filters', 'Linked', 'LinksCount', 'Links' $Properties = $Output['Reports'][$Report] | Select-Properties -ExcludeProperty ($FirstProperties + $EndProperties) -AllProperties -WarningAction SilentlyContinue $DisplayProperties = @( $FirstProperties foreach ($Property in $Properties) { $Property } $EndProperties ) $Output['Reports'][$Report] = $Output['Reports'][$Report] | Select-Object -Property $DisplayProperties } } $Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Name #-Descending #$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Count -Descending if (-not $SkipCleanup) { Write-Verbose "Invoke-GPOZaurrContent - Cleaning up output" Remove-EmptyValue -Hashtable $Output -Recursive } if ($Extended) { $Output } else { if ($Output.Reports) { if ($OutputType -eq 'Object') { $Output.Reports } if ($OutputType -eq 'HTML') { if (-not $OutputPath) { $OutputPath = Get-FileName -Extension 'html' -Temporary Write-Warning "Invoke-GPOZaurrContent - OutputPath not given. Using $OutputPath" } Write-Verbose "Invoke-GPOZaurrContent - Generating HTML output" New-HTML { New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey New-HTMLTableOption -DataStore JavaScript foreach ($Key in $Output.Reports.Keys) { New-HTMLTab -Name $Key { Write-Verbose "Invoke-GPOZaurrContent - Generating HTML Table for $Key" New-HTMLTable -DataTable $Output.Reports[$Key] -Filtering -Title $Key } } } -FilePath $OutputPath -ShowHTML:$Open -Online:$Online } } else { Write-Warning "Invoke-GPOZaurrContent - There was no data output for requested types." } } } [scriptblock] $SourcesAutoCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $Script:GPODitionary.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" } } Register-ArgumentCompleter -CommandName Invoke-GPOZaurrContent -ParameterName Type -ScriptBlock $SourcesAutoCompleter function Invoke-GPOZaurrPermission { [cmdletBinding(SupportsShouldProcess)] param( [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [parameter(Position = 0)] [scriptblock] $PermissionRules, # ParameterSet1 [Parameter(ParameterSetName = 'GPOName')][string] $GPOName, # ParameterSet2 [Parameter(ParameterSetName = 'GPOGUID')][alias('GUID', 'GPOID')][string] $GPOGuid, # ParameterSet3 [parameter(ParameterSetName = 'Level', Mandatory)][int] $Level, [parameter(ParameterSetName = 'Level', Mandatory)][int] $Limit, # ParameterSet4 [parameter(ParameterSetName = 'Linked', Mandatory)][validateset('Root', 'DomainControllers', 'Site', 'Other')][string] $Linked, # ParameterSet5 [parameter(ParameterSetName = 'ADObject', ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject, # ParameterSet6 [parameter(ParameterSetName = 'Filter')][string] $Filter = "(objectClass -eq 'organizationalUnit' -or objectClass -eq 'domainDNS' -or objectClass -eq 'site')", [parameter(ParameterSetName = 'Filter')][string] $SearchBase, [parameter(ParameterSetName = 'Filter')][Microsoft.ActiveDirectory.Management.ADSearchScope] $SearchScope, # All other paramerrs are for for all parametersets [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [validateSet('Unknown', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'All')][string[]] $Type, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [Array] $ApprovedGroups, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [alias('Principal')][Array] $Trustee, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [Microsoft.GroupPolicy.GPPermissionType] $TrusteePermissionType, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [alias('PrincipalType')][validateset('DistinguishedName', 'Name', 'Sid')][string] $TrusteeType = 'DistinguishedName', [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [System.Collections.IDictionary] $GPOCache, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [alias('ForestName')][string] $Forest, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [string[]] $ExcludeDomains, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [alias('Domain', 'Domains')][string[]] $IncludeDomains, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [System.Collections.IDictionary] $ExtendedForestInformation, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [parameter(ParameterSetName = 'Level')] [switch] $LimitAdministrativeGroupsToDomain, [Parameter(ParameterSetName = 'GPOGUID')] [Parameter(ParameterSetName = 'GPOName')] [parameter(ParameterSetName = 'Filter')] [parameter(ParameterSetName = 'ADObject')] [parameter(ParameterSetName = 'Linked')] [switch] $SkipDuplicates ) if ($PermissionRules) { $Rules = & $PermissionRules } else { Write-Warning "Invoke-GPOZaurrPermission - No rules defined. Stopping processing." return } $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation if ($LimitAdministrativeGroupsToDomain) { # This will get administrative based on IncludeDomains if given. It means that if GPO has Domain admins added from multiple domains it will only find one, and remove all other Domain Admins (if working with Domain Admins that is) $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ForestInformation } else { $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest #-IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ForestInformation } if ($PSCmdlet.ParameterSetName -ne 'Level') { $Splat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ForestInformation SkipDuplicates = $SkipDuplicates.IsPresent } if ($ADObject) { $Splat['ADObject'] = $ADObject } elseif ($Linked) { $Splat['Linked'] = $Linked } elseif ($GPOName) { } elseif ($GPOGuid) { } else { if ($Filter) { $Splat['Filter'] = $Filter } if ($SearchBase) { $Splat['SearchBase'] = $SearchBase } if ($SearchScope) { $Splat['SearchScope'] = $SearchScope } } Get-GPOZaurrLink @Splat | ForEach-Object -Process { $GPO = $_ foreach ($Rule in $Rules) { if ($Rule.Action -eq 'Owner') { if ($Rule.Type -eq 'Administrative') { # We check for Owner (sometimes it can be empty) if ($GPO.Owner) { $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($GPO.Owner)"] } else { $AdministrativeGroup = $null } if (-not $AdministrativeGroup) { $DefaultPrincipal = $ADAdministrativeGroups["$($GPO.DomainName)"]['DomainAdmins'] Write-Verbose "Invoke-GPOZaurrPermission - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) to $DefaultPrincipal" #Set-ADACLOwner -ADObject $GPO.GPODistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference Set-GPOZaurrOwner -GPOGuid $GPO.Guid -IncludeDomains $GPO.Domain -Principal $DefaultPrincipal -WhatIf:$WhatIfPreference } } elseif ($Rule.Type -eq 'Default') { Write-Verbose "Invoke-GPOZaurrPermission - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) to $($Rule.Principal)" #Set-ADACLOwner -ADObject $GPO.GPODistinguishedName -Principal $Rule.Principal -Verbose:$false -WhatIf:$WhatIfPreference Set-GPOZaurrOwner -GPOGuid $GPO.Guid -IncludeDomains $GPO.Domain -Principal $Rule.Principal -WhatIf:$WhatIfPreference } } elseif ($Rule.Action -eq 'Remove') { $GPOPermissions = Get-GPOZaurrPermission -GPOGuid $GPO.GUID -IncludeDomains $GPO.DomainName -IncludePermissionType $Rule.IncludePermissionType -ExcludePermissionType $Rule.ExcludePermissionType -Type $Rule.Type -IncludeGPOObject -PermitType $Rule.PermitType -Principal $Rule.Principal -PrincipalType $Rule.PrincipalType -ExcludePrincipal $Rule.ExcludePrincipal -ExcludePrincipalType $Rule.ExcludePrincipalType -ADAdministrativeGroups $ADAdministrativeGroups foreach ($Permission in $GPOPermissions) { Remove-PrivPermission -Principal $Permission.Sid -PrincipalType Sid -GPOPermission $Permission -IncludePermissionType $Permission.Permission } } elseif ($Rule.Action -eq 'Add') { # Initially we were askng for same domain as user requested, but in fact we need to apply GPODomain as it can be linked to different domain $SplatPermissions = @{ #Forest = $Forest IncludeDomains = $GPO.DomainName #ExcludeDomains = $ExcludeDomains #ExtendedForestInformation = $ForestInformation GPOGuid = $GPO.GUID IncludePermissionType = $Rule.IncludePermissionType Type = $Rule.Type PermitType = $Rule.PermitType Principal = $Rule.Principal ADAdministrativeGroups = $ADAdministrativeGroups } if ($Rule.PrincipalType) { $SplatPermissions.PrincipalType = $Rule.PrincipalType } Add-GPOZaurrPermission @SplatPermissions } } } } else { # This is special case based on different command $Report = Get-GPOZaurrLinkSummary -Report OneLink $AffectedGPOs = foreach ($GPO in $Report) { $Property = "Level$($Level)" if ($GPO."$Property" -gt $Limit) { foreach ($Rule in $Rules) { if ($Rule.Action -eq 'Owner') { if ($Rule.Type -eq 'Administrative') { # We check for Owner (sometimes it can be empty) if ($GPO.Owner) { $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($GPO.Owner)"] } else { $AdministrativeGroup = $null } if (-not $AdministrativeGroup) { $DefaultPrincipal = $ADAdministrativeGroups["$($GPO.DomainName)"]['DomainAdmins'] Write-Verbose "Invoke-GPOZaurrPermission - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) to $DefaultPrincipal" #Set-ADACLOwner -ADObject $GPO.GPODistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference Set-GPOZaurrOwner -GPOGuid $GPO.Guid -IncludeDomains $GPO.Domain -Principal $DefaultPrincipal -WhatIf:$WhatIfPreference } } elseif ($Rule.Type -eq 'Default') { Write-Verbose "Invoke-GPOZaurrPermission - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) to $($Rule.Principal)" #Set-ADACLOwner -ADObject $GPO.GPODistinguishedName -Principal $Rule.Principal -Verbose:$false -WhatIf:$WhatIfPreference Set-GPOZaurrOwner -GPOGuid $GPO.Guid -IncludeDomains $GPO.Domain -Principal $Rule.Principal -WhatIf:$WhatIfPreference } } elseif ($Rule.Action -eq 'Remove') { $GPOPermissions = Get-GPOZaurrPermission -GPOGuid $GPO.GUID -IncludeDomains $GPO.DomainName -IncludePermissionType $Rule.IncludePermissionType -ExcludePermissionType $Rule.ExcludePermissionType -Type $Rule.Type -IncludeGPOObject -PermitType $Rule.PermitType -Principal $Rule.Principal -PrincipalType $Rule.PrincipalType -ExcludePrincipal $Rule.ExcludePrincipal -ExcludePrincipalType $Rule.ExcludePrincipalType -ADAdministrativeGroups $ADAdministrativeGroups foreach ($Permission in $GPOPermissions) { Remove-PrivPermission -Principal $Permission.Sid -PrincipalType Sid -GPOPermission $Permission -IncludePermissionType $Permission.Permission } } elseif ($Rule.Action -eq 'Add') { # Initially we were askng for same domain as user requested, but in fact we need to apply GPODomain as it can be linked to different domain $SplatPermissions = @{ #Forest = $Forest IncludeDomains = $GPO.DomainName #ExcludeDomains = $ExcludeDomains #ExtendedForestInformation = $ForestInformation GPOGuid = $GPO.GUID IncludePermissionType = $Rule.IncludePermissionType Type = $Rule.Type PermitType = $Rule.PermitType Principal = $Rule.Principal ADAdministrativeGroups = $ADAdministrativeGroups } if ($Rule.PrincipalType) { $SplatPermissions.PrincipalType = $Rule.PrincipalType } Add-GPOZaurrPermission @SplatPermissions } } } } $AffectedGPOs } } function Invoke-GPOZaurrSupport { [cmdletBinding()] param( [ValidateSet('NativeHTML', 'HTML', 'XML', 'Object')][string] $Type = 'HTML', [alias('Server')][string] $ComputerName, [alias('User')][string] $UserName, [string] $Path, [string] $Splitter = [System.Environment]::NewLine, [switch] $PreventShow, [switch] $Offline, [switch] $ForceGPResult ) # if user didn't choose anything, lets run as currently logged in user locally if (-not $UserName -and -not $ComputerName) { $UserName = $Env:USERNAME # we can also check if the session is Administrative and if so request computer policies if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { $ComputerName = $Env:COMPUTERNAME } } If ($Type -eq 'HTML') { $Exists = Get-Command -Name 'New-HTML' -ErrorAction SilentlyContinue if (-not $Exists) { Write-Warning "Invoke-GPOZaurrSupport - PSWriteHTML module is required for HTML functionality. Use XML, Object or NativeHTML option instead." return } } $Command = Get-Command -Name 'Get-GPResultantSetOfPolicy' -ErrorAction SilentlyContinue $NativeCommand = Get-Command -Name 'gpresult' -ErrorAction SilentlyContinue if (-not $Command -and -not $NativeCommand) { Write-Warning "Invoke-GPOZaurrSupport - Neither gpresult or Get-GPResultantSetOfPolicy are available. Terminating." return } $SplatPolicy = @{} if ($Type -in 'Object', 'XML', 'HTML') { if ($Path) { $SplatPolicy['Path'] = $Path } else { $SplatPolicy['Path'] = [io.path]::GetTempFileName().Replace('.tmp', ".xml") } $SplatPolicy['ReportType'] = 'xml' } elseif ($Type -eq 'NativeHTML') { if ($Path) { $SplatPolicy['Path'] = $Path } else { $SplatPolicy['Path'] = [io.path]::GetTempFileName().Replace('.tmp', ".html") } $SplatPolicy['ReportType'] = 'html' } if ($ComputerName) { $SplatPolicy['Computer'] = $ComputerName } if ($UserName) { $SplatPolicy['User'] = $UserName } if ($Command -and -not $ForceGPResult) { try { #Write-Verbose "Request-GPOZaurr - ComputerName: $($SplatPolicy['Computer']) UserName: $($SplatPolicy['User'])" $ResultantSetPolicy = Get-GPResultantSetOfPolicy @SplatPolicy -ErrorAction Stop } catch { if ($_.Exception.Message -eq 'Exception from HRESULT: 0x80041003') { Write-Warning "Request-GPOZaurr - Are you running as admin? $($_.Exception.Message)" return } else { $ErrorMessage = $($_.Exception.Message).Replace([Environment]::NewLine, ' ') Write-Warning "Request-GPOZaurr - Error: $ErrorMessage" return } } } else { $Arguments = @( if ($SplatPolicy['Computer']) { "/S $ComputerName" } if ($SplatPolicy['User']) { "/USER $($SplatPolicy['User'])" } if ($SplatPolicy['ReportType'] -eq 'HTML') { '/H' } elseif ($SplatPolicy['ReportType'] -eq 'XML') { '/X' } $SplatPolicy['Path'] "/F" ) Write-Verbose "Invoke-GPOZaurrSupport - GPResult Arguments: $Arguments" Start-Process -NoNewWindow -FilePath 'gpresult' -ArgumentList $Arguments -Wait } if ($Type -eq 'NativeHTML') { if (-not $PreventShow) { Write-Verbose "Invoke-GPOZaurrSupport - Opening up file $($SplatPolicy['Path'])" Start-Process -FilePath $SplatPolicy['Path'] } return } # Loads created XML by resultant Output if ($SplatPolicy.Path -and (Test-Path -LiteralPath $SplatPolicy.Path)) { [xml] $PolicyContent = Get-Content -LiteralPath $SplatPolicy.Path if ($PolicyContent) { # lets remove temporary XML file Remove-Item -LiteralPath $SplatPolicy.Path } else { Write-Warning "Request-GPOZaurr - Couldn't load XML file from drive $($SplatPolicy.Path). Terminating." return } } else { Write-Warning "Request-GPOZaurr - Couldn't find XML file on drive $($SplatPolicy.Path). Terminating." return } if ($ComputerName) { if (-not $PolicyContent.Rsop.'ComputerResults'.EventsDetails) { Write-Warning "Request-GPOZaurr - Windows Events for Group Policy are missing. Amount of data will be limited. Firewall issue?" } } if ($Type -eq 'XML') { $PolicyContent.Rsop } else { $Output = [ordered] @{ ResultantSetPolicy = $ResultantSetPolicy } if ($PolicyContent.Rsop.ComputerResults) { $Output.ComputerResults = ConvertFrom-XMLRSOP -Content $PolicyContent.Rsop -ResultantSetPolicy $ResultantSetPolicy -ResultsType 'ComputerResults' -Splitter $Splitter } if ($PolicyContent.Rsop.UserResults) { $Output.UserResults = ConvertFrom-XMLRSOP -Content $PolicyContent.Rsop -ResultantSetPolicy $ResultantSetPolicy -ResultsType 'UserResults' -Splitter $Splitter } New-GPOZaurrReportConsole -Results $Output if ($Type -contains 'Object') { $Output } elseif ($Type -contains 'HTML') { New-GPOZaurrReportHTML -Path $Path -Offline:$Offline -Open:(-not $PreventShow) -Support $Output } } } <# GPRESULT [/S system [/U username [/P [password]]]] [/SCOPE scope] [/USER targetusername] [/R | /V | /Z] [(/X | /H) <filename> [/F]] Description: This command line tool displays the Resultant Set of Policy (RSoP) information for a target user and computer. Parameter List: /S system Specifies the remote system to connect to. /U [domain\]user Specifies the user context under which the command should run. Can not be used with /X, /H. /P [password] Specifies the password for the given user context. Prompts for input if omitted. Cannot be used with /X, /H. /SCOPE scope Specifies whether the user or the computer settings need to be displayed. Valid values: "USER", "COMPUTER". /USER [domain\]user Specifies the user name for which the RSoP data is to be displayed. /X <filename> Saves the report in XML format at the location and with the file name specified by the <filename> parameter. (valid in Windows Vista SP1 and later and Windows Server 2008 and later) /H <filename> Saves the report in HTML format at the location and with the file name specified by the <filename> parameter. (valid in Windows at least Vista SP1 and at least Windows Server 2008) /F Forces Gpresult to overwrite the file name specified in the /X or /H command. /R Displays RSoP summary data. /V Specifies that verbose information should be displayed. Verbose information provides additional detailed settings that have been applied with a precedence of 1. /Z Specifies that the super-verbose information should be displayed. Super- verbose information provides additional detailed settings that have been applied with a precedence of 1 and higher. This allows you to see if a setting was set in multiple places. See the Group Policy online help topic for more information. /? Displays this help message. Examples: GPRESULT /R GPRESULT /H GPReport.html GPRESULT /USER targetusername /V GPRESULT /S system /USER targetusername /SCOPE COMPUTER /Z GPRESULT /S system /U username /P password /SCOPE USER /V #> function New-GPOZaurrWMI { [cmdletBinding(SupportsShouldProcess)] param( [parameter(Mandatory)][string] $Name, [string] $Description = ' ', [string] $Namespace = 'root\CIMv2', [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;$Namespace;", $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: $($_.DisplayName) guid: $($_.ID) for $($_.DomainName) already exists." } } } } function Optimize-GPOZaurr { [cmdletBinding()] param( ) $GPOS = Get-GPOZaurr -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($GPO in $GPOS) { if ($GPO.UserSettingsAvailable -eq $false -and $GPO.ComputerSettingsAvailable -eq $false) { if ($GPO.Enabled -ne 'All setttings disabled') { # $GPO } } elseif ($GPO.UserSettingsAvailable -eq $false) { } elseif ($GPO.ComputerSettingsAvailable -eq $false) { } } } function Remove-GPOPermission { [cmdletBinding()] param( [validateSet('Unknown', 'NotWellKnown', 'NotWellKnownAdministrative', 'Administrative', 'NotAdministrative', 'All')][string[]] $Type, [Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType, [Microsoft.GroupPolicy.GPPermissionType[]] $ExcludePermissionType, [validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'Allow', [string[]] $Principal, [validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'Sid', [string[]] $ExcludePrincipal, [validateset('DistinguishedName', 'Name', 'Sid')][string] $ExcludePrincipalType = 'Sid' ) if ($Type) { @{ Action = 'Remove' Type = $Type IncludePermissionType = $IncludePermissionType ExcludePermissionType = $ExcludePermissionType PermitType = $PermitType Principal = $Principal PrincipalType = $PrincipalType ExcludePrincipal = $ExcludePrincipal ExcludePrincipalType = $ExcludePrincipalType } } } function Remove-GPOZaurr { [cmdletBinding(SupportsShouldProcess)] param( [parameter(Mandatory)][validateset('Empty', 'Unlinked')][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 { if ($Type -contains 'Empty') { if ($_.ComputerSettingsAvailable -eq $false -and $_.UserSettingsAvailable -eq $false) { if ($BackupRequired) { try { Write-Verbose "Remove-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName)" $BackupInfo = Backup-GPO -Guid $_.Guid -Domain $_.DomainName -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer $BackupInfo $BackupOK = $true } catch { Write-Warning "Remove-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" $BackupOK = $false } } if (($BackupRequired -and $BackupOK) -or (-not $BackupRequired)) { try { Write-Verbose "Remove-GPOZaurr - Removing GPO $($_.DisplayName) from $($_.DomainName)" Remove-GPO -Domain $_.DomainName -Guid $_.Guid -ErrorAction Stop #-Server $QueryServer } catch { Write-Warning "Remove-GPOZaurr - Removing GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" } } $Count++ if ($LimitProcessing -eq $Count) { break } } } if ($Type -contains 'Unlinked') { if ($_.Linked -eq $false) { if ($BackupRequired) { try { Write-Verbose "Remove-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName)" $BackupInfo = Backup-GPO -Guid $_.Guid -Domain $_.DomainName -Path $BackupFinalPath -ErrorAction Stop #-Server $QueryServer $BackupInfo $BackupOK = $true } catch { Write-Warning "Remove-GPOZaurr - Backing up GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" $BackupOK = $false } } if (($BackupRequired -and $BackupOK) -or (-not $BackupRequired)) { try { Write-Verbose "Remove-GPOZaurr - Removing GPO $($_.DisplayName) from $($_.DomainName)" Remove-GPO -Domain $_.DomainName -Guid $_.Guid -ErrorAction Stop #-Server $QueryServer } catch { Write-Warning "Remove-GPOZaurr - Removing GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)" } } $Count++ if ($LimitProcessing -eq $Count) { break } } } } } End { } } function Remove-GPOZaurrBroken { [alias('Remove-GPOZaurrOrphaned')] [cmdletBinding(SupportsShouldProcess)] param( [ValidateSet('SYSVOL', 'AD')][string[]] $Type = @('SYSVOL', 'AD'), [string] $BackupPath, [switch] $BackupDated, [int] $LimitProcessing = [int32]::MaxValue, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) if ($BackupPath) { if ($BackupDated) { $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))" } else { $BackupFinalPath = $BackupPath } } else { $BackupFinalPath = '' } Get-GPOZaurrBroken -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object { if ($Type -contains 'SYSVOL') { if ($_.Status -eq 'Not available in AD') { $_ } } if ($Type -contains 'AD') { if ($_.Status -eq 'Not available on SYSVOL') { $_ } } } | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object { $GPO = $_ if ($GPO.Status -eq 'Not available in AD') { Write-Verbose "Remove-GPOZaurrBroken - Processing [AD] $($GPO.Path)" if ($BackupFinalPath) { Try { Write-Verbose "Remove-GPOZaurrBroken - Backing up $($GPO.Path)" Copy-Item -LiteralPath $GPO.Path -Recurse -Destination $BackupFinalPath -ErrorAction Stop $BackupWorked = $true } catch { Write-Warning "Remove-GPOZaurrBroken - Error backing up $($GPO.Path) error: $($_.Exception.Message)" $BackupWorked = $false } } if ($BackupWorked -or $BackupFinalPath -eq '') { Write-Verbose "Remove-GPOZaurrBroken - Deleting $($GPO.Path)" try { Remove-Item -Recurse -Force -LiteralPath $GPO.Path -ErrorAction Stop } catch { Write-Warning "Remove-GPOZaurrBroken - Failed to remove file $($GPO.Path): $($_.Exception.Message)." } } } elseif ($GPO.Status -eq 'Not available on SYSVOL') { Write-Verbose "Remove-GPOZaurrBroken - Processing [SYSVOL] $($GPO.DistinguishedName)" try { $ExistingObject = Get-ADObject -Identity $GPO.DistinguishedName -Server $GPO.DomainName -ErrorAction Stop } catch { Write-Warning "Remove-GPOZaurrBroken - Error getting $($GPO.DistinguishedName) from AD error: $($_.Exception.Message)" $ExistingObject = $null } if ($ExistingObject -and $ExistingObject.ObjectClass -eq 'groupPolicyContainer') { Write-Verbose "Remove-GPOZaurrBroken - Removing DN: $($GPO.DistinguishedName) / ObjectClass: $($ExistingObject.ObjectClass)" try { Remove-ADObject -Server $GPO.DomainName -Identity $GPO.DistinguishedName -Recursive -Confirm:$false -ErrorAction Stop } catch { Write-Warning "Remove-GPOZaurrBroken - Failed to remove $($GPO.DistinguishedName) from AD error: $($_.Exception.Message)" } } else { Write-Warning "Remove-GPOZaurrBroken - DistinguishedName $($GPO.DistinguishedName) not found or ObjectClass is not groupPolicyContainer ($($ExistingObject.ObjectClass))" } } } } function Remove-GPOZaurrDuplicateObject { [cmdletBinding(SupportsShouldProcess)] param( [int] $LimitProcessing = [int32]::MaxValue, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $getGPOZaurrDuplicateObjectSplat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation } $DuplicateGpoObjects = Get-GPOZaurrDuplicateObject @getGPOZaurrDuplicateObjectSplat foreach ($Duplicate in $DuplicateGpoObjects | Select-Object -First $LimitProcessing) { try { Remove-ADObject -Identity $Duplicate.ObjectGUID -Recursive -ErrorAction Stop -Server $Duplicate.DomainName -Confirm:$false } catch { Write-Warning "Remove-GPOZaurrDuplicateObject - Deleting $($Duplicate.ConflictDN) / $($Duplicate.DomainName) via GUID: $($Duplicate.ObjectGUID) failed with error: $($_.Exception.Message)" } } } function Remove-GPOZaurrFolders { [cmdletBinding(SupportsShouldProcess)] param( [string] $BackupPath, [switch] $BackupDated, [ValidateSet('All', 'Netlogon', 'Sysvol')][string[]] $Type = 'All', [Parameter(Mandatory)][ValidateSet('NTFRS', 'Empty')][string] $FolderType, [string[]] $FolderName, [int] $LimitProcessing = [int32]::MaxValue, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) if ($BackupPath) { if ($BackupDated) { $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))" } else { $BackupFinalPath = $BackupPath } } else { $BackupFinalPath = '' } Get-GPOZaurrFolders -Type $Type -FolderType $FolderType -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object { if ($FolderName) { foreach ($Folder in $FolderName) { if ($_.Name -eq $Folder) { $_ } } } else { $_ } } | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object { if ($BackupFinalPath) { $SYSVOLRoot = "\\$($_.DomainName)\SYSVOL\$($_.DomainName)\" $DestinationFile = ($_.FullName).Replace($SYSVOLRoot, '') #$DestinationMissingFolder = $DestinationFile.Replace($DestinationFile, '') $DestinationFilePath = [system.io.path]::Combine($BackupFinalPath, $DestinationFile) #$DestinationFolderPath = [system.io.path]::Combine($BackupFinalPath, $DestinationMissingFolder) Write-Verbose "Remove-GPOZaurrFolders - Backing up $($_.FullName)" Try { Copy-Item -LiteralPath $_.FullName -Recurse -Destination $DestinationFilePath -ErrorAction Stop -Force $BackupWorked = $true } catch { Write-Warning "Remove-GPOZaurrFolders - Error backing up error: $($_.Exception.Message)" $BackupWorked = $false } } if ($BackupWorked -or $BackupFinalPath -eq '') { try { Write-Verbose "Remove-GPOZaurrFolders - Removing $($_.FullName)" Remove-Item -Path $_.FullName -Force -Recurse } catch { Write-Warning "Remove-GPOZaurrFolders - Failed to remove directory $($_.FullName): $($_.Exception.Message)." } } } } function Remove-GPOZaurrLegacyFiles { [cmdletBinding(SupportsShouldProcess)] param( [string] $BackupPath, [switch] $BackupDated, [switch] $RemoveEmptyFolders, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [int] $LimitProcessing = [int32]::MaxValue ) if ($BackupPath) { if ($BackupDated) { $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))" } else { $BackupFinalPath = $BackupPath } } else { $BackupFinalPath = '' } $Splat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation Verbose = $VerbosePreference } [Array] $Deleted = Get-GPOZaurrLegacyFiles @Splat | Select-Object -First $LimitProcessing | ForEach-Object { Write-Verbose "Remove-GPOZaurrLegacyFiles - Processing $($_.FullName)" if ($BackupFinalPath) { $SYSVOLRoot = "\\$($_.DomainName)\SYSVOL\$($_.DomainName)\policies\" $DestinationFile = ($_.FullName).Replace($SYSVOLRoot, '') #$DestinationMissingFolder = $DestinationFile.Replace($DestinationFile, '') $DestinationFilePath = [system.io.path]::Combine($BackupFinalPath, $DestinationFile) #$DestinationFolderPath = [system.io.path]::Combine($BackupFinalPath, $DestinationMissingFolder) Write-Verbose "Remove-GPOZaurrLegacyFiles - Backing up $($_.FullName)" $Created = New-Item -ItemType File -Path $DestinationFilePath -Force if ($Created) { Try { Copy-Item -LiteralPath $_.FullName -Recurse -Destination $DestinationFilePath -ErrorAction Stop -Force $BackupWorked = $true } catch { Write-Warning "Remove-GPOZaurrLegacyFiles - Error backing up error: $($_.Exception.Message)" $BackupWorked = $false } } else { $BackupWorked = $false } } if ($BackupWorked -or $BackupFinalPath -eq '') { try { Write-Verbose "Remove-GPOZaurrLegacyFiles - Deleting $($_.FullName)" Remove-Item -Path $_.FullName -ErrorAction Stop -Force $_ } catch { Write-Warning "Remove-GPOZaurrLegacyFiles - Failed to remove file $($_.FullName): $($_.Exception.Message)." } } } if ($Deleted.Count -gt 0) { if ($RemoveEmptyFolders) { $FoldersToCheck = $Deleted.DirectoryName | Sort-Object -Unique foreach ($Folder in $FoldersToCheck) { $FolderName = $Folder.Substring($Folder.Length - 4) if ($FolderName -eq '\Adm') { try { $MeasureCount = Get-ChildItem -LiteralPath $Folder -Force -ErrorAction Stop | Select-Object -First 1 | Measure-Object } catch { Write-Warning "Remove-GPOZaurrLegacyFiles - Couldn't verify if folder $Folder is empty. Skipping. Error: $($_.Exception.Message)." continue } if ($MeasureCount.Count -eq 0) { Write-Verbose "Remove-GPOZaurrLegacyFiles - Deleting empty folder $($Folder)" try { Remove-Item -LiteralPath $Folder -Force -Recurse:$false } catch { Write-Warning "Remove-GPOZaurrLegacyFiles - Failed to remove folder $($Folder): $($_.Exception.Message)." } } else { Write-Verbose "Remove-GPOZaurrLegacyFiles - Skipping not empty folder from deletion $($Folder)" } } } } } } function Remove-GPOZaurrPermission { [cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Global')] param( [Parameter(ParameterSetName = 'GPOName', Mandatory)] [string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID', Mandatory)] [alias('GUID', 'GPOID')][string] $GPOGuid, [string[]] $Principal, [validateset('DistinguishedName', 'Name', 'NetbiosName', 'Sid')][string] $PrincipalType = 'Sid', [validateset('Unknown', 'NotAdministrative', 'Default')][string[]] $Type = 'Default', [alias('PermissionType')][Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType, [Microsoft.GroupPolicy.GPPermissionType[]] $ExcludePermissionType, [switch] $SkipWellKnown, [switch] $SkipAdministrative, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [int] $LimitProcessing ) Begin { $Count = 0 $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ForestInformation if ($Type -eq 'Unknown') { if ($SkipAdministrative -or $SkipWellKnown) { Write-Warning "Remove-GPOZaurrPermission - Using SkipAdministrative or SkipWellKnown while looking for Unknown doesn't make sense as only Unknown will be displayed." } } } Process { if ($Type -contains 'Named' -and $Principal.Count -eq 0) { Write-Warning "Remove-GPOZaurrPermission - When using type Named you need to provide names to remove. Terminating." return } # $GPOPermission.GPOSecurity.RemoveTrustee($GPOPermission.Sid) #void RemoveTrustee(string trustee) #void RemoveTrustee(System.Security.Principal.IdentityReference identity) #$GPOPermission.GPOSecurity.Remove #void RemoveAt(int index) #void IList[GPPermission].RemoveAt(int index) #void IList.RemoveAt(int index) foreach ($Domain in $ForestInformation.Domains) { $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] if ($GPOName) { $getGPOSplat = @{ Name = $GPOName Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } } elseif ($GPOGuid) { $getGPOSplat = @{ Guid = $GPOGuid Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } } else { $getGPOSplat = @{ All = $true Domain = $Domain Server = $QueryServer ErrorAction = 'SilentlyContinue' } } Get-GPO @getGPOSplat | ForEach-Object -Process { $GPOSecurity = $_.GetSecurityInfo() $getPrivPermissionSplat = @{ Principal = $Principal PrincipalType = $PrincipalType #Accounts = $Accounts GPO = $_ SkipWellKnown = $SkipWellKnown.IsPresent SkipAdministrative = $SkipAdministrative.IsPresent IncludeOwner = $false IncludeGPOObject = $true IncludePermissionType = $IncludePermissionType ExcludePermissionType = $ExcludePermissionType ADAdministrativeGroups = $ADAdministrativeGroups SecurityRights = $GPOSecurity } if ($Type -ne 'Default') { $getPrivPermissionSplat['Type'] = $Type } [Array] $GPOPermissions = Get-PrivPermission @getPrivPermissionSplat if ($GPOPermissions.Count -gt 0) { foreach ($Permission in $GPOPermissions) { Remove-PrivPermission -Principal $Permission.PrincipalSid -PrincipalType Sid -GPOPermission $Permission -IncludePermissionType $Permission.Permission #-IncludeDomains $GPO.DomainName } $Count++ if ($Count -eq $LimitProcessing) { # skipping skips per removed permission not per gpo. break } } } } <# Get-GPOZaurrPermission @Splat | ForEach-Object -Process { $GPOPermission = $_ if ($Type -contains 'Unknown') { if ($GPOPermission.SidType -eq 'Unknown') { #Write-Verbose "Remove-GPOZaurrPermission - Removing $($GPOPermission.Sid) from $($GPOPermission.DisplayName) at $($GPOPermission.DomainName)" if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, "Removing $($GPOPermission.Sid) from $($GPOPermission.DisplayName) at $($GPOPermission.DomainName)")) { try { Write-Verbose "Remove-GPOZaurrPermission - Removing permission $($GPOPermission.Permission) for $($GPOPermission.Sid)" $GPOPermission.GPOSecurity.RemoveTrustee($GPOPermission.Sid) $GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity) #$GPOPermission.GPOSecurity.RemoveAt($GPOPermission.GPOSecurityPermissionItem) #$GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity) } catch { Write-Warning "Remove-GPOZaurrPermission - Removing permission $($GPOPermission.Permission) for $($GPOPermission.Sid) with error: $($_.Exception.Message)" } # Set-GPPPermission doesn't work on Unknown Accounts } $Count++ if ($Count -eq $LimitProcessing) { # skipping skips per removed permission not per gpo. break } } } if ($Type -contains 'Named') { } if ($Type -contains 'NotAdministrative') { } if ($Type -contains 'Default') { Remove-PrivPermission -Principal $Principal -PrincipalType $PrincipalType -GPOPermission $GPOPermission -IncludePermissionType $IncludePermissionType } #Set-GPPermission -PermissionLevel None -TargetName $GPOPermission.Sid -Verbose -DomainName $GPOPermission.DomainName -Guid $GPOPermission.GUID #-WhatIf #Set-GPPermission -PermissionLevel GpoRead -TargetName 'Authenticated Users' -TargetType Group -Verbose -DomainName $Domain -Guid $_.GUID -WhatIf } #> } 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 Repair-GPOZaurrNetLogonOwner { [cmdletBinding(SupportsShouldProcess)] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [string] $Principal = 'S-1-5-32-544', [int] $LimitProcessing = [int32]::MaxValue ) $Identity = Convert-Identity -Identity $Principal -Verbose:$false if ($Identity.Error) { Write-Warning "Repair-GPOZaurrNetLogonOwner - couldn't convert Identity $Principal to desired name. Error: $($Identity.Error)" return } $Principal = $Identity.Name $getGPOZaurrNetLogonSplat = @{ OwnerOnly = $true Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation } Get-GPOZaurrNetLogon @getGPOZaurrNetLogonSplat -Verbose | Where-Object { if ($_.OwnerSid -ne 'S-1-5-32-544') { $_ } } | Select-Object -First $LimitProcessing | ForEach-Object { if ($PSCmdlet.ShouldProcess($_.FullName, "Setting NetLogon Owner to $($Principal)")) { Set-FileOwner -JustPath -Path $_.FullName -Owner $Principal -Verbose:$true -WhatIf:$WhatIfPreference } } } function Repair-GPOZaurrPermissionConsistency { [cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')] param( [Parameter(ParameterSetName = 'GPOName')][string] $GPOName, [Parameter(ParameterSetName = 'GPOGUID')][alias('GUID', 'GPOID')][string] $GPOGuid, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [int] $LimitProcessing = [int32]::MaxValue ) $ConsistencySplat = @{ Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation Verbose = $VerbosePreference } if ($GPOName) { $ConsistencySplat['GPOName'] = $GPOName } elseif ($GPOGuid) { $ConsistencySplat['GPOGuid'] = $GPOGUiD } else { $ConsistencySplat['Type'] = 'Inconsistent' } Get-GPOZaurrPermissionConsistency @ConsistencySplat -IncludeGPOObject | Where-Object { if ($_.ACLConsistent -eq $false) { $_ } } | Select-Object -First $LimitProcessing | ForEach-Object { #Write-Verbose "Repair-GPOZaurrPermissionConsistency - Repairing GPO consistency $($_.DisplayName) from domain: $($_.DomainName)" if ($PSCmdlet.ShouldProcess($_.DisplayName, "Reparing GPO permissions consistency in domain $($_.DomainName)")) { try { $_.IncludeGPOObject.MakeAclConsistent() } catch { $ErrorMessage = $_.Exception.Message Write-Warning "Repair-GPOZaurrPermissionConsistency - Failed to set consistency: $($ErrorMessage)." } } } } 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 $_.DomainName -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.DomainName) / BackupId: $($GPO.ID)" try { Restore-GPO -Path $BackupFolder -BackupId $GPO.ID -Domain $GPO.DomainName } catch { Write-Warning "Restore-GPOZaurr - Restoring GPO $($GPO.DisplayName) from $($GPO.DomainName) 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.DomainName) / BackupId: $($GPO.ID)" try { Restore-GPO -Path $BackupFolder -Domain $GPO.DomainName -BackupId $GPO.ID } catch { Write-Warning "Restore-GPOZaurr - Restoring GPO $($GPO.DisplayName) from $($GPO.DomainName) 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 ($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 Write-Verbose "Save-GPOZaurrFiles - Gathering GPO data" $Count = 0 $GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($GPO in $GPOS) { $Count++ Write-Verbose "Save-GPOZaurrFiles - Processing GPO ($Count/$($GPOS.Count)) $($GPO.DomainName) | $($GPO.DisplayName)" $XMLContent = Get-GPOReport -ID $GPO.Guid -ReportType XML -Domain $GPO.DomainName $GPODOmainFolder = [io.path]::Combine($GPOPath, $GPO.DomainName) if (-not (Test-Path -Path $GPODOmainFolder)) { $null = New-Item -ItemType Directory -Path $GPODOmainFolder -Force } $Path = [io.path]::Combine($GPODOmainFolder, "$($GPO.Guid).xml") $XMLContent | Set-Content -LiteralPath $Path -Force -Encoding Unicode } $GPOListPath = [io.path]::Combine($GPOPath, "GPOList.xml") $GPOs | Export-Clixml -Depth 5 -Path $GPOListPath } } function Set-GPOOwner { [cmdletBinding()] param( [validateset('Administrative', 'Default')][string] $Type = 'Default', [string] $Principal ) if ($Type -eq 'Default') { if ($Principal) { @{ Action = 'Owner' Type = 'Default' Principal = $Principal } } } elseif ($Type -eq 'Administrative') { @{ Action = 'Owner' Type = 'Administrative' Principal = '' } } } function Set-GPOZaurrOwner { <# .SYNOPSIS Sets GPO Owner to Domain Admins or other choosen account .DESCRIPTION Sets GPO Owner to Domain Admins or other choosen account. GPO Owner is set in AD and SYSVOL unless specified otherwise. If account doesn't require change, no change is done. .PARAMETER Type Unknown - finds unknown Owners and sets them to Administrative (Domain Admins) or chosen principal NotMatching - find administrative groups only and if sysvol and gpo doesn't match - replace with chosen principal or Domain Admins if not specified NotAdministrative - combination of Unknown/NotMatching and NotAdministrative - replace with chosen principal or Domain Admins if not specified All - if Owner is known it checks if it's Administrative, if it sn't it fixes that. If owner is unknown it fixes it .PARAMETER GPOName Name of GPO. By default all GPOs are targetted .PARAMETER GPOGuid GUID of GPO. By default all GPOs are targetted .PARAMETER Forest Target different Forest, by default current forest is used .PARAMETER ExcludeDomains Exclude domain from search, by default whole forest is scanned .PARAMETER IncludeDomains Include only specific domains, by default whole forest is scanned .PARAMETER ExtendedForestInformation Ability to provide Forest Information from another command to speed up processing .PARAMETER Principal Parameter description .PARAMETER SkipSysvol Set GPO Owner only in Active Directory. By default GPO Owner is being set in both places .PARAMETER LimitProcessing Allows to specify maximum number of items that will be fixed in a single run. It doesn't affect amount of GPOs processed .EXAMPLE Set-GPOZaurrOwner -Type All -Verbose -WhatIf -LimitProcessing 2 .NOTES General notes #> [cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Type')] param( [Parameter(ParameterSetName = 'Type', Mandatory)] [validateset('Unknown', 'NotAdministrative', 'NotMatching', 'All')][string] $Type, [Parameter(ParameterSetName = 'Named')][string] $GPOName, [Parameter(ParameterSetName = 'Named')][alias('GUID', 'GPOID')][string] $GPOGuid, [Parameter(ParameterSetName = 'Type')] [Parameter(ParameterSetName = 'Named')] [alias('ForestName')][string] $Forest, [Parameter(ParameterSetName = 'Type')] [Parameter(ParameterSetName = 'Named')] [string[]] $ExcludeDomains, [Parameter(ParameterSetName = 'Type')] [Parameter(ParameterSetName = 'Named')] [alias('Domain', 'Domains')][string[]] $IncludeDomains, [Parameter(ParameterSetName = 'Type')] [Parameter(ParameterSetName = 'Named')] [System.Collections.IDictionary] $ExtendedForestInformation, [Parameter(ParameterSetName = 'Type')] [Parameter(ParameterSetName = 'Named')] [string] $Principal, [switch] $SkipSysvol, [Parameter(ParameterSetName = 'Type')] [Parameter(ParameterSetName = 'Named')] [int] $LimitProcessing = [int32]::MaxValue, [switch] $Force ) Begin { #Write-Verbose "Set-GPOZaurrOwner - Getting ADAdministrativeGroups" $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation #Write-Verbose "Set-GPOZaurrOwner - Processing GPO for Type $Type" } Process { $getGPOZaurrOwnerSplat = @{ IncludeSysvol = -not $SkipSysvol.IsPresent Forest = $Forest IncludeDomains = $IncludeDomains ExcludeDomains = $ExcludeDomains ExtendedForestInformation = $ExtendedForestInformation ADAdministrativeGroups = $ADAdministrativeGroups Verbose = $VerbosePreference SkipBroken = $true } if ($GPOName) { $getGPOZaurrOwnerSplat['GPOName'] = $GPOName } elseif ($GPOGuid) { $getGPOZaurrOwnerSplat['GPOGuid'] = $GPOGUiD } $Count = 0 Get-GPOZaurrOwner @getGPOZaurrOwnerSplat | Where-Object { $Count++ if ($_.Owner) { $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($_.Owner)"] } else { $AdministrativeGroup = $null } if (-not $SkipSysvol) { if ($_.SysvolOwner) { $AdministrativeGroupSysvol = $ADAdministrativeGroups['ByNetBIOS']["$($_.SysvolOwner)"] } else { $AdministrativeGroupSysvol = $null } } if ($Force) { Write-Verbose "Set-GPOZaurrOwner - Force was used to push new owner to $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner)." $_ } else { if ($Type -eq 'NotAdministrative') { if (-not $AdministrativeGroup -or (-not $AdministrativeGroupSysvol -and -not $SkipSysvol)) { $_ } else { if ($AdministrativeGroup -ne $AdministrativeGroupSysvol) { Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). Fixing required." $_ } } } elseif ($Type -eq 'Unknown') { if (-not $_.Owner -or (-not $_.SysvolOwner -and -not $SkipSysvol)) { $_ } } elseif ($Type -eq 'NotMatching') { if ($SkipSysvol) { Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). SysVol scanning is disabled. Skipping." } else { if ($AdministrativeGroup -ne $AdministrativeGroupSysvol) { #Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). Fixing required." $_ } } } else { # we run with no type, that means we need to either set it to principal or to Administrative if ($_.Owner) { # we check if Principal is not set $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($_.Owner)"] if (-not $SkipSysvol -and $_.SysvolOwner) { $AdministrativeGroupSysvol = $ADAdministrativeGroups['ByNetBIOS']["$($_.SysvolOwner)"] if (-not $AdministrativeGroup -or -not $AdministrativeGroupSysvol) { $_ } } else { if (-not $AdministrativeGroup) { $_ } } } else { $_ } } } } | Select-Object -First $LimitProcessing | ForEach-Object -Process { $GPO = $_ if (-not $Principal) { $DefaultPrincipal = $ADAdministrativeGroups["$($_.DomainName)"]['DomainAdmins'] } else { $DefaultPrincipal = $Principal } if ($Action -eq 'OnlyGPO') { Write-Verbose "Set-GPOZaurrOwner - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) (SID: $($GPO.OwnerSID)) to $DefaultPrincipal" Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference } elseif ($Action -eq 'OnlyFileSystem') { if (-not $SkipSysvol) { Write-Verbose "Set-GPOZaurrOwner - Changing Sysvol Owner GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.SysvolOwner) (SID: $($GPO.SysvolSid)) to $DefaultPrincipal" Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference } } else { Write-Verbose "Set-GPOZaurrOwner - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) (SID: $($GPO.OwnerSID)) to $DefaultPrincipal" Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference if (-not $SkipSysvol) { Write-Verbose "Set-GPOZaurrOwner - Changing Sysvol Owner GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.SysvolOwner) (SID: $($GPO.SysvolSid)) to $DefaultPrincipal" Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference } } } } End { } } $ModuleFunctions = @{ GroupPolicy = @{ 'Add-GPOPermission' = '' 'Add-GPOZaurrPermission' = '' 'Backup-GPOZaurr' = '' 'Clear-GPOZaurrSysvolDFSR' = '' 'ConvertFrom-CSExtension' = '' 'Find-CSExtension' = '' 'Get-GPOZaurr' = '' 'Get-GPOZaurrAD' = '' 'Get-GPOZaurrBackupInformation' = '' 'Get-GPOZaurrBroken' = 'Get-GPOZaurrSysvol' 'Get-GPOZaurrDictionary' = '' 'Get-GPOZaurrDuplicateObject' = '' 'Get-GPOZaurrFiles' = '' 'Get-GPOZaurrFilesPolicyDefinition' = 'Get-GPOZaurrFilesPolicyDefinitions' 'Get-GPOZaurrFolders' = '' 'Get-GPOZaurrInheritance' = '' 'Get-GPOZaurrLegacyFiles' = '' 'Get-GPOZaurrLink' = '' 'Get-GPOZaurrLinkSummary' = '' 'Get-GPOZaurrNetLogon' = '' 'Get-GPOZaurrOwner' = '' 'Get-GPOZaurrPassword' = '' 'Get-GPOZaurrPermission' = '' 'Get-GPOZaurrPermissionConsistency' = '' 'Get-GPOZaurrPermissionRoot' = '' 'Get-GPOZaurrPermissionSummary' = '' 'Get-GPOZaurrSysvolDFSR' = '' 'Get-GPOZaurrWMI' = '' 'Invoke-GPOZaurr' = 'Show-GPOZaurr', 'Show-GPO' 'Invoke-GPOZaurrPermission' = '' 'Invoke-GPOZaurrSupport' = '' 'New-GPOZaurrWMI' = '' 'Optimize-GPOZaurr' = '' 'Remove-GPOPermission' = '' 'Remove-GPOZaurr' = '' 'Remove-GPOZaurrBroken' = 'Remove-GPOZaurrOrphaned' 'Remove-GPOZaurrDuplicateObject' = '' 'Remove-GPOZaurrFolders' = '' 'Remove-GPOZaurrLegacyFiles' = '' 'Remove-GPOZaurrPermission' = '' 'Remove-GPOZaurrWMI' = '' 'Repair-GPOZaurrNetLogonOwner' = '' 'Repair-GPOZaurrPermissionConsistency' = '' 'Restore-GPOZaurr' = '' 'Save-GPOZaurrFiles' = '' 'Set-GPOOwner' = '' 'Set-GPOZaurrOwner' = '' 'Find-GPO' = '' 'Get-GPOZaurrFilesPolicyDefinitions' = '' 'Get-GPOZaurrSysvol' = '' 'Remove-GPOZaurrOrphaned' = '' 'Show-GPO' = '' 'Show-GPOZaurr' = '' } ActiveDirectory = @{ 'Add-GPOPermission' = '' 'Add-GPOZaurrPermission' = '' 'Backup-GPOZaurr' = '' 'Clear-GPOZaurrSysvolDFSR' = '' 'ConvertFrom-CSExtension' = '' 'Find-CSExtension' = '' 'Get-GPOZaurr' = '' 'Get-GPOZaurrAD' = '' 'Get-GPOZaurrBackupInformation' = '' 'Get-GPOZaurrBroken' = 'Get-GPOZaurrSysvol' 'Get-GPOZaurrDictionary' = '' 'Get-GPOZaurrDuplicateObject' = '' 'Get-GPOZaurrFiles' = '' 'Get-GPOZaurrFilesPolicyDefinition' = 'Get-GPOZaurrFilesPolicyDefinitions' 'Get-GPOZaurrFolders' = '' 'Get-GPOZaurrInheritance' = '' 'Get-GPOZaurrLegacyFiles' = '' 'Get-GPOZaurrLink' = '' 'Get-GPOZaurrLinkSummary' = '' 'Get-GPOZaurrNetLogon' = '' 'Get-GPOZaurrOwner' = '' 'Get-GPOZaurrPassword' = '' 'Get-GPOZaurrPermission' = '' 'Get-GPOZaurrPermissionConsistency' = '' 'Get-GPOZaurrPermissionRoot' = '' 'Get-GPOZaurrPermissionSummary' = '' 'Get-GPOZaurrSysvolDFSR' = '' 'Get-GPOZaurrWMI' = '' 'Invoke-GPOZaurr' = 'Show-GPOZaurr', 'Show-GPO' 'Invoke-GPOZaurrPermission' = '' 'Invoke-GPOZaurrSupport' = '' 'New-GPOZaurrWMI' = '' 'Optimize-GPOZaurr' = '' 'Remove-GPOPermission' = '' 'Remove-GPOZaurr' = '' 'Remove-GPOZaurrBroken' = 'Remove-GPOZaurrOrphaned' 'Remove-GPOZaurrDuplicateObject' = '' 'Remove-GPOZaurrFolders' = '' 'Remove-GPOZaurrLegacyFiles' = '' 'Remove-GPOZaurrPermission' = '' 'Remove-GPOZaurrWMI' = '' 'Repair-GPOZaurrNetLogonOwner' = '' 'Repair-GPOZaurrPermissionConsistency' = '' 'Restore-GPOZaurr' = '' 'Save-GPOZaurrFiles' = '' 'Set-GPOOwner' = '' 'Set-GPOZaurrOwner' = '' 'Find-GPO' = '' 'Get-GPOZaurrFilesPolicyDefinitions' = '' 'Get-GPOZaurrSysvol' = '' 'Remove-GPOZaurrOrphaned' = '' 'Show-GPO' = '' 'Show-GPOZaurr' = '' } } [Array] $FunctionsAll = 'Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Clear-GPOZaurrSysvolDFSR', 'ConvertFrom-CSExtension', 'Find-CSExtension', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrBroken', 'Get-GPOZaurrDictionary', 'Get-GPOZaurrDuplicateObject', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrNetLogon', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrPermissionIssue', 'Get-GPOZaurrPermissionRoot', 'Get-GPOZaurrPermissionSummary', 'Get-GPOZaurrSysvolDFSR', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrContent', 'Invoke-GPOZaurrPermission', 'Invoke-GPOZaurrSupport', 'New-GPOZaurrWMI', 'Optimize-GPOZaurr', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrBroken', 'Remove-GPOZaurrDuplicateObject', 'Remove-GPOZaurrFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrNetLogonOwner', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner' [Array] $AliasesAll = 'Find-GPO', 'Get-GPOZaurrFilesPolicyDefinitions', 'Get-GPOZaurrSysvol', 'Remove-GPOZaurrOrphaned', 'Show-GPO', 'Show-GPOZaurr' $AliasesToRemove = [System.Collections.Generic.List[string]]::new() $FunctionsToRemove = [System.Collections.Generic.List[string]]::new() foreach ($Module in $ModuleFunctions.Keys) { try { Import-Module -Name $Module -ErrorAction Stop } catch { foreach ($Function in $ModuleFunctions[$Module].Keys) { $FunctionsToRemove.Add($Function) $ModuleFunctions[$Module][$Function] | ForEach-Object { if ($_) { $AliasesToRemove.Add($_) } } } } } $FunctionsToLoad = foreach ($Function in $FunctionsAll) { if ($Function -notin $FunctionsToRemove) { $Function } } $AliasesToLoad = foreach ($Alias in $AliasesAll) { if ($Alias -notin $AliasesToRemove) { $Alias } } Export-ModuleMember -Function @($FunctionsToLoad) -Alias @($AliasesToLoad) # SIG # Begin signature block # MIIgQAYJKoZIhvcNAQcCoIIgMTCCIC0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4RntQ22QycMIKAf+IePNUGda # pBSgghtvMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0B # AQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg # Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg # +XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT # XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5 # a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g # 0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1 # roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf # GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G # A1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLL # gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3 # cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmr # EthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+ # fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5Q # Z7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu # 838fYxAe+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw # 8jCCBTAwggQYoAMCAQICEAQJGBtf1btmdVNDtW+VUAgwDQYJKoZIhvcNAQELBQAw # ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS # b290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPjTsxx/ # DhGvZ3cH0wsxSRnP0PtFmbE620T1f+Wondsy13Hqdp0FLreP+pJDwKX5idQ3Gde2 # qvCchqXYJawOeSg6funRZ9PG+yknx9N7I5TkkSOWkHeC+aGEI2YSVDNQdLEoJrsk # acLCUvIUZ4qJRdQtoaPpiCwgla4cSocI3wz14k1gGL6qxLKucDFmM3E+rHCiq85/ # 6XzLkqHlOzEcz+ryCuRXu0q16XTmK/5sy350OTYNkO/ktU6kqepqCquE86xnTrXE # 94zRICUj6whkPlKWwfIPEvTFjg/BougsUfdzvL2FsWKDc0GCB+Q4i2pzINAPZHM8 # np+mM6n9Gd8lk9ECAwEAAaOCAc0wggHJMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD # VR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkGCCsGAQUFBwEBBG0w # azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF # BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js # ME8GA1UdIARIMEYwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczov # L3d3dy5kaWdpY2VydC5jb20vQ1BTMAoGCGCGSAGG/WwDMB0GA1UdDgQWBBRaxLl7 # KgqjpepxA8Bg+S32ZXUOWDAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823I # DzANBgkqhkiG9w0BAQsFAAOCAQEAPuwNWiSz8yLRFcgsfCUpdqgdXRwtOhrE7zBh # 134LYP3DPQ/Er4v97yrfIFU3sOH20ZJ1D1G0bqWOWuJeJIFOEKTuP3GOYw4TS63X # X0R58zYUBor3nEZOXP+QsRsHDpEV+7qvtVHCjSSuJMbHJyqhKSgaOnEoAjwukaPA # JRHinBRHoXpoaK+bp1wgXNlxsQyPu6j4xRJon89Ay0BEpRPw5mQMJQhCMrI2iiQC # /i9yfhzXSUWW6Fkd6fp0ZGuy62ZD2rOwjNXpDd32ASDOmTFjPQgaGLOBm0/GkxAG # /AeB+ova+YJJ92JuoVP6EpQYhS6SkepobEQysmah5xikmmRR7zCCBT0wggQloAMC # AQICEATV3B9I6snYUgC6zZqbKqcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQTAeFw0yMDA2MjYwMDAwMDBaFw0yMzA3MDcxMjAwMDBaMHoxCzAJBgNV # BAYTAlBMMRIwEAYDVQQIDAnFmmzEhXNraWUxETAPBgNVBAcTCEthdG93aWNlMSEw # HwYDVQQKDBhQcnplbXlzxYJhdyBLxYJ5cyBFVk9URUMxITAfBgNVBAMMGFByemVt # eXPFgmF3IEvFgnlzIEVWT1RFQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBAL+ygd4sga4ZC1G2xXvasYSijwWKgwapZ69wLaWaZZIlY6YvXTGQnIUnk+Tg # 7EoT7mQiMSaeSPOrn/Im6N74tkvRfQJXxY1cnt3U8//U5grhh/CULdd6M3/Z4h3n # MCq7LQ1YVaa4MYub9F8WOdXO84DANoNVG/t7YotL4vzqZil3S9pHjaidp3kOXGJc # vxrCPAkRFBKvUmYo23QPFa0Rd0qA3bFhn97WWczup1p90y2CkOf28OVOOObv1fNE # EqMpLMx0Yr04/h+LPAAYn6K4YtIu+m3gOhGuNc3B+MybgKePAeFIY4EQzbqvCMy1 # iuHZb6q6ggRyqrJ6xegZga7/gV0CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrE # uXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBQYsTUn6BxQICZOCZA0CxS0TZSU # ZjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAw # bjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1j # cy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYB # BQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGE # BggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0 # LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQC # MAAwDQYJKoZIhvcNAQELBQADggEBAJq9bM+JbCwEYuMBtXoNAfH1SRaMLXnLe0py # VK6el0Z1BtPxiNcF4iyHqMNVD4iOrgzLEVzx1Bf/sYycPEnyG8Gr2tnl7u1KGSjY # enX4LIXCZqNEDQCeTyMstNv931421ERByDa0wrz1Wz5lepMeCqXeyiawqOxA9fB/ # 106liR12vL2tzGC62yXrV6WhD6W+s5PpfEY/chuIwVUYXp1AVFI9wi2lg0gaTgP/ # rMfP1wfVvaKWH2Bm/tU5mwpIVIO0wd4A+qOhEia3vn3J2Zz1QDxEprLcLE9e3Gmd # G5+8xEypTR23NavhJvZMgY2kEXBEKEEDaXs0LoPbn6hMcepR2A4wggZqMIIFUqAD # AgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEw # MjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQK # EwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRl # cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq # 5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0 # iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZb # FP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3 # gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8 # c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkR # j3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG # A1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgB # hv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v # Q1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAA # dABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQA # dQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQA # aQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIA # ZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcA # aABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQA # IABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4A # IABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSME # GDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnka # g0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUF # BwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEG # CCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRB # c3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuG # ziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+ # /iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgd # KG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB # 2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7E # LlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qN # e9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEF # BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ # RCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIB # IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZU # XKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHr # zzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LN # b3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQ # xl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao # 8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQID # AQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMB # BggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1Ud # IASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6 # Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggr # BgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAA # QwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAA # YQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUA # cgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4A # ZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAA # bABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAA # aQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIA # ZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8C # AQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLN # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXD # UOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2Q # wsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JL # FuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP # +1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3 # cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcCAQEwgYYwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC # NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUgUC87Is6 # TINrMmtgB62qxmuzrgAwDQYJKoZIhvcNAQEBBQAEggEACu6iCc/Iuui1TKe9PryI # kXB/awClcO/ZYAW2XBLbfvVMIMRfi2+azDljKjBsSjAS8yJuJVipnyKiD9IQ3e9C # nph3crt2fjk+tcxdWbgW59nrLMAdKpEJGHQ3p99zKuD9s2JgyTW9AGQ4rs3gugUo # kOp6CnI8oP+Yk0/fGQ9DdcsBt7OyMuULbE5jkrBYcyGbA0U+SkPPg8Hp+z+FH3Hk # P1zRZo4xOcSE/QfvlX94jXF8dhMI4bYf+vXjGBbLI0Vs6zvhOCzmRQh+OPEDBoa/ # FK2X7YFmuAvyOWJv1hG3xajwfdA+0OkPVyBpBJ/OWmXkMnvk557Ak07tN/cITUwM # VaGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr # 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMjAxMTIzMTc1MjU3WjAjBgkqhkiG9w0BCQQxFgQUXV9M # poLakEAfZwW5x+COynv2sLwwDQYJKoZIhvcNAQEBBQAEggEAcQNVE8F/vZUAW9w4 # Md5+KKGFZnrEdgxXBf77lWJ97gV5yvfhmXc2XnJxV7Zirqzzhiov8E2ao3E3h8/G # lF306bz/eDEPcRz6irUgYijUOhVGjF2jsWy+c88lBOrjq61KCkYa80kvTJz41LXx # XtLNi8M1EooATAR6vlR1Twjl3K8p67o9QD+bmFFVCcIltKZb744j44ahfdBDo7nB # uwcDar5L3WC3yoSG4nnkqq5Axp3kkTK59ob5Tzte2i3ZxoT8R/IbJczORJ0QxMQq # BgCBTbPsSHIeDvHnY6ZO/kIxpz69Cz0+z4YqCdWP84mlClcKFHutYyZzu3P0+rhF # 8JKZBw== # SIG # End signature block |