mirror of
https://github.com/EvotecIT/GPOZaurr.git
synced 2026-07-26 11:49:17 +00:00
Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37483c91b5 | |||
| 9172948be3 | |||
| 14567959f7 | |||
| 6a9ad14083 | |||
| e00de40cd2 | |||
| 6f806698e9 | |||
| 2e1571b0d7 | |||
| ffedd2d9d2 | |||
| 911bc50f22 | |||
| 0383d249e9 | |||
| 8070179d27 | |||
| db67e6191e | |||
| db9b6d60c6 | |||
| 2c965d81d5 | |||
| bff372affe | |||
| 138a0f411e | |||
| 1c37a9d1df | |||
| 30535c1baf | |||
| 56c722edd0 | |||
| 1f43ae21fe | |||
| ef90ef05f7 | |||
| 43b304d252 | |||
| 24e691edb9 | |||
| 1c787735d4 | |||
| 13a2aa6482 | |||
| 1ac1f83abd | |||
| 65b2867588 | |||
| 1043a2d512 | |||
| 7002813027 | |||
| 03b8e4f2cb | |||
| 680762733e | |||
| 7a37d5b9f6 | |||
| a1e9a4a3fa | |||
| 8cbb3272b2 | |||
| 8da0c1793d | |||
| 5a7b20ca2b | |||
| ebffba3b95 | |||
| a79071c395 | |||
| a5a889b468 | |||
| 62d73041ea | |||
| d88307047c | |||
| 7d85d41747 | |||
| a1fab2ceb2 | |||
| 99f07f61e3 | |||
| 58136f0c43 | |||
| b55729d8e0 | |||
| 6206a33a2e | |||
| 165381b1e3 | |||
| ea1de10fd9 | |||
| 03453b6bdb | |||
| 4d7f49902d | |||
| 508d778d06 | |||
| 5c3157f15f | |||
| b33f42b320 | |||
| f8d3b363e7 | |||
| a68d917d19 | |||
| 946bb3029f | |||
| 975bf5b889 | |||
| 6fa0818375 | |||
| 2abec9579a | |||
| 75a8083368 | |||
| 06c711694e | |||
| 8a38ac4556 | |||
| 9d2ae288c0 | |||
| 0bc50328ee | |||
| a74b662c9b | |||
| 37d8db92f7 | |||
| d094344f9f | |||
| a24b1ff958 | |||
| de076a1cb4 | |||
| 663eecc01b | |||
| d1b7d71ad9 | |||
| d888f135e0 | |||
| 81ecf580c4 | |||
| 34c9d59150 | |||
| 80580b7dce | |||
| 304f70a4d9 | |||
| ab6c61ac52 | |||
| dbe79a7833 | |||
| 0cc05c4aeb | |||
| c10a4c088e | |||
| 015afd69a4 | |||
| 2fa9b202bc | |||
| 25bbf8d52e | |||
| 8a009bf288 | |||
| ed76fc9513 | |||
| 646c22bdab | |||
| d4f8cdc8e8 | |||
| 166d4fb767 | |||
| 0ca98a4165 | |||
| a5bcbbf285 | |||
| 513179fd03 | |||
| 72cd1da9ae | |||
| 3fe52c99b5 | |||
| fc6fe14579 | |||
| bd5a73439d | |||
| 469332e543 | |||
| 6cbe3a375a | |||
| c987ca9863 | |||
| f0d24e1902 | |||
| 61b9c916e7 | |||
| deebcb825d | |||
| 7adfd1ef65 | |||
| 828c221ac9 | |||
| 8e80fd423d | |||
| f52fd60fef | |||
| 9594afb246 | |||
| 7c52284b48 | |||
| 2fd64952af | |||
| 15538e8559 | |||
| a36a470538 | |||
| 0a5d65761c | |||
| 7ed3cb6dad | |||
| 4d9edde212 | |||
| 7ba40e3981 | |||
| ec81655019 | |||
| 752a34204b | |||
| 0afe6c6d23 | |||
| 2a300747a1 | |||
| 94b8a71192 | |||
| 49b9eccfd1 | |||
| 92b5f00d7f | |||
| 1f625e4e90 | |||
| 7ac406ca24 | |||
| 4d584902e8 | |||
| 7e6efb81f1 | |||
| 16b0a0f4b6 | |||
| 85eaa4e5d6 | |||
| 8c555a4fa9 | |||
| 8bc85a4c91 | |||
| 84ad1df972 | |||
| 71a12316e3 | |||
| 4a3a49440f | |||
| 0b2e2ea96c | |||
| 4ae3bbce12 | |||
| 471bc04fb2 | |||
| 4d050c536d | |||
| 508402b4bf | |||
| 6f1ac33df8 | |||
| 5130ef77e5 | |||
| 19c2b6bf90 | |||
| 5b1aaae915 | |||
| d239c979e9 | |||
| 35c04c88c0 | |||
| 048347c830 | |||
| 563ba55342 | |||
| 5a62968925 | |||
| a47fa85a8c | |||
| b156c91c7c | |||
| d44e27e68c | |||
| e5dc00007f |
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$T = Get-GPOZaurrPermission -Type Unknown -Verbose
|
||||
$T | Out-HtmlView -ScrollX -Filtering -DisablePaging -ScrollY -Online
|
||||
$T #| Out-HtmlView #-ScrollX -Filtering -DisablePaging -ScrollY -Online
|
||||
@@ -1,11 +1,11 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Step 1 - Create report
|
||||
$Report = Get-GPOZaurrPermission -Type All
|
||||
$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOutput.xlsx -ExcelWorkSheetName 'GPO Permissions Before' -AutoFilter -AutoFit
|
||||
#$Report = Get-GPOZaurrPermission -Type All
|
||||
#$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOutput.xlsx -ExcelWorkSheetName 'GPO Permissions Before' -AutoFilter -AutoFit
|
||||
|
||||
# Step 2 - Verify couple of GPOS returned with whatif
|
||||
#Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 4 -WhatIf
|
||||
Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 4 -WhatIf
|
||||
|
||||
# Step 3 - Confirm the change without whatif
|
||||
#Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 4
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$SummaryHashtable = Get-GPOZaurrLink -AsHashTable -Summary
|
||||
$SummaryHashtable | Format-Table -AutoSize
|
||||
$SummaryHashtable[5]
|
||||
@@ -1,3 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrLink | Format-Table -AutoSize *
|
||||
Get-GPOZaurrLink -Verbose | Format-Table -AutoSize *
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
Get-GPOZaurrLink -SearchBase 'OU=Domain Controllers,DC=ad,DC=evotec,DC=xyz' | Format-Table -AutoSize
|
||||
|
||||
Get-GPOZaurrLink -SearchBase 'OU=Computers,OU=Production,DC=ad,DC=evotec,DC=xyz' | Format-Table -AutoSize
|
||||
Get-GPOZaurrLink -SearchBase 'OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz' | Format-Table -AutoSize
|
||||
|
||||
Get-GPOZaurrLink -SearchBase 'DC=ad,DC=evotec,DC=xyz' -SearchScope Base | Format-Table -AutoSize
|
||||
@@ -1,6 +1,7 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-ADOrganizationalUnit -Filter * -Properties distinguishedName, LinkedGroupPolicyObjects | Get-GPOZaurrLink | Format-Table
|
||||
Get-ADOrganizationalUnit -Filter * -Properties canonicalname, distinguishedName, LinkedGroupPolicyObjects | Get-GPOZaurrLink | Format-Table
|
||||
# gpLink is required property. While LinkedGroupPolicyObjects does work it doesn't contain information about enabled/enforced GPO
|
||||
Get-ADOrganizationalUnit -Filter * -Properties distinguishedName, LinkedGroupPolicyObjects, gpLink | Get-GPOZaurrLink | Format-Table
|
||||
Get-ADOrganizationalUnit -Filter * -Properties canonicalname, distinguishedName, LinkedGroupPolicyObjects, gpLink | Get-GPOZaurrLink | Format-Table
|
||||
Get-ADObject -Filter * -Properties distinguishedName, gplink -Server 'ad.evotec.pl' | Get-GPOZaurrLink | Format-Table -AutoSize
|
||||
Get-GPOZaurrLink | Format-Table -AutoSize
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrLink -Site 'Katowice-1','Katowice-2' | Format-Table -AutoSize *
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrLink -Linked Site | Format-Table -AutoSize *
|
||||
@@ -0,0 +1,8 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Summary = Get-GPOZaurrLink -Summary
|
||||
$Summary | Format-Table -AutoSize *
|
||||
|
||||
$OneGPO = $Summary | Where-Object { $_.DisplayName -eq 'ALL | Enable RDP' }
|
||||
$OneGPO.Links
|
||||
$OneGPO.LinksObjects | Format-Table *
|
||||
@@ -0,0 +1,41 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Clear-Host
|
||||
|
||||
$GPOName = 'TEST | Deny Admins'
|
||||
|
||||
$T = Get-GPOZaurrPermission -GPOName $GPOName #-IncludePermissionType GpoEditDeleteModifySecurity -PermitType Allow -Principal 'Domain Admins' -PrincipalType 'Name'
|
||||
$T | Format-Table *
|
||||
|
||||
|
||||
return
|
||||
|
||||
<#
|
||||
# this will go and check for both Domain Admins / Enterprise Admins - if found won't do anything
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEditDeleteModifySecurity -Type Administrative -Verbose
|
||||
# this will go thru, because PrincipalType is not set to look by Name. Be precise with what you ask for
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEditDeleteModifySecurity -Principal 'Domain Admins' -Verbose
|
||||
# this will be detected as already existing
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEditDeleteModifySecurity -Principal 'Domain Admins' -PrincipalType Name -Verbose
|
||||
# this will be added only if it doesn't exists - assuming that przemyslaw.klys is displayed in Get-GPOZaurrPermissions
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoApply -Principal 'przemyslaw.klys' -PrincipalType Name -Verbose
|
||||
# this will be added only if it doesn't exists - assuming that przemyslaw.klys is displayed in Get-GPOZaurrPermissions
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEditDeleteModifySecurity -Principal 'przemyslaw.klys' -PrincipalType Name -Verbose
|
||||
# this will ADD system if it doesn't eists
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEditDeleteModifySecurity -Type WellKnownAdministrative -Verbose
|
||||
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoApply -Principal 'przemyslaw.klys' -Verbose
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEditDeleteModifySecurity -Principal 'przemyslaw.klys' -Verbose
|
||||
# this will not work because we already have GPOEditDeleteModifySecurity which is higher than GpoEDIT
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoEdit -Principal 'przemyslaw.klys' -Verbose
|
||||
#>
|
||||
Add-GPOZaurrPermission -GPOName $GPOName -Type AuthenticatedUsers -PermissionType GpoRead -Verbose #-WhatIf
|
||||
|
||||
#$T = Get-GPOZaurrPermission -GPOName $GPOName #-ExcludePermissionType GpoApply,GpoRead -IncludeGPOObject
|
||||
#$T | Format-Table -AutoSize *
|
||||
|
||||
#Remove-GPOZaurrPermission -GPOName $GPOName -PermissionType GpoApply -Principal 'przemyslaw.klys' -PrincipalType Name -Verbose
|
||||
|
||||
#$T = Get-GPOZaurrPermission -GPOName $GPOName #-ExcludePermissionType GpoApply,GpoRead -IncludeGPOObject
|
||||
#$T = Get-GPOZaurrPermission -GPOName $GPOName #-ExcludePermissionType GpoApply,GpoRead -IncludeGPOObject
|
||||
#$T | Format-Table -AutoSize *
|
||||
@@ -9,4 +9,4 @@ Invoke-GPOZaurrPermission -Linked Root -Verbose {
|
||||
Remove-GPOPermission -Type NotAdministrative, NotWellKnownAdministrative -IncludePermissionType GpoEdit, GpoEditDeleteModifySecurity
|
||||
Add-GPOPermission -Type Administrative -IncludePermissionType GpoEditDeleteModifySecurity
|
||||
#Add-GPOPermission -Type WellKnownAdministrative -IncludePermissionType GpoEditDeleteModifySecurity
|
||||
} #-WhatIf #| Format-Table *
|
||||
} -WhatIf
|
||||
@@ -1,11 +1,11 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Invoke-GPOZaurrPermission -Verbose -SearchBase 'OU=Computers,OU=Production,DC=ad,DC=evotec,DC=xyz' {
|
||||
Invoke-GPOZaurrPermission -Verbose -SearchBase 'OU=Production,DC=ad,DC=evotec,DC=pl' {
|
||||
Set-GPOOwner -Type Administrative
|
||||
#Set-GPOOwner -Principal 'EVOTEC\Enterprise Admins'
|
||||
#Set-GPOOwner -Principal 'Domain Admins'
|
||||
#Remove-GPOPermission -Type Administrative -IncludePermissionType GPOCustom
|
||||
Remove-GPOPermission -Type NotAdministrative, NotWellKnownAdministrative -IncludePermissionType GpoEdit, GpoEditDeleteModifySecurity
|
||||
#Add-GPOPermission -Type Administrative -IncludePermissionType GpoEditDeleteModifySecurity
|
||||
#Add-GPOPermission -Type WellKnownAdministrative -IncludePermissionType GpoEditDeleteModifySecurity
|
||||
} -WhatIf #| Format-Table *
|
||||
Add-GPOPermission -Type Administrative -IncludePermissionType GpoEditDeleteModifySecurity
|
||||
Add-GPOPermission -Type WellKnownAdministrative -IncludePermissionType GpoEditDeleteModifySecurity
|
||||
} -WhatIf
|
||||
@@ -1,10 +1,10 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Find broken GPOs
|
||||
Get-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.pl' | Format-Table
|
||||
Get-GPOZaurrBroken -Verbose | Format-Table # -IncludeDomains 'ad.evotec.pl' | Format-Table
|
||||
|
||||
# this allows you to process X amount of orphaned folders/files (good for testing)
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf #-IncludeDomains 'ad.evotec.pl' #-LimitProcessing 2
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -Type AD, SYSVOL -LimitProcessing 1 #-IncludeDomains 'ad.evotec.pl' #-LimitProcessing 2
|
||||
|
||||
# this runs for whole SYSVOL and checks things against GPOS
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf -Type AD, SYSVOL
|
||||
@@ -8,7 +8,7 @@ Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
#Get-GPOZaurrLinkSummary -Report 'LinksSummary' -UnlimitedProperties | Format-Table *
|
||||
|
||||
$Report = Get-GPOZaurrLinkSummary #-UnlimitedProperties
|
||||
$Report | Format-Table *
|
||||
$Report | Format-Table
|
||||
$Report.MultipleLinks | Format-Table *
|
||||
$Report.OneLink | Format-Table *
|
||||
$Report.LinksSummary | Format-Table *
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Output = Invoke-GPOZaurrContent -Verbose -OutputType HTML, Object -Open
|
||||
$Output = Invoke-GPOZaurrContent -Verbose -OutputType HTML, Object -Open -Type LocalGroups
|
||||
$Output | Format-Table
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Policies = Get-GPOZaurrFilesPolicyDefinition -Signature
|
||||
$Policies | Format-Table
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Report on empty folders
|
||||
Get-GPOZaurrFolders -FolderType Empty | Format-Table *
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Cleanup based on https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/manually-clearing-the-conflictanddeleted-folder-in-dfsr/ba-p/395711
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Invoke-GPOZaurrSupport -ComputerName 'ad1.ad.evotec.xyz' -UserName 'EVOTEC\przemyslaw.klys' -Type NativeHTML -ForceGPResult -Verbose
|
||||
|
||||
#Invoke-GPOZaurrSupport -ComputerName 'ad1.ad.evotec.xyz' -UserName 'EVOTEC\przemyslaw.klys' -Type HTML -Verbose -Path $PSScriptRoot\Reports\GPOSupport.html
|
||||
Invoke-GPOZaurrSupport -Type HTML -Verbose -Path $PSScriptRoot\Reports\GPOSupport.html -ComputerName ad1.ad.evotec.xyz
|
||||
return
|
||||
#$Support1 = Invoke-GPOZaurrSupport -ComputerName 'AD1' -UserName 'przemyslaw.klys' -Type Object
|
||||
#$Support = Invoke-GPOZaurrSupport -ComputerName 'AD1' -UserName 'EVOTEC\Administrator' -Type Object
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Extracts CSE from registry
|
||||
$AllRegistryExtensions = Find-CSExtension
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Output = Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type GPOOrphans -PassThru
|
||||
$Output
|
||||
|
||||
Write-Color -Text 'Output of nested report' -Color DarkYellow -LinesBefore 1 -LinesAfter 1
|
||||
$Output.GPOOrphans
|
||||
#$Output = Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -PassThru -Type GPOConsistency, GPOList, GPODuplicates, GPOBroken, GPOOwners, NetLogonOwners, GPOPermissionsRead, GPOPermissionsAdministrative,GPOPermissionsUnknown
|
||||
Invoke-GPOZaurr #-FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type GPOList
|
||||
@@ -1,3 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type NetLogonPermissions, GPOOrphans, GPOList, GPOConsistency, GPOOwners, GPODuplicates
|
||||
Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type GPOConsistency, GPOList, GPODuplicates, GPOBroken, GPOOwners, NetLogonOwners, GPOPermissionsRead, GPOPermissionsAdministrative, GPOPermissionsUnknown, GPOPermissions
|
||||
@@ -5,7 +5,7 @@ $Types = @(
|
||||
@{ Name = 'GPOConsistency'; Path = "$PSScriptRoot\Reports\GPOConsistency.html" }
|
||||
@{ Name = 'GPODuplicates'; Path = "$PSScriptRoot\Reports\GPODuplicates.html" }
|
||||
@{ Name = 'GPOList'; Path = "$PSScriptRoot\Reports\GPOList.html" }
|
||||
@{ Name = 'GPOOrphans'; Path = "$PSScriptRoot\Reports\GPOOrphans.html" }
|
||||
@{ Name = 'GPOBroken'; Path = "$PSScriptRoot\Reports\GPOBroken.html" }
|
||||
@{ Name = 'GPOPassword'; Path = "$PSScriptRoot\Reports\GPOPassword.html" }
|
||||
@{ Name = 'NetLogonPermissions'; Path = "$PSScriptRoot\Reports\NetLogonPermissions.html" }
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$GPOS = Get-GPOZaurr -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\Empty' -ExcludeGroupPolicies @(
|
||||
Skip-GroupPolicy -Name 'de14_usr_std'
|
||||
Skip-GroupPolicy -Name 'de14_usr_std' -DomaiName 'ad.evotec.xyz'
|
||||
)
|
||||
$GPOS | Format-Table -AutoSize *
|
||||
@@ -3,6 +3,6 @@
|
||||
# Optimize All
|
||||
Optimize-GPOZaurr -All -Verbose -IncludeDomains 'ad.evotec.pl' -LimitProcessing 1 {
|
||||
Skip-GroupPolicy -Name 'TEST | Empty GPO - AD.EVOTEC.PL CrossDomain GPO' -DomaiName 'ad.evotec.pl'
|
||||
}
|
||||
} -WhatIf
|
||||
# Optimize just one
|
||||
#Optimize-GPOZaurr -GPOName 'TEST | Empty GPO - AD.EVOTEC.PL CrossDomain GPO' -WhatIf -Verbose
|
||||
Optimize-GPOZaurr -GPOName 'TEST | Empty GPO - AD.EVOTEC.PL CrossDomain GPO' -WhatIf -Verbose
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrPermissionAnalysis | Format-Table *
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Repair-GPOZaurrPermission -Verbose -WhatIf -Type Administrative -LimitProcessing 1
|
||||
@@ -0,0 +1,5 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
#Get-GPOZaurrBrokenLink -Verbose | Format-Table -AutoSize *
|
||||
|
||||
Get-GPOZaurrBrokenLink -Verbose -IncludeDomains ad.evotec.pl | Format-Table -AutoSize *
|
||||
@@ -0,0 +1,5 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Repair-GPOZaurrBrokenLink -Verbose -LimitProcessing 1 -WhatIf
|
||||
|
||||
#Repair-GPOZaurrBrokenLink -Verbose -IncludeDomains ad.evotec.pl -LimitProcessing 30 #-WhatIf
|
||||
+6
-6
@@ -4,11 +4,11 @@
|
||||
CmdletsToExport = @()
|
||||
CompanyName = 'Evotec'
|
||||
CompatiblePSEditions = @('Desktop')
|
||||
Copyright = '(c) 2011 - 2020 Przemyslaw Klys @ Evotec. All rights reserved.'
|
||||
Copyright = '(c) 2011 - 2021 Przemyslaw Klys @ Evotec. All rights reserved.'
|
||||
Description = 'Group Policy Eater is a PowerShell module that aims to gather information about Group Policies but also allows fixing issues that you may find in them.'
|
||||
FunctionsToExport = @('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', 'Set-GPOZaurrStatus', 'Skip-GroupPolicy')
|
||||
FunctionsToExport = @('Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Clear-GPOZaurrSysvolDFSR', 'ConvertFrom-CSExtension', 'Find-CSExtension', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrBroken', 'Get-GPOZaurrBrokenLink', '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-GPOZaurrPermissionAnalysis', '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-GPOZaurrBrokenLink', 'Repair-GPOZaurrNetLogonOwner', 'Repair-GPOZaurrPermission', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner', 'Set-GPOZaurrStatus', 'Skip-GroupPolicy')
|
||||
GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde'
|
||||
ModuleVersion = '0.0.98'
|
||||
ModuleVersion = '0.0.121'
|
||||
PowerShellVersion = '5.1'
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
@@ -18,15 +18,15 @@
|
||||
}
|
||||
}
|
||||
RequiredModules = @(@{
|
||||
ModuleVersion = '0.0.190'
|
||||
ModuleVersion = '0.0.195'
|
||||
ModuleName = 'PSSharedGoods'
|
||||
Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.103'
|
||||
ModuleVersion = '0.0.112'
|
||||
ModuleName = 'ADEssentials'
|
||||
Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.122'
|
||||
ModuleVersion = '0.0.132'
|
||||
ModuleName = 'PSWriteHTML'
|
||||
Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c'
|
||||
}, 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLDriveMapSettings {
|
||||
function ConvertTo-XMLDriveMapSettings {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
function ConvertTo-XMLFolderRedirection {
|
||||
[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 ($Folder in $GPO.DataSet) {
|
||||
foreach ($Location in $Folder.Location) {
|
||||
[PSCustomObject] @{
|
||||
DestinationPath = $Location.DestinationPath
|
||||
SecuritySID = $Location.SecurityGroup.SID.'#text'
|
||||
SecurityName = $Location.SecurityGroup.Name.'#text'
|
||||
GrantExclusiveRights = if ($Folder.GrantExclusiveRights -eq 'true') { $true } else { $false }
|
||||
MoveContents = if ($Folder.MoveContents -eq 'true') { $true } else { $false }
|
||||
FollowParent = if ($Folder.FollowParent -eq 'true') { $true } else { $false }
|
||||
ApplyToDownLevel = if ($Folder.ApplyToDownLevel -eq 'true') { $true } else { $false }
|
||||
DoNotCare = if ($Folder.DoNotCare -eq 'true') { $true } else { $false }
|
||||
RedirectToLocal = if ($Folder.RedirectToLocal -eq 'true') { $true } else { $false }
|
||||
PolicyRemovalBehavior = $Folder.PolicyRemovalBehavior # : LeaveContents
|
||||
ConfigurationControl = if ($Folder.ConfigurationControl -eq 'GP') { 'Group Policy' } else { $Folder.ConfigurationControl } # : GP
|
||||
PrimaryComputerEvaluation = $Folder.PrimaryComputerEvaluation # : PrimaryComputerPolicyDisabled
|
||||
}
|
||||
}
|
||||
}
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
foreach ($Folder in $GPO.DataSet) {
|
||||
foreach ($Location in $Folder.Location) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
Id = $Folder.Id
|
||||
DestinationPath = $Location.DestinationPath
|
||||
SecuritySID = $Location.SecurityGroup.SID.'#text'
|
||||
SecurityName = $Location.SecurityGroup.Name.'#text'
|
||||
GrantExclusiveRights = if ($Folder.GrantExclusiveRights -eq 'true') { $true } else { $false }
|
||||
MoveContents = if ($Folder.MoveContents -eq 'true') { $true } else { $false }
|
||||
FollowParent = if ($Folder.FollowParent -eq 'true') { $true } else { $false }
|
||||
ApplyToDownLevel = if ($Folder.ApplyToDownLevel -eq 'true') { $true } else { $false }
|
||||
DoNotCare = if ($Folder.DoNotCare -eq 'true') { $true } else { $false }
|
||||
RedirectToLocal = if ($Folder.RedirectToLocal -eq 'true') { $true } else { $false }
|
||||
PolicyRemovalBehavior = $Folder.PolicyRemovalBehavior # : LeaveContents
|
||||
ConfigurationControl = if ($Folder.ConfigurationControl -eq 'GP') { 'Group Policy' } else { $Folder.ConfigurationControl } # : GP
|
||||
PrimaryComputerEvaluation = $Folder.PrimaryComputerEvaluation # : PrimaryComputerPolicyDisabled
|
||||
Linked = $GPO.Linked
|
||||
LinksCount = $GPO.LinksCount
|
||||
Links = $GPO.Links
|
||||
}
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLGenericPolicy {
|
||||
function ConvertTo-XMLGenericPolicy {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLGenericPublicKey {
|
||||
function ConvertTo-XMLGenericPublicKey {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLGenericSecuritySettings {
|
||||
function ConvertTo-XMLGenericSecuritySettings {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLLocalGroups {
|
||||
function ConvertTo-XMLLocalGroups {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLPrinter {
|
||||
function ConvertTo-XMLPrinter {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLPrinterInternal {
|
||||
function ConvertTo-XMLPrinterInternal {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLRegistryAutologonOnReport {
|
||||
function ConvertTo-XMLRegistryAutologonOnReport {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertTo-XMLTaskScheduler {
|
||||
function ConvertTo-XMLTaskScheduler {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function Get-GPOCategories {
|
||||
function Get-GPOCategories {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
function Get-GPOPrivInheritance {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[parameter(ParameterSetName = 'ADObject', ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject,
|
||||
[System.Collections.IDictionary] $CacheReturnedGPOs,
|
||||
[System.Collections.IDictionary] $ForestInformation,
|
||||
[string] $Domain,
|
||||
[switch] $SkipDomainRoot,
|
||||
[switch] $SkipDomainControllers
|
||||
)
|
||||
foreach ($Object in $ADObject) {
|
||||
if ($SkipDomainRoot) {
|
||||
if ($Object.DistinguishedName -eq $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']) {
|
||||
# other skips Domain Root
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ($SkipDomainControllers) {
|
||||
if ($Object.DistinguishedName -eq $ForestInformation['DomainsExtended'][$Domain]['DomainControllersContainer']) {
|
||||
# other skips Domain Controllers
|
||||
continue
|
||||
}
|
||||
}
|
||||
$Inheritance = Get-GPInheritance -Target $Object.DistinguishedName
|
||||
foreach ($Link in $Inheritance.GpoLinks) {
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $Link.DisplayName
|
||||
DomainName = $Domain
|
||||
GUID = $Link.GPOID
|
||||
Enabled = $Link.Enabled
|
||||
Enforced = $Link.Enforced
|
||||
Order = $Link.Order
|
||||
Target = $Object.DistinguishedName
|
||||
TargetCanonical = $Object.CanonicalName
|
||||
TargetObjectClass = $Object.objectClass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
function Get-GPOPrivInheritanceLoop {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject,
|
||||
[System.Collections.IDictionary] $CacheReturnedGPOs,
|
||||
[System.Collections.IDictionary] $ForestInformation,
|
||||
[validateset('Root', 'DomainControllers', 'OrganizationalUnit')][string[]] $Linked,
|
||||
[string] $SearchBase,
|
||||
[Microsoft.ActiveDirectory.Management.ADSearchScope] $SearchScope,
|
||||
[string] $Filter
|
||||
)
|
||||
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']
|
||||
$Splat['Filter'] = "(objectClass -eq 'organizationalUnit')"
|
||||
$Splat['SearchBase'] = $SearchBase
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
Get-GPOPrivInheritance -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation
|
||||
}
|
||||
if ($Linked -contains 'Root') {
|
||||
$SearchBase = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']
|
||||
$Splat['Filter'] = "objectClass -eq 'domainDNS'"
|
||||
$Splat['SearchBase'] = $SearchBase
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
Get-GPOPrivInheritance -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation
|
||||
}
|
||||
if ($Linked -contains 'Site') {
|
||||
# Sites are defined only in primary domain
|
||||
# Sites are not supported by Get-GPInheritance
|
||||
}
|
||||
if ($Linked -contains 'OrganizationalUnit') {
|
||||
$SearchBase = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']
|
||||
$Splat['Filter'] = "(objectClass -eq 'organizationalUnit')"
|
||||
$Splat['SearchBase'] = $SearchBase
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
Get-GPOPrivInheritance -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -SkipDomainRoot -SkipDomainControllers
|
||||
}
|
||||
}
|
||||
} elseif ($Filter) {
|
||||
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)"
|
||||
}
|
||||
Get-GPOPrivInheritance -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Get-GPOPrivInheritance -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObject -Domain '' -ForestInformation $ForestInformation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
function Get-GPOPrivLink {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[parameter(ParameterSetName = 'ADObject', ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject,
|
||||
[System.Collections.IDictionary] $CacheReturnedGPOs,
|
||||
[System.Collections.IDictionary] $ForestInformation,
|
||||
[string] $Domain,
|
||||
[switch] $SkipDomainRoot,
|
||||
[switch] $SkipDomainControllers,
|
||||
[switch] $AsHashTable,
|
||||
[switch] $SkipDuplicates
|
||||
)
|
||||
foreach ($Object in $ADObject) {
|
||||
if ($SkipDomainRoot) {
|
||||
if ($Object.DistinguishedName -eq $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']) {
|
||||
# other skips Domain Root
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ($SkipDomainControllers) {
|
||||
if ($Object.DistinguishedName -eq $ForestInformation['DomainsExtended'][$Domain]['DomainControllersContainer']) {
|
||||
# other skips Domain Controllers
|
||||
continue
|
||||
}
|
||||
}
|
||||
$OutputGPOs = Get-PrivGPOZaurrLink -Object $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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
function Get-GPOZaurrLinkInheritance {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Short description
|
||||
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
|
||||
.PARAMETER ADObject
|
||||
Parameter description
|
||||
|
||||
.PARAMETER Filter
|
||||
Parameter description
|
||||
|
||||
.PARAMETER SearchBase
|
||||
Parameter description
|
||||
|
||||
.PARAMETER SearchScope
|
||||
Parameter description
|
||||
|
||||
.PARAMETER Linked
|
||||
Parameter description
|
||||
|
||||
.PARAMETER Limited
|
||||
Parameter description
|
||||
|
||||
.PARAMETER SkipDuplicates
|
||||
Parameter description
|
||||
|
||||
.PARAMETER GPOCache
|
||||
Parameter description
|
||||
|
||||
.PARAMETER Forest
|
||||
Parameter description
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Parameter description
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Parameter description
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Parameter description
|
||||
|
||||
.PARAMETER AsHashTable
|
||||
Parameter description
|
||||
|
||||
.PARAMETER Summary
|
||||
Parameter description
|
||||
|
||||
.EXAMPLE
|
||||
$Output = Get-GPOZaurrLinkInheritance -Summary
|
||||
$Output | Format-Table
|
||||
|
||||
$Output[5]
|
||||
|
||||
$Output[5].Links | Format-Table
|
||||
$Output[5].LinksObjects | Format-Table
|
||||
|
||||
.NOTES
|
||||
This is based on Get-GPInheritance which isn't ideal and doesn't support sites. Get-GPOZaurrLink is better. Leaving in case I need it later on for private use only.
|
||||
#>
|
||||
[cmdletbinding(DefaultParameterSetName = 'All')]
|
||||
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', 'OrganizationalUnit')][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,
|
||||
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[switch] $AsHashTable,
|
||||
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[switch] $Summary
|
||||
)
|
||||
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 $Filter -and -not $Linked) {
|
||||
# We choose ALL, except SITE which is not supported gor Get-GPInheritance
|
||||
# that's why it's better to use Get-GPOZaurrLink
|
||||
#$Linked = 'Root', 'DomainControllers', 'Site', 'OrganizationalUnit'
|
||||
}
|
||||
$getGPOPrivInheritanceLoopSplat = @{
|
||||
Linked = $Linked
|
||||
ForestInformation = $ForestInformation
|
||||
CacheReturnedGPOs = $CacheReturnedGPOs
|
||||
SearchScope = $SearchScope
|
||||
SearchBase = $SearchBase
|
||||
ADObject = $ADObject
|
||||
Filter = $Filter
|
||||
}
|
||||
Remove-EmptyValue -Hashtable $getGPOPrivInheritanceLoopSplat -Recursive
|
||||
|
||||
# we need to use nested functions to support pipeline output and as hashtable and reporting that returns single value
|
||||
if ($AsHashTable -or $Summary) {
|
||||
$HashTable = [ordered] @{}
|
||||
$SummaryHashtable = [ordered] @{}
|
||||
$Links = Get-GPOPrivInheritanceLoop @getGPOPrivInheritanceLoopSplat
|
||||
foreach ($Link in $Links) {
|
||||
$Key = -join ($Link.DomainName, $Link.GUID)
|
||||
if (-not $HashTable[$Key]) {
|
||||
$HashTable[$Key] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
$HashTable[$Key].Add($Link)
|
||||
}
|
||||
foreach ($Key in $HashTable.Keys) {
|
||||
[Array] $Link = $HashTable[$Key]
|
||||
$EnabledLinks = $Link.Enabled.Where( { $_ -eq $true }, 'split')
|
||||
if ($EnabledLinks[0].Count -gt 0) {
|
||||
$IsLinked = $true
|
||||
} else {
|
||||
$IsLinked = $false
|
||||
}
|
||||
$SummaryLink = [PSCustomObject] @{
|
||||
DisplayName = $Link[0].DisplayName
|
||||
DomainName = $Link[0].DomainName
|
||||
GUID = $Link[0].GUID
|
||||
Linked = $IsLinked
|
||||
LinksCount = $Link.Count
|
||||
LinksEnabledCount = $EnabledLinks[0].Count
|
||||
LinksDisabledCount = $EnabledLinks[1].Count
|
||||
Links = $Link.Target
|
||||
LinksObjects = $Link
|
||||
}
|
||||
$SummaryHashtable[$Key] = $SummaryLink
|
||||
}
|
||||
if ($AsHashTable -and $Summary) {
|
||||
$SummaryHashtable
|
||||
} elseif ($AsHashTable) {
|
||||
$HashTable
|
||||
} elseif ($Summary) {
|
||||
$SummaryHashtable.Values
|
||||
}
|
||||
} else {
|
||||
Get-GPOPrivInheritanceLoop @getGPOPrivInheritanceLoopSplat
|
||||
}
|
||||
}
|
||||
End {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
function Get-GPOZaurrLinkLoop {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject,
|
||||
[System.Collections.IDictionary] $CacheReturnedGPOs,
|
||||
[System.Collections.IDictionary] $ForestInformation,
|
||||
[validateset('All', 'Root', 'DomainControllers', 'Site', 'OrganizationalUnit')][string[]] $Linked,
|
||||
[string] $SearchBase,
|
||||
[Microsoft.ActiveDirectory.Management.ADSearchScope] $SearchScope,
|
||||
[string] $Filter,
|
||||
[switch] $SkipDuplicates,
|
||||
[string[]] $Site
|
||||
)
|
||||
if (-not $ADObject) {
|
||||
if ($Site) {
|
||||
foreach ($S in $Site) {
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
Write-Verbose "Get-GPOZaurrLink - Getting GPO links for site $Site"
|
||||
# Sites are defined only in primary domain
|
||||
if ($ForestInformation['DomainsExtended'][$Domain]['DNSRoot'] -eq $ForestInformation['DomainsExtended'][$Domain]['Forest']) {
|
||||
$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]
|
||||
}
|
||||
$Splat['Filter'] = "(objectClass -eq 'site') -and (name -eq '$S')"
|
||||
$Splat['SearchBase'] = -join ("CN=Configuration,", $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName'])
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($SearchBase -or $SearchScope -or $Filter) {
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
if (-not $Filter) {
|
||||
$Filter = "(objectClass -eq 'organizationalUnit' -or objectClass -eq 'domainDNS' -or objectClass -eq 'site')"
|
||||
}
|
||||
$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)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
} elseif (-not $Filter) {
|
||||
# if not linked, we force it to All
|
||||
if (-not $Linked) {
|
||||
$Linked = 'All'
|
||||
}
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
Write-Verbose "Get-GPOZaurrLink - Getting GPO links for domain $Domain"
|
||||
$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 'Root' -or $Linked -contains 'All') {
|
||||
Write-Verbose "Get-GPOZaurrLink - Getting GPO links for domain $Domain at ROOT level"
|
||||
$Splat['Filter'] = "objectClass -eq 'domainDNS'"
|
||||
$Splat['SearchBase'] = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
if ($Linked -contains 'Site' -or $Linked -contains 'All') {
|
||||
Write-Verbose "Get-GPOZaurrLink - Getting GPO links for domain $Domain at SITE level"
|
||||
# Sites are defined only in primary domain
|
||||
if ($ForestInformation['DomainsExtended'][$Domain]['DNSRoot'] -eq $ForestInformation['DomainsExtended'][$Domain]['Forest']) {
|
||||
$Splat['Filter'] = "(objectClass -eq 'site')"
|
||||
$Splat['SearchBase'] = -join ("CN=Configuration,", $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName'])
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Linked -contains 'DomainControllers' -or $Linked -contains 'All') {
|
||||
Write-Verbose "Get-GPOZaurrLink - Getting GPO links for domain $Domain at DC level"
|
||||
$Splat['Filter'] = "(objectClass -eq 'organizationalUnit')"
|
||||
$Splat['SearchBase'] = $ForestInformation['DomainsExtended'][$Domain]['DomainControllersContainer']
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
if ($Linked -contains 'OrganizationalUnit' -or $Linked -contains 'All') {
|
||||
Write-Verbose "Get-GPOZaurrLink - Getting GPO links for domain $Domain at OU level"
|
||||
$Splat['Filter'] = "(objectClass -eq 'organizationalUnit')"
|
||||
$Splat['SearchBase'] = $ForestInformation['DomainsExtended'][$Domain]['DistinguishedName']
|
||||
try {
|
||||
$ADObjectGPO = Get-ADObject @Splat
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrLink - Get-ADObject error $($_.Exception.Message)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -SkipDomainRoot -SkipDomainControllers -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<#
|
||||
elseif ($Filter) {
|
||||
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)"
|
||||
}
|
||||
if ($ADObjectGPO) {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObjectGPO -Domain $Domain -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
}
|
||||
#>
|
||||
} else {
|
||||
Get-GPOPrivLink -CacheReturnedGPOs $CacheReturnedGPOs -ADObject $ADObject -Domain '' -ForestInformation $ForestInformation -AsHashTable:$AsHashTable -SkipDuplicates:$SkipDuplicates
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,10 @@
|
||||
if (-not $ADAdministrativeGroups) {
|
||||
$ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
}
|
||||
$AdministrativeExists = @{
|
||||
$AdministrativeExists = [ordered] @{
|
||||
DisplayName = $GPOPermissions[0].DisplayName
|
||||
DomainName = $GPOPermissions[0].DomainName
|
||||
GUID = $GPOPermissions[0].GUID
|
||||
Skip = $false
|
||||
DomainAdmins = $false
|
||||
EnterpriseAdmins = $false
|
||||
@@ -55,5 +58,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
$AdministrativeExists
|
||||
[PSCustomObject] $AdministrativeExists
|
||||
}
|
||||
@@ -6,69 +6,71 @@
|
||||
[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
|
||||
}
|
||||
}
|
||||
}
|
||||
$ObjectsToProcess = $Object.GpLink -split '\]\['
|
||||
} 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."
|
||||
}
|
||||
$ObjectsToProcess = $Object.LinkedGroupPolicyObjects -split '\]\['
|
||||
} else {
|
||||
$ObjectsToProcess = $null
|
||||
}
|
||||
$ObjectsToProcess | ForEach-Object -Process {
|
||||
$Link = $_ -replace 'LDAP://' -replace '\]' -replace '\['
|
||||
if ($Link.Length -gt 10) {
|
||||
$SplitGPLink = $Link -split ';'
|
||||
$DN = $SplitGPLink[0]
|
||||
$Option = $SplitGPLink[1]
|
||||
if ($Option -eq '0') {
|
||||
$Enforced = $false
|
||||
$Enabled = $true
|
||||
} elseif ($Option -eq '1') {
|
||||
$Enabled = $false
|
||||
$Enforced = $false
|
||||
} elseif ($Option -eq '2') {
|
||||
$Enabled = $true
|
||||
$Enforced = $true
|
||||
} elseif ($Option -eq '3') {
|
||||
$Enabled = $false
|
||||
$Enforced = $true
|
||||
} else {
|
||||
if ($Object.GpLink) {
|
||||
Write-Warning "Get-PrivGPOZaurrLink - This should't happen. Please investigate - Option: $Option"
|
||||
} else {
|
||||
$Output['GPODomainDistinguishedName'] = ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDC
|
||||
$Output['GPODistinguishedName'] = $_
|
||||
[PSCustomObject] $Output
|
||||
Write-Warning "Get-PrivGPOZaurrLink - Property GPLink is required to be able to tell if Enabled/Enforced is added. Skipping those settings."
|
||||
}
|
||||
$Enabled = $null
|
||||
$Enforced = $null
|
||||
}
|
||||
$DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $DN -ToDomainCN
|
||||
$Output = [ordered] @{
|
||||
DistinguishedName = $Object.DistinguishedName
|
||||
#Domain = ConvertFrom-DistinguishedName -DistinguishedName $Object.DistinguishedName -ToDomainCN
|
||||
CanonicalName = if ($Object.CanonicalName) { $Object.CanonicalName.TrimEnd('/') } else { $Object.CanonicalName }
|
||||
Guid = [Regex]::Match($DN, '(?={)(.*)(?<=})').Value -replace '{' -replace '}'
|
||||
Enforced = $Enforced
|
||||
Enabled = $Enabled
|
||||
ObjectClass = $Object.ObjectClass
|
||||
}
|
||||
$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 $DN -ToDC
|
||||
$Output['GPODistinguishedName'] = $DN
|
||||
# This is object name, usually used for sites
|
||||
$Output['Name'] = $Object.Name
|
||||
[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 $DN -ToDC
|
||||
$Output['GPODistinguishedName'] = $DN
|
||||
[PSCustomObject] $Output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+114
-61
@@ -9,25 +9,67 @@
|
||||
[string] $Splitter = [System.Environment]::NewLine,
|
||||
[switch] $ReturnObject,
|
||||
[System.Collections.IDictionary] $ExcludeGroupPolicies,
|
||||
[string[]] $Type
|
||||
[string[]] $Type,
|
||||
[System.Collections.IDictionary] $LinksSummaryCache
|
||||
)
|
||||
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
|
||||
|
||||
$DisplayName = $XMLContent.GPO.Name
|
||||
$DomainName = $XMLContent.GPO.Identifier.Domain.'#text'
|
||||
|
||||
if ($LinksSummaryCache) {
|
||||
$SearchGUID = -join ($XMLContent.GPO.Identifier.Domain.'#text', $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}')
|
||||
if ($LinksSummaryCache[$SearchGUID]) {
|
||||
$Linked = $LinksSummaryCache[$SearchGUID].Linked
|
||||
$LinksEnabledCount = $LinksSummaryCache[$SearchGUID].LinksEnabledCount
|
||||
$LinksDisabledCount = $LinksSummaryCache[$SearchGUID].LinksDisabledCount
|
||||
$LinksTotalCount = $LinksSummaryCache[$SearchGUID].LinksCount
|
||||
$Links = $LinksSummaryCache[$SearchGUID].Links
|
||||
$LinksObjects = $LinksSummaryCache[$SearchGUID].LinksObjects
|
||||
} else {
|
||||
$Linked = $true
|
||||
$Linked = $false
|
||||
$LinksEnabledCount = 0
|
||||
$LinksDisabledCount = 0
|
||||
$LinksTotalCount = 0
|
||||
$Links = $null
|
||||
$LinksObjects = $null
|
||||
}
|
||||
} else {
|
||||
$Linked = $false
|
||||
$LinksEnabledCount = 0
|
||||
$LinksDisabledCount = 0
|
||||
$LinksTotalCount = 0
|
||||
if ($XMLContent.GPO.LinksTo) {
|
||||
$LinkSplit = ([Array] $XMLContent.GPO.LinksTo).Where( { $_.Enabled -eq $true }, 'Split')
|
||||
[Array] $LinksEnabled = $LinkSplit[0]
|
||||
[Array] $LinksDisabled = $LinkSplit[1]
|
||||
$LinksEnabledCount = $LinksEnabled.Count
|
||||
$LinksDisabledCount = $LinksDisabled.Count
|
||||
$LinksTotalCount = ([Array] $XMLContent.GPO.LinksTo).Count
|
||||
if ($LinksEnabledCount -eq 0) {
|
||||
$Linked = $false
|
||||
} else {
|
||||
$Linked = $true
|
||||
}
|
||||
$Links = @(
|
||||
$XMLContent.GPO.LinksTo | ForEach-Object -Process {
|
||||
if ($_) {
|
||||
$_.SOMPath
|
||||
}
|
||||
}
|
||||
) -join $Splitter
|
||||
$LinksObjects = $XMLContent.GPO.LinksTo | ForEach-Object -Process {
|
||||
if ($_) {
|
||||
[PSCustomObject] @{
|
||||
CanonicalName = $_.SOMPath
|
||||
Enabled = $_.Enabled
|
||||
NoOverride = $_.NoOverride
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$Linked = $false
|
||||
$LinksEnabledCount = 0
|
||||
$LinksDisabledCount = 0
|
||||
$LinksTotalCount = 0
|
||||
$Links = $null
|
||||
$LinksObjects = $null
|
||||
}
|
||||
}
|
||||
# Find proper values for enabled/disabled user/computer settings
|
||||
if ($XMLContent.GPO.Computer.Enabled -eq 'False') {
|
||||
@@ -35,7 +77,7 @@
|
||||
} elseif ($XMLContent.GPO.Computer.Enabled -eq 'True') {
|
||||
$ComputerEnabled = $true
|
||||
} else {
|
||||
Write-Warning "Get-XMLGPO - Computer enabled not set to true or false. Weird."
|
||||
Write-Warning "Get-XMLGPO - Computer enabled not set to true or false [$DisplayName/$DomainName]. Weird."
|
||||
$ComputerEnabled = $null
|
||||
}
|
||||
if ($XMLContent.GPO.User.Enabled -eq 'False') {
|
||||
@@ -43,7 +85,7 @@
|
||||
} elseif ($XMLContent.GPO.User.Enabled -eq 'True') {
|
||||
$UserEnabled = $true
|
||||
} else {
|
||||
Write-Warning "Get-XMLGPO - User enabled not set to true or false. Weird."
|
||||
Write-Warning "Get-XMLGPO - User enabled not set to true or false [$DisplayName/$DomainName] . Weird."
|
||||
$UserEnabled = $null
|
||||
}
|
||||
# Translate Enabled to same as GPO GUI
|
||||
@@ -62,8 +104,8 @@
|
||||
}
|
||||
|
||||
# This is kind of old way of doing things, but it's superseded by other way below
|
||||
$ComputerSettingsAvailable = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
|
||||
$UserSettingsAvailable = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
|
||||
[bool] $ComputerSettingsAvailable = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
|
||||
[bool] $UserSettingsAvailable = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
|
||||
|
||||
if ($ComputerSettingsAvailable -eq $false -and $UserSettingsAvailable -eq $false) {
|
||||
$NoSettings = $true
|
||||
@@ -74,35 +116,58 @@
|
||||
# $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-XMLGPO - things went sideways $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
|
||||
# This is additional check we do for error check to prevent false-positives for EMPTY on non-english language
|
||||
$PreCheckOutputUser = $false
|
||||
$PreCheckOutputComputer = $false
|
||||
foreach ($Extension in $XMLContent.GPO.User.ExtensionData) {
|
||||
if ($Extension.Error) {
|
||||
$PreCheckOutputUser = $true
|
||||
}
|
||||
$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-XMLGPO - things went sideways $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
foreach ($Extension in $XMLContent.GPO.Computer.ExtensionData) {
|
||||
if ($Extension.Error) {
|
||||
$PreCheckOutputComputer = $true
|
||||
}
|
||||
$KeysToLoop
|
||||
}
|
||||
if ($PreCheckOutputComputer -eq $true -or $PreCheckOutputUser -eq $true) {
|
||||
# in some cases GPResult seems to return an error - this was first noticed by user when using Dutch based system
|
||||
# I am not sure if it's possible to fix this error for users, but once that happens checking if GPO is empty fails using the method below
|
||||
# therefore we will use the old method of assuming something is empty or not empty in such case
|
||||
Write-Warning "Get-XMLGPO - Reading GPO content [$DisplayName/$DomainName] returned an error. This may be because of non-english language. Assesing EMPTY using old method which can report false positives. Be careful please."
|
||||
$OutputUser = @()
|
||||
$OutputComputer = @()
|
||||
} else {
|
||||
[Array] $OutputUser = foreach ($ExtensionType in $XMLContent.GPO.User.ExtensionData.Extension) {
|
||||
if ($ExtensionType) {
|
||||
$GPOSettingTypeSplit = ($ExtensionType.type -split ':')
|
||||
try {
|
||||
$KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') }
|
||||
} catch {
|
||||
Write-Warning "Get-XMLGPO - things went sideways [$DisplayName/$DomainName]. Error $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
}
|
||||
$KeysToLoop
|
||||
}
|
||||
[Array] $OutputComputer = foreach ($ExtensionType in $XMLContent.GPO.Computer.ExtensionData.Extension) {
|
||||
if ($ExtensionType) {
|
||||
$GPOSettingTypeSplit = ($ExtensionType.type -split ':')
|
||||
try {
|
||||
$KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') }
|
||||
} catch {
|
||||
Write-Warning "Get-XMLGPO - things went sideways [$DisplayName/$DomainName]. Error $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
}
|
||||
$KeysToLoop
|
||||
}
|
||||
|
||||
[bool] $ComputerSettingsAvailable = if ($OutputComputer.Count -gt 0) { $true } else { $false }
|
||||
[bool] $UserSettingsAvailable = if ($OutputUser.Count -gt 0) { $true } else { $false }
|
||||
}
|
||||
|
||||
$ComputerSettingsAvailable = if ($OutputComputer) { $true } else { $false }
|
||||
$UserSettingsAvailable = if ($OutputUser) { $true } else { $false }
|
||||
|
||||
if (-not $ComputerSettingsAvailable -and -not $UserSettingsAvailable) {
|
||||
if ($ComputerSettingsAvailable -eq $false -and $UserSettingsAvailable -eq $false) {
|
||||
$Empty = $true
|
||||
} else {
|
||||
$Empty = $false
|
||||
@@ -217,6 +282,7 @@
|
||||
'DisplayName' = $XMLContent.GPO.Name
|
||||
'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text'
|
||||
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}'
|
||||
'Days' = (New-TimeSpan -Start ([DateTime] $XMLContent.GPO.ModifiedTime) -End (Get-Date)).Days
|
||||
'Empty' = $Empty
|
||||
'Linked' = $Linked
|
||||
'Enabled' = $EnabledBool
|
||||
@@ -238,8 +304,8 @@
|
||||
'UserSettingsAvailable' = $UserSettingsAvailable
|
||||
#'ComputerSettingsAvailableReal' = $ComputerSettingsAvailableReal
|
||||
#'UserSettingsAvailableReal' = $UserSettingsAvailableReal
|
||||
'ComputerSettingsTypes' = $OutputComputer.Name
|
||||
'UserSettingsTypes' = $OutputUser.Name
|
||||
'ComputerSettingsTypes' = $OutputComputer.Name -join ", "
|
||||
'UserSettingsTypes' = $OutputUser.Name -join ", "
|
||||
'ComputerEnabled' = $ComputerEnabled
|
||||
'UserEnabled' = $UserEnabled
|
||||
'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
|
||||
@@ -249,6 +315,7 @@
|
||||
'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false }
|
||||
'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension
|
||||
'NoSettings' = $NoSettings
|
||||
'Description' = $GPO.Description
|
||||
'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime
|
||||
'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime
|
||||
'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime
|
||||
@@ -280,22 +347,8 @@
|
||||
}
|
||||
)
|
||||
'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
|
||||
}
|
||||
}
|
||||
}
|
||||
'Links' = $Links
|
||||
'LinksObjects' = $LinksObjects
|
||||
'GPOObject' = $GPO
|
||||
}
|
||||
if ($GPOOutput.ACL) {
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
$GPOZaurrBrokenLink = [ordered] @{
|
||||
Name = 'Group Policy Broken Links'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrBrokenLink -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
Processing = {
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['RequireDeletion'] = $Script:Reporting['GPOBrokenLink']['Data'].Count
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['WillFixPerDomain'] = @{}
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['Unique'] = @{}
|
||||
foreach ($Link In $Script:Reporting['GPOBrokenLink']['Data']) {
|
||||
$DomainName = ConvertFrom-DistinguishedName -ToDomainCN -DistinguishedName $Link.DistinguishedName
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOBrokenLink']['Variables']['WillFixPerDomain'][$DomainName]) {
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['WillFixPerDomain'][$DomainName] = 0
|
||||
}
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['WillFixPerDomain'][$DomainName]++
|
||||
# Lets do unique OU counting
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['Unique'][$Link.CanonicalName] = $Link
|
||||
}
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['UniqueObjects'] = $Script:Reporting['GPOBrokenLink']['Variables']['Unique'].Keys
|
||||
|
||||
if ($Script:Reporting['GPOBrokenLink']['Data'].Count -gt 0) {
|
||||
$Script:Reporting['GPOBrokenLink']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOBrokenLink']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
RequireDeletion = 0
|
||||
WillFixPerDomain = $null
|
||||
UniqueObjects = $null
|
||||
Unique = $null
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"When GPO is deleted correctly, it usually is removed from AD, SYSVOL, and any link to it is also discarded. "
|
||||
"Unfortunately, this is true only if the GPO is created and linked within the same domain. "
|
||||
"If GPO is linked in another domain, this leaves a broken link hanging on before it was linked. "
|
||||
"Additionally, the Remove-GPO cmdlet doesn't handle site link deletions, which causes dead links to be stuck on sites until those are manually deleted. "
|
||||
"This means that any GPOs deleted using PowerShell may leave a trail."
|
||||
}
|
||||
New-HTMLText -Text @(
|
||||
'As it stands currently there are ',
|
||||
$Script:Reporting['GPOBrokenLink']['Data'].Count,
|
||||
' broken links that need to be deleted over '
|
||||
$Script:Reporting['GPOBrokenLink']['Variables']['UniqueObjects'].Count,
|
||||
' unique objects. '
|
||||
) -FontSize 10pt -FontWeight normal, bold, normal, bold, normal -LineBreak
|
||||
if ($Script:Reporting['GPOBrokenLink']['Data'].Count -ne 0) {
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOBrokenLink']['Variables']['WillFixPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOBrokenLink']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
}
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOBrokenLink']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartLegend -Names 'Bad' -Color Salmon
|
||||
New-ChartBar -Name 'Broken Links' -Value $Script:Reporting['GPOBrokenLink']['Data'].Count
|
||||
} -Title 'Broken Links' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Broken Links' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBrokenLink']['Data'] -Filtering {
|
||||
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
New-HTMLSection -Name 'Steps to remove Broken Links' {
|
||||
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 GPO links. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenLinkBefore.html -Verbose -Type GPOBrokenLink
|
||||
}
|
||||
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-GPOZaurrBrokenLink -Verbose
|
||||
$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 Broken Links' {
|
||||
New-HTMLText -Text "Following command when executed, runs internally command that lists all broken links. After finding them all it delets them according to given criteria. "
|
||||
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-GPOZaurrBrokenLink -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 {
|
||||
Repair-GPOZaurrBrokenLink -Verbose -LimitProcessing 2
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed removes only first X number of links. Keep in mind that 5 broken links on a single Organizational Unit are treated as one. 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\GPOZaurrBrokenLinkAfter.html -Verbose -Type GPOBrokenLink
|
||||
}
|
||||
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 -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOBrokenLink']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBrokenLink']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,11 +52,12 @@
|
||||
}
|
||||
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. "
|
||||
"When GPO is created, it creates an entry in Active Directory (metadata) and SYSVOL (content). "
|
||||
"Two different places mean two different sets of permissions. "
|
||||
"The group Policy module is making sure the data in both places is correct. "
|
||||
"However, it's not necessarily the case for different reasons, and often permissions go out of sync between AD and SYSVOL. "
|
||||
"This test verifies the 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 is 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 {
|
||||
@@ -111,10 +112,10 @@
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentBefore.html -Verbose -Type GPOConsistency
|
||||
}
|
||||
New-HTMLText -Text {
|
||||
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
|
||||
@@ -143,8 +144,12 @@
|
||||
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-HTMLText -Text @(
|
||||
"Unfortunetly this step is manual until automation is developed. "
|
||||
"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."
|
||||
"Please keep in mind that it's possible inconsistent downlevel permissions fix will not be required once the top level fix is applied. "
|
||||
"Rerun report to find out if you've just fixed top-level permissions. "
|
||||
)
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
@@ -155,7 +160,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsBefore.html -Verbose -Type GPODuplicates
|
||||
}
|
||||
New-HTMLText -Text {
|
||||
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. "
|
||||
}
|
||||
@@ -106,7 +106,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
$GPOZaurrLinks = [ordered] @{
|
||||
Name = 'Group Policy Links'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrLink -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -Summary
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOLinks']['Data'] -Filtering
|
||||
if ($Script:Reporting['GPOLinks']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOLinks']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,16 @@
|
||||
if (-not $Script:Reporting['GPOList']['Variables']['GPOApplypermissionNoPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOList']['Variables']['GPOApplypermissionNoPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if ($GPO.Enabled -eq $false -or $GPO.Empty -eq $true -or $GPO.Linked -eq $false -or $GPO.ApplyPermission -eq $false) {
|
||||
|
||||
if ($GPO.Days -le $Script:Reporting['GPOList']['Variables']['GPOOlderThan']) {
|
||||
# Skip GPOS that are younger than 30 days
|
||||
$Script:Reporting['GPOList']['Variables']['GPOSkip']++
|
||||
}
|
||||
if (($GPO.Enabled -eq $false -or $GPO.Empty -eq $true -or $GPO.Linked -eq $false -or $GPO.ApplyPermission -eq $false) -and $GPO.Days -le $Script:Reporting['GPOList']['Variables']['GPOOlderThan']) {
|
||||
# Skip GPOS that are younger than 30 days
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotValidButSkip']++
|
||||
}
|
||||
if (($GPO.Enabled -eq $false -or $GPO.Empty -eq $true -or $GPO.Linked -eq $false -or $GPO.ApplyPermission -eq $false) -and $GPO.Days) {
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotValid']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotValidPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
@@ -133,13 +142,14 @@
|
||||
}
|
||||
}
|
||||
$Script:Reporting['GPOList']['Variables']['GPOTotal'] = $Script:Reporting['GPOList']['Data'].Count
|
||||
if ($Script:Reporting['GPOList']['Variables']['GPONotValid'] -gt 0) {
|
||||
if ($Script:Reporting['GPOList']['Variables']['GPONotValid'] -gt 0 -and $Script:Reporting['GPOList']['Variables']['GPONotValidButSkip'] -ne $Script:Reporting['GPOList']['Variables']['GPONotValid']) {
|
||||
$Script:Reporting['GPOList']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOList']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
GPOOlderThan = 30
|
||||
GPONotValidPerDomain = $null
|
||||
GPOValidPerDomain = $null
|
||||
GPONotOptimizedPerDomain = $null
|
||||
@@ -170,8 +180,10 @@
|
||||
GPOLinkedButEmpty = 0
|
||||
GPOEnabled = 0
|
||||
GPODisabled = 0
|
||||
GPOSkip = 0
|
||||
GPOValid = 0
|
||||
GPONotValid = 0
|
||||
GPONotValidButSkip = 0
|
||||
GPOLinkedButLinkDisabled = 0
|
||||
GPOTotal = 0
|
||||
ApplyPermissionYes = 0
|
||||
@@ -191,7 +203,7 @@
|
||||
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']['GPONotValid'] -FontWeight normal, bold {
|
||||
New-HTMLListItem -Text "Group Policies ", "NOT", " valid: ", $Script:Reporting['GPOList']['Variables']['GPONotValid'] -FontWeight normal, bold, 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
|
||||
@@ -200,7 +212,11 @@
|
||||
New-HTMLListItem -Text "Group Policies that are disabled (both user/computer sections): ", $Script:Reporting['GPOList']['Variables']['GPODisabled'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that have no Apply Permission: ", $Script:Reporting['GPOList']['Variables']['ApplyPermissionNo'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
} -Color Black, Red, Black, Red, Black
|
||||
New-HTMLListItem -Text @(
|
||||
"Group Policies ", "NOT", " valid, to skip: ", $Script:Reporting['GPOList']['Variables']['GPONotValidButSkip'], " (modified less than $($Script:Reporting['GPOList']['Variables']['GPOOlderThan']) days ago)"
|
||||
) -FontWeight 'normal', 'bold', 'normal', 'bold', 'normal' -Color 'Black', 'Red', 'Black', 'Red', 'Black'
|
||||
New-HTMLListItem -Text "Group Policies recently modified: ", $Script:Reporting['GPOList']['Variables']['GPOSkip'], " (modified less than $($Script:Reporting['GPOList']['Variables']['GPOOlderThan']) days ago)" -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
@@ -276,6 +292,13 @@
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOList']['Variables']['GPONotOptimizedPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
# Just in case report is too big and the full file is not attached
|
||||
New-HTMLText -FontSize 10pt -Text "To generate up to date report please execute: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Install-Module GPOZaurr -Force', ' or ', ' install module manually.' -Color RoyalBlue, None, None
|
||||
New-HTMLListItem -Text 'Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPListBefore.html -Verbose -Type GPOList' -Color RoyalBlue
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text 'Steps above will generate above summary with more details allowing you to get up to date report and steps on how to fix it.'
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
@@ -392,7 +415,7 @@
|
||||
"You would do so using following approach "
|
||||
) -FontSize 10pt -FontWeight bold, normal
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type Empty, Unlinked, Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor' {
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Empty, Unlinked, Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor' {
|
||||
Skip-GroupPolicy -Name 'TEST | Drive Mapping'
|
||||
Skip-GroupPolicy -Name 'Default Domain Policy'
|
||||
Skip-GroupPolicy -Name 'Default Domain Controllers Policy' -DomaiName 'JustOneDomain'
|
||||
@@ -405,6 +428,13 @@
|
||||
"Now go ahead and find what's there"
|
||||
)
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Remove GPOs (Manual)' {
|
||||
New-HTMLText -Text @(
|
||||
"Please condider deleting GPOs manually if the amount of GPOs to delete is small enough. "
|
||||
"Deleting 1-5-30 GPOs manually on domain of 4000 GPOs will be much faster than doing it in controlled manner with automated steps mentioned on next steps. "
|
||||
"What can take 30 minutes manually, can take 8 hours using automated script, because of amount of checks required by the script over and over to delete a single GPO. "
|
||||
) -FontWeight normal, bold, normal, bold, normal, bold, normal, normal -Color Black, Red, Black, Red, Black
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Remove GPOs that are EMPTY' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed removes every ",
|
||||
@@ -416,26 +446,26 @@
|
||||
"You can skip parameters related to backup if you did backup all GPOs prior to running remove command. "
|
||||
) -FontWeight normal, bold, normal, bold, normal, bold, normal, normal -Color Black, Red, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -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 {
|
||||
Remove-GPOZaurr -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -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 deleted 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-GPOZaurr -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -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-GPOZaurr -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Empty -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first X empty GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
|
||||
@@ -455,26 +485,26 @@
|
||||
"You can skip parameters related to backup if you did backup all GPOs prior to running remove command. "
|
||||
) -FontWeight normal, bold, normal, bold, normal, bold, normal, normal -Color Black, Red, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -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 {
|
||||
Remove-GPOZaurr -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -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 deleted 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-GPOZaurr -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -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-GPOZaurr -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first X unlinked GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
|
||||
@@ -497,26 +527,26 @@
|
||||
""
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -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 {
|
||||
Remove-GPOZaurr -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -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 deleted 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-GPOZaurr -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -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-GPOZaurr -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type Disabled -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first X disabled GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur. "
|
||||
@@ -539,26 +569,26 @@
|
||||
""
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf
|
||||
Remove-GPOZaurr -RequireDays 30 -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -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 {
|
||||
Remove-GPOZaurr -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -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 deleted 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-GPOZaurr -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose
|
||||
Remove-GPOZaurr -RequireDays 30 -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -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-GPOZaurr -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
Remove-GPOZaurr -RequireDays 30 -Type NoApplyPermission -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first X NoApplyPermission GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur. "
|
||||
@@ -612,7 +642,7 @@
|
||||
}
|
||||
New-HTMLText -Text "If there are no more problems to solve, GPOs to optimize in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
# }
|
||||
# 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
|
||||
# } -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
@@ -272,6 +272,7 @@
|
||||
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 '
|
||||
"they think do not belong. "
|
||||
) -FontWeight normal, bold, normal
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
@@ -283,7 +284,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
$GPOZaurrOrphans = [ordered] @{
|
||||
Name = 'Orphaned Group Policies'
|
||||
Name = 'Broken Group Policies'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
@@ -7,41 +7,52 @@
|
||||
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
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'] = @{}
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'] = @{}
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'] = @{}
|
||||
foreach ($GPO in $Script:Reporting['GPOBroken']['Data']) {
|
||||
if (-not $Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName] = 0
|
||||
if (-not $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'][$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]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOBroken']['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]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'Permissions issue') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'ObjectClass issue') {
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'][$GPO.DomainName]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted'] -gt 0) {
|
||||
$Script:Reporting['GPOOrphans']['ActionRequired'] = $true
|
||||
if ($Script:Reporting['GPOBroken']['Variables']['ToBeDeleted'] -gt 0) {
|
||||
$Script:Reporting['GPOBroken']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOOrphans']['ActionRequired'] = $false
|
||||
$Script:Reporting['GPOBroken']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
NotAvailableInAD = 0
|
||||
NotAvailableOnSysvol = 0
|
||||
NotAvailablePermissionIssue = 0
|
||||
NotAvailablePermissionIssuePerDomain = $null
|
||||
ToBeDeleted = 0
|
||||
ToBeDeletedPerDomain = $null
|
||||
NotAvailableInAD = 0
|
||||
NotAvailableOnSysvol = 0
|
||||
NotAvailablePermissionIssue = 0
|
||||
NotAvailablePermissionIssuePerDomain = $null
|
||||
ToBeDeleted = 0
|
||||
ToBeDeletedPerDomain = $null
|
||||
NotAvailableObjectClassIssue = 0
|
||||
NotAvailableObjectClassIssuePerDomain = $null
|
||||
}
|
||||
Overview = {
|
||||
<#
|
||||
@@ -58,15 +69,15 @@
|
||||
}
|
||||
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
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOBroken']['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']
|
||||
New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue']
|
||||
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
|
||||
}
|
||||
#>
|
||||
@@ -85,14 +96,15 @@
|
||||
} -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
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies which exists, but have wrong ObjectClass: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOBroken']['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
|
||||
foreach ($Domain in $Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOBroken']['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
|
||||
@@ -100,25 +112,26 @@
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOOrphans']['Summary']
|
||||
& $Script:GPOConfiguration['GPOBroken']['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']
|
||||
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'ObjectClass Issue', 'Permissions Issue' -Color Crimson, LightCoral, MediumOrchid, IndianRed
|
||||
New-ChartBar -Name 'Broken' -Value $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue'], $Script:Reporting['GPOBroken']['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-HTMLTable -DataTable $Script:Reporting['GPOBroken']['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 "ObjectClass issue" -BackgroundColor MediumOrchid -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Status' -Value "Permissions issue" -BackgroundColor MediumVioletRed -ComparisonType string -Color White
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory' {
|
||||
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory / ObjectClass issue' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLWizard {
|
||||
@@ -133,7 +146,7 @@
|
||||
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
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoBefore.html -Verbose -Type GPOBroken
|
||||
}
|
||||
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."
|
||||
@@ -243,23 +256,61 @@
|
||||
"If there's nothing else to be deleted on AD side, we can skip to next step step. "
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Fix GPOs of wrong ObjectClass' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed runs cleanup procedure that removes all GPOs which have ObjectClass of Container, rather than required groupPolicyContainer. "
|
||||
"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 GPO metadata from AD, and any files/folders from SYSVOL.'
|
||||
) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type ObjectClass -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 ObjectClass -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 as backup process doesn't see GPOs that are of wrong ObjectClass. "
|
||||
"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 ObjectClass -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 ObjectClass -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, 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
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoAfter.html -Verbose -Type GPOBroken
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrphans']['WarningsAndErrors']) {
|
||||
if ($Script:Reporting['GPOBroken']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBroken']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
|
||||
@@ -77,18 +77,19 @@
|
||||
#>
|
||||
}
|
||||
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 -FontSize 10pt -Text @(
|
||||
"By default, GPO creation is usually maintained by Domain Admins or Enterprise Admins. "
|
||||
"When GPO is created by Domain Admins or Enterprise Admins group members, the GPO Owner is set to Domain Admins. "
|
||||
"When GPO is created by a 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 the relevant user. "
|
||||
"GPO Owners should be Domain Admins or Enterprise Admins to prevent abuse. "
|
||||
"If that isn't so, it means the owner can fully control GPO and potentially change its settings in an uncontrolled way. "
|
||||
"While at the moment of creation of new GPO, it's not a problem, in the long term, it's possible such a person may no longer be admin, yet keep their rights over GPO. "
|
||||
"As your 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 is 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
|
||||
@@ -217,7 +218,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
Permissions = Get-GPOZaurrPermission -Type Administrative -IncludePermissionType GpoEditDeleteModifySecurity -ReturnSecurityWhenNoData -IncludeGPOObject -ReturnSingleObject -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
$Object['PermissionsPerRow'] = $Object['Permissions'] | ForEach-Object { $_ }
|
||||
$Object['PermissionsAnalysis'] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
$Object
|
||||
}
|
||||
Processing = {
|
||||
@@ -33,6 +34,8 @@
|
||||
$Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFixPerDomain'][$GPO[0].DomainName]++
|
||||
}
|
||||
# lets create table and add it there
|
||||
$Script:Reporting['GPOPermissionsAdministrative']['Data'].'PermissionsAnalysis'.Add($Analysis)
|
||||
}
|
||||
if ($Script:Reporting['GPOPermissionsAdministrative']['Variables']['WillFix'] -gt 0) {
|
||||
$Script:Reporting['GPOPermissionsAdministrative']['ActionRequired'] = $true
|
||||
@@ -88,11 +91,16 @@
|
||||
} -Title 'Group Policy Permissions' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Administrative Users Analysis' {
|
||||
New-HTMLSection -Name 'Group Policy Administrative Users Permissions Summary' {
|
||||
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 'Group Policy Administrative Users Analysis' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsAdministrative']['Data'].PermissionsAnalysis -Filtering {
|
||||
# New-HTMLTableCondition -Name 'Permission' -Value '' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 7, 15, 30, 45, 60
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
New-HTMLSection -Name 'Steps to fix Group Policy Administrative Users' {
|
||||
New-HTMLContainer {
|
||||
@@ -184,7 +192,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
$GPOZaurrPermissionsAnalysis = [ordered] @{
|
||||
Name = 'Group Policy Permissions Analysis'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
$Object = [ordered] @{
|
||||
Permissions = Get-GPOZaurrPermission -ReturnSecurityWhenNoData -IncludeGPOObject -ReturnSingleObject -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
$Object['PermissionsPerRow'] = $Object['Permissions'] | ForEach-Object { $_ }
|
||||
$Object['PermissionsAnalysis'] = Get-GPOZaurrPermissionAnalysis -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -Permissions $Object.Permissions
|
||||
$Object['PermissionsIssues'] = Get-GPOZaurrPermissionIssue -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
$Object
|
||||
}
|
||||
Processing = {
|
||||
# Create Per Domain Variables
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrativePerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrativePerDomain'] = @{}
|
||||
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsersPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAuthenticatedUsersPerDomain'] = @{}
|
||||
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixSystemPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystemPerDomain'] = @{}
|
||||
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixUnknownPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknownPerDomain'] = @{}
|
||||
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotFixPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixPerDomain'] = @{}
|
||||
|
||||
$Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'] = @{}
|
||||
|
||||
foreach ($GPO in $Script:Reporting['GPOPermissions']['Data'].PermissionsIssues) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if ($GPO.PermissionIssue) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotRead']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['Read']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['Total']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
foreach ($GPO in $Script:Reporting['GPOPermissions']['Data'].PermissionsAnalysis) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillFixPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillNotFixPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotFixPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrativePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrativePerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrativePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrativePerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsersPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsersPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillFixSystemPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixSystemPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystemPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystemPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillFixUnknownPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixUnknownPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknownPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknownPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
|
||||
# Checks
|
||||
if ($GPO.Administrative -eq $true) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrative']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrativePerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrative']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrativePerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
|
||||
if ($GPO.AuthenticatedUsers -eq $true) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAuthenticatedUsers']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAuthenticatedUsersPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsers']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsersPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
|
||||
if ($GPO.System -eq $true) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystem']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystemPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixSystem']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixSystemPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
|
||||
if ($GPO.Unknown -eq $false) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknown']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknownPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixUnknown']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixUnknownPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
|
||||
if ($GPO.Status -eq $false) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotFix']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotFixPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOPermissions']['Variables']['WillFix'] -gt 0 -or $Script:Reporting['GPOPermissions']['Variables']['CouldNotRead'] -gt 0) {
|
||||
$Script:Reporting['GPOPermissions']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
# Issues / Couldnt read report
|
||||
Read = 0
|
||||
ReadPerDomain = $null
|
||||
CouldNotRead = 0
|
||||
CouldNotReadPerDomain = $null
|
||||
TotalPerDomain = $null
|
||||
Total = 0
|
||||
# Permissions analysis
|
||||
WillFix = 0
|
||||
WillNotFix = 0
|
||||
WillFixAdministrative = 0
|
||||
WillNotTouchAdministrative = 0
|
||||
WillFixUnknown = 0
|
||||
WillNotTouchUnknown = 0
|
||||
WillNotTouchSystem = 0
|
||||
WillFixSystem = 0
|
||||
WillNotTouchAuthenticatedUsers = 0
|
||||
WillFixAuthenticatedUsers = 0
|
||||
WillNotTouchAuthenticatedUsersPerDomain = $null
|
||||
WillFixAuthenticatedUsersPerDomain = $null
|
||||
WillNotTouchSystemPerDomain = $null
|
||||
WillFixSystemPerDomain = $null
|
||||
WillFixAdministrativePerDomain = $null
|
||||
WillNotTouchAdministrativePerDomain = $null
|
||||
WillNotFixPerDomain = $null
|
||||
WillFixPerDomain = $null
|
||||
WillFixUnknownPerDomain = $null
|
||||
WillNotTouchUnknownPerDomain = $null
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -Text "When GPO is created it gets a handful of standard permissions. Those are:"
|
||||
New-HTMLList {
|
||||
New-HTMLListItem -Text "NT AUTHORITY\Authenticated Users with GpoApply permissions"
|
||||
New-HTMLListItem -Text "Domain Admins and Enterprise Admins with Edit/Delete/Modify permissions"
|
||||
New-HTMLListItem -Text "SYSTEM account with Edit/Delete/Modify permissions"
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text "But then IT people change those permissions to their own needs. While most changes make sense and are required to be able to target proper groups of people, some changes are not required or even bad. "
|
||||
|
||||
New-HTMLText -Text "First problem relates to NT AUTHORITY\Authenticated Users" -FontSize 10pt -FontWeight bold -TextDecoration underline -Alignment center
|
||||
|
||||
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. "
|
||||
"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. "
|
||||
"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. "
|
||||
}
|
||||
New-HTMLList {
|
||||
New-HTMLListItem -Text "Before MS16-072 is installed, user group policies were retrieved by using the user’s security context. "
|
||||
New-HTMLListItem -Text "After MS16-072 is installed, user group policies are retrieved by using the computer's security context."
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text @(
|
||||
"Unfortunetly it's not as simple as it sounds. While checking for permissions mostly works fine, it's possible a Group Policy has totally removed account permissions from being able to asses any of it. ",
|
||||
"The account we're using "
|
||||
"$($Env:USERDOMAIN)\$($Env:USERNAME.ToUpper())",
|
||||
" may simply not have enough permisions to properly asses permissions for a GPO. "
|
||||
"Therefore we're using dual assesment for this situation, where first assesment is checking for GPO visibility or lack of it, and second assesment is checking for direct permissions assignement. "
|
||||
"We just were able to detect the problem, but hopefully higher level account (Domain Admin) should be able to provide full assesment. "
|
||||
) -FontWeight normal, normal, bold, normal -Color None, None, BlueDiamond, none, none
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "First assesment results: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text "Group Policies couldn't read at all: ", $Script:Reporting['GPOPermissions']['Variables']['CouldNotRead'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies with permissions allowing read: ", $Script:Reporting['GPOPermissions']['Variables']['Read'] -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['GPOPermissions']['Variables']['CouldNotReadPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text @(
|
||||
"$Domain requires ",
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$Domain],
|
||||
" changes out of ",
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$Domain],
|
||||
"."
|
||||
) -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "Second Assesment results " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies requiring Authenticated Users with GpoRead permission: ', $Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsers'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAuthenticatedUsers'] -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['GPOPermissions']['Variables']['WillFixAuthenticatedUsersPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsersPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text "Second problem relates to Domain Admins and Enterprise Admins" -FontSize 10pt -FontWeight bold -TextDecoration underline -Alignment center
|
||||
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. "
|
||||
"Domain Admins and Enterprise Admins have to have either GPOEditModify permissions or at the very least GPOCustom. "
|
||||
"When GPOCustom is set it usually means there's a mix of Allow and Deny permission in place (for example deny GPOApply). "
|
||||
"In such case we're assuming you know what you're doing. However it's always possible to review those permissions, as those are marked in the table for review. "
|
||||
} -LineBreak
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "Assesment results " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies requiring Administrative permission fix: ', $Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrative'] -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['GPOPermissions']['Variables']['WillFixAdministrativePerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrativePerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -Text "Third problem relates to SYSTEM account" -FontSize 10pt -FontWeight bold -TextDecoration underline -Alignment center
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"When GPO is created by default it gets SYSTEM account 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 SYSTEM 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 SYSTEM permission fix: ', $Script:Reporting['GPOPermissions']['Variables']['WillFixSystem'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystem'] -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['GPOPermissions']['Variables']['WillFixSystemPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissions']['Variables']['WillFixSystemPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -Text "Fourth problem relates to UNKNOWN SID" -FontSize 10pt -FontWeight bold -TextDecoration underline -Alignment center
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"Sometimes groups or users are deleted in Active Directory and unfortunetly their permissions are not cleaned automatically. "
|
||||
"Those are left in-place and stay there forever until removed. "
|
||||
} -LineBreak
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "Assesment results " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies requiring Unknown permission removal: ', $Script:Reporting['GPOPermissions']['Variables']['WillFixUnknown'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknown'] -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['GPOPermissions']['Variables']['WillFixUnknownPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissions']['Variables']['WillFixUnknownPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt -LineBreak
|
||||
# Just in case report is too big and the full file is not attached
|
||||
New-HTMLText -FontSize 10pt -Text "To generate up to date report please execute: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Install-Module GPOZaurr -Force', ' or ', ' install module manually.' -Color RoyalBlue, None, None
|
||||
New-HTMLListItem -Text 'Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsBefore.html -Verbose -Type GPOPermissions' -Color RoyalBlue
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text 'Steps above will generate above summary with more details allowing you to get up to date report and steps on how to fix it.'
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOPermissions']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Yes', 'No' -Color SpringGreen, Salmon
|
||||
New-ChartBar -Name 'Visible Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['Read'], $Script:Reporting['GPOPermissions']['Variables']['CouldNotRead']
|
||||
New-ChartBar -Name 'Overall Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotFix'], $Script:Reporting['GPOPermissions']['Variables']['WillFix']
|
||||
New-ChartBar -Name 'Administrative Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrative'], $Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrative']
|
||||
New-ChartBar -Name 'Authenticated Users Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrative'], $Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsers']
|
||||
New-ChartBar -Name 'System Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchSystem'], $Script:Reporting['GPOPermissions']['Variables']['WillFixSystem']
|
||||
New-ChartBar -Name 'Unknown Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchUnknown'], $Script:Reporting['GPOPermissions']['Variables']['WillFixUnknown']
|
||||
} -Title 'Group Policy Permissions' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Visibility Analysis' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['Data'].PermissionsIssues -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 'Group Policy Permissions Analysis' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLText -Text 'Explanation to table columns:' -FontSize 10pt
|
||||
New-HTMLList {
|
||||
New-HTMLListItem -FontWeight bold, normal -Text "Status", " - means GPO has at least one problem with permissions. "
|
||||
New-HTMLListItem -FontWeight bold, normal -Text "Administrative", " - means GPO has problem with either Domain Admins or Enterprise Admins not having proper permissions. "
|
||||
New-HTMLListItem -FontWeight bold, normal -Text "AuthenticatedUsers", " - means GPO has Authenticated Users missing either as GPOApply or GPORead. "
|
||||
New-HTMLListItem -FontWeight bold, normal -Text "System", " - means GPO has SYSTEM permission missing or lacking proper permissions. "
|
||||
New-HTMLListItem -FontWeight bold, normal -Text "DomainAdmins", " - means GPO has Domain Admins missing or having wrong permissions. "
|
||||
New-HTMLListItem -FontWeight bold, normal -Text "EnterpriseAdmins", " - means GPO has Enterprise Admins missing or having wrong permissions. "
|
||||
} -FontSize 10pt
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['Data'].PermissionsAnalysis -Filtering {
|
||||
New-HTMLTableCondition -Name 'Status' -Value 'True' -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Administrative' -Value 'True' -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'AuthenticatedUsers' -Value 'True' -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'System' -Value 'True' -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Unknown' -Value 'False' -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'DomainAdmins' -Value 'True' -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'EnterpriseAdmins' -Value 'True' -BackgroundColor SpringGreen -ComparisonType string
|
||||
|
||||
# Mark as warning
|
||||
New-HTMLTableCondition -Name 'DomainAdminsPermission' -Value 'GpoCustom' -BackgroundColor Moccasin -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'EnterpriseAdminsPermission' -Value 'GpoCustom' -BackgroundColor Moccasin -ComparisonType string
|
||||
# Reverse
|
||||
New-HTMLTableCondition -Name 'Status' -Value 'True' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'Administrative' -Value 'True' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'AuthenticatedUsers' -Value 'True' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'System' -Value 'True' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'Unknown' -Value 'False' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'DomainAdmins' -Value 'True' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'EnterpriseAdmins' -Value 'True' -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
|
||||
New-TableEvent -TableID 'GPOPermissionsAll' -SourceColumnName 'GUID' -TargetColumnID 1 # TargetColumnID 1 eq GUID on the other table
|
||||
} -PagingOptions 7, 15, 30, 45, 60
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'All Permissions' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['Data'].PermissionsPerRow -Filtering {
|
||||
New-HTMLTableHeader -Names 'PrincipalNetBiosName', 'PrincipalDistinguishedName', 'PrincipalDomainName', 'PrincipalName', 'PrincipalSid', 'PrincipalSidType' -Title 'Account Information'
|
||||
New-HTMLTableCondition -Name 'Permission' -Value 'GpoEditDeleteModifySecurity' -BackgroundColor HotPink -ComparisonType string -Operator eq
|
||||
New-HTMLTableCondition -Name 'Permission' -Value 'GpoCustom' -BackgroundColor Moccasin -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Permission' -Value 'GpoApply' -BackgroundColor Orange -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Permission' -Value 'GpoRead' -BackgroundColor MediumSpringGreen -ComparisonType string -Operator eq
|
||||
New-HTMLTableCondition -Name 'PrincipalSidType' -Value 'Unknown' -BackgroundColor Salmon -ComparisonType string -Operator eq
|
||||
} -PagingOptions 7, 15, 30, 45, 60 -DataTableID 'GPOPermissionsAll'
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
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\GPOZaurrGPOPermissionsBefore.html -Verbose -Type GPOPermissions
|
||||
}
|
||||
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 {
|
||||
# This gets all permissions
|
||||
$AllPermissions = Get-GPOZaurrPermission
|
||||
$AllPermissions | Format-Table
|
||||
|
||||
# this analyses permissions
|
||||
$PermissionsAnalysis = Get-GPOZaurrPermissionAnalysis
|
||||
$PermissionsAnalysis | 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 permissions' {
|
||||
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 'Add Administrative Groups permissions' {
|
||||
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 'Add SYSTEM permissions' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command will find any GPO which doesn't have SYSTEM account 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 SYSTEM 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 WellKnownAdministrative -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 WellKnownAdministrative -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 WellKnownAdministrative -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 WellKnownAdministrative -PermissionType GpoEditDeleteModifySecurity -All -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed adds SYSTEM account (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 'Remove UNKNOWN permissions' {
|
||||
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\GPOZaurrGPOPermissionsAfter.html -Verbose -Type GPOPermissions
|
||||
}
|
||||
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 -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
Execute = {
|
||||
[ordered] @{
|
||||
Permissions = Get-GPOZaurrPermission -Type AuthenticatedUsers -ReturnSecurityWhenNoData -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
Issues = Get-GPOZaurrPermissionIssue
|
||||
Issues = Get-GPOZaurrPermissionIssue -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
}
|
||||
Processing = {
|
||||
@@ -93,7 +93,7 @@
|
||||
} -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()),
|
||||
"$($Env:USERDOMAIN)\$($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. "
|
||||
@@ -249,7 +249,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
}
|
||||
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
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
function New-GPOZaurrReportHTML {
|
||||
function New-GPOZaurrReportHTML {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[System.Collections.IDictionary] $Support,
|
||||
[string] $Path,
|
||||
[switch] $Offline,
|
||||
[switch] $Online,
|
||||
[switch] $Open
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$PSDefaultParameterValues = @{
|
||||
"New-HTMLTable:WarningAction" = 'SilentlyContinue'
|
||||
}
|
||||
@@ -194,5 +189,5 @@ function New-GPOZaurrReportHTML {
|
||||
}
|
||||
}
|
||||
}
|
||||
} -Online:(-not $Offline.IsPresent) -Open:$Open.IsPresent -FilePath $Path
|
||||
} -Online:$Online.IsPresent -Open:$Open.IsPresent -FilePath $Path
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
$Script:GPOConfiguration = [ordered] @{
|
||||
GPOOrphans = $GPOZaurrOrphans
|
||||
GPOBroken = $GPOZaurrOrphans
|
||||
GPOBrokenLink = $GPOZaurrBrokenLink
|
||||
GPOOwners = $GPOZaurrOwners
|
||||
GPOConsistency = $GPOZaurrConsistency
|
||||
GPODuplicates = $GPOZaurrDuplicates
|
||||
GPOList = $GPOZaurrList
|
||||
GPOLinks = $GPOZaurrLinks
|
||||
GPOPassword = $GPOZaurrPassword
|
||||
GPOPermissions = $GPOZaurrPermissions
|
||||
GPOPermissions = $GPOZaurrPermissionsAnalysis
|
||||
GPOPermissionsAdministrative = $GPOZaurrPermissionsAdministrative
|
||||
GPOPermissionsRead = $GPOZaurrPermissionsRead
|
||||
GPOPermissionsRoot = $GPOZaurrPermissionsRoot
|
||||
|
||||
@@ -336,6 +336,21 @@
|
||||
}
|
||||
}
|
||||
FolderRedirection = @{
|
||||
Types = @(
|
||||
@{
|
||||
Category = 'FolderRedirectionSettings'
|
||||
Settings = 'Folder'
|
||||
}
|
||||
)
|
||||
GPOPath = 'Windows Settings -> Folder Redirection'
|
||||
Code = {
|
||||
ConvertTo-XMLFolderRedirection -GPO $GPO
|
||||
}
|
||||
CodeSingle = {
|
||||
ConvertTo-XMLFolderRedirection -GPO $GPO -SingleObject
|
||||
}
|
||||
}
|
||||
FolderRedirectionPolicy = @{
|
||||
Types = @(
|
||||
@{
|
||||
Category = 'RegistrySettings'
|
||||
@@ -1252,6 +1267,21 @@
|
||||
ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'Windows Components/Windows Remote Management (WinRM)*' -SingleObject
|
||||
}
|
||||
}
|
||||
WindowsTimeService = @{
|
||||
Types = @(
|
||||
@{
|
||||
Category = 'RegistrySettings'
|
||||
Settings = 'Policy'
|
||||
}
|
||||
)
|
||||
GPOPath = 'Policies -> Administrative Templates -> System/Windows Time Service'
|
||||
Code = {
|
||||
ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Windows Time Service*'
|
||||
}
|
||||
CodeSingle = {
|
||||
ConvertTo-XMLGenericPolicy -GPO $GPO -Category 'System/Windows Time Service*' -SingleObject
|
||||
}
|
||||
}
|
||||
WindowsUpdate = @{
|
||||
Types = @(
|
||||
@{
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
)
|
||||
$Differences = @{ }
|
||||
$SysvolHash = @{ }
|
||||
|
||||
$GPOGUIDS = $GPOs.ID.GUID
|
||||
$GPOGUIDS = ConvertFrom-DistinguishedName -DistinguishedName $GPOs.DistinguishedName
|
||||
$SysVolPath = "\\$($Server)\SYSVOL\$Domain\Policies"
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL from \\$($Server)\SYSVOL\$Domain\Policies"
|
||||
try {
|
||||
@@ -19,10 +18,10 @@
|
||||
$Sysvol = $Null
|
||||
}
|
||||
foreach ($_ in $SYSVOL) {
|
||||
$GUID = $_.Name -replace '{' -replace '}'
|
||||
$GUID = $_.Name
|
||||
$SysvolHash[$GUID] = $_
|
||||
}
|
||||
$Files = $SYSVOL.Name -replace '{' -replace '}'
|
||||
$Files = $SYSVOL.Name
|
||||
if ($Files) {
|
||||
$Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual
|
||||
foreach ($_ in $Comparing) {
|
||||
@@ -30,8 +29,10 @@
|
||||
# we skip policy definitions
|
||||
continue
|
||||
}
|
||||
$ADStatus = $PoliciesAD[$_.InputObject]
|
||||
if ($_.SideIndicator -eq '==') {
|
||||
$Found = 'Exists'
|
||||
#$Found = 'Exists'
|
||||
$Found = $ADStatus
|
||||
} elseif ($_.SideIndicator -eq '<=') {
|
||||
$Found = 'Not available on SYSVOL'
|
||||
} elseif ($_.SideIndicator -eq '=>') {
|
||||
@@ -41,8 +42,10 @@
|
||||
$Found = 'Not available in AD'
|
||||
}
|
||||
} else {
|
||||
# This shouldn't happen at all
|
||||
$Found = 'Orphaned GPO'
|
||||
}
|
||||
|
||||
$Differences[$_.InputObject] = $Found
|
||||
}
|
||||
}
|
||||
@@ -50,85 +53,60 @@
|
||||
$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
|
||||
}
|
||||
$GPOGuid = ConvertFrom-DistinguishedName -DistinguishedName $GPO.DistinguishedName
|
||||
if ($GPO.DisplayName) {
|
||||
$GPODisplayName = $GPO.DisplayName
|
||||
$GPOName = $GPO.Name
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing [$($Domain)]($Count/$($GPOS.Count)) $($GPO.DisplayName)"
|
||||
} else {
|
||||
$FullPath = -join ($SysVolPath, "\{$($GPO.Id.Guid)}")
|
||||
$ACL = $null
|
||||
$Owner = ''
|
||||
$GPOName = $GPOGuid
|
||||
$GPODisplayName = $GPOGuid
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing [$($Domain)]($Count/$($GPOS.Count)) $($GPOGuid)"
|
||||
}
|
||||
if ($null -ne $SysvolHash[$GPOGuid].FullName) {
|
||||
$FullPath = $SysvolHash[$GPOGuid].FullName
|
||||
$ErrorMessage = ''
|
||||
} else {
|
||||
$FullPath = -join ($SysVolPath, "\$($GPOGuid)")
|
||||
$ErrorMessage = 'Not found on SYSVOL'
|
||||
}
|
||||
if ($null -eq $Differences[$GPO.Id.Guid]) {
|
||||
$SysVolStatus = 'Unknown Issue'
|
||||
if ($null -eq $Differences[$GPOGuid]) {
|
||||
$SysVolStatus = 'Unknown issue'
|
||||
} else {
|
||||
$SysVolStatus = $Differences[$GPO.Id.Guid]
|
||||
$SysVolStatus = $Differences[$GPOGuid]
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DisplayName = $GPODisplayName
|
||||
Status = $SysVolStatus
|
||||
DomainName = $GPO.DomainName
|
||||
DomainName = $Domain
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = $SysVolStatus
|
||||
GpoStatus = $GPO.GpoStatus
|
||||
Owner = $GPO.Owner
|
||||
FileOwner = $Owner
|
||||
Id = $GPO.Id.Guid
|
||||
ObjectClass = $GPO.ObjectClass
|
||||
Id = $GPOName
|
||||
Path = $FullPath
|
||||
DistinguishedName = -join ("CN={", $GPO.Id.Guid, "},", $PoliciesSearchBase)
|
||||
DistinguishedName = -join ("CN=", $GPOGuid, ",", $PoliciesSearchBase)
|
||||
Description = $GPO.Description
|
||||
CreationTime = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
UserVersion = $GPO.UserVersion
|
||||
ComputerVersion = $GPO.ComputerVersion
|
||||
WmiFilter = $GPO.WmiFilter
|
||||
CreationTime = $GPO.Created
|
||||
ModificationTime = $GPO.Modified
|
||||
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') {
|
||||
if ($Differences[$_] -in 'Not available in AD') {
|
||||
$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
|
||||
ObjectClass = ''
|
||||
Id = $_
|
||||
Path = $FullPath
|
||||
DistinguishedName = -join ("CN={", $_, "},", $PoliciesSearchBase)
|
||||
DistinguishedName = -join ("CN=", $_, ",", $PoliciesSearchBase)
|
||||
Description = $null
|
||||
CreationTime = $SysvolHash[$_].CreationTime
|
||||
ModificationTime = $SysvolHash[$_].LastWriteTime
|
||||
UserVersion = $null
|
||||
ComputerVersion = $null
|
||||
WmiFilter = $null
|
||||
Error = $ErrorMessage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
}
|
||||
}
|
||||
#>
|
||||
if (-not $PermissionsAnalysis['Skip']) {
|
||||
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!"
|
||||
@@ -118,7 +118,7 @@
|
||||
# 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) {
|
||||
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++
|
||||
@@ -132,7 +132,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($PermissionsAnalysis['EnterpriseAdmins'] -eq $false) {
|
||||
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++
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function Clear-GPOZaurrSysvolDFSR {
|
||||
function Clear-GPOZaurrSysvolDFSR {
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function ConvertFrom-CSExtension {
|
||||
function ConvertFrom-CSExtension {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[string[]] $CSE,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function Find-CSExtension {
|
||||
function Find-CSExtension {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[string[]] $CSE,
|
||||
|
||||
+10
-4
@@ -47,6 +47,10 @@
|
||||
if ($OwnerOnly -or $PermissionsOnly -and $Type) {
|
||||
Write-Warning "Get-GPOZaurr - Using PermissionOnly or OwnerOnly with Type is not supported. "
|
||||
}
|
||||
if (-not $GPOPath) {
|
||||
# This is needed, because Get-GPOReport doesn't deliver full scope of links, just some of it. It doesn't cover OUs with blocked inheritance, sites or crosslinked
|
||||
$LinksSummaryCache = Get-GPOZaurrLink -AsHashTable -Summary -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
}
|
||||
}
|
||||
Process {
|
||||
if (-not $GPOPath) {
|
||||
@@ -86,7 +90,7 @@
|
||||
Write-Warning "Get-GPOZaurr - Failed to get [$($GPO.DomainName)]($Count/$($GroupPolicies.Count)) $($GPO.DisplayName) GPOReport: $($_.Exception.Message). Skipping."
|
||||
continue
|
||||
}
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $GPO -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent -ExcludeGroupPolicies $ExcludeGPO -Type $Type
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $GPO -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent -ExcludeGroupPolicies $ExcludeGPO -Type $Type -LinksSummaryCache $LinksSummaryCache
|
||||
} else {
|
||||
$GPO
|
||||
}
|
||||
@@ -96,9 +100,11 @@
|
||||
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 -ExcludeGroupPolicies $ExcludeGPO -Type $Type
|
||||
if ($_.Name -ne 'GPOList.xml') {
|
||||
$XMLContent = [XML]::new()
|
||||
$XMLContent.Load($_.FullName)
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -PermissionsOnly:$PermissionsOnly.IsPresent -ExcludeGroupPolicies $ExcludeGPO -Type $Type
|
||||
}
|
||||
}
|
||||
Write-Verbose "Get-GPOZaurr - Finished GPO content from XML files"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,47 @@
|
||||
function Get-GPOZaurrBroken {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Detects broken or otherwise damaged Group Policies
|
||||
|
||||
.DESCRIPTION
|
||||
Detects broken or otherwise damaged Group Policies providing insight whether GPO exists in both AD and SYSVOL.
|
||||
It provides few statuses:
|
||||
- Permissions issue - means account couldn't read GPO due to permissions
|
||||
- ObjectClass issue - means that ObjectClass is of type Container, rather than expected groupPolicyContainer
|
||||
- Not available on SYSVOL - means SYSVOL data is missing, yet AD metadata is available
|
||||
- Not available in AD - means AD metadata is missing, yet SYSVOL data is available
|
||||
- Exists - means AD metadata and SYSVOL data are available
|
||||
|
||||
.PARAMETER Forest
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Exclude domain from search, by default whole forest is scanned
|
||||
|
||||
.PARAMETER ExcludeDomainControllers
|
||||
Exclude specific domain controllers, by default there are no exclusions, as long as VerifyDomainControllers switch is enabled. Otherwise this parameter is ignored.
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Include only specific domains, by default whole forest is scanned
|
||||
|
||||
.PARAMETER IncludeDomainControllers
|
||||
Include only specific domain controllers, by default all domain controllers are included, as long as VerifyDomainControllers switch is enabled. Otherwise this parameter is ignored.
|
||||
|
||||
.PARAMETER SkipRODC
|
||||
Skip Read-Only Domain Controllers. By default all domain controllers are included.
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Ability to provide Forest Information from another command to speed up processing
|
||||
|
||||
.PARAMETER VerifyDomainControllers
|
||||
Forces cmdlet to check GPO Existance on Domain Controllers rather then per domain
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrBroken -Verbose | Format-Table
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[alias('Get-GPOZaurrSysvol')]
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
@@ -8,7 +51,6 @@
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[alias('DomainControllers')][string[]] $IncludeDomainControllers,
|
||||
[switch] $SkipRODC,
|
||||
[Array] $GPOs,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[switch] $VerifyDomainControllers
|
||||
)
|
||||
@@ -21,31 +63,31 @@
|
||||
$PoliciesAD = @{}
|
||||
if ($SystemsContainer) {
|
||||
$PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer)
|
||||
$PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer
|
||||
$PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer -Properties Name, gPCFileSysPath, DisplayName, DistinguishedName, Description, Created, Modified, ObjectClass, ObjectGUID
|
||||
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'
|
||||
if ($Policy.ObjectClass -eq 'Container') {
|
||||
# This usually means GPO deletion process somehow failed and while object itself stayed it isn't groupPolicyContainer anymore
|
||||
$PoliciesAD[$GUIDFromDN] = 'ObjectClass issue'
|
||||
} else {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Permissions issue'
|
||||
$GUID = $Policy.Name
|
||||
if ($GUID -and $GUIDFromDN) {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Exists'
|
||||
} else {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Permissions issue'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Get-GPOZaurrBroken - Couldn't get GPOs from $Domain. Skipping"
|
||||
}
|
||||
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 ($PoliciesInAD.Count -ge 2) {
|
||||
if (-not $VerifyDomainControllers) {
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Domain -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
Test-SysVolFolders -GPOs $PoliciesInAD -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
|
||||
Test-SysVolFolders -GPOs $PoliciesInAD -Server $Server.Hostname -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
function Get-GPOZaurrBrokenLink {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Finds any GPO link that doesn't have a matching GPO (already removed GPO).
|
||||
|
||||
.DESCRIPTION
|
||||
Finds any GPO link that doesn't have a matching GPO (already removed GPO).
|
||||
|
||||
.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
|
||||
Get-GPOZaurrBrokenLink -Verbose | Format-Table -AutoSize *
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrBrokenLink -Verbose -IncludeDomains ad.evotec.pl | Format-Table -AutoSize *
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
$ErrorFound = $false
|
||||
$PoliciesAD = @{}
|
||||
# We need to request all GPOS from Forest. Requesting just for any domain won't be enough
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -Extended # -Extended
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0]
|
||||
$SystemsContainer = $ForestInformation['DomainsExtended'][$Domain].SystemsContainer
|
||||
if ($SystemsContainer) {
|
||||
$PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer)
|
||||
try {
|
||||
$PoliciesInAD = Get-ADObject -ErrorAction Stop -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer -Properties Name, gPCFileSysPath, DisplayName, DistinguishedName, Description, Created, Modified, ObjectClass, ObjectGUID
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrBrokenLink - An error occured while searching $PoliciesSearchBase. Error $($_.Exception.Message). Please resolve this before continuing."
|
||||
$ErrorFound = $true
|
||||
break
|
||||
}
|
||||
foreach ($Policy in $PoliciesInAD) {
|
||||
$GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName
|
||||
# $Key = "$($Domain)$($GuidFromDN)"
|
||||
$Key = $Policy.DistinguishedName
|
||||
if ($Policy.ObjectClass -eq 'Container') {
|
||||
# This usually means GPO deletion process somehow failed and while object itself stayed it isn't groupPolicyContainer anymore
|
||||
$PoliciesAD[$Key] = 'ObjectClass issue'
|
||||
} else {
|
||||
$GUID = $Policy.Name
|
||||
if ($GUID -and $GUIDFromDN) {
|
||||
$PoliciesAD[$Key] = 'Exists'
|
||||
} else {
|
||||
$PoliciesAD[$Key] = 'Permissions issue'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Get-GPOZaurrBroken - Couldn't get GPOs from $Domain. Skipping"
|
||||
}
|
||||
}
|
||||
if ($ErrorFound) {
|
||||
return
|
||||
}
|
||||
# In case of links we can request here whatever user requested.
|
||||
# This will search for broken links in domain user requested
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -Extended -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
$Links = Get-GPOZaurrLinkLoop -Linked 'All' -ForestInformation $ForestInformation
|
||||
foreach ($Link in $Links) {
|
||||
if (-not $PoliciesAD[$Link.GPODistinguishedName]) {
|
||||
$Link
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
function Get-GPOZaurrFilesPolicyDefinition {
|
||||
function Get-GPOZaurrFilesPolicyDefinition {
|
||||
[alias('Get-GPOZaurrFilesPolicyDefinitions')]
|
||||
[cmdletbinding()]
|
||||
param(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function Get-GPOZaurrFolders {
|
||||
function Get-GPOZaurrFolders {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('All', 'Netlogon', 'Sysvol')][string[]] $Type = 'All',
|
||||
|
||||
+104
-166
@@ -1,13 +1,15 @@
|
||||
function Get-GPOZaurrLink {
|
||||
[cmdletbinding()]
|
||||
[cmdletbinding(DefaultParameterSetName = 'Linked')]
|
||||
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')",
|
||||
# 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 = 'Linked')][validateset('All', 'Root', 'DomainControllers', 'Site', 'OrganizationalUnit')][string[]] $Linked,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')][string[]] $Site,
|
||||
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
@@ -19,30 +21,47 @@
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[switch] $SkipDuplicates,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[System.Collections.IDictionary] $GPOCache,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[alias('ForestName')][string] $Forest,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[string[]] $ExcludeDomains,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[switch] $AsHashTable,
|
||||
|
||||
[parameter(ParameterSetName = 'Site')]
|
||||
[parameter(ParameterSetName = 'Filter')]
|
||||
[parameter(ParameterSetName = 'ADObject')]
|
||||
[parameter(ParameterSetName = 'Linked')]
|
||||
[switch] $Summary
|
||||
)
|
||||
Begin {
|
||||
$CacheReturnedGPOs = [ordered] @{}
|
||||
@@ -52,6 +71,7 @@
|
||||
# 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) {
|
||||
Write-Verbose "Get-GPOZaurrLink - Building GPO cache for domain $Domain"
|
||||
$QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
|
||||
Get-GPO -All -DomainName $Domain -Server $QueryServer | ForEach-Object {
|
||||
$GPOCache["$Domain$($_.ID.Guid)"] = $_
|
||||
@@ -60,173 +80,91 @@
|
||||
}
|
||||
}
|
||||
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]
|
||||
$getGPOLoopSplat = @{
|
||||
Linked = $Linked
|
||||
ForestInformation = $ForestInformation
|
||||
SearchScope = $SearchScope
|
||||
SearchBase = $SearchBase
|
||||
ADObject = $ADObject
|
||||
Filter = $Filter
|
||||
SkipDuplicates = $SkipDuplicates
|
||||
Site = $Site
|
||||
}
|
||||
Remove-EmptyValue -Hashtable $getGPOLoopSplat -Recursive
|
||||
$getGPOLoopSplat['CacheReturnedGPOs'] = $CacheReturnedGPOs
|
||||
if ($AsHashTable -or $Summary) {
|
||||
$HashTable = [ordered] @{}
|
||||
$SummaryHashtable = [ordered] @{}
|
||||
$Links = Get-GPOZaurrLinkLoop @getGPOLoopSplat
|
||||
foreach ($Link in $Links) {
|
||||
$Key = -join ($Link.DomainName, $Link.GUID)
|
||||
if (-not $HashTable[$Key]) {
|
||||
$HashTable[$Key] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
$HashTable[$Key].Add($Link)
|
||||
}
|
||||
foreach ($Key in $HashTable.Keys) {
|
||||
[Array] $Link = $HashTable[$Key]
|
||||
$EnabledLinks = $Link.Enabled.Where( { $_ -eq $true }, 'split')
|
||||
|
||||
$LinkedRoot = $false
|
||||
$LinkedRootPlaces = [System.Collections.Generic.List[string]]::new()
|
||||
$LinkedSite = $false
|
||||
$LinkedSitePlaces = [System.Collections.Generic.List[string]]::new()
|
||||
$LinkedOU = $false
|
||||
$LinkedCrossDomain = $false
|
||||
$LinkedCrossDomainPlaces = [System.Collections.Generic.List[string]]::new()
|
||||
foreach ($InternalLink in $Link) {
|
||||
if ($InternalLink.ObjectClass -eq 'domainDNS') {
|
||||
$LinkedRoot = $true
|
||||
$LinkedRootPlaces.Add($InternalLink.DistinguishedName)
|
||||
} elseif ($InternalLink.ObjectClass -eq 'site') {
|
||||
$LinkedSite = $true
|
||||
$LinkedSitePlaces.Add($InternalLink.Name)
|
||||
} else {
|
||||
$LinkedOU = $true
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$CN = ConvertFrom-DistinguishedName -ToDomainCN -DistinguishedName $InternalLink.distinguishedName
|
||||
$GPOCN = ConvertFrom-DistinguishedName -ToDomainCN -DistinguishedName $InternalLink.GPODomainDistinguishedName
|
||||
if ($CN -ne $GPOCN) {
|
||||
$LinkedCrossDomain = $true
|
||||
$LinkedCrossDomainPlaces.Add($InternalLink.DistinguishedName)
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($EnabledLinks[0].Count -gt 0) {
|
||||
$IsLinked = $true
|
||||
} else {
|
||||
$IsLinked = $false
|
||||
}
|
||||
$SummaryLink = [PSCustomObject] @{
|
||||
DisplayName = $Link[0].DisplayName
|
||||
DomainName = $Link[0].DomainName
|
||||
GUID = $Link[0].GUID
|
||||
Linked = $IsLinked
|
||||
LinksCount = $Link.Count
|
||||
LinksEnabledCount = $EnabledLinks[0].Count
|
||||
LinksDisabledCount = $EnabledLinks[1].Count
|
||||
LinkedCrossDomain = $LinkedCrossDomain
|
||||
LinkedRoot = $LinkedRoot
|
||||
LinkedSite = $LinkedSite
|
||||
LinkedOU = $LinkedOU
|
||||
LinkedSitePlaces = $LinkedSitePlaces
|
||||
LinkedRootPlaces = $LinkedRootPlaces
|
||||
LinkedCrossDomainPlaces = $LinkedCrossDomainPlaces
|
||||
Links = $Link.DistinguishedName
|
||||
LinksObjects = $Link
|
||||
}
|
||||
$SummaryHashtable[$Key] = $SummaryLink
|
||||
}
|
||||
if ($AsHashTable -and $Summary) {
|
||||
$SummaryHashtable
|
||||
} elseif ($AsHashTable) {
|
||||
$HashTable
|
||||
} elseif ($Summary) {
|
||||
$SummaryHashtable.Values
|
||||
}
|
||||
} else {
|
||||
foreach ($Object in $ADObject) {
|
||||
Get-PrivGPOZaurrLink -Object $Object -Limited:$Limited.IsPresent -GPOCache $GPOCache
|
||||
}
|
||||
Get-GPOZaurrLinkLoop @getGPOLoopSplat
|
||||
}
|
||||
}
|
||||
End {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
$CacheSummaryLinks = [ordered] @{} # cache
|
||||
|
||||
# Get all links
|
||||
$Links = Get-GPOZaurrLink -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
$Links = Get-GPOZaurrLink -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Linked Root, DomainControllers, Other
|
||||
foreach ($Link in $Links) {
|
||||
if (-not $CacheSummaryLinks["$($Link.DomainName)$($Link.Guid)"]) {
|
||||
$CacheSummaryLinks["$($Link.DomainName)$($Link.Guid)"] = [System.Collections.Generic.List[System.Object]]::new()
|
||||
|
||||
@@ -26,13 +26,13 @@
|
||||
} 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 ($ACL.Owner) {
|
||||
$IdentityOwner = Convert-Identity -Identity $ACL.Owner -Verbose:$false
|
||||
$IdentityOwnerAdvanced = Get-WinADObject -Identity $ACL.Owner -Cache -Verbose:$false
|
||||
} else {
|
||||
$IdentityOwner = [PSCustomObject] @{ Name = ''; SID = ''; Type = 'Unknown' }
|
||||
$IdentityOwnerAdvanced = [PSCustomObject] @{ ObjectClass = '' }
|
||||
}
|
||||
if (-not $OwnerOnly) {
|
||||
if (-not $SkipOwner) {
|
||||
if ($IdentityOwner.SID -eq "S-1-5-32-544") {
|
||||
|
||||
@@ -44,25 +44,6 @@
|
||||
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) {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
function Get-GPOZaurrPermissionAnalysis {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
|
||||
[Array] $Permissions
|
||||
)
|
||||
if (-not $ADAdministrativeGroups) {
|
||||
$ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
}
|
||||
|
||||
if (-not $Permissions) {
|
||||
$Permissions = Get-GPOZaurrPermission -IncludePermissionType GpoEditDeleteModifySecurity, GpoApply, GpoCustom, GpoRead -ReturnSecurityWhenNoData -IncludeGPOObject -ReturnSingleObject -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
foreach ($GPO in $Permissions) {
|
||||
$AdministrativeExists = [ordered] @{
|
||||
DisplayName = $GPO[0].DisplayName
|
||||
DomainName = $GPO[0].DomainName
|
||||
GUID = $GPO[0].GUID
|
||||
Status = $false
|
||||
Administrative = $false
|
||||
AuthenticatedUsers = $false
|
||||
System = $false
|
||||
Unknown = $false
|
||||
#StatusAuthenticatedUsers = $false
|
||||
#StatusSystem = $false
|
||||
DomainAdmins = $false
|
||||
EnterpriseAdmins = $false
|
||||
AuthenticatedUsersPermission = $null
|
||||
DomainAdminsPermission = $null
|
||||
EnterpriseAdminsPermission = $null
|
||||
SystemPermission = $null
|
||||
UnknownPermission = $null
|
||||
}
|
||||
foreach ($Permission in $GPO) {
|
||||
if ($Permission.GPOSecurityPermissionItem) {
|
||||
# We are looking for administrative but we need to make sure we got correct administrative
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['BySID'][$Permission.PrincipalSid]
|
||||
if ($AdministrativeGroup) {
|
||||
$PermissionType = [Microsoft.GroupPolicy.GPPermissionType]::GpoEditDeleteModifySecurity, [Microsoft.GroupPolicy.GPPermissionType]::GpoCustom
|
||||
if ($Permission.Permission -in $PermissionType) {
|
||||
if ($AdministrativeGroup.SID -like '*-519') {
|
||||
$AdministrativeExists['EnterpriseAdmins'] = $true
|
||||
$AdministrativeExists['EnterpriseAdminsPermission'] = $Permission.Permission
|
||||
} elseif ($AdministrativeGroup.SID -like '*-512') {
|
||||
$AdministrativeExists['DomainAdmins'] = $true
|
||||
$AdministrativeExists['DomainAdminsPermission'] = $Permission.Permission
|
||||
}
|
||||
} else {
|
||||
if ($AdministrativeGroup.SID -like '*-519') {
|
||||
$AdministrativeExists['EnterpriseAdminsPermission'] = $Permission.Permission
|
||||
} elseif ($AdministrativeGroup.SID -like '*-512') {
|
||||
$AdministrativeExists['DomainAdminsPermission'] = $Permission.Permission
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($AdministrativeExists['DomainAdmins'] -and $AdministrativeExists['EnterpriseAdmins']) {
|
||||
$AdministrativeExists['Administrative'] = $true
|
||||
}
|
||||
if ($Permission.PrincipalSid -eq 'S-1-5-11') {
|
||||
$PermissionType = [Microsoft.GroupPolicy.GPPermissionType]::GpoApply, [Microsoft.GroupPolicy.GPPermissionType]::GpoRead
|
||||
if ($Permission.Permission -in $PermissionType) {
|
||||
$AdministrativeExists['AuthenticatedUsers'] = $true
|
||||
}
|
||||
$AdministrativeExists['AuthenticatedUsersPermission'] = $Permission.Permission
|
||||
}
|
||||
if ($Permission.PrincipalSid -eq 'S-1-5-18') {
|
||||
$PermissionType = [Microsoft.GroupPolicy.GPPermissionType]::GpoEditDeleteModifySecurity
|
||||
if ($Permission.Permission -in $PermissionType) {
|
||||
$AdministrativeExists['System'] = $true
|
||||
}
|
||||
$AdministrativeExists['SystemPermission'] = $Permission.Permission
|
||||
}
|
||||
if ($Permission.PrincipalSidType -eq 'Unknown') {
|
||||
$AdministrativeExists['Unknown'] = $true
|
||||
$AdministrativeExists['UnknownPermission'] = $Permission.Permission
|
||||
}
|
||||
if ( $AdministrativeExists['System'] -eq $true -and $AdministrativeExists['AuthenticatedUsers'] -eq $true -and $AdministrativeExists['Administrative'] -eq $true -and $AdministrativeExists['Unknown'] -eq $false) {
|
||||
$AdministrativeExists['Status'] = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
[PSCustomObject] $AdministrativeExists
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
function Get-GPOZaurrSysvolDFSR {
|
||||
function Get-GPOZaurrSysvolDFSR {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
[switch] $ShowWarning,
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[switch] $Online
|
||||
)
|
||||
Reset-GPOZaurrStatus # This makes sure types are at it's proper status
|
||||
|
||||
@@ -143,7 +144,10 @@
|
||||
|
||||
# Generate pretty HTML
|
||||
$TimeLogHTML = Start-TimeLog
|
||||
Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report' -Color Yellow, DarkGray, Yellow
|
||||
if (-not $FilePath) {
|
||||
$FilePath = Get-FileName -Extension 'html' -Temporary
|
||||
}
|
||||
Write-Color -Text '[i]', '[HTML ] ', "Generating HTML report ($FilePath)" -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
|
||||
@@ -182,7 +186,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
} -Online -ShowHTML:(-not $HideHTML) -FilePath $FilePath
|
||||
} -Online:$Online.IsPresent -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) {
|
||||
|
||||
@@ -95,7 +95,10 @@
|
||||
$Output['CategoriesFull'] = [ordered] @{}
|
||||
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Loading GPO Report to Categories"
|
||||
$CountGPO = 0
|
||||
[Array] $GPOCategories = foreach ($GPO in $GPOs) {
|
||||
$CountGPO++
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Processing [$CountGPO/$($GPOs.Count)] $($GPO.DisplayName)"
|
||||
if ($GPOPath) {
|
||||
$GPOOutput = $GPO.GPOOutput
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
[parameter(ParameterSetName = 'Level', Mandatory)][int] $Limit,
|
||||
|
||||
# ParameterSet4
|
||||
[parameter(ParameterSetName = 'Linked', Mandatory)][validateset('Root', 'DomainControllers', 'Site', 'Other')][string] $Linked,
|
||||
[parameter(ParameterSetName = 'Linked', Mandatory)][validateset('Root', 'DomainControllers', 'Site', 'OrganizationalUnit')][string] $Linked,
|
||||
|
||||
# ParameterSet5
|
||||
[parameter(ParameterSetName = 'ADObject', ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject,
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
[string] $Path,
|
||||
[string] $Splitter = [System.Environment]::NewLine,
|
||||
[switch] $PreventShow,
|
||||
[switch] $Offline #,
|
||||
# [switch] $ForceGPResult
|
||||
[switch] $Online
|
||||
)
|
||||
# if user didn't choose anything, lets run as currently logged in user locally
|
||||
if (-not $UserName -and -not $ComputerName) {
|
||||
@@ -154,7 +153,7 @@
|
||||
if ($Type -contains 'Object') {
|
||||
$Output
|
||||
} elseif ($Type -contains 'HTML') {
|
||||
New-GPOZaurrReportHTML -Path $Path -Offline:$Offline -Open:(-not $PreventShow) -Support $Output
|
||||
New-GPOZaurrReportHTML -Path $Path -Online:$Online -Open:(-not $PreventShow) -Support $Output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[string[]] $GPOPath,
|
||||
[string] $BackupPath,
|
||||
[switch] $BackupDated
|
||||
[switch] $BackupDated,
|
||||
[int] $RequireDays
|
||||
)
|
||||
Begin {
|
||||
if ($BackupPath) {
|
||||
@@ -60,13 +61,19 @@
|
||||
$DeleteRequired = $true
|
||||
}
|
||||
}
|
||||
if ($RequireDays) {
|
||||
if ($RequireDays -ge $_.Days) {
|
||||
# GPO was modified recently and we don't want to touch it yet, maybe edit is in progress
|
||||
$DeleteRequired = $false
|
||||
}
|
||||
}
|
||||
if ($_.Exclude -eq $true) {
|
||||
Write-Verbose "Remove-GPOZaurr - Excluded GPO $($_.DisplayName) from $($_.DomainName). Skipping!"
|
||||
} elseif ($DeleteRequired) {
|
||||
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 = Backup-GPO -Guid $_.Guid -Domain $_.DomainName -Path $BackupFinalPath -ErrorAction Stop
|
||||
$BackupInfo
|
||||
$BackupOK = $true
|
||||
} catch {
|
||||
@@ -77,7 +84,7 @@
|
||||
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
|
||||
Remove-GPO -Domain $_.DomainName -Guid $_.Guid -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurr - Removing GPO $($_.DisplayName) from $($_.DomainName) failed: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,54 @@
|
||||
function Remove-GPOZaurrBroken {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Finds and removes broken Group Policies from SYSVOL or AD or both.
|
||||
|
||||
.DESCRIPTION
|
||||
Finds and removes broken Group Policies from SYSVOL or AD or both. Assesment is based on Get-GPOZaurrBroken and there are 3 supported types:
|
||||
- AD - meaning GPOs which have no SYSVOL content will be deleted from AD
|
||||
- SYSVOL - meaning GPOs which have no AD content will be deleted from SYSVOL
|
||||
- ObjectClass - meaning GPOs which have ObjectClass category of Container rather than groupPolicyContainer will be deleted from AD & SYSVOL
|
||||
|
||||
.PARAMETER Type
|
||||
Choose one or more types to delete. Options are AD, ObjectClass, SYSVOL
|
||||
|
||||
.PARAMETER BackupPath
|
||||
Path to optional backup of SYSVOL content before deletion
|
||||
|
||||
.PARAMETER BackupDated
|
||||
Forces backup to be created within folder that has date in it
|
||||
|
||||
.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
|
||||
|
||||
.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
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -Type AD, SYSVOL
|
||||
|
||||
.EXAMPLE
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -Type AD, SYSVOL -IncludeDomains 'ad.evotec.pl' -LimitProcessing 2
|
||||
|
||||
.EXAMPLE
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf -Type AD, SYSVOL
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[alias('Remove-GPOZaurrOrphaned')]
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[ValidateSet('SYSVOL', 'AD')][string[]] $Type = @('SYSVOL', 'AD'),
|
||||
[parameter(Mandatory, Position = 0)][ValidateSet('SYSVOL', 'AD', 'ObjectClass')][string[]] $Type,
|
||||
[string] $BackupPath,
|
||||
[switch] $BackupDated,
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
@@ -32,10 +78,15 @@
|
||||
$_
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'ObjectClass') {
|
||||
if ($_.Status -eq 'ObjectClass issue') {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} | 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 ($GPO.Status -in 'Not available in AD', 'ObjectClass issue') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing from SYSVOL [$($GPO.Status)] $($GPO.Path)"
|
||||
if ($BackupFinalPath) {
|
||||
Try {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Backing up $($GPO.Path)"
|
||||
@@ -47,30 +98,33 @@
|
||||
}
|
||||
}
|
||||
if ($BackupWorked -or $BackupFinalPath -eq '') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Deleting $($GPO.Path)"
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing $($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)"
|
||||
}
|
||||
if ($GPO.Status -in 'Not available on SYSVOL', 'ObjectClass issue') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing from AD [$($GPO.Status)] $($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)"
|
||||
#>
|
||||
if ($GPO -and $GPO.ObjectClass -in 'groupPolicyContainer', 'Container') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing DN: $($GPO.DistinguishedName) / ObjectClass: $($GPO.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))"
|
||||
Write-Warning "Remove-GPOZaurrBroken - DistinguishedName $($GPO.DistinguishedName) not found or ObjectClass is not groupPolicyContainer/Container ($($GPO.ObjectClass))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function Remove-GPOZaurrFolders {
|
||||
function Remove-GPOZaurrFolders {
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[string] $BackupPath,
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
function Repair-GPOZaurrBrokenLink {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Removes any link to GPO that no longer exists.
|
||||
|
||||
.DESCRIPTION
|
||||
Removes any link to GPO that no longer exists. It scans all site, organizational unit or domain root making sure every single link that may be linking to GPO that doesn't exists anymore is gone.
|
||||
|
||||
.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 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
|
||||
Repair-GPOZaurrBrokenLink -Verbose -LimitProcessing 1 -WhatIf
|
||||
|
||||
.EXAMPLE
|
||||
Repair-GPOZaurrBrokenLink -Verbose -IncludeDomains ad.evotec.pl -LimitProcessing 1 -WhatIf
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[int] $LimitProcessing
|
||||
)
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -Extended
|
||||
$Links = Get-GPOZaurrBrokenLink -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ForestInformation
|
||||
$Cache = @{}
|
||||
foreach ($Link in $Links) {
|
||||
if (-not $Cache[$Link.DistinguishedName]) {
|
||||
$Cache[$Link.DistinguishedName] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
$Cache[$Link.DistinguishedName].Add($Link)
|
||||
}
|
||||
$Count = 0
|
||||
foreach ($Key in $Cache.Keys) {
|
||||
$Count++
|
||||
Write-Verbose "Repair-GPOZaurrBrokenLink - processing [$Count/$($Cache.Keys.Count)] $Key "
|
||||
$Domain = ConvertFrom-DistinguishedName -ToDomainCN -DistinguishedName $Key
|
||||
$Server = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
|
||||
$Object = Get-ADObject -Identity $Key -Server $Server -Properties gpLink
|
||||
#$MatchLinks = [Regex]::Matches($Object.gpLink, '(?<=\[)(.*?)(?=\])').Value
|
||||
$MatchLinks = [Regex]::Matches($Object.gpLink, '(?<=LDAP:\/\/)(.*?)(?=])').Value
|
||||
$Found = $false
|
||||
$FixedLinks = foreach ($Match in $MatchLinks) {
|
||||
$SplittedValue = $Match -split ';'
|
||||
$GPODN = $SplittedValue[0]
|
||||
# Check if list of non-existing GPOs contains legitimate GPO
|
||||
if ($Cache[$Key].GPODistinguishedName -notcontains $GPODN) {
|
||||
"[LDAP://$Match]"
|
||||
Write-Verbose "Repair-GPOZaurrBrokenLink - legitimate link to GPO $GPODN ($Key)"
|
||||
} else {
|
||||
$Found = $true
|
||||
Write-Verbose "Repair-GPOZaurrBrokenLink - preparing for removal link to $GPODN ($Key)"
|
||||
}
|
||||
}
|
||||
if ($Found) {
|
||||
$NewGpLink = $($FixedLinks -join '')
|
||||
if ($NewGpLink) {
|
||||
try {
|
||||
Write-Verbose "Repair-GPOZaurrBrokenLink - setting gpLink to $Key - $NewGPLink"
|
||||
Set-ADObject -Identity $Key -Server $Server -Replace @{ gPLink = $NewGpLink } -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Repair-GPOZaurrBrokenLink - setting gpLink to $Key - $NewGpLink failed! Error $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Write-Verbose "Repair-GPOZaurrBrokenLink - clearing gpLink for $Key (no other links)"
|
||||
Set-ADObject -Identity $Key -Server $Server -Clear gPLink -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Repair-GPOZaurrBrokenLink - clearing gpLink for $Key failed! Error $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
if ($LimitProcessing -eq $Count) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,35 @@
|
||||
function Repair-GPOZaurrNetLogonOwner {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets new owner to each file in NetLogon share.
|
||||
|
||||
.DESCRIPTION
|
||||
Sets new owner to each file in NetLogon share.
|
||||
|
||||
.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
|
||||
Provide named owner. If not provided default S-1-5-32-544 is used.
|
||||
|
||||
.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
|
||||
Repair-GPOZaurrNetLogonOwner -WhatIf -Verbose -IncludeDomains ad.evotec.pl
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
function Repair-GPOZaurrPermission {
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory)][ValidateSet('AuthenticatedUsers', 'Unknown', 'System', 'Administrative', 'All')][string[]] $Type,
|
||||
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[int] $LimitProcessing = [int32]::MaxValue
|
||||
)
|
||||
Get-GPOZaurrPermissionAnalysis -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains | Where-Object {
|
||||
$RequiresProcessing = $false
|
||||
if ($_.Status -eq $false) {
|
||||
if ($Type -contains 'System' -or $Type -contains 'All') {
|
||||
if ($_.System -eq $false) {
|
||||
$RequiresProcessing = $true
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'Administrative' -or $Type -contains 'All') {
|
||||
if ($_.Administrative -eq $false) {
|
||||
$RequiresProcessing = $true
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'AuthenticatedUsers' -or $Type -contains 'All') {
|
||||
if ($_.AuthenticatedUsers -eq $false) {
|
||||
$RequiresProcessing = $true
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'Unknown' -or $Type -contains 'All') {
|
||||
if ($_.Unknown -eq $true) {
|
||||
$RequiresProcessing = $true
|
||||
}
|
||||
}
|
||||
if ($RequiresProcessing -eq $true) {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} | Select-Object -First $LimitProcessing | ForEach-Object {
|
||||
$GPO = $_
|
||||
if ($GPO.Status -eq $false) {
|
||||
if ($GPO.System -eq $false) {
|
||||
Add-GPOZaurrPermission -Type WellKnownAdministrative -PermissionType GpoEditDeleteModifySecurity -GPOGuid $GPO.GUID -IncludeDomains $GPO.DomainName
|
||||
}
|
||||
if ($GPO.Administrative -eq $false) {
|
||||
Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -GPOGuid $GPO.GUID -IncludeDomains $GPO.DomainName
|
||||
}
|
||||
if ($GPO.AuthenticatedUsers -eq $false) {
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -GPOGuid $GPO.GUID -IncludeDomains $GPO.DomainName
|
||||
}
|
||||
if ($GPO.Unknown -eq $true) {
|
||||
Remove-GPOZaurrPermission -Type Unknown -GPOGuid $GPO.GUID -IncludeDomains $GPO.DomainName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ $Configuration = @{
|
||||
RequiredModules = @(
|
||||
@{ ModuleName = 'PSSharedGoods'; ModuleVersion = 'Latest'; Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe' }
|
||||
@{ ModuleName = 'ADEssentials'; ModuleVersion = 'Latest'; Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f' }
|
||||
@{ ModuleName = 'PSWriteHTML'; ModuleVersion = "0.0.122"; Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c' }
|
||||
@{ ModuleName = 'PSWriteHTML'; ModuleVersion = "Latest"; Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c' }
|
||||
)
|
||||
ExternalModuleDependencies = @(
|
||||
#"ActiveDirectory"
|
||||
@@ -277,10 +277,10 @@ $Configuration = @{
|
||||
Verbose = $false
|
||||
}
|
||||
PublishModule = @{ # requires Enable to be on to process all of that
|
||||
Enabled = $false
|
||||
Enabled = $true
|
||||
Prerelease = ''
|
||||
RequireForce = $false
|
||||
GitHub = $false
|
||||
GitHub = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Installing](#installing)
|
||||
- [Updating](#updating)
|
||||
- [Resources](#resources)
|
||||
- [Changelog](#changelog)
|
||||
|
||||
## Installing
|
||||
@@ -55,8 +56,119 @@ That's it. Whenever there's a new version, you run the command, and you can enjo
|
||||
|
||||
**The essential thing** is if something works for you on production, keep using it till you test the new version on a test computer. I do changes that may not be big, but big enough that auto-update may break your code. For example, small rename to a parameter and your code stops working! Be responsible!
|
||||
|
||||
## Resources
|
||||
|
||||
To understand the usage I've created blog post you may find useful
|
||||
|
||||
- [The only command you will ever need to understand and fix your Group Policies (GPO)](https://evotec.xyz/the-only-command-you-will-ever-need-to-understand-and-fix-your-group-policies-gpo/)
|
||||
|
||||
## Changelog
|
||||
|
||||
- 0.0.121 - 2021.02.10
|
||||
- [x] Improvement to `Get-GPOZaurr` - added description [#13](https://github.com/EvotecIT/GPOZaurr/issues/13)
|
||||
- [x] Improvement to `Invoke-GPOZaurr -Type GPOList` - added description [#13](https://github.com/EvotecIT/GPOZaurr/issues/13)
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Report GPOAnalysis - added `FolderRedirection`
|
||||
- [x] Report GPOAnalysis - renamed `FolderRedirection` to `FolderRedirectionPolicy`
|
||||
- [x] Improved `Invoke-GPOZaurrContent` as mentioned above for `GPOAnalysis`
|
||||
- 0.0.120 - 2021.02.10
|
||||
- [x] Improvement to `Get-GPOZaurr` to warn if there is potential issue with EMPTY (which can happen on non-english system)
|
||||
- [x] In such case GPOZaurr will asses EMPTY or not using old method which doesn't detect all EMPTY cases but shouldn't provide false positives
|
||||
- 0.0.119
|
||||
- Broken release - weird
|
||||
- 0.0.118 - 2021.02.09
|
||||
- [x] Added information where the report is saved
|
||||
- [x] Small improvement to `Get-GPOZaurr` to exlicitly define variable types
|
||||
- 0.0.117 - 2021.02.09
|
||||
- [x] Small fix to `Get-GPOZaurr` to exclude GPOList.xml which is used in offline mode by `Save-GPOZaurrFiles`
|
||||
- 0.0.116 - 2021.02.08
|
||||
- [x] Improved `Remove-GPOZaurrBroken` to handle ObjectClass problem, and removed reduntant check
|
||||
- 0.0.115 - 2021.02.07
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] `GPOList` - clarified some texts, changed 7 days to 30 days as default
|
||||
- [x] `NetLogonPermissions` - fixed missing text
|
||||
- [x] Fixes `Get-GPOZaurrNetLogon` error on empty Owner - [#9](https://github.com/EvotecIT/GPOZaurr/issues/9)
|
||||
- 0.0.114 - 2021.01.27
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] HTML now uses offline mode by default (no CDN) - increase in size of HTML up to 3MB
|
||||
- [x] Using Online switch forces use of CDN - smaller files. For example `Invoke-GPOZaurr -Type GPOList -Online`
|
||||
- [ ] Improved `Invoke-GPOZaurrSupport`
|
||||
- [x] HTML now uses offline mode by default (no CDN) - increase in size of HTML up to 3MB
|
||||
- [x] Using Online switch forces use of CDN - smaller files. For example `Invoke-GPOZaurrSupport -Online`
|
||||
- [x] Removed parameter Offline, added parameter Online
|
||||
- [x] The cmdlet is not really production ready. It's work in progress
|
||||
- 0.0.113 - 2021.01.25
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Report GPOAnalysis - added WindowsTimeService
|
||||
- [x] Improved `Invoke-GPOZaurrContent`
|
||||
- [x] Added `WindowsTimeService` type
|
||||
- 0.0.112 - 2021.01.25
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- 0.0.111 - 2021.01.24
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- 0.0.110 - 2021.01.22
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- 0.0.109 - 2021.01.11
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- 0.0.108 - 2021.01.11
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Improved `GPOConsistency`
|
||||
- 0.0.107 - 2021.01.11
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- 0.0.106 - 2021.01.11
|
||||
- [x] Improved `Invoke-GPOZaurrContent`
|
||||
- 0.0.105 - 2021.01.05
|
||||
- [x] Improved `Get-GPOZaurr`
|
||||
- [x] Improved report `GPOBrokenLink`
|
||||
- 0.0.104 - 2021.01.04
|
||||
- [x] Improved `Get-GPOZaurrBrokenLink`
|
||||
- [x] Improved `Repair-GPOZaurrBrokenLink`
|
||||
- [x] Improved `Get-GPOZaurr`
|
||||
- [x] Improved report `GPOBrokenLink`
|
||||
- 0.0.103 - 2021.01.04
|
||||
- [x] Improved `Get-GPOZaurr`
|
||||
- [x] Added new report `GPOBrokenLink`
|
||||
- [x] Added `Get-GPOZaurrBrokenLink`
|
||||
- [x] Added `Repair-GPOZaurrBrokenLink`
|
||||
- 0.0.102 - 2021.01.02
|
||||
- [x] Improved `Get-GPOZaurrLink`
|
||||
- [x] Supports all links across forest
|
||||
- [x] Renamed Linked validate set from `Other` to `OrganizationalUnit`
|
||||
- [x] Improved `Get-GPOZaurrLinkSummary`
|
||||
- [x] Improved/BugFix `Get-GPOZaurr` to properly detect linked GPOs in sites/cross-domain
|
||||
- [x] Improved `Invoke-GPOZaurrPermission`
|
||||
- [x] Renamed Linked validate set from `Other` to `OrganizationalUnit`
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Added `GPOLinks` basic list
|
||||
- 0.0.101 - 23.12.2020
|
||||
- [x] Improved `Get-GPOZaurrBroken`
|
||||
- [x] It now detects `ObjectClass Issue`
|
||||
- [x] Heavily improved performance
|
||||
- [x] Removed some useless properties for this particular cmdlet
|
||||
- [x] All states: `Not available on SYSVOL`, `Not available in AD`, `Exists`, `Permissions Issue`, `ObjectClass Issue`
|
||||
- [x] Improved help
|
||||
- [x] Improved `Remove-GPOZaurrBroken`
|
||||
- [x] It now deals with `ObjectClass Issue`
|
||||
- [x] Heavily improved performance
|
||||
- [x] Removed some useless properties for this particular cmdlet
|
||||
- [x] Now requires manual type insert AD, SYSVOL or ObjectClass (or all of them). Before it was auto using AD/SYSVOL.
|
||||
- [x] Improved help
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOList`
|
||||
- [x] Renamed `GPOOrphans` to `GPOBroken`
|
||||
- [x] Improved `GPOBroken` with `ObjectClass issue`
|
||||
- 0.0.100 - 21.12.2020
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOPermissionsRead`
|
||||
- [x] Type `GPOPermissions`
|
||||
- 0.0.99 - 13.12.2020
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOList` - require GPO to be 7 days old for deletion to be proposed
|
||||
- [x] Type `GPOPermissions` - one stop for permissions
|
||||
- [x] Allows Steps to be chosen via their menu and out-of-order
|
||||
- [x] Improved `Remove-GPOZaurr` - added `RequireDays` parameter to prevent deletion of just modified GPOs
|
||||
- [x] Added `Get-GPOZaurrPermissionAnalysis`
|
||||
- [x] Added `Repair-GPOZaurrPermission`
|
||||
- 0.0.98 - 10.12.2020
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOList` - fixed unexpected ending of cmdlet when error occurs (for example deleted GPO while script is running) which could impact results
|
||||
|
||||
Reference in New Issue
Block a user