Add 12 AI-generated PowerShell scripts with documentation

⚠️ IMPORTANT: These scripts are AI-GENERATED and UNTESTED

Exchange Scripts (5):
- Get-MailboxPermissions.ps1: Audit delegate access permissions
- Get-InactiveMailboxes.ps1: Identify stale mailboxes
- Compare-MailboxDatabases.ps1: Database health comparison
- Export-DistributionGroups.ps1: Distribution group inventory
- Get-MailflowStats.ps1: Transport log analysis

Active Directory Scripts (3):
- Get-ADUserLastLogon.ps1: True LastLogon across all DCs
- Export-OUStructure.ps1: OU hierarchy with GPO links
- Compare-ADGroupMemberships.ps1: Compare user group memberships

System Maintenance Scripts (4):
- Get-ServerInventory.ps1: Hardware/software inventory report
- Monitor-DiskSpace.ps1: Disk space monitoring with alerts
- Backup-ExchangeCertificates.ps1: Certificate backup to PFX
- Test-ExchangeHealth.ps1: Aggregated Exchange health checks

Documentation:
- Updated CLAUDE.md with AI-generated scripts section
- Added AI-GENERATED-SCRIPTS.md with warnings and testing guide

All scripts include prominent warnings and follow established patterns
from existing scripts. Require thorough testing before production use.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Martien de Kleijn
2025-10-15 10:52:44 +02:00
parent 5e9d160d48
commit 62134801aa
14 changed files with 2565 additions and 0 deletions

View File

