mirror of
https://github.com/EvotecIT/GPOZaurr.git
synced 2026-07-26 20:00:09 +00:00
Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ab9c0ee21 | |||
| 8646b07e41 | |||
| 1fe0b6b37e | |||
| 553de6dc2b | |||
| 24fd00e110 | |||
| 6e5ae262d4 | |||
| 74192f5d48 | |||
| d0dc7a7052 | |||
| d465f404c9 | |||
| 64bd95d85c | |||
| 569c7ed910 | |||
| 6fec1a264f | |||
| 7eefe96ee7 | |||
| a54f183ed3 | |||
| f5b73918bb | |||
| 25a59572dd | |||
| a6a847d720 | |||
| 20d005f600 | |||
| 7b2f2c69be | |||
| 1126071fc4 | |||
| 70fbc2d5f8 | |||
| fd844fd7a2 | |||
| a258689880 | |||
| 268b2f76f5 | |||
| ec90f38ad9 | |||
| aaf83b68de | |||
| 7ba8891b26 | |||
| 2df56e72b5 | |||
| 1bc966c409 | |||
| 887608b7b8 | |||
| f8b3be0f68 | |||
| 826460f739 | |||
| 9e34f4297c | |||
| 0da051a53b | |||
| f5fb9e2dee | |||
| 9784370c77 | |||
| 83363bb843 | |||
| 6c2e774b10 | |||
| b70cccf0db | |||
| 1788615711 | |||
| 0a863de36b | |||
| 31dd7e31a6 | |||
| 46a927b92d | |||
| 3e5c5fd6fb | |||
| 2a92c90b0c | |||
| 0f8fbb1220 | |||
| c37dd91403 | |||
| bc55cd09c5 | |||
| 2331e449b1 | |||
| d27760931e | |||
| 64bc1ff092 | |||
| 6f92043c3a | |||
| 7ce5027abd | |||
| 0c8e23f6af | |||
| 731ba420b9 | |||
| 8790be18bb | |||
| e495411714 | |||
| fab33b8d14 | |||
| e00c93b798 | |||
| ee5e03d666 | |||
| ad2067c714 | |||
| b5e4fd07dc | |||
| 981e71483a | |||
| 26e9486894 | |||
| 2f874b8dc8 | |||
| f1e211e560 | |||
| 4d82b51577 | |||
| ee9effa4f1 | |||
| af673cef1c | |||
| 38bf93fef3 | |||
| 3811bf68ad | |||
| e5d897d354 | |||
| 901be9fc66 | |||
| 57f9d02635 | |||
| d7e1ccd2f2 | |||
| 8d7ef2512d | |||
| ed417e7d9e | |||
| df07cc5a6b | |||
| 68f7a9292c | |||
| dc53cb36e0 | |||
| 7ff834fbfa | |||
| f5cf5dac4d | |||
| 5ffe89dec3 | |||
| 191b709ccf | |||
| b3e15d66ea | |||
| b3751d1dcc | |||
| 928d66eb9d | |||
| 72ecf97497 | |||
| aa8773ce73 | |||
| 6f11155914 | |||
| fd5413164a | |||
| 295f2e0a48 | |||
| 13e5672b3f | |||
| a0723227c0 | |||
| 72b2d2482b | |||
| 0c12761601 | |||
| 4ca5c8f1e7 | |||
| 53cdbdded7 | |||
| bed9da4367 | |||
| 1226c2758d | |||
| 8b2e655723 | |||
| 1061484247 | |||
| b4f014855b | |||
| 8b1bdfd39b | |||
| 1f43ec4d66 | |||
| f3f5b22090 | |||
| eb47bcd4b0 | |||
| 87a91bd632 | |||
| d0bd7b1cdc | |||
| dfff00d4cf | |||
| ba5a7e1549 | |||
| b126f397f7 | |||
| afd8e052de | |||
| 52edd47532 | |||
| da5fbf8341 | |||
| 6129aa6818 | |||
| 041639f430 | |||
| 12f79247dd | |||
| a957bc6450 | |||
| fb5b9c5024 | |||
| cb2dfe0a0f | |||
| e2950af1b6 | |||
| 1b70d39984 | |||
| 91d3776403 | |||
| b4748918f5 | |||
| a1d8393060 | |||
| 4a519004f5 | |||
| 8234c6cf50 | |||
| f50a495690 | |||
| 6c2e29122e | |||
| 250683e830 | |||
| 0f04582b42 | |||
| d9de90718d | |||
| e8651b71fd | |||
| ecdd6fb8ec | |||
| 945e993843 | |||
| fdf78ecadb | |||
| fc9b191578 | |||
| 04e5eaf63c | |||
| 494b1582ca | |||
| c33b820e36 |
@@ -1,9 +1,8 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Backup GPOs
|
||||
$BackupPath = "$Env:UserProfile\Desktop\GPO"
|
||||
$GPOSummary = Backup-GPOZaurr -BackupPath $BackupPath -Verbose -Type Unlinked -BackupDated #-LimitProcessing 1
|
||||
$GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All -IncludeDomains 'ad.evotec.pl' #-BackupDated #-LimitProcessing 1
|
||||
$GPOSummary | Format-Table -AutoSize
|
||||
|
||||
# Confirm GPOs are backed up properly
|
||||
## Confirm GPOs are backed up properly
|
||||
Get-GPOZaurrBackupInformation -BackupFolder $GPOSummary[0].BackupDirectory | Format-Table -a
|
||||
@@ -1,5 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Remove GPOS
|
||||
$BackupPath = "$Env:UserProfile\Desktop\GPO"
|
||||
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath $BackupPath -BackupDated -LimitProcessing 2 -Verbose -WhatIf
|
||||
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -BackupDated -LimitProcessing 2 -Verbose -WhatIf
|
||||
@@ -0,0 +1,7 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
#$GPOS = Get-GPOZaurr -GPOName 'TEST | Office Configuration'
|
||||
#$GPOS | Format-Table -AutoSize *
|
||||
|
||||
$GPOS = Get-GPOZaurr -GPOName 'New Group Policy Object3'
|
||||
$GPOS | Format-Table -AutoSize *
|
||||
@@ -0,0 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Issues = Get-GPOZaurrPermissionIssue
|
||||
$Issues | Format-Table
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$T = Get-GPOZaurrPermission -GPOName 'Default Domain Policy' -Type 'All'
|
||||
$T = Get-GPOZaurrPermission -GPOName 'Default Domain Policy' -Type 'All' -IncludeOwner
|
||||
$T | Format-Table *
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$T = Get-GPOZaurrPermission -Type Unknown -Verbose
|
||||
$T | Format-Table *
|
||||
$T | Out-HtmlView -ScrollX -Filtering -DisablePaging -ScrollY -Online
|
||||
@@ -1,17 +1,21 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# This Example shows how to deal with GPOs that have owner that doesn't exists anymore (deleted userr or diff domain) - EmptyOrUnknown
|
||||
# This Example shows how to deal with GPOs that have owner that doesn't exists anymore (deleted userr or diff domain) - Unknown
|
||||
# And also can fix at the same time NotAdministrative - this basically looks for users/groups that are not Domain Admins or Enterprise Admins
|
||||
# regardless if current user is still Domain Admin or not
|
||||
|
||||
$GPOs = Get-GPOZaurrOwner -IncludeSysvol #-GPOName
|
||||
$GPOs | Format-Table DisplayName, Owner, OwnerSID, OwnerType, SysvolOwner, SysvolSID, SysvolType
|
||||
$GPOs = Get-GPOZaurrOwner -IncludeSysvol
|
||||
$GPOs | Format-Table DisplayName, Owner, OwnerSID, OwnerType, SysvolOwner, SysvolSID, SysvolType
|
||||
|
||||
#Set-GPOZaurrOwner -Type Unknown -Verbose #-LimitProcessing 2 -WhatIf
|
||||
Set-GPOZaurrOwner -Type NotAdministrative -Verbose -LimitProcessing 2 -WhatIf
|
||||
#Set-GPOZaurrOwner -Type NotMatching -Verbose -LimitProcessing 2 -WhatIf
|
||||
Set-GPOZaurrOwner -Type Unknown -Verbose -WhatIf #-LimitProcessing 2
|
||||
Set-GPOZaurrOwner -Type All -Verbose -LimitProcessing 2 -WhatIf -IncludeDomains 'ad.evotec.pl'
|
||||
Set-GPOZaurrOwner -Type NotMatching -Verbose -LimitProcessing 2 -WhatIf
|
||||
|
||||
#Set-GPOZaurrOwner -GPOName 'COMPUTERS | Enable Sets' -Verbose -Principal 'przemyslaw.klys' -WhatIf #-SkipSysvol
|
||||
#Set-GPOZaurrOwner -GPOName 'New Group Policy Object' -Verbose #-WhatIf #-SkipSysvol
|
||||
#$GPOs = Get-GPOZaurrOwner -IncludeSysvol #-GPOName 'New Group Policy Object'
|
||||
#$GPOs | Format-Table DisplayName, Owner, OwnerSID
|
||||
# This will only set it to przemyslaw klys if the owner is not Domain Admins / Enterprise Admins
|
||||
# it's not working currently for any other object
|
||||
# You can enforce -Force to set it to any other principal
|
||||
Set-GPOZaurrOwner -GPOName 'COMPUTERS | Enable Sets' -Verbose -Principal 'przemyslaw.klys' -WhatIf:$false -Force #-SkipSysvol
|
||||
Set-GPOZaurrOwner -GPOName 'New Group Policy Object' -Verbose -WhatIf #-SkipSysvol
|
||||
|
||||
$GPOs = Get-GPOZaurrOwner -IncludeSysvol #-GPOName 'New Group Policy Object'
|
||||
$GPOs | Format-Table DisplayName, Owner, OwnerSID
|
||||
@@ -9,7 +9,7 @@ $GPOs | Format-Table DisplayName, Owner, OwnerSID, OwnerType
|
||||
Set-GPOZaurrOwner -GPOName 'ALL | Enable RDP' -WhatIf
|
||||
|
||||
# If principal is given it is set (of course if it exits). Otherwise warning is returned.
|
||||
Set-GPOZaurrOwner -GPOName 'ALL | Enable RDP' -Principal 'przemyslaw.klys' #-WhatIf
|
||||
Set-GPOZaurrOwner -GPOName 'ALL | Enable RDP' -Principal 'przemyslaw.klys' -WhatIf
|
||||
|
||||
$GPOs = Get-GPOZaurr #-GPOName 'New Group Policy Object'
|
||||
$GPOs | Format-Table DisplayName, Owner, OwnerSID, OwnerType
|
||||
@@ -5,7 +5,7 @@ $Report = Get-GPOZaurrOwner -IncludeSysvol
|
||||
$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOwners.xlsx -ExcelWorkSheetName 'GPO Owners Before' -AutoFilter -AutoFit
|
||||
|
||||
# Step 2 - Fix owners / use WhatIf and LimitProcessing / verify changes before/after
|
||||
Set-GPOZaurrOwner -Type NotAdministrative -Verbose -LimitProcessing 2 -WhatIf
|
||||
Set-GPOZaurrOwner -Type All -Verbose:$true -WhatIf # -LimitProcessing 2
|
||||
|
||||
# Step 3
|
||||
$Report = Get-GPOZaurrOwner -IncludeSysvol
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -LimitProcessing 3 -All -WhatIf -Verbose #-IncludeDomains 'ad.evotec.pl'
|
||||
|
||||
Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -LimitProcessing 100 -All -WhatIf -Verbose #-IncludeDomains 'ad.evotec.pl'
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Add-GPOZaurrPermission -GPOName 'New Group Policy Object' -Type AuthenticatedUsers -PermissionType GpoRead -Verbose -WhatIf
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrPermission -IncludePermissionType GpoRead -Type AuthenticatedUsers -ReturnSecurityWhenNoData | Format-Table
|
||||
@@ -1,17 +1,23 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Clear-Host
|
||||
|
||||
$T = Get-GPOZaurrPermission -GPOName 'ALL | Enable RDP' #-IncludeGPOObject #-ExcludePermissionType GpoApply,GpoRead -IncludeGPOObject
|
||||
$T | Format-Table -AutoSize *
|
||||
|
||||
Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'przemyslaw.klys' -Verbose
|
||||
#Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'CN=Przemysław Kłys,OU=Users,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz' -PrincipalType DistinguishedName -Verbose #-WhatIf
|
||||
#Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'CN=Administrator,OU=Special,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz' -PrincipalType DistinguishedName -Verbose #-WhatIf
|
||||
|
||||
Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'przemyslaw.klys' -PrincipalType Name -Verbose #-WhatIf
|
||||
Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'S-1-5-21-853615985-2870445339-3163598659-1105' -PrincipalType Sid -Verbose #-WhatIf
|
||||
|
||||
$T = Get-GPOZaurrPermission -GPOName 'ALL | Enable RDP' #-ExcludePermissionType GpoApply,GpoRead -IncludeGPOObject
|
||||
$T | Format-Table -AutoSize *
|
||||
|
||||
Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEditDeleteModifySecurity -Principal 'Domain Admins' -Verbose
|
||||
Remove-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'przemyslaw.klys' -PrincipalType Name -Verbose
|
||||
Add-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEditDeleteModifySecurity -Principal 'Domain Admins' -PrincipalType Name -Verbose #-WhatIf
|
||||
Remove-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'przemyslaw.klys' -PrincipalType Name -Verbose #-WhatIf
|
||||
Remove-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'EVOTEC\przemyslaw.klys' -PrincipalType NetbiosName -Verbose #-WhatIf
|
||||
Remove-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Principal 'CN=Przemysław Kłys,OU=Users,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz' -PrincipalType DistinguishedName -Verbose #-WhatIf
|
||||
Remove-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEdit -Type NotAdministrative -Verbose
|
||||
|
||||
Remove-GPOZaurrPermission -GPOName 'ALL | Enable RDP' -PermissionType GpoEditDeleteModifySecurity -PrincipalType DistinguishedName -Principal 'CN=Domain Admins,CN=Users,DC=ad,DC=evotec,DC=pl' -Verbose
|
||||
$T = Get-GPOZaurrPermission -GPOName 'ALL | Enable RDP' #-ExcludePermissionType GpoApply,GpoRead -IncludeGPOObject
|
||||
$T | Format-Table -AutoSize *
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
Get-GPOZaurrPermissionConsistency -GPOName 'Default Domain Controllers Policy' -IncludeDomains 'ad.evotec.xyz'
|
||||
|
||||
Repair-GPOZaurrPermissionConsistency -GPOName 'Default Domain Controllers Policy' -IncludeDomains 'ad.evotec.xyz' -Verbose #-WhatIf -Verbose
|
||||
Repair-GPOZaurrPermissionConsistency -GPOName 'Default Domain Controllers Policy' -IncludeDomains 'ad.evotec.xyz' -Verbose -WhatIf
|
||||
@@ -1,5 +1,5 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrPermissionConsistency -Type All | Format-Table
|
||||
Get-GPOZaurrPermissionConsistency -Type All -VerifyInheritance | Format-Table
|
||||
|
||||
Repair-GPOZaurrPermissionConsistency -LimitProcessing 2 -WhatIf -Verbose
|
||||
@@ -1,3 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrSysvol -Verbose | Format-Table
|
||||
Get-GPOZaurrSysvol -Verbose | Format-Table
|
||||
#Get-GPOZaurrSysvol | Out-HtmlView -ScrollX
|
||||
@@ -1,7 +1,10 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Find broken GPOs
|
||||
Get-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.pl' | Format-Table
|
||||
|
||||
# this allows you to process X amount of orphaned folders/files (good for testing)
|
||||
Remove-GPOZaurrOrphanedSysvolFolders -Verbose -WhatIf -LimitProcessing 2 -IncludeDomains 'ad.evotec.xyz'
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -IncludeDomains 'ad.evotec.pl' #-LimitProcessing 2
|
||||
|
||||
# this runs for whole SYSVOL and checks things against GPOS
|
||||
Remove-GPOZaurrOrphanedSysvolFolders -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 #-WhatIf
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf
|
||||
@@ -1,4 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
#Save-GPOZaurrFiles -GPOPath $ENV:USERPROFILE\Desktop\GPOExport -DeleteExisting
|
||||
Save-GPOZaurrFiles -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExportEvotec' #-DeleteExisting
|
||||
Save-GPOZaurrFiles -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExportEvotec' -DeleteExisting -Verbose
|
||||
@@ -1,12 +0,0 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Asses GPO based on exported data
|
||||
$Output = Invoke-GPOZaurr -GPOPath $Env:USERPROFILE\Desktop\GPOExport -Verbose
|
||||
#$Output = Invoke-GPOZaurr -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExportTest' -Verbose
|
||||
$Output | Format-Table *
|
||||
|
||||
# Export to Excel
|
||||
foreach ($Key in $Output.Reports.Keys) {
|
||||
$Output.Reports[$Key] | ConvertTo-Excel -FilePath $Env:USERPROFILE\Desktop\EFGPOAnalysis.xlsx -ExcelWorkSheetName $Key -AutoFilter -AutoFit -FreezeTopRowFirstColumn
|
||||
}
|
||||
Start-Process "$Env:USERPROFILE\Desktop\EFGPOAnalysis.xlsx"
|
||||
@@ -1,20 +1,22 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Output = Invoke-GPOZaurr -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExportEvotec1'
|
||||
#$Output = Invoke-GPOZaurrContent -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPODefender'
|
||||
$Output = Invoke-GPOZaurrContent -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExport' -Type WindowsHelloForBusiness
|
||||
$Output | Format-Table
|
||||
$Output.Reports.RegistrySettings | Format-Table *
|
||||
$Output.Reports.InternetExplorerZones | Format-Table *
|
||||
$Output.WindowsDefenderExploitGuard | Format-Table
|
||||
$Output.Reports.RegistrySetting | Format-Table
|
||||
|
||||
# Report to Excel of translated reports
|
||||
foreach ($Key in $Output.Reports.Keys) {
|
||||
$Output.Reports[$Key] | ConvertTo-Excel -FilePath $Env:USERPROFILE\Desktop\GPOAnalysis.xlsx -ExcelWorkSheetName $Key -AutoFilter -AutoFit -FreezeTopRowFirstColumn
|
||||
foreach ($Key in $Output.Keys) {
|
||||
$Output[$Key] | ConvertTo-Excel -FilePath $Env:USERPROFILE\Desktop\GPOAnalysis.xlsx -ExcelWorkSheetName $Key -AutoFilter -AutoFit -FreezeTopRowFirstColumn
|
||||
}
|
||||
|
||||
# Report to HTML of translated reports
|
||||
New-HTML {
|
||||
foreach ($Key in $Output.Reports.Keys) {
|
||||
New-HTMLTableOption -DataStore JavaScript
|
||||
foreach ($Key in $Output.Keys) {
|
||||
New-HTMLTab -Name $Key {
|
||||
New-HTMLTable -DataTable $Output.Reports[$Key] -Filtering
|
||||
New-HTMLTable -DataTable $Output[$Key] -Filtering -Title $Key
|
||||
}
|
||||
}
|
||||
} -FilePath $Env:USERPROFILE\Desktop\GPOAnalysis.html -ShowHTML -Online
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# This gets the same thing as earlier examples
|
||||
# with a difference where one entry per gpo and all settings for that GPO is stored under settings property.
|
||||
$Output = Invoke-GPOZaurr -SingleObject
|
||||
$Output = Invoke-GPOZaurrContent -SingleObject -Verbose
|
||||
$Output | Format-Table
|
||||
$Output.Reports.RegistrySettings | Format-Table *
|
||||
$Output.Reports.RegistrySettings[0].Settings | Format-Table *
|
||||
@@ -1,9 +1,11 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Use Save-GPOZaurrFiles -GPOPath $ENV:USERPROFILE\Desktop\GPOExportAudit
|
||||
$Output = Invoke-GPOZaurr -GPOPath $ENV:USERPROFILE\Desktop\GPOExportAudit -Verbose #-SkipCleanup #-Type PoliciesPrinters, Policies
|
||||
$Output | Format-Table *
|
||||
|
||||
# This will allow you to process offline data more than once
|
||||
# It's useful for when you want to request different types
|
||||
$Output = Invoke-GPOZaurrContent -GPOPath $ENV:USERPROFILE\Desktop\GPOExportAudit -Extended -Verbose
|
||||
$Output | Format-Table *
|
||||
$Output.Reports | Format-Table
|
||||
|
||||
# Export to Excel
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Output = Invoke-GPOZaurrContent -Verbose -OutputType HTML, Object -Open
|
||||
$Output | Format-Table
|
||||
@@ -0,0 +1,23 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Asses GPO based on exported data
|
||||
$Output = Invoke-GPOZaurrContent -GPOPath $Env:USERPROFILE\Desktop\GPOExport_2020.10.12 -Verbose -Type RegistrySetting
|
||||
$Output | Format-Table *
|
||||
|
||||
|
||||
# Export to Excel
|
||||
foreach ($Key in $Output.Keys) {
|
||||
$Output[$Key] | ConvertTo-Excel -FilePath $Env:USERPROFILE\Desktop\EFGPOAnalysis.xlsx -ExcelWorkSheetName $Key -AutoFilter -AutoFit -FreezeTopRowFirstColumn
|
||||
}
|
||||
# Show the Excel
|
||||
Start-Process "$Env:USERPROFILE\Desktop\EFGPOAnalysis.xlsx"
|
||||
|
||||
# Show HTML
|
||||
New-HTML {
|
||||
New-HTMLTableOption -DataStore JavaScript
|
||||
foreach ($Key in $Output.Keys) {
|
||||
New-HTMLTab -Name $Key {
|
||||
New-HTMLTable -DataTable $Output[$Key] -Filtering -Title $Key
|
||||
}
|
||||
}
|
||||
} -FilePath $Env:USERPROFILE\Desktop\EFGPOAnalysis.html -ShowHTML -Online
|
||||
@@ -0,0 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Output = Invoke-GPOZaurrContent -GPOPath $ENV:USERPROFILE\Desktop\GPOTestingUserAccess -Verbose
|
||||
$Output | Format-Table *
|
||||
@@ -1,5 +1,81 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Invoke-GPOZaurrSupport -ComputerName 'AD1' -UserName 'przemyslaw.klys' -Type HTML
|
||||
#$Support
|
||||
# $Support.ComputerResults.GPO | select name, @{LABEL=”LinkOrder“;EXPRESSION={$_.link.linkorder}} | sort linkorder
|
||||
Invoke-GPOZaurrSupport -ComputerName 'ad1.ad.evotec.xyz' -UserName 'EVOTEC\przemyslaw.klys' -Type NativeHTML -ForceGPResult -Verbose
|
||||
|
||||
return
|
||||
#$Support1 = Invoke-GPOZaurrSupport -ComputerName 'AD1' -UserName 'przemyslaw.klys' -Type Object
|
||||
#$Support = Invoke-GPOZaurrSupport -ComputerName 'AD1' -UserName 'EVOTEC\Administrator' -Type Object
|
||||
#$Support.ComputerResults.ExtensionData
|
||||
|
||||
|
||||
foreach ($GpoType in @('UserResults', 'ComputerResults')) {
|
||||
if ($Support.$GpoType.ExtensionData) {
|
||||
foreach ($Extension in $Support.$GpoType.ExtensionData) {
|
||||
$GPOSettingTypeSplit = ($Extension.type -split ':')
|
||||
#$Extension
|
||||
#$Extension | Get-Member -MemberType Properties -ErrorAction Stop
|
||||
$KeysToLoop = $Extension | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin @($GPOSettingTypeSplit[0], 'xmlns', 'blocked', 'type') }
|
||||
foreach ($GpoSettings in $KeysToLoop.Name) {
|
||||
$Extension.$GpoSettings | Format-Table
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# $env:LOGONSERVER
|
||||
# if($env:LOGONSERVER -match "MicrosoftAccount") {"Logged on with Windows account"}
|
||||
|
||||
# If it reports \\MicrosoftAccount, they are using their Windows account to sign in instead of domain credentials.
|
||||
#You can check this by using the IF statement as shown here:
|
||||
|
||||
|
||||
#[System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().Name
|
||||
|
||||
#[System.DirectoryServices.ActiveDirectory.DomainController]::
|
||||
|
||||
# https://concurrency.com/blog/may-2018/domain-controller-selection
|
||||
#echo %logonserver% - This shows the DC that was used to authenticate and log in the current user
|
||||
#nltest /dsgetsite - This shows the AD site that the current server has detected that it's in
|
||||
#nltest /dclist: (include the colon at the end) - This shows the list of DCs in the current domain, including which site each is in.
|
||||
# nslookup -type=srv _ldap._tcp.mydomain.local. - This will query the primary DNS server for all domain controller SRV records. This should return all of the DCs in the domain. In this example, MGLABDC4 and MGLABDC5 are returned.
|
||||
# nslookup -type=srv _ldap._tcp.mysitename._sites.dc._msdcs.mydomain.local. - This will query the primary DNS server for domain controllers that are registered in "mysitename". In this example, only MGLABDC4 is in the site that was queried, which matches the information we found with nltest /dclist: previously.
|
||||
|
||||
|
||||
# Windows 10
|
||||
|
||||
#Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
|
||||
#Add-WindowsCapability -Online -Name 'Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0'
|
||||
#Remove-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
|
||||
|
||||
Write-Output %logonserver%
|
||||
nltest /dsgetsite
|
||||
nltest /dclist:
|
||||
|
||||
$env:LOGONSERVER
|
||||
[System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().Name
|
||||
nslookup -type=srv _ldap._tcp.ad.colmore.com.
|
||||
nslookup -type=srv _ldap._tcp.birmingham._sites.dc._msdcs.ad.colmore.com.
|
||||
|
||||
|
||||
function Get-PreData {
|
||||
param(
|
||||
|
||||
)
|
||||
[PSCustomObject] @{
|
||||
Site = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().Name
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ComputerDataInformation {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[string] $ComputerName = $Env:COMPUTERNAME
|
||||
)
|
||||
[PSCustomObject] @{
|
||||
Bios = Get-ComputerBios -ComputerName $ComputerName
|
||||
Network = Get-ComputerNetwork -ComputerName $ComputerName
|
||||
NetworkCard = Get-ComputerNetworkCard -ComputerName $ComputerName
|
||||
}
|
||||
}
|
||||
Get-ComputerDataInformation -ComputerName 'AD1.AD.EVOTEC.XYZ'
|
||||
#Get-ComputerNetwork -ComputerName 'AD1.AD.EVOTEC.XYZ' #| Format-Table
|
||||
@@ -0,0 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
ConvertFrom-CSExtension -CSE '{FC715823-C5FB-11D1-9EEF-00A0C90347FF}'
|
||||
ConvertFrom-CSExtension -CSE '{A2E30F80-D7DE-11d2-BBDE-00C04F86AE3B}'
|
||||
@@ -0,0 +1,10 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Default Permissions:
|
||||
# 'GpoApply', 'GpoEdit', 'GPOCustom', 'GpoEditDeleteModifySecurity', 'GPORead'
|
||||
# If you want to see also owners
|
||||
# 'GpoOwner'
|
||||
# If you want to include Root Level Permissions
|
||||
# 'GpoRootCreate', 'GpoRootOwner'
|
||||
$SummaryPermission = Get-GPOZaurrPermissionSummary -IncludePermissionType 'GpoCustom', 'GpoEdit', 'GpoEditDeleteModifySecurity', 'GpoOwner', 'GpoRootCreate', 'GpoRootOwner'
|
||||
$SummaryPermission | Sort-Object -Property Permission | Format-Table
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrPermissionRoot -Verbose | Format-Table *
|
||||
@@ -0,0 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$FilesAll = Get-GPOZaurrNetlogon
|
||||
$FilesAll | Format-Table -a *
|
||||
@@ -0,0 +1,7 @@
|
||||
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
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type NetLogonPermissions, GPOOrphans, GPOList, GPOConsistency, GPOOwners, GPODuplicates
|
||||
@@ -0,0 +1,15 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
$Types = @(
|
||||
@{ Name = 'GPOOwners'; Path = "$PSScriptRoot\Reports\GPOOwners.html" }
|
||||
@{ 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 = 'GPOPassword'; Path = "$PSScriptRoot\Reports\GPOPassword.html" }
|
||||
@{ Name = 'NetLogonPermissions'; Path = "$PSScriptRoot\Reports\NetLogonPermissions.html" }
|
||||
)
|
||||
|
||||
foreach ($Type in $Types) {
|
||||
Invoke-GPOZaurr -FilePath $Type.Path -Type $Type.Name -HideHTML
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Repair-GPOZaurrNetLogonOwner -WhatIf:$true -Verbose -IncludeDomains ad.evotec.pl
|
||||
@@ -0,0 +1,3 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Get-GPOZaurrPassword -GPOPath 'C:\Users\przemyslaw.klys\Desktop\GPOExport_2020.10.12'
|
||||
@@ -0,0 +1,2 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
$ModuleName = (Get-ChildItem $PSScriptRoot\*.psd1).BaseName
|
||||
$PrimaryModule = Get-ChildItem -Path $PSScriptRoot -Filter '*.psd1' -Recurse -ErrorAction SilentlyContinue -Depth 1
|
||||
if (-not $PrimaryModule) {
|
||||
throw "Path $PSScriptRoot doesn't contain PSD1 files. Failing tests."
|
||||
}
|
||||
if ($PrimaryModule.Count -ne 1) {
|
||||
throw 'More than one PSD1 files detected. Failing tests.'
|
||||
}
|
||||
$PSDInformation = Import-PowerShellDataFile -Path $PrimaryModule.FullName
|
||||
$RequiredModules = @(
|
||||
'Pester'
|
||||
'PSWriteColor'
|
||||
'PSParseHTML'
|
||||
if ($PSDInformation.RequiredModules) {
|
||||
$PSDInformation.RequiredModules
|
||||
}
|
||||
)
|
||||
foreach ($Module in $RequiredModules) {
|
||||
if ($Module -is [System.Collections.IDictionary]) {
|
||||
$Exists = Get-Module -ListAvailable -Name $Module.ModuleName
|
||||
if (-not $Exists) {
|
||||
Write-Warning "$ModuleName - Downloading $($Module.ModuleName) from PSGallery"
|
||||
Install-Module -Name $Module.ModuleName -Force -SkipPublisherCheck
|
||||
}
|
||||
} else {
|
||||
$Exists = Get-Module -ListAvailable $Module -ErrorAction SilentlyContinue
|
||||
if (-not $Exists) {
|
||||
Install-Module -Name $Module -Force -SkipPublisherCheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Color 'ModuleName: ', $ModuleName, ' Version: ', $PSDInformation.ModuleVersion -Color Yellow, Green, Yellow, Green -LinesBefore 2
|
||||
Write-Color 'PowerShell Version: ', $PSVersionTable.PSVersion -Color Yellow, Green
|
||||
Write-Color 'PowerShell Edition: ', $PSVersionTable.PSEdition -Color Yellow, Green
|
||||
Write-Color 'Required modules: ' -Color Yellow
|
||||
foreach ($Module in $PSDInformation.RequiredModules) {
|
||||
if ($Module -is [System.Collections.IDictionary]) {
|
||||
Write-Color ' [>] ', $Module.ModuleName, ' Version: ', $Module.ModuleVersion -Color Yellow, Green, Yellow, Green
|
||||
} else {
|
||||
Write-Color ' [>] ', $Module -Color Yellow, Green
|
||||
}
|
||||
}
|
||||
Write-Color
|
||||
|
||||
Import-Module $PSScriptRoot\*.psd1 -Force
|
||||
Import-Module Pester -Force
|
||||
$Configuration = [PesterConfiguration]::Default
|
||||
$Configuration.Run.Path = "$PSScriptRoot\Tests"
|
||||
$Configuration.Run.Exit = $true
|
||||
$Configuration.Should.ErrorAction = 'Continue'
|
||||
$Configuration.CodeCoverage.Enabled = $false
|
||||
$Configuration.Output.Verbosity = 'Detailed'
|
||||
$Result = Invoke-Pester -Configuration $Configuration
|
||||
#$result = Invoke-Pester -Script $PSScriptRoot\Tests -Verbose -Output Detailed #-EnableExit
|
||||
|
||||
if ($Result.FailedCount -gt 0) {
|
||||
throw "$($Result.FailedCount) tests failed."
|
||||
}
|
||||
+20
-15
@@ -1,29 +1,34 @@
|
||||
@{
|
||||
AliasesToExport = 'Get-GPOZaurrFilesPolicyDefinitions', 'Find-GPO'
|
||||
AliasesToExport = @('Get-GPOZaurrSysvol', 'Get-GPOZaurrFilesPolicyDefinitions', 'Show-GPOZaurr', 'Show-GPO', 'Find-GPO', 'Remove-GPOZaurrOrphaned')
|
||||
Author = 'Przemyslaw Klys'
|
||||
CmdletsToExport = @()
|
||||
CompanyName = 'Evotec'
|
||||
CompatiblePSEditions = 'Desktop'
|
||||
CompatiblePSEditions = @('Desktop')
|
||||
Copyright = '(c) 2011 - 2020 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-GPOZaurrDictionary', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrSysvol', 'Get-GPOZaurrSysvolDFSR', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrPermission', 'Invoke-GPOZaurrSupport', 'New-GPOZaurrWMI', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrOrphanedSysvolFolders', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner'
|
||||
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')
|
||||
GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde'
|
||||
ModuleVersion = '0.0.54'
|
||||
ModuleVersion = '0.0.82'
|
||||
PowerShellVersion = '5.1'
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
Tags = 'Windows', 'ActiveDirectory', 'GPO', 'GroupPolicy'
|
||||
Tags = @('Windows', 'ActiveDirectory', 'GPO', 'GroupPolicy')
|
||||
ProjectUri = 'https://github.com/EvotecIT/GPOZaurr'
|
||||
ExternalModuleDependencies = 'ActiveDirectory', 'GroupPolicy', 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility'
|
||||
ExternalModuleDependencies = @('CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security')
|
||||
}
|
||||
}
|
||||
RequiredModules = @{
|
||||
ModuleVersion = '0.0.166'
|
||||
ModuleName = 'PSSharedGoods'
|
||||
Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.66'
|
||||
ModuleName = 'ADEssentials'
|
||||
Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f'
|
||||
}, 'ActiveDirectory', 'GroupPolicy', 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility'
|
||||
RequiredModules = @(@{
|
||||
ModuleVersion = '0.0.188'
|
||||
ModuleName = 'PSSharedGoods'
|
||||
Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.102'
|
||||
ModuleName = 'ADEssentials'
|
||||
Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.120'
|
||||
ModuleName = 'PSWriteHTML'
|
||||
Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c'
|
||||
}, 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security')
|
||||
RootModule = 'GPOZaurr.psm1'
|
||||
}
|
||||
@@ -1,39 +1,95 @@
|
||||
function ConvertTo-XMLAccountPolicy {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO
|
||||
[PSCustomObject] $GPO,
|
||||
[switch] $SingleObject
|
||||
)
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
ClearTextPassword = 'Not Set'
|
||||
LockoutBadCount = 'Not Set'
|
||||
LockoutDuration = 'Not Set'
|
||||
MaximumPasswordAge = 'Not Set'
|
||||
MinimumPasswordAge = 'Not Set'
|
||||
MinimumPasswordLength = 'Not Set'
|
||||
PasswordComplexity = 'Not Set'
|
||||
PasswordHistorySize = 'Not Set'
|
||||
ResetLockoutCount = 'Not Set'
|
||||
MaxClockSkew = 'Not Set'
|
||||
MaxRenewAge = 'Not Set'
|
||||
MaxServiceAge = 'Not Set'
|
||||
MaxTicketAge = 'Not Set'
|
||||
TicketValidateClient = 'Not Set'
|
||||
}
|
||||
foreach ($GPOEntry in $GPO.DataSet) {
|
||||
if ($GPOEntry.SettingBoolean) {
|
||||
$CreateGPO[$($GPOEntry.Name)] = if ($GPOEntry.SettingBoolean -eq 'true') { 'Enabled' } elseif ($GPOEntry.SettingBoolean -eq 'false') { 'Disabled' } else { 'Not set' };
|
||||
} elseif ($GPOEntry.SettingNumber) {
|
||||
$CreateGPO[$($GPOEntry.Name)] = [int] $GPOEntry.SettingNumber
|
||||
if ($SingleObject) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
Count = 0
|
||||
Settings = $null
|
||||
}
|
||||
[Array] $CreateGPO['Settings'] = @(
|
||||
$Settings = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
ClearTextPassword = 'Not Set'
|
||||
LockoutBadCount = 'Not Set'
|
||||
LockoutDuration = 'Not Set'
|
||||
MaximumPasswordAge = 'Not Set'
|
||||
MinimumPasswordAge = 'Not Set'
|
||||
MinimumPasswordLength = 'Not Set'
|
||||
PasswordComplexity = 'Not Set'
|
||||
PasswordHistorySize = 'Not Set'
|
||||
ResetLockoutCount = 'Not Set'
|
||||
MaxClockSkew = 'Not Set'
|
||||
MaxRenewAge = 'Not Set'
|
||||
MaxServiceAge = 'Not Set'
|
||||
MaxTicketAge = 'Not Set'
|
||||
TicketValidateClient = 'Not Set'
|
||||
}
|
||||
foreach ($GPOEntry in $GPO.DataSet) {
|
||||
if ($GPOEntry.SettingBoolean) {
|
||||
$Settings[$($GPOEntry.Name)] = if ($GPOEntry.SettingBoolean -eq 'true') { 'Enabled' } elseif ($GPOEntry.SettingBoolean -eq 'false') { 'Disabled' } else { 'Not set' };
|
||||
} elseif ($GPOEntry.SettingNumber) {
|
||||
$Settings[$($GPOEntry.Name)] = [int] $GPOEntry.SettingNumber
|
||||
}
|
||||
}
|
||||
[PSCustomObject] $Settings
|
||||
)
|
||||
|
||||
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
ClearTextPassword = 'Not Set'
|
||||
LockoutBadCount = 'Not Set'
|
||||
LockoutDuration = 'Not Set'
|
||||
MaximumPasswordAge = 'Not Set'
|
||||
MinimumPasswordAge = 'Not Set'
|
||||
MinimumPasswordLength = 'Not Set'
|
||||
PasswordComplexity = 'Not Set'
|
||||
PasswordHistorySize = 'Not Set'
|
||||
ResetLockoutCount = 'Not Set'
|
||||
MaxClockSkew = 'Not Set'
|
||||
MaxRenewAge = 'Not Set'
|
||||
MaxServiceAge = 'Not Set'
|
||||
MaxTicketAge = 'Not Set'
|
||||
TicketValidateClient = 'Not Set'
|
||||
}
|
||||
foreach ($GPOEntry in $GPO.DataSet) {
|
||||
if ($GPOEntry.SettingBoolean) {
|
||||
$CreateGPO[$($GPOEntry.Name)] = if ($GPOEntry.SettingBoolean -eq 'true') { 'Enabled' } elseif ($GPOEntry.SettingBoolean -eq 'false') { 'Disabled' } else { 'Not set' };
|
||||
} elseif ($GPOEntry.SettingNumber) {
|
||||
$CreateGPO[$($GPOEntry.Name)] = [int] $GPOEntry.SettingNumber
|
||||
}
|
||||
}
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
+217
-102
@@ -2,7 +2,7 @@
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
[switch] $FullObject
|
||||
[switch] $SingleObject
|
||||
)
|
||||
$SettingType = @{
|
||||
'0' = 'No Auditing'
|
||||
@@ -10,107 +10,222 @@
|
||||
'2' = 'Failure'
|
||||
'3' = 'Success, Failure'
|
||||
}
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
AuditAccountLogon = 'Not configured'
|
||||
AuditAccountManage = 'Not configured'
|
||||
AuditDSAccess = 'Not configured'
|
||||
AuditLogonEvents = 'Not configured'
|
||||
AuditObjectAccess = 'Not configured'
|
||||
AuditPolicyChange = 'Not configured'
|
||||
AuditPrivilegeUse = 'Not configured'
|
||||
AuditProcessTracking = 'Not configured'
|
||||
AuditSystemEvents = 'Not configured'
|
||||
# Advanced Policies
|
||||
AuditAccountLockout = 'Not configured'
|
||||
AuditApplicationGenerated = 'Not configured'
|
||||
AuditApplicationGroupManagement = 'Not configured'
|
||||
AuditAuditPolicyChange = 'Not configured'
|
||||
AuditAuthenticationPolicyChange = 'Not configured'
|
||||
AuditAuthorizationPolicyChange = 'Not configured'
|
||||
AuditCentralAccessPolicyStaging = 'Not configured'
|
||||
AuditCertificationServices = 'Not configured'
|
||||
AuditComputerAccountManagement = 'Not configured'
|
||||
AuditCredentialValidation = 'Not configured'
|
||||
AuditDetailedDirectoryServiceReplication = 'Not configured'
|
||||
AuditDetailedFileShare = 'Not configured'
|
||||
AuditDirectoryServiceAccess = 'Not configured'
|
||||
AuditDirectoryServiceChanges = 'Not configured'
|
||||
AuditDirectoryServiceReplication = 'Not configured'
|
||||
AuditDistributionGroupManagement = 'Not configured'
|
||||
AuditDPAPIActivity = 'Not configured'
|
||||
AuditFileShare = 'Not configured'
|
||||
AuditFileSystem = 'Not configured'
|
||||
AuditFilteringPlatformConnection = 'Not configured'
|
||||
AuditFilteringPlatformPacketDrop = 'Not configured'
|
||||
AuditFilteringPlatformPolicyChange = 'Not configured'
|
||||
AuditGroupMembership = 'Not configured'
|
||||
AuditHandleManipulation = 'Not configured'
|
||||
AuditIPsecDriver = 'Not configured'
|
||||
AuditIPsecExtendedMode = 'Not configured'
|
||||
AuditIPsecMainMode = 'Not configured'
|
||||
AuditIPsecQuickMode = 'Not configured'
|
||||
AuditKerberosAuthenticationService = 'Not configured'
|
||||
AuditKerberosServiceTicketOperations = 'Not configured'
|
||||
AuditKernelObject = 'Not configured'
|
||||
AuditLogoff = 'Not configured'
|
||||
AuditLogon = 'Not configured'
|
||||
AuditMPSSVCRuleLevelPolicyChange = 'Not configured'
|
||||
AuditNetworkPolicyServer = 'Not configured'
|
||||
AuditNonSensitivePrivilegeUse = 'Not configured'
|
||||
AuditOtherAccountLogonEvents = 'Not configured'
|
||||
AuditOtherAccountManagementEvents = 'Not configured'
|
||||
AuditOtherLogonLogoffEvents = 'Not configured'
|
||||
AuditOtherObjectAccessEvents = 'Not configured'
|
||||
AuditOtherPolicyChangeEvents = 'Not configured'
|
||||
AuditOtherPrivilegeUseEvents = 'Not configured'
|
||||
AuditOtherSystemEvents = 'Not configured'
|
||||
AuditPNPActivity = 'Not configured'
|
||||
AuditProcessCreation = 'Not configured'
|
||||
AuditProcessTermination = 'Not configured'
|
||||
AuditRegistry = 'Not configured'
|
||||
AuditRemovableStorage = 'Not configured'
|
||||
AuditRPCEvents = 'Not configured'
|
||||
AuditSAM = 'Not configured'
|
||||
AuditSecurityGroupManagement = 'Not configured'
|
||||
AuditSecurityStateChange = 'Not configured'
|
||||
AuditSecuritySystemExtension = 'Not configured'
|
||||
AuditSensitivePrivilegeUse = 'Not configured'
|
||||
AuditSpecialLogon = 'Not configured'
|
||||
AuditSystemIntegrity = 'Not configured'
|
||||
AuditUserDeviceClaims = 'Not configured'
|
||||
AuditUserAccountManagement = 'Not configured'
|
||||
}
|
||||
foreach ($GPOEntry in $GPO.DataSet) {
|
||||
if ($GPOEntry.PolicyTarget) {
|
||||
# Category = 'AuditSettings', Settings = 'AuditSetting'
|
||||
$Category = $GPOEntry.SubcategoryName -replace ' ', '' -replace '-', '' -replace '/', ''
|
||||
if ($CreateGPO["$($Category)"]) {
|
||||
$CreateGPO["$($Category)"] = $SettingType["$($GPOEntry.SettingValue)"]
|
||||
}
|
||||
} else {
|
||||
# Category = 'SecuritySettings', Settings = 'Audit'
|
||||
$SuccessAttempts = try { [bool]::Parse($GPOEntry.SuccessAttempts) } catch { $null };
|
||||
$FailureAttempts = try { [bool]::Parse($GPOEntry.FailureAttempts) } catch { $null };
|
||||
if ($SuccessAttempts -and $FailureAttempts) {
|
||||
$Setting = 'Success, Failure'
|
||||
} elseif ($SuccessAttempts) {
|
||||
$Setting = 'Success'
|
||||
} elseif ($FailureAttempts) {
|
||||
$Setting = 'Failure'
|
||||
} else {
|
||||
$Setting = 'Not configured'
|
||||
}
|
||||
$CreateGPO["$($GPOEntry.Name)"] = $Setting
|
||||
if ($SingleObject) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
Count = 0
|
||||
Settings = $null
|
||||
}
|
||||
[Array] $CreateGPO['Settings'] = @(
|
||||
$Settings = [ordered]@{
|
||||
AuditAccountLogon = 'Not configured'
|
||||
AuditAccountManage = 'Not configured'
|
||||
AuditDSAccess = 'Not configured'
|
||||
AuditLogonEvents = 'Not configured'
|
||||
AuditObjectAccess = 'Not configured'
|
||||
AuditPolicyChange = 'Not configured'
|
||||
AuditPrivilegeUse = 'Not configured'
|
||||
AuditProcessTracking = 'Not configured'
|
||||
AuditSystemEvents = 'Not configured'
|
||||
# Advanced Policies
|
||||
AuditAccountLockout = 'Not configured'
|
||||
AuditApplicationGenerated = 'Not configured'
|
||||
AuditApplicationGroupManagement = 'Not configured'
|
||||
AuditAuditPolicyChange = 'Not configured'
|
||||
AuditAuthenticationPolicyChange = 'Not configured'
|
||||
AuditAuthorizationPolicyChange = 'Not configured'
|
||||
AuditCentralAccessPolicyStaging = 'Not configured'
|
||||
AuditCertificationServices = 'Not configured'
|
||||
AuditComputerAccountManagement = 'Not configured'
|
||||
AuditCredentialValidation = 'Not configured'
|
||||
AuditDetailedDirectoryServiceReplication = 'Not configured'
|
||||
AuditDetailedFileShare = 'Not configured'
|
||||
AuditDirectoryServiceAccess = 'Not configured'
|
||||
AuditDirectoryServiceChanges = 'Not configured'
|
||||
AuditDirectoryServiceReplication = 'Not configured'
|
||||
AuditDistributionGroupManagement = 'Not configured'
|
||||
AuditDPAPIActivity = 'Not configured'
|
||||
AuditFileShare = 'Not configured'
|
||||
AuditFileSystem = 'Not configured'
|
||||
AuditFilteringPlatformConnection = 'Not configured'
|
||||
AuditFilteringPlatformPacketDrop = 'Not configured'
|
||||
AuditFilteringPlatformPolicyChange = 'Not configured'
|
||||
AuditGroupMembership = 'Not configured'
|
||||
AuditHandleManipulation = 'Not configured'
|
||||
AuditIPsecDriver = 'Not configured'
|
||||
AuditIPsecExtendedMode = 'Not configured'
|
||||
AuditIPsecMainMode = 'Not configured'
|
||||
AuditIPsecQuickMode = 'Not configured'
|
||||
AuditKerberosAuthenticationService = 'Not configured'
|
||||
AuditKerberosServiceTicketOperations = 'Not configured'
|
||||
AuditKernelObject = 'Not configured'
|
||||
AuditLogoff = 'Not configured'
|
||||
AuditLogon = 'Not configured'
|
||||
AuditMPSSVCRuleLevelPolicyChange = 'Not configured'
|
||||
AuditNetworkPolicyServer = 'Not configured'
|
||||
AuditNonSensitivePrivilegeUse = 'Not configured'
|
||||
AuditOtherAccountLogonEvents = 'Not configured'
|
||||
AuditOtherAccountManagementEvents = 'Not configured'
|
||||
AuditOtherLogonLogoffEvents = 'Not configured'
|
||||
AuditOtherObjectAccessEvents = 'Not configured'
|
||||
AuditOtherPolicyChangeEvents = 'Not configured'
|
||||
AuditOtherPrivilegeUseEvents = 'Not configured'
|
||||
AuditOtherSystemEvents = 'Not configured'
|
||||
AuditPNPActivity = 'Not configured'
|
||||
AuditProcessCreation = 'Not configured'
|
||||
AuditProcessTermination = 'Not configured'
|
||||
AuditRegistry = 'Not configured'
|
||||
AuditRemovableStorage = 'Not configured'
|
||||
AuditRPCEvents = 'Not configured'
|
||||
AuditSAM = 'Not configured'
|
||||
AuditSecurityGroupManagement = 'Not configured'
|
||||
AuditSecurityStateChange = 'Not configured'
|
||||
AuditSecuritySystemExtension = 'Not configured'
|
||||
AuditSensitivePrivilegeUse = 'Not configured'
|
||||
AuditSpecialLogon = 'Not configured'
|
||||
AuditSystemIntegrity = 'Not configured'
|
||||
AuditUserDeviceClaims = 'Not configured'
|
||||
AuditUserAccountManagement = 'Not configured'
|
||||
}
|
||||
foreach ($GPOEntry in $GPO.DataSet) {
|
||||
if ($GPOEntry.PolicyTarget) {
|
||||
# Category = 'AuditSettings', Settings = 'AuditSetting'
|
||||
$Category = $GPOEntry.SubcategoryName -replace ' ', '' -replace '-', '' -replace '/', ''
|
||||
if ($Settings["$($Category)"]) {
|
||||
$Settings["$($Category)"] = $SettingType["$($GPOEntry.SettingValue)"]
|
||||
}
|
||||
} else {
|
||||
# Category = 'SecuritySettings', Settings = 'Audit'
|
||||
$SuccessAttempts = try { [bool]::Parse($GPOEntry.SuccessAttempts) } catch { $null };
|
||||
$FailureAttempts = try { [bool]::Parse($GPOEntry.FailureAttempts) } catch { $null };
|
||||
if ($SuccessAttempts -and $FailureAttempts) {
|
||||
$Setting = 'Success, Failure'
|
||||
} elseif ($SuccessAttempts) {
|
||||
$Setting = 'Success'
|
||||
} elseif ($FailureAttempts) {
|
||||
$Setting = 'Failure'
|
||||
} else {
|
||||
$Setting = 'Not configured'
|
||||
}
|
||||
$Settings["$($GPOEntry.Name)"] = $Setting
|
||||
}
|
||||
}
|
||||
[PSCustomObject] $Settings
|
||||
)
|
||||
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
AuditAccountLogon = 'Not configured'
|
||||
AuditAccountManage = 'Not configured'
|
||||
AuditDSAccess = 'Not configured'
|
||||
AuditLogonEvents = 'Not configured'
|
||||
AuditObjectAccess = 'Not configured'
|
||||
AuditPolicyChange = 'Not configured'
|
||||
AuditPrivilegeUse = 'Not configured'
|
||||
AuditProcessTracking = 'Not configured'
|
||||
AuditSystemEvents = 'Not configured'
|
||||
# Advanced Policies
|
||||
AuditAccountLockout = 'Not configured'
|
||||
AuditApplicationGenerated = 'Not configured'
|
||||
AuditApplicationGroupManagement = 'Not configured'
|
||||
AuditAuditPolicyChange = 'Not configured'
|
||||
AuditAuthenticationPolicyChange = 'Not configured'
|
||||
AuditAuthorizationPolicyChange = 'Not configured'
|
||||
AuditCentralAccessPolicyStaging = 'Not configured'
|
||||
AuditCertificationServices = 'Not configured'
|
||||
AuditComputerAccountManagement = 'Not configured'
|
||||
AuditCredentialValidation = 'Not configured'
|
||||
AuditDetailedDirectoryServiceReplication = 'Not configured'
|
||||
AuditDetailedFileShare = 'Not configured'
|
||||
AuditDirectoryServiceAccess = 'Not configured'
|
||||
AuditDirectoryServiceChanges = 'Not configured'
|
||||
AuditDirectoryServiceReplication = 'Not configured'
|
||||
AuditDistributionGroupManagement = 'Not configured'
|
||||
AuditDPAPIActivity = 'Not configured'
|
||||
AuditFileShare = 'Not configured'
|
||||
AuditFileSystem = 'Not configured'
|
||||
AuditFilteringPlatformConnection = 'Not configured'
|
||||
AuditFilteringPlatformPacketDrop = 'Not configured'
|
||||
AuditFilteringPlatformPolicyChange = 'Not configured'
|
||||
AuditGroupMembership = 'Not configured'
|
||||
AuditHandleManipulation = 'Not configured'
|
||||
AuditIPsecDriver = 'Not configured'
|
||||
AuditIPsecExtendedMode = 'Not configured'
|
||||
AuditIPsecMainMode = 'Not configured'
|
||||
AuditIPsecQuickMode = 'Not configured'
|
||||
AuditKerberosAuthenticationService = 'Not configured'
|
||||
AuditKerberosServiceTicketOperations = 'Not configured'
|
||||
AuditKernelObject = 'Not configured'
|
||||
AuditLogoff = 'Not configured'
|
||||
AuditLogon = 'Not configured'
|
||||
AuditMPSSVCRuleLevelPolicyChange = 'Not configured'
|
||||
AuditNetworkPolicyServer = 'Not configured'
|
||||
AuditNonSensitivePrivilegeUse = 'Not configured'
|
||||
AuditOtherAccountLogonEvents = 'Not configured'
|
||||
AuditOtherAccountManagementEvents = 'Not configured'
|
||||
AuditOtherLogonLogoffEvents = 'Not configured'
|
||||
AuditOtherObjectAccessEvents = 'Not configured'
|
||||
AuditOtherPolicyChangeEvents = 'Not configured'
|
||||
AuditOtherPrivilegeUseEvents = 'Not configured'
|
||||
AuditOtherSystemEvents = 'Not configured'
|
||||
AuditPNPActivity = 'Not configured'
|
||||
AuditProcessCreation = 'Not configured'
|
||||
AuditProcessTermination = 'Not configured'
|
||||
AuditRegistry = 'Not configured'
|
||||
AuditRemovableStorage = 'Not configured'
|
||||
AuditRPCEvents = 'Not configured'
|
||||
AuditSAM = 'Not configured'
|
||||
AuditSecurityGroupManagement = 'Not configured'
|
||||
AuditSecurityStateChange = 'Not configured'
|
||||
AuditSecuritySystemExtension = 'Not configured'
|
||||
AuditSensitivePrivilegeUse = 'Not configured'
|
||||
AuditSpecialLogon = 'Not configured'
|
||||
AuditSystemIntegrity = 'Not configured'
|
||||
AuditUserDeviceClaims = 'Not configured'
|
||||
AuditUserAccountManagement = 'Not configured'
|
||||
}
|
||||
foreach ($GPOEntry in $GPO.DataSet) {
|
||||
if ($GPOEntry.PolicyTarget) {
|
||||
# Category = 'AuditSettings', Settings = 'AuditSetting'
|
||||
$Category = $GPOEntry.SubcategoryName -replace ' ', '' -replace '-', '' -replace '/', ''
|
||||
if ($CreateGPO["$($Category)"]) {
|
||||
$CreateGPO["$($Category)"] = $SettingType["$($GPOEntry.SettingValue)"]
|
||||
}
|
||||
} else {
|
||||
# Category = 'SecuritySettings', Settings = 'Audit'
|
||||
$SuccessAttempts = try { [bool]::Parse($GPOEntry.SuccessAttempts) } catch { $null };
|
||||
$FailureAttempts = try { [bool]::Parse($GPOEntry.FailureAttempts) } catch { $null };
|
||||
if ($SuccessAttempts -and $FailureAttempts) {
|
||||
$Setting = 'Success, Failure'
|
||||
} elseif ($SuccessAttempts) {
|
||||
$Setting = 'Success'
|
||||
} elseif ($FailureAttempts) {
|
||||
$Setting = 'Failure'
|
||||
} else {
|
||||
$Setting = 'Not configured'
|
||||
}
|
||||
$CreateGPO["$($GPOEntry.Name)"] = $Setting
|
||||
}
|
||||
}
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
@@ -2,52 +2,141 @@ function ConvertTo-XMLGenericPolicy {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
[string[]] $Category
|
||||
[string[]] $Category,
|
||||
[switch] $SingleObject
|
||||
)
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
}
|
||||
$UsedNames = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
[Array] $Policies = foreach ($Cat in $Category) {
|
||||
$GPO.DataSet | Where-Object { $_.Category -like $Cat }
|
||||
}
|
||||
if ($Policies.Count -gt 0) {
|
||||
foreach ($Policy in $Policies) {
|
||||
#if ($Policy.Category -notlike $Category) {
|
||||
# We check again for Category because one GPO can have multiple categories
|
||||
# First check checks GPO globally,
|
||||
# continue
|
||||
#}
|
||||
$Name = Format-ToTitleCase -Text $Policy.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$CreateGPO[$Name] = $Policy.State
|
||||
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
|
||||
}
|
||||
|
||||
foreach ($Setting in @('DropDownList', 'Numeric', 'EditText', 'Text', 'CheckBox', 'ListBox')) {
|
||||
if ($Policy.$Setting) {
|
||||
foreach ($Value in $Policy.$Setting) {
|
||||
if ($Value.Name) {
|
||||
$SubName = Format-ToTitleCase -Text $Value.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$SubName = -join ($Name, $SubName)
|
||||
if ($SubName -notin $UsedNames) {
|
||||
$UsedNames.Add($SubName)
|
||||
[Array] $CreateGPO['Settings'] = @(
|
||||
$Settings = [ordered] @{}
|
||||
foreach ($Policy in $Policies) {
|
||||
#if ($Policy.Category -notlike $Category) {
|
||||
# We check again for Category because one GPO can have multiple categories
|
||||
# First check checks GPO globally,
|
||||
# continue
|
||||
#}
|
||||
|
||||
$Name = Format-ToTitleCase -Text $Policy.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$Settings[$Name] = $Policy.State
|
||||
|
||||
foreach ($Setting in @('DropDownList', 'Numeric', 'EditText', 'Text', 'CheckBox', 'ListBox')) {
|
||||
if ($Policy.$Setting) {
|
||||
foreach ($Value in $Policy.$Setting) {
|
||||
if ($Value.Name) {
|
||||
$SubName = Format-ToTitleCase -Text $Value.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$SubName = -join ($Name, $SubName)
|
||||
if ($SubName -notin $UsedNames) {
|
||||
$UsedNames.Add($SubName)
|
||||
} else {
|
||||
$TimesUsed = $UsedNames | Group-Object | Where-Object { $_.Name -eq $SubName }
|
||||
$NumberToUse = $TimesUsed.Count + 1
|
||||
# We add same name 2nd and 3rd time to make sure we count properly
|
||||
$UsedNames.Add($SubName)
|
||||
# We now build property name based on amnount of times
|
||||
$SubName = -join ($SubName, "$NumberToUse")
|
||||
}
|
||||
if ($Value.Value -is [string]) {
|
||||
$Settings["$SubName"] = $Value.Value
|
||||
} elseif ($Value.Value -is [System.Xml.XmlElement]) {
|
||||
|
||||
<#
|
||||
if ($null -eq $Value.Value.Name) {
|
||||
# Shouldn't happen but lets see
|
||||
Write-Verbose $Value
|
||||
} else {
|
||||
$TimesUsed = $UsedNames | Group-Object | Where-Object { $_.Name -eq $SubName }
|
||||
$NumberToUse = $TimesUsed.Count + 1
|
||||
# We add same name 2nd and 3rd time to make sure we count properly
|
||||
$UsedNames.Add($SubName)
|
||||
# We now build property name based on amnount of times
|
||||
$SubName = -join ($SubName, "$NumberToUse")
|
||||
$CreateGPO["$SubName"] = $Value.Value.Name
|
||||
}
|
||||
if ($Value.Value -is [string]) {
|
||||
$CreateGPO["$SubName"] = $Value.Value
|
||||
} elseif ($Value.Value -is [System.Xml.XmlElement]) {
|
||||
|
||||
<#
|
||||
#>
|
||||
if ($Value.Value.Element) {
|
||||
$Settings["$SubName"] = $Value.Value.Element.Data -join '; '
|
||||
} elseif ($null -eq $Value.Value.Name) {
|
||||
# Shouldn't happen but lets see
|
||||
Write-Verbose "Tracking $Value"
|
||||
} else {
|
||||
$Settings["$SubName"] = $Value.Value.Name
|
||||
}
|
||||
|
||||
} elseif ($Value.State) {
|
||||
$Settings["$SubName"] = $Value.State
|
||||
} elseif ($null -eq $Value.Value) {
|
||||
# This is most likely Setting 'Text
|
||||
# Do nothing, usually it's just a text to display
|
||||
#Write-Verbose "Skipping value for display because it's empty. Name: $($Value.Name)"
|
||||
} else {
|
||||
# shouldn't happen
|
||||
Write-Verbose $Value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
[PSCustomObject] $Settings
|
||||
)
|
||||
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
}
|
||||
foreach ($Policy in $Policies) {
|
||||
#if ($Policy.Category -notlike $Category) {
|
||||
# We check again for Category because one GPO can have multiple categories
|
||||
# First check checks GPO globally,
|
||||
# continue
|
||||
#}
|
||||
$Name = Format-ToTitleCase -Text $Policy.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$CreateGPO[$Name] = $Policy.State
|
||||
|
||||
foreach ($Setting in @('DropDownList', 'Numeric', 'EditText', 'Text', 'CheckBox', 'ListBox')) {
|
||||
if ($Policy.$Setting) {
|
||||
foreach ($Value in $Policy.$Setting) {
|
||||
if ($Value.Name) {
|
||||
$SubName = Format-ToTitleCase -Text $Value.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$SubName = -join ($Name, $SubName)
|
||||
if ($SubName -notin $UsedNames) {
|
||||
$UsedNames.Add($SubName)
|
||||
} else {
|
||||
$TimesUsed = $UsedNames | Group-Object | Where-Object { $_.Name -eq $SubName }
|
||||
$NumberToUse = $TimesUsed.Count + 1
|
||||
# We add same name 2nd and 3rd time to make sure we count properly
|
||||
$UsedNames.Add($SubName)
|
||||
# We now build property name based on amnount of times
|
||||
$SubName = -join ($SubName, "$NumberToUse")
|
||||
}
|
||||
if ($Value.Value -is [string]) {
|
||||
$CreateGPO["$SubName"] = $Value.Value
|
||||
} elseif ($Value.Value -is [System.Xml.XmlElement]) {
|
||||
|
||||
<#
|
||||
if ($null -eq $Value.Value.Name) {
|
||||
# Shouldn't happen but lets see
|
||||
Write-Verbose $Value
|
||||
@@ -56,34 +145,35 @@ function ConvertTo-XMLGenericPolicy {
|
||||
}
|
||||
|
||||
#>
|
||||
if ($Value.Value.Element) {
|
||||
$CreateGPO["$SubName"] = $Value.Value.Element.Data -join '; '
|
||||
} elseif ($null -eq $Value.Value.Name) {
|
||||
# Shouldn't happen but lets see
|
||||
Write-Verbose "Tracking $Value"
|
||||
} else {
|
||||
$CreateGPO["$SubName"] = $Value.Value.Name
|
||||
}
|
||||
if ($Value.Value.Element) {
|
||||
$CreateGPO["$SubName"] = $Value.Value.Element.Data -join '; '
|
||||
} elseif ($null -eq $Value.Value.Name) {
|
||||
# Shouldn't happen but lets see
|
||||
Write-Verbose "Tracking $Value"
|
||||
} else {
|
||||
$CreateGPO["$SubName"] = $Value.Value.Name
|
||||
}
|
||||
|
||||
} elseif ($Value.State) {
|
||||
$CreateGPO["$SubName"] = $Value.State
|
||||
} elseif ($null -eq $Value.Value) {
|
||||
# This is most likely Setting 'Text
|
||||
# Do nothing, usually it's just a text to display
|
||||
#Write-Verbose "Skipping value for display because it's empty. Name: $($Value.Name)"
|
||||
} else {
|
||||
# shouldn't happen
|
||||
Write-Verbose $Value
|
||||
} elseif ($Value.State) {
|
||||
$CreateGPO["$SubName"] = $Value.State
|
||||
} elseif ($null -eq $Value.Value) {
|
||||
# This is most likely Setting 'Text
|
||||
# Do nothing, usually it's just a text to display
|
||||
#Write-Verbose "Skipping value for display because it's empty. Name: $($Value.Name)"
|
||||
} else {
|
||||
# shouldn't happen
|
||||
Write-Verbose $Value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
#}
|
||||
}
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
#}
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,11 @@ function ConvertTo-XMLGenericPublicKey {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
[string[]] $Category
|
||||
[string[]] $Category,
|
||||
[switch] $SingleObject
|
||||
)
|
||||
|
||||
$SkipNames = ('Name', 'LocalName', 'NamespaceURI', 'Prefix', 'NodeType', 'ParentNode', 'OwnerDocument', 'IsEmpty', 'Attributes', 'HasAttributes', 'SchemaInfo', 'InnerXml', 'InnerText', 'NextSibling', 'PreviousSibling', 'ChildNodes', 'FirstChild', 'LastChild', 'HasChildNodes', 'IsReadOnly', 'OuterXml', 'BaseURI', 'PreviousText')
|
||||
<#
|
||||
$UsedNames = [System.Collections.Generic.List[string]]::new()
|
||||
[Array] $Settings = foreach ($Cat in $Category) {
|
||||
$GPO.DataSet | Where-Object { $null -ne $_.$Cat }
|
||||
}
|
||||
#>
|
||||
foreach ($Setting in $GPO.DataSet) {
|
||||
if ($SingleObject) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
@@ -20,23 +14,57 @@ function ConvertTo-XMLGenericPublicKey {
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
Count = 0
|
||||
Settings = $null
|
||||
}
|
||||
$SettingName = $Setting.Name -split ":"
|
||||
$CreateGPO['CreatedTime'] = $GPO.CreatedTime # : 06.06.2020 18:03:36
|
||||
$CreateGPO['ModifiedTime'] = $GPO.ModifiedTime # : 17.06.2020 16:08:10
|
||||
$CreateGPO['ReadTime'] = $GPO.ReadTime # : 13.08.2020 10:15:37
|
||||
$CreateGPO['SecurityDescriptor'] = $GPO.SecurityDescriptor # : SecurityDescriptor
|
||||
$CreateGPO['FilterDataAvailable'] = $GPO.FilterDataAvailable # : True
|
||||
[Array] $CreateGPO['Settings'] = foreach ($Setting in $GPO.DataSet) {
|
||||
$SettingName = $Setting.Name -split ":"
|
||||
$MySettings = [ordered] @{
|
||||
CreatedTime = $GPO.CreatedTime # : 06.06.2020 18:03:36
|
||||
ModifiedTime = $GPO.ModifiedTime # : 17.06.2020 16:08:10
|
||||
ReadTime = $GPO.ReadTime # : 13.08.2020 10:15:37
|
||||
SecurityDescriptor = $GPO.SecurityDescriptor # : SecurityDescriptor
|
||||
FilterDataAvailable = $GPO.FilterDataAvailable # : True
|
||||
}
|
||||
$Name = $SettingName[1]
|
||||
#$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$MySettings['Name'] = $Name # $Setting.Name
|
||||
|
||||
ConvertTo-XMLNested -CreateGPO $MySettings -Setting $Setting -SkipNames $SkipNames #-Name $Name
|
||||
[PSCustomObject] $MySettings
|
||||
}
|
||||
|
||||
#$Divider = $SettingName[0]
|
||||
$Name = $SettingName[1]
|
||||
#$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$CreateGPO['Name'] = $Name # $Setting.Name
|
||||
#$CreateGPO['GPOSettingOrder'] = $Setting.GPOSettingOrder
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
foreach ($Setting in $GPO.DataSet) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
}
|
||||
$SettingName = $Setting.Name -split ":"
|
||||
$CreateGPO['CreatedTime'] = $GPO.CreatedTime # : 06.06.2020 18:03:36
|
||||
$CreateGPO['ModifiedTime'] = $GPO.ModifiedTime # : 17.06.2020 16:08:10
|
||||
$CreateGPO['ReadTime'] = $GPO.ReadTime # : 13.08.2020 10:15:37
|
||||
$CreateGPO['SecurityDescriptor'] = $GPO.SecurityDescriptor # : SecurityDescriptor
|
||||
$CreateGPO['FilterDataAvailable'] = $GPO.FilterDataAvailable # : True
|
||||
|
||||
#foreach ($Property in ($Setting.Properties | Get-Member -MemberType Properties).Name) {
|
||||
$Name = $SettingName[1]
|
||||
#$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$CreateGPO['Name'] = $Name # $Setting.Name
|
||||
#$CreateGPO['GPOSettingOrder'] = $Setting.GPOSettingOrder
|
||||
|
||||
#foreach ($Property in ($Setting.Properties | Get-Member -MemberType Properties).Name) {
|
||||
|
||||
ConvertTo-XMLNested -CreateGPO $CreateGPO -Setting $Setting -SkipNames $SkipNames #-Name $Name
|
||||
<#
|
||||
$Properties = $Setting.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames }
|
||||
foreach ($Property in $Properties) {
|
||||
If ($Property -eq 'Value') {
|
||||
@@ -54,33 +82,25 @@ function ConvertTo-XMLGenericPublicKey {
|
||||
} else {
|
||||
throw
|
||||
}
|
||||
|
||||
<#
|
||||
foreach ($SubProperty in $SubProperties) {
|
||||
if ($SubProperty -eq 'Name') {
|
||||
|
||||
} elseif ($SubProperty -eq 'String') {
|
||||
|
||||
} elseif ($SubProperty -eq 'Number') {
|
||||
|
||||
}
|
||||
#$Name = Format-CamelCaseToDisplayName -Text $SubProperty #-RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
#$CreateGPO[$Name] = $Setting.$Property.$SubProperty
|
||||
}
|
||||
#>
|
||||
}
|
||||
} else {
|
||||
$Name = Format-CamelCaseToDisplayName -Text $Property #-RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$CreateGPO[$Name] = $Setting.$Property
|
||||
if ($Setting.$Property -is [System.Xml.XmlElement]) {
|
||||
$SubPropeties = $Setting.$Property.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames }
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
$CreateGPO[$Name] = $Setting.$Property
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$CreateGPO['Filters'] = $Setting.Filters
|
||||
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
#>
|
||||
$CreateGPO['Filters'] = $Setting.Filters
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ function ConvertTo-XMLGenericSecuritySettings {
|
||||
[string[]] $Category
|
||||
)
|
||||
$SkipNames = ('Name', 'LocalName', 'NamespaceURI', 'Prefix', 'NodeType', 'ParentNode', 'OwnerDocument', 'IsEmpty', 'Attributes', 'HasAttributes', 'SchemaInfo', 'InnerXml', 'InnerText', 'NextSibling', 'PreviousSibling', 'Value', 'ChildNodes', 'FirstChild', 'LastChild', 'HasChildNodes', 'IsReadOnly', 'OuterXml', 'BaseURI', 'PreviousText')
|
||||
$UsedNames = [System.Collections.Generic.List[string]]::new()
|
||||
#$UsedNames = [System.Collections.Generic.List[string]]::new()
|
||||
[Array] $Settings = foreach ($Cat in $Category) {
|
||||
$GPO.DataSet | Where-Object { $null -ne $_.$Cat }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
function ConvertTo-XMLNested {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[System.Collections.IDictionary] $CreateGPO,
|
||||
[System.Xml.XmlElement] $Setting,
|
||||
[string[]] $SkipNames,
|
||||
[string] $Name
|
||||
)
|
||||
|
||||
$Properties = $Setting.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames }
|
||||
$TempName = $Name
|
||||
foreach ($Property in $Properties) {
|
||||
If ($Property -eq 'Value') {
|
||||
if ($Setting.$Property) {
|
||||
#$SubProperties = $Setting.$Property.PSObject.Properties.Name
|
||||
if ($Setting.$Property.Name) {
|
||||
$Name = $Setting.$Property.Name
|
||||
} else {
|
||||
if (-not $Name) {
|
||||
$Name = 'Value'
|
||||
}
|
||||
}
|
||||
if ($Setting.$Property.Number) {
|
||||
$CreateGPO[$Name] = $Setting.$Property.Number
|
||||
} elseif ($Setting.$Property.String) {
|
||||
$CreateGPO[$Name] = $Setting.$Property.String
|
||||
} else {
|
||||
$CreateGPO[$Name] = $Setting.$Property
|
||||
|
||||
#throw
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$Name = -join ($Name, $Property)
|
||||
$Name = Format-CamelCaseToDisplayName -Text $Name #-RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
if ($Setting.$Property -is [System.Xml.XmlElement]) {
|
||||
#$SubPropeties = $Setting.$Property.PSObject.Properties.Name | Where-Object { $_ -notin $SkipNames }
|
||||
|
||||
|
||||
ConvertTo-XMLNested -Setting $Setting.$Property -CreateGPO $CreateGPO -Name $Name -SkipNames $SkipNames
|
||||
|
||||
|
||||
} else {
|
||||
$CreateGPO[$Name] = $Setting.$Property
|
||||
}
|
||||
}
|
||||
$Name = $TempName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
function ConvertTo-XMLUserRightsAssignment {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
[switch] $SingleObject
|
||||
)
|
||||
|
||||
$UserRightsTranslation = @{
|
||||
SeNetworkLogonRight = 'Access this computer from the network'
|
||||
SeMachineAccountPrivilege = 'Add workstations to domain'
|
||||
SeIncreaseQuotaPrivilege = 'Adjust memory quotas for a process'
|
||||
SeInteractiveLogonRight = 'Allow log on locally'
|
||||
SeBackupPrivilege = 'Back up files and directories'
|
||||
SeChangeNotifyPrivilege = 'Bypass traverse checking Everyone'
|
||||
SeSystemTimePrivilege = 'Change the system time'
|
||||
SeCreatePagefilePrivilege = 'Create a pagefile'
|
||||
SeDebugPrivilege = 'Debug programs'
|
||||
SeEnableDelegationPrivilege = 'Enable computer and user accounts to be trusted for delegation'
|
||||
SeRemoteShutdownPrivilege = 'Force shutdown from a remote system'
|
||||
SeAuditPrivilege = 'Generate security audits'
|
||||
SeIncreaseBasePriorityPrivilege = 'Increase scheduling priority'
|
||||
SeLoadDriverPrivilege = 'Load and unload device drivers'
|
||||
SeBatchLogonRight = 'Log on as a batch job'
|
||||
SeSecurityPrivilege = 'Manage auditing and security log'
|
||||
SeSystemEnvironmentPrivilege = 'Modify firmware environment values'
|
||||
SeProfileSingleProcessPrivilege = 'Profile single process'
|
||||
SeSystemProfilePrivilege = 'Profile system performance'
|
||||
SeUndockPrivilege = 'Remove computer from docking station'
|
||||
SeAssignPrimaryTokenPrivilege = 'Replace a process level token'
|
||||
SeRestorePrivilege = 'Restore files and directories'
|
||||
SeShutdownPrivilege = 'Shut down the system'
|
||||
SeTakeOwnershipPrivilege = 'Take ownership of files or other objects'
|
||||
}
|
||||
|
||||
if ($SingleObject) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
Count = 0
|
||||
Settings = $null
|
||||
}
|
||||
[Array] $CreateGPO['Settings'] = foreach ($Entry in $GPO.DataSet) {
|
||||
foreach ($Member in $Entry.Member) {
|
||||
[PSCustomObject]@{
|
||||
'UserRightsAssignment' = $Entry.Name
|
||||
'UserRightsAssignmentDescription' = $UserRightsTranslation[$Entry.Name]
|
||||
'Name' = $Member.Name.'#text'
|
||||
'Sid' = $Member.SID.'#text'
|
||||
}
|
||||
}
|
||||
}
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
foreach ($Entry in $GPO.DataSet) {
|
||||
foreach ($Member in $Entry.Member) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
#GpoCategory = $GPOEntry.GpoCategory
|
||||
#GpoSettings = $GPOEntry.GpoSettings
|
||||
}
|
||||
$CreateGPO['UserRightsAssignment'] = $Entry.Name
|
||||
$CreateGPO['UserRightsAssignmentDescription'] = $UserRightsTranslation[$Entry.Name]
|
||||
$CreateGPO['Name'] = $Member.Name.'#text'
|
||||
$CreateGPO['Sid'] = $Member.SID.'#text'
|
||||
#$CreateGPO['CreatedTime'] = $GPO.CreatedTime # : 06.06.2020 18:03:36
|
||||
#$CreateGPO['ModifiedTime'] = $GPO.ModifiedTime # : 17.06.2020 16:08:10
|
||||
#$CreateGPO['ReadTime'] = $GPO.ReadTime # : 13.08.2020 10:15:37
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
function Find-GPOPassword {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[string] $Path
|
||||
)
|
||||
#Convert XML in a String file
|
||||
[string]$XMLString = Get-Content -LiteralPath $Path
|
||||
#Check if Cpassword Exist in the file
|
||||
if ($XMLString.Contains("cpassword")) {
|
||||
#Take the Cpassword Value from XML String file
|
||||
[string]$Cpassword = [regex]::matches($XMLString, '(cpassword=).+?(?=\")')
|
||||
$Cpassword = $Cpassword.split('(\")')[1]
|
||||
#Check if Cpassword has a value
|
||||
if ($Cpassword.Length -gt 20 -and $Cpassword -notlike '*cpassword*') {
|
||||
$Mod = ($Cpassword.length % 4)
|
||||
switch ($Mod) {
|
||||
'1' { $Cpassword = $Cpassword.Substring(0, $Cpassword.Length - 1) }
|
||||
'2' { $Cpassword += ('=' * (4 - $Mod)) }
|
||||
'3' { $Cpassword += ('=' * (4 - $Mod)) }
|
||||
}
|
||||
$Base64Decoded = [Convert]::FromBase64String($Cpassword)
|
||||
$AesObject = [System.Security.Cryptography.AesCryptoServiceProvider]::new()
|
||||
#Use th AES Key
|
||||
[Byte[]] $AesKey = @(0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b)
|
||||
$AesIV = New-Object Byte[]($AesObject.IV.Length)
|
||||
$AesObject.IV = $AesIV
|
||||
$AesObject.Key = $AesKey
|
||||
$DecryptorObject = $AesObject.CreateDecryptor()
|
||||
[Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
|
||||
#Convert Hash variable in a String valute
|
||||
$Password = [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
|
||||
} else {
|
||||
$Password = ''
|
||||
}
|
||||
}
|
||||
$Password
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
function Get-GitHubVersion {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)][string] $Cmdlet,
|
||||
[Parameter(Mandatory)][string] $RepositoryOwner,
|
||||
[Parameter(Mandatory)][string] $RepositoryName
|
||||
)
|
||||
$App = Get-Command -Name $Cmdlet -ErrorAction SilentlyContinue
|
||||
if ($App) {
|
||||
[Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/$RepositoryOwner/$RepositoryName/releases" -Verbose:$false)
|
||||
$LatestVersion = $GitHubReleases[0]
|
||||
if (-not $LatestVersion.Errors) {
|
||||
if ($App.Version -eq $LatestVersion.Version) {
|
||||
"Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)"
|
||||
} elseif ($App.Version -lt $LatestVersion.Version) {
|
||||
"Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?"
|
||||
} elseif ($App.Version -gt $LatestVersion.Version) {
|
||||
"Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!"
|
||||
}
|
||||
} else {
|
||||
"Current: $($App.Version)"
|
||||
}
|
||||
}
|
||||
}
|
||||
+196
-98
@@ -5,7 +5,7 @@
|
||||
[Object] $SecurityRights,
|
||||
|
||||
[string[]] $Principal,
|
||||
[validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'Sid',
|
||||
[validateset('DistinguishedName', 'Name', 'NetbiosName', 'Sid')][string] $PrincipalType = 'Sid',
|
||||
|
||||
[switch] $SkipWellKnown,
|
||||
[switch] $SkipAdministrative,
|
||||
@@ -20,7 +20,7 @@
|
||||
[switch] $IncludeGPOObject,
|
||||
[System.Collections.IDictionary] $ADAdministrativeGroups,
|
||||
[validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All',
|
||||
[System.Collections.IDictionary] $Accounts,
|
||||
#[System.Collections.IDictionary] $Accounts,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
Begin {
|
||||
@@ -100,6 +100,11 @@
|
||||
return
|
||||
}
|
||||
}
|
||||
if ($GPOPermission.Trustee.Domain) {
|
||||
$UserMerge = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name)
|
||||
} else {
|
||||
$UserMerge = $null
|
||||
}
|
||||
if ($Principal) {
|
||||
if ($PrincipalType -eq 'Sid') {
|
||||
if ($Principal -notcontains $GPOPermission.Trustee.Sid.Value) {
|
||||
@@ -110,8 +115,11 @@
|
||||
return
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'Name') {
|
||||
$UserMerge = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name)
|
||||
if ($Principal -notcontains $UserMerge -and $Principal -notcontains $GPOPermission.Trustee.Name) {
|
||||
if ($Principal -notcontains $GPOPermission.Trustee.Name) {
|
||||
return
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'NetbiosName') {
|
||||
if ($Principal -notcontains $UserMerge) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -126,56 +134,76 @@
|
||||
return
|
||||
}
|
||||
} elseif ($ExcludePrincipalType -eq 'Name') {
|
||||
$UserMerge = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name)
|
||||
if ($ExcludePrincipal -contains $UserMerge -or $ExcludePrincipal -contains $GPOPermission.Trustee.Name) {
|
||||
if ($ExcludePrincipal -contains $GPOPermission.Trustee.Name) {
|
||||
return
|
||||
}
|
||||
} elseif ($ExcludePrincipalType -eq 'NetbiosName') {
|
||||
if ($ExcludePrincipal -contains $UserMerge) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
$ReturnObject = [ordered] @{
|
||||
DisplayName = $GPO.DisplayName # : ALL | Enable RDP
|
||||
GUID = $GPO.ID
|
||||
DomainName = $GPO.DomainName # : ad.evotec.xyz
|
||||
Enabled = $GPO.GpoStatus
|
||||
Description = $GPO.Description
|
||||
CreationDate = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
PermissionType = if ($GPOPermission.Denied -eq $true) { 'Deny' } else { 'Allow' }
|
||||
Permission = $GPOPermission.Permission # : GpoEditDeleteModifySecurity
|
||||
Inherited = $GPOPermission.Inherited # : False
|
||||
Domain = $GPOPermission.Trustee.Domain #: EVOTEC
|
||||
DistinguishedName = $GPOPermission.Trustee.DSPath #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz
|
||||
Name = $GPOPermission.Trustee.Name #: Domain Admins
|
||||
Sid = $GPOPermission.Trustee.Sid.Value #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512
|
||||
SidType = $GPOPermission.Trustee.SidType #: Group
|
||||
}
|
||||
if ($Accounts) {
|
||||
$A = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name)
|
||||
if ($A -and $Accounts[$A]) {
|
||||
$ReturnObject['UserPrincipalName'] = $Accounts[$A].UserPrincipalName
|
||||
$ReturnObject['AccountEnabled'] = $Accounts[$A].Enabled
|
||||
$ReturnObject['DistinguishedName'] = $Accounts[$A].DistinguishedName
|
||||
$ReturnObject['PasswordLastSet'] = if ($Accounts[$A].PasswordLastSet) { $Accounts[$A].PasswordLastSet } else { '' }
|
||||
$ReturnObject['LastLogonDate'] = if ($Accounts[$A].LastLogonDate ) { $Accounts[$A].LastLogonDate } else { '' }
|
||||
if (-not $ReturnObject['Sid']) {
|
||||
$ReturnObject['Sid'] = $Accounts[$A].Sid.Value
|
||||
}
|
||||
if ($Accounts[$A].ObjectClass -eq 'group') {
|
||||
$ReturnObject['SidType'] = 'Group'
|
||||
} elseif ($Accounts[$A].ObjectClass -eq 'user') {
|
||||
$ReturnObject['SidType'] = 'User'
|
||||
} elseif ($Accounts[$A].ObjectClass -eq 'computer') {
|
||||
$ReturnObject['SidType'] = 'Computer'
|
||||
} else {
|
||||
$ReturnObject['SidType'] = 'EmptyOrUnknown'
|
||||
|
||||
<#
|
||||
# Sets permissions name, domain, distinguishedname to proper values
|
||||
if ($GPOPermission.Trustee.Name) {
|
||||
$DomainPlusName = -join ($GPOPermission.Trustee.Domain, '\', $GPOPermission.Trustee.Name)
|
||||
if ($GPOPermission.Trustee.DSPath) {
|
||||
$NetbiosConversion = ConvertFrom-NetbiosName -Identity $DomainPlusName
|
||||
if ($NetbiosConversion.DomainName) {
|
||||
$UserNameDomain = $NetbiosConversion.DomainName
|
||||
$UserName = $NetbiosConversion.Name
|
||||
}
|
||||
} else {
|
||||
$ReturnObject['UserPrincipalName'] = ''
|
||||
$ReturnObject['AccountEnabled'] = ''
|
||||
$ReturnObject['PasswordLastSet'] = ''
|
||||
$ReturnObject['LastLogonDate'] = ''
|
||||
$UserNameDomain = ''
|
||||
$Username = $DomainPlusName
|
||||
}
|
||||
} else {
|
||||
$DomainPlusName = ''
|
||||
$UserNameDomain = ''
|
||||
$Username = ''
|
||||
}
|
||||
#>
|
||||
|
||||
# I don't trust the returned data, some stuff like 'alias' shows up for groups. To unify it with everything else... using my own function
|
||||
$PermissionAccount = Get-WinADObject -Identity $GPOPermission.Trustee.Sid.Value -AddType -Cache -Verbose:$false
|
||||
if ($PermissionAccount) {
|
||||
$UserNameDomain = $PermissionAccount.DomainName
|
||||
$UserName = $PermissionAccount.Name
|
||||
$SidType = $PermissionAccount.Type
|
||||
$ObjectClass = $PermissionAccount.ObjectClass
|
||||
} else {
|
||||
$ConvertFromSID = ConvertFrom-SID -SID $GPOPermission.Trustee.Sid.Value
|
||||
$UserNameDomain = ''
|
||||
$Username = $ConvertFromSID.Name
|
||||
$SidType = $ConvertFromSID.Type
|
||||
if ($SidType -eq 'Unknown') {
|
||||
$ObjectClass = 'unknown'
|
||||
} else {
|
||||
$ObjectClass = 'foreignSecurityPrincipal'
|
||||
}
|
||||
}
|
||||
$ReturnObject = [ordered] @{
|
||||
DisplayName = $GPO.DisplayName # : ALL | Enable RDP
|
||||
GUID = $GPO.ID
|
||||
DomainName = $GPO.DomainName # : ad.evotec.xyz
|
||||
Enabled = $GPO.GpoStatus
|
||||
Description = $GPO.Description
|
||||
CreationDate = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
PermissionType = if ($GPOPermission.Denied -eq $true) { 'Deny' } else { 'Allow' }
|
||||
Permission = $GPOPermission.Permission # : GpoEditDeleteModifySecurity
|
||||
Inherited = $GPOPermission.Inherited # : False
|
||||
PrincipalNetBiosName = $UserMerge
|
||||
PrincipalDistinguishedName = $GPOPermission.Trustee.DSPath #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz
|
||||
PrincipalDomainName = $UserNameDomain #: EVOTEC
|
||||
PrincipalName = $UserName #: Domain Admins
|
||||
PrincipalSid = $GPOPermission.Trustee.Sid.Value #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512
|
||||
PrincipalSidType = $SidType #$GPOPermission.Trustee.SidType #: Group
|
||||
PrincipalObjectClass = $ObjectClass
|
||||
}
|
||||
|
||||
|
||||
if ($IncludeGPOObject) {
|
||||
$ReturnObject['GPOObject'] = $GPO
|
||||
$ReturnObject['GPOSecurity'] = $SecurityRights
|
||||
@@ -183,69 +211,139 @@
|
||||
}
|
||||
[PSCustomObject] $ReturnObject
|
||||
}
|
||||
if ($IncludeOwner.IsPresent) {
|
||||
if ($IncludeOwner) {
|
||||
if ($GPO.Owner) {
|
||||
$SplittedOwner = $GPO.Owner.Split('\')
|
||||
$DomainOwner = $SplittedOwner[0] #: EVOTEC
|
||||
$DomainUserName = $SplittedOwner[1] #: Domain Admins
|
||||
$SID = $ADAdministrativeGroups['ByNetBIOS']["$($GPO.Owner)"].Sid.Value
|
||||
if ($SID) {
|
||||
$SIDType = 'Group'
|
||||
$DistinguishedName = $ADAdministrativeGroups['ByNetBIOS']["$($GPO.Owner)"].DistinguishedName
|
||||
# I don't trust the returned data, some stuff like 'alias' shows up for groups. To unify it with everything else... using my own function
|
||||
$OwnerAccount = Get-WinADObject -Identity $GPO.Owner -AddType -Cache -Verbose:$false
|
||||
if ($OwnerAccount) {
|
||||
$UserNameDomain = $OwnerAccount.DomainName
|
||||
$UserName = $OwnerAccount.Name
|
||||
$SidType = $OwnerAccount.Type
|
||||
$OwnerObjectClass = $OwnerAccount.ObjectClass
|
||||
$SID = $OwnerAccount.ObjectSID
|
||||
} else {
|
||||
$SIDType = ''
|
||||
$DistinguishedName = ''
|
||||
$ConvertFromSID = ConvertFrom-SID -SID $GPO.Owner
|
||||
$UserNameDomain = ''
|
||||
$Username = $ConvertFromSID.Name
|
||||
$SidType = $ConvertFromSID.Type
|
||||
if ($SidType -eq 'Unknown') {
|
||||
$OwnerObjectClass = 'unknown'
|
||||
} else {
|
||||
$OwnerObjectClass = 'foreignSecurityPrincipal'
|
||||
}
|
||||
$SID = $ConvertFromSID.SID
|
||||
}
|
||||
} else {
|
||||
$DomainOwner = $GPO.Owner
|
||||
$DomainUserName = ''
|
||||
$UserName = ''
|
||||
$UserNameDomain = ''
|
||||
$SID = ''
|
||||
$SIDType = 'EmptyOrUnknown'
|
||||
$SIDType = 'Unknown'
|
||||
$DistinguishedName = ''
|
||||
$OwnerObjectClass = 'unknown'
|
||||
}
|
||||
$ReturnObject = [ordered] @{
|
||||
DisplayName = $GPO.DisplayName # : ALL | Enable RDP
|
||||
GUID = $GPO.Id
|
||||
DomainName = $GPO.DomainName # : ad.evotec.xyz
|
||||
Enabled = $GPO.GpoStatus
|
||||
Description = $GPO.Description
|
||||
CreationDate = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
Permission = 'GpoOwner' # : GpoEditDeleteModifySecurity
|
||||
Inherited = $false # : False
|
||||
Domain = $DomainOwner
|
||||
DistinguishedName = $DistinguishedName #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz
|
||||
Name = $DomainUserName
|
||||
Sid = $SID #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512
|
||||
SidType = $SIDType # #: Group
|
||||
}
|
||||
if ($Accounts) {
|
||||
$A = $GPO.Owner
|
||||
if ($A -and $Accounts[$A]) {
|
||||
$ReturnObject['UserPrincipalName'] = $Accounts[$A].UserPrincipalName
|
||||
$ReturnObject['AccountEnabled'] = $Accounts[$A].Enabled
|
||||
$ReturnObject['DistinguishedName'] = $Accounts[$A].DistinguishedName
|
||||
$ReturnObject['PasswordLastSet'] = if ($Accounts[$A].PasswordLastSet) { $Accounts[$A].PasswordLastSet } else { '' }
|
||||
$ReturnObject['LastLogonDate'] = if ($Accounts[$A].LastLogonDate ) { $Accounts[$A].LastLogonDate } else { '' }
|
||||
if (-not $ReturnObject['Sid']) {
|
||||
$ReturnObject['Sid'] = $Accounts[$A].Sid.Value
|
||||
}
|
||||
if ($Accounts[$A].ObjectClass -eq 'group') {
|
||||
$ReturnObject['SidType'] = 'Group'
|
||||
} elseif ($Accounts[$A].ObjectClass -eq 'user') {
|
||||
$ReturnObject['SidType'] = 'User'
|
||||
} elseif ($Accounts[$A].ObjectClass -eq 'computer') {
|
||||
$ReturnObject['SidType'] = 'Computer'
|
||||
} else {
|
||||
$ReturnObject['SidType'] = 'EmptyOrUnknown'
|
||||
# We have to process it for owners after querying user because $Owners are not as established as standard permissions so we don't know a lot
|
||||
|
||||
if ($Type -contains 'Administrative' -and $Type -notcontains 'All') {
|
||||
if ($SID) {
|
||||
$IsAdministrative = $ADAdministrativeGroups['BySID'][$SID]
|
||||
if (-not $IsAdministrative) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
$ReturnObject['UserPrincipalName'] = ''
|
||||
$ReturnObject['AccountEnabled'] = ''
|
||||
$ReturnObject['PasswordLastSet'] = ''
|
||||
$ReturnObject['LastLogonDate'] = ''
|
||||
# if there is no SID, it's not administrative
|
||||
return
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'NotWellKnownAdministrative' -and $Type -notcontains 'All') {
|
||||
# We check for SYSTEM account
|
||||
# Maybe we should make it a function and provide more
|
||||
if ($SID -eq 'S-1-5-18') {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'WellKnownAdministrative' -and $Type -notcontains 'All') {
|
||||
# We check for SYSTEM account
|
||||
# Maybe we should make it a function and provide more
|
||||
if ($SID -ne 'S-1-5-18') {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'Unknown' -and $Type -notcontains 'All') {
|
||||
# May need updates if there's more types
|
||||
if ($SidType -ne 'Unknown') {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'AuthenticatedUsers' -and $Type -notcontains 'All') {
|
||||
if ($SID -ne 'S-1-5-11') {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'DomainComputers' -and $Type -notcontains 'All') {
|
||||
$DomainComputersSID = -join ($ExtendedForestInformation['DomainsExtended'][$GPO.DomainName].DomainSID, '-515')
|
||||
if ($SID -ne $DomainComputersSID) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ($Principal) {
|
||||
if ($PrincipalType -eq 'Sid') {
|
||||
if ($Principal -notcontains $SID) {
|
||||
return
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'DistinguishedName') {
|
||||
if ($Principal -notcontains $DistinguishedName) {
|
||||
return
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'Name') {
|
||||
if ($Principal -notcontains $UserName) {
|
||||
return
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'NetbiosName') {
|
||||
if ($Principal -notcontains $GPO.Owner) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($ExcludePrincipal) {
|
||||
if ($ExcludePrincipalType -eq 'Sid') {
|
||||
if ($ExcludePrincipal -contains $SID) {
|
||||
return
|
||||
}
|
||||
} elseif ($ExcludePrincipalType -eq 'DistinguishedName') {
|
||||
if ($ExcludePrincipal -contains $DistinguishedName) {
|
||||
return
|
||||
}
|
||||
} elseif ($ExcludePrincipalType -eq 'Name') {
|
||||
if ($ExcludePrincipal -contains $UserName) {
|
||||
return
|
||||
}
|
||||
} elseif ($ExcludePrincipalType -eq 'NetbiosName') {
|
||||
if ($ExcludePrincipal -contains $GPO.Owner) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ReturnObject = [ordered] @{
|
||||
DisplayName = $GPO.DisplayName # : ALL | Enable RDP
|
||||
GUID = $GPO.Id
|
||||
DomainName = $GPO.DomainName # : ad.evotec.xyz
|
||||
Enabled = $GPO.GpoStatus
|
||||
Description = $GPO.Description
|
||||
CreationDate = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
PermissionType = 'Allow'
|
||||
Permission = 'GpoOwner' # : GpoEditDeleteModifySecurity
|
||||
Inherited = $false # : False
|
||||
PrincipalNetBiosName = $GPO.Owner
|
||||
PrincipalDistinguishedName = $DistinguishedName #: CN = Domain Admins, CN = Users, DC = ad, DC = evotec, DC = xyz
|
||||
PrincipalDomainName = $UserNameDomain
|
||||
PrincipalName = $UserName
|
||||
PrincipalSid = $SID #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 - 512
|
||||
PrincipalSidType = $SIDType # #: Group
|
||||
PrincipalObjectClass = $OwnerObjectClass
|
||||
}
|
||||
if ($IncludeGPOObject) {
|
||||
$ReturnObject['GPOObject'] = $GPO
|
||||
$ReturnObject['GPOSecurity'] = $SecurityRights
|
||||
|
||||
+93
-11
@@ -6,7 +6,8 @@
|
||||
[switch] $PermissionsOnly,
|
||||
[switch] $OwnerOnly,
|
||||
[System.Collections.IDictionary] $ADAdministrativeGroups,
|
||||
[string] $Splitter = [System.Environment]::NewLine
|
||||
[string] $Splitter = [System.Environment]::NewLine,
|
||||
[switch] $ReturnObject
|
||||
)
|
||||
if ($XMLContent.GPO.LinksTo) {
|
||||
$LinkSplit = ([Array] $XMLContent.GPO.LinksTo).Where( { $_.Enabled -eq $true }, 'Split')
|
||||
@@ -26,13 +27,11 @@
|
||||
$LinksDisabledCount = 0
|
||||
$LinksTotalCount = 0
|
||||
}
|
||||
if ($null -eq $XMLContent.GPO.Computer.ExtensionData -and $null -eq $XMLContent.GPO.User.ExtensionData) {
|
||||
$Empty = $true
|
||||
} else {
|
||||
$Empty = $false
|
||||
}
|
||||
|
||||
|
||||
#if ($null -eq $XMLContent.GPO.Computer.ExtensionData -and $null -eq $XMLContent.GPO.User.ExtensionData) {
|
||||
# $Empty = $true
|
||||
#} else {
|
||||
# $Empty = $false
|
||||
#}
|
||||
# Find proper values for enabled/disabled user/computer settings
|
||||
if ($XMLContent.GPO.Computer.Enabled -eq 'False') {
|
||||
$ComputerEnabled = $false
|
||||
@@ -54,6 +53,79 @@
|
||||
} elseif ($UserEnabled -eq $false -and $ComputerEnabled -eq $true) {
|
||||
$Enabled = 'User configuration settings disabled'
|
||||
}
|
||||
|
||||
$ComputerSettingsAvailable = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
|
||||
$UserSettingsAvailable = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
|
||||
|
||||
if ($ComputerSettingsAvailable -eq $false -and $UserSettingsAvailable -eq $false) {
|
||||
$Empty = $true
|
||||
} else {
|
||||
$Empty = $false
|
||||
}
|
||||
|
||||
# $OutputUser = $XMLContent.GPO.User.ExtensionData.Extension | Where-Object { $_.PSObject.Properties.TypeNameOfValue -in 'System.Xml.XmlElement', 'System.Object[]' }
|
||||
# $OutputComputer = $XMLContent.GPO.Computer.ExtensionData.Extension | Where-Object { $_.PSObject.Properties.TypeNameOfValue -in 'System.Xml.XmlElement', 'System.Object[]' }
|
||||
|
||||
$OutputUser = foreach ($ExtensionType in $XMLContent.GPO.User.ExtensionData.Extension) {
|
||||
if ($ExtensionType) {
|
||||
$GPOSettingTypeSplit = ($ExtensionType.type -split ':')
|
||||
try {
|
||||
$KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') }
|
||||
} catch {
|
||||
Write-Warning "Get-XMLStandard - things went sideways $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
}
|
||||
$KeysToLoop
|
||||
}
|
||||
$OutputComputer = foreach ($ExtensionType in $XMLContent.GPO.Computer.ExtensionData.Extension) {
|
||||
if ($ExtensionType) {
|
||||
$GPOSettingTypeSplit = ($ExtensionType.type -split ':')
|
||||
try {
|
||||
$KeysToLoop = $ExtensionType | Get-Member -MemberType Properties -ErrorAction Stop | Where-Object { $_.Name -notin 'type', $GPOSettingTypeSplit[0] -and $_.Name -notin @('Blocked') }
|
||||
} catch {
|
||||
Write-Warning "Get-XMLStandard - things went sideways $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
}
|
||||
$KeysToLoop
|
||||
}
|
||||
|
||||
$ComputerSettingsAvailableReal = if ($OutputComputer) { $true } else { $false }
|
||||
$UserSettingsAvailableReal = if ($OutputUser) { $true } else { $false }
|
||||
|
||||
if (-not $ComputerSettingsAvailableReal -and -not $UserSettingsAvailableReal) {
|
||||
$EmptyMaybe = $true
|
||||
} else {
|
||||
$EmptyMaybe = $false
|
||||
}
|
||||
|
||||
$ComputerProblem = $false
|
||||
if ($ComputerEnabled -eq $true -and $ComputerSettingsAvailableReal -eq $true) {
|
||||
$ComputerOptimized = $true
|
||||
} elseif ($ComputerEnabled -eq $true -and $ComputerSettingsAvailableReal -eq $false) {
|
||||
$ComputerOptimized = $false
|
||||
} elseif ($ComputerEnabled -eq $false -and $ComputerSettingsAvailableReal -eq $false) {
|
||||
$ComputerOptimized = $true
|
||||
} else {
|
||||
# Enabled $false, but ComputerData is there.
|
||||
$ComputerOptimized = $false
|
||||
$ComputerProblem = $true
|
||||
}
|
||||
|
||||
$UserProblem = $false
|
||||
if ($UserEnabled -eq $true -and $UserSettingsAvailableReal -eq $true) {
|
||||
$UserOptimized = $true
|
||||
} elseif ($UserEnabled -eq $true -and $UserSettingsAvailableReal -eq $false) {
|
||||
$UserOptimized = $false
|
||||
} elseif ($UserEnabled -eq $false -and $UserSettingsAvailableReal -eq $false) {
|
||||
$UserOptimized = $true
|
||||
} else {
|
||||
# Enabled $false, but UserData is there.
|
||||
$UserOptimized = $false
|
||||
$UserProblem = $true
|
||||
}
|
||||
|
||||
if (-not $PermissionsOnly) {
|
||||
if ($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text') {
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text')"]
|
||||
@@ -66,7 +138,7 @@
|
||||
$OwnerType = 'NotAdministrative'
|
||||
}
|
||||
} else {
|
||||
$OwnerType = 'EmptyOrUnknown'
|
||||
$OwnerType = 'Unknown'
|
||||
}
|
||||
}
|
||||
if ($PermissionsOnly) {
|
||||
@@ -117,15 +189,24 @@
|
||||
'DomainName' = $XMLContent.GPO.Identifier.Domain.'#text'
|
||||
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText -replace '{' -replace '}'
|
||||
'Empty' = $Empty
|
||||
'EmptyMaybe' = $EmptyMaybe
|
||||
'Linked' = $Linked
|
||||
'LinksCount' = $LinksTotalCount
|
||||
'LinksEnabledCount' = $LinksEnabledCount
|
||||
'LinksDisabledCount' = $LinksDisabledCount
|
||||
'Enabled' = $Enabled
|
||||
'ComputerEnabled' = $ComputerEnabled
|
||||
'ComputerOptimized' = $ComputerOptimized
|
||||
'ComputerProblem' = $ComputerProblem
|
||||
'UserEnabled' = $UserEnabled
|
||||
'ComputerSettingsAvailable' = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
|
||||
'UserSettingsAvailable' = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
|
||||
'UserOptimized' = $UserOptimized
|
||||
'UserProblem' = $UserProblem
|
||||
'ComputerSettingsAvailable' = $ComputerSettingsAvailable
|
||||
'UserSettingsAvailable' = $UserSettingsAvailable
|
||||
'ComputerSettingsAvailableReal' = $ComputerSettingsAvailableReal
|
||||
'UserSettingsAvailableReal' = $UserSettingsAvailableReal
|
||||
'ComputerSettingsTypes' = $OutputComputer.Name
|
||||
'UserSettingsTypes' = $OutputUser.Name
|
||||
'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
|
||||
'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false }
|
||||
'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension
|
||||
@@ -181,6 +262,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
'GPOObject' = $GPO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
$GPOZaurrAnalysis = [ordered] @{
|
||||
Name = 'Group Policy Content'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Invoke-GPOZaurrContent
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
foreach ($Key in $Script:Reporting['GPOAnalysis']['Data'].Keys) {
|
||||
New-HTMLTab -Name $Key {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOAnalysis']['Data'][$Key] -Filtering -Title $Key
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOAnalysis']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOAnalysis']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
$GPOZaurrBlockedInheritance = [ordered] @{
|
||||
Name = 'Group Policy Blocked Inhertiance'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrInheritance -IncludeBlockedObjects -OnlyBlockedInheritance
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['Data'] -Filtering
|
||||
if ($Script:Reporting['GPOBlockedInheritance']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
$GPOZaurrConsistency = [ordered] @{
|
||||
Name = 'GPO Permissions Consistency'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = { Get-GPOZaurrPermissionConsistency -Type All -VerifyInheritance }
|
||||
Processing = {
|
||||
foreach ($GPO in $Script:Reporting['GPOConsistency']['Data']) {
|
||||
if ($GPO.ACLConsistent -eq $true) {
|
||||
$Script:Reporting['GPOConsistency']['Variables']['Consistent']++
|
||||
} else {
|
||||
$Script:Reporting['GPOConsistency']['Variables']['Inconsistent']++
|
||||
}
|
||||
if ($GPO.ACLConsistentInside -eq $true) {
|
||||
$Script:Reporting['GPOConsistency']['Variables']['ConsistentInside']++
|
||||
} else {
|
||||
$Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOConsistency']['Variables']['Inconsistent'].Count -gt 0 -or $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'].Count -gt 0 ) {
|
||||
$Script:Reporting['GPOConsistency']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOConsistency']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
Consistent = 0
|
||||
Inconsistent = 0
|
||||
ConsistentInside = 0
|
||||
InconsistentInside = 0
|
||||
}
|
||||
Overview = {
|
||||
New-HTMLPanel {
|
||||
New-HTMLText -Text 'Following chart presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Top level permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['Consistent'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Inherited permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Inconsistent inherited permissions: ", $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them.'
|
||||
New-HTMLChart {
|
||||
New-ChartLegend -Names 'Bad', 'Good' -Color PaleGreen, Salmon
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Consistent', 'Inconsistent'
|
||||
New-ChartBar -Name 'TopLevel' -Value $Script:Reporting['GPOConsistency']['Variables']['Consistent'], $Script:Reporting['GPOConsistency']['Variables']['Inconsistent']
|
||||
New-ChartBar -Name 'Inherited' -Value $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'], $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']
|
||||
} -Title 'Permissions Consistency' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"When GPO is created it creates an entry in Active Directory (metadata) and SYSVOL (content). "
|
||||
"Two different places meens two different sets of permissions. Group Policy module is making sure the data in both places is correct. "
|
||||
"However, for different reasons it's not nessecary the case and often permissions go out of sync between AD and SYSVOL. "
|
||||
"This test verifies consistency of policies between AD and SYSVOL in two ways. "
|
||||
"It checks top level permissions for a GPO, and then checks if all files within said GPO are inheriting permissions or have different permissions in place. "
|
||||
}
|
||||
New-HTMLText -Text 'Following list presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Top level permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['Consistent'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Inherited permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Inconsistent inherited permissions: ", $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them. Please notice that if ', `
|
||||
' Not available ', 'is visible in the table you should first fix related, more pressing issue, before fixing permissions inconsistency.' -FontWeight normal, bold, normal
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOConsistency']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Consistent', 'Inconsistent' -Color PaleGreen, Salmon
|
||||
New-ChartBar -Name 'TopLevel' -Value $Script:Reporting['GPOConsistency']['Variables']['Consistent'], $Script:Reporting['GPOConsistency']['Variables']['Inconsistent']
|
||||
New-ChartBar -Name 'Inherited' -Value $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'], $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']
|
||||
} -Title 'Permissions Consistency' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Permissions Consistency' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOConsistency']['Data'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'ACLConsistent' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ACLConsistent' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ACLConsistent' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
if ($Script:Reporting['GPOConsistency']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOConsistency']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix - Permissions Consistency' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLText -Text 'Following steps will guide you how to fix permissions consistency'
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing permissions inconsistencies. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentBefore.html -Verbose -Type GPOConsistency
|
||||
}
|
||||
New-HTMLText -Text {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOOutput = Get-GPOZaurrPermissionConsistency
|
||||
$GPOOutput | Format-Table # do your actions as desired
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Fix inconsistent permissions' {
|
||||
New-HTMLText -Text "Following command when executed fixes inconsistent permissions."
|
||||
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
|
||||
New-HTMLText -Text "Make sure to fill in TargetDomain to match your Domain Admin permission account"
|
||||
|
||||
New-HTMLCodeBlock -Code {
|
||||
Repair-GPOZaurrPermissionConsistency -IncludeDomains "TargetDomain" -Verbose -WhatIf
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Repair-GPOZaurrPermissionConsistency -LimitProcessing 2 -IncludeDomains "TargetDomain"
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed repairs only first X inconsistent permissions. Use LimitProcessing parameter to prevent mass fixing and increase the counter when no errors occur."
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
|
||||
}
|
||||
New-HTMLText -Text "If there's nothing else to be fixed, we can skip to next step step"
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Fix inconsistent downlevel permissions' {
|
||||
New-HTMLText -Text "Unfortunetly this step is manual until automation is developed. "
|
||||
New-HTMLText -Text "If there are inconsistent permissions found inside GPO one has to fix them manually by going into SYSVOL and making sure inheritance is enabled, and that permissions are consistent across all files."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentAfter.html -Verbose -Type GPOConsistency
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
$GPOZaurrDuplicates = [ordered] @{
|
||||
Name = 'Duplicate (CNF) Group Policies'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrDuplicateObject
|
||||
}
|
||||
Processing = {
|
||||
$Script:Reporting['GPODuplicates']['Variables']['RequireDeletion'] = $Script:Reporting['GPODuplicates']['Data'].Count
|
||||
if ($Script:Reporting['GPODuplicates']['Data'].Count -gt 0) {
|
||||
$Script:Reporting['GPODuplicates']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPODuplicates']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
RequireDeletion = 0
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Resources = @(
|
||||
'https://social.technet.microsoft.com/wiki/contents/articles/15435.active-directory-duplicate-object-name-resolution.aspx'
|
||||
'https://kickthatcomputer.wordpress.com/2014/11/22/seek-and-destroy-duplicate-ad-objects-with-cnf-in-the-name/'
|
||||
)
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"CNF objects, Conflict objects or Duplicate Objects are created in Active Directory when there is simultaneous creation of an AD object under the same container "
|
||||
"on two separate Domain Controllers near about the same time or before the replication occurs. "
|
||||
"This results in a conflict and the same is exhibited by a CNF (Duplicate) object. "
|
||||
"While it doesn't nessecary has a huge impact on Active Directory it's important to keep Active Directory in proper, healthy state. "
|
||||
}
|
||||
New-HTMLText -Text 'As it stands currently there are ', $Script:Reporting['GPODuplicates']['Data'].Count, ' CNF (Duplicate) Group Policy objects to be deleted.' -FontSize 10pt -FontWeight normal, bold, normal
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPODuplicates']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartLegend -Names 'Bad' -Color PaleGreen, Salmon
|
||||
New-ChartBar -Name 'Duplicate (CNF) object' -Value $Script:Reporting['GPODuplicates']['Data'].Count
|
||||
} -Title 'Duplicate (CNF) Objects' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy CNF (Duplicate) Objects' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPODuplicates']['Data'] -Filtering {
|
||||
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
if ($Script:Reporting['GPODuplicates']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPODuplicates']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix - Remove duplicate (CNF) objects' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLText -Text 'Following steps will guide you how to remove duplicate objects'
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing duplicate GPO objects. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsBefore.html -Verbose -Type GPODuplicates
|
||||
}
|
||||
New-HTMLText -Text {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOOutput = Get-GPOZaurrDuplicateObject
|
||||
$GPOOutput | Format-Table # do your actions as desired
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Remove CNF objects' {
|
||||
New-HTMLText -Text "Following command when executed, runs internally command that lists all duplicate objects."
|
||||
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
|
||||
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrDuplicateObject -WhatIf -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrDuplicateObject -Verbose -LimitProcessing 2
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed removes only first X duplicate objects. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsAfter.html -Verbose -Type GPODuplicates
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
$GPOZaurrFiles = [ordered] @{
|
||||
Name = 'SYSVOL (NetLogon) Files List'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrFiles
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOFiles']['Data'] -Filtering
|
||||
if ($Script:Reporting['GPOFiles']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOFiles']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
$GPOZaurrList = [ordered] @{
|
||||
Name = 'Group Policy Summary'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurr
|
||||
}
|
||||
Processing = {
|
||||
foreach ($GPO in $Script:Reporting['GPOList']['Data']) {
|
||||
if ($GPO.Linked -eq $false -and $GPO.Empty -eq $true) {
|
||||
# Not linked, Empty
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmptyAndUnlinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotLinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmpty']++
|
||||
} elseif ($GPO.Linked -eq $true -and $GPO.Empty -eq $true) {
|
||||
# Linked, But EMPTY
|
||||
$Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmpty']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOLinked']++
|
||||
} elseif ($GPO.Linked -eq $false) {
|
||||
# Not linked, but not EMPTY
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotLinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotEmpty']++
|
||||
} elseif ($GPO.Empty -eq $true) {
|
||||
# Linked, But EMPTY
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmpty']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOLinked']++
|
||||
} else {
|
||||
# Linked, not EMPTY
|
||||
$Script:Reporting['GPOList']['Variables']['GPOValid']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPOLinked']++
|
||||
$Script:Reporting['GPOList']['Variables']['GPONotEmpty']++
|
||||
}
|
||||
if ($GPO.LinksDisabledCount -eq $GPO.LinksCount -and $GPO.LinksCount -gt 0) {
|
||||
$Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled']++
|
||||
}
|
||||
|
||||
if ($GPO.ComputerOptimized -eq $true) {
|
||||
$Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes']++
|
||||
} else {
|
||||
$Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo']++
|
||||
}
|
||||
if ($GPO.ComputerProblem -eq $true) {
|
||||
$Script:Reporting['GPOList']['Variables']['ComputerProblemYes']++
|
||||
} else {
|
||||
$Script:Reporting['GPOList']['Variables']['ComputerProblemNo']++
|
||||
}
|
||||
if ($GPO.UserOptimized -eq $true) {
|
||||
$Script:Reporting['GPOList']['Variables']['UserOptimizedYes']++
|
||||
} else {
|
||||
$Script:Reporting['GPOList']['Variables']['UserOptimizedNo']++
|
||||
}
|
||||
if ($GPO.UserProblem -eq $true) {
|
||||
$Script:Reporting['GPOList']['Variables']['UserProblemYes']++
|
||||
} else {
|
||||
$Script:Reporting['GPOList']['Variables']['UserProblemNo']++
|
||||
}
|
||||
if ($GPO.UserProblem -or $GPO.ComputerProblem) {
|
||||
$Script:Reporting['GPOList']['Variables']['GPOWithProblems']++
|
||||
}
|
||||
}
|
||||
$Script:Reporting['GPOList']['Variables']['GPOTotal'] = $Script:Reporting['GPOList']['Data'].Count
|
||||
if ($Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'].Count -gt 0) {
|
||||
$Script:Reporting['GPOList']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOList']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
GPOWithProblems = 0
|
||||
ComputerOptimizedYes = 0
|
||||
ComputerOptimizedNo = 0
|
||||
ComputerProblemYes = 0
|
||||
ComputerProblemNo = 0
|
||||
UserOptimizedYes = 0
|
||||
UserOptimizedNo = 0
|
||||
UserProblemYes = 0
|
||||
UserProblemNo = 0
|
||||
GPONotLinked = 0
|
||||
GPOLinked = 0
|
||||
GPOEmpty = 0
|
||||
GPONotEmpty = 0
|
||||
GPOEmptyAndUnlinked = 0
|
||||
GPOEmptyOrUnlinked = 0
|
||||
GPOLinkedButEmpty = 0
|
||||
GPOValid = 0
|
||||
GPOLinkedButLinkDisabled = 0
|
||||
GPOTotal = 0
|
||||
}
|
||||
Overview = {
|
||||
New-HTMLPanel {
|
||||
New-HTMLText -Text 'Following chart presents ', 'Linked / Empty and Unlinked Group Policies' -FontSize 10pt -FontWeight normal, bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies total: ', $Script:Reporting['GPOList']['Variables']['GPOTotal'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies valid: ", $Script:Reporting['GPOList']['Variables']['GPOValid'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies to delete: ", $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $Script:Reporting['GPOList']['Variables']['GPONotLinked'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $Script:Reporting['GPOList']['Variables']['GPOEmpty'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are linked, but empty: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are linked, but link disabled: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
}
|
||||
New-HTMLText -FontSize 10pt -Text 'Usually empty or unlinked Group Policies are safe to delete.'
|
||||
New-HTMLChart -Title 'Group Policies Summary' {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
#New-ChartLegend -Names 'Unlinked', 'Linked', 'Empty', 'Total' -Color Salmon, PaleGreen, PaleVioletRed, PaleTurquoise
|
||||
New-ChartLegend -Names 'Good', 'Bad' -Color PaleGreen, Salmon
|
||||
#New-ChartBar -Name 'Group Policies' -Value $Script:Reporting['GPOList']['Variables']['GPONotLinked'], $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPOEmpty'], $Script:Reporting['GPOList']['Variables']['GPOTotal']
|
||||
New-ChartBar -Name 'Linked' -Value $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPONotLinked']
|
||||
New-ChartBar -Name 'Empty' -Value $Script:Reporting['GPOList']['Variables']['GPONotEmpty'], $Script:Reporting['GPOList']['Variables']['GPOEmpty']
|
||||
New-ChartBar -Name 'Valid' -Value $Script:Reporting['GPOList']['Variables']['GPOValid'], $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']
|
||||
} -TitleAlignment center
|
||||
}
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -TextBlock {
|
||||
"Over time Administrators add more and more group policies, as business requirements change. "
|
||||
"Due to neglection or thinking it may serve it's purpose later on a lot of Group Policies often have no value at all. "
|
||||
"Either the Group Policy is not linked to anything and just stays unlinked forever, or GPO is linked, but the link (links) are disabled. "
|
||||
"Additionally sometimes new GPO is created without any settings or the settings are removed over time, but GPO stays in place. "
|
||||
} -FontSize 10pt
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies total: ', $Script:Reporting['GPOList']['Variables']['GPOTotal'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies valid: ", $Script:Reporting['GPOList']['Variables']['GPOValid'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies to delete: ", $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $Script:Reporting['GPOList']['Variables']['GPONotLinked'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $Script:Reporting['GPOList']['Variables']['GPOEmpty'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are linked, but empty: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are linked, but link disabled: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text "Additionally, we're reviewing Group Policies that have their section disabled, but contain data. Please review them and make sure this configuration is as expected!" -FontSize 10pt
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies with problems: ', $Script:Reporting['GPOList']['Variables']['GPOWithProblems'] -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies that have content (computer), but are disabled: ', $Script:Reporting['GPOList']['Variables']['ComputerProblemYes'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that have content (user), but are disabled: ", $Script:Reporting['GPOList']['Variables']['UserProblemYes'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text "For best performance it's recommended that if there are no settings of certain kind (Computer or User settings) it's best to disable them. " -FontSize 10pt
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies with optimization: ' -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies that are optimized (computer) ', $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are optimized (user): ", $Script:Reporting['GPOList']['Variables']['UserOptimizedYes'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
New-HTMLListItem -Text 'Group Policies without optimization: ' -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies that are not optimized (computer): ', $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies that are not optimized (user): ", $Script:Reporting['GPOList']['Variables']['UserOptimizedNo'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -TextBlock {
|
||||
'All empty or unlinked Group Policies can be automatically deleted. Please review output in the table and follow steps below table to cleanup Group Policies. '
|
||||
'GPOs that have content, but are disabled require manual intervention. '
|
||||
"If performance is an issue you should consider disabling user or computer sections of GPO when those are not used. "
|
||||
} -FontSize 10pt
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOList']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart -Title 'Group Policies Empty & Unlinked' {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Names 'Yes', 'No' -Color SpringGreen, Salmon
|
||||
New-ChartBar -Name 'Linked' -Value $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPONotLinked']
|
||||
New-ChartBar -Name 'Empty' -Value $Script:Reporting['GPOList']['Variables']['GPONotEmpty'], $Script:Reporting['GPOList']['Variables']['GPOEmpty']
|
||||
New-ChartBar -Name 'Valid' -Value $Script:Reporting['GPOList']['Variables']['GPOValid'], $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']
|
||||
New-ChartBar -Name 'With problem (computers)' -Value $Script:Reporting['GPOList']['Variables']['ComputerProblemNo'], $Script:Reporting['GPOList']['Variables']['ComputerProblemYes']
|
||||
New-ChartBar -Name 'With problem (users)' -Value $Script:Reporting['GPOList']['Variables']['UserProblemNo'], $Script:Reporting['GPOList']['Variables']['UserProblemYes']
|
||||
New-ChartBar -Name 'Optimized Computers' -Value $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes'], $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo']
|
||||
New-ChartBar -Name 'Optimized Users' -Value $Script:Reporting['GPOList']['Variables']['UserOptimizedYes'], $Script:Reporting['GPOList']['Variables']['UserOptimizedNo']
|
||||
} -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policies List' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOList']['Data'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Empty' -Value $true -BackgroundColor Salmon -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Linked' -Value $false -BackgroundColor Salmon -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ComputerProblem' -Value $true -BackgroundColor Salmon -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'UserProblem' -Value $true -BackgroundColor Salmon -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ComputerOptimized' -Value $false -BackgroundColor Salmon -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'UserOptimized' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
|
||||
# reverse
|
||||
New-HTMLTableCondition -Name 'Empty' -Value $false -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Linked' -Value $true -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ComputerProblem' -Value $false -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'UserProblem' -Value $false -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'ComputerOptimized' -Value $true -BackgroundColor SpringGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'UserOptimized' -Value $true -BackgroundColor SpringGreen -TextTransform capitalize -ComparisonType string
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
if ($Script:Reporting['GPOList']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOList']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix - Empty & Unlinked Group Policies' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLText -Text 'Following steps will guide you how to remove empty or unlinked group policies'
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrEmptyUnlinked.html -Verbose -Type GPOList
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOOutput = Get-GPOZaurr
|
||||
$GPOOutput | Format-Table
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Remove GPOs that are EMPTY or UNLINKED' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed removes every ",
|
||||
"EMPTY"
|
||||
" or "
|
||||
"NOT LINKED"
|
||||
" Group Policy. Make sure when running it for the first time to run it with ",
|
||||
"WhatIf",
|
||||
" parameter as shown below to prevent accidental removal.",
|
||||
"Make sure to use BackupPath which will make sure that for each GPO that is about to be deleted a backup is made to folder on a desktop."
|
||||
) -FontWeight normal, bold, normal, bold, normal, bold, normal, normal -Color Black, Red, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first empty or unlinked GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
|
||||
"Please make sure to check if backup is made as well before going all in."
|
||||
}
|
||||
New-HTMLText -Text "If there's nothing else to be deleted on SYSVOL side, we can skip to next step step"
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrEmptyUnlinkedAfter.html -Verbose -Type GPOList
|
||||
}
|
||||
New-HTMLText -Text "If there are no more empty or unlinked GPOs in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
$GPOZaurrNetLogonPermissions = [ordered] @{
|
||||
Name = 'NetLogon Permissions'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrNetLogon
|
||||
}
|
||||
Processing = {
|
||||
foreach ($File in $Script:Reporting['NetLogonPermissions']['Data']) {
|
||||
if ($File.FileSystemRights -eq 'Owner') {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners']++
|
||||
if ($File.PrincipalType -eq 'WellKnownAdministrative') {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrative']++
|
||||
} elseif ($File.PrincipalType -eq 'Administrative') {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrative']++
|
||||
} else {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative']++
|
||||
}
|
||||
if ($File.PrincipalSid -eq 'S-1-5-32-544') {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators']++
|
||||
} elseif ($File.PrincipalType -in 'WellKnownAdministrative', 'Administrative') {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators']++
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix']++
|
||||
} else {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix']++
|
||||
}
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['Owner'].Add($File)
|
||||
} else {
|
||||
$Script:Reporting['NetLogonPermissions']['Variables']['NonOwner'].Add($File)
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'].Count -gt 0) {
|
||||
$Script:Reporting['NetLogonPermissions']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['NetLogonPermissions']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
NetLogonOwners = 0
|
||||
NetLogonOwnersAdministrators = 0
|
||||
NetLogonOwnersNotAdministrative = 0
|
||||
NetLogonOwnersAdministrative = 0
|
||||
NetLogonOwnersAdministrativeNotAdministrators = 0
|
||||
NetLogonOwnersToFix = 0
|
||||
Owner = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
NonOwner = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
Overview = {
|
||||
New-HTMLPanel {
|
||||
New-HTMLText -Text 'Following chart presents ', 'NetLogon Summary' -FontSize 10pt -FontWeight normal, bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'NetLogon Files in Total: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'NetLogon BUILTIN\Administrators as Owner: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "NetLogon Owners requiring change: ", $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Not Administrative: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Administrative, but not BUILTIN\Administrators: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
} -FontSize 10pt
|
||||
#New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.'
|
||||
New-HTMLChart {
|
||||
New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen
|
||||
New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson
|
||||
} -Title 'NetLogon Owners' -TitleAlignment center
|
||||
}
|
||||
New-HTMLPanel {
|
||||
|
||||
}
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -TextBlock {
|
||||
"NetLogon is crucial part of Active Directory. Files stored there are available on each and every computer or server in the company. "
|
||||
"Keeping those files clean and secure is very important task. "
|
||||
"It's important that NetLogon file owners are set to BUILTIN\Administrators (SID: S-1-5-32-544). "
|
||||
"Owners have full control over the file object. Current owner of the file may be an Administrator but it doesn't guarentee that he/she will be in the future. "
|
||||
"That's why as a best-practice it's recommended to change any non-administrative owners to BUILTIN\Administrators, and even Administrative accounts should be replaced with it. "
|
||||
} -FontSize 10pt
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'NetLogon Files in Total: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'NetLogon BUILTIN\Administrators as Owner: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "NetLogon Owners requiring change: ", $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -FontWeight normal, bold {
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Not Administrative: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Administrative, but not BUILTIN\Administrators: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators'] -FontWeight normal, bold
|
||||
}
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text "Follow the steps below table to get NetLogon Owners into compliant state." -FontSize 10pt
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTab -Name 'NetLogon Owners' {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['NetLogonPermissions']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen
|
||||
New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson
|
||||
} -Title 'NetLogon Owners' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'NetLogon Files List' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['Owner'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor LightGreen -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor Salmon -ComparisonType string -Operator ne
|
||||
New-HTMLTableCondition -Name 'PrincipalType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['NetLogonPermissions']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix NetLogon Owners ' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLText -Text 'Following steps will guide you how to fix NetLogon Owners and make them compliant.'
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonBefore.html -Verbose -Type NetLogon
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$NetLogonOutput = Get-GPOZaurrNetLogon -Verbose
|
||||
$NetLogonOutput | Format-Table
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Set non-compliant file owners to BUILTIN\Administrators' {
|
||||
New-HTMLText -Text "Following command when executed runs internally command that lists all file owners and if it doesn't match changes it BUILTIN\Administrators. It doesn't change compliant owners."
|
||||
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
|
||||
|
||||
New-HTMLCodeBlock -Code {
|
||||
Repair-GPOZaurrNetLogonOwner -Verbose -WhatIf
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Repair-GPOZaurrNetLogonOwner -Verbose -LimitProcessing 2
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed sets new owner only on first X non-compliant NetLogon files. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur."
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonAfter.html -Verbose -Type NetLogon
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
New-HTMLTab -Name 'NetLogon Permissions' {
|
||||
New-HTMLSection -Name 'NetLogon Files List' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['NonOwner'] -Filtering
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
$GPOZaurrOrphans = [ordered] @{
|
||||
Name = 'Orphaned Group Policies'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrBroken
|
||||
}
|
||||
Processing = {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'] = @{}
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'] = @{}
|
||||
foreach ($GPO in $Script:Reporting['GPOOrphans']['Data']) {
|
||||
if (-not $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if ($GPO.Status -eq 'Not available in AD') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'Not available on SYSVOL') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'Permissions issue') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted'].Count -gt 0) {
|
||||
$Script:Reporting['GPOOrphans']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOOrphans']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
NotAvailableInAD = 0
|
||||
NotAvailableOnSysvol = 0
|
||||
NotAvailablePermissionIssue = 0
|
||||
NotAvailablePermissionIssuePerDomain = $null
|
||||
ToBeDeleted = 0
|
||||
ToBeDeletedPerDomain = $null
|
||||
}
|
||||
Overview = {
|
||||
<#
|
||||
New-HTMLPanel {
|
||||
New-HTMLText -TextBlock {
|
||||
"Group Policies are stored in two places - Active Directory (metadata) and SYSVOL (content)."
|
||||
"Since those are managed in different ways, replicated in different ways it's possible because of different issues they get out of sync."
|
||||
} -LineBreak
|
||||
New-HTMLText -Text "For example:"
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'USN Rollback in AD could cause group policies to reappar in Active Directory, yet SYSVOL data would be unavailable'
|
||||
New-HTMLListItem -Text 'Group Policy deletion failing to delete GPO content'
|
||||
New-HTMLListItem -Text 'DFSR replication failing between DCs'
|
||||
}
|
||||
New-HTMLText -Text 'Following chart presents ', 'Broken / Orphaned Group Policies' -FontSize 10pt -FontWeight normal, bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.'
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed
|
||||
New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']
|
||||
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
|
||||
}
|
||||
#>
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -TextBlock {
|
||||
"Group Policies are stored in two places - Active Directory (metadata) and SYSVOL (content)."
|
||||
"Since those are managed in different ways, replicated in different ways it's possible because of different issues they get out of sync."
|
||||
} -FontSize 10pt -LineBreak
|
||||
New-HTMLText -Text "For example:" -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'USN Rollback in AD could cause already deleted Group Policies to reapper in Active Directory, yet SYSVOL data would be unavailable'
|
||||
New-HTMLListItem -Text 'Group Policy deletion failing to delete GPO content'
|
||||
New-HTMLListItem -Text 'Permission issue preventing deletion of GPO content'
|
||||
New-HTMLListItem -Text 'Failing DFSR replication between DCs'
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following problems were detected:' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text "Please review output in table and follow the steps below table to get Active Directory Group Policies in healthy state." -FontSize 10pt
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOOrphans']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed
|
||||
New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']
|
||||
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Health State of Group Policies' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['Data'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Status' -Value "Not available in AD" -BackgroundColor Salmon -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Status' -Value "Not available on SYSVOL" -BackgroundColor LightCoral -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Status' -Value "Permissions issue" -BackgroundColor MediumVioletRed -ComparisonType string -Color White
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoBefore.html -Verbose -Type GPOOrphans
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOOutput = Get-GPOZaurrBroken -Verbose
|
||||
$GPOOutput | Format-Table
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Make a backup (optional)' {
|
||||
New-HTMLText -TextBlock {
|
||||
"The process fixing broken GPOs will delete AD or SYSVOL content depending on type of a problem. "
|
||||
"While it's always useful to have a backup, this backup won't actually backup those broken group policies"
|
||||
" for a simple reason that those are not backupable. You can't back up GPO if there is no SYSVOL content"
|
||||
" and you can't backup GPO if there's only SYSVOL content. "
|
||||
"However, since the script does make changes to GPOs it's advised to have a backup anyways! "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All
|
||||
$GPOSummary | Format-Table # only if you want to display output of backup
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. "
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Fix GPOs not available in AD' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed runs cleanup procedure that removes all broken GPOs on SYSVOL side. ",
|
||||
"Make sure when running it for the first time to run it with ",
|
||||
"WhatIf ",
|
||||
"parameter as shown below to prevent accidental removal. ",
|
||||
'When run it will remove any GPO remains from SYSVOL, that should not be there, as AD metadata is already gone.'
|
||||
"Please notice I'm using SYSVOL as a type, because the removal will happen on SYSVOL. "
|
||||
) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type SYSVOL -WhatIf -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type SYSVOL -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. "
|
||||
"Keep in mind that what backup command does is simply copy SYSVOL content to given place. "
|
||||
"Since there is no GPO metadata in AD there's no real restore process for this step. "
|
||||
"It's there to make sure if someone kept some data in there and wants to get access to it, he/she can. "
|
||||
} -LineBreak
|
||||
New-HTMLText -Text "Once happy with results please follow with command (this will start deletion process): " -LineBreak -FontWeight bold
|
||||
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type SYSVOL -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type SYSVOL -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur. "
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly. "
|
||||
"If there's nothing else to be deleted on SYSVOL side, we can skip to next step step. "
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Fix GPOs not available on SYSVOL' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed runs cleanup procedure that removes all broken GPOs on Active Directory side."
|
||||
"Make sure when running it for the first time to run it with ",
|
||||
"WhatIf",
|
||||
" parameter as shown below to prevent accidental removal."
|
||||
'When run it will remove any GPO remains from AD, that should not be there, as SYSVOL content is already gone.'
|
||||
"Please notice I'm using AD as a type, because the removal will happen on AD side. "
|
||||
) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type AD -WhatIf -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type AD -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. "
|
||||
"Keep in mind that there is no backup for this. "
|
||||
"Since there is no SYSVOL data, and only AD object is there there's no real restore process for this step. "
|
||||
"Once you delete it, it's gone. "
|
||||
} -LineBreak
|
||||
New-HTMLText -Text 'Once happy with results please follow with command (this will start deletion process): ' -LineBreak -FontWeight bold
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type AD -LimitProcessing 2 -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type AD -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur. "
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly. "
|
||||
"If there's nothing else to be deleted on AD side, we can skip to next step step. "
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoAfter.html -Verbose -Type GPOOrphans
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrphans']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
$GPOZaurrOwners = [ordered] @{
|
||||
Name = 'Group Policy Owners'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = { Get-GPOZaurrOwner -IncludeSysvol }
|
||||
Processing = {
|
||||
# Create Per Domain Variables
|
||||
$Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'] = @{}
|
||||
$Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'] = @{}
|
||||
foreach ($GPO in $Script:Reporting['GPOOwners']['Data']) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
# Checks
|
||||
if ($GPO.IsOwnerConsistent) {
|
||||
$Script:Reporting['GPOOwners']['Variables']['IsConsistent']++
|
||||
} else {
|
||||
$Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']++
|
||||
}
|
||||
if ($GPO.IsOwnerAdministrative) {
|
||||
$Script:Reporting['GPOOwners']['Variables']['IsAdministrative']++
|
||||
} else {
|
||||
$Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']++
|
||||
}
|
||||
if (($GPO.IsOwnerAdministrative -eq $false -or $GPO.IsOwnerConsistent -eq $false) -and $GPO.SysvolExists -eq $true) {
|
||||
$Script:Reporting['GPOOwners']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.SysvolExists -eq $false) {
|
||||
$Script:Reporting['GPOOwners']['Variables']['RequiresDiffFix']++
|
||||
$Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOOwners']['Variables']['WillNotTouch']++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOwners']['Variables']['WillFix'].Count -gt 0) {
|
||||
$Script:Reporting['GPOOwners']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOOwners']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
IsAdministrative = 0
|
||||
IsNotAdministrative = 0
|
||||
IsConsistent = 0
|
||||
IsNotConsistent = 0
|
||||
WillFix = 0
|
||||
RequiresDiffFix = 0
|
||||
WillNotTouch = 0
|
||||
RequiresDiffFixPerDomain = $null
|
||||
WillFixPerDomain = $null
|
||||
}
|
||||
Overview = {
|
||||
<#
|
||||
New-HTMLPanel {
|
||||
New-HTMLText -Text 'Following chart presents Group Policy owners and whether they are administrative and consistent. By design an owner of Group Policy should be Domain Admins or Enterprise Admins group only to prevent malicious takeover. ', `
|
||||
"It's also important that owner in Active Directory matches owner on SYSVOL (file system)." -FontSize 10pt
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Non-Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Owners consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsConsistent'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Owners not-consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Yes', 'No' -Color PaleGreen, Orchid
|
||||
New-ChartBar -Name 'Is administrative' -Value $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'], $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']
|
||||
New-ChartBar -Name 'Is consistent' -Value $Script:Reporting['GPOOwners']['Variables']['IsConsistent'], $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']
|
||||
} -Title 'Group Policy Owners' -TitleAlignment center
|
||||
}
|
||||
#>
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"By default GPO creation is usually maintained by Domain Admins or Enterprise Admins. "
|
||||
"When GPO is created by member of Domain Admins or Enterprise Admins group the GPO Owner is set to Domain Admins. "
|
||||
"When GPO is created by member of Group Policy Creator Owners or other group has delegated rights to create a GPO the owner of said GPO is not Domain Admins group but is assigned to relevant user. "
|
||||
"GPO Owners should be Domain Admins or Enterprise Admins to prevent abuse. If that isn't so it means owner is able to fully control GPO and potentially change it's settings in uncontrolled way. "
|
||||
"While at the moment of creation of new GPO it's not a problem, in long term it's possible such person may no longer be admin, yet keep their rights over GPO. "
|
||||
}
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"As you're aware Group Policies are stored in 2 places. In Active Directory (metadata) and SYSVOL (settings). This means that there are 2 places where GPO Owners exists. "
|
||||
"This also means that for multiple reasons AD and SYSVOL can be out of sync when it comes to their permissions which can lead to uncontrolled ability to modify them. "
|
||||
"Ownership in Active Directory and Ownership of SYSVOL for said GPO are required to be the same. "
|
||||
}
|
||||
New-HTMLText -Text "Here's a short summary of ", "Group Policy Owners", ": " -FontSize 10pt -FontWeight normal, bold, normal
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Non-Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Owners consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsConsistent'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Owners not-consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text "Following will need to happen: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies requiring owner change: ', $Script:Reporting['GPOOwners']['Variables']['WillFix'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which can't be fixed (no SYSVOL?): ", $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFix'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies unaffected: ", $Script:Reporting['GPOOwners']['Variables']['WillNotTouch'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOwners']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require fixing using, ', 'different methods:' -FontSize 10pt -FontWeight bold, bold -Color Black, RedRobin -TextDecoration none, underline
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOOwners']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Yes', 'No' -Color LightGreen, Salmon
|
||||
New-ChartBar -Name 'Is administrative' -Value $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'], $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']
|
||||
New-ChartBar -Name 'Is consistent' -Value $Script:Reporting['GPOOwners']['Variables']['IsConsistent'], $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']
|
||||
} -Title 'Group Policy Owners' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Owners' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOwners']['Data'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'IsOwnerConsistent' -Value $false -BackgroundColor Salmon -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'IsOwnerAdministrative' -Value $false -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix Group Policy Owners' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
#New-HTMLText -Text 'Following steps will guide you how to fix group policy owners'
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with fixing Group Policy Owners. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOwnersBefore.html -Verbose -Type GPOOwners
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$OwnersGPO = Get-GPOZaurrOwner -IncludeSysvol -Verbose
|
||||
$OwnersGPO | Format-Table
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Make a backup (optional)' {
|
||||
New-HTMLText -TextBlock {
|
||||
"The process of fixing GPO Owner does NOT touch GPO content. It simply changes owners on AD and SYSVOL at the same time. "
|
||||
"However, it's always good to have a backup before executing changes that may impact Active Directory. "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All
|
||||
$GPOSummary | Format-Table # only if you want to display output of backup
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. "
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Set GPO Owners to Administrative (Domain Admins)' {
|
||||
New-HTMLText -Text "Following command will find any GPO which doesn't have proper GPO Owner (be it due to inconsistency or not being Domain Admin) and will enforce new GPO Owner. "
|
||||
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Set-GPOZaurrOwner -Type All -Verbose -WhatIf
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Set-GPOZaurrOwner -Type All -Verbose -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data."
|
||||
} -LineBreak
|
||||
New-HTMLText -Text "Once happy with results please follow with command (this will start fixing process): " -LineBreak -FontWeight bold
|
||||
New-HTMLCodeBlock -Code {
|
||||
Set-GPOZaurrOwner -Type All -Verbose -LimitProcessing 2
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Set-GPOZaurrOwner -Type All -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed sets new owner only on first X non-compliant GPO Owners for AD/SYSVOL. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur."
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOwnersAfter.html -Verbose -Type GPOOwners
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOwners']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOwners']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
$GPOZaurrPassword = [ordered] @{
|
||||
Name = 'Group Policy Passwords'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrPassword
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPassword']['Data'] -Filtering
|
||||
|
||||
if ($Script:Reporting['GPOPassword']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPassword']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
$GPOZaurrPermissions = [ordered] @{
|
||||
Name = 'Group Policy Permissions'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrPermission -Type All -IncludePermissionType GpoEditDeleteModifySecurity, GpoEdit, GpoCustom #-IncludeOwner
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
$GPOZaurrPermissionsRead = [ordered] @{
|
||||
Name = 'Group Policy Authenticated Users Permissions'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
[ordered] @{
|
||||
Permissions = Get-GPOZaurrPermission -Type AuthenticatedUsers -ReturnSecurityWhenNoData
|
||||
Issues = Get-GPOZaurrPermissionIssue
|
||||
}
|
||||
}
|
||||
Processing = {
|
||||
# This is a workaround - we need to use it since we have 0 permissions
|
||||
|
||||
# Create Per Domain Variables
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'] = @{}
|
||||
|
||||
foreach ($GPO in $Script:Reporting['GPOPermissionsRead']['Data'].Issues) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if ($GPO.PermissionIssue) {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead']++
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['Read']++
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['ReadPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
foreach ($GPO in $Script:Reporting['GPOPermissionsRead']['Data'].Permissions) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
# Checks
|
||||
if ($GPO.Permission -in 'GpoApply', 'GpoRead') {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouch']++
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouchPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] -gt 0 -or $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead'] -gt 0) {
|
||||
$Script:Reporting['GPOPermissionsRead']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissionsRead']['ActionRequired'] = $false
|
||||
}
|
||||
# Summary from 2 reports
|
||||
$Script:Reporting['GPOPermissionsRead']['Variables']['TotalToFix'] = $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] + $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead']
|
||||
}
|
||||
Variables = @{
|
||||
WillFix = 0
|
||||
WillNotTouch = 0
|
||||
WillFixPerDomain = $null
|
||||
WillNotTouchPerDomain = $null
|
||||
CouldNotRead = 0
|
||||
CouldNotReadPerDomain = $null
|
||||
Read = 0
|
||||
ReadPerDomain = $null
|
||||
TotalToFix = 0
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -TextBlock {
|
||||
"When GPO is created one of the permissions that are required for proper functioning of Group Policies is NT AUTHORITY\Authenticated Users. "
|
||||
"Some Administrators don't follow best practices and trying to remove GpoApply permission, remove also GpoRead permission from a GPO which can have consequences. "
|
||||
} -LineBreak
|
||||
New-HTMLText -Text "On June 14th, 2016 Microsoft released [HotFix](https://support.microsoft.com/en-gb/help/3159398/ms16-072-description-of-the-security-update-for-group-policy-june-14-2) that requires Authenticated Users to be present on all Group Policies to function properly: " -FontSize 10pt
|
||||
New-HTMLText -TextBlock {
|
||||
"MS16-072 changes the security context with which user group policies are retrieved. "
|
||||
"This by-design behavior change protects customers’ computers from a security vulnerability. "
|
||||
"Before MS16-072 is installed, user group policies were retrieved by using the user’s security context. "
|
||||
"After MS16-072 is installed, user group policies are retrieved by using the computer's security context."
|
||||
} -FontStyle italic -FontSize 10pt -FontWeight bold -LineBreak
|
||||
New-HTMLText -FontSize 10pt -Text @(
|
||||
"There are two parts to this assesment. Reading all Group Policies Permissions that account ",
|
||||
$($Env:USERNAME.ToUpper()),
|
||||
" has permissions to read and provide detailed assesment about permissions. ",
|
||||
"Second assesment checks for permissions that this account is not able to read at all, and therefore it has no visibility about permissions set on it. "
|
||||
"We just were able to detect the problem, but hopefully higher level account (Domain Admin) should be able to provide full assesment. "
|
||||
) -FontWeight normal, bold, normal
|
||||
New-HTMLText -FontSize 10pt -Text "First assesment results: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies requiring Authenticated Users with GpoRead permission: ', $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouch'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOPermissionsRead']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -FontSize 10pt -Text "Secondary assesment results: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text "Group Policies couldn't read at all: ", $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies with permissions allowing read: ", $Script:Reporting['GPOPermissionsRead']['Variables']['Read'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text @(
|
||||
"That means we need to fix permissions on: "
|
||||
$($Script:Reporting['GPOPermissionsRead']['Variables']['TotalToFix'])
|
||||
" out of "
|
||||
$($Script:Reporting['GPOPermissionsRead']['Data'].Issues).Count
|
||||
" Group Policies. "
|
||||
) -FontSize 10pt -FontWeight bold, bold, normal, bold, normal -Color Black, FreeSpeechRed, Black, Black -LineBreak -TextDecoration none, underline, underline, underline, none
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOPermissionsRead']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Yes', 'No' -Color LightGreen, Salmon
|
||||
New-ChartBar -Name 'Authenticated Users Available' -Value $Script:Reporting['GPOPermissionsRead']['Variables']['WillNotTouch'], $Script:Reporting['GPOPermissionsRead']['Variables']['WillFix']
|
||||
New-ChartBar -Name 'Accessible Group Policies' -Value $Script:Reporting['GPOPermissionsRead']['Variables']['Read'], $Script:Reporting['GPOPermissionsRead']['Variables']['CouldNotRead']
|
||||
} -Title 'Group Policy Permissions' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Authenticated Users Analysis' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRead']['Data'].Permissions -Filtering {
|
||||
New-HTMLTableCondition -Name 'Permission' -Value '' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 7, 15, 30, 45, 60
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Issues Assesment' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRead']['Data'].Issues -Filtering {
|
||||
New-HTMLTableCondition -Name 'PermissionIssue' -Value $true -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 7, 15, 30, 45, 60 -DefaultSortColumn PermissionIssue -DefaultSortOrder Descending
|
||||
}
|
||||
New-HTMLSection -Name 'Steps to fix Group Policy Authenticated Users' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLWizard {
|
||||
New-HTMLWizardStep -Name 'Prepare environment' {
|
||||
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
|
||||
New-HTMLCodeBlock -Code {
|
||||
Install-Module GPOZaurr -Force
|
||||
Import-Module GPOZaurr -Force
|
||||
} -Style powershell
|
||||
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Prepare report' {
|
||||
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with fixing Group Policy Authenticated Users. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsReadBefore.html -Verbose -Type GPOPermissionsRead
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"When executed it will take a while to generate all data and provide you with new report depending on size of environment. "
|
||||
"GPOs with problems will be those not having any value for Permission/PermissionType columns. "
|
||||
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$AuthenticatedUsers = Get-GPOZaurrPermission -Type AuthenticatedUsers -ReturnSecurityWhenNoData
|
||||
$AuthenticatedUsers | Format-Table
|
||||
}
|
||||
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Make a backup (optional)' {
|
||||
New-HTMLText -TextBlock {
|
||||
"The process of fixing GPO Permissions does NOT touch GPO content. It simply adds permissionss on AD and SYSVOL at the same time for given GPO. "
|
||||
"However, it's always good to have a backup before executing changes that may impact Active Directory. "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOSummary = Backup-GPOZaurr -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -Type All
|
||||
$GPOSummary | Format-Table # only if you want to display output of backup
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Above command when executed will make a backup to Desktop, create GPO folder and within it it will put all those GPOs. "
|
||||
}
|
||||
}
|
||||
|
||||
New-HTMLWizardStep -Name 'Add Authenticated Users ability to read all GPO' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command will find any GPO which doesn't have Authenticated User as GpoRead or GpoApply and will add it as GpoRead. ",
|
||||
"This change doesn't change GpoApply permission, therefore it won't change to whom the GPO applies to. ",
|
||||
"It ensures that COMPUTERS can read GPO properly to be able to Apply it. ",
|
||||
"Make sure when running it for the first time to run it with ",
|
||||
"WhatIf",
|
||||
" parameter as shown below to prevent accidental adding of permissions."
|
||||
) -FontWeight normal, normal, normal, normal, bold, normal -Color Black, Black, Black, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -WhatIf -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -WhatIf -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data."
|
||||
} -LineBreak
|
||||
New-HTMLText -Text "Once happy with results please follow with command (this will start fixing process): " -LineBreak -FontWeight bold
|
||||
New-HTMLCodeBlock -Code {
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -Verbose -LimitProcessing 2
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -Verbose -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"This command when executed adds Authenticated Users (GpoRead permission) only on first X non-compliant Group Policies. "
|
||||
"Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur."
|
||||
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. "
|
||||
"In case of any issues please review and action accordingly."
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Verification report' {
|
||||
New-HTMLText -TextBlock {
|
||||
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOPermissionsReadAfter.html -Verbose -Type GPOPermissionsRead
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOPermissionsRead']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRead']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
$GPOZaurrPermissionsRoot = [ordered] @{
|
||||
Name = 'Group Policies Root Permissions'
|
||||
Enabled = $true
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrPermissionRoot -SkipNames
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRoot']['Data'] -Filtering
|
||||
if ($Script:Reporting['GPOPermissionsRoot']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRoot']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
$GPOZaurrSysVolLegacyFiles = [ordered] @{
|
||||
Name = 'SYSVOL Legacy ADM Files'
|
||||
Enabled = $false
|
||||
Action = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrLegacyFiles
|
||||
}
|
||||
Processing = {
|
||||
|
||||
}
|
||||
Variables = @{
|
||||
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLTable -DataTable $Script:Reporting['SysVolLegacyFiles']['Data'] -Filtering
|
||||
if ($Script:Reporting['SysVolLegacyFiles']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['SysVolLegacyFiles']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
function New-GPOZaurrReportConsole {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[System.Collections.IDictionary] $Results
|
||||
)
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
[PSCustomObject] $GPOPermission,
|
||||
[alias('PermissionType')][Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType
|
||||
)
|
||||
if ($GPOPermission.Name) {
|
||||
$Text = "Removing SID: $($GPOPermission.Sid), Name: $($GPOPermission.Domain)\$($GPOPermission.Name), SidType: $($GPOPermission.SidType) from domain $($GPOPermission.DomainName)"
|
||||
if ($GPOPermission.PrincipalName) {
|
||||
$Text = "Removing SID: $($GPOPermission.PrincipalSid), Name: $($GPOPermission.PrincipalDomainName)\$($GPOPermission.PrincipalName), SidType: $($GPOPermission.PrincipalSidType) from domain $($GPOPermission.DomainName)"
|
||||
} else {
|
||||
$Text = "Removing SID: $($GPOPermission.Sid), Name: EMPTY, SidType: $($GPOPermission.SidType) from domain $($GPOPermission.DomainName)"
|
||||
$Text = "Removing SID: $($GPOPermission.PrincipalSid), Name: EMPTY, SidType: $($GPOPermission.PrincipalSidType) from domain $($GPOPermission.DomainName)"
|
||||
}
|
||||
if ($PrincipalType -eq 'DistinguishedName') {
|
||||
if ($GPOPermission.DistinguishedName -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) {
|
||||
if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, $Text)) {
|
||||
try {
|
||||
Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.Name) / Type: $($GPOPermission.PermissionType)"
|
||||
Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.PrincipalName) / Type: $($GPOPermission.PermissionType)"
|
||||
$GPOPermission.GPOSecurity.Remove($GPOPermission.GPOSecurityPermissionItem)
|
||||
$GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity)
|
||||
} catch {
|
||||
@@ -24,10 +24,10 @@
|
||||
}
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'Sid') {
|
||||
if ($GPOPermission.Sid -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) {
|
||||
if ($GPOPermission.PrincipalSid -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) {
|
||||
if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, $Text)) {
|
||||
try {
|
||||
Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.Name) / Type: $($GPOPermission.PermissionType)"
|
||||
Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.PrincipalName) / Type: $($GPOPermission.PermissionType)"
|
||||
$GPOPermission.GPOSecurity.Remove($GPOPermission.GPOSecurityPermissionItem)
|
||||
$GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity)
|
||||
} catch {
|
||||
@@ -51,10 +51,10 @@
|
||||
}
|
||||
}
|
||||
} elseif ($PrincipalType -eq 'Name') {
|
||||
if ($GPOPermission.Name -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) {
|
||||
if ($GPOPermission.PrincipalName -eq $Principal -and $GPOPermission.Permission -eq $IncludePermissionType) {
|
||||
if ($PSCmdlet.ShouldProcess($GPOPermission.DisplayName, $Text)) {
|
||||
try {
|
||||
Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.Name) / Type: $($GPOPermission.PermissionType)"
|
||||
Write-Verbose "Remove-GPOZaurrPermission - Removing permission $IncludePermissionType for $($Principal) / $($GPOPermission.PrincipalName) / Type: $($GPOPermission.PermissionType)"
|
||||
$GPOPermission.GPOSecurity.Remove($GPOPermission.GPOSecurityPermissionItem)
|
||||
$GPOPermission.GPOObject.SetSecurityInfo($GPOPermission.GPOSecurity)
|
||||
} catch {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
function Reset-GPOZaurrStatus {
|
||||
param(
|
||||
|
||||
)
|
||||
#if (-not $Script:GPOConfigurationClean) {
|
||||
# $Script:GPOConfigurationClean = Copy-Dictionary -Dictionary $Script:GPOConfiguration
|
||||
#} else {
|
||||
# $Script:GPOConfiguration = Copy-Dictionary -Dictionary $Script:GPOConfigurationClean
|
||||
#}
|
||||
if (-not $Script:DefaultTypes) {
|
||||
$Script:DefaultTypes = foreach ($T in $Script:GPOConfiguration.Keys) {
|
||||
if ($Script:GPOConfiguration[$T].Enabled) {
|
||||
$T
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($T in $Script:GPOConfiguration.Keys) {
|
||||
$Script:GPOConfiguration[$T]['Enabled'] = $false
|
||||
}
|
||||
foreach ($T in $Script:DefaultTypes) {
|
||||
$Script:GPOConfiguration[$T]['Enabled'] = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
$Script:GPOConfiguration = [ordered] @{
|
||||
GPOOrphans = $GPOZaurrOrphans
|
||||
GPOOwners = $GPOZaurrOwners
|
||||
GPOConsistency = $GPOZaurrConsistency
|
||||
GPODuplicates = $GPOZaurrDuplicates
|
||||
GPOList = $GPOZaurrList
|
||||
GPOPassword = $GPOZaurrPassword
|
||||
GPOPermissions = $GPOZaurrPermissions
|
||||
GPOPermissionsRead = $GPOZaurrPermissionsRead
|
||||
GPOPermissionsRoot = $GPOZaurrPermissionsRoot
|
||||
GPOFiles = $GPOZaurrFiles
|
||||
GPOBlockedInheritance = $GPOZaurrBlockedInheritance
|
||||
GPOAnalysis = $GPOZaurrAnalysis
|
||||
NetLogonPermissions = $GPOZaurrNetLogonPermissions
|
||||
SysVolLegacyFiles = $GPOZaurrSysVolLegacyFiles
|
||||
}
|
||||
+573
-256
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,18 @@
|
||||
param(
|
||||
[Array] $GPOs,
|
||||
[string] $Server,
|
||||
[string] $Domain
|
||||
[string] $Domain,
|
||||
[System.Collections.IDictionary] $PoliciesAD,
|
||||
[string] $PoliciesSearchBase
|
||||
)
|
||||
$Differences = @{ }
|
||||
$SysvolHash = @{ }
|
||||
|
||||
$GPOGUIDS = $GPOs.ID.GUID
|
||||
$SysVolPath = "\\$($Server)\SYSVOL\$Domain\Policies"
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL from \\$($Server)\SYSVOL\$Domain\Policies"
|
||||
try {
|
||||
$SYSVOL = Get-ChildItem -Path "\\$($Server)\SYSVOL\$Domain\Policies" -ErrorAction Stop
|
||||
$SYSVOL = Get-ChildItem -Path "\\$($Server)\SYSVOL\$Domain\Policies" -Exclude 'PolicyDefinitions' -ErrorAction Stop -Verbose:$false
|
||||
} catch {
|
||||
$Sysvol = $Null
|
||||
}
|
||||
@@ -22,12 +26,20 @@
|
||||
if ($Files) {
|
||||
$Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual
|
||||
foreach ($_ in $Comparing) {
|
||||
if ($_.InputObject -eq 'PolicyDefinitions') {
|
||||
# we skip policy definitions
|
||||
continue
|
||||
}
|
||||
if ($_.SideIndicator -eq '==') {
|
||||
$Found = 'Exists'
|
||||
} elseif ($_.SideIndicator -eq '<=') {
|
||||
$Found = 'Not available on SYSVOL'
|
||||
} elseif ($_.SideIndicator -eq '=>') {
|
||||
$Found = 'Orphaned GPO'
|
||||
if ($PoliciesAD[$_.InputObject]) {
|
||||
$Found = $PoliciesAD[$_.InputObject]
|
||||
} else {
|
||||
$Found = 'Not available in AD'
|
||||
}
|
||||
} else {
|
||||
$Found = 'Orphaned GPO'
|
||||
}
|
||||
@@ -35,82 +47,89 @@
|
||||
}
|
||||
}
|
||||
$GPOSummary = @(
|
||||
$Count = 0
|
||||
foreach ($GPO in $GPOS) {
|
||||
$Count++
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing [$($GPO.DomainName)]($Count/$($GPOS.Count)) $($GPO.DisplayName)"
|
||||
if ($null -ne $SysvolHash[$GPO.Id.GUID].FullName) {
|
||||
$FullPath = $SysvolHash[$GPO.Id.GUID].FullName
|
||||
try {
|
||||
$ACL = Get-Acl -Path $SysvolHash[$GPO.Id.GUID].FullName -ErrorAction Stop
|
||||
$ACL = Get-Acl -Path $SysvolHash[$GPO.Id.GUID].FullName -ErrorAction Stop -Verbose:$false
|
||||
$Owner = $ACL.Owner
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrSysvol - ACL reading (1) failed for $FullPath with error: $($_.Exception.Message)"
|
||||
Write-Warning "Get-GPOZaurrBroken - ACL reading (1) failed for $FullPath with error: $($_.Exception.Message)"
|
||||
$ACL = $null
|
||||
$Owner = ''
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
}
|
||||
} else {
|
||||
$FullPath = -join ($SysVolPath, "\{$($GPO.Id.Guid)}")
|
||||
$ACL = $null
|
||||
$Owner = ''
|
||||
$ErrorMessage = 'Not found on SYSVOL'
|
||||
}
|
||||
if ($null -eq $Differences[$GPO.Id.Guid]) {
|
||||
$SysVolStatus = 'Not available on SYSVOL'
|
||||
$SysVolStatus = 'Unknown Issue'
|
||||
} else {
|
||||
$SysVolStatus = $Differences[$GPO.Id.Guid]
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $GPO.DisplayName
|
||||
Status = $Differences[$GPO.Id.Guid]
|
||||
DomainName = $GPO.DomainName
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = $SysVolStatus
|
||||
Owner = $GPO.Owner
|
||||
FileOwner = $Owner
|
||||
Id = $GPO.Id.Guid
|
||||
GpoStatus = $GPO.GpoStatus
|
||||
Path = $FullPath
|
||||
Description = $GPO.Description
|
||||
CreationTime = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
UserVersion = $GPO.UserVersion
|
||||
ComputerVersion = $GPO.ComputerVersion
|
||||
WmiFilter = $GPO.WmiFilter
|
||||
Error = $ErrorMessage
|
||||
DisplayName = $GPO.DisplayName
|
||||
Status = $SysVolStatus
|
||||
DomainName = $GPO.DomainName
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = $SysVolStatus
|
||||
GpoStatus = $GPO.GpoStatus
|
||||
Owner = $GPO.Owner
|
||||
FileOwner = $Owner
|
||||
Id = $GPO.Id.Guid
|
||||
Path = $FullPath
|
||||
DistinguishedName = -join ("CN={", $GPO.Id.Guid, "},", $PoliciesSearchBase)
|
||||
Description = $GPO.Description
|
||||
CreationTime = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
UserVersion = $GPO.UserVersion
|
||||
ComputerVersion = $GPO.ComputerVersion
|
||||
WmiFilter = $GPO.WmiFilter
|
||||
Error = $ErrorMessage
|
||||
}
|
||||
}
|
||||
# Now we need to list thru Sysvol files and fine those that do not exists as GPO and create dummy GPO objects to show orphaned gpos
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL differences and creating dummy objects"
|
||||
foreach ($_ in $Differences.Keys) {
|
||||
if ($Differences[$_] -eq 'Orphaned GPO') {
|
||||
if ($SysvolHash[$_].BaseName -notcontains 'PolicyDefinitions') {
|
||||
$FullPath = $SysvolHash[$_].FullName
|
||||
try {
|
||||
$ACL = Get-Acl -Path $FullPath -ErrorAction Stop
|
||||
$Owner = $ACL.Owner
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrSysvol - ACL reading (2) failed for $FullPath with error: $($_.Exception.Message)"
|
||||
$ACL = $null
|
||||
$Owner = $null
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
}
|
||||
if ($Differences[$_] -in 'Not available in AD', 'Permissions issue') {
|
||||
$FullPath = $SysvolHash[$_].FullName
|
||||
try {
|
||||
$ACL = Get-Acl -Path $FullPath -ErrorAction Stop
|
||||
$Owner = $ACL.Owner
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrBroken - ACL reading (2) failed for $FullPath with error: $($_.Exception.Message)"
|
||||
$ACL = $null
|
||||
$Owner = $null
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
}
|
||||
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $SysvolHash[$_].BaseName
|
||||
Status = 'Orphaned GPO'
|
||||
DomainName = $Domain
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = $Differences[$GPO.Id.Guid]
|
||||
Owner = ''
|
||||
FileOwner = $Owner
|
||||
Id = $_
|
||||
GpoStatus = 'Orphaned'
|
||||
Path = $FullPath
|
||||
Description = $null
|
||||
CreationTime = $SysvolHash[$_].CreationTime
|
||||
ModificationTime = $SysvolHash[$_].LastWriteTime
|
||||
UserVersion = $null
|
||||
ComputerVersion = $null
|
||||
WmiFilter = $null
|
||||
Error = $ErrorMessage
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $SysvolHash[$_].BaseName
|
||||
Status = $Differences[$_]
|
||||
DomainName = $Domain
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = 'Exists' #$Differences[$GPO.Id.Guid]
|
||||
GpoStatus = $Differences[$_]
|
||||
Owner = ''
|
||||
FileOwner = $Owner
|
||||
Id = $_
|
||||
Path = $FullPath
|
||||
DistinguishedName = -join ("CN={", $_, "},", $PoliciesSearchBase)
|
||||
Description = $null
|
||||
CreationTime = $SysvolHash[$_].CreationTime
|
||||
ModificationTime = $SysvolHash[$_].LastWriteTime
|
||||
UserVersion = $null
|
||||
ComputerVersion = $null
|
||||
WmiFilter = $null
|
||||
Error = $ErrorMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+169
-146
@@ -1,11 +1,9 @@
|
||||
function Add-GPOZaurrPermission {
|
||||
[cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'GPOGUID')]
|
||||
[cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'GPOName')]
|
||||
param(
|
||||
[Parameter(ParameterSetName = 'GPOName', Mandatory)]
|
||||
[string] $GPOName,
|
||||
|
||||
[Parameter(ParameterSetName = 'GPOGUID', Mandatory)]
|
||||
[alias('GUID', 'GPOID')][string] $GPOGuid,
|
||||
[Parameter(ParameterSetName = 'GPOName', Mandatory)][string] $GPOName,
|
||||
[Parameter(ParameterSetName = 'GPOGUID', Mandatory)][alias('GUID', 'GPOID')][string] $GPOGuid,
|
||||
[Parameter(ParameterSetName = 'All', Mandatory)][switch] $All,
|
||||
|
||||
[Parameter(ParameterSetName = 'ADObject', Mandatory)]
|
||||
[alias('OrganizationalUnit', 'DistinguishedName')][Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject,
|
||||
@@ -55,7 +53,6 @@
|
||||
}
|
||||
$Splat['ExcludeDomains'] = $ExcludeDomains
|
||||
$Splat['ExtendedForestInformation'] = $ExtendedForestInformation
|
||||
#$Splat['ExcludePermissionType'] = $ExcludePermissionType
|
||||
$Splat['IncludePermissionType'] = $PermissionType
|
||||
$Splat['SkipWellKnown'] = $SkipWellKnown.IsPresent
|
||||
$Splat['SkipAdministrative'] = $SkipAdministrative.IsPresent
|
||||
@@ -64,150 +61,176 @@
|
||||
DomainAdmins = $false
|
||||
EnterpriseAdmins = $false
|
||||
}
|
||||
$CountFixed = 0
|
||||
|
||||
# This should always return results. When no data is found it should return basic information that will allow us to add credentials.
|
||||
[Array] $GPOPermissions = Get-GPOZaurrPermission @Splat -ReturnSecurityWhenNoData
|
||||
# When it has GPOSecurityPermissionItem property it means it has permissions, if it doesn't it means we have clean object to process
|
||||
if ($GPOPermissions.GPOSecurityPermissionItem) {
|
||||
# Permission exists, but may be incomplete
|
||||
foreach ($GPOPermission in $GPOPermissions) {
|
||||
if ($Type -eq 'Default') {
|
||||
# We were looking for specific principal and we got it. nothing to do
|
||||
# this is for standard users such as przemyslaw.klys / adam.gonzales
|
||||
return
|
||||
} elseif ($Type -eq 'Administrative') {
|
||||
# We are looking for administrative but we need to make sure we got correct administrative
|
||||
if ($GPOPermission.Permission -eq $PermissionType) {
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['BySID'][$GPOPermission.SID]
|
||||
if ($AdministrativeGroup.SID -like '*-519') {
|
||||
$AdministrativeExists['EnterpriseAdmins'] = $true
|
||||
} elseif ($AdministrativeGroup.SID -like '*-512') {
|
||||
$AdministrativeExists['DomainAdmins'] = $true
|
||||
Do {
|
||||
# This should always return results. When no data is found it should return basic information that will allow us to add credentials.
|
||||
Get-GPOZaurrPermission @Splat -ReturnSecurityWhenNoData -ReturnSingleObject | ForEach-Object {
|
||||
# Prepare data to clean
|
||||
$Skip = $false
|
||||
$AdministrativeExists['EnterpriseAdmins'] = $false
|
||||
$AdministrativeExists['DomainAdmins'] = $false
|
||||
$GPOPermissions = $_
|
||||
# Verification Phase
|
||||
# When it has GPOSecurityPermissionItem property it means it has permissions, if it doesn't it means we have clean object to process
|
||||
if ($GPOPermissions.GPOSecurityPermissionItem) {
|
||||
# Permission exists, but may be incomplete
|
||||
foreach ($GPOPermission in $GPOPermissions) {
|
||||
if ($Type -eq 'Default') {
|
||||
# We were looking for specific principal and we got it. nothing to do
|
||||
# this is for standard users such as przemyslaw.klys / adam.gonzales
|
||||
$Skip = $true
|
||||
break
|
||||
} elseif ($Type -eq 'Administrative') {
|
||||
# We are looking for administrative but we need to make sure we got correct administrative
|
||||
if ($GPOPermission.Permission -eq $PermissionType) {
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['BySID'][$GPOPermission.PrincipalSid]
|
||||
if ($AdministrativeGroup.SID -like '*-519') {
|
||||
$AdministrativeExists['EnterpriseAdmins'] = $true
|
||||
} elseif ($AdministrativeGroup.SID -like '*-512') {
|
||||
$AdministrativeExists['DomainAdmins'] = $true
|
||||
}
|
||||
}
|
||||
if ($AdministrativeExists['DomainAdmins'] -and $AdministrativeExists['EnterpriseAdmins']) {
|
||||
$Skip = $true
|
||||
break
|
||||
}
|
||||
} elseif ($Type -eq 'WellKnownAdministrative') {
|
||||
# this is for SYSTEM account
|
||||
$Skip = $true
|
||||
break
|
||||
} elseif ($Type -eq 'AuthenticatedUsers') {
|
||||
# this is for Authenticated Users
|
||||
$Skip = $true
|
||||
break
|
||||
}
|
||||
<#
|
||||
if ($AdministrativeGroup) {
|
||||
$DomainAdminsSID = -join ($ForestInformation['DomainsExtended'][$GPOPermission.DomainName].DomainSID, '-512')
|
||||
$EnterpriseAdminsSID = -join ($ForestInformation['DomainsExtended'][$GPOPermission.DomainName].DomainSID, '-519')
|
||||
if ($GPOPermission.SID -eq $DomainAdminsSID) {
|
||||
$AdministrativeExists['DomainAdmins'] = $true
|
||||
} elseif ($GPOPermission.SID -eq $EnterpriseAdminsSID) {
|
||||
$AdministrativeExists['EnterpriseAdmins'] = $true
|
||||
}
|
||||
}
|
||||
if (-not $Skip) {
|
||||
if (-not $GPOPermissions) {
|
||||
# This is bad - things went wrong
|
||||
Write-Warning "Add-GPOZaurrPermission - Couldn't get permissions for GPO. Things aren't what they should be. Skipping!"
|
||||
} else {
|
||||
$GPO = $GPOPermissions[0]
|
||||
if ($GPOPermissions.GPOSecurityPermissionItem) {
|
||||
# We asked, we got response, now we need to check if maybe we're missing one of the two administrative groups
|
||||
if ($Type -eq 'Administrative') {
|
||||
# this is a case where something was returned. Be it Domain Admins or Enterprise Admins or both. But we still need to check because it may have been Domain Admins from other domain or just one of the two required groups
|
||||
if ($AdministrativeExists['DomainAdmins'] -eq $false) {
|
||||
$Principal = $ADAdministrativeGroups[$GPO.DomainName]['DomainAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($AdministrativeExists['EnterpriseAdmins'] -eq $false) {
|
||||
$Principal = $ADAdministrativeGroups[$ForestInformation.Forest.RootDomain]['EnterpriseAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Default') {
|
||||
# This shouldn't really happen, as if we got response, and it didn't exists it wouldn't be here
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType skipped for $($Principal). This shouldn't even happen!"
|
||||
}
|
||||
} else {
|
||||
# We got no response. That means we either asked incorrectly or we need to fix permission. Trying to do so
|
||||
if ($Type -eq 'Default') {
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PrincipalType -eq 'DistinguishedName') {
|
||||
$ADIdentity = Get-WinADObject -Identity $Principal
|
||||
if ($ADIdentity) {
|
||||
Write-Verbose "Add-GPOZaurrPermission - Need to convert DN $Principal to SID $($ADIdentity.ObjectSID) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$Principal = $ADIdentity.ObjectSID
|
||||
}
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal)"
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Administrative') {
|
||||
# this is a case where both Domain Admins/Enterprise Admins were missing
|
||||
$Principal = $ADAdministrativeGroups[$GPO.DomainName]['DomainAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
$Principal = $ADAdministrativeGroups[$ForestInformation.Forest.RootDomain]['EnterpriseAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'WellKnownAdministrative') {
|
||||
$Principal = 'S-1-5-18'
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal (SYSTEM) / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) (SYSTEM) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'AuthenticatedUsers') {
|
||||
$Principal = 'S-1-5-11'
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
$CountFixed++
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal (Authenticated Users) / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) (Authenticated Users) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#>
|
||||
}
|
||||
} elseif ($Type -eq 'WellKnownAdministrative') {
|
||||
# this is for SYSTEM account
|
||||
return
|
||||
} elseif ($Type -eq 'AuthenticatedUsers') {
|
||||
# this is for Authenticated Users
|
||||
return
|
||||
}
|
||||
if ($CountFixed -ge $LimitProcessing) {
|
||||
# We want to exit foreach-object, but ForEach-Object doesn't really allow that
|
||||
# that's why there is Do/While which will make sure that breaks doesn't break what it's not supposed to
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $GPOPermissions) {
|
||||
# This is bad - things went wrong
|
||||
Write-Warning "Add-GPOZaurrPermission - Couldn't get permissions for GPO. Things aren't what they should be. Skipping!"
|
||||
} else {
|
||||
$GPO = $GPOPermissions[0]
|
||||
if ($GPOPermissions.GPOSecurityPermissionItem) {
|
||||
# We asked, we got response, now we need to check if maybe we're missing one of the two administrative groups
|
||||
if ($Type -eq 'Administrative') {
|
||||
# this is a case where something was returned. Be it Domain Admins or Enterprise Admins or both. But we still need to check because it may have been Domain Admins from other domain or just one of the two required groups
|
||||
if ($AdministrativeExists['DomainAdmins'] -eq $false) {
|
||||
$Principal = $ADAdministrativeGroups[$GPO.DomainName]['DomainAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($AdministrativeExists['EnterpriseAdmins'] -eq $false) {
|
||||
$Principal = $ADAdministrativeGroups[$ForestInformation.Forest.RootDomain]['EnterpriseAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Default') {
|
||||
# This shouldn't really happen, as if we got response, and it didn't exists it wouldn't be here
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType skipped for $($Principal). This shouldn't even happen!"
|
||||
}
|
||||
} else {
|
||||
# We got no response. That means we either asked incorrectly or we need to fix permission. Trying to do so
|
||||
if ($Type -eq 'Default') {
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal)"
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Administrative') {
|
||||
# this is a case where both Domain Admins/Enterprise Admins were missing
|
||||
$Principal = $ADAdministrativeGroups[$GPO.DomainName]['DomainAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
$Principal = $ADAdministrativeGroups[$ForestInformation.Forest.RootDomain]['EnterpriseAdmins']
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'WellKnownAdministrative') {
|
||||
$Principal = 'S-1-5-18'
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal (SYSTEM) / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) (SYSTEM) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'AuthenticatedUsers') {
|
||||
$Principal = 'S-1-5-11'
|
||||
Write-Verbose "Add-GPOZaurrPermission - Adding permission $PermissionType for $($Principal) to $($GPO.DisplayName) at $($GPO.DomainName)"
|
||||
if ($PSCmdlet.ShouldProcess($GPO.DisplayName, "Adding $Principal (Authenticated Users) / $PermissionType to $($GPO.DisplayName) at $($GPO.DomainName)")) {
|
||||
try {
|
||||
$AddPermission = [Microsoft.GroupPolicy.GPPermission]::new($Principal, $PermissionType, $Inheritable.IsPresent)
|
||||
$GPO.GPOSecurity.Add($AddPermission)
|
||||
$GPO.GPOObject.SetSecurityInfo($GPO.GPOSecurity)
|
||||
} catch {
|
||||
Write-Warning "Add-GPOZaurrPermission - Adding permission $PermissionType failed for $($Principal) (Authenticated Users) with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} while ($false)
|
||||
}
|
||||
@@ -69,7 +69,8 @@ function ConvertFrom-CSExtension {
|
||||
'{D76B9641-3288-4f75-942D-087DE603E3EA}' = 'AdmPwd'
|
||||
'{9650FDBC-053A-4715-AD14-FC2DC65E8330}' = 'Unknown'
|
||||
'{B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A}' = 'EFS Recovery'
|
||||
'{A2E30F80-D7DE-11d2-BBDE-00C04F86AE3B}' = 'Internet Explorer Settings'
|
||||
'{A2E30F80-D7DE-11d2-BBDE-00C04F86AE3B}' = 'Internet Explorer Maintenance Policy Processing'
|
||||
'{FC715823-C5FB-11D1-9EEF-00A0C90347FF}' = 'Internet Explorer Maintenance Extension Protocol' # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpie/f566a58a-4114-4981-b1e2-30b9d1a3c0e6
|
||||
}
|
||||
foreach ($C in $CSE) {
|
||||
if (-not $Limited) {
|
||||
|
||||
@@ -9,7 +9,7 @@ function Find-CSExtension {
|
||||
foreach ($Key in $Keys.PSSubKeys) {
|
||||
$RegistryKey = Get-PSRegistry -RegistryPath "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\$Key" -ComputerName $ComputerName
|
||||
if ($CSE) {
|
||||
foreach ($C in CSE) {
|
||||
foreach ($C in $CSE) {
|
||||
if ($RegistryKey.DefaultKey -eq $Key) {
|
||||
[PSCustomObject] @{ Name = $Key; Description = $RegistryKey.DefaultKey }
|
||||
}
|
||||
|
||||
+22
-10
@@ -13,7 +13,7 @@
|
||||
[switch] $PermissionsOnly,
|
||||
[switch] $OwnerOnly,
|
||||
[switch] $Limited,
|
||||
|
||||
[switch] $ReturnObject,
|
||||
[System.Collections.IDictionary] $ADAdministrativeGroups
|
||||
)
|
||||
Begin {
|
||||
@@ -29,9 +29,13 @@
|
||||
if (-not $GPOPath) {
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$QueryServer = $ForestInformation.QueryServers[$Domain]['HostName'][0]
|
||||
$Count = 0
|
||||
if ($GPOName) {
|
||||
Get-GPO -Name $GPOName -Domain $Domain -Server $QueryServer -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
|
||||
$GroupPolicies = Get-GPO -Name $GPOName -Domain $Domain -Server $QueryServer -ErrorAction SilentlyContinue
|
||||
$GroupPolicies | ForEach-Object {
|
||||
$Count++
|
||||
#Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
|
||||
Write-Verbose "Get-GPOZaurr - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)"
|
||||
if (-not $Limited) {
|
||||
try {
|
||||
$XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain -ErrorAction Stop
|
||||
@@ -39,14 +43,17 @@
|
||||
Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping."
|
||||
continue
|
||||
}
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent
|
||||
} else {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} elseif ($GPOGuid) {
|
||||
Get-GPO -Guid $GPOGuid -Domain $Domain -Server $QueryServer -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
|
||||
$GroupPolicies = Get-GPO -Guid $GPOGuid -Domain $Domain -Server $QueryServer -ErrorAction SilentlyContinue
|
||||
$GroupPolicies | ForEach-Object {
|
||||
$Count++
|
||||
#Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
|
||||
Write-Verbose "Get-GPOZaurr - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)"
|
||||
if (-not $Limited) {
|
||||
try {
|
||||
$XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain -ErrorAction Stop
|
||||
@@ -54,14 +61,17 @@
|
||||
Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping."
|
||||
continue
|
||||
}
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent
|
||||
} else {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Get-GPO -All -Server $QueryServer -Domain $Domain -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
|
||||
$GroupPolicies = Get-GPO -All -Server $QueryServer -Domain $Domain -ErrorAction SilentlyContinue
|
||||
$GroupPolicies | ForEach-Object {
|
||||
$Count++
|
||||
#Write-Verbose "Get-GPOZaurr - Getting GPO $($_.DisplayName) / ID: $($_.ID) from $Domain"
|
||||
Write-Verbose "Get-GPOZaurr - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)"
|
||||
if (-not $Limited) {
|
||||
try {
|
||||
$XMLContent = Get-GPOReport -ID $_.ID -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain -ErrorAction Stop
|
||||
@@ -69,7 +79,7 @@
|
||||
Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping."
|
||||
continue
|
||||
}
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent
|
||||
} else {
|
||||
$_
|
||||
}
|
||||
@@ -78,11 +88,13 @@
|
||||
}
|
||||
} else {
|
||||
foreach ($Path in $GPOPath) {
|
||||
Write-Verbose "Get-GPOZaurr - Getting GPO content from XML files"
|
||||
Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml | ForEach-Object {
|
||||
$XMLContent = [XML]::new()
|
||||
$XMLContent.Load($_.FullName)
|
||||
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -PermissionsOnly:$PermissionsOnly.IsPresent
|
||||
}
|
||||
Write-Verbose "Get-GPOZaurr - Finished GPO content from XML files"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
function Get-GPOZaurrBroken {
|
||||
[alias('Get-GPOZaurrSysvol')]
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[string[]] $ExcludeDomainControllers,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[alias('DomainControllers')][string[]] $IncludeDomainControllers,
|
||||
[switch] $SkipRODC,
|
||||
[Array] $GPOs,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[switch] $VerifyDomainControllers
|
||||
)
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation -Extended
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$TimeLog = Start-TimeLog
|
||||
Write-Verbose "Get-GPOZaurrBroken - Starting process for $Domain"
|
||||
$QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0]
|
||||
$SystemsContainer = $ForestInformation['DomainsExtended'][$Domain].SystemsContainer
|
||||
$PoliciesAD = @{}
|
||||
if ($SystemsContainer) {
|
||||
$PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer)
|
||||
$PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer
|
||||
foreach ($Policy in $PoliciesInAD) {
|
||||
$GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName
|
||||
$GUIDFromDN = $GUIDFromDN -replace '{' -replace '}'
|
||||
$GUID = $Policy.Name -replace '{' -replace '}'
|
||||
if ($GUID -and $GUIDFromDN) {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Exists'
|
||||
} else {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Permissions issue'
|
||||
}
|
||||
}
|
||||
}
|
||||
Try {
|
||||
[Array]$GPOs = Get-GPO -All -Domain $Domain -Server $QueryServer
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrBroken - Couldn't get GPOs from $Domain. Error: $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
if ($GPOs.Count -ge 2) {
|
||||
if (-not $VerifyDomainControllers) {
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Domain -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
} else {
|
||||
foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) {
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing $Domain \ $($Server.HostName.Trim())"
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Server.Hostname -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Get-GPOZaurrBroken - GPO count for $Domain is less then 2. This is not expected for fully functioning domain. Skipping processing SYSVOL folder."
|
||||
}
|
||||
$TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner
|
||||
Write-Verbose "Get-GPOZaurrBroken - Finishing process for $Domain (Time to process: $TimeEnd)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
function Get-GPOZaurrDuplicateObject {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
|
||||
$getWinADDuplicateObjectSplat = @{
|
||||
Forest = $Forest
|
||||
IncludeDomains = $IncludeDomains
|
||||
ExcludeDomains = $ExcludeDomains
|
||||
ExtendedForestInformation = $ExtendedForestInformation
|
||||
PartialMatchDistinguishedName = "*,CN=Policies,CN=System,DC=*"
|
||||
Extended = $true
|
||||
}
|
||||
|
||||
$DuplicateObjects = Get-WinADDuplicateObject @getWinADDuplicateObjectSplat
|
||||
$DuplicateObjects
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
[ValidateSet('All', 'Netlogon', 'Sysvol')][string[]] $Type = 'All',
|
||||
[ValidateSet('None', 'MACTripleDES', 'MD5', 'RIPEMD160', 'SHA1', 'SHA256', 'SHA384', 'SHA512')][string] $HashAlgorithm = 'None',
|
||||
[switch] $Signature,
|
||||
[switch] $Limited,
|
||||
[switch] $AsHashTable,
|
||||
[switch] $Extended,
|
||||
[switch] $ExtendedMetaData,
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
@@ -265,7 +265,7 @@
|
||||
$SuggestedAction = 'Requires verification'
|
||||
$SuggestedActionComment = 'Not able to auto asses'
|
||||
}
|
||||
if ($Limited) {
|
||||
if (-not $ExtendedMetaData) {
|
||||
$MetaData = [ordered] @{
|
||||
LocationType = $FileType.Name
|
||||
FullName = $_.FullName
|
||||
@@ -282,18 +282,39 @@
|
||||
}
|
||||
} else {
|
||||
$MetaData = Get-FileMetaData -File $_ -AsHashTable
|
||||
$MetaData['SuggestedAction'] = $SuggestedAction
|
||||
$MetaData['SuggestedActionComment'] = $SuggestedActionComment
|
||||
$MetaData['BelongsToGPO'] = $BelongsToGPO
|
||||
$MetaData['GPODisplayName'] = $GPODisplayName
|
||||
}
|
||||
if ($Signature) {
|
||||
$DigitalSignature = Get-AuthenticodeSignature -FilePath $_.Fullname
|
||||
$MetaData['SignatureStatus'] = $DigitalSignature.Status
|
||||
$MetaData['IsOSBinary'] = $DigitalSignature.IsOSBinary
|
||||
$MetaData['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject
|
||||
if ($Extended) {
|
||||
$MetaData['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer
|
||||
$MetaData['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber
|
||||
$MetaData['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore
|
||||
$MetaData['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter
|
||||
$MetaData['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint
|
||||
try {
|
||||
$DigitalSignature = Get-AuthenticodeSignature -FilePath $_.Fullname -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrFiles - Error when reading signature: $($_.Exception.Message)"
|
||||
}
|
||||
if ($DigitalSignature) {
|
||||
$MetaData['SignatureStatus'] = $DigitalSignature.Status
|
||||
$MetaData['IsOSBinary'] = $DigitalSignature.IsOSBinary
|
||||
$MetaData['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject
|
||||
if ($Extended) {
|
||||
$MetaData['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer
|
||||
$MetaData['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber
|
||||
$MetaData['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore
|
||||
$MetaData['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter
|
||||
$MetaData['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint
|
||||
}
|
||||
} else {
|
||||
$MetaData['SignatureStatus'] = 'Not available'
|
||||
$MetaData['IsOSBinary'] = $null
|
||||
$MetaData['SignatureCertificateSubject'] = $null
|
||||
if ($Extended) {
|
||||
$MetaData['SignatureCertificateIssuer'] = $null
|
||||
$MetaData['SignatureCertificateSerialNumber'] = $null
|
||||
$MetaData['SignatureCertificateNotBefore'] = $null
|
||||
$MetaData['SignatureCertificateNotAfter'] = $null
|
||||
$MetaData['SignatureCertificateThumbprint'] = $null
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($HashAlgorithm -ne 'None') {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
$OrganizationalUnits = Get-ADOrganizationalUnit -Filter * -Properties gpOptions, canonicalName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0]
|
||||
foreach ($OU in $OrganizationalUnits) {
|
||||
$InheritanceInformation = [Ordered] @{
|
||||
DistinguishedName = $OU.DistinguishedName
|
||||
CanonicalName = $OU.canonicalName
|
||||
BlockedInheritance = if ($OU.gpOptions -eq 1) { $true } else { $false }
|
||||
}
|
||||
@@ -34,8 +33,8 @@
|
||||
if ($InheritanceInformation.BlockedInheritance -eq $true) {
|
||||
$InheritanceInformation['UsersCount'] = $null
|
||||
$InheritanceInformation['ComputersCount'] = $null
|
||||
$InheritanceInformation['Users'] = Get-ADUser -SearchBase $InheritanceInformation.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] -Filter *
|
||||
$InheritanceInformation['Computers'] = Get-ADComputer -SearchBase $InheritanceInformation.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] -Filter *
|
||||
[Array] $InheritanceInformation['Users'] = (Get-ADUser -SearchBase $OU.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] -Filter *).SamAccountName
|
||||
[Array] $InheritanceInformation['Computers'] = (Get-ADComputer -SearchBase $OU.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['HostName'][0] -Filter *).SamAccountName
|
||||
$InheritanceInformation['UsersCount'] = $InheritanceInformation['Users'].Count
|
||||
$InheritanceInformation['ComputersCount'] = $InheritanceInformation['Computers'].Count
|
||||
} else {
|
||||
@@ -53,6 +52,7 @@
|
||||
[PSCustomObject] $InheritanceInformation
|
||||
}
|
||||
}
|
||||
$InheritanceInformation['DistinguishedName'] = $OU.DistinguishedName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
}
|
||||
foreach ($e in $err) {
|
||||
Write-Warning "Get-GPOZaurrLegacy - $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
|
||||
Write-Warning "Get-GPOZaurrLegacyFiles - $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
function Get-GPOZaurrNetLogon {
|
||||
[cmdletBinding(DefaultParameterSetName = 'Default')]
|
||||
param(
|
||||
[parameter(ParameterSetName = 'OwnerOnly')][switch] $OwnerOnly,
|
||||
[parameter(ParameterSetName = 'SkipOwner')][switch] $SkipOwner,
|
||||
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
$ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
$FilesAll = foreach ($Domain in $ForestInformation.Domains) {
|
||||
$Path = -join ("\\", $Domain, '\Netlogon')
|
||||
$PathOnSysvol = -join ("\\", $Domain, "\SYSVOL\", $Domain, "\Scripts")
|
||||
$Files = Get-ChildItem -LiteralPath $Path -Recurse -Force -ErrorVariable Err -ErrorAction SilentlyContinue
|
||||
foreach ($e in $err) {
|
||||
Write-Warning "Get-GPOZaurrNetLogon - Listing file failed with error $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
|
||||
}
|
||||
foreach ($File in $Files) {
|
||||
try {
|
||||
$ACL = Get-Acl -Path $File.FullName -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrNetLogon - ACL reading failed for $($File.FullName) with error $($_.Exception.Message) ($($_.CategoryInfo.Reason))"
|
||||
}
|
||||
if ($ACL.Owner) {
|
||||
$IdentityOwner = Convert-Identity -Identity $ACL.Owner -Verbose:$false
|
||||
} else {
|
||||
$IdentityOwner = [PSCustomObject] @{ SID = ''; Type = 'Uknown' }
|
||||
}
|
||||
if (-not $OwnerOnly) {
|
||||
if (-not $SkipOwner) {
|
||||
[PSCustomObject] @{
|
||||
FullName = $File.FullName
|
||||
Extension = $File.Extension
|
||||
CreationTime = $File.CreationTime
|
||||
LastAccessTime = $File.LastAccessTime
|
||||
LastWriteTime = $File.LastWriteTime
|
||||
Attributes = $File.Attributes
|
||||
AccessControlType = 'Allow' # : Allow
|
||||
Principal = $IdentityOwner.Name # : BUILTIN\Administrators
|
||||
PrincipalSid = $IdentityOwner.SID
|
||||
PrincipalType = $IdentityOwner.Type
|
||||
FileSystemRights = 'Owner' # : FullControl
|
||||
IsInherited = $false
|
||||
FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol)
|
||||
#Owner = $ACL.Owner
|
||||
}
|
||||
}
|
||||
$FilePermission = Get-FilePermissions -Path $_.FullName -ACLS $ACL -Verbose:$false
|
||||
foreach ($Perm in $FilePermission) {
|
||||
$Identity = Convert-Identity -Identity $Perm.Principal -Verbose:$false
|
||||
[PSCustomObject] @{
|
||||
FullName = $File.FullName
|
||||
Extension = $File.Extension
|
||||
CreationTime = $File.CreationTime
|
||||
LastAccessTime = $File.LastAccessTime
|
||||
LastWriteTime = $File.LastWriteTime
|
||||
Attributes = $File.Attributes
|
||||
AccessControlType = $Perm.AccessControlType # : Allow
|
||||
Principal = $Identity.Name # : BUILTIN\Administrators
|
||||
PrincipalSid = $Identity.SID
|
||||
PrincipalType = $Identity.Type
|
||||
FileSystemRights = $Perm.FileSystemRights # : FullControl
|
||||
IsInherited = $Perm.IsInherited # : True
|
||||
FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[PSCustomObject] @{
|
||||
FullName = $File.FullName
|
||||
Extension = $File.Extension
|
||||
CreationTime = $File.CreationTime
|
||||
LastAccessTime = $File.LastAccessTime
|
||||
LastWriteTime = $File.LastWriteTime
|
||||
Attributes = $File.Attributes
|
||||
Owner = $IdentityOwner.Name
|
||||
OwnerSid = $IdentityOwner.SID
|
||||
OwnerType = $IdentityOwner.Type
|
||||
FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$FilesAll
|
||||
}
|
||||
@@ -1,10 +1,54 @@
|
||||
function Get-GPOZaurrOwner {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Gets owners of GPOs from Active Directory and SYSVOL
|
||||
|
||||
.DESCRIPTION
|
||||
Gets owners of GPOs from Active Directory and SYSVOL
|
||||
|
||||
.PARAMETER GPOName
|
||||
Name of GPO. By default all GPOs are returned
|
||||
|
||||
.PARAMETER GPOGuid
|
||||
GUID of GPO. By default all GPOs are returned
|
||||
|
||||
.PARAMETER IncludeSysvol
|
||||
Includes Owner from SYSVOL as well
|
||||
|
||||
.PARAMETER SkipBroken
|
||||
Doesn't display GPOs that have no SYSVOL content (orphaned GPOs)
|
||||
|
||||
.PARAMETER Forest
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Exclude domain from search, by default whole forest is scanned
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Include only specific domains, by default whole forest is scanned
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Ability to provide Forest Information from another command to speed up processing
|
||||
|
||||
.PARAMETER ADAdministrativeGroups
|
||||
Ability to provide AD Administrative Groups from another command to speed up processing
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrOwner -Verbose -IncludeSysvol
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrOwner -Verbose -IncludeSysvol -SkipBroken
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[cmdletbinding(DefaultParameterSetName = 'Default')]
|
||||
param(
|
||||
[Parameter(ParameterSetName = 'GPOName')][string] $GPOName,
|
||||
[Parameter(ParameterSetName = 'GPOGUID')][alias('GUID', 'GPOID')][string] $GPOGuid,
|
||||
|
||||
[switch] $IncludeSysvol,
|
||||
[switch] $SkipBroken,
|
||||
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
@@ -17,6 +61,7 @@
|
||||
if (-not $ADAdministrativeGroups) {
|
||||
$ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
}
|
||||
$Count = 0
|
||||
}
|
||||
Process {
|
||||
$getGPOZaurrADSplat = @{
|
||||
@@ -32,8 +77,9 @@
|
||||
}
|
||||
$Objects = Get-GPOZaurrAD @getGPOZaurrADSplat
|
||||
foreach ($_ in $Objects) {
|
||||
Write-Verbose "Get-GPOZaurrOwner - Processing GPO: $($_.DisplayName) from domain: $($_.DomainName)"
|
||||
$ACL = Get-ADACLOwner -ADObject $_.GPODistinguishedName -Resolve -ADAdministrativeGroups $ADAdministrativeGroups
|
||||
$Count++
|
||||
Write-Verbose "Get-GPOZaurrOwner - Processing GPO [$Count/$($Objects.Count)]: $($_.DisplayName) from domain: $($_.DomainName)"
|
||||
$ACL = Get-ADACLOwner -ADObject $_.GPODistinguishedName -Resolve -ADAdministrativeGroups $ADAdministrativeGroups -Verbose:$false
|
||||
$Object = [ordered] @{
|
||||
DisplayName = $_.DisplayName
|
||||
DomainName = $_.DomainName
|
||||
@@ -43,12 +89,23 @@
|
||||
OwnerType = $ACL.OwnerType
|
||||
}
|
||||
if ($IncludeSysvol) {
|
||||
$FileOwner = Get-FileOwner -JustPath -Path $_.Path -Resolve
|
||||
$FileOwner = Get-FileOwner -JustPath -Path $_.Path -Resolve -Verbose:$false
|
||||
$Object['SysvolOwner'] = $FileOwner.OwnerName
|
||||
$Object['SysvolSid'] = $FileOwner.OwnerSid
|
||||
$Object['SysvolType'] = $FileOwner.OwnerType
|
||||
$Object['SysvolPath'] = $_.Path
|
||||
$Object['IsOwnerConsistent'] = if ($ACL.OwnerName -eq $FileOwner.OwnerName) { $true } else { $false }
|
||||
$Object['IsOwnerAdministrative'] = if ($Object['SysvolType'] -eq 'Administrative' -and $Object['OwnerType'] -eq 'Administrative') { $true } else { $false }
|
||||
if (Test-Path -LiteralPath $Object['SysvolPath']) {
|
||||
$Object['SysvolExists'] = $true
|
||||
} else {
|
||||
$Object['SysvolExists'] = $false
|
||||
}
|
||||
} else {
|
||||
$Object['IsOwnerAdministrative'] = if ($Object['OwnerType'] -eq 'Administrative') { $true } else { $false }
|
||||
}
|
||||
if ($SkipBroken -and $Object['SysvolExists'] -eq $false) {
|
||||
continue
|
||||
}
|
||||
$Object['DistinguishedName'] = $_.GPODistinguishedName
|
||||
[PSCUstomObject] $Object
|
||||
|
||||
+84
-103
@@ -1,4 +1,35 @@
|
||||
function Get-GPOZaurrPassword {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Tries to find CPassword in Group Policies or given path and translate it to readable value
|
||||
|
||||
.DESCRIPTION
|
||||
Tries to find CPassword in Group Policies or given path and translate it to readable value
|
||||
|
||||
.PARAMETER Forest
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Exclude domain from search, by default whole forest is scanned
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Include only specific domains, by default whole forest is scanned
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Ability to provide Forest Information from another command to speed up processing
|
||||
|
||||
.PARAMETER GPOPath
|
||||
Path where Group Policy content is located or where backup is located
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrPassword -GPOPath 'C:\Users\przemyslaw.klys\Desktop\GPOExport_2020.10.12'
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrPassword
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
@@ -7,115 +38,65 @@
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[string[]] $GPOPath
|
||||
)
|
||||
if (-not $GPOPath) {
|
||||
if (-not $ExtendedForestInformation) {
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
} else {
|
||||
$ForestInformation = $ExtendedForestInformation
|
||||
}
|
||||
|
||||
[Array] $GPOPath = foreach ($Domain in $ForestInformation.Domains) {
|
||||
-join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies')
|
||||
}
|
||||
}
|
||||
if (-not $GPOPath) {
|
||||
return
|
||||
}
|
||||
foreach ($Path in $GPOPath) {
|
||||
#Extract the all XML files in the Folders
|
||||
$Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml
|
||||
$Output = foreach ($XMLFileName in $Items) {
|
||||
#Convert XML in a String file
|
||||
[string]$XMLString = Get-Content ($XMLFileName.FullName)
|
||||
#Check if Cpassword Exist in the file
|
||||
if ($XMLString.Contains("cpassword")) {
|
||||
#Take the Cpassword Value from XML String file
|
||||
[string]$Cpassword = [regex]::matches($XMLString, '(cpassword=).+?(?=\")')
|
||||
$Cpassword = $Cpassword.split('(\")')[1]
|
||||
#Check if Cpassword has a value
|
||||
if ($Cpassword.Length -gt 20 -and $Cpassword -notlike '*cpassword*') {
|
||||
$Mod = ($Cpassword.length % 4)
|
||||
switch ($Mod) {
|
||||
'1' { $Cpassword = $Cpassword.Substring(0, $Cpassword.Length - 1) }
|
||||
'2' { $Cpassword += ('=' * (4 - $Mod)) }
|
||||
'3' { $Cpassword += ('=' * (4 - $Mod)) }
|
||||
}
|
||||
$Base64Decoded = [Convert]::FromBase64String($Cpassword)
|
||||
$AesObject = [System.Security.Cryptography.AesCryptoServiceProvider]::new()
|
||||
#Use th AES Key
|
||||
[Byte[]] $AesKey = @(0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b)
|
||||
$AesIV = New-Object Byte[]($AesObject.IV.Length)
|
||||
$AesObject.IV = $AesIV
|
||||
$AesObject.Key = $AesKey
|
||||
$DecryptorObject = $AesObject.CreateDecryptor()
|
||||
[Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
|
||||
#Convert Hash variable in a String valute
|
||||
$Password = [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
|
||||
} else {
|
||||
$Password = ''
|
||||
}
|
||||
#[string]$GPOguid = [regex]::matches($XMLFileName.DirectoryName, '(?<=\{).+?(?=\})')
|
||||
#$GPODetail = Get-GPO -guid $GPOguid
|
||||
[xml] $XMLContent = $XMLString
|
||||
|
||||
#if (-not $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword -and -not $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword) {
|
||||
#Write-Host ''
|
||||
#}
|
||||
if ($GPOPath) {
|
||||
foreach ($Path in $GPOPath) {
|
||||
$Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml -ErrorAction SilentlyContinue -ErrorVariable err
|
||||
$Output = foreach ($XMLFileName in $Items) {
|
||||
$Password = Find-GPOPassword -Path $XMLFileName.FullName
|
||||
if ($Password) {
|
||||
$PasswordStatus = $true
|
||||
} else {
|
||||
$PasswordStatus = $false
|
||||
if ($XMLFileName.FullName -match '{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}') {
|
||||
$GPOGUID = $matches[0]
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
RootPath = $Path
|
||||
PasswordFile = $XMLFileName.FullName
|
||||
GUID = $GPOGUID
|
||||
Password = $Password
|
||||
}
|
||||
}
|
||||
|
||||
[PsCustomObject] @{
|
||||
'Name' = $XMLContent.GPO.Name
|
||||
'Links' = $XMLContent.GPO.LinksTo #| Select-Object -ExpandProperty SOMPath
|
||||
'Enabled' = $XMLContent.GPO.GpoStatus
|
||||
'PasswordStatus' = $PasswordStatus
|
||||
#'GPO' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups
|
||||
'User' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.name
|
||||
'Cpassword' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword
|
||||
'CpasswordMap' = $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword
|
||||
'Password' = $Password
|
||||
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText
|
||||
|
||||
'Domain' = $XMLContent.GPO.Identifier.Domain
|
||||
|
||||
'ComputerSettingsAvailable' = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
|
||||
'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
|
||||
'ComputerEnabled' = [bool] $XMLContent.GPO.Computer.Enabled
|
||||
'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false }
|
||||
'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension
|
||||
|
||||
'UserSettingsAvailable' = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
|
||||
'UserEnabled' = [bool] $XMLContent.GPO.User.Enabled
|
||||
'UserSettingsStatus' = if ($XMLContent.GPO.User.VersionDirectory -eq 0 -and $XMLContent.GPO.User.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
|
||||
'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false }
|
||||
'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension
|
||||
|
||||
|
||||
'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime
|
||||
'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime
|
||||
'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime
|
||||
|
||||
'WMIFilter' = $GPO.WmiFilter.name
|
||||
'WMIFilterDescription' = $GPO.WmiFilter.Description
|
||||
'Path' = $GPO.Path
|
||||
#'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' }
|
||||
'ACL' = $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$Path = -join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies')
|
||||
#Extract the all XML files in the Folder
|
||||
$Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml -ErrorAction SilentlyContinue -ErrorVariable err
|
||||
$Output = foreach ($XMLFileName in $Items) {
|
||||
$Password = Find-GPOPassword -Path $XMLFileName.FullName
|
||||
if ($Password) {
|
||||
# match regex
|
||||
if ($XMLFileName.FullName -match '{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}') {
|
||||
$GPOGUID = $matches[0]
|
||||
$GPO = Get-GPOZaurrAD -GPOGuid $GPOGUID -IncludeDomains $Domain
|
||||
[PSCustomObject] @{
|
||||
'User' = $_.trustee.name.'#Text'
|
||||
'Permission Type' = $_.type.PermissionType
|
||||
'Inherited' = $_.Inherited
|
||||
'Permissions' = $_.Standard.GPOGroupedAccessEnum
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
PasswordFile = $XMLFileName.FullName
|
||||
Password = $Password
|
||||
Created = $GPO.Created
|
||||
Modified = $GPO.Modified
|
||||
Description = $GPO.Description
|
||||
}
|
||||
} else {
|
||||
[PSCustomObject] @{
|
||||
DisplayName = ''
|
||||
DomainName = ''
|
||||
GUID = ''
|
||||
PasswordFile = $XMLFileName.FullName
|
||||
Password = $Password
|
||||
Created = ''
|
||||
Modified = ''
|
||||
Description = ''
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#Write-Host "I find a Password [ " $Password " ] The GPO named:" $GPODetail" and th file is:" $XMLFileName
|
||||
|
||||
} #if($XMLContent.Contains("cpassword")
|
||||
}
|
||||
foreach ($e in $err) {
|
||||
Write-Warning "Get-GPOZaurrPassword - $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
|
||||
}
|
||||
$Output
|
||||
}
|
||||
$Output
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,13 @@
|
||||
[alias('GUID', 'GPOID')][string] $GPOGuid,
|
||||
|
||||
[string[]] $Principal,
|
||||
[validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'Sid',
|
||||
[validateset('DistinguishedName', 'Name', 'NetbiosName', 'Sid')][string] $PrincipalType = 'Sid',
|
||||
|
||||
[validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All',
|
||||
|
||||
[switch] $SkipWellKnown,
|
||||
[switch] $SkipAdministrative,
|
||||
[switch] $ResolveAccounts,
|
||||
#[switch] $ResolveAccounts,
|
||||
|
||||
[switch] $IncludeOwner,
|
||||
[Microsoft.GroupPolicy.GPPermissionType[]] $IncludePermissionType,
|
||||
@@ -31,7 +31,8 @@
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[System.Collections.IDictionary] $ADAdministrativeGroups,
|
||||
[switch] $ReturnSecurityWhenNoData # if no data return all data
|
||||
[switch] $ReturnSecurityWhenNoData, # if no data return all data
|
||||
[switch] $ReturnSingleObject # forces return of single object per GPO as one for ForEach-Object processing
|
||||
)
|
||||
Begin {
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Extended
|
||||
@@ -43,6 +44,7 @@
|
||||
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) {
|
||||
@@ -60,6 +62,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
#>
|
||||
}
|
||||
Process {
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
@@ -96,7 +99,7 @@
|
||||
Principal = $Principal
|
||||
PrincipalType = $PrincipalType
|
||||
PermitType = $PermitType
|
||||
Accounts = $Accounts
|
||||
#Accounts = $Accounts
|
||||
Type = $Type
|
||||
GPO = $_
|
||||
SkipWellKnown = $SkipWellKnown.IsPresent
|
||||
@@ -134,7 +137,11 @@
|
||||
$ReturnObject
|
||||
}
|
||||
} else {
|
||||
$Output
|
||||
if ($ReturnSingleObject) {
|
||||
, $Output
|
||||
} else {
|
||||
$Output
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
}
|
||||
Process {
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$TimeLog = Start-TimeLog
|
||||
Write-Verbose "Get-GPOZaurrPermissionConsistency - Starting process for $Domain"
|
||||
$QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0]
|
||||
if ($GPOName) {
|
||||
$getGPOSplat = @{
|
||||
@@ -39,30 +41,49 @@
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
}
|
||||
Get-GPO @getGPOSplat | ForEach-Object -Process {
|
||||
try {
|
||||
$IsConsistent = $_.IsAclConsistent()
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Warning "Get-GPOZaurrPermissionConsistency - Failed to get consistency: $($_.Exception.Message)."
|
||||
$IsConsistent = 'Not available.'
|
||||
}
|
||||
$SysVolpath = -join ('\\', $Domain, '\sysvol\', $Domain, '\Policies\{', $_.ID.GUID, '}')
|
||||
if ($VerifyInheritance) {
|
||||
$FolderPermissions = Get-WinADSharePermission -Path $SysVolpath
|
||||
[Array] $NotInheritedPermissions = foreach ($File in $FolderPermissions) {
|
||||
if ($File.Path -ne $SysVolpath -and $File.IsInherited -eq $false) {
|
||||
$File
|
||||
}
|
||||
}
|
||||
if ($NotInheritedPermissions.Count -eq 0) {
|
||||
$ACLConsistentInside = $true
|
||||
} else {
|
||||
$ACLConsistentInside = $false
|
||||
$GroupPolicies = Get-GPO @getGPOSplat
|
||||
$Count = 0
|
||||
$GroupPolicies | ForEach-Object -Process {
|
||||
$GPO = $_
|
||||
$Count++
|
||||
Write-Verbose "Get-GPOZaurrPermissionConsistency - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)"
|
||||
$SysVolpath = -join ('\\', $Domain, '\sysvol\', $Domain, '\Policies\{', $GPO.ID.GUID, '}')
|
||||
if (Test-Path -LiteralPath $SysVolpath) {
|
||||
try {
|
||||
$IsConsistent = $GPO.IsAclConsistent()
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Warning "Get-GPOZaurrPermissionConsistency - Processing $($GPO.DisplayName) / $($GPO.DomainName) failed to get consistency with error: $($_.Exception.Message)."
|
||||
$IsConsistent = 'Not available'
|
||||
}
|
||||
} else {
|
||||
$ACLConsistentInside = $null
|
||||
Write-Warning "Get-GPOZaurrPermissionConsistency - Processing $($GPO.DisplayName) / $($GPO.DomainName) failed as path $SysvolPath doesn't exists!"
|
||||
$IsConsistent = $false
|
||||
}
|
||||
if ($VerifyInheritance) {
|
||||
if ($IsConsistent -eq $true) {
|
||||
$FolderPermissions = Get-WinADSharePermission -Path $SysVolpath -Verbose:$false
|
||||
if ($FolderPermissions) {
|
||||
[Array] $NotInheritedPermissions = foreach ($File in $FolderPermissions) {
|
||||
if ($File.Path -ne $SysVolpath -and $File.IsInherited -eq $false) {
|
||||
$File
|
||||
}
|
||||
}
|
||||
if ($NotInheritedPermissions.Count -eq 0) {
|
||||
$ACLConsistentInside = $true
|
||||
} else {
|
||||
$ACLConsistentInside = $false
|
||||
}
|
||||
} else {
|
||||
$ACLConsistentInside = 'Not available'
|
||||
$NotInheritedPermissions = $null
|
||||
}
|
||||
} else {
|
||||
# Since top level permissions are inconsistent we don't even try to asses inside permissions
|
||||
$ACLConsistentInside = $IsConsistent
|
||||
$NotInheritedPermissions = $null
|
||||
}
|
||||
}
|
||||
$Object = [ordered] @{
|
||||
DisplayName = $_.DisplayName # : New Group Policy Object
|
||||
@@ -94,26 +115,28 @@
|
||||
[PSCustomObject] $Object
|
||||
} elseif ($Type -eq 'Inconsistent') {
|
||||
if ($VerifyInheritance) {
|
||||
if (-not $IsConsistent -or -not $ACLConsistentInside) {
|
||||
if (-not ($IsConsistent -eq $true) -or (-not $ACLConsistentInside -eq $true)) {
|
||||
[PSCustomObject] $Object
|
||||
}
|
||||
} else {
|
||||
if (-not $IsConsistent) {
|
||||
if (-not ($IsConsistent -eq $true)) {
|
||||
[PSCustomObject] $Object
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Consistent') {
|
||||
if ($VerifyInheritance) {
|
||||
if ($IsConsistent -and $ACLConsistentInside) {
|
||||
if ($IsConsistent -eq $true -and $ACLConsistentInside -eq $true) {
|
||||
[PSCustomObject] $Object
|
||||
}
|
||||
} else {
|
||||
if ($IsConsistent) {
|
||||
if ($IsConsistent -eq $true) {
|
||||
[PSCustomObject] $Object
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner
|
||||
Write-Verbose "Get-GPOZaurrPermissionConsistency - Finishing process for $Domain (Time to process: $TimeEnd)"
|
||||
}
|
||||
}
|
||||
End {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
function Get-GPOZaurrPermissionIssue {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Detects Group Policy missing Authenticated Users permission while not having higher permissions.
|
||||
|
||||
.DESCRIPTION
|
||||
Detects Group Policy missing Authenticated Users permission while not having higher permissions.
|
||||
|
||||
.PARAMETER Forest
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Exclude domain from search, by default whole forest is scanned
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Include only specific domains, by default whole forest is scanned
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Ability to provide Forest Information from another command to speed up processing
|
||||
|
||||
.EXAMPLE
|
||||
$Issues = Get-GPOZaurrPermissionIssue
|
||||
$Issues | Format-Table
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation -Extended
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$TimeLog = Start-TimeLog
|
||||
Write-Verbose "Get-GPOZaurrPermissionIssue - Starting process for $Domain"
|
||||
$QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0]
|
||||
$SystemsContainer = $ForestInformation['DomainsExtended'][$Domain].SystemsContainer
|
||||
if ($SystemsContainer) {
|
||||
$PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer)
|
||||
$Properties = 'DisplayName', 'Name', 'DistinguishedName', 'ObjectClass', 'WhenCreated', 'WhenChanged'
|
||||
$PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer -Properties $Properties
|
||||
foreach ($Policy in $PoliciesInAD) {
|
||||
$GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName
|
||||
$GUIDFromDN = $GUIDFromDN -replace '{' -replace '}'
|
||||
$GUID = $Policy.Name -replace '{' -replace '}'
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $Policy.DisplayName
|
||||
DomainName = $Domain
|
||||
PermissionIssue = -not ($GUID -and $GUIDFromDN)
|
||||
ObjectClass = $Policy.ObjectClass
|
||||
Name = $Policy.Name
|
||||
DistinguishedName = $Policy.DistinguishedName
|
||||
GUID = $GUIDFromDN
|
||||
WhenCreated = $Policy.WhenCreated
|
||||
WhenChanged = $Policy.WhenChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
$TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner
|
||||
Write-Verbose "Get-GPOZaurrPermissionIssue - Finishing process for $Domain (Time to process: $TimeEnd)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
function Get-GPOZaurrPermissionRoot {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('GpoRootCreate', 'GpoRootOwner')][string[]] $IncludePermissionType,
|
||||
[ValidateSet('GpoRootCreate', 'GpoRootOwner')][string[]] $ExcludePermissionType,
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[switch] $SkipNames
|
||||
)
|
||||
Begin {
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Extended
|
||||
}
|
||||
Process {
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
$DomainDistinguishedName = $ForestInformation['DomainsExtended'][$Domain].DistinguishedName
|
||||
$QueryServer = $ForestInformation['QueryServers'][$Domain].HostName[0]
|
||||
$getADACLSplat = @{
|
||||
ADObject = "CN=Policies,CN=System,$DomainDistinguishedName"
|
||||
IncludeActiveDirectoryRights = 'GenericAll', 'CreateChild', 'WriteOwner', 'WriteDACL'
|
||||
IncludeObjectTypeName = 'All', 'Group-Policy-Container'
|
||||
IncludeInheritedObjectTypeName = 'All', 'Group-Policy-Container'
|
||||
ADRightsAsArray = $true
|
||||
ResolveTypes = $true
|
||||
}
|
||||
$GPOPermissionsGlobal = Get-ADACL @getADACLSplat -Verbose:$false
|
||||
$GPOs = Get-ADObject -SearchBase "CN=Policies,CN=System,$DomainDistinguishedName" -SearchScope OneLevel -Filter * -Properties DisplayName -Server $QueryServer -Verbose:$false
|
||||
foreach ($Permission in $GPOPermissionsGlobal) {
|
||||
$CustomPermission = foreach ($_ in $Permission.ActiveDirectoryRights) {
|
||||
if ($_ -in 'WriteDACL', 'WriteOwner', 'GenericAll' ) {
|
||||
'GpoRootOwner'
|
||||
}
|
||||
if ($_ -in 'CreateChild', 'GenericAll') {
|
||||
'GpoRootCreate'
|
||||
}
|
||||
}
|
||||
$CustomPermission = $CustomPermission | Sort-Object -Unique
|
||||
foreach ($SinglePermission in $CustomPermission) {
|
||||
if ($SinglePermission -in $ExcludePermissionType) {
|
||||
continue
|
||||
}
|
||||
if ($IncludePermissionType.Count -gt 0 -and $SinglePermission -notin $IncludePermissionType) {
|
||||
continue
|
||||
}
|
||||
$OutputEntry = [ordered] @{
|
||||
PrincipalName = $Permission.Principal
|
||||
Permission = $SinglePermission
|
||||
PermissionType = $Permission.AccessControlType
|
||||
PrincipalSidType = $Permission.PrincipalType
|
||||
PrincipalObjectClass = $Permission.PrincipalObjectType
|
||||
PrincipalDomainName = $Permission.PrincipalObjectDomain
|
||||
PrincipalSid = $Permission.PrincipalObjectSid
|
||||
DomainName = $Domain
|
||||
GPOCount = $GPOs.Count
|
||||
}
|
||||
if (-not $SkipNames) {
|
||||
$OutputEntry['GPONames'] = $GPOs.DisplayName
|
||||
}
|
||||
[PSCustomObject] $OutputEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
End {}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
function Get-GPOZaurrPermissionSummary {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All',
|
||||
[validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'All',
|
||||
[ValidateSet('GpoApply', 'GpoEdit', 'GpoCustom', 'GpoEditDeleteModifySecurity', 'GpoRead', 'GpoOwner', 'GpoRootCreate', 'GpoRootOwner')][string[]] $IncludePermissionType,
|
||||
[ValidateSet('GpoApply', 'GpoEdit', 'GpoCustom', 'GpoEditDeleteModifySecurity', 'GpoRead', 'GpoOwner', 'GpoRootCreate', 'GpoRootOwner')][string[]] $ExcludePermissionType,
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[string] $Separator
|
||||
)
|
||||
|
||||
$IncludePermTypes = [System.Collections.Generic.List[Microsoft.GroupPolicy.GPPermissionType]]::new()
|
||||
$ExcludePermTypes = [System.Collections.Generic.List[Microsoft.GroupPolicy.GPPermissionType]]::new()
|
||||
$CustomPermissions = [System.Collections.Generic.List[string]]::new()
|
||||
foreach ($PermType in $IncludePermissionType) {
|
||||
if ($PermType -in 'GpoApply', 'GpoEdit', 'GPOCustom', 'GpoEditDeleteModifySecurity', 'GPORead') {
|
||||
$IncludePermTypes.Add([Microsoft.GroupPolicy.GPPermissionType]::$PermType)
|
||||
} elseif ($PermType -in 'GpoOwner') {
|
||||
$IncludeOwner = $true
|
||||
} elseif ($PermType -in 'GpoRootCreate', 'GpoRootOwner') {
|
||||
$CustomPermissions.Add($PermType)
|
||||
}
|
||||
}
|
||||
foreach ($PermType in $ExcludePermissionType) {
|
||||
if ($PermType -in 'GpoApply', 'GpoEdit', 'GPOCustom', 'GpoEditDeleteModifySecurity', 'GPORead') {
|
||||
$ExcludePermTypes.Add([Microsoft.GroupPolicy.GPPermissionType]::$PermType)
|
||||
} elseif ($PermType -in 'GpoOwner') {
|
||||
$IncludeOwner = $false
|
||||
} elseif ($PermType -in 'GpoRootCreate', 'GpoRootOwner') {
|
||||
$CustomPermissions.Add($PermType)
|
||||
}
|
||||
}
|
||||
|
||||
$RootPermissions = Get-GPOZaurrPermissionRoot -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
$Permissions = Get-GPOZaurrPermission -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -IncludePermissionType $IncludePermTypes -ExcludePermissionType $ExcludePermissionType -Type $Type -PermitType $PermitType -IncludeOwner:$IncludeOwner
|
||||
$Entries = @(
|
||||
foreach ($Permission in $Permissions) {
|
||||
[PSCustomObject] @{
|
||||
PrincipalName = $Permission.PrincipalName
|
||||
PrincipalDomainName = $Permission.PrincipalDomainName
|
||||
Permission = $Permission.Permission
|
||||
PermissionType = $Permission.PermissionType
|
||||
PrincipalSid = $Permission.PrincipalSid
|
||||
PrincipalSidType = $Permission.PrincipalSidType
|
||||
PrincipalObjectClass = $Permission.PrincipalObjectClass
|
||||
DisplayName = $Permission.DisplayName
|
||||
DomainName = $Permission.DomainName
|
||||
}
|
||||
}
|
||||
foreach ($RootPermission in $RootPermissions) {
|
||||
[PSCustomObject] @{
|
||||
PrincipalName = $RootPermission.PrincipalName
|
||||
PrincipalDomainName = $RootPermission.PrincipalDomainName
|
||||
Permission = $RootPermission.Permission
|
||||
PermissionType = $RootPermission.PermissionType
|
||||
PrincipalSid = $RootPermission.PrincipalSid
|
||||
PrincipalSidType = $RootPermission.PrincipalSidType
|
||||
PrincipalObjectClass = $RootPermission.PrincipalObjectClass
|
||||
DisplayName = $RootPermission.GPONames
|
||||
DomainName = $RootPermission.DomainName
|
||||
}
|
||||
}
|
||||
)
|
||||
$PermissionsData = [ordered] @{}
|
||||
foreach ($Entry in $Entries) {
|
||||
$Key = -join ($Entry.Permission, $Entry.PrincipalName, $Entry.PrincipalDomainName)
|
||||
if (-not $PermissionsData[$Key]) {
|
||||
$PermissionsData[$Key] = [PSCustomObject] @{
|
||||
Permission = $Entry.Permission
|
||||
PrincipalName = $Entry.PrincipalName
|
||||
PrincipalDomainName = $Entry.PrincipalDomainName
|
||||
PrincipalSidType = $Entry.PrincipalSidType
|
||||
DomainName = $Entry.DomainName
|
||||
PermissionType = $Entry.PermissionType
|
||||
GPOCOunt = 0
|
||||
GPONames = [System.Collections.Generic.List[string]]::new()
|
||||
}
|
||||
}
|
||||
#if ($IncludeNames) {
|
||||
$PermissionsData[$Key].GPONames.Add($Entry.DisplayName)
|
||||
#}
|
||||
$PermissionsData[$Key].GPOCOunt = $PermissionsData[$Key].GPOCOunt + ($Entry.DisplayName).Count
|
||||
}
|
||||
$PermissionsData.Values
|
||||
|
||||
<#
|
||||
($Entries | Group-Object -Property Permission, PrincipalSidType, PrincipalName, PrincipalDomainName, DomainName, PermissionType) | ForEach-Object {
|
||||
$Property = $_.Name -split ', '
|
||||
Write-Verbose "$Property - $($Property.Count)"
|
||||
if ($Property[0] -eq 'GpoOwner') {
|
||||
[PSCustomObject] @{
|
||||
Permission = $Property[0]
|
||||
PrincipalSidType = $Property[1]
|
||||
PrincipalName = $Property[2]
|
||||
PrincipalDomainName = $Property[3]
|
||||
DomainName = $Property[4]
|
||||
PermissionType = 'Allow'
|
||||
GPOCount = $_.Count
|
||||
GPONames = if ($Separator) { $_.Group.DisplayName -join $Separator } else { $_.Group.DisplayName }
|
||||
}
|
||||
} elseif ($Property.Count -eq 5) {
|
||||
[PSCustomObject] @{
|
||||
Permission = $Property[0]
|
||||
PrincipalSidType = $Property[1]
|
||||
PrincipalName = $Property[2]
|
||||
PrincipalDomainName = $Property[3]
|
||||
DomainName = $Property[4]
|
||||
PermissionType = if ($Property[5]) { $Property[5] } else { 'Owner' }
|
||||
GPOCount = $_.Count
|
||||
GPONames = if ($Separator) { $_.Group.DisplayName -join $Separator } else { $_.Group.DisplayName }
|
||||
}
|
||||
} else {
|
||||
[PSCustomObject] @{
|
||||
Permission = $Property[0]
|
||||
PrincipalSidType = $Property[1]
|
||||
PrincipalName = ''
|
||||
PrincipalDomainName = ''
|
||||
DomainName = $Property[2]
|
||||
PermissionType = if ($Property[3]) { $Property[3] } else { 'Owner' }
|
||||
GPOCount = $_.Count
|
||||
GPONames = if ($Separator) { $_.Group.DisplayName -join $Separator } else { $_.Group.DisplayName }
|
||||
}
|
||||
}
|
||||
}
|
||||
#>
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
function Get-GPOZaurrSysvol {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[string[]] $ExcludeDomainControllers,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[alias('DomainControllers')][string[]] $IncludeDomainControllers,
|
||||
[switch] $SkipRODC,
|
||||
[Array] $GPOs,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[switch] $VerifyDomainControllers
|
||||
)
|
||||
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
Write-Verbose "Get-WinADGPOSysvolFolders - Processing $Domain"
|
||||
$QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0]
|
||||
Try {
|
||||
[Array]$GPOs = Get-GPO -All -Domain $Domain -Server $QueryServer
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrSysvol - Couldn't get GPOs from $Domain. Error: $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
if ($GPOs.Count -ge 2) {
|
||||
if (-not $VerifyDomainControllers) {
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Domain -Domain $Domain
|
||||
} else {
|
||||
foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) {
|
||||
Write-Verbose "Get-GPOZaurrSysvol - Processing $Domain \ $($Server.HostName.Trim())"
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Server.Hostname -Domain $Domain
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Get-GPOZaurrSysvol - GPO count for $Domain is less then 2. This is not expected for fully functioning domain. Skipping processing SYSVOL folder."
|
||||
}
|
||||
}
|
||||
}
|
||||
+116
-200
@@ -1,223 +1,139 @@
|
||||
function Invoke-GPOZaurr {
|
||||
[alias('Find-GPO')]
|
||||
[cmdletBinding(DefaultParameterSetName = 'Default')]
|
||||
[alias('Show-GPOZaurr', 'Show-GPO')]
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[Parameter(ParameterSetName = 'Default')][alias('ForestName')][string] $Forest,
|
||||
[Parameter(ParameterSetName = 'Default')][string[]] $ExcludeDomains,
|
||||
[Parameter(ParameterSetName = 'Default')][alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[Parameter(ParameterSetName = 'Default')][System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[Parameter(ParameterSetName = 'Local')][string] $GPOPath,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[string] $FilePath,
|
||||
[string[]] $Type,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[string] $Splitter = [System.Environment]::NewLine,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $FullObjects,
|
||||
|
||||
<#
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[ValidateSet('HTML', 'Object', 'Excel')][string[]] $OutputType = 'Object',
|
||||
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[string] $OutputPath,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $Open,
|
||||
#>
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $CategoriesOnly,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $SingleObject,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $SkipNormalize,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $SkipCleanup
|
||||
[switch] $PassThru,
|
||||
[switch] $HideHTML
|
||||
)
|
||||
if ($Type.Count -eq 0) {
|
||||
$Type = $Script:GPODitionary.Keys
|
||||
Reset-GPOZaurrStatus # This makes sure types are at it's proper status
|
||||
|
||||
$Script:Reporting = [ordered] @{}
|
||||
$Script:Reporting['Version'] = Get-GitHubVersion -Cmdlet 'Invoke-GPOZaurr' -RepositoryOwner 'evotecit' -RepositoryName 'GPOZaurr'
|
||||
Write-Color '[i]', "[GPOZaurr] ", 'Version', ' [Informative] ', $Script:Reporting['Version'] -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
|
||||
|
||||
# Verify requested types are supported
|
||||
$Supported = [System.Collections.Generic.List[string]]::new()
|
||||
[Array] $NotSupported = foreach ($T in $Type) {
|
||||
if ($T -notin $Script:GPOConfiguration.Keys ) {
|
||||
$T
|
||||
} else {
|
||||
$Supported.Add($T)
|
||||
}
|
||||
}
|
||||
if ($GPOPath) {
|
||||
if (Test-Path -LiteralPath $GPOPath) {
|
||||
$GPOFiles = Get-ChildItem -LiteralPath $GPOPath -Recurse -File -Filter *.xml
|
||||
[Array] $GPOs = foreach ($File in $GPOFiles) {
|
||||
if ($File.Name -ne 'GPOList.xml') {
|
||||
try {
|
||||
[xml] $GPORead = Get-Content -LiteralPath $File.FullName
|
||||
} catch {
|
||||
Write-Warning "Invoke-GPOZaurr - Couldn't process $($File.FullName) error: $($_.Exception.message)"
|
||||
continue
|
||||
}
|
||||
if ($Supported) {
|
||||
Write-Color '[i]', "[GPOZaurr] ", 'Supported types', ' [Informative] ', "Chosen by user: ", ($Supported -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta
|
||||
}
|
||||
if ($NotSupported) {
|
||||
Write-Color '[i]', "[GPOZaurr] ", 'Not supported types', ' [Informative] ', "Following types are not supported: ", ($NotSupported -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta
|
||||
Write-Color '[i]', "[GPOZaurr] ", 'Not supported types', ' [Informative] ', "Please use one/multiple from the list: ", ($Script:GPOConfiguration.Keys -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta
|
||||
return
|
||||
}
|
||||
|
||||
# Lets make sure we only enable those types which are requestd by user
|
||||
if ($Type) {
|
||||
foreach ($T in $Script:GPOConfiguration.Keys) {
|
||||
$Script:GPOConfiguration[$T].Enabled = $false
|
||||
}
|
||||
# Lets enable all requested ones
|
||||
foreach ($T in $Type) {
|
||||
$Script:GPOConfiguration[$T].Enabled = $true
|
||||
}
|
||||
}
|
||||
|
||||
# Build data
|
||||
foreach ($T in $Script:GPOConfiguration.Keys) {
|
||||
if ($Script:GPOConfiguration[$T].Enabled -eq $true) {
|
||||
$Script:Reporting[$T] = [ordered] @{
|
||||
Name = $Script:GPOConfiguration[$T].Name
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
WarningsAndErrors = $null
|
||||
Time = $null
|
||||
Summary = $null
|
||||
Variables = Copy-Dictionary -Dictionary $Script:GPOConfiguration[$T]['Variables']
|
||||
}
|
||||
$TimeLogGPOList = Start-TimeLog
|
||||
Write-Color -Text '[i]', '[Start] ', $($Script:GPOConfiguration[$T]['Name']) -Color Yellow, DarkGray, Yellow
|
||||
$Script:Reporting[$T]['Data'] = Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Execute'] -WarningVariable CommandWarnings -ErrorVariable CommandErrors
|
||||
Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Processing']
|
||||
$Script:Reporting[$T]['WarningsAndErrors'] = @(
|
||||
foreach ($War in $CommandWarnings) {
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $GPORead.GPO.Name
|
||||
DomainName = $GPORead.GPO.Identifier.Domain.'#text'
|
||||
GUID = $GPORead.GPO.Identifier.Identifier.'#text' -replace '{' -replace '}'
|
||||
GPOOutput = $GPORead
|
||||
Type = 'Warning'
|
||||
Comment = $War
|
||||
Reason = ''
|
||||
TargetName = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Invoke-GPOZaurr - $GPOPath doesn't exists."
|
||||
return
|
||||
}
|
||||
} else {
|
||||
[Array] $GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
}
|
||||
# This caches single reports.
|
||||
$TemporaryCachedSingleReports = [ordered] @{}
|
||||
$TemporaryCachedSingleReports['ReportsSingle'] = [ordered] @{}
|
||||
# This will be returned
|
||||
$Output = [ordered] @{}
|
||||
$Output['Reports'] = [ordered] @{}
|
||||
$Output['CategoriesFull'] = [ordered] @{}
|
||||
|
||||
[Array] $GPOCategories = foreach ($GPO in $GPOs) {
|
||||
if ($GPOPath) {
|
||||
$GPOOutput = $GPO.GPOOutput
|
||||
} else {
|
||||
[xml] $GPOOutput = Get-GPOReport -Guid $GPO.GUID -Domain $GPO.DomainName -ReportType Xml
|
||||
}
|
||||
Get-GPOCategories -GPO $GPO -GPOOutput $GPOOutput.GPO -Splitter $Splitter -FullObjects:$FullObjects -CachedCategories $Output['CategoriesFull']
|
||||
}
|
||||
$Output['Categories'] = $GPOCategories | Select-Object -Property * -ExcludeProperty DataSet
|
||||
if ($CategoriesOnly) {
|
||||
# Return Categories only
|
||||
return $Output['Categories']
|
||||
}
|
||||
# We check our dictionary for reports that are based on reports to make sure we run CodeSingle separatly
|
||||
[Array] $FindRequiredSingle = foreach ($Key in $Script:GPODitionary.Keys) {
|
||||
$Script:GPODitionary[$Key].ByReports.Report
|
||||
}
|
||||
# Build reports based on categories
|
||||
if ($Output['CategoriesFull'].Count -gt 0) {
|
||||
foreach ($Report in $Type) {
|
||||
foreach ($CategoryType in $Script:GPODitionary[$Report].Types) {
|
||||
$Category = $CategoryType.Category
|
||||
$Settings = $CategoryType.Settings
|
||||
# Those are checks for making sure we have data to be even able to process it
|
||||
if (-not $Output['CategoriesFull'][$Category]) {
|
||||
continue
|
||||
}
|
||||
if (-not $Output['CategoriesFull'][$Category][$Settings]) {
|
||||
continue
|
||||
}
|
||||
# Translation
|
||||
$CategorizedGPO = $Output['CategoriesFull'][$Category][$Settings]
|
||||
foreach ($GPO in $CategorizedGPO) {
|
||||
if (-not $Output['Reports'][$Report]) {
|
||||
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
# Create temporary storage for "single gpo" reports
|
||||
# it's required if we want to base reports on other reports later on
|
||||
if (-not $TemporaryCachedSingleReports['ReportsSingle'][$Report]) {
|
||||
$TemporaryCachedSingleReports['ReportsSingle'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
# Make sure translated gpo is null
|
||||
$TranslatedGpo = $null
|
||||
if ($SingleObject -or ($Report -in $FindRequiredSingle)) {
|
||||
# We either create 1 GPO with multiple settings to return it as user requested it
|
||||
# Or we process it only because we need to base it for reports based on other reports
|
||||
if (-not $Script:GPODitionary[$Report]['CodeSingle']) {
|
||||
# sometimes code and code single are identical. To not define things two times, one can just skip it
|
||||
If ($Script:GPODitionary[$Report]['Code']) {
|
||||
$Script:GPODitionary[$Report]['CodeSingle'] = $Script:GPODitionary[$Report]['Code']
|
||||
}
|
||||
}
|
||||
if ($Script:GPODitionary[$Report]['CodeSingle']) {
|
||||
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeSingle']
|
||||
if ($Report -in $FindRequiredSingle) {
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$TemporaryCachedSingleReports['ReportsSingle'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
if ($SingleObject) {
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$Output['Reports'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $SingleObject) {
|
||||
# We want each GPO to be listed multiple times if it makes sense for reporting
|
||||
# think drive mapping - showing 1 mapping of a drive per object even if there are 50 drive mappings within 1 gpo
|
||||
# this would result in 50 objects created
|
||||
if ($Script:GPODitionary[$Report]['Code']) {
|
||||
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['Code']
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$Output['Reports'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
foreach ($Err in $CommandErrors) {
|
||||
[PSCustomObject] @{
|
||||
Type = 'Error'
|
||||
Comment = $Err
|
||||
Reason = $Err.CategoryInfo.Reason
|
||||
TargetName = $Err.CategoryInfo.TargetName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Those reports are based on other reports (for example already processed registry settings)
|
||||
# This is useful where going thru registry collections may not be efficient enough to try and read it directly again
|
||||
foreach ($Report in $Type) {
|
||||
foreach ($ReportType in $Script:GPODitionary[$Report].ByReports) {
|
||||
if (-not $Output['Reports'][$Report]) {
|
||||
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
$FindReport = $ReportType.Report
|
||||
foreach ($GPO in $TemporaryCachedSingleReports['ReportsSingle'][$FindReport]) {
|
||||
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeReport']
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$Output['Reports'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Normalize - meaning that before we return each GPO report we make sure that each entry has the same column names regardless which one is first.
|
||||
# Normally if you would have a GPO with just 2 entries for given subject (say LAPS), and then another GPO having 5 settings for the same type
|
||||
# and you would display them one after another - all entries would be shown using first object which has less properties then 2nd or 3rd object
|
||||
# to make sure all objects are having same (even empty) properties we "normalize" it
|
||||
if (-not $SkipNormalize) {
|
||||
foreach ($Report in [string[]] $Output['Reports'].Keys) {
|
||||
$FirstProperties = 'DisplayName', 'DomainName', 'GUID', 'GpoType'
|
||||
$EndProperties = 'CreatedTime', 'ModifiedTime', 'ReadTime', 'Filters', 'Linked', 'LinksCount', 'Links'
|
||||
$Properties = $Output['Reports'][$Report] | Select-Properties -ExcludeProperty ($FirstProperties + $EndProperties) -AllProperties -WarningAction SilentlyContinue
|
||||
$DisplayProperties = @(
|
||||
$FirstProperties
|
||||
foreach ($Property in $Properties) {
|
||||
$Property
|
||||
}
|
||||
$EndProperties
|
||||
)
|
||||
$Output['Reports'][$Report] = $Output['Reports'][$Report] | Select-Object -Property $DisplayProperties
|
||||
if ($Script:GPOConfiguration[$T]['Summary']) {
|
||||
$Script:Reporting[$T]['Summary'] = Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Summary']
|
||||
}
|
||||
$TimeEndGPOList = Stop-TimeLog -Time $TimeLogGPOList -Option OneLiner
|
||||
$Script:Reporting[$T]['Time'] = $TimeEndGPOList
|
||||
Write-Color -Text '[i]', '[End ] ', $($Script:GPOConfiguration[$T]['Name']), " [Time to execute: $TimeEndGPOList]" -Color Yellow, DarkGray, Yellow, DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Name #-Descending
|
||||
#$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Count -Descending
|
||||
# Generate pretty HTML
|
||||
$TimeLogHTML = Start-TimeLog
|
||||
Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report' -Color Yellow, DarkGray, Yellow
|
||||
New-HTML -Author 'Przemysław Kłys' -TitleText 'GPOZaurr Report' {
|
||||
New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey
|
||||
New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
|
||||
New-HTMLPanelStyle -BorderRadius 0px
|
||||
New-HTMLTableOption -DataStore JavaScript -BoolAsString
|
||||
|
||||
if (-not $SkipCleanup) {
|
||||
Remove-EmptyValue -Hashtable $Output -Recursive
|
||||
New-HTMLHeader {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLSection {
|
||||
New-HTMLText -Text "Report generated on $(Get-Date)" -Color Blue
|
||||
} -JustifyContent flex-start -Invisible
|
||||
New-HTMLSection {
|
||||
New-HTMLText -Text "GPOZaurr - $($Script:Reporting['Version'])" -Color Blue
|
||||
} -JustifyContent flex-end -Invisible
|
||||
}
|
||||
}
|
||||
|
||||
if ($Type.Count -eq 1) {
|
||||
foreach ($T in $Script:GPOConfiguration.Keys) {
|
||||
if ($Script:GPOConfiguration[$T].Enabled -eq $true) {
|
||||
& $Script:GPOConfiguration[$T]['Solution']
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($T in $Script:GPOConfiguration.Keys) {
|
||||
if ($Script:GPOConfiguration[$T].Enabled -eq $true) {
|
||||
New-HTMLTab -Name $Script:GPOConfiguration[$T]['Name'] {
|
||||
& $Script:GPOConfiguration[$T]['Solution']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} -Online -ShowHTML:(-not $HideHTML) -FilePath $FilePath
|
||||
$TimeLogEndHTML = Stop-TimeLog -Time $TimeLogHTML -Option OneLiner
|
||||
Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report', " [Time to execute: $TimeLogEndHTML]" -Color Yellow, DarkGray, Yellow, DarkGray
|
||||
if ($PassThru) {
|
||||
$Script:Reporting
|
||||
}
|
||||
return $Output
|
||||
Reset-GPOZaurrStatus # This makes sure types are at it's proper status
|
||||
|
||||
}
|
||||
|
||||
[scriptblock] $SourcesAutoCompleter = {
|
||||
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
||||
|
||||
$Script:GPODitionary.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
|
||||
$Script:GPOConfiguration.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
|
||||
}
|
||||
|
||||
Register-ArgumentCompleter -CommandName Invoke-GPOZaurr -ParameterName Type -ScriptBlock $SourcesAutoCompleter
|
||||
@@ -0,0 +1,263 @@
|
||||
function Invoke-GPOZaurrContent {
|
||||
[alias('Find-GPO')]
|
||||
[cmdletBinding(DefaultParameterSetName = 'Default')]
|
||||
param(
|
||||
[Parameter(ParameterSetName = 'Default')][alias('ForestName')][string] $Forest,
|
||||
[Parameter(ParameterSetName = 'Default')][string[]] $ExcludeDomains,
|
||||
[Parameter(ParameterSetName = 'Default')][alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[Parameter(ParameterSetName = 'Default')][System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[Parameter(ParameterSetName = 'Local')][string] $GPOPath,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[string[]] $Type,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[string] $Splitter = [System.Environment]::NewLine,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $FullObjects,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[ValidateSet('HTML', 'Object')][string[]] $OutputType = 'Object',
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[string] $OutputPath,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $Open,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $Online,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $CategoriesOnly,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $SingleObject,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $SkipNormalize,
|
||||
|
||||
[Parameter(ParameterSetName = 'Default')]
|
||||
[Parameter(ParameterSetName = 'Local')]
|
||||
[switch] $SkipCleanup,
|
||||
|
||||
[switch] $Extended
|
||||
)
|
||||
if ($Type.Count -eq 0) {
|
||||
$Type = $Script:GPODitionary.Keys
|
||||
}
|
||||
if ($GPOPath) {
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Reading GPOs from $GPOPath"
|
||||
if (Test-Path -LiteralPath $GPOPath) {
|
||||
$GPOFiles = Get-ChildItem -LiteralPath $GPOPath -Recurse -File -Filter *.xml
|
||||
[Array] $GPOs = foreach ($File in $GPOFiles) {
|
||||
if ($File.Name -ne 'GPOList.xml') {
|
||||
try {
|
||||
[xml] $GPORead = Get-Content -LiteralPath $File.FullName
|
||||
} catch {
|
||||
Write-Warning "Invoke-GPOZaurrContent - Couldn't process $($File.FullName) error: $($_.Exception.message)"
|
||||
continue
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $GPORead.GPO.Name
|
||||
DomainName = $GPORead.GPO.Identifier.Domain.'#text'
|
||||
GUID = $GPORead.GPO.Identifier.Identifier.'#text' -replace '{' -replace '}'
|
||||
GPOOutput = $GPORead
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Invoke-GPOZaurrContent - $GPOPath doesn't exists."
|
||||
return
|
||||
}
|
||||
} else {
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Query AD for GPOs"
|
||||
[Array] $GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
}
|
||||
# This caches single reports.
|
||||
$TemporaryCachedSingleReports = [ordered] @{}
|
||||
$TemporaryCachedSingleReports['ReportsSingle'] = [ordered] @{}
|
||||
# This will be returned
|
||||
$Output = [ordered] @{}
|
||||
$Output['Reports'] = [ordered] @{}
|
||||
$Output['CategoriesFull'] = [ordered] @{}
|
||||
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Loading GPO Report to Categories"
|
||||
[Array] $GPOCategories = foreach ($GPO in $GPOs) {
|
||||
if ($GPOPath) {
|
||||
$GPOOutput = $GPO.GPOOutput
|
||||
} else {
|
||||
[xml] $GPOOutput = Get-GPOReport -Guid $GPO.GUID -Domain $GPO.DomainName -ReportType Xml
|
||||
}
|
||||
Get-GPOCategories -GPO $GPO -GPOOutput $GPOOutput.GPO -Splitter $Splitter -FullObjects:$FullObjects -CachedCategories $Output['CategoriesFull']
|
||||
}
|
||||
$Output['Categories'] = $GPOCategories | Select-Object -Property * -ExcludeProperty DataSet
|
||||
if ($CategoriesOnly) {
|
||||
# Return Categories only
|
||||
return $Output['Categories']
|
||||
}
|
||||
# We check our dictionary for reports that are based on reports to make sure we run CodeSingle separatly
|
||||
[Array] $FindRequiredSingle = foreach ($Key in $Script:GPODitionary.Keys) {
|
||||
$Script:GPODitionary[$Key].ByReports.Report
|
||||
}
|
||||
# Build reports based on categories
|
||||
if ($Output['CategoriesFull'].Count -gt 0) {
|
||||
foreach ($Report in $Type) {
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Processing report type $Report"
|
||||
foreach ($CategoryType in $Script:GPODitionary[$Report].Types) {
|
||||
$Category = $CategoryType.Category
|
||||
$Settings = $CategoryType.Settings
|
||||
# Those are checks for making sure we have data to be even able to process it
|
||||
if (-not $Output['CategoriesFull'][$Category]) {
|
||||
continue
|
||||
}
|
||||
if (-not $Output['CategoriesFull'][$Category][$Settings]) {
|
||||
continue
|
||||
}
|
||||
# Translation
|
||||
$CategorizedGPO = $Output['CategoriesFull'][$Category][$Settings]
|
||||
foreach ($GPO in $CategorizedGPO) {
|
||||
if (-not $Output['Reports'][$Report]) {
|
||||
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
# Create temporary storage for "single gpo" reports
|
||||
# it's required if we want to base reports on other reports later on
|
||||
if (-not $TemporaryCachedSingleReports['ReportsSingle'][$Report]) {
|
||||
$TemporaryCachedSingleReports['ReportsSingle'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
# Make sure translated gpo is null
|
||||
$TranslatedGpo = $null
|
||||
if ($SingleObject -or ($Report -in $FindRequiredSingle)) {
|
||||
# We either create 1 GPO with multiple settings to return it as user requested it
|
||||
# Or we process it only because we need to base it for reports based on other reports
|
||||
if (-not $Script:GPODitionary[$Report]['CodeSingle']) {
|
||||
# sometimes code and code single are identical. To not define things two times, one can just skip it
|
||||
If ($Script:GPODitionary[$Report]['Code']) {
|
||||
$Script:GPODitionary[$Report]['CodeSingle'] = $Script:GPODitionary[$Report]['Code']
|
||||
}
|
||||
}
|
||||
if ($Script:GPODitionary[$Report]['CodeSingle']) {
|
||||
#Write-Verbose "Invoke-GPOZaurrContent - Processing $Report single entry mode"
|
||||
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeSingle']
|
||||
if ($Report -in $FindRequiredSingle) {
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$TemporaryCachedSingleReports['ReportsSingle'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
if ($SingleObject) {
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$Output['Reports'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $SingleObject) {
|
||||
# We want each GPO to be listed multiple times if it makes sense for reporting
|
||||
# think drive mapping - showing 1 mapping of a drive per object even if there are 50 drive mappings within 1 gpo
|
||||
# this would result in 50 objects created
|
||||
if ($Script:GPODitionary[$Report]['Code']) {
|
||||
#Write-Verbose "Invoke-GPOZaurrContent - Processing $Report multi entry mode"
|
||||
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['Code']
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$Output['Reports'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Those reports are based on other reports (for example already processed registry settings)
|
||||
# This is useful where going thru registry collections may not be efficient enough to try and read it directly again
|
||||
foreach ($Report in $Type) {
|
||||
foreach ($ReportType in $Script:GPODitionary[$Report].ByReports) {
|
||||
if (-not $Output['Reports'][$Report]) {
|
||||
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
}
|
||||
$FindReport = $ReportType.Report
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Processing reports based on other report $Report ($FindReport)"
|
||||
foreach ($GPO in $TemporaryCachedSingleReports['ReportsSingle'][$FindReport]) {
|
||||
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeReport']
|
||||
foreach ($T in $TranslatedGpo) {
|
||||
$Output['Reports'][$Report].Add($T)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Normalize - meaning that before we return each GPO report we make sure that each entry has the same column names regardless which one is first.
|
||||
# Normally if you would have a GPO with just 2 entries for given subject (say LAPS), and then another GPO having 5 settings for the same type
|
||||
# and you would display them one after another - all entries would be shown using first object which has less properties then 2nd or 3rd object
|
||||
# to make sure all objects are having same (even empty) properties we "normalize" it
|
||||
if (-not $SkipNormalize) {
|
||||
foreach ($Report in [string[]] $Output['Reports'].Keys) {
|
||||
$FirstProperties = 'DisplayName', 'DomainName', 'GUID', 'GpoType'
|
||||
#$EndProperties = 'CreatedTime', 'ModifiedTime', 'ReadTime', 'Filters', 'Linked', 'LinksCount', 'Links'
|
||||
$EndProperties = 'Filters', 'Linked', 'LinksCount', 'Links'
|
||||
$Properties = $Output['Reports'][$Report] | Select-Properties -ExcludeProperty ($FirstProperties + $EndProperties) -AllProperties -WarningAction SilentlyContinue
|
||||
$DisplayProperties = @(
|
||||
$FirstProperties
|
||||
foreach ($Property in $Properties) {
|
||||
$Property
|
||||
}
|
||||
$EndProperties
|
||||
)
|
||||
$Output['Reports'][$Report] = $Output['Reports'][$Report] | Select-Object -Property $DisplayProperties
|
||||
}
|
||||
}
|
||||
|
||||
$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Name #-Descending
|
||||
#$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Count -Descending
|
||||
|
||||
if (-not $SkipCleanup) {
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Cleaning up output"
|
||||
Remove-EmptyValue -Hashtable $Output -Recursive
|
||||
}
|
||||
if ($Extended) {
|
||||
$Output
|
||||
} else {
|
||||
if ($Output.Reports) {
|
||||
if ($OutputType -eq 'Object') {
|
||||
$Output.Reports
|
||||
}
|
||||
if ($OutputType -eq 'HTML') {
|
||||
if (-not $OutputPath) {
|
||||
$OutputPath = Get-FileName -Extension 'html' -Temporary
|
||||
Write-Warning "Invoke-GPOZaurrContent - OutputPath not given. Using $OutputPath"
|
||||
}
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Generating HTML output"
|
||||
New-HTML {
|
||||
New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
|
||||
New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey
|
||||
New-HTMLTableOption -DataStore JavaScript
|
||||
foreach ($Key in $Output.Reports.Keys) {
|
||||
New-HTMLTab -Name $Key {
|
||||
Write-Verbose "Invoke-GPOZaurrContent - Generating HTML Table for $Key"
|
||||
New-HTMLTable -DataTable $Output.Reports[$Key] -Filtering -Title $Key
|
||||
}
|
||||
}
|
||||
} -FilePath $OutputPath -ShowHTML:$Open -Online:$Online
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Invoke-GPOZaurrContent - There was no data output for requested types."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[scriptblock] $SourcesAutoCompleter = {
|
||||
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
||||
|
||||
$Script:GPODitionary.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
|
||||
}
|
||||
Register-ArgumentCompleter -CommandName Invoke-GPOZaurrContent -ParameterName Type -ScriptBlock $SourcesAutoCompleter
|
||||
@@ -7,26 +7,30 @@
|
||||
[string] $Path,
|
||||
[string] $Splitter = [System.Environment]::NewLine,
|
||||
[switch] $PreventShow,
|
||||
[switch] $Offline
|
||||
[switch] $Offline,
|
||||
[switch] $ForceGPResult
|
||||
)
|
||||
# if user didn't choose anything, lets run as currently logged in user locally
|
||||
if (-not $UserName -and -not $ComputerName) {
|
||||
$UserName = $Env:USERNAME
|
||||
|
||||
# we can also check if the session is Administrative and if so request computer policies
|
||||
if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
$ComputerName = $Env:COMPUTERNAME
|
||||
}
|
||||
}
|
||||
|
||||
If ($Type -eq 'HTML') {
|
||||
$Exists = Get-Command -Name 'New-HTML' -ErrorAction SilentlyContinue
|
||||
if (-not $Exists) {
|
||||
Write-Warning "Request-GPOZaurr - PSWriteHTML module is required for HTML functionality. Use XML, Object or NativeHTML option instead."
|
||||
Write-Warning "Invoke-GPOZaurrSupport - PSWriteHTML module is required for HTML functionality. Use XML, Object or NativeHTML option instead."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$Command = Get-Command -Name 'Get-GPResultantSetOfPolicy' -ErrorAction SilentlyContinue
|
||||
$NativeCommand = Get-Command -Name 'gpresult' -ErrorAction SilentlyContinue
|
||||
if (-not $Command -and -not $NativeCommand) {
|
||||
Write-Warning "Invoke-GPOZaurrSupport - Neither gpresult or Get-GPResultantSetOfPolicy are available. Terminating."
|
||||
return
|
||||
}
|
||||
|
||||
$SplatPolicy = @{}
|
||||
if ($Type -in 'Object', 'XML', 'HTML') {
|
||||
@@ -50,22 +54,42 @@
|
||||
if ($UserName) {
|
||||
$SplatPolicy['User'] = $UserName
|
||||
}
|
||||
try {
|
||||
#Write-Verbose "Request-GPOZaurr - ComputerName: $($SplatPolicy['Computer']) UserName: $($SplatPolicy['User'])"
|
||||
$ResultantSetPolicy = Get-GPResultantSetOfPolicy @SplatPolicy -ErrorAction Stop
|
||||
} catch {
|
||||
if ($_.Exception.Message -eq 'Exception from HRESULT: 0x80041003') {
|
||||
Write-Warning "Request-GPOZaurr - Are you running as admin? $($_.Exception.Message)"
|
||||
return
|
||||
} else {
|
||||
$ErrorMessage = $($_.Exception.Message).Replace([Environment]::NewLine, ' ')
|
||||
Write-Warning "Request-GPOZaurr - Error: $ErrorMessage"
|
||||
return
|
||||
if ($Command -and -not $ForceGPResult) {
|
||||
try {
|
||||
#Write-Verbose "Request-GPOZaurr - ComputerName: $($SplatPolicy['Computer']) UserName: $($SplatPolicy['User'])"
|
||||
$ResultantSetPolicy = Get-GPResultantSetOfPolicy @SplatPolicy -ErrorAction Stop
|
||||
} catch {
|
||||
if ($_.Exception.Message -eq 'Exception from HRESULT: 0x80041003') {
|
||||
Write-Warning "Request-GPOZaurr - Are you running as admin? $($_.Exception.Message)"
|
||||
return
|
||||
} else {
|
||||
$ErrorMessage = $($_.Exception.Message).Replace([Environment]::NewLine, ' ')
|
||||
Write-Warning "Request-GPOZaurr - Error: $ErrorMessage"
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$Arguments = @(
|
||||
if ($SplatPolicy['Computer']) {
|
||||
"/S $ComputerName"
|
||||
}
|
||||
if ($SplatPolicy['User']) {
|
||||
"/USER $($SplatPolicy['User'])"
|
||||
}
|
||||
if ($SplatPolicy['ReportType'] -eq 'HTML') {
|
||||
'/H'
|
||||
} elseif ($SplatPolicy['ReportType'] -eq 'XML') {
|
||||
'/X'
|
||||
}
|
||||
$SplatPolicy['Path']
|
||||
"/F"
|
||||
)
|
||||
Write-Verbose "Invoke-GPOZaurrSupport - GPResult Arguments: $Arguments"
|
||||
Start-Process -NoNewWindow -FilePath 'gpresult' -ArgumentList $Arguments -Wait
|
||||
}
|
||||
if ($Type -eq 'NativeHTML') {
|
||||
if (-not $PreventShow) {
|
||||
Write-Verbose "Invoke-GPOZaurrSupport - Opening up file $SplatPolicy['Path']"
|
||||
Write-Verbose "Invoke-GPOZaurrSupport - Opening up file $($SplatPolicy['Path'])"
|
||||
Start-Process -FilePath $SplatPolicy['Path']
|
||||
}
|
||||
return
|
||||
@@ -109,4 +133,72 @@
|
||||
New-GPOZaurrReportHTML -Path $Path -Offline:$Offline -Open:(-not $PreventShow) -Support $Output
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
|
||||
|
||||
GPRESULT [/S system [/U username [/P [password]]]] [/SCOPE scope]
|
||||
[/USER targetusername] [/R | /V | /Z] [(/X | /H) <filename> [/F]]
|
||||
|
||||
Description:
|
||||
This command line tool displays the Resultant Set of Policy (RSoP)
|
||||
information for a target user and computer.
|
||||
|
||||
Parameter List:
|
||||
/S system Specifies the remote system to connect to.
|
||||
|
||||
/U [domain\]user Specifies the user context under which the
|
||||
command should run.
|
||||
Can not be used with /X, /H.
|
||||
|
||||
/P [password] Specifies the password for the given user
|
||||
context. Prompts for input if omitted.
|
||||
Cannot be used with /X, /H.
|
||||
|
||||
/SCOPE scope Specifies whether the user or the
|
||||
computer settings need to be displayed.
|
||||
Valid values: "USER", "COMPUTER".
|
||||
|
||||
/USER [domain\]user Specifies the user name for which the
|
||||
RSoP data is to be displayed.
|
||||
|
||||
/X <filename> Saves the report in XML format at the
|
||||
location and with the file name specified
|
||||
by the <filename> parameter. (valid in Windows
|
||||
Vista SP1 and later and Windows Server 2008 and later)
|
||||
|
||||
/H <filename> Saves the report in HTML format at the
|
||||
location and with the file name specified by
|
||||
the <filename> parameter. (valid in Windows
|
||||
at least Vista SP1 and at least Windows Server 2008)
|
||||
|
||||
/F Forces Gpresult to overwrite the file name
|
||||
specified in the /X or /H command.
|
||||
|
||||
/R Displays RSoP summary data.
|
||||
|
||||
/V Specifies that verbose information should
|
||||
be displayed. Verbose information provides
|
||||
additional detailed settings that have
|
||||
been applied with a precedence of 1.
|
||||
|
||||
/Z Specifies that the super-verbose
|
||||
information should be displayed. Super-
|
||||
verbose information provides additional
|
||||
detailed settings that have been applied
|
||||
with a precedence of 1 and higher. This
|
||||
allows you to see if a setting was set in
|
||||
multiple places. See the Group Policy
|
||||
online help topic for more information.
|
||||
|
||||
/? Displays this help message.
|
||||
|
||||
|
||||
Examples:
|
||||
GPRESULT /R
|
||||
GPRESULT /H GPReport.html
|
||||
GPRESULT /USER targetusername /V
|
||||
GPRESULT /S system /USER targetusername /SCOPE COMPUTER /Z
|
||||
GPRESULT /S system /U username /P password /SCOPE USER /V
|
||||
#>
|
||||
@@ -0,0 +1,19 @@
|
||||
function Optimize-GPOZaurr {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
|
||||
)
|
||||
|
||||
$GPOS = Get-GPOZaurr -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
foreach ($GPO in $GPOS) {
|
||||
if ($GPO.UserSettingsAvailable -eq $false -and $GPO.ComputerSettingsAvailable -eq $false) {
|
||||
if ($GPO.Enabled -ne 'All setttings disabled') {
|
||||
# $GPO
|
||||
}
|
||||
} elseif ($GPO.UserSettingsAvailable -eq $false) {
|
||||
|
||||
} elseif ($GPO.ComputerSettingsAvailable -eq $false) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
function Remove-GPOZaurrBroken {
|
||||
[alias('Remove-GPOZaurrOrphaned')]
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[ValidateSet('SYSVOL', 'AD')][string[]] $Type = @('SYSVOL', 'AD'),
|
||||
[string] $BackupPath,
|
||||
[switch] $BackupDated,
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
if ($BackupPath) {
|
||||
if ($BackupDated) {
|
||||
$BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))"
|
||||
} else {
|
||||
$BackupFinalPath = $BackupPath
|
||||
}
|
||||
} else {
|
||||
$BackupFinalPath = ''
|
||||
}
|
||||
Get-GPOZaurrBroken -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object {
|
||||
if ($Type -contains 'SYSVOL') {
|
||||
if ($_.Status -eq 'Not available in AD') {
|
||||
$_
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'AD') {
|
||||
if ($_.Status -eq 'Not available on SYSVOL') {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object {
|
||||
if ($_.Status -eq 'Not available in AD') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Processing $($_.Path)"
|
||||
if ($BackupFinalPath) {
|
||||
Try {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Backing up $($_.Path)"
|
||||
Copy-Item -LiteralPath $_.Path -Recurse -Destination $BackupFinalPath -ErrorAction Stop
|
||||
$BackupWorked = $true
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrBroken - Error backing up error: $($_.Exception.Message)"
|
||||
$BackupWorked = $false
|
||||
}
|
||||
}
|
||||
if ($BackupWorked -or $BackupFinalPath -eq '') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Deleting $($_.Path)"
|
||||
try {
|
||||
Remove-Item -Recurse -Force -LiteralPath $_.Path
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrBroken - Failed to remove file $($_.Path): $($_.Exception.Message)."
|
||||
}
|
||||
}
|
||||
} elseif ($_.Status -eq 'Not available on SYSVOL') {
|
||||
try {
|
||||
$ExistingObject = Get-ADObject -Identity $_.DistinguishedName -Server $_.DomainName -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrBroken - Error getting $($_.DistinguishedName) from AD error: $($_.Exception.Message)"
|
||||
$ExistingObject = $null
|
||||
}
|
||||
if ($ExistingObject -and $ExistingObject.ObjectClass -eq 'groupPolicyContainer') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing DN: $($_.DistinguishedName) / ObjectClass: $($ExistingObject.ObjectClass)"
|
||||
try {
|
||||
Remove-ADObject -Server $_.DomainName -Identity $_.DistinguishedName -Recursive -Confirm:$false
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrBroken - Failed to remove $($_.DistinguishedName) from AD error: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Remove-GPOZaurrBroken - DistinguishedName $($_.DistinguishedName) not found or ObjectClass is not groupPolicyContainer ($($ExistingObject.ObjectClass))"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
function Remove-GPOZaurrDuplicateObject {
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
|
||||
$getGPOZaurrDuplicateObjectSplat = @{
|
||||
Forest = $Forest
|
||||
IncludeDomains = $IncludeDomains
|
||||
ExcludeDomains = $ExcludeDomains
|
||||
ExtendedForestInformation = $ExtendedForestInformation
|
||||
}
|
||||
|
||||
$DuplicateGpoObjects = Get-GPOZaurrDuplicateObject @getGPOZaurrDuplicateObjectSplat
|
||||
foreach ($Duplicate in $DuplicateGpoObjects | Select-Object -First $LimitProcessing) {
|
||||
try {
|
||||
Remove-ADObject -Identity $Duplicate.ObjectGUID -Recursive -ErrorAction Stop -Server $Duplicate.DomainName
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrDuplicateObject - Deleting $($Duplicate.ConflictDN) / $($Duplicate.DomainName) via GUID: $($Duplicate.ObjectGUID) failed with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
function Remove-GPOZaurrOrphanedSysvolFolders {
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[string] $BackupPath,
|
||||
[switch] $BackupDated,
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation
|
||||
)
|
||||
if ($BackupPath) {
|
||||
if ($BackupDated) {
|
||||
$BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))"
|
||||
} else {
|
||||
$BackupFinalPath = $BackupPath
|
||||
}
|
||||
} else {
|
||||
$BackupFinalPath = ''
|
||||
}
|
||||
Get-GPOZaurrSysvol -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object {
|
||||
if ($_.Status -eq 'Orphaned GPO') {
|
||||
$_
|
||||
}
|
||||
} | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object {
|
||||
Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Processing $($_.Path)"
|
||||
if ($BackupFinalPath) {
|
||||
Try {
|
||||
Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Backing up $($_.Path)"
|
||||
Copy-Item -LiteralPath $_.Path -Recurse -Destination $BackupFinalPath -ErrorAction Stop
|
||||
$BackupWorked = $true
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrOrphanedSysvolFolders - Error backing up error: $($_.Exception.Message)"
|
||||
$BackupWorked = $false
|
||||
}
|
||||
}
|
||||
if ($BackupWorked -or $BackupFinalPath -eq '') {
|
||||
Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Deleting $($_.Path)"
|
||||
try {
|
||||
Remove-Item -Recurse -Force -LiteralPath $_.Path
|
||||
} catch {
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Warning "Remove-GPOZaurrOrphanedSysvolFolders - Failed to remove file $($_.Path): $($ErrorMessage)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
[alias('GUID', 'GPOID')][string] $GPOGuid,
|
||||
|
||||
[string[]] $Principal,
|
||||
[validateset('DistinguishedName', 'Name', 'Sid')][string] $PrincipalType = 'Sid',
|
||||
[validateset('DistinguishedName', 'Name', 'NetbiosName', 'Sid')][string] $PrincipalType = 'Sid',
|
||||
|
||||
[validateset('Unknown', 'NotAdministrative', 'Default')][string[]] $Type = 'Default',
|
||||
|
||||
@@ -72,11 +72,11 @@
|
||||
}
|
||||
}
|
||||
Get-GPO @getGPOSplat | ForEach-Object -Process {
|
||||
$GPOSecurity = $_.GetSecurityInfo()
|
||||
$getPrivPermissionSplat = @{
|
||||
Principal = $Principal
|
||||
PrincipalType = $PrincipalType
|
||||
Accounts = $Accounts
|
||||
Type = $Type
|
||||
#Accounts = $Accounts
|
||||
GPO = $_
|
||||
SkipWellKnown = $SkipWellKnown.IsPresent
|
||||
SkipAdministrative = $SkipAdministrative.IsPresent
|
||||
@@ -85,11 +85,15 @@
|
||||
IncludePermissionType = $IncludePermissionType
|
||||
ExcludePermissionType = $ExcludePermissionType
|
||||
ADAdministrativeGroups = $ADAdministrativeGroups
|
||||
SecurityRights = $GPOSecurity
|
||||
}
|
||||
if ($Type -ne 'Default') {
|
||||
$getPrivPermissionSplat['Type'] = $Type
|
||||
}
|
||||
[Array] $GPOPermissions = Get-PrivPermission @getPrivPermissionSplat
|
||||
if ($GPOPermissions.Count -gt 0) {
|
||||
foreach ($Permission in $GPOPermissions) {
|
||||
Remove-PrivPermission -Principal $Permission.Sid -PrincipalType Sid -GPOPermission $Permission -IncludePermissionType $Permission.Permission #-IncludeDomains $GPO.DomainName
|
||||
Remove-PrivPermission -Principal $Permission.PrincipalSid -PrincipalType Sid -GPOPermission $Permission -IncludePermissionType $Permission.Permission #-IncludeDomains $GPO.DomainName
|
||||
}
|
||||
$Count++
|
||||
if ($Count -eq $LimitProcessing) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
function Repair-GPOZaurrNetLogonOwner {
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[string] $Principal = 'S-1-5-32-544',
|
||||
[int] $LimitProcessing = [int32]::MaxValue
|
||||
)
|
||||
$Identity = Convert-Identity -Identity $Principal -Verbose:$false
|
||||
if ($Identity.Error) {
|
||||
Write-Warning "Repair-GPOZaurrNetLogonOwner - couldn't convert Identity $Principal to desired name. Error: $($Identity.Error)"
|
||||
return
|
||||
}
|
||||
$Principal = $Identity.Name
|
||||
|
||||
$getGPOZaurrNetLogonSplat = @{
|
||||
OwnerOnly = $true
|
||||
Forest = $Forest
|
||||
IncludeDomains = $IncludeDomains
|
||||
ExcludeDomains = $ExcludeDomains
|
||||
ExtendedForestInformation = $ExtendedForestInformation
|
||||
}
|
||||
|
||||
Get-GPOZaurrNetLogon @getGPOZaurrNetLogonSplat | Select-Object -First $LimitProcessing | Where-Object {
|
||||
if ($_.OwnerSid -ne 'S-1-5-32-544') {
|
||||
$_
|
||||
}
|
||||
} | ForEach-Object {
|
||||
if ($PSCmdlet.ShouldProcess($_.FullName, "Setting NetLogon Owner to $($Principal)")) {
|
||||
Set-FileOwner -JustPath -Path $_.FullName -Owner $Principal -Verbose:$true -WhatIf:$WhatIfPreference
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,12 @@
|
||||
}
|
||||
}
|
||||
$null = New-Item -ItemType Directory -Path $GPOPath -Force
|
||||
Write-Verbose "Save-GPOZaurrFiles - Gathering GPO data"
|
||||
$Count = 0
|
||||
$GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
foreach ($GPO in $GPOS) {
|
||||
$Count++
|
||||
Write-Verbose "Save-GPOZaurrFiles - Processing GPO ($Count/$($GPOS.Count)) $($GPO.DomainName) | $($GPO.DisplayName)"
|
||||
$XMLContent = Get-GPOReport -ID $GPO.Guid -ReportType XML -Domain $GPO.DomainName
|
||||
$GPODOmainFolder = [io.path]::Combine($GPOPath, $GPO.DomainName)
|
||||
if (-not (Test-Path -Path $GPODOmainFolder)) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
function Set-GPOZaurrOwner {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Short description
|
||||
Sets GPO Owner to Domain Admins or other choosen account
|
||||
|
||||
.DESCRIPTION
|
||||
Long description
|
||||
Sets GPO Owner to Domain Admins or other choosen account. GPO Owner is set in AD and SYSVOL unless specified otherwise. If account doesn't require change, no change is done.
|
||||
|
||||
.PARAMETER Type
|
||||
Unknown - finds unknown Owners and sets them to Administrative (Domain Admins) or chosen principal
|
||||
@@ -12,34 +12,34 @@
|
||||
NotAdministrative - combination of Unknown/NotMatching and NotAdministrative - replace with chosen principal or Domain Admins if not specified
|
||||
All - if Owner is known it checks if it's Administrative, if it sn't it fixes that. If owner is unknown it fixes it
|
||||
.PARAMETER GPOName
|
||||
Parameter description
|
||||
Name of GPO. By default all GPOs are targetted
|
||||
|
||||
.PARAMETER GPOGuid
|
||||
Parameter description
|
||||
GUID of GPO. By default all GPOs are targetted
|
||||
|
||||
.PARAMETER Forest
|
||||
Parameter description
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Parameter description
|
||||
Exclude domain from search, by default whole forest is scanned
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Parameter description
|
||||
Include only specific domains, by default whole forest is scanned
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Parameter description
|
||||
Ability to provide Forest Information from another command to speed up processing
|
||||
|
||||
.PARAMETER Principal
|
||||
Parameter description
|
||||
|
||||
.PARAMETER SkipSysvol
|
||||
Parameter description
|
||||
Set GPO Owner only in Active Directory. By default GPO Owner is being set in both places
|
||||
|
||||
.PARAMETER LimitProcessing
|
||||
Parameter description
|
||||
Allows to specify maximum number of items that will be fixed in a single run. It doesn't affect amount of GPOs processed
|
||||
|
||||
.EXAMPLE
|
||||
An example
|
||||
Set-GPOZaurrOwner -Type All -Verbose -WhatIf -LimitProcessing 2
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
@@ -76,7 +76,8 @@
|
||||
|
||||
[Parameter(ParameterSetName = 'Type')]
|
||||
[Parameter(ParameterSetName = 'Named')]
|
||||
[int] $LimitProcessing = [int32]::MaxValue
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
[switch] $Force
|
||||
)
|
||||
Begin {
|
||||
#Write-Verbose "Set-GPOZaurrOwner - Getting ADAdministrativeGroups"
|
||||
@@ -92,13 +93,16 @@
|
||||
ExtendedForestInformation = $ExtendedForestInformation
|
||||
ADAdministrativeGroups = $ADAdministrativeGroups
|
||||
Verbose = $VerbosePreference
|
||||
SkipBroken = $true
|
||||
}
|
||||
if ($GPOName) {
|
||||
$getGPOZaurrOwnerSplat['GPOName'] = $GPOName
|
||||
} elseif ($GPOGuid) {
|
||||
$getGPOZaurrOwnerSplat['GPOGuid'] = $GPOGUiD
|
||||
}
|
||||
$Count = 0
|
||||
Get-GPOZaurrOwner @getGPOZaurrOwnerSplat | Where-Object {
|
||||
$Count++
|
||||
if ($_.Owner) {
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($_.Owner)"]
|
||||
} else {
|
||||
@@ -111,45 +115,50 @@
|
||||
$AdministrativeGroupSysvol = $null
|
||||
}
|
||||
}
|
||||
if ($Type -eq 'NotAdministrative') {
|
||||
if (-not $AdministrativeGroup -or (-not $AdministrativeGroupSysvol -and -not $SkipSysvol)) {
|
||||
$_
|
||||
} else {
|
||||
if ($AdministrativeGroup -ne $AdministrativeGroupSysvol) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). Fixing required."
|
||||
$_
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Unknown') {
|
||||
if (-not $_.Owner -or (-not $_.SysvolOwner -and -not $SkipSysvol)) {
|
||||
$_
|
||||
}
|
||||
} elseif ($Type -eq 'NotMatching') {
|
||||
if ($SkipSysvol) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). SysVol scanning is disabled. Skipping."
|
||||
} else {
|
||||
if ($AdministrativeGroup -ne $AdministrativeGroupSysvol) {
|
||||
#Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). Fixing required."
|
||||
$_
|
||||
}
|
||||
}
|
||||
if ($Force) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Force was used to push new owner to $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner)."
|
||||
$_
|
||||
} else {
|
||||
# we run with no type, that means we need to either set it to principal or to Administrative
|
||||
if ($_.Owner) {
|
||||
# we check if Principal is not set
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($_.Owner)"]
|
||||
if (-not $SkipSysvol -and $_.SysvolOwner) {
|
||||
$AdministrativeGroupSysvol = $ADAdministrativeGroups['ByNetBIOS']["$($_.SysvolOwner)"]
|
||||
if (-not $AdministrativeGroup -or -not $AdministrativeGroupSysvol) {
|
||||
if ($Type -eq 'NotAdministrative') {
|
||||
if (-not $AdministrativeGroup -or (-not $AdministrativeGroupSysvol -and -not $SkipSysvol)) {
|
||||
$_
|
||||
} else {
|
||||
if ($AdministrativeGroup -ne $AdministrativeGroupSysvol) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). Fixing required."
|
||||
$_
|
||||
}
|
||||
}
|
||||
} elseif ($Type -eq 'Unknown') {
|
||||
if (-not $_.Owner -or (-not $_.SysvolOwner -and -not $SkipSysvol)) {
|
||||
$_
|
||||
}
|
||||
} elseif ($Type -eq 'NotMatching') {
|
||||
if ($SkipSysvol) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). SysVol scanning is disabled. Skipping."
|
||||
} else {
|
||||
if ($AdministrativeGroup -ne $AdministrativeGroupSysvol) {
|
||||
#Write-Verbose "Set-GPOZaurrOwner - Detected mismatch GPO: $($_.DisplayName) from domain: $($_.DomainName) - owner $($_.Owner) / sysvol owner $($_.SysvolOwner). Fixing required."
|
||||
$_
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# we run with no type, that means we need to either set it to principal or to Administrative
|
||||
if ($_.Owner) {
|
||||
# we check if Principal is not set
|
||||
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($_.Owner)"]
|
||||
if (-not $SkipSysvol -and $_.SysvolOwner) {
|
||||
$AdministrativeGroupSysvol = $ADAdministrativeGroups['ByNetBIOS']["$($_.SysvolOwner)"]
|
||||
if (-not $AdministrativeGroup -or -not $AdministrativeGroupSysvol) {
|
||||
$_
|
||||
}
|
||||
} else {
|
||||
if (-not $AdministrativeGroup) {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (-not $AdministrativeGroup) {
|
||||
$_
|
||||
}
|
||||
$_
|
||||
}
|
||||
} else {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} | Select-Object -First $LimitProcessing | ForEach-Object -Process {
|
||||
@@ -161,15 +170,15 @@
|
||||
}
|
||||
if ($Action -eq 'OnlyGPO') {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) (SID: $($GPO.OwnerSID)) to $DefaultPrincipal"
|
||||
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
|
||||
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
|
||||
} elseif ($Action -eq 'OnlyFileSystem') {
|
||||
if (-not $SkipSysvol) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Changing Sysvol Owner GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.SysvolOwner) (SID: $($GPO.SysvolSid)) to $DefaultPrincipal"
|
||||
Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference
|
||||
Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference
|
||||
}
|
||||
} else {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) (SID: $($GPO.OwnerSID)) to $DefaultPrincipal"
|
||||
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
|
||||
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
|
||||
if (-not $SkipSysvol) {
|
||||
Write-Verbose "Set-GPOZaurrOwner - Changing Sysvol Owner GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.SysvolOwner) (SID: $($GPO.SysvolSid)) to $DefaultPrincipal"
|
||||
Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user