Public/Test-DCPortHealth.ps1
|
function Test-DCPortHealth { <# .SYNOPSIS Tests critical port availability across all Active Directory domain controllers and outputs a colour-coded summary report. .DESCRIPTION Checks all domain controllers in the current domain against a predefined list of ports required for AD health — including Kerberos, LDAP, DNS, RPC, SMB, and Global Catalog. Uses async TCP connections with a configurable timeout so unreachable hosts do not cause the script to hang. Outputs results to the console as a colour-coded table and optionally exports to CSV. .PARAMETER TimeoutSeconds TCP connection timeout per port in seconds. Default is 3. .PARAMETER ExportPath Optional path to export results as CSV. Example: "C:\temp\DCPortHealth.csv" .EXAMPLE Test-DCPortHealth Checks all DCs with default 3 second timeout. .EXAMPLE Test-DCPortHealth -TimeoutSeconds 5 -ExportPath "C:\temp\DCPortHealth.csv" Checks all DCs with 5 second timeout and exports to CSV. .NOTES Author: K Shankar R Karanth Website: https://karanth.ovh Version: 1.0 Run as Domain Admin or equivalent with network access to DCs. Port reference: 88 — Kerberos 53 — DNS 135 — RPC Endpoint Mapper 137 — NetBIOS Name Service 138 — NetBIOS Datagram 139 — NetBIOS Session 389 — LDAP 445 — SMB 464 — Kerberos Password Change 636 — LDAPS 3268 — Global Catalog LDAP 3269 — Global Catalog LDAPS 3389 — RDP #> [CmdletBinding()] param ( [int]$TimeoutSeconds = 3, [string]$ExportPath = "C:\ADOpsKit\Reports\Test-DCPortHealth\$(Get-Date -Format 'yyyy-MM-dd')_DCPortHealth.csv" ) # ============ PORT DEFINITIONS ============ $portsToCheck = [ordered]@{ 88 = 'Kerberos' 53 = 'DNS' 135 = 'RPC' 137 = 'NetBIOS-NS' 138 = 'NetBIOS-DGM' 139 = 'NetBIOS-SSN' 389 = 'LDAP' 445 = 'SMB' 464 = 'Kerberos-PW' 636 = 'LDAPS' 3268 = 'GC-LDAP' 3269 = 'GC-LDAPS' 3389 = 'RDP' } # ============ MAIN ============ $domainControllers = Get-ADDomainController -Filter * $results = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($dc in $domainControllers) { Write-Host "`nChecking $($dc.HostName) [$($dc.Site)]..." -ForegroundColor Cyan foreach ($entry in $portsToCheck.GetEnumerator()) { $port = $entry.Key $serviceName = $entry.Value $isOpen = Test-ADOKTcpPort -ComputerName $dc.HostName -Port $port -TimeoutSeconds $TimeoutSeconds $status = if ($isOpen) { 'Open' } else { 'Closed' } $color = if ($isOpen) { 'Green' } else { 'Red' } Write-Host " Port $($port.ToString().PadRight(5)) $($serviceName.PadRight(14)) $status" -ForegroundColor $color $results.Add([PSCustomObject]@{ DomainController = $dc.HostName Site = $dc.Site Port = $port Service = $serviceName Status = $status }) } } # ============ SUMMARY ============ $closed = $results | Where-Object { $_.Status -eq 'Closed' } Write-Host "`n===== CLOSED PORTS SUMMARY =====" -ForegroundColor Yellow if ($closed.Count -eq 0) { Write-Host "All ports open on all domain controllers." -ForegroundColor Green } else { $closed | Sort-Object DomainController, Port | Format-Table DomainController, Site, Port, Service, Status -AutoSize } # ============ EXPORT ============ if ($ExportPath -ne "") { $exportDir = Split-Path $ExportPath if ($exportDir -and -not (Test-Path $exportDir)) { New-Item -ItemType Directory -Path $exportDir -Force | Out-Null } $results | Sort-Object DomainController, Port | Export-Csv -Path $ExportPath -NoTypeInformation Write-Host "Full results exported to $ExportPath" -ForegroundColor Green } } |