@ -0,0 +1,215 @@
<#
.SYNOPSIS
Compare group memberships between two Active Directory users
.DESCRIPTION
Shows differences in group memberships between two users. Useful for
access troubleshooting, onboarding/offboarding validation, or role comparison.
.PARAMETER User1
First user (SamAccountName, UserPrincipalName, or DistinguishedName)
.PARAMETER User2
Second user (SamAccountName, UserPrincipalName, or DistinguishedName)
.PARAMETER OutputFolder
Destination folder for reports. Default: .\GroupComparison-<date>
.PARAMETER ShowCommonGroups
Include groups common to both users in report (default: $true)
.NOTES
⚠️ AI-GENERATED SCRIPT - UNTESTED
This script was generated by Claude AI and has not been tested in production.
Review and test thoroughly in a non-production environment before use.
- Requires Active Directory PowerShell module
- Run with appropriate AD permissions
- Includes nested group memberships
.EXAMPLE
.\Compare-ADGroupMemberships.ps1 -User1 "jsmith" -User2 "jdoe"
.EXAMPLE
.\Compare-ADGroupMemberships.ps1 -User1 "john.smith@domain.com" -User2 "jane.doe@domain.com" -ShowCommonGroups $false
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$User1,
[Parameter(Mandatory = $true)]
[string]$User2,
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("GroupComparison-" + (Get-Date -Format "yyyyMMdd-HHmm"))),
[bool]$ShowCommonGroups = $true
)
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
Write-Host "[$(NowTag)] Comparing AD group memberships..." -ForegroundColor Green
# Import AD module
try {
Import-Module ActiveDirectory -ErrorAction Stop
} catch {
Write-Host "[$(NowTag)] ERROR: Active Directory module not available" -ForegroundColor Red
exit 1
}
# Create output folder
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
# Get user objects
Write-Host "[$(NowTag)] Retrieving user information..."
try {
$adUser1 = Get-ADUser -Identity $User1 -Properties MemberOf -ErrorAction Stop
Write-Host "[$(NowTag)] User 1: $($adUser1.Name) ($($adUser1.SamAccountName))"
} catch {
Write-Host "[$(NowTag)] ERROR: Could not find user: $User1" -ForegroundColor Red
exit 1
}
try {
$adUser2 = Get-ADUser -Identity $User2 -Properties MemberOf -ErrorAction Stop
Write-Host "[$(NowTag)] User 2: $($adUser2.Name) ($($adUser2.SamAccountName))"
} catch {
Write-Host "[$(NowTag)] ERROR: Could not find user: $User2" -ForegroundColor Red
exit 1
}
# Get group memberships
Write-Host "[$(NowTag)] Retrieving group memberships..."
$user1Groups = @()
if ($adUser1.MemberOf) {
foreach ($groupDN in $adUser1.MemberOf) {
try {
$group = Get-ADGroup -Identity $groupDN -Properties Description -ErrorAction SilentlyContinue
if ($group) {
$user1Groups += [PSCustomObject]@{
Name = $group.Name
DN = $group.DistinguishedName
Description = $group.Description
GroupScope = $group.GroupScope
GroupCategory = $group.GroupCategory
}
}
} catch {}
}
}
$user2Groups = @()
if ($adUser2.MemberOf) {
foreach ($groupDN in $adUser2.MemberOf) {
try {
$group = Get-ADGroup -Identity $groupDN -Properties Description -ErrorAction SilentlyContinue
if ($group) {
$user2Groups += [PSCustomObject]@{
Name = $group.Name
DN = $group.DistinguishedName
Description = $group.Description
GroupScope = $group.GroupScope
GroupCategory = $group.GroupCategory
}
}
} catch {}
}
}
Write-Host "[$(NowTag)] User 1 group count: $($user1Groups.Count)"
Write-Host "[$(NowTag)] User 2 group count: $($user2Groups.Count)"
# Compare memberships
$user1GroupNames = $user1Groups | Select-Object -ExpandProperty Name
$user2GroupNames = $user2Groups | Select-Object -ExpandProperty Name
$uniqueToUser1 = $user1Groups | Where-Object { $_.Name -notin $user2GroupNames }
$uniqueToUser2 = $user2Groups | Where-Object { $_.Name -notin $user1GroupNames }
$commonGroups = $user1Groups | Where-Object { $_.Name -in $user2GroupNames }
# Export results
Write-Host "[$(NowTag)] Exporting comparison results..."
# Unique to User 1
if ($uniqueToUser1.Count -gt 0) {
$file1 = Join-Path $OutputFolder "Groups-UniqueToUser1-$($adUser1.SamAccountName).csv"
$uniqueToUser1 | Sort-Object Name | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $file1
Write-Host "[$(NowTag)] Groups unique to $($adUser1.Name): $file1" -ForegroundColor Cyan
}
# Unique to User 2
if ($uniqueToUser2.Count -gt 0) {
$file2 = Join-Path $OutputFolder "Groups-UniqueToUser2-$($adUser2.SamAccountName).csv"
$uniqueToUser2 | Sort-Object Name | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $file2
Write-Host "[$(NowTag)] Groups unique to $($adUser2.Name): $file2" -ForegroundColor Cyan
}
# Common groups
if ($ShowCommonGroups -and $commonGroups.Count -gt 0) {
$fileCommon = Join-Path $OutputFolder "Groups-Common.csv"
$commonGroups | Sort-Object Name | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $fileCommon
Write-Host "[$(NowTag)] Common groups: $fileCommon" -ForegroundColor Green
}
# Summary report
$summaryFile = Join-Path $OutputFolder "Comparison-Summary.txt"
$summary = @"
Active Directory Group Membership Comparison
Generated: $(Get-Date)
User 1: $($adUser1.Name) ($($adUser1.SamAccountName))
User 2: $($adUser2.Name) ($($adUser2.SamAccountName))
SUMMARY:
User 1 Total Groups: $($user1Groups.Count)
User 2 Total Groups: $($user2Groups.Count)
Common Groups: $($commonGroups.Count)
Groups Unique to User 1: $($uniqueToUser1.Count)
Groups Unique to User 2: $($uniqueToUser2.Count)
GROUPS UNIQUE TO USER 1 ($($adUser1.Name)):
$($uniqueToUser1 | ForEach-Object { " - $($_.Name)" } | Out-String)
GROUPS UNIQUE TO USER 2 ($($adUser2.Name)):
$($uniqueToUser2 | ForEach-Object { " - $($_.Name)" } | Out-String)
$(if ($ShowCommonGroups) {
"COMMON GROUPS:
$($commonGroups | ForEach-Object { " - $($_.Name)" } | Out-String)"
} else { "" })
"@
$summary | Out-File -FilePath $summaryFile -Encoding UTF8
Write-Host "[$(NowTag)] Summary report: $summaryFile" -ForegroundColor Green
# Console output
Write-Host "`nCOMPARISON RESULTS:" -ForegroundColor Cyan
Write-Host " Common Groups: $($commonGroups.Count)" -ForegroundColor Green
Write-Host " Unique to $($adUser1.Name): $($uniqueToUser1.Count)" -ForegroundColor Yellow
Write-Host " Unique to $($adUser2.Name): $($uniqueToUser2.Count)" -ForegroundColor Yellow
if ($uniqueToUser1.Count -gt 0) {
Write-Host "`nGroups Only in $($adUser1.Name):" -ForegroundColor Yellow
$uniqueToUser1 | Select-Object -First 10 | ForEach-Object {
Write-Host " - $($_.Name)"
}
if ($uniqueToUser1.Count -gt 10) {
Write-Host " ... and $($uniqueToUser1.Count - 10) more (see CSV)"
}
}
if ($uniqueToUser2.Count -gt 0) {
Write-Host "`nGroups Only in $($adUser2.Name):" -ForegroundColor Yellow
$uniqueToUser2 | Select-Object -First 10 | ForEach-Object {
Write-Host " - $($_.Name)"
}
if ($uniqueToUser2.Count -gt 10) {
Write-Host " ... and $($uniqueToUser2.Count - 10) more (see CSV)"
}
}
Write-Host "`n[$(NowTag)] Comparison complete! Output folder: $OutputFolder"

