Public/Get-ADPeerGroupStatistics.ps1

function Get-ADPeerGroupStatistics {
    <#
    .SYNOPSIS
        Finds active directory peers of a user (matching Department and Title) and outputs common group memberships with statistics.
 
    .DESCRIPTION
        Analyzes the target user along with their peers in the specified OU tree. Aggregates group memberships,
        filters out specified exclusion patterns, applies a minimum threshold percentage, and outputs structured
        objects sorted by match percentage.
 
    .PARAMETER Identity
        The target AD user object (accepts pipeline input). Must contain or evaluate Department, Title, and SamAccountName properties.
 
    .PARAMETER ExcludeGroup
        An array of group names or wildcard patterns to ignore (e.g., "*VPN*", "Domain Users").
 
    .EXAMPLE
        Get-ADUser "jdoe" -Properties Department, Title | Get-ADPeerGroupStatistics
 
    .EXAMPLE
        Get-ADPeerGroupStatistics -Identity $NewHire -ExcludeGroup "*Temp*", "Domain Users"
    #>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Identity,

        [Parameter(Mandatory = $false)]
        [string[]]$ExcludeGroup = @(
            "Domain Users",
            "VPN_Users",
            "VMWare Notifications" # Add any default exclusions here
        )
    )

    # Ensure Active Directory module is available
    if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
        Write-Error "The ActiveDirectory PowerShell module is required but not installed."
        return
    }

    # 1. Get the target user and their attributes
    Write-Host "Fetching details for target user: $Identity..." -ForegroundColor Cyan
    $targetUser = Get-ADUser -Identity $Identity -Properties Department, Title

    if (-not $targetUser) {
        Write-Error "User '$Identity' not found in Active Directory."
        return
    }

    $department = $targetUser.Department
    $title = $targetUser.Title

    if (-not $department -or -not $title) {
        Write-Warning "Target user is missing a Department or Title attribute. Results may be empty."
        Write-Host "Department: '$department' | Title: '$title'" -ForegroundColor Yellow
    }

    # 2. Find all peers within the target OU and sub-OUs
    Write-Host "Finding peers in OU and sub-OUs with Department: '$department' and Title: '$title'..." -ForegroundColor Cyan
    $targetOU = "OU=Accounts,DC=tcu,DC=ad,DC=local"
    $filter = "Department -eq '$department' -and Title -eq '$title' -and sAMAccountName -ne '$($targetUser.sAMAccountName)'"
    
    $peers = Get-ADUser -Filter $filter -SearchBase $targetOU -SearchScope Subtree -Properties MemberOf | Where-Object { $_.Enabled -eq $true }

    $totalPeers = @($peers).Count

    if ($totalPeers -eq 0) {
        Write-Host "No active peers found sharing the exact Department and Title in the specified OU or its sub-OUs." -ForegroundColor Yellow
        return
    }

    Write-Host "Found $totalPeers active peer(s). Analyzing group memberships..." -ForegroundColor Green

    # 3. Aggregate group memberships across all peers`
    $groupCounter = @{}

    foreach ($peer in $peers) {
        foreach ($groupDN in $peer.MemberOf) {
            if ($groupCounter.ContainsKey($groupDN)) {
                $groupCounter[$groupDN]++
            } else {
                $groupCounter[$groupDN] = 1
            }
        }
    }

    # 4. Process results, fetch the full AD group object, and filter out excluded groups
    $results = foreach ($groupDN in $groupCounter.Keys) {
        $count = $groupCounter[$groupDN]
        $percentage = [Math]::Round(($count / $totalPeers) * 100, 2)

        $groupObj = Get-ADGroup -Identity $groupDN -Properties Description, DisplayName, GroupScope, GroupCategory -ErrorAction SilentlyContinue

        if ($groupObj) {
            # Check if the group name matches any item in the exclusion list
            $isExcluded = $false
            foreach ($pattern in $ExcludeGroup) {
                if ($groupObj.Name -like $pattern) {
                    $isExcluded = $true
                    break
                }
            }

            # Only process/output the group if it's NOT excluded
            if (-not $isExcluded) {
                $groupObj | Add-Member -NotePropertyName "PeerMatchPercentage" -NotePropertyValue $percentage -Force
                $groupObj | Add-Member -NotePropertyName "PeerCountWithGroup" -NotePropertyValue $count -Force
                $groupObj | Add-Member -NotePropertyName "TotalPeersAnalyzed" -NotePropertyValue $totalPeers -Force

                $groupObj
            }
        }
    }

    # 5. Return sorted results cleanly down the pipeline
    Write-Host "Analysis complete. Outputting filtered AD group objects..." -ForegroundColor Green

    $results | Sort-Object PeerMatchPercentage -Descending
}