This comprehensive PowerShell module provides enterprise-grade Active Directory health monitoring and reporting capabilities. It combines DCDiag and RepAdmin functionality with modern PowerShell practices, error handling, and flexible output formats.
Overview
The script performs comprehensive Active Directory forest health checks including:
- Domain controller diagnostics across all domains in the forest
- Replication status and failure analysis
- Flexible reporting with multiple output formats
- Error handling and validation
- Performance optimizations for large environments
Prerequisites
Before running this script, ensure the following requirements are met:
Software Requirements
- PowerShell 5.1 or later (PowerShell 7+ recommended)
- Active Directory PowerShell Module installed
- Domain Controller Tools (DCDiag and RepAdmin) available
- Administrative privileges on domain controllers
Permissions Required
- Domain Admin or equivalent rights for comprehensive testing
- Read access to all domain controllers in the forest
- Network connectivity to all DCs on required ports
Network Requirements
- Access to LDAP ports (389, 636, 3268, 3269)
- DNS resolution for all domain controllers
- Kerberos authentication (port 88)
- RPC endpoint mapper (port 135) and dynamic RPC ports
Security Considerations
- Run with least privilege: Use dedicated service accounts when possible
- Secure credential storage: Avoid hardcoded passwords
- Audit logging: Enable monitoring of diagnostic activities
- Network security: Ensure encrypted connections where possible
- Data protection: Handle diagnostic output securely
PowerShell Module: AD-HealthCheck
Installation and Setup
# Import required modules
Import-Module ActiveDirectory
Import-Module Microsoft.PowerShell.Utility
# Verify required tools are available
if (-not (Get-Command dcdiag.exe -ErrorAction SilentlyContinue))
{
throw "DCDiag tool not found. Please install Remote Server Administration Tools (RSAT)."
}
if (-not (Get-Command repadmin.exe -ErrorAction SilentlyContinue))
{
throw "RepAdmin tool not found. Please install Remote Server Administration Tools (RSAT)."
}
Core Functions
Get-ForestDomainControllers Function
function Get-ForestDomainControllers
{
<#
.SYNOPSIS
Retrieves all domain controllers from all domains in the current forest.
.DESCRIPTION
This function discovers all domains in the current Active Directory forest
and returns a comprehensive list of all domain controllers.
.PARAMETER Credential
Optional credentials for forest access
.EXAMPLE
$DCs = Get-ForestDomainControllers
.EXAMPLE
$DCs = Get-ForestDomainControllers -Credential $cred
#>
[CmdletBinding()]
param(
[PSCredential]$Credential
)
try
{
Write-Verbose "Discovering forest domains..."
$forestParams = @{}
if ($Credential) { $forestParams.Credential = $Credential }
$forest = Get-ADForest @forestParams
$domainControllers = [System.Collections.Generic.List[PSObject]]::new()
foreach ($domainName in $forest.Domains)
{
try
{
Write-Verbose "Getting domain controllers for domain: $domainName"
$domainParams = @{
Identity = $domainName
}
if ($Credential) { $domainParams.Credential = $Credential }
$domain = Get-ADDomain @domainParams
foreach ($dc in $domain.ReplicaDirectoryServers)
{
$dcObject = [PSCustomObject]@{
Name = $dc
Domain = $domainName
Site = $null
OperatingSystem = $null
IPv4Address = $null
}
# Get additional DC information
try
{
$dcInfo = Get-ADDomainController -Identity $dc @domainParams
$dcObject.Site = $dcInfo.Site
$dcObject.OperatingSystem = $dcInfo.OperatingSystem
$dcObject.IPv4Address = $dcInfo.IPv4Address
}
catch
{
Write-Warning "Could not retrieve detailed information for DC: $dc"
}
$domainControllers.Add($dcObject)
}
}
catch
{
Write-Error "Failed to process domain $domainName : $($_.Exception.Message)"
}
}
Write-Verbose "Found $($domainControllers.Count) domain controllers"
return $domainControllers
}
catch
{
throw "Failed to retrieve forest domain controllers: $($_.Exception.Message)"
}
}
Get-DCDiagReport Function
function Get-DCDiagReport
{
<#
.SYNOPSIS
Performs DCDiag tests on a specified domain controller.
.DESCRIPTION
Executes DCDiag against a domain controller and parses the results
into a structured PowerShell object for analysis and reporting.
.PARAMETER ComputerName
The name or IP address of the domain controller to test
.PARAMETER Tests
Specific tests to run (default: all tests)
.PARAMETER Verbose
Include verbose DCDiag output
.EXAMPLE
$report = Get-DCDiagReport -ComputerName "DC01.contoso.com"
.EXAMPLE
$report = Get-DCDiagReport -ComputerName "DC01" -Tests @("Connectivity", "Replications")
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ComputerName,
[string[]]$Tests = @(),
[switch]$IncludeVerbose
)
try
{
Write-Verbose "Running DCDiag tests on $ComputerName"
# Build DCDiag command
$dcdiagArgs = @("/s:$ComputerName")
if ($IncludeVerbose) { $dcdiagArgs += "/v" }
if ($Tests.Count -gt 0)
{
foreach ($test in $Tests)
{
$dcdiagArgs += "/test:$test"
}
}
# Execute DCDiag
$dcdiagOutput = & dcdiag.exe $dcdiagArgs 2>&1
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 1)
{
throw "DCDiag failed with exit code: $LASTEXITCODE"
}
# Parse results
$results = [PSCustomObject]@{
ComputerName = $ComputerName
Timestamp = Get-Date
OverallStatus = "Unknown"
Tests = @{}
Errors = @()
Warnings = @()
}
$currentTest = $null
$testResults = @{}
foreach ($line in $dcdiagOutput)
{
$lineStr = $line.ToString()
# Parse test start
if ($lineStr -match "Starting test: (.+)")
{
$currentTest = $matches[1].Trim()
Write-Verbose "Processing test: $currentTest"
}
# Parse test results
if ($lineStr -match "\.+ (PASSED|FAILED)")
{
$status = $matches[1]
if ($currentTest)
{
$testResults[$currentTest] = $status
$currentTest = $null
}
}
# Parse errors and warnings
if ($lineStr -match "Error|Fatal")
{
$results.Errors += $lineStr.Trim()
}
elseif ($lineStr -match "Warning")
{
$results.Warnings += $lineStr.Trim()
}
}
$results.Tests = $testResults
# Determine overall status
$failedTests = ($testResults.Values | Where-Object { $_ -eq "FAILED" }).Count
$results.OverallStatus = if ($failedTests -eq 0) { "PASSED" } else { "FAILED" }
Write-Verbose "DCDiag completed for $ComputerName. Status: $($results.OverallStatus)"
return $results
}
catch
{
throw "DCDiag test failed for $ComputerName : $($_.Exception.Message)"
}
}
Get-ReplicationReport Function
function Get-ReplicationReport
{
<#
.SYNOPSIS
Generates a comprehensive Active Directory replication report.
.DESCRIPTION
Uses RepAdmin to analyze replication status across the forest
and provides detailed reporting on replication health and failures.
.PARAMETER ComputerName
Specific domain controller to analyze (default: all DCs)
.PARAMETER IncludeSuccessful
Include successful replications in the report
.EXAMPLE
$report = Get-ReplicationReport
.EXAMPLE
$report = Get-ReplicationReport -ComputerName "DC01" -IncludeSuccessful
#>
[CmdletBinding()]
param(
[string]$ComputerName = "*",
[switch]$IncludeSuccessful
)
try
{
Write-Verbose "Gathering replication information..."
# Execute repadmin
$repadminOutput = & repadmin.exe /showrepl $ComputerName /csv 2>&1
if ($LASTEXITCODE -ne 0)
{
throw "RepAdmin failed with exit code: $LASTEXITCODE"
}
# Parse CSV output
$replData = $repadminOutput | ConvertFrom-Csv
$replicationReport = [System.Collections.Generic.List[PSObject]]::new()
foreach ($entry in $replData)
{
$replObject = [PSCustomObject]@{
DestinationDC = $entry.'Destination DSA'
DestinationSite = $entry.'Destination DSA Site'
NamingContext = $entry.'Naming Context'
SourceDC = $entry.'Source DSA'
SourceSite = $entry.'Source DSA Site'
TransportType = $entry.'Transport Type'
NumberOfFailures = [int]$entry.'Number of Failures'
LastFailureTime = if ($entry.'Last Failure Time') {
try { [datetime]$entry.'Last Failure Time' } catch { $entry.'Last Failure Time' }
} else { $null }
LastSuccessTime = if ($entry.'Last Success Time') {
try { [datetime]$entry.'Last Success Time' } catch { $entry.'Last Success Time' }
} else { $null }
LastFailureStatus = $entry.'Last Failure Status'
ConsecutiveFailures = [int]$entry.'Number of Failures'
ReplicationHealth = $null
}
# Determine replication health
if ($replObject.NumberOfFailures -eq 0)
{
$replObject.ReplicationHealth = "Healthy"
}
elseif ($replObject.NumberOfFailures -le 3)
{
$replObject.ReplicationHealth = "Warning"
}
else
{
$replObject.ReplicationHealth = "Critical"
}
# Add time since last success
if ($replObject.LastSuccessTime)
{
$timeSinceSuccess = (Get-Date) - $replObject.LastSuccessTime
Add-Member -InputObject $replObject -NotePropertyName "HoursSinceLastSuccess" -NotePropertyValue $timeSinceSuccess.TotalHours
}
if ($IncludeSuccessful -or $replObject.NumberOfFailures -gt 0)
{
$replicationReport.Add($replObject)
}
}
Write-Verbose "Processed $($replicationReport.Count) replication partnerships"
return $replicationReport
}
catch
{
throw "Replication report generation failed: $($_.Exception.Message)"
}
}
Export Functions
function Export-ADHealthReport
{
<#
.SYNOPSIS
Exports Active Directory health report to various formats.
.DESCRIPTION
Combines DCDiag and replication data into comprehensive reports
with support for multiple output formats including HTML, CSV, and JSON.
.PARAMETER DCDiagResults
Array of DCDiag test results
.PARAMETER ReplicationResults
Array of replication status results
.PARAMETER OutputPath
Path for output files
.PARAMETER Format
Output format: HTML, CSV, JSON, or Console
.EXAMPLE
Export-ADHealthReport -DCDiagResults $dcResults -ReplicationResults $replResults -OutputPath "C:\Reports" -Format "HTML"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[PSObject[]]$DCDiagResults,
[Parameter(Mandatory = $true)]
[PSObject[]]$ReplicationResults,
[string]$OutputPath = ".",
[ValidateSet("HTML", "CSV", "JSON", "Console")]
[string]$Format = "Console"
)
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
switch ($Format) {
"HTML" {
$htmlReport = Generate-HTMLReport -DCDiagResults $DCDiagResults -ReplicationResults $ReplicationResults
$htmlPath = Join-Path $OutputPath "AD-HealthReport-$timestamp.html"
$htmlReport | Out-File -FilePath $htmlPath -Encoding UTF8
Write-Host "HTML report saved to: $htmlPath" -ForegroundColor Green
}
"CSV" {
$csvPath = Join-Path $OutputPath "DCDiag-Results-$timestamp.csv"
$DCDiagResults | Export-Csv -Path $csvPath -NoTypeInformation
$replPath = Join-Path $OutputPath "Replication-Results-$timestamp.csv"
$ReplicationResults | Export-Csv -Path $replPath -NoTypeInformation
Write-Host "CSV reports saved to: $csvPath and $replPath" -ForegroundColor Green
}
"JSON" {
$report = @{
Timestamp = Get-Date
DCDiagResults = $DCDiagResults
ReplicationResults = $ReplicationResults
}
$jsonPath = Join-Path $OutputPath "AD-HealthReport-$timestamp.json"
$report | ConvertTo-Json -Depth 10 | Out-File -FilePath $jsonPath -Encoding UTF8
Write-Host "JSON report saved to: $jsonPath" -ForegroundColor Green
}
"Console" {
Show-ConsoleReport -DCDiagResults $DCDiagResults -ReplicationResults $ReplicationResults
}
}
}
function Generate-HTMLReport
{
param($DCDiagResults, $ReplicationResults)
$html = @"
<!DOCTYPE html>
<html>
<head>
<title>Active Directory Health Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1, h2 { color: #2E5AA8; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.passed { background-color: #d4edda; }
.failed { background-color: #f8d7da; }
.warning { background-color: #fff3cd; }
.critical { background-color: #f8d7da; }
.healthy { background-color: #d4edda; }
</style>
</head>
<body>
<h1>Active Directory Health Report</h1>
<p>Generated: $(Get-Date)</p>
<h2>DCDiag Test Results Summary</h2>
<table>
<tr>
<th>Domain Controller</th>
<th>Overall Status</th>
<th>Failed Tests</th>
<th>Errors</th>
<th>Warnings</th>
</tr>
"@
foreach ($result in $DCDiagResults)
{
$failedTests = ($result.Tests.Values | Where-Object { $_ -eq "FAILED" }).Count
$statusClass = if ($result.OverallStatus -eq "PASSED") { "passed" } else { "failed" }
$html += @"
<tr class="$statusClass">
<td>$($result.ComputerName)</td>
<td>$($result.OverallStatus)</td>
<td>$failedTests</td>
<td>$($result.Errors.Count)</td>
<td>$($result.Warnings.Count)</td>
</tr>
"@
}
$html += @"
</table>
<h2>Replication Status Summary</h2>
<table>
<tr>
<th>Source DC</th>
<th>Destination DC</th>
<th>Naming Context</th>
<th>Failures</th>
<th>Status</th>
<th>Last Success</th>
</tr>
"@
foreach ($repl in ($ReplicationResults | Sort-Object SourceDC, NamingContext))
{
$statusClass = switch ($repl.ReplicationHealth)
{
"Healthy" { "healthy" }
"Warning" { "warning" }
"Critical" { "critical" }
default { "" }
}
$html += @"
<tr class="$statusClass">
<td>$($repl.SourceDC)</td>
<td>$($repl.DestinationDC)</td>
<td>$($repl.NamingContext)</td>
<td>$($repl.NumberOfFailures)</td>
<td>$($repl.ReplicationHealth)</td>
<td>$($repl.LastSuccessTime)</td>
</tr>
"@
}
$html += @"
</table>
</body>
</html>
"@
return $html
}
function Show-ConsoleReport
{
param($DCDiagResults, $ReplicationResults)
Write-Host "`n=== Active Directory Health Report ===" -ForegroundColor Cyan
Write-Host "Generated: $(Get-Date)" -ForegroundColor Gray
Write-Host "`n--- DCDiag Results Summary ---" -ForegroundColor Yellow
$DCDiagResults | Format-Table ComputerName, OverallStatus, @{
Name = "Failed Tests"
Expression = { ($_.Tests.Values | Where-Object { $_ -eq "FAILED" }).Count }
}, @{
Name = "Errors"
Expression = { $_.Errors.Count }
}, @{
Name = "Warnings"
Expression = { $_.Warnings.Count }
} -AutoSize
Write-Host "`n--- Replication Issues ---" -ForegroundColor Yellow
$criticalRepl = $ReplicationResults | Where-Object { $_.NumberOfFailures -gt 0 }
if ($criticalRepl)
{
$criticalRepl | Format-Table SourceDC, DestinationDC, NamingContext, NumberOfFailures, LastFailureStatus -AutoSize
}
else
{
Write-Host "No replication failures detected!" -ForegroundColor Green
}
}
Main Execution Script
function Start-ADHealthCheck
{
<#
.SYNOPSIS
Performs comprehensive Active Directory health check.
.DESCRIPTION
Main function that orchestrates DCDiag and replication testing
across the entire forest with comprehensive reporting.
.PARAMETER OutputPath
Directory for output files
.PARAMETER Format
Output format for reports
.PARAMETER IncludeSuccessfulReplication
Include successful replication in reports
.PARAMETER Credential
Credentials for forest access
.EXAMPLE
Start-ADHealthCheck -OutputPath "C:\Reports" -Format "HTML"
.EXAMPLE
Start-ADHealthCheck -Format "Console" -IncludeSuccessfulReplication
#>
[CmdletBinding()]
param(
[string]$OutputPath = ".",
[ValidateSet("HTML", "CSV", "JSON", "Console")]
[string]$Format = "Console",
[switch]$IncludeSuccessfulReplication,
[PSCredential]$Credential
)
try
{
Write-Host "Starting Active Directory Health Check..." -ForegroundColor Green
# Verify prerequisites
Test-Prerequisites
# Get all domain controllers
Write-Host "Discovering domain controllers..." -ForegroundColor Yellow
$forestParams = @{}
if ($Credential) { $forestParams.Credential = $Credential }
$domainControllers = Get-ForestDomainControllers @forestParams
Write-Host "Found $($domainControllers.Count) domain controllers" -ForegroundColor Green
# Run DCDiag tests
Write-Host "Running DCDiag tests..." -ForegroundColor Yellow
$dcdiagResults = [System.Collections.Generic.List[PSObject]]::new()
$counter = 0
foreach ($dc in $domainControllers)
{
$counter++
Write-Progress -Activity "Running DCDiag Tests" -Status "Testing $($dc.Name)" -PercentComplete (($counter / $domainControllers.Count) * 100)
try
{
$result = Get-DCDiagReport -ComputerName $dc.Name
$dcdiagResults.Add($result)
}
catch
{
Write-Warning "DCDiag failed for $($dc.Name): $($_.Exception.Message)"
}
}
Write-Progress -Activity "Running DCDiag Tests" -Completed
# Run replication tests
Write-Host "Analyzing replication status..." -ForegroundColor Yellow
$replicationResults = Get-ReplicationReport -IncludeSuccessful:$IncludeSuccessfulReplication
# Generate reports
Write-Host "Generating reports..." -ForegroundColor Yellow
Export-ADHealthReport -DCDiagResults $dcdiagResults -ReplicationResults $replicationResults -OutputPath $OutputPath -Format $Format
# Summary
$failedDCs = ($dcdiagResults | Where-Object { $_.OverallStatus -eq "FAILED" }).Count
$replIssues = ($replicationResults | Where-Object { $_.NumberOfFailures -gt 0 }).Count
Write-Host "`n=== Health Check Summary ===" -ForegroundColor Cyan
Write-Host "Domain Controllers Tested: $($dcdiagResults.Count)" -ForegroundColor White
Write-Host "Domain Controllers with Issues: $failedDCs" -ForegroundColor $(if ($failedDCs -eq 0) { "Green" } else { "Red" })
Write-Host "Replication Partnerships with Issues: $replIssues" -ForegroundColor $(if ($replIssues -eq 0) { "Green" } else { "Red" })
if ($failedDCs -eq 0 -and $replIssues -eq 0)
{
Write-Host "✓ Active Directory forest appears healthy!" -ForegroundColor Green
}
else
{
Write-Host "⚠ Issues detected - review detailed reports" -ForegroundColor Red
}
}
catch
{
Write-Error "Health check failed: $($_.Exception.Message)"
throw
}
}
function Test-Prerequisites
{
# Test required modules
if (-not (Get-Module -ListAvailable -Name ActiveDirectory))
{
throw "Active Directory PowerShell module is not installed"
}
# Test required tools
if (-not (Get-Command dcdiag.exe -ErrorAction SilentlyContinue))
{
throw "DCDiag tool not found. Install Remote Server Administration Tools (RSAT)"
}
if (-not (Get-Command repadmin.exe -ErrorAction SilentlyContinue))
{
throw "RepAdmin tool not found. Install Remote Server Administration Tools (RSAT)"
}
# Test AD connectivity
try
{
$null = Get-ADForest -ErrorAction Stop
}
catch
{
throw "Cannot connect to Active Directory forest: $($_.Exception.Message)"
}
}
Usage Examples
Basic Health Check
# Simple console output
Start-ADHealthCheck
# Generate HTML report
Start-ADHealthCheck -OutputPath "C:\Reports" -Format "HTML"
# Include successful replications
Start-ADHealthCheck -Format "Console" -IncludeSuccessfulReplication
Advanced Usage
# Use alternate credentials
$cred = Get-Credential
Start-ADHealthCheck -Credential $cred -Format "JSON" -OutputPath "C:\Reports"
# Test specific domain controller
$dcResult = Get-DCDiagReport -ComputerName "DC01.contoso.com" -IncludeVerbose
# Get replication status for specific DC
$replStatus = Get-ReplicationReport -ComputerName "DC01.contoso.com"
Scheduled Automation
# Create scheduled task for daily health checks
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\AD-HealthCheck.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At "6:00 AM"
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName "AD Health Check" -Action $action -Trigger $trigger -Settings $settings
Troubleshooting
Common DCDiag Test Failures
Connectivity Test Failures
Symptoms: "Connectivity" test fails
Common Causes:
- Network connectivity issues
- DNS resolution problems
- Firewall blocking required ports
- Domain controller service stopped
Solutions:
# Test network connectivity
Test-NetConnection -ComputerName "DC01.contoso.com" -Port 389
Test-NetConnection -ComputerName "DC01.contoso.com" -Port 636
# Test DNS resolution
Resolve-DnsName "DC01.contoso.com"
nslookup DC01.contoso.com
# Check AD services
Get-Service -ComputerName "DC01" -Name "NTDS","DNS","Netlogon","KDC"
Replication Test Failures
Symptoms: "Replications" test fails
Common Causes:
- Network latency or timeouts
- Authentication issues
- Knowledge Consistency Checker (KCC) problems
- Lingering objects
Solutions:
# Force replication
repadmin /replicate DC02.contoso.com DC01.contoso.com "DC=contoso,DC=com"
# Check replication topology
repadmin /showreps DC01.contoso.com
# Force KCC to run
repadmin /kcc DC01.contoso.com
Services Test Failures
Symptoms: "Services" test fails
Common Causes:
- Required AD services not running
- Service account issues
- Registry corruption
Solutions:
# Check critical AD services
$services = @("NTDS", "DNS", "Netlogon", "KDC", "W32Time")
Get-Service -Name $services | Format-Table Name, Status, StartType
# Restart services if needed
Restart-Service -Name "NTDS" -Force
Common Replication Issues
High Failure Counts
Symptoms: NumberOfFailures > 0 in replication report
Investigation Steps:
# Check replication status
repadmin /showrepl * /csv | ConvertFrom-Csv | Where-Object {$_.'Number of Failures' -gt 0}
# Check event logs for replication errors
Get-WinEvent -LogName "Directory Service" -MaxEvents 100 | Where-Object {$_.Id -in @(1311, 1388, 1925, 2042)}
# Verify naming contexts
repadmin /showreps * | findstr "FAIL"
Time Synchronization Issues
Symptoms: Authentication failures, Kerberos errors
Solutions:
# Check time synchronization
w32tm /query /status
w32tm /query /peers
# Force time sync
w32tm /resync /force
# Configure reliable time source
w32tm /config /manualpeerlist:"time.windows.com" /syncfromflags:manual
Performance Optimization
Large Forest Considerations
For forests with many domain controllers, consider:
# Run tests in parallel
$jobs = @()
foreach ($dc in $domainControllers)
{
$jobs += Start-Job -ScriptBlock {
param($dcName)
Get-DCDiagReport -ComputerName $dcName
} -ArgumentList $dc.Name
}
# Wait for completion and gather results
$results = $jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
Memory and Resource Management
# Monitor script performance
Measure-Command { Start-ADHealthCheck }
# Use streaming for large datasets
Get-ReplicationReport | Where-Object {$_.NumberOfFailures -gt 0} | Export-Csv "Issues.csv" -NoTypeInformation
Integration Examples
SCOM Integration
# Custom SCOM discovery script
function New-SCOMDiscovery
{
$domainControllers = Get-ForestDomainControllers
foreach ($dc in $domainControllers)
{
# Create SCOM discovery data
$discoveryData = @{
ComputerName = $dc.Name
Domain = $dc.Domain
Site = $dc.Site
}
# Submit to SCOM
}
}
Email Reporting
function Send-ADHealthReport
{
param(
[string[]]$Recipients,
[string]$SmtpServer,
[PSCredential]$Credential
)
$report = Start-ADHealthCheck -Format "HTML" -OutputPath $env:TEMP
$mailParams = @{
To = $Recipients
From = "ad-health@contoso.com"
Subject = "Active Directory Health Report - $(Get-Date -Format 'yyyy-MM-dd')"
Body = "Please see attached Active Directory health report."
SmtpServer = $SmtpServer
Attachments = $report
}
if ($Credential) { $mailParams.Credential = $Credential }
Send-MailMessage @mailParams
}
PowerBI Integration
# Export data for PowerBI analysis
function Export-PowerBIData
{
$dcResults = Start-ADHealthCheck -Format "JSON"
# Transform for PowerBI
$powerBIData = $dcResults | Select-Object @{
Name = "Date"
Expression = { Get-Date -Format "yyyy-MM-dd" }
}, ComputerName, OverallStatus, @{
Name = "FailedTestCount"
Expression = { ($_.Tests.Values | Where-Object { $_ -eq "FAILED" }).Count }
}
$powerBIData | Export-Csv "PowerBI-ADHealth.csv" -NoTypeInformation
}
Best Practices
Security Best Practices
- Use dedicated service accounts for automated health checks
- Store credentials securely using Windows Credential Manager or Azure Key Vault
- Implement audit logging for all diagnostic activities
- Restrict access to health check reports containing sensitive information
- Use encrypted connections when possible
Operational Best Practices
- Schedule regular health checks (daily recommended)
- Set up alerting for critical failures
- Maintain historical data for trend analysis
- Document remediation procedures for common failures
- Test disaster recovery scenarios regularly
Performance Best Practices
- Run during maintenance windows for comprehensive tests
- Use parallel processing for large environments
- Implement caching for repeated operations
- Monitor resource usage during execution
- Optimize network connectivity between test systems and DCs
Advanced Configuration
Custom Test Selection
# Define custom test suites
$CriticalTests = @("Connectivity", "Replications", "Services", "Advertising")
$FullTests = @("Connectivity", "Advertising", "CheckSDRefDom", "CrossRefValidation", "DFSREvent", "DNS", "FrsEvent", "Intersite", "KccEvent", "KnowsOfRoleHolders", "MachineAccount", "NCSecDesc", "NetLogons", "ObjectsReplicated", "Replications", "RidManager", "Services", "SystemLog", "VerifyReferences")
# Run specific test suite
Start-ADHealthCheck -Tests $CriticalTests
Multi-Forest Support
function Get-MultiForestHealth
{
param([string[]]$Forests)
$allResults = @()
foreach ($forest in $Forests)
{
try
{
$forestResults = Start-ADHealthCheck -Forest $forest
$allResults += $forestResults
}
catch
{
Write-Warning "Failed to check forest $forest : $_"
}
}
return $allResults
}