View File

@ -0,0 +1,201 @@
<#
.SYNOPSIS
Export Active Directory OU structure with GPO links
.DESCRIPTION
Documents the complete OU hierarchy including GPO links, inheritance settings,
and description. Useful for documentation, disaster recovery, or comparing
environments.
.PARAMETER OutputFolder
Destination folder for reports. Default: .\OU-Structure-<date>
.PARAMETER IncludeGPODetails
Include detailed GPO information (default: $true)
.PARAMETER SearchBase
Optional starting point for OU export (default: domain root)
.NOTES
⚠️ AI-GENERATED SCRIPT - UNTESTED
This script was generated by Claude AI and has not been tested in production.
Review and test thoroughly in a non-production environment before use.
- Requires Active Directory PowerShell module
- Run with appropriate AD permissions
- Exports to both CSV (detailed) and TXT (tree view)
.EXAMPLE
.\Export-OUStructure.ps1
.EXAMPLE
.\Export-OUStructure.ps1 -SearchBase "OU=Departments,DC=domain,DC=com"
#>
[CmdletBinding()]
param(
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("OU-Structure-" + (Get-Date -Format "yyyyMMdd-HHmm"))),
[bool]$IncludeGPODetails = $true,
[string]$SearchBase = ""
)
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
Write-Host "[$(NowTag)] Exporting OU structure..." -ForegroundColor Green
# Import AD module
try {
Import-Module ActiveDirectory -ErrorAction Stop
} catch {
Write-Host "[$(NowTag)] ERROR: Active Directory module not available" -ForegroundColor Red
exit 1
}
# Create output folder
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
# Get domain info
$domain = Get-ADDomain
if (-not $SearchBase) {
$SearchBase = $domain.DistinguishedName
}
Write-Host "[$(NowTag)] Domain: $($domain.DNSRoot)"
Write-Host "[$(NowTag)] Search Base: $SearchBase"
# Get all OUs
Write-Host "[$(NowTag)] Retrieving organizational units..."
$ouParams = @{
Filter = '*'
SearchBase = $SearchBase
Properties = @('Description', 'gPLink', 'gPOptions', 'ProtectedFromAccidentalDeletion', 'WhenCreated', 'WhenChanged')
}
$ous = Get-ADOrganizationalUnit @ouParams | Sort-Object DistinguishedName
$ouCount = ($ous | Measure-Object).Count
Write-Host "[$(NowTag)] Found $ouCount organizational unit(s)"
# Build GPO name lookup if requested
$gpoLookup = @{}
if ($IncludeGPODetails) {
Write-Host "[$(NowTag)] Building GPO lookup table..."
try {
Import-Module GroupPolicy -ErrorAction Stop
$gpos = Get-GPO -All -ErrorAction SilentlyContinue
foreach ($gpo in $gpos) {
$gpoLookup[$gpo.Id.ToString()] = $gpo.DisplayName
}
Write-Host "[$(NowTag)] Found $($gpos.Count) GPO(s)"
} catch {
Write-Host "[$(NowTag)] WARNING: Could not load GroupPolicy module - GPO names will show as GUIDs" -ForegroundColor Yellow
}
}
# Process OUs
$ouDetails = @()
$treeLines = @()
foreach ($ou in $ous) {
# Calculate depth for tree view
$depth = ($ou.DistinguishedName -split ',OU=').Count - 1
$indent = " " * $depth
# Parse GPO links
$gpoLinks = @()
$gpoNames = @()
if ($ou.gPLink) {
# gPLink format: [LDAP://cn={GUID},cn=policies,cn=system,DC=domain,DC=com;0]
$links = $ou.gPLink -split '\]\[' -replace '^\[' -replace '\]$'
foreach ($link in $links) {
if ($link -match '\{([^\}]+)\}') {
$gpoGuid = $matches[1]
$gpoName = if ($gpoLookup.ContainsKey($gpoGuid)) {
$gpoLookup[$gpoGuid]
} else {
$gpoGuid
}
# Parse link order and enforcement
$linkOrder = if ($link -match ';(\d+)') { $matches[1] } else { "0" }
$enforced = $linkOrder -band 1 # Bit 0 indicates enforcement
$disabled = $linkOrder -band 2 # Bit 1 indicates disabled
$gpoNames += $gpoName
$gpoLinks += [PSCustomObject]@{
OU = $ou.Name
GPOName = $gpoName
GPOGUID = $gpoGuid
LinkOrder = $linkOrder
Enforced = ($enforced -eq 1)
Disabled = ($disabled -eq 2)
}
}
}
}
# GPO inheritance blocked?
$inheritanceBlocked = ($ou.gPOptions -eq 1)
# Tree view line
$protectedMark = if ($ou.ProtectedFromAccidentalDeletion) { "[P]" } else { "" }
$inheritMark = if ($inheritanceBlocked) { "[BLOCKED]" } else { "" }
$gpoMark = if ($gpoNames.Count -gt 0) { " (GPOs: $($gpoNames -join ', '))" } else { "" }
$treeLines += "$indent$($ou.Name) $protectedMark$inheritMark$gpoMark"
# Detailed record
$ouDetails += [PSCustomObject]@{
Name = $ou.Name
DistinguishedName = $ou.DistinguishedName
Description = $ou.Description
ProtectedFromAccidentalDeletion = $ou.ProtectedFromAccidentalDeletion
GPOInheritanceBlocked = $inheritanceBlocked
LinkedGPOs = ($gpoNames -join "; ")
LinkedGPOCount = $gpoNames.Count
WhenCreated = $ou.WhenCreated
WhenChanged = $ou.WhenChanged
}
}
# Export detailed CSV
$csvFile = Join-Path $OutputFolder "OU-Structure.csv"
$ouDetails | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $csvFile
Write-Host "[$(NowTag)] OU details exported: $csvFile" -ForegroundColor Green
# Export GPO links if available
if ($IncludeGPODetails -and $gpoLinks.Count -gt 0) {
$gpoLinksFile = Join-Path $OutputFolder "OU-GPO-Links.csv"
$gpoLinks | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $gpoLinksFile
Write-Host "[$(NowTag)] GPO links exported: $gpoLinksFile" -ForegroundColor Green
}
# Export tree view
$treeFile = Join-Path $OutputFolder "OU-Tree.txt"
$treeHeader = @"
Active Directory OU Structure
Domain: $($domain.DNSRoot)
Search Base: $SearchBase
Generated: $(Get-Date)
Legend:
[P] = Protected from Accidental Deletion
[BLOCKED] = GPO Inheritance Blocked
"@
$treeHeader | Out-File -FilePath $treeFile -Encoding UTF8
$treeLines | Out-File -FilePath $treeFile -Encoding UTF8 -Append
Write-Host "[$(NowTag)] Tree view exported: $treeFile" -ForegroundColor Green
# Summary
Write-Host "`nOU STRUCTURE SUMMARY:" -ForegroundColor Cyan
Write-Host " Total OUs: $ouCount"
Write-Host " Protected OUs: $(($ouDetails | Where-Object ProtectedFromAccidentalDeletion).Count)"
Write-Host " OUs with GPO Inheritance Blocked: $(($ouDetails | Where-Object GPOInheritanceBlocked).Count)"
Write-Host " OUs with Linked GPOs: $(($ouDetails | Where-Object { $_.LinkedGPOCount -gt 0 }).Count)"
Write-Host "`n[$(NowTag)] Export complete! Output folder: $OutputFolder"

