mirror of
https://github.com/EvotecIT/GPOZaurr.git
synced 2026-07-26 20:00:09 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c689708ba | |||
| bb18d488b4 | |||
| e72d81aee3 | |||
| 70ed52cdf9 | |||
| 3b414e712c | |||
| 7ed1410fd9 | |||
| 6d2e476916 | |||
| b7ed5e0c5a | |||
| 72bd69d0e6 | |||
| b5c1f3dcd5 | |||
| bc773e2e83 | |||
| 8db961cba6 | |||
| 227639d0f6 | |||
| e03f819461 | |||
| fb955f645b | |||
| 3fc3b74798 | |||
| 695f4dbe6a | |||
| 5d69cef128 | |||
| 062374b4be | |||
| e61329ac66 | |||
| d3328dbb81 | |||
| 5803371b5e | |||
| 263f364ede | |||
| 16a181155b | |||
| 962d8829d7 | |||
| 4155c35cc6 | |||
| 614d8cbda5 | |||
| 43e7f2e746 | |||
| 9a3ed3b6ce | |||
| 50f0cfc1a6 |
@@ -7,4 +7,7 @@ Add-GPOZaurrPermission -GPOName 'New Group Policy Object' -Type AuthenticatedUse
|
||||
Add-GPOZaurrPermission -Type AuthenticatedUsers -PermissionType GpoRead -All -WhatIf -Verbose
|
||||
|
||||
# Add Domain Admins/Enterprise Admins to all that need it
|
||||
Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -All -WhatIf -Verbose
|
||||
Add-GPOZaurrPermission -Type Administrative -PermissionType GpoEditDeleteModifySecurity -All -WhatIf -Verbose
|
||||
|
||||
# Add ranom name to all that need it
|
||||
Add-GPOZaurrPermission -All -Principal SVC_AGPM -PrincipalType Name -PermissionType GpoEditDeleteModifySecurity -Verbose -LimitProcessing 2 -WhatIf
|
||||
@@ -0,0 +1,10 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
Remove-GPOZaurrLinkEmptyOU -Verbose -LimitProcessing 3 -WhatIf
|
||||
|
||||
$Exclude = @(
|
||||
"OU=Groups,OU=Production,DC=ad,DC=evotec,DC=pl"
|
||||
"OU=Test \, OU,OU=ITR02,DC=ad,DC=evotec,DC=xyz"
|
||||
)
|
||||
|
||||
Remove-GPOZaurrLinkEmptyOU -Verbose -LimitProcessing 3 -WhatIf -ExcludeOrganizationalUnit $Exclude
|
||||
@@ -0,0 +1,5 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
#Get-GPOZaurrOrganizationalUnit -Verbose -Option Unlink | Format-Table
|
||||
|
||||
Invoke-GPOZaurr -Type GPOOrganizationalUnit -Online -FilePath $PSScriptRoot\Reports\GPOZaurrOU.html
|
||||
+5
-5
@@ -6,9 +6,9 @@
|
||||
CompatiblePSEditions = @('Desktop')
|
||||
Copyright = '(c) 2011 - 2021 Przemyslaw Klys @ Evotec. All rights reserved.'
|
||||
Description = 'Group Policy Eater is a PowerShell module that aims to gather information about Group Policies but also allows fixing issues that you may find in them.'
|
||||
FunctionsToExport = @('Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Clear-GPOZaurrSysvolDFSR', 'ConvertFrom-CSExtension', 'Find-CSExtension', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrBroken', 'Get-GPOZaurrBrokenLink', 'Get-GPOZaurrDictionary', 'Get-GPOZaurrDuplicateObject', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrNetLogon', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionAnalysis', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrPermissionIssue', 'Get-GPOZaurrPermissionRoot', 'Get-GPOZaurrPermissionSummary', 'Get-GPOZaurrSysvolDFSR', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrContent', 'Invoke-GPOZaurrPermission', 'Invoke-GPOZaurrSupport', 'New-GPOZaurrWMI', 'Optimize-GPOZaurr', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrBroken', 'Remove-GPOZaurrDuplicateObject', 'Remove-GPOZaurrFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrBrokenLink', 'Repair-GPOZaurrNetLogonOwner', 'Repair-GPOZaurrPermission', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner', 'Set-GPOZaurrStatus', 'Skip-GroupPolicy')
|
||||
FunctionsToExport = @('Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Clear-GPOZaurrSysvolDFSR', 'ConvertFrom-CSExtension', 'Find-CSExtension', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrBroken', 'Get-GPOZaurrBrokenLink', 'Get-GPOZaurrDictionary', 'Get-GPOZaurrDuplicateObject', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrNetLogon', 'Get-GPOZaurrOrganizationalUnit', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionAnalysis', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrPermissionIssue', 'Get-GPOZaurrPermissionRoot', 'Get-GPOZaurrPermissionSummary', 'Get-GPOZaurrSysvolDFSR', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrContent', 'Invoke-GPOZaurrPermission', 'Invoke-GPOZaurrSupport', 'New-GPOZaurrWMI', 'Optimize-GPOZaurr', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrBroken', 'Remove-GPOZaurrDuplicateObject', 'Remove-GPOZaurrFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrLinkEmptyOU', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrBrokenLink', 'Repair-GPOZaurrNetLogonOwner', 'Repair-GPOZaurrPermission', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner', 'Set-GPOZaurrStatus', 'Skip-GroupPolicy')
|
||||
GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde'
|
||||
ModuleVersion = '0.0.125'
|
||||
ModuleVersion = '0.0.129'
|
||||
PowerShellVersion = '5.1'
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
@@ -18,15 +18,15 @@
|
||||
}
|
||||
}
|
||||
RequiredModules = @(@{
|
||||
ModuleVersion = '0.0.198'
|
||||
ModuleVersion = '0.0.208'
|
||||
ModuleName = 'PSSharedGoods'
|
||||
Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.124'
|
||||
ModuleVersion = '0.0.130'
|
||||
ModuleName = 'ADEssentials'
|
||||
Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f'
|
||||
}, @{
|
||||
ModuleVersion = '0.0.148'
|
||||
ModuleVersion = '0.0.158'
|
||||
ModuleName = 'PSWriteHTML'
|
||||
Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c'
|
||||
}, 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security')
|
||||
|
||||
+46
-6
@@ -2,13 +2,53 @@
|
||||
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue -Recurse )
|
||||
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue -Recurse )
|
||||
|
||||
#Dot source the files
|
||||
Foreach ($import in @($Public + $Private)) {
|
||||
Try {
|
||||
. $import.fullname
|
||||
} Catch {
|
||||
Write-Error -Message "Failed to import function $($import.fullname): $_"
|
||||
$AssemblyFolders = Get-ChildItem -Path $PSScriptRoot\Lib -Directory -ErrorAction SilentlyContinue
|
||||
if ($AssemblyFolders.BaseName -contains 'Standard') {
|
||||
$Assembly = @( Get-ChildItem -Path $PSScriptRoot\Lib\Standard\*.dll -ErrorAction SilentlyContinue )
|
||||
} else {
|
||||
if ($PSEdition -eq 'Core') {
|
||||
$Assembly = @( Get-ChildItem -Path $PSScriptRoot\Lib\Core\*.dll -ErrorAction SilentlyContinue )
|
||||
} else {
|
||||
$Assembly = @( Get-ChildItem -Path $PSScriptRoot\Lib\Default\*.dll -ErrorAction SilentlyContinue )
|
||||
}
|
||||
}
|
||||
$FoundErrors = @(
|
||||
Foreach ($Import in @($Assembly)) {
|
||||
try {
|
||||
Add-Type -Path $Import.Fullname -ErrorAction Stop
|
||||
} catch [System.Reflection.ReflectionTypeLoadException] {
|
||||
Write-Warning "Processing $($Import.Name) Exception: $($_.Exception.Message)"
|
||||
$LoaderExceptions = $($_.Exception.LoaderExceptions) | Sort-Object -Unique
|
||||
foreach ($E in $LoaderExceptions) {
|
||||
Write-Warning "Processing $($Import.Name) LoaderExceptions: $($E.Message)"
|
||||
}
|
||||
$true
|
||||
#Write-Error -Message "StackTrace: $($_.Exception.StackTrace)"
|
||||
} catch {
|
||||
Write-Warning "Processing $($Import.Name) Exception: $($_.Exception.Message)"
|
||||
$LoaderExceptions = $($_.Exception.LoaderExceptions) | Sort-Object -Unique
|
||||
foreach ($E in $LoaderExceptions) {
|
||||
Write-Warning "Processing $($Import.Name) LoaderExceptions: $($E.Message)"
|
||||
}
|
||||
$true
|
||||
#Write-Error -Message "StackTrace: $($_.Exception.StackTrace)"
|
||||
}
|
||||
}
|
||||
#Dot source the files
|
||||
Foreach ($Import in @($Private + $Public)) {
|
||||
Try {
|
||||
. $Import.Fullname
|
||||
} Catch {
|
||||
Write-Error -Message "Failed to import functions from $($import.Fullname): $_"
|
||||
$true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($FoundErrors.Count -gt 0) {
|
||||
$ModuleName = (Get-ChildItem $PSScriptRoot\*.psd1).BaseName
|
||||
Write-Warning "Importing module $ModuleName failed. Fix errors before continuing."
|
||||
break
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function '*' -Alias '*'
|
||||
@@ -0,0 +1,101 @@
|
||||
function ConvertTo-XMLCertificates {
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
[PSCustomObject] $GPO,
|
||||
[string[]] $Category,
|
||||
[switch] $SingleObject
|
||||
)
|
||||
$SkipNames = ('Name', 'LocalName', 'NamespaceURI', 'Prefix', 'NodeType', 'ParentNode', 'OwnerDocument', 'IsEmpty', 'Attributes', 'HasAttributes', 'SchemaInfo', 'InnerXml', 'InnerText', 'NextSibling', 'PreviousSibling', 'ChildNodes', 'FirstChild', 'LastChild', 'HasChildNodes', 'IsReadOnly', 'OuterXml', 'BaseURI', 'PreviousText')
|
||||
if ($SingleObject) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
Count = 0
|
||||
Settings = $null
|
||||
}
|
||||
[Array] $CreateGPO['Settings'] = foreach ($Setting in $GPO.DataSet) {
|
||||
$SettingName = $Setting.Name -split ":"
|
||||
$MySettings = [ordered] @{
|
||||
CreatedTime = $GPO.CreatedTime # : 06.06.2020 18:03:36
|
||||
ModifiedTime = $GPO.ModifiedTime # : 17.06.2020 16:08:10
|
||||
ReadTime = $GPO.ReadTime # : 13.08.2020 10:15:37
|
||||
SecurityDescriptor = $GPO.SecurityDescriptor # : SecurityDescriptor
|
||||
FilterDataAvailable = $GPO.FilterDataAvailable # : True
|
||||
}
|
||||
$Name = $SettingName[1]
|
||||
#$Name = Format-ToTitleCase -Text $Setting.Name -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':'
|
||||
$MySettings['Name'] = $Name # $Setting.Name
|
||||
|
||||
ConvertTo-XMLNested -CreateGPO $MySettings -Setting $Setting -SkipNames $SkipNames #-Name $Name
|
||||
|
||||
if ($MySettings.Data) {
|
||||
$bytes = $MySettings.Data -replace '\r?\n' -split '(?<=\G.{2})' -ne '' -replace '^', '0x' -as [byte[]]
|
||||
$CertificateData = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($bytes)
|
||||
|
||||
$MySettings['NotBefore'] = $CertificateData.NotBefore
|
||||
$MySettings['NotAfter'] = $CertificateData.NotAfter
|
||||
$MySettings['HasPrivateKey'] = $CertificateData.HasPrivateKey
|
||||
$MySettings['Thumbprint'] = $CertificateData.Thumbprint
|
||||
$MySettings['SerialNumber'] = $CertificateData.SerialNumber
|
||||
$MySettings['Version'] = $CertificateData.Version
|
||||
$MySettings['Handle'] = $CertificateData.Handle
|
||||
$MySettings['SignatureAlgorithm'] = $CertificateData.SignatureAlgorithm.Value
|
||||
$MySettings['SignatureAlgorithmName'] = $CertificateData.SignatureAlgorithm.FriendlyName
|
||||
$MySettings['KeyUsages'] = $CertificateData.Extensions.KeyUsages
|
||||
$MySettings.Remove('Data')
|
||||
}
|
||||
|
||||
[PSCustomObject] $MySettings
|
||||
}
|
||||
|
||||
$CreateGPO['Count'] = $CreateGPO['Settings'].Count
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
} else {
|
||||
foreach ($Setting in $GPO.DataSet) {
|
||||
$CreateGPO = [ordered]@{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DomainName = $GPO.DomainName
|
||||
GUID = $GPO.GUID
|
||||
GpoType = $GPO.GpoType
|
||||
}
|
||||
$SettingName = $Setting.Name -split ":"
|
||||
$CreateGPO['CreatedTime'] = $GPO.CreatedTime # : 06.06.2020 18:03:36
|
||||
$CreateGPO['ModifiedTime'] = $GPO.ModifiedTime # : 17.06.2020 16:08:10
|
||||
$CreateGPO['ReadTime'] = $GPO.ReadTime # : 13.08.2020 10:15:37
|
||||
$CreateGPO['SecurityDescriptor'] = $GPO.SecurityDescriptor # : SecurityDescriptor
|
||||
$CreateGPO['FilterDataAvailable'] = $GPO.FilterDataAvailable # : True
|
||||
|
||||
$Name = $SettingName[1]
|
||||
$CreateGPO['Name'] = $Name # $Setting.Name
|
||||
|
||||
ConvertTo-XMLNested -CreateGPO $CreateGPO -Setting $Setting -SkipNames $SkipNames #-Name $Name
|
||||
|
||||
if ($CreateGPO.Data) {
|
||||
$bytes = $CreateGPO.Data -replace '\r?\n' -split '(?<=\G.{2})' -ne '' -replace '^', '0x' -as [byte[]]
|
||||
$CertificateData = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($bytes)
|
||||
|
||||
$CreateGPO['NotBefore'] = $CertificateData.NotBefore
|
||||
$CreateGPO['NotAfter'] = $CertificateData.NotAfter
|
||||
$CreateGPO['HasPrivateKey'] = $CertificateData.HasPrivateKey
|
||||
$CreateGPO['Thumbprint'] = $CertificateData.Thumbprint
|
||||
$CreateGPO['SerialNumber'] = $CertificateData.SerialNumber
|
||||
$CreateGPO['Version'] = $CertificateData.Version
|
||||
$CreateGPO['Handle'] = $CertificateData.Handle
|
||||
$CreateGPO['SignatureAlgorithm'] = $CertificateData.SignatureAlgorithm.Value
|
||||
$CreateGPO['SignatureAlgorithmName'] = $CertificateData.SignatureAlgorithm.FriendlyName
|
||||
$CreateGPO['KeyUsages'] = $CertificateData.Extensions.KeyUsages
|
||||
$CreateGPO.Remove('Data')
|
||||
}
|
||||
$CreateGPO['Filters'] = $Setting.Filters
|
||||
$CreateGPO['Linked'] = $GPO.Linked
|
||||
$CreateGPO['LinksCount'] = $GPO.LinksCount
|
||||
$CreateGPO['Links'] = $GPO.Links
|
||||
[PSCustomObject] $CreateGPO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,6 +290,7 @@
|
||||
'Problem' = $Problem
|
||||
'ApplyPermission' = $null
|
||||
'Exclude' = $Exclude
|
||||
'Description' = $GPO.Description
|
||||
'ComputerPolicies' = $XMLContent.GPO.Computer.ExtensionData.Name -join ", "
|
||||
'UserPolicies' = $XMLContent.GPO.User.ExtensionData.Name -join ", "
|
||||
'LinksCount' = $LinksTotalCount
|
||||
@@ -315,7 +316,6 @@
|
||||
'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false }
|
||||
'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension
|
||||
'NoSettings' = $NoSettings
|
||||
'Description' = $GPO.Description
|
||||
'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime
|
||||
'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime
|
||||
'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"By default, Default Domain Policy is linked to the domain and is inherited to all the child objects of the domain hierarchy. "
|
||||
"So does any other policies linked to the top level OU's. "
|
||||
}
|
||||
New-HTMLText -Text "Block Inheritance" -FontSize 10pt -FontWeight bold
|
||||
New-HTMLText -Text "Blocked Inheritance" -FontSize 10pt -FontWeight bold
|
||||
New-HTMLText -FontSize 10pt -Text @(
|
||||
"As GPOs can be inherited by default, they can also be blocked, if required using the Block Inheritance. "
|
||||
"If the Block Inheritance setting is enabled, the inheritance of group policy setting is blocked. "
|
||||
@@ -85,10 +85,10 @@
|
||||
New-HTMLText -Text @(
|
||||
'As it stands currently there are ',
|
||||
$Script:Reporting['GPOBlockedInheritance']['Data'].Count,
|
||||
' organiational units with '
|
||||
' organisational units with '
|
||||
'GPO Inheritance Block'
|
||||
' out of which '
|
||||
$Script:Reporting['GPOBlockedInheritance']['Variables']['Exclude'].Count,
|
||||
$Script:Reporting['GPOBlockedInheritance']['Variables']['Exclude'],
|
||||
' are marked as Excluded '
|
||||
'(approved by IT). '
|
||||
) -FontSize 10pt -FontWeight normal, bold, normal, bold, normal, bold, normal, bold -LineBreak
|
||||
@@ -96,9 +96,9 @@
|
||||
New-HTMLText -Text 'Users & Computers affected by inheritance blocks:' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text $Script:Reporting['GPOBlockedInheritance']['Variables']['UsersAffected'], ' users affected due to inheritance blocks' -FontWeight bold, normal
|
||||
New-HTMLListItem -Text $Script:Reporting['GPOBlockedInheritance']['Variables']['UsersAffectedExclude'], ' users affected, but approved/Exclude, due to inheritance blocks' -FontWeight bold, normal
|
||||
New-HTMLListItem -Text $Script:Reporting['GPOBlockedInheritance']['Variables']['UsersAffectedExclude'], ' users affected, but approved/excluded, due to inheritance blocks' -FontWeight bold, normal
|
||||
New-HTMLListItem -Text $Script:Reporting['GPOBlockedInheritance']['Variables']['ComputersAffected'], ' computers affected due to inheritance blocks' -FontWeight bold, normal
|
||||
New-HTMLListItem -Text $Script:Reporting['GPOBlockedInheritance']['Variables']['ComputersAffectedExclude'], ' computers affected, but approved/Exclude, due to inheritance blocks' -FontWeight bold, normal
|
||||
New-HTMLListItem -Text $Script:Reporting['GPOBlockedInheritance']['Variables']['ComputersAffectedExclude'], ' computers affected, but approved/excluded, due to inheritance blocks' -FontWeight bold, normal
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -Text 'Following domains require:' -FontSize 10pt -FontWeight bold
|
||||
@@ -118,7 +118,7 @@
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartLegend -Names 'Affected', 'Affected, but Exclude' -Color Salmon, PaleGreen
|
||||
New-ChartLegend -Names 'Affected', 'Affected, but excluded' -Color Salmon, PaleGreen
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartBar -Name 'Users' -Value $Script:Reporting['GPOBlockedInheritance']['Variables']['UsersAffected'], $Script:Reporting['GPOBlockedInheritance']['Variables']['UsersAffectedExclude']
|
||||
New-ChartBar -Name 'Computers' -Value $Script:Reporting['GPOBlockedInheritance']['Variables']['ComputersAffected'], $Script:Reporting['GPOBlockedInheritance']['Variables']['ComputersAffectedExclude']
|
||||
@@ -137,14 +137,15 @@
|
||||
New-TableCondition -Name 'UsersCount' -Value 0
|
||||
New-TableCondition -Name 'ComputersCount' -Value 0
|
||||
} -BackgroundColor Salmon -FailBackgroundColor Amber -HighlightHeaders 'UsersCount', 'ComputersCount'
|
||||
} -PagingOptions 10, 20, 30, 40, 50 -SearchBuilder -ExcludeProperty GroupPolicies
|
||||
New-TableColumnOption -Hidden $true -ColumnIndex 8
|
||||
} -PagingOptions 5, 10, 20, 30, 40, 50 -SearchBuilder -ExcludeProperty GroupPolicies
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policies affecting objects in Organizational Units with Blocked Inheritance' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['Data'].GroupPolicies -Filtering {
|
||||
New-TableCondition -Name 'Enabled' -Value $true -BackgroundColor SpringGreen -FailBackgroundColor Salmon
|
||||
New-TableCondition -Name 'Enforced' -Value $true -BackgroundColor Amber -FailBackgroundColor AirForceBlue
|
||||
New-TableCondition -Name 'LinkedDirectly' -Value $true -BackgroundColor Amber -FailBackgroundColor AirForceBlue
|
||||
} -PagingOptions 10, 20, 30, 40, 50 -SearchBuilder -ExcludeProperty GroupPolicies -DataTableID 'TableWithGroupPoliciesBlockedInheritance'
|
||||
} -PagingOptions 5, 10, 20, 30, 40, 50 -SearchBuilder -DataTableID 'TableWithGroupPoliciesBlockedInheritance'
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
New-HTMLSection -Name 'Steps to fix - Organizational Units with Group Policy Blocked Inheritance' {
|
||||
@@ -174,7 +175,7 @@
|
||||
}
|
||||
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
|
||||
New-HTMLCodeBlock -Code {
|
||||
$GPOOutput = Get-GPOZaurrInheritance -IncludeBlockedObjects -IncludeExcludeObjects -OnlyBlockedInheritance
|
||||
$GPOOutput = Get-GPOZaurrInheritance -IncludeBlockedObjects -IncludeExcludedObjects -OnlyBlockedInheritance
|
||||
$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."
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
$GPOZaurrOrganizationalUnit = [ordered] @{
|
||||
Name = 'Group Policy Organizational Units'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
Execute = {
|
||||
Get-GPOZaurrOrganizationalUnit -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
Processing = {
|
||||
# Create Per Domain Variables
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['RequiresDiffFixPerDomain'] = @{}
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'] = @{}
|
||||
foreach ($OU in $Script:Reporting['GPOOrganizationalUnit']['Data']) {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['TotalOU']++
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'][$OU.DomainName]) {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'][$OU.DomainName] = 0
|
||||
}
|
||||
|
||||
if ($OU.Status -contains 'Unlink GPO' -and $OU.Status -contains 'Delete OU') {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPODeleteOU']++
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'][$OU.DomainName]++
|
||||
} elseif ($OU.Status -contains 'Unlink GPO') {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPO']++
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'][$OU.DomainName]++
|
||||
} elseif ($OU.Status -contains 'Delete OU') {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['DeleteOU']++
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFix']++
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'][$OU.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['Variables']['Legitimate']++
|
||||
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFix'] -gt 0) {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOOrganizationalUnit']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
TotalOU = 0
|
||||
UnlinkGPO = 0
|
||||
UnlinkGPODeleteOU = 0
|
||||
DeleteOU = 0
|
||||
Legitimate = 0
|
||||
WillFix = 0
|
||||
WillFixPerDomain = $null
|
||||
}
|
||||
Overview = {
|
||||
|
||||
}
|
||||
Summary = {
|
||||
New-HTMLText -FontSize 10pt -Text @(
|
||||
"In most Active Directories there are a lot of Organizational Units that have different use cases to store different type of objects. "
|
||||
"As Active Directories change over time you can often find Organizational Units with linked GPOs and no objects inside. "
|
||||
"In some cases thats's expected, but in some cases it's totally unnessecary, and for very large AD can be a problem. "
|
||||
"Additionally only User and Computer objects can have GPO applied to them, so having GPO applied to a any other object type won't really work. "
|
||||
)
|
||||
New-HTMLText -FontSize 10pt -Text "Following can happen: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Organizational Units that can have Group Policies unlinked (objects exists): ', $Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPO'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Organizational Units that can have Group Policies unlinked and OU removed (be careful!) (no objects): ', $Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPODeleteOU'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Organizational Units that can be deleted (no objects/no gpos): ", $Script:Reporting['GPOOrganizationalUnit']['Variables']['DeleteOU'] -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['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOrganizationalUnit']['Variables']['WillFixPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text @(
|
||||
"Please make sure that you really want to unlink GPO or delete Organizational Unit before executing changes. Sometimes it's completly valid to keep one or the other. "
|
||||
"Unlinking GPO from OU that has no Computer or User objects is fairly safe exercise. Removing OU requires a bit more dive in, and should only be executed if you know what you're doing. "
|
||||
) -FontWeight normal, bold -Color None, Red -FontSize 10pt
|
||||
}
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOOrganizationalUnit']['Summary']
|
||||
}
|
||||
New-HTMLPanel {
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type bar -Distributed
|
||||
New-ChartAxisY -LabelMaxWidth 200 -LabelAlign left -Show
|
||||
New-ChartBar -Name "Unlink GPO ($($Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPO']))" -Value $Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPO']
|
||||
New-ChartBar -Name "Unlink GPO & Delete OU ($($Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPODeleteOU']))" -Value $Script:Reporting['GPOOrganizationalUnit']['Variables']['UnlinkGPODeleteOU']
|
||||
New-ChartBar -Name "Delete OU ($($Script:Reporting['GPOOrganizationalUnit']['Variables']['DeleteOU']))" -Value $Script:Reporting['GPOOrganizationalUnit']['Variables']['DeleteOU']
|
||||
} -Title 'Organizational Units' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Organizational Units' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOrganizationalUnit']['Data'] -Filtering {
|
||||
New-TableHeader -ResponsiveOperations none -Names 'GPONames', 'Objects'
|
||||
New-HTMLTableCondition -Name 'Status' -ComparisonType string -Value 'Unlink GPO, Delete OU' -BackgroundColor Salmon -Row
|
||||
New-HTMLTableCondition -Name 'Status' -ComparisonType string -Value 'Unlink GPO' -BackgroundColor YellowOrange -Row
|
||||
New-HTMLTableCondition -Name 'Status' -ComparisonType string -Value 'Delete OU' -BackgroundColor Red -Row
|
||||
New-HTMLTableCondition -Name 'Status' -ComparisonType string -Value 'OK' -BackgroundColor LightGreen -Row
|
||||
} -PagingOptions 10, 20, 30, 40, 50 -SearchBuilder -ExcludeProperty GPO
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
New-HTMLSection -Name 'Steps to fix Group Organizational Units' {
|
||||
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 unlinking unused Group Policies. To generate new report please use:"
|
||||
New-HTMLCodeBlock -Code {
|
||||
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOrganizationalUnitBefore.html -Verbose -Type GPOOrganizationalUnit
|
||||
}
|
||||
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-GPOZaurrOrganizationalUnit -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 'Unlink unused Group Policies' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed runs cleanup procedure that unlinks all Group Policies from Organizational Units that have no user or computer objects. "
|
||||
"Make sure when running it for the first time to run it with ",
|
||||
"WhatIf",
|
||||
" parameter as shown below to prevent accidental unlinking."
|
||||
'When run it will remove any GPO links from Organizational Units that have no objects applicable for GPOs.'
|
||||
) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrLinkEmptyOU -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-GPOZaurrLinkEmptyOU -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 removed matches expected data. "
|
||||
"Keep in mind that there is no backup for this, and if link is removed you would need to relink it yourself."
|
||||
"Once you remove it, it's gone. "
|
||||
} -LineBreak
|
||||
New-HTMLText -Text 'Once happy with results please follow with command (this will start removal process): ' -LineBreak -FontWeight bold
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrLinkEmptyOU -WhatIf -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-GPOZaurrLinkEmptyOU -WhatIf -LimitProcessing 2 -Verbose -IncludeDomains 'YourDomainYouHavePermissionsFor'
|
||||
}
|
||||
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. "
|
||||
} -LineBreak
|
||||
New-HTMLText -TextBlock {
|
||||
"It's possible to exclude certain OU's from having GPO's unlinked using follwing method: "
|
||||
} -FontWeight bold
|
||||
New-HTMLCodeBlock -Code {
|
||||
$Exclude = @(
|
||||
"OU=Groups,OU=Production,DC=ad,DC=evotec,DC=pl"
|
||||
"OU=Test \, OU,OU=ITR02,DC=ad,DC=evotec,DC=xyz"
|
||||
)
|
||||
Remove-GPOZaurrLinkEmptyOU -Verbose -LimitProcessing 3 -WhatIf -ExcludeOrganizationalUnit $Exclude
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Delete unused Organizational Units' {
|
||||
New-HTMLText -Text @(
|
||||
"Following automation is not yet implemented. Requires more testing as potentially it could do more damage than help."
|
||||
)
|
||||
}
|
||||
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\GPOZaurrGPOOrganizationalUnitAfter.html -Verbose -Type GPOOrganizationalUnit
|
||||
}
|
||||
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
|
||||
}
|
||||
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center -EnableAllAnchors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrganizationalUnit']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOrganizationalUnit']['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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies which exists, but have wrong ObjectClass: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assessed due to permissions issue: ", $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
GPOOwners = $GPOZaurrOwners
|
||||
GPOConsistency = $GPOZaurrConsistency
|
||||
GPODuplicates = $GPOZaurrDuplicates
|
||||
GPOOrganizationalUnit = $GPOZaurrOrganizationalUnit
|
||||
GPOList = $GPOZaurrList
|
||||
GPOLinks = $GPOZaurrLinks
|
||||
GPOPassword = $GPOZaurrPassword
|
||||
|
||||
@@ -789,10 +789,10 @@
|
||||
)
|
||||
GPOPath = 'Policies -> Windows Settings -> Security Settings -> Public Key Policies'
|
||||
Code = {
|
||||
ConvertTo-XMLGenericPublicKey -GPO $GPO
|
||||
ConvertTo-XMLCertificates -GPO $GPO
|
||||
}
|
||||
CodeSingle = {
|
||||
ConvertTo-XMLGenericPublicKey -GPO $GPO -SingleObject
|
||||
ConvertTo-XMLCertificates -GPO $GPO -SingleObject
|
||||
}
|
||||
}
|
||||
<#
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
function Get-GPOZaurrOrganizationalUnit {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[ValidateSet('OK', 'Unlink', 'Delete')][string[]] $Option
|
||||
)
|
||||
$CachedOu = [ordered] @{}
|
||||
$CachedGPO = [ordered] @{}
|
||||
$ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
$GroupPolicies = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
|
||||
foreach ($GPO in $GroupPolicies) {
|
||||
$CachedGPO[$GPO.GPODistinguishedName] = $GPO
|
||||
}
|
||||
foreach ($Domain in $ForestInformation.Domains) {
|
||||
Write-Verbose "Get-GPOZaurrOrganizationalUnit - Processing $($Domain)"
|
||||
$CountTop = 0
|
||||
$TopOrganizationalUnits = Get-ADOrganizationalUnit -Filter * -Properties LinkedGroupPolicyObjects, DistinguishedName, ntSecurityDescriptor -Server $ForestInformation['QueryServers'][$Domain]['hostname'][0] -SearchScope OneLevel
|
||||
foreach ($TopOU in $TopOrganizationalUnits) {
|
||||
$CountTop++
|
||||
Write-Verbose "Get-GPOZaurrOrganizationalUnit - Processing $($Domain) / $($TOPOU.DistinguishedName) [$CountTop/$($TopOrganizationalUnits.Count)]"
|
||||
# cache top ou
|
||||
if ($TopOU.LinkedGroupPolicyObjects) {
|
||||
$LinkedGPOs = $CachedGPO[$TopOU.LinkedGroupPolicyObjects]
|
||||
} else {
|
||||
$LinkedGPOs = $null
|
||||
}
|
||||
$CachedOu[$TopOU.DistinguishedName] = [ordered]@{
|
||||
'LinkedGroupPolicyObjects' = $TopOU.LinkedGroupPolicyObjects
|
||||
'LinkedGroupPolicy' = $LinkedGPOs
|
||||
'Objects' = [ordered] @{}
|
||||
'ObjectsClasses' = [ordered] @{}
|
||||
'ObjectsCountDirect' = 0
|
||||
'ObjectsCountIndirect' = 0
|
||||
'ObjectsCountTotal' = 0
|
||||
'Level' = 'Top'
|
||||
'Domain' = $Domain
|
||||
}
|
||||
|
||||
# cache children OUs
|
||||
$OUs = Get-ADOrganizationalUnit -SearchScope Subtree -SearchBase $TopOU.DistinguishedName -Server $ForestInformation['QueryServers'][$Domain]['hostname'][0] -Properties LinkedGroupPolicyObjects, DistinguishedName -Filter *
|
||||
Write-Verbose "Get-GPOZaurrOrganizationalUnit - Processing $($Domain) / $($TOPOU.DistinguishedName) [$CountTop/$($TopOrganizationalUnits.Count)], found $($OUs.Count) OU's to process."
|
||||
foreach ($OU in $OUs) {
|
||||
if (-not $CachedOu[$OU.DistinguishedName]) {
|
||||
if ($OU.LinkedGroupPolicyObjects) {
|
||||
$LinkedGPOs = $CachedGPO[$OU.LinkedGroupPolicyObjects]
|
||||
} else {
|
||||
$LinkedGPOs = $null
|
||||
}
|
||||
$CachedOu[$OU.DistinguishedName] = [ordered]@{
|
||||
'LinkedGroupPolicyObjects' = $OU.LinkedGroupPolicyObjects
|
||||
'LinkedGroupPolicy' = $LinkedGPOs
|
||||
'Objects' = [ordered] @{}
|
||||
'ObjectsClasses' = [ordered] @{}
|
||||
'ObjectsCountDirect' = 0
|
||||
'ObjectsCountIndirect' = 0
|
||||
'ObjectsCountTotal' = 0
|
||||
'Level' = 'Child'
|
||||
'Domain' = $Domain
|
||||
}
|
||||
}
|
||||
}
|
||||
# Find all objects in those OUs
|
||||
$ObjectsInOu = Get-ADObject -LDAPFilter "(|(ObjectClass=user)(ObjectClass=contact)(ObjectClass=computer)(ObjectClass=group)(objectClass=inetOrgPerson))" -SearchBase $TopOU.distinguishedName -Server $ForestInformation['QueryServers'][$Domain]['hostname'][0]
|
||||
Write-Verbose "Get-GPOZaurrOrganizationalUnit - Processing $($Domain) / $($TOPOU.DistinguishedName) [$CountTop/$($TopOrganizationalUnits.Count)], found $($ObjectsInOu.Count) objects to process."
|
||||
foreach ($Object in $ObjectsInOu) {
|
||||
$Place = ConvertFrom-DistinguishedName -ToOrganizationalUnit -DistinguishedName $Object.DistinguishedName
|
||||
$AllOUs = ConvertFrom-DistinguishedName -ToMultipleOrganizationalUnit -IncludeParent -DistinguishedName $Place
|
||||
foreach ($OU in $AllOUs) {
|
||||
if ($OU -eq $Place) {
|
||||
$CachedOu[$OU]['Objects'][$Object.DistinguishedName] = $Object
|
||||
$CachedOu[$OU]['ObjectsClasses'][$Object.ObjectClass] = ''
|
||||
$CachedOu[$OU]['ObjectsCountDirect']++
|
||||
} else {
|
||||
$CachedOu[$OU]['ObjectsClasses'][$Object.ObjectClass] = ''
|
||||
$CachedOu[$OU]['ObjectsCountIndirect']++
|
||||
}
|
||||
$CachedOu[$OU]['ObjectsCountTotal']++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($OU in $CachedOu.Keys) {
|
||||
$ObjectClasses = [string[]] $CachedOu[$OU]['ObjectsClasses'].Keys
|
||||
|
||||
if ($CachedOu[$OU]['ObjectsCountTotal'] -eq 0 -and $CachedOu[$OU]['LinkedGroupPolicyObjects'].Count -gt 0) {
|
||||
$Status = "Unlink GPO", 'Delete OU'
|
||||
} elseif ($CachedOu[$OU]['ObjectsCountTotal'] -eq 0 -and $CachedOu[$OU]['LinkedGroupPolicyObjects'].Count -eq 0) {
|
||||
$Status = 'Delete OU'
|
||||
} elseif ($CachedOU[$Ou]['ObjectsCountTotal'] -gt 0 -and $CachedOu[$OU]['LinkedGroupPolicyObjects'].Count -gt 0 -and $ObjectClasses -notcontains 'User' -and $ObjectClasses -notcontains 'Computer' ) {
|
||||
$Status = "Unlink GPO"
|
||||
} else {
|
||||
$Status = 'OK'
|
||||
}
|
||||
|
||||
|
||||
if ($Option) {
|
||||
$Found = $false
|
||||
if ($Option -contains 'Ok' -and $Status -contains 'OK') {
|
||||
$Found = $true
|
||||
} elseif ($Option -contains 'Unlink' -and $Status -contains 'Unlink GPO') {
|
||||
$Found = $true
|
||||
} elseif ($Option -contains 'Delete' -and $Status -contains 'Delete OU') {
|
||||
$Found = $true
|
||||
}
|
||||
if (-not $Found) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[PSCustomObject] @{
|
||||
Organizationalunit = $OU
|
||||
Level = $CachedOu[$OU]['Level']
|
||||
DomainName = $CachedOu[$OU]['Domain']
|
||||
Status = $Status
|
||||
GPOCount = $CachedOu[$OU]['LinkedGroupPolicyObjects'].Count
|
||||
ObjectCountDirect = $CachedOu[$OU]['ObjectsCountDirect']
|
||||
ObjectCountIndirect = $CachedOu[$OU]['ObjectsCountIndirect']
|
||||
ObjectCountTotal = $CachedOu[$OU]['ObjectsCountTotal']
|
||||
ObjectClasses = $ObjectClasses
|
||||
GPONames = $CachedOu[$OU]['LinkedGroupPolicy'].DisplayName
|
||||
Objects = $CachedOu[$OU]['Objects'].Values.Name
|
||||
GPO = $CachedOu[$OU]['LinkedGroupPolicy']
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,9 @@
|
||||
$DisplayProperties = @(
|
||||
$FirstProperties
|
||||
foreach ($Property in $Properties) {
|
||||
$Property
|
||||
if ($Property -notin $FirstProperties -and $Property -notin $EndProperties) {
|
||||
$Property
|
||||
}
|
||||
}
|
||||
$EndProperties
|
||||
)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
function Remove-GPOZaurrLinkEmptyOU {
|
||||
[cmdletbinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[alias('ForestName')][string] $Forest,
|
||||
[string[]] $ExcludeDomains,
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
|
||||
[string[]] $ExcludeOrganizationalUnit,
|
||||
|
||||
[int] $LimitProcessing = [int32]::MaxValue
|
||||
)
|
||||
|
||||
$Processed = 0
|
||||
$OrganizationalUnits = Get-GPOZaurrOrganizationalUnit -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Option Unlink
|
||||
foreach ($OU in $OrganizationalUnits) {
|
||||
if ($OU.Status -contains 'Unlink GPO') {
|
||||
if ($OU.OrganizationalUnit -in $ExcludeOrganizationalUnit) {
|
||||
Write-Verbose "Remove-GPOZaurrLinkEmptyOU - Processing $($OU.Organizationalunit) was skipped as it's excluded."
|
||||
continue
|
||||
}
|
||||
Write-Verbose "Remove-GPOZaurrLinkEmptyOU - Processing $($OU.Organizationalunit) found OU with GPOs to unlink"
|
||||
$Processed++
|
||||
foreach ($GPO in $OU.GPO) {
|
||||
Write-Verbose "Remove-GPOZaurrLinkEmptyOU - Removing $($GPO.DisplayName) link from $($OU.Organizationalunit)"
|
||||
try {
|
||||
Remove-GPLink -ErrorAction Stop -Guid $GPO.GUID -Domain $GPO.DomainName -Target $OU.Organizationalunit
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrLinkEmptyOU - Error removing link of $($GPO.DisplayName) from $($OU.OrganizationalUnit) error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
if ($Processed -ge $LimitProcessing) {
|
||||
Write-Verbose "Remove-GPOZaurrLinkEmptyOU - Limit processing hit, stopping."
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,9 @@
|
||||
.PARAMETER LimitProcessing
|
||||
Allows to specify maximum number of items that will be fixed in a single run. It doesn't affect amount of GPOs processed
|
||||
|
||||
.PARAMETER Force
|
||||
Pushes new owner regardless if it's already set or not
|
||||
|
||||
.EXAMPLE
|
||||
Set-GPOZaurrOwner -Type All -Verbose -WhatIf -LimitProcessing 2
|
||||
|
||||
@@ -72,11 +75,16 @@
|
||||
[Parameter(ParameterSetName = 'Named')]
|
||||
[string] $Principal,
|
||||
|
||||
[Parameter(ParameterSetName = 'Type')]
|
||||
[Parameter(ParameterSetName = 'Named')]
|
||||
[switch] $SkipSysvol,
|
||||
|
||||
[Parameter(ParameterSetName = 'Type')]
|
||||
[Parameter(ParameterSetName = 'Named')]
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
|
||||
[Parameter(ParameterSetName = 'Type')]
|
||||
[Parameter(ParameterSetName = 'Named')]
|
||||
[switch] $Force
|
||||
)
|
||||
Begin {
|
||||
|
||||
@@ -55,6 +55,22 @@ To understand the usage I've created blog post you may find useful
|
||||
|
||||
## Changelog
|
||||
|
||||
- 0.0.129
|
||||
- Added `Get-GPOZaurrOrganizationalUnit` and added `GPOOrganizationalUnit` in `Invoke-GPOZaurr` (preview)
|
||||
- Added `Remove-GPOZaurrLinkEmptyOU` which allows removing links from Empty OUs (preview)
|
||||
- Small update to parameter sets for `Set-GPOZaurrOwner`
|
||||
- 0.0.128 - 2021.05.26
|
||||
- ☑ Improved `Invoke-GPOZaurrContent` - type `PublicKeyPoliciesCertificates` - added more certificate information
|
||||
- ☑ Improved `Invoke-GPOZaurr` - type `GPOAnalysis` - added more certificate information
|
||||
- 0.0.128 Alpha 1 - 2021.05.17
|
||||
- 🐛 Fixes errors when normalizing properties [#17](https://github.com/EvotecIT/GPOZaurr/issues/17)
|
||||
- 0.0.127 - 2021.04.15
|
||||
- ☑ Improved `Invoke-GPOZaurr`
|
||||
- ☑ Report `GPOList` - moved description closer to statuses
|
||||
- ☑ Improved `Get-GPOZaurr` - moved description closer to statuses
|
||||
- 0.0.126 - 2021.04.12
|
||||
- ☑ Improved `Invoke-GPOZaurr`
|
||||
- ☑ Report `GPOBlockedInheritance` - hidden DistinguishedName, fixed some small typos
|
||||
- 0.0.125 - 2021.04.11
|
||||
- ☑ Improved `Invoke-GPOZaurr`
|
||||
- ☑ Report `GPOBlockedInheritance` - small fixes
|
||||
|
||||
Reference in New Issue
Block a user