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,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"