Files
GPOZaurr/Public/Get-GPOZaurrOrganizationalUnit.ps1
T
2024-11-20 20:35:46 +01:00

198 lines
10 KiB
PowerShell

function Get-GPOZaurrOrganizationalUnit {
<#
.SYNOPSIS
Retrieves information about Group Policy Objects (GPOs) linked to Organizational Units (OUs) within a specified forest.
.DESCRIPTION
This function retrieves detailed information about the GPOs linked to OUs within a specified forest. It provides information on linked GPOs, objects within OUs, and counts of objects at different levels.
.PARAMETER Forest
Specifies the name of the forest to retrieve information from.
.PARAMETER ExcludeDomains
Specifies an array of domains to exclude from processing.
.PARAMETER IncludeDomains
Specifies an array of domains to include for processing.
.PARAMETER ExtendedForestInformation
Specifies additional information about the forest.
.PARAMETER Option
Specifies the action to perform on the retrieved data. Valid values are 'OK', 'Unlink', or 'Delete'.
.PARAMETER ExcludeOrganizationalUnit
Specifies an array of OUs to exclude from processing.
.EXAMPLE
Get-GPOZaurrOrganizationalUnit -Forest "contoso.com" -IncludeDomains "child.contoso.com" -ExcludeDomains "test.contoso.com" -ExtendedForestInformation $ExtendedInfo -Option "OK" -ExcludeOrganizationalUnit "OU=Test,DC=contoso,DC=com"
Retrieves information about GPOs linked to OUs in the "contoso.com" forest, including the "child.contoso.com" domain, excluding the "test.contoso.com" domain, with additional forest information, performing the 'OK' action, and excluding the "OU=Test,DC=contoso,DC=com" OU.
#>
[CmdletBinding()]
param(
[alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[System.Collections.IDictionary] $ExtendedForestInformation,
[ValidateSet('OK', 'Unlink', 'Delete')][string[]] $Option,
[alias('ExcludeOU', 'Exclusions')][string[]] $ExcludeOrganizationalUnit
)
$CachedOu = [ordered] @{}
$CachedGPO = [ordered] @{}
$ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
$DefaultFolders = Get-WellKnownFolders -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
[Array] $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'
'RootLevel' = $TopOU.Name
'Domain' = $Domain
}
# cache children OUs
[Array] $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'
'RootLevel' = $TopOU.Name
'Domain' = $Domain
}
}
}
# Find all objects in those OUs
$ObjectsInOu = Get-ADObject -LDAPFilter "(|(ObjectClass=user)(ObjectClass=contact)(ObjectClass=computer)(ObjectClass=group)(objectClass=inetOrgPerson)(ObjectClass=PrintQueue))" -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
[Array] $AllOUs = ConvertFrom-DistinguishedName -ToMultipleOrganizationalUnit -IncludeParent -DistinguishedName $Place
foreach ($OU in $AllOUs) {
if (-not $CachedOu[$OU]) {
Write-Warning "Get-GPOZaurrOrganizationalUnit - Object $($Object.DistinguishedName) is in OU $($OU) but it's not in cache. This should not happen. Please report this issue."
Write-Warning "Get-GPOZaurrOrganizationalUnit - Debug information: Place: $($Place), AllOUs: $($AllOUs.Count)"
continue
}
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 ($ExcludeOrganizationalUnit) {
foreach ($ExcludedOU in $ExcludeOrganizationalUnit) {
if ($OU -like $ExcludedOU) {
$Found = $false
break
}
}
}
foreach ($Exclude in $DefaultFolders) {
if ($OU -eq "$Exclude") {
$Found = $false
break
}
}
if (-not $Found) {
continue
}
} else {
if ($ExcludeOrganizationalUnit) {
foreach ($ExcludedOU in $ExcludeOrganizationalUnit) {
if ($OU -like $ExcludedOU) {
$Status = 'Excluded'
break
}
}
}
foreach ($Exclude in $DefaultFolders) {
if ($OU -eq "$Exclude") {
$Status = 'Excluded, Default OU'
break
}
}
}
[PSCustomObject] @{
Organizationalunit = $OU
Level = $CachedOu[$OU]['Level']
RootLevel = $CachedOu[$OU]['RootLevel']
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']
}
}
}