View File

@ -0,0 +1,185 @@
<#
.SYNOPSIS
Report on inactive Active Directory user accounts based on true LastLogon
.DESCRIPTION
Queries all domain controllers to find the most recent LastLogon time for users.
The LastLogon attribute is not replicated, so each DC must be queried individually
to get accurate results. Identifies accounts inactive for specified days.
.PARAMETER InactiveDays
Number of days without logon to consider account inactive (default: 90)
.PARAMETER OutputFolder
Destination folder for reports. Default: .\InactiveADUsers-<date>
.PARAMETER SearchBase
Optional OU to limit search scope (default: entire domain)
.PARAMETER IncludeDisabled
Include disabled accounts in the report (default: $false)
.NOTES
⚠️ AI-GENERATED SCRIPT - UNTESTED
This script was generated by Claude AI and has not been tested in production.
Review and test thoroughly in a non-production environment before use.
- Requires Active Directory PowerShell module
- Run with appropriate AD permissions
- Can be slow in large environments (queries each DC)
- LastLogon attribute is not replicated between DCs
.EXAMPLE
.\Get-ADUserLastLogon.ps1
.EXAMPLE
.\Get-ADUserLastLogon.ps1 -InactiveDays 180 -SearchBase "OU=Users,DC=domain,DC=com"
#>
[CmdletBinding()]
param(
[int]$InactiveDays = 90,
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("InactiveADUsers-" + (Get-Date -Format "yyyyMMdd-HHmm"))),
[string]$SearchBase = "",
[bool]$IncludeDisabled = $false
)
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
Write-Host "[$(NowTag)] Starting AD user lastlogon analysis..." -ForegroundColor Green
# Import AD module
try {
Import-Module ActiveDirectory -ErrorAction Stop
} catch {
Write-Host "[$(NowTag)] ERROR: Active Directory module not available" -ForegroundColor Red
exit 1
}
# Create output folder
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
$cutoffDate = (Get-Date).AddDays(-$InactiveDays)
Write-Host "[$(NowTag)] Cutoff date: $($cutoffDate.ToString('yyyy-MM-dd'))"
# Get all domain controllers
Write-Host "[$(NowTag)] Discovering domain controllers..."
$domainControllers = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName
Write-Host "[$(NowTag)] Found $($domainControllers.Count) domain controller(s)"
# Get all users
Write-Host "[$(NowTag)] Retrieving AD users..."
$getUserParams = @{
Filter = "ObjectClass -eq 'user' -and Enabled -eq 'True'"
Properties = @('LastLogon', 'LastLogonDate', 'WhenCreated', 'Department', 'Title', 'Description', 'PasswordLastSet', 'PasswordNeverExpires', 'Enabled')
}
if ($SearchBase) {
$getUserParams['SearchBase'] = $SearchBase
}
if ($IncludeDisabled) {
$getUserParams['Filter'] = "ObjectClass -eq 'user'"
}
$users = Get-ADUser @getUserParams
$userCount = ($users | Measure-Object).Count
Write-Host "[$(NowTag)] Found $userCount user(s) to analyze"
# Query each DC for LastLogon and find the most recent
$userLogonData = @()
$current = 0
foreach ($user in $users) {
$current++
$pct = [int](($current / $userCount) * 100)
Write-Progress -Activity "Querying LastLogon from Domain Controllers" -Status "Processing $($user.SamAccountName) ($current/$userCount)" -PercentComplete $pct
$mostRecentLogon = $null
foreach ($dc in $domainControllers) {
try {
$userFromDC = Get-ADUser -Identity $user.SamAccountName -Server $dc -Properties LastLogon -ErrorAction SilentlyContinue
if ($userFromDC.LastLogon) {
$logonDate = [DateTime]::FromFileTime($userFromDC.LastLogon)
if ($null -eq $mostRecentLogon -or $logonDate -gt $mostRecentLogon) {
$mostRecentLogon = $logonDate
}
}
} catch {
Write-Verbose "Could not query $dc for $($user.SamAccountName)"
}
}
# Determine if inactive
$isInactive = $false
$reason = ""
$daysSinceLogon = "N/A"
if ($null -eq $mostRecentLogon) {
$isInactive = $true
$reason = "Never logged on"
} elseif ($mostRecentLogon -lt $cutoffDate) {
$isInactive = $true
$daysSinceLogon = [int]((Get-Date) - $mostRecentLogon).TotalDays
$reason = "Last logon: $($mostRecentLogon.ToString('yyyy-MM-dd'))"
} else {
$daysSinceLogon = [int]((Get-Date) - $mostRecentLogon).TotalDays
}
if ($isInactive) {
$userLogonData += [PSCustomObject]@{
SamAccountName = $user.SamAccountName
DisplayName = $user.Name
UserPrincipalName = $user.UserPrincipalName
Enabled = $user.Enabled
LastLogon = $mostRecentLogon
DaysSinceLogon = $daysSinceLogon
LastLogonDate = $user.LastLogonDate # Replicated attribute for comparison
WhenCreated = $user.WhenCreated
PasswordLastSet = $user.PasswordLastSet
PasswordNeverExpires = $user.PasswordNeverExpires
Department = $user.Department
Title = $user.Title
Description = $user.Description
DistinguishedName = $user.DistinguishedName
Reason = $reason
}
}
}
Write-Progress -Activity "Querying LastLogon from Domain Controllers" -Completed
# Export results
Write-Host "[$(NowTag)] Found $($userLogonData.Count) inactive user(s)"
if ($userLogonData.Count -gt 0) {
$csvFile = Join-Path $OutputFolder "Inactive-ADUsers.csv"
$userLogonData | Sort-Object DaysSinceLogon -Descending |
Export-Csv -NoTypeInformation -Encoding UTF8 -Path $csvFile
Write-Host "[$(NowTag)] Report exported: $csvFile" -ForegroundColor Green
# Summary statistics
Write-Host "`nINACTIVE USER SUMMARY:" -ForegroundColor Cyan
Write-Host " Total Inactive Users: $($userLogonData.Count)"
Write-Host " Never Logged On: $(($userLogonData | Where-Object { $_.DaysSinceLogon -eq 'N/A' }).Count)"
Write-Host " Enabled Accounts: $(($userLogonData | Where-Object Enabled).Count)"
Write-Host " Disabled Accounts: $(($userLogonData | Where-Object { -not $_.Enabled }).Count)"
Write-Host " Password Never Expires: $(($userLogonData | Where-Object PasswordNeverExpires).Count)"
# By department
$byDept = $userLogonData | Where-Object Department | Group-Object Department | Sort-Object Count -Descending | Select-Object -First 5
if ($byDept) {
Write-Host "`nTop 5 Departments with Inactive Users:" -ForegroundColor Cyan
$byDept | ForEach-Object {
Write-Host " $($_.Name): $($_.Count)"
}
}
} else {
Write-Host "[$(NowTag)] No inactive users found!" -ForegroundColor Green
}
Write-Host "`n[$(NowTag)] Analysis complete! Output folder: $OutputFolder"