Private/ADMonitor/Detections/Test-ADOUPermissionChange.ps1

# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0
# https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/
# AI/LLM use: see AI-USAGE.md for required attribution
function Test-ADOUPermissionChange {
    [CmdletBinding()]
    param(
        [array]$ACLChanges = @()
    )

    $indicators = [System.Collections.Generic.List[PSCustomObject]]::new()

    if ($ACLChanges.Count -eq 0) { return @() }

    # Filter to OU-related ACL changes (not AdminSDHolder, not domain root)
    $ouChanges = @($ACLChanges | Where-Object {
        $_.ObjectDN -ne 'AdminSDHolder' -and (
            ($_.ContainsKey('ObjectDN') -and $_.ObjectDN -match 'OU=') -or
            ($_.ContainsKey('ObjectName') -and $_.ObjectName -match 'OU=')
        )
    })

    if ($ouChanges.Count -eq 0) { return @() }

    # Group by object
    $byObject = @{}
    foreach ($change in $ouChanges) {
        $key = if ($change.ContainsKey('ObjectName')) { $change.ObjectName } else { $change.ObjectDN }
        if (-not $byObject.ContainsKey($key)) {
            $byObject[$key] = [System.Collections.Generic.List[hashtable]]::new()
        }
        $byObject[$key].Add($change)
    }

    foreach ($objName in $byObject.Keys) {
        $objChanges = @($byObject[$objName])
        $detectionId = "adOUPermissionChange_$($objName -replace '[=,\s\\\/]', '_')"

        $changeDescriptions = @($objChanges | ForEach-Object {
            $identity = if ($_.ContainsKey('Identity')) { $_.Identity } else { 'Unknown' }
            $rights = if ($_.ContainsKey('Rights')) { $_.Rights } else { '' }
            "$($_.ChangeType): $identity ($rights)"
        })

        $indicators.Add([PSCustomObject]@{
            DetectionId   = $detectionId
            DetectionName = "OU Permission Change: $objName"
            DetectionType = 'adOUPermissionChange'
            Description   = "OU PERMISSION CHANGE - ACL modified on '$objName': $($changeDescriptions -join '; ')"
            Details       = @{
                ObjectName = $objName
                Changes    = @($objChanges)
            }
            Count         = $objChanges.Count
            Score         = 0
            Severity      = ''
        })
    }

    return @($indicators)
}