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,191 @@
<#
.SYNOPSIS
Identify inactive mailboxes based on LastLogonTime
.DESCRIPTION
Finds mailboxes with no logon activity within specified days.
Useful for identifying stale accounts, cost optimization, and cleanup planning.
.PARAMETER InactiveDays
Number of days without logon to consider mailbox inactive (default: 90)
.PARAMETER OutputFolder
Destination folder for reports. Default: .\InactiveMailboxes-<date>
.PARAMETER IncludeShared
Include shared mailboxes in the report (default: $false)
.PARAMETER IncludeSize
Include mailbox size information (slower, 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.
- Run in Exchange Management Shell with appropriate RBAC permissions
- LastLogonTime may be null for never-accessed mailboxes
- Size calculation can be slow with large mailbox counts
.EXAMPLE
.\Get-InactiveMailboxes.ps1
.EXAMPLE
.\Get-InactiveMailboxes.ps1 -InactiveDays 180 -IncludeShared $true
#>
[CmdletBinding()]
param(
[int]$InactiveDays = 90,
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("InactiveMailboxes-" + (Get-Date -Format "yyyyMMdd-HHmm"))),
[bool]$IncludeShared = $false,
[bool]$IncludeSize = $true
)
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
Write-Host "[$(NowTag)] Searching for mailboxes inactive for $InactiveDays+ days..." -ForegroundColor Green
# 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 mailboxes
Write-Host "[$(NowTag)] Retrieving mailboxes..."
$filter = if ($IncludeShared) {
{ RecipientTypeDetails -notmatch "^Remote" -and Database }
} else {
{ RecipientTypeDetails -eq "UserMailbox" -and Database }
}
$mailboxes = Get-Mailbox -ResultSize Unlimited -ErrorAction SilentlyContinue |
Where-Object $filter
$mbCount = ($mailboxes | Measure-Object).Count
Write-Host "[$(NowTag)] Found $mbCount mailboxes to check"
# Get statistics per database to avoid prompts
Write-Host "[$(NowTag)] Retrieving mailbox statistics..."
$statsByDb = @{}
$databases = ($mailboxes.Database | Sort-Object -Unique)
$dbIdx = 0
foreach ($db in $databases) {
$dbIdx++
Write-Progress -Activity "Collecting Mailbox Statistics" -Status "Database $db ($dbIdx/$($databases.Count))" -PercentComplete ([int](($dbIdx / $databases.Count) * 100))
try {
$stats = Get-MailboxStatistics -Database $db -ErrorAction SilentlyContinue
foreach ($stat in $stats) {
$statsByDb[$stat.DisplayName] = $stat
}
} catch {
Write-Host "[$(NowTag)] WARNING: Could not get statistics for database $db" -ForegroundColor Yellow
}
}
Write-Progress -Activity "Collecting Mailbox Statistics" -Completed
# Analyze for inactive mailboxes
Write-Host "[$(NowTag)] Analyzing mailbox activity..."
$inactiveMailboxes = @()
$current = 0
foreach ($mb in $mailboxes) {
$current++
$pct = [int](($current / $mbCount) * 100)
Write-Progress -Activity "Analyzing Mailboxes" -Status "Processing $($mb.DisplayName) ($current/$mbCount)" -PercentComplete $pct
$stat = $statsByDb[$mb.DisplayName]
$lastLogon = if ($stat) { $stat.LastLogonTime } else { $null }
$isInactive = $false
$reason = ""
if ($null -eq $lastLogon) {
$isInactive = $true
$reason = "Never logged on"
} elseif ($lastLogon -lt $cutoffDate) {
$isInactive = $true
$reason = "Last logon: $($lastLogon.ToString('yyyy-MM-dd'))"
}
if ($isInactive) {
$daysSinceLogon = if ($lastLogon) {
[int]((Get-Date) - $lastLogon).TotalDays
} else {
"N/A"
}
$size = $null
$itemCount = $null
if ($IncludeSize -and $stat) {
$size = $stat.TotalItemSize
$itemCount = $stat.ItemCount
}
$inactiveMailboxes += [PSCustomObject]@{
DisplayName = $mb.DisplayName
PrimarySmtpAddress = $mb.PrimarySmtpAddress
RecipientTypeDetails = $mb.RecipientTypeDetails
Database = $mb.Database
LastLogonTime = $lastLogon
DaysSinceLogon = $daysSinceLogon
WhenCreated = $mb.WhenCreated
TotalItemSize = $size
ItemCount = $itemCount
Reason = $reason
}
}
}
Write-Progress -Activity "Analyzing Mailboxes" -Completed
# Export results
Write-Host "[$(NowTag)] Found $($inactiveMailboxes.Count) inactive mailboxes"
if ($inactiveMailboxes.Count -gt 0) {
$csvFile = Join-Path $OutputFolder "Inactive-Mailboxes.csv"
$inactiveMailboxes | Sort-Object DaysSinceLogon -Descending |
Export-Csv -NoTypeInformation -Encoding UTF8 -Path $csvFile
Write-Host "[$(NowTag)] Report exported: $csvFile" -ForegroundColor Green
# Summary by type
$byType = $inactiveMailboxes | Group-Object RecipientTypeDetails
Write-Host "`nInactive Mailboxes by Type:"
foreach ($type in $byType) {
Write-Host " $($type.Name): $($type.Count)"
}
# Calculate potential storage savings
if ($IncludeSize) {
$totalBytes = 0
foreach ($mb in $inactiveMailboxes) {
if ($mb.TotalItemSize) {
try {
$sizeStr = [string]$mb.TotalItemSize
if ($sizeStr -match "([\d\.,]+)\s*(KB|MB|GB|TB)") {
$num = [double]($matches[1] -replace ',', '.')
$bytes = switch ($matches[2].ToUpper()) {
"KB" { $num * 1KB }
"MB" { $num * 1MB }
"GB" { $num * 1GB }
"TB" { $num * 1TB }
}
$totalBytes += $bytes
}
} catch {}
}
}
$totalGB = [math]::Round($totalBytes / 1GB, 2)
Write-Host "`nPotential storage savings: $totalGB GB" -ForegroundColor Cyan
}
} else {
Write-Host "[$(NowTag)] No inactive mailboxes found!" -ForegroundColor Green
}
Write-Host "`n[$(NowTag)] Analysis complete! Output folder: $OutputFolder"