Headlines

Active Directory health checks for domain controllers: Complete Step-by-Step Guide 2026

In our previous article i have explained to generate Comprehensive report for SCCM server and its components. which will make administrator easy to find and troubleshoot the error. At Here i will be explaining you to generate report from your domain controllers and along to score card of the server, recommendation and remediations.

This script is safe.

Copy the below code and paste in your domain controller with any name with extension .ps1

example: ADHealthCheck.ps1

# ==========================================================
# COMPREHENSIVE ACTIVE DIRECTORY HEALTH CHECK SCRIPT
# M Server Pro Pvt. Ltd. - Infrastructure & EUC Practice
# Author: Habib
# Version: 3.2 
# ==========================================================

# ================= CONFIGURATION ==========================
param(
    [string]$OutputPath = "C:\ADHealthReports",
    [switch]$OpenReport,
    [switch]$ExportCSV
)

# Company Information
$CompanyInfo = @{
    Name = "M Server Pro Pvt. Ltd."
    Practice = "Infrastructure & EUC Practice"
    Author = "Habib"
    Version = "3.2"
    SupportEmail = "infrastructure@techoneglobal.com"
}

# Compliance thresholds
$Thresholds = @{
    MaxCPU = 85
    MaxMemory = 90
    MinDiskSpaceGB = 15
    MaxUptimeDays = 30
    InactiveUserDays = 90
    InactiveComputerDays = 60
    GPOStaleDays = 180
    MaxGPOSizeMB = 100
}

# ================= INITIALIZATION =========================
function Initialize-Script {
    Write-Host "Initializing AD Health Check..." -ForegroundColor Cyan
    
    # Create output directory if they don't exist
    $directories = @($OutputPath, "$OutputPath\Logs", "$OutputPath\Archives", "$OutputPath\CSV")
    foreach ($dir in $directories) {
        if (!(Test-Path $dir)) {
            New-Item -ItemType Directory -Path $dir -Force | Out-Null
            Write-Host "Created directory: $dir" -ForegroundColor Green
        }
    }
    
    # Import required modules
    $requiredModules = @("ActiveDirectory", "GroupPolicy")
    foreach ($module in $requiredModules) {
        try {
            Import-Module $module -ErrorAction Stop
            Write-Host "Imported module: $module" -ForegroundColor Green
        }
        catch {
            Write-Host "ERROR: Failed to import module $module" -ForegroundColor Red
            Write-Host "Please install RSAT tools: Install-WindowsFeature RSAT-AD-PowerShell" -ForegroundColor Yellow
            exit 1
        }
    }
    
    # Try to import DNS module but don't fail if not available
    try {
        Import-Module DnsServer -ErrorAction SilentlyContinue
        Write-Host "Imported module: DnsServer" -ForegroundColor Green
    }
    catch {
        Write-Host "Note: DnsServer module not available. DNS checks will be limited." -ForegroundColor Yellow
    }
}

# ================= HTML REPORT FUNCTIONS ==================
function Get-HTMLHeader {
    param($DomainName, $ForestName)
    
    $ReportDate = Get-Date -Format "MMMM dd, yyyy"
    $ReportTime = Get-Date -Format "HH:mm:ss"
    
    return @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Active Directory Health Check Report - $DomainName</title>
<style>
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f8f9fa;
    margin: 0;
    padding: 0;
    color: #333;
    line-height: 1.6;
}

.container {
    max-width: 1400px;
    margin: 0 auto;
    padding: 20px;
}

/* Header Styles */
.report-header {
    background: linear-gradient(135deg, #0b5394 0%, #083e6b 100%);
    color: white;
    padding: 30px;
    border-bottom: 5px solid #ffc107;
    margin-bottom: 30px;
    border-radius: 8px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

.company-info {
    margin-bottom: 20px;
}

.company-name {
    font-size: 1.8rem;
    font-weight: 700;
    margin: 0;
    color: white;
}

.company-practice {
    font-size: 1rem;
    opacity: 0.9;
    margin: 5px 0 0 0;
    font-style: italic;
}

.report-header h1 {
    margin: 10px 0 0 0;
    font-size: 2.2rem;
    font-weight: 600;
    color: white;
}

.report-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-top: 20px;
    background: rgba(255, 255, 255, 0.1);
    padding: 15px;
    border-radius: 6px;
}

.meta-item {
    flex: 1;
    min-width: 200px;
}

.meta-label {
    font-size: 0.85rem;
    opacity: 0.9;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 5px;
}

.meta-value {
    font-size: 1.1rem;
    font-weight: 600;
}

.author-info {
    font-size: 0.9rem;
    margin-top: 10px;
    opacity: 0.8;
}

/* Card Styles */
.card {
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    margin-bottom: 25px;
    overflow: hidden;
    border: 1px solid #dee2e6;
}

.card-header {
    background: linear-gradient(to right, #e6f2ff, white);
    padding: 15px 20px;
    border-bottom: 2px solid #dee2e6;
}

.card-header h2 {
    margin: 0;
    color: #0b5394;
    font-size: 1.3rem;
    font-weight: 600;
}

.card-body {
    padding: 20px;
}

/* Table Styles */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.95rem;
}

.data-table th {
    background-color: #e9ecef;
    color: #495057;
    font-weight: 600;
    padding: 12px 15px;
    text-align: left;
    border-bottom: 2px solid #dee2e6;
}

.data-table td {
    padding: 10px 15px;
    border-bottom: 1px solid #dee2e6;
    vertical-align: middle;
}

.data-table tr:hover {
    background-color: #f8f9fa;
}

/* Status Indicators */
.status {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 20px;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.status-healthy {
    background-color: #d4edda;
    color: #155724;
    border: 1px solid #c3e6cb;
}

.status-warning {
    background-color: #fff3cd;
    color: #856404;
    border: 1px solid #ffeaa7;
}

.status-critical {
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
}

.status-info {
    background-color: #d1ecf1;
    color: #0c5460;
    border: 1px solid #bee5eb;
}

/* Progress Bars */
.progress-container {
    width: 100%;
    background-color: #e9ecef;
    border-radius: 10px;
    overflow: hidden;
    height: 20px;
}

.progress-bar {
    height: 100%;
    border-radius: 10px;
    transition: width 0.3s ease;
}

.progress-cpu {
    background: linear-gradient(90deg, #28a745, #17a2b8);
}

.progress-memory {
    background: linear-gradient(90deg, #28a745, #ffc107);
}

.progress-disk {
    background: linear-gradient(90deg, #28a745, #dc3545);
}

/* Summary Grid */
.summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 20px;
    margin: 20px 0;
}

.summary-card {
    text-align: center;
    padding: 20px;
    border-radius: 8px;
    background: white;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    border-top: 4px solid;
}

.summary-card .count {
    font-size: 2.2rem;
    font-weight: 700;
    margin: 10px 0;
    color: #0b5394;
}

.summary-card .label {
    font-size: 0.9rem;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 1px;
}

.summary-card.total { border-top-color: #0b5394; }
.summary-card.healthy { border-top-color: #28a745; }
.summary-card.warning { border-top-color: #ffc107; }
.summary-card.critical { border-top-color: #dc3545; }

/* Footer */
.report-footer {
    background: #343a40;
    color: white;
    padding: 20px;
    margin-top: 40px;
    border-radius: 8px;
    text-align: center;
}

.company-footer {
    margin-bottom: 15px;
    font-size: 1.1rem;
    font-weight: 600;
    color: #ffc107;
}

.report-footer small {
    color: #adb5bd;
    font-size: 0.9rem;
}

/* Responsive Design */
@media (max-width: 768px) {
    .container {
        padding: 10px;
    }
    
    .report-header {
        padding: 20px;
    }
    
    .report-header h1 {
        font-size: 1.8rem;
    }
    
    .data-table {
        font-size: 0.85rem;
    }
    
    .summary-grid {
        grid-template-columns: 1fr;
    }
}

/* Print Styles */
@media print {
    body {
        background: white;
    }
    
    .card {
        box-shadow: none;
        border: 1px solid #ddd;
    }
    
    .report-footer {
        background: white;
        color: black;
        border-top: 2px solid #ddd;
    }
}
</style>
</head>
<body>
<div class="report-header">
<div class="container">
<div class="company-info">
<div class="company-name">$($CompanyInfo.Name)</div>
<div class="company-practice">$($CompanyInfo.Practice)</div>
</div>
<h1>Active Directory Health Check Report</h1>
<div class="author-info">Prepared by: $($CompanyInfo.Author) | Version: $($CompanyInfo.Version)</div>
<div class="report-meta">
<div class="meta-item">
<div class="meta-label">Domain</div>
<div class="meta-value">$DomainName</div>
</div>
<div class="meta-item">
<div class="meta-label">Forest</div>
<div class="meta-value">$ForestName</div>
</div>
<div class="meta-item">
<div class="meta-label">Report Date</div>
<div class="meta-value">$ReportDate</div>
</div>
<div class="meta-item">
<div class="meta-label">Report Time</div>
<div class="meta-value">$ReportTime</div>
</div>
</div>
</div>
</div>
<div class="container">
"@
}

function Get-HTMLFooter {
    return @"
</div>
<div class="report-footer">
<div class="container">
<div class="company-footer">$($CompanyInfo.Name)</div>
<small>Report generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Active Directory Health Check v$($CompanyInfo.Version)</small><br>
<small>Prepared by: $($CompanyInfo.Author) | $($CompanyInfo.Practice)</small><br>
<small>This report contains confidential information for authorized personnel only.</small>
</div>
</div>
<script>
// Simple interactive features
document.addEventListener('DOMContentLoaded', function() {
    // Toggle table rows on mobile
    if (window.innerWidth <= 768) {
        document.querySelectorAll('.card-header').forEach(header => {
            header.style.cursor = 'pointer';
            header.addEventListener('click', function() {
                const cardBody = this.nextElementSibling;
                cardBody.style.display = cardBody.style.display === 'none' ? 'block' : 'none';
            });
        });
    }
    
    // Highlight critical rows
    document.querySelectorAll('.status-critical').forEach(element => {
        const row = element.closest('tr');
        if (row) {
            row.style.backgroundColor = 'rgba(220, 53, 69, 0.05)';
        }
    });
    
    // Highlight warning rows
    document.querySelectorAll('.status-warning').forEach(element => {
        const row = element.closest('tr');
        if (row) {
            row.style.backgroundColor = 'rgba(255, 193, 7, 0.05)';
        }
    });
});
</script>
</body>
</html>
"@
}

# ================= HEALTH CHECK FUNCTIONS ================
function Get-DCHealth {
    param($DCs)
    
    Write-Host "Checking Domain Controller Health..." -ForegroundColor Cyan
    
    $dcResults = @()
    foreach ($DC in $DCs) {
        try {
            $DCName = $DC.HostName
            Write-Host "  Checking $DCName..." -ForegroundColor Gray
            
            # Get DC info
            $Roles = @()
            if ($DC.OperationMasterRoles -contains "PDCEmulator") { $Roles += "PDC" }
            if ($DC.OperationMasterRoles -contains "RIDMaster") { $Roles += "RID" }
            if ($DC.OperationMasterRoles -contains "InfrastructureMaster") { $Roles += "Infra" }
            if ($DC.OperationMasterRoles -contains "SchemaMaster") { $Roles += "Schema" }
            if ($DC.OperationMasterRoles -contains "DomainNamingMaster") { $Roles += "DomainNaming" }
            $RolesString = if ($Roles.Count -gt 0) { $Roles -join ", " } else { "Member" }
            
            # Get performance metrics
            $OS = Get-CimInstance Win32_OperatingSystem -ComputerName $DCName -ErrorAction Stop
            $CPU = [math]::Round((Get-Counter -ComputerName $DCName '\Processor(_Total)\% Processor Time' -ErrorAction Stop).CounterSamples.CookedValue, 1)
            $Mem = [math]::Round((($OS.TotalVisibleMemorySize - $OS.FreePhysicalMemory) / $OS.TotalVisibleMemorySize) * 100, 1)
            
            $Disk = Get-CimInstance Win32_LogicalDisk -ComputerName $DCName -Filter "DeviceID='C:'" -ErrorAction Stop
            $DiskFree = [math]::Round($Disk.FreeSpace / 1GB, 1)
            $DiskTotal = [math]::Round($Disk.Size / 1GB, 1)
            $Uptime = (New-TimeSpan -Start $OS.LastBootUpTime -End (Get-Date)).Days
            
            # Determine status
            $Status = "Healthy"
            $StatusClass = "status-healthy"
            
            if ($CPU -gt $Thresholds.MaxCPU -or $Mem -gt $Thresholds.MaxMemory -or $DiskFree -lt $Thresholds.MinDiskSpaceGB) {
                $Status = "Critical"
                $StatusClass = "status-critical"
            }
            elseif ($CPU -gt 70 -or $Mem -gt 80 -or $DiskFree -lt 25 -or $Uptime -gt $Thresholds.MaxUptimeDays) {
                $Status = "Warning"
                $StatusClass = "status-warning"
            }
            
            $dcResults += [PSCustomObject]@{
                DCName = $DCName
                Site = $DC.Site
                Roles = $RolesString
                CPU = $CPU
                Memory = $Mem
                DiskFreeGB = $DiskFree
                DiskTotalGB = $DiskTotal
                UptimeDays = $Uptime
                Status = $Status
                StatusClass = $StatusClass
            }
            
            Write-Host "    CPU: $CPU%, Memory: $Mem%, Disk: $DiskFree GB free, Status: $Status" -ForegroundColor Green
            
        }
        catch {
            Write-Host "    ERROR: Failed to query $($DC.HostName): $($_.Exception.Message)" -ForegroundColor Red
            $dcResults += [PSCustomObject]@{
                DCName = $DC.HostName
                Site = "Error"
                Roles = "Error"
                CPU = 0
                Memory = 0
                DiskFreeGB = 0
                DiskTotalGB = 0
                UptimeDays = 0
                Status = "Error"
                StatusClass = "status-critical"
            }
        }
    }
    
    return $dcResults
}

function Get-ADServicesStatus {
    param($DCs)
    
    Write-Host "Checking AD Services Status..." -ForegroundColor Cyan
    
    $servicesResults = @()
    $Services = @("NTDS", "DNS", "Netlogon", "KDC", "W32Time")
    
    foreach ($DC in $DCs) {
        Write-Host "  Checking services on $($DC.HostName)..." -ForegroundColor Gray
        
        $serviceStatus = @{}
        $allRunning = $true
        $runningCount = 0
        
        foreach ($Service in $Services) {
            try {
                $Status = (Get-Service -ComputerName $DC.HostName -Name $Service -ErrorAction Stop).Status
                $serviceStatus[$Service] = if ($Status -eq "Running") { "Running" } else { "Stopped" }
                $allRunning = $allRunning -and ($Status -eq "Running")
                if ($Status -eq "Running") { $runningCount++ }
            }
            catch {
                $serviceStatus[$Service] = "Error"
                $allRunning = $false
            }
        }
        
        $OverallStatus = if ($allRunning) { "Healthy" } elseif ($runningCount -eq 0) { "Critical" } else { "Degraded" }
        $OverallClass = if ($allRunning) { "status-healthy" } elseif ($runningCount -eq 0) { "status-critical" } else { "status-warning" }
        
        $servicesResults += [PSCustomObject]@{
            DCName = $DC.HostName
            NTDS = $serviceStatus["NTDS"]
            DNS = $serviceStatus["DNS"]
            Netlogon = $serviceStatus["Netlogon"]
            KDC = $serviceStatus["KDC"]
            W32Time = $serviceStatus["W32Time"]
            Overall = $OverallStatus
            OverallClass = $OverallClass
        }
        
        Write-Host "    Services status: $OverallStatus ($runningCount/$($Services.Count) running)" -ForegroundColor Green
    }
    
    return $servicesResults
}

function Get-ReplicationStatus {
    Write-Host "Checking AD Replication..." -ForegroundColor Cyan
    
    try {
        $replication = Get-ADReplicationFailure -Target * -ErrorAction Stop
        $failures = ($replication | Where-Object {$_.FailureCount -gt 0}).Count
        
        if ($failures -eq 0) {
            Write-Host "  Replication: Healthy (No failures detected)" -ForegroundColor Green
        }
        else {
            Write-Host "  Replication: Warning ($failures failures detected)" -ForegroundColor Yellow
        }
        
        return $failures
    }
    catch {
        Write-Host "  ERROR: Failed to check replication: $($_.Exception.Message)" -ForegroundColor Red
        return -1
    }
}

function Get-UserAccountAnalysis {
    Write-Host "Analyzing User Accounts..." -ForegroundColor Cyan
    
    try {
        $Users = Get-ADUser -Filter * -Properties Enabled, LastLogonDate, PasswordNeverExpires, PasswordExpired, LockedOut, whenCreated -ErrorAction Stop
        
        $analysis = @{
            Total = $Users.Count
            Enabled = ($Users | Where-Object {$_.Enabled}).Count
            Disabled = ($Users | Where-Object {-not $_.Enabled}).Count
            Locked = ($Users | Where-Object {$_.LockedOut}).Count
            PasswordNeverExpires = ($Users | Where-Object {$_.PasswordNeverExpires}).Count
            PasswordExpired = ($Users | Where-Object {$_.PasswordExpired}).Count
            Inactive30 = ($Users | Where-Object {$_.Enabled -and $_.LastLogonDate -and $_.LastLogonDate -lt (Get-Date).AddDays(-30)}).Count
            Inactive60 = ($Users | Where-Object {$_.Enabled -and $_.LastLogonDate -and $_.LastLogonDate -lt (Get-Date).AddDays(-60)}).Count
            Inactive90 = ($Users | Where-Object {$_.Enabled -and $_.LastLogonDate -and $_.LastLogonDate -lt (Get-Date).AddDays(-90)}).Count
        }
        
        Write-Host "  Total Users: $($analysis.Total)" -ForegroundColor Green
        Write-Host "  Enabled Users: $($analysis.Enabled)" -ForegroundColor Green
        Write-Host "  Password Never Expires: $($analysis.PasswordNeverExpires)" -ForegroundColor $(if ($analysis.PasswordNeverExpires -eq 0) { "Green" } else { "Yellow" })
        Write-Host "  Inactive (90+ days): $($analysis.Inactive90)" -ForegroundColor $(if ($analysis.Inactive90 -eq 0) { "Green" } else { "Yellow" })
        
        return $analysis
    }
    catch {
        Write-Host "  ERROR: Failed to analyze user accounts: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
}

function Get-ComputerAccountAnalysis {
    Write-Host "Analyzing Computer Accounts..." -ForegroundColor Cyan
    
    try {
        $Computers = Get-ADComputer -Filter * -Properties Enabled, LastLogonDate, OperatingSystem, whenCreated -ErrorAction Stop
        
        $analysis = @{
            Total = $Computers.Count
            Enabled = ($Computers | Where-Object {$_.Enabled}).Count
            Disabled = ($Computers | Where-Object {-not $_.Enabled}).Count
            Servers = ($Computers | Where-Object {$_.OperatingSystem -match 'Server'}).Count
            Workstations = ($Computers | Where-Object {$_.OperatingSystem -notmatch 'Server' -and $_.OperatingSystem -notmatch '$'}).Count
            DCs = ($Computers | Where-Object {$_.OperatingSystem -match 'Server' -and $_.DNSHostName -match 'DC'}).Count
            Inactive30 = 0
            Inactive60 = 0
            Inactive90 = 0
        }
        
        foreach ($Computer in $Computers) {
            if ($Computer.LastLogonDate -and $Computer.LastLogonDate -lt (Get-Date).AddDays(-30)) {
                $analysis.Inactive30++
            }
            if ($Computer.LastLogonDate -and $Computer.LastLogonDate -lt (Get-Date).AddDays(-60)) {
                $analysis.Inactive60++
            }
            if (($Computer.LastLogonDate -and $Computer.LastLogonDate -lt (Get-Date).AddDays(-90)) -or 
                (-not $Computer.LastLogonDate -and $Computer.whenCreated -lt (Get-Date).AddDays(-90))) {
                $analysis.Inactive90++
            }
        }
        
        Write-Host "  Total Computers: $($analysis.Total)" -ForegroundColor Green
        Write-Host "  Servers: $($analysis.Servers)" -ForegroundColor Green
        Write-Host "  Domain Controllers: $($analysis.DCs)" -ForegroundColor Green
        Write-Host "  Inactive (90+ days): $($analysis.Inactive90)" -ForegroundColor $(if ($analysis.Inactive90 -eq 0) { "Green" } else { "Yellow" })
        
        return $analysis
    }
    catch {
        Write-Host "  ERROR: Failed to analyze computer accounts: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
}

function Get-ADRecycleBinStatus {
    Write-Host "Checking AD Recycle Bin..." -ForegroundColor Cyan
    
    try {
        $RecycleBin = Get-ADOptionalFeature -Filter 'name -like "Recycle Bin Feature"' -ErrorAction Stop
        $RBEnabled = $RecycleBin.EnabledScopes.Count -gt 0
        
        if ($RBEnabled) {
            try {
                $DeletedUsers = (Get-ADObject -Filter 'isDeleted -eq $true -and objectClass -eq "user"' -IncludeDeletedObjects -ErrorAction Stop).Count
            }
            catch {
                $DeletedUsers = "Access Denied"
            }
            
            try {
                $DeletedComputers = (Get-ADObject -Filter 'isDeleted -eq $true -and objectClass -eq "computer"' -IncludeDeletedObjects -ErrorAction Stop).Count
            }
            catch {
                $DeletedComputers = "Access Denied"
            }
            
            try {
                $DeletedGroups = (Get-ADObject -Filter 'isDeleted -eq $true -and objectClass -eq "group"' -IncludeDeletedObjects -ErrorAction Stop).Count
            }
            catch {
                $DeletedGroups = "Access Denied"
            }
            
            Write-Host "  Recycle Bin: Enabled" -ForegroundColor Green
            Write-Host "  Deleted Users: $DeletedUsers" -ForegroundColor Gray
            Write-Host "  Deleted Computers: $DeletedComputers" -ForegroundColor Gray
            
            return [PSCustomObject]@{
                Enabled = $true
                Status = "Enabled"
                StatusClass = "status-healthy"
                DeletedUsers = $DeletedUsers
                DeletedComputers = $DeletedComputers
                DeletedGroups = $DeletedGroups
            }
        }
        else {
            Write-Host "  Recycle Bin: Disabled" -ForegroundColor Yellow
            return [PSCustomObject]@{
                Enabled = $false
                Status = "Disabled"
                StatusClass = "status-warning"
                DeletedUsers = "N/A"
                DeletedComputers = "N/A"
                DeletedGroups = "N/A"
            }
        }
    }
    catch {
        Write-Host "  ERROR: Failed to check Recycle Bin: $($_.Exception.Message)" -ForegroundColor Red
        return [PSCustomObject]@{
            Enabled = $false
            Status = "Error"
            StatusClass = "status-critical"
            DeletedUsers = "Error"
            DeletedComputers = "Error"
            DeletedGroups = "Error"
        }
    }
}

function Get-PasswordPolicyStatus {
    Write-Host "Checking Password Policy..." -ForegroundColor Cyan
    
    try {
        $Policy = Get-ADDefaultDomainPasswordPolicy -ErrorAction Stop
        
        $status = [PSCustomObject]@{
            MinLength = $Policy.MinPasswordLength
            Complexity = $Policy.ComplexityEnabled
            MaxAge = $Policy.MaxPasswordAge.Days
            HistoryCount = $Policy.PasswordHistoryCount
            LockoutThreshold = $Policy.LockoutThreshold
            LockoutDuration = $Policy.LockoutDuration
            LockoutObservationWindow = $Policy.LockoutObservationWindow
        }
        
        Write-Host "  Password Policy retrieved successfully" -ForegroundColor Green
        return $status
    }
    catch {
        Write-Host "  ERROR: Failed to get password policy: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
}

# ================= GPO ANALYSIS FUNCTIONS =================
function Get-GPOAnalysis {
    Write-Host "Analyzing Group Policy Objects..." -ForegroundColor Cyan
    
    try {
        $AllGPOs = Get-GPO -All -ErrorAction Stop
        Write-Host "  Found $($AllGPOs.Count) GPOs in domain" -ForegroundColor Green
        
        $GPOAnalysis = @()
        $GPOStatistics = @{
            Total = $AllGPOs.Count
            Unlinked = 0
            Stale = 0
            Large = 0
            Disabled = 0
            MissingPermissions = 0
        }
        
        foreach ($GPO in $AllGPOs) {
            $GPODetails = @{
                Name = $GPO.DisplayName
                ID = $GPO.Id
                Owner = $GPO.Owner
                Created = $GPO.CreationTime
                Modified = $GPO.ModificationTime
                Enabled = $GPO.GpoStatus -eq "AllSettingsEnabled"
                WmiFilter = $GPO.WmiFilter.Name
            }
            
            # Get GPO links
            try {
                $GPOLinks = Get-GPOReport -Guid $GPO.Id -ReportType XML
                $XMLReport = [xml]$GPOLinks
                $Links = $XMLReport.GPO.LinksTo
                $LinkCount = if ($Links) { $Links.Count } else { 0 }
                $GPODetails.LinkCount = $LinkCount
                
                if ($LinkCount -eq 0) {
                    $GPOStatistics.Unlinked++
                    $GPODetails.IsUnlinked = $true
                }
                else {
                    $GPODetails.IsUnlinked = $false
                }
            }
            catch {
                $GPODetails.LinkCount = "Error"
                $GPODetails.IsUnlinked = $null
            }
            
            # Check if GPO is stale (not modified in X days)
            $DaysSinceModification = (Get-Date) - $GPO.ModificationTime
            $GPODetails.DaysSinceModification = $DaysSinceModification.Days
            
            if ($DaysSinceModification.Days -gt $Thresholds.GPOStaleDays) {
                $GPOStatistics.Stale++
                $GPODetails.IsStale = $true
            }
            else {
                $GPODetails.IsStale = $false
            }
            
            # Check GPO size
            try {
                $GPOFolder = "\\$DomainName\SYSVOL\$DomainName\Policies\{$($GPO.Id)}\"
                if (Test-Path $GPOFolder) {
                    $GPOSize = (Get-ChildItem $GPOFolder -Recurse | Measure-Object Length -Sum).Sum / 1MB
                    $GPODetails.SizeMB = [math]::Round($GPOSize, 2)
                    
                    if ($GPOSize -gt $Thresholds.MaxGPOSizeMB) {
                        $GPOStatistics.Large++
                        $GPODetails.IsLarge = $true
                    }
                    else {
                        $GPODetails.IsLarge = $false
                    }
                }
                else {
                    $GPODetails.SizeMB = "N/A"
                    $GPODetails.IsLarge = $false
                }
            }
            catch {
                $GPODetails.SizeMB = "Error"
                $GPODetails.IsLarge = $false
            }
            
            if (-not $GPODetails.Enabled) {
                $GPOStatistics.Disabled++
            }
            
            $GPOAnalysis += [PSCustomObject]$GPODetails
        }
        
        # Get orphaned GPOs (in SYSVOL but not in AD)
        Write-Host "  Checking for orphaned GPOs in SYSVOL..." -ForegroundColor Gray
        $OrphanedGPOs = Get-OrphanedGPOs -DomainName $DomainName
        $GPOStatistics.Orphaned = $OrphanedGPOs.Count
        
        return @{
            GPOs = $GPOAnalysis
            Statistics = $GPOStatistics
            OrphanedGPOs = $OrphanedGPOs
        }
    }
    catch {
        Write-Host "  ERROR: Failed to analyze GPOs: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
}

function Get-OrphanedGPOs {
    param($DomainName)
    
    $Orphaned = @()
    
    try {
        $SYSVOLPath = "\\$DomainName\SYSVOL\$DomainName\Policies\"
        if (Test-Path $SYSVOLPath) {
            $GPODirs = Get-ChildItem -Path $SYSVOLPath -Directory -Filter "{*}"
            
            foreach ($Dir in $GPODirs) {
                $GPOID = $Dir.Name.Trim('{}')
                $GPO = $null
                
                try {
                    $GPO = Get-GPO -Guid $GPOID -ErrorAction SilentlyContinue
                }
                catch {}
                
                if (-not $GPO) {
                    $DirSize = (Get-ChildItem $Dir.FullName -Recurse | Measure-Object Length -Sum).Sum / 1MB
                    $Orphaned += [PSCustomObject]@{
                        ID = $GPOID
                        Path = $Dir.FullName
                        SizeMB = [math]::Round($DirSize, 2)
                        LastModified = $Dir.LastWriteTime
                    }
                }
            }
        }
    }
    catch {
        Write-Host "    WARNING: Could not check for orphaned GPOs" -ForegroundColor Yellow
    }
    
    return $Orphaned
}

# ================= DNS HEALTH FUNCTIONS ===================
function Get-DNSHealth {
    param($DCs)
    
    Write-Host "Checking DNS Health..." -ForegroundColor Cyan
    
    $DNSResults = @()
    
    foreach ($DC in $DCs) {
        try {
            Write-Host "  Checking DNS on $($DC.HostName)..." -ForegroundColor Gray
            
            # Check if DNS module is available
            if (Get-Module -Name DnsServer -ListAvailable) {
                Import-Module DnsServer -ErrorAction SilentlyContinue
                
                # Get DNS zones
                $Zones = Get-DnsServerZone -ComputerName $DC.HostName -ErrorAction Stop
                
                $ZoneStats = @{
                    ForwardZones = ($Zones | Where-Object { -not $_.IsReverseLookupZone }).Count
                    ReverseZones = ($Zones | Where-Object { $_.IsReverseLookupZone }).Count
                    ADIntegrated = ($Zones | Where-Object { $_.ZoneType -eq "Primary" -and $_.IsDsIntegrated }).Count
                }
                
                # Check for stale records
                $StaleRecords = 0
                foreach ($Zone in ($Zones | Where-Object { -not $_.IsReverseLookupZone -and $_.ZoneName -ne "TrustAnchors" })) {
                    try {
                        $Records = Get-DnsServerResourceRecord -ComputerName $DC.HostName -ZoneName $Zone.ZoneName -ErrorAction SilentlyContinue
                        $StaleRecords += ($Records | Where-Object { 
                            $_.Timestamp -and $_.Timestamp -lt (Get-Date).AddDays(-30) 
                        }).Count
                    }
                    catch {}
                }
                
                # Check scavenging settings
                $Scavenging = Get-DnsServerScavenging -ComputerName $DC.HostName -ErrorAction SilentlyContinue
                
                $DNSResults += [PSCustomObject]@{
                    DCName = $DC.HostName
                    Zones = $Zones.Count
                    ForwardZones = $ZoneStats.ForwardZones
                    ReverseZones = $ZoneStats.ReverseZones
                    ADIntegrated = $ZoneStats.ADIntegrated
                    StaleRecords = $StaleRecords
                    ScavengingEnabled = if ($Scavenging) { $Scavenging.ScavengingState } else { $false }
                    NoRefreshInterval = if ($Scavenging) { $Scavenging.NoRefreshInterval.Days } else { 0 }
                    RefreshInterval = if ($Scavenging) { $Scavenging.RefreshInterval.Days } else { 0 }
                    Status = "Checked"
                }
                
                Write-Host "    Zones: $($Zones.Count) (Forward: $($ZoneStats.ForwardZones), Reverse: $($ZoneStats.ReverseZones))" -ForegroundColor Green
            }
            else {
                $DNSResults += [PSCustomObject]@{
                    DCName = $DC.HostName
                    Zones = "DNS Module Not Available"
                    ForwardZones = "N/A"
                    ReverseZones = "N/A"
                    ADIntegrated = "N/A"
                    StaleRecords = "N/A"
                    ScavengingEnabled = "N/A"
                    NoRefreshInterval = "N/A"
                    RefreshInterval = "N/A"
                    Status = "DNS Module Missing"
                }
                
                Write-Host "    DNS Server module not installed on $($DC.HostName)" -ForegroundColor Yellow
            }
            
        }
        catch {
            Write-Host "    ERROR: Failed to check DNS on $($DC.HostName): $($_.Exception.Message)" -ForegroundColor Red
            $DNSResults += [PSCustomObject]@{
                DCName = $DC.HostName
                Zones = "Error"
                ForwardZones = "Error"
                ReverseZones = "Error"
                ADIntegrated = "Error"
                StaleRecords = "Error"
                ScavengingEnabled = "Error"
                NoRefreshInterval = "Error"
                RefreshInterval = "Error"
                Status = "Error"
            }
        }
    }
    
    return $DNSResults
}

# ================= SITE & REPLICATION FUNCTIONS ===========
function Get-ADSiteReplicationInfo {
    Write-Host "Checking AD Sites & Replication..." -ForegroundColor Cyan
    
    try {
        # Alternative method to get site information using ADSI
        Write-Host "  Getting AD Sites information..." -ForegroundColor Gray
        
        # Method 1: Try using Get-ADObject if Get-ADSite is not available
        $Sites = @()
        try {
            # Try to get sites using ADSI
            $SitesContainer = [ADSI]"LDAP://CN=Sites,CN=Configuration,$((Get-ADDomain).DistinguishedName)"
            $SiteObjects = $SitesContainer.Children | Where-Object { $_.SchemaClassName -eq "site" }
            
            foreach ($Site in $SiteObjects) {
                $Sites += [PSCustomObject]@{
                    Name = $Site.Name
                    Location = $Site.Location
                    Description = $Site.Description
                }
            }
            
            Write-Host "    Found $($Sites.Count) AD sites using ADSI" -ForegroundColor Green
        }
        catch {
            Write-Host "    WARNING: Could not retrieve detailed site information" -ForegroundColor Yellow
            # Create a simple site object
            $Sites = @([PSCustomObject]@{
                Name = "Default-First-Site-Name"
                Location = ""
                Description = "Default site"
            })
        }
        
        # Get replication failures
        $ReplicationFailures = 0
        try {
            $replication = Get-ADReplicationFailure -Target * -ErrorAction Stop
            $ReplicationFailures = ($replication | Where-Object {$_.FailureCount -gt 0}).Count
        }
        catch {
            Write-Host "    WARNING: Could not get replication failures" -ForegroundColor Yellow
        }
        
        # Get replication summary
        $ReplicationSummary = @()
        try {
            $ReplicationSummary = Get-ADReplicationSummary -ErrorAction Stop | Select-Object -First 5
        }
        catch {
            Write-Host "    WARNING: Could not get detailed replication summary" -ForegroundColor Yellow
        }
        
        return @{
            Sites = $Sites
            ReplicationFailures = $ReplicationFailures
            ReplicationSummary = $ReplicationSummary
        }
    }
    catch {
        Write-Host "  ERROR: Failed to get site/replication info: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
}

# ================= FSMO ROLES FUNCTIONS ===================
function Get-FSMORoles {
    Write-Host "Checking FSMO Roles..." -ForegroundColor Cyan
    
    try {
        $FSMORoles = @()
        
        # Get all FSMO role holders
        $Forest = Get-ADForest -ErrorAction Stop
        $Domain = Get-ADDomain -ErrorAction Stop
        
        $FSMORoles += [PSCustomObject]@{
            Role = "Schema Master"
            Holder = $Forest.SchemaMaster
            RoleType = "Forest"
        }
        
        $FSMORoles += [PSCustomObject]@{
            Role = "Domain Naming Master"
            Holder = $Forest.DomainNamingMaster
            RoleType = "Forest"
        }
        
        $FSMORoles += [PSCustomObject]@{
            Role = "PDC Emulator"
            Holder = $Domain.PDCEmulator
            RoleType = "Domain"
        }
        
        $FSMORoles += [PSCustomObject]@{
            Role = "RID Master"
            Holder = $Domain.RIDMaster
            RoleType = "Domain"
        }
        
        $FSMORoles += [PSCustomObject]@{
            Role = "Infrastructure Master"
            Holder = $Domain.InfrastructureMaster
            RoleType = "Domain"
        }
        
        # Check if roles are online
        foreach ($Role in $FSMORoles) {
            try {
                $HostName = $Role.Holder -replace "^.*\\", ""
                $Test = Test-Connection -ComputerName $HostName -Count 1 -Quiet -ErrorAction Stop
                $Role | Add-Member -NotePropertyName IsOnline -NotePropertyValue $Test -Force
            }
            catch {
                $Role | Add-Member -NotePropertyName IsOnline -NotePropertyValue $false -Force
            }
        }
        
        Write-Host "  All FSMO roles located" -ForegroundColor Green
        return $FSMORoles
    }
    catch {
        Write-Host "  ERROR: Failed to get FSMO roles: $($_.Exception.Message)" -ForegroundColor Red
        return $null
    }
}

# ================= BACKUP & RECOVERY FUNCTIONS ============
function Get-BackupStatus {
    param($DCs)
    
    Write-Host "Checking Backup Status..." -ForegroundColor Cyan
    
    $BackupStatus = @()
    
    foreach ($DC in $DCs) {
        try {
            Write-Host "  Checking backups on $($DC.HostName)..." -ForegroundColor Gray
            
            # Check for Windows Server Backup events
            $BackupEvents = $null
            try {
                $BackupEvents = Get-WinEvent -ComputerName $DC.HostName -FilterHashtable @{
                    LogName = 'Application'
                    ProviderName = 'Microsoft-Windows-Backup'
                    ID = 4, 5
                    StartTime = (Get-Date).AddDays(-7)
                } -MaxEvents 5 -ErrorAction SilentlyContinue
            }
            catch {
                Write-Host "    WARNING: Could not access backup events on $($DC.HostName)" -ForegroundColor Yellow
            }
            
            $LastBackup = if ($BackupEvents) { 
                ($BackupEvents | Sort-Object TimeCreated -Descending | Select-Object -First 1).TimeCreated 
            } else { 
                $null 
            }
            
            $BackupStatus += [PSCustomObject]@{
                DCName = $DC.HostName
                LastBackup = $LastBackup
                DaysSinceLastBackup = if ($LastBackup) { ((Get-Date) - $LastBackup).Days } else { 999 }
                BackupEventsFound = if ($BackupEvents) { $BackupEvents.Count } else { 0 }
            }
            
            $StatusText = if ($LastBackup) { "Last backup: $($LastBackup.ToString('yyyy-MM-dd HH:mm'))" } else { "No recent backup events found" }
            Write-Host "    $StatusText" -ForegroundColor $(if ($LastBackup) { "Green" } else { "Yellow" })
            
        }
        catch {
            Write-Host "    ERROR: Failed to check backups on $($DC.HostName)" -ForegroundColor Red
            $BackupStatus += [PSCustomObject]@{
                DCName = $DC.HostName
                LastBackup = $null
                DaysSinceLastBackup = 999
                BackupEventsFound = 0
            }
        }
    }
    
    return $BackupStatus
}

# ================= TRUST RELATIONSHIPS ====================
function Get-TrustRelationships {
    Write-Host "Checking Trust Relationships..." -ForegroundColor Cyan
    
    try {
        $Trusts = Get-ADTrust -Filter * -ErrorAction Stop
        
        $TrustAnalysis = foreach ($Trust in $Trusts) {
            [PSCustomObject]@{
                Name = $Trust.Name
                Direction = $Trust.Direction
                Type = $Trust.TrustType
                Transitive = $Trust.Transitive
                Created = $Trust.whenCreated
            }
        }
        
        Write-Host "  Found $($Trusts.Count) trust relationships" -ForegroundColor Green
        return $TrustAnalysis
    }
    catch {
        Write-Host "  WARNING: Failed to get trust relationships: $($_.Exception.Message)" -ForegroundColor Yellow
        return $null
    }
}

# ================= MAIN SCRIPT ============================
Write-Host "=========================================" -ForegroundColor Cyan
Write-Host "COMPREHENSIVE AD HEALTH CHECK v$($CompanyInfo.Version)" -ForegroundColor Cyan
Write-Host "Company: $($CompanyInfo.Name)" -ForegroundColor Cyan
Write-Host "Author: $($CompanyInfo.Author)" -ForegroundColor Cyan
Write-Host "Practice: $($CompanyInfo.Practice)" -ForegroundColor Cyan
Write-Host "=========================================" -ForegroundColor Cyan

# Initialize
Initialize-Script

try {
    # Get domain information
    Write-Host "Getting domain information..." -ForegroundColor Cyan
    $DomainInfo = Get-ADDomain -ErrorAction Stop
    $ForestInfo = Get-ADForest -ErrorAction Stop
    $DomainName = $DomainInfo.DNSRoot
    $ForestName = $ForestInfo.Name
    
    Write-Host "Domain: $DomainName" -ForegroundColor Green
    Write-Host "Forest: $ForestName" -ForegroundColor Green
    Write-Host "Domain Mode: $($DomainInfo.DomainMode)" -ForegroundColor Green
    Write-Host "Forest Mode: $($ForestInfo.ForestMode)" -ForegroundColor Green
    
    # Get all Domain Controllers
    $DCs = Get-ADDomainController -Filter *
    Write-Host "Found $($DCs.Count) Domain Controller(s):" -ForegroundColor Green
    $DCs | ForEach-Object { Write-Host "  - $($_.HostName) ($($_.Site))" -ForegroundColor White }
    
    # Run comprehensive health checks
    Write-Host "`nRunning comprehensive health checks..." -ForegroundColor Cyan
    
    # 1. DC Health
    $dcHealth = Get-DCHealth -DCs $DCs
    
    # 2. AD Services Status
    $adServices = Get-ADServicesStatus -DCs $DCs
    
    # 3. Replication Status
    $replicationFailures = Get-ReplicationStatus
    
    # 4. User Account Analysis
    $userAnalysis = Get-UserAccountAnalysis
    
    # 5. Computer Account Analysis
    $computerAnalysis = Get-ComputerAccountAnalysis
    
    # 6. AD Recycle Bin Status
    $recycleBinStatus = Get-ADRecycleBinStatus
    
    # 7. Password Policy Status
    $passwordPolicy = Get-PasswordPolicyStatus
    
    # 8. GPO Analysis
    $gpoAnalysis = Get-GPOAnalysis
    
    # 9. DNS Health
    $dnsHealth = Get-DNSHealth -DCs $DCs
    
    # 10. AD Site & Replication Info
    $siteReplicationInfo = Get-ADSiteReplicationInfo
    
    # 11. FSMO Roles
    $fsmoRoles = Get-FSMORoles
    
    # 12. Backup Status
    $backupStatus = Get-BackupStatus -DCs $DCs
    
    # 13. Trust Relationships
    $trustRelationships = Get-TrustRelationships
    
    # Calculate overall scores
    $criticalCount = ($dcHealth | Where-Object { $_.Status -eq "Critical" }).Count
    $warningCount = ($dcHealth | Where-Object { $_.Status -eq "Warning" }).Count
    
    $overallScore = 100
    if ($dcHealth.Count -gt 0) {
        $healthyPercent = ($dcHealth | Where-Object { $_.Status -eq "Healthy" }).Count / $dcHealth.Count * 100
        $overallScore = [math]::Round($healthyPercent * 0.6, 1)  # DC health contributes 60%
    }
    
    # Additional scoring components
    if ($userAnalysis) {
        $userScore = 100
        if ($userAnalysis.PasswordNeverExpires -gt 0) { $userScore -= 10 }
        if ($userAnalysis.Inactive90 -gt 0) { $userScore -= 10 }
        if ($userAnalysis.Locked -gt 0) { $userScore -= 5 }
        $overallScore += [math]::Round($userScore * 0.1, 1)  # User health contributes 10%
    }
    
    if ($gpoAnalysis) {
        $gpoScore = 100
        if ($gpoAnalysis.Statistics.Unlinked -gt 0) { $gpoScore -= 10 }
        if ($gpoAnalysis.Statistics.Stale -gt 0) { $gpoScore -= 10 }
        if ($gpoAnalysis.Statistics.Orphaned -gt 0) { $gpoScore -= 20 }
        $overallScore += [math]::Round($gpoScore * 0.1, 1)  # GPO health contributes 10%
    }
    
    if ($siteReplicationInfo -and $siteReplicationInfo.ReplicationFailures -gt 0) {
        $overallScore -= 10  # Replication failures deduct 10%
    }
    
    $overallScore = [math]::Max(0, [math]::Min(100, $overallScore))
    
    # Generate HTML report
    $ReportFile = "$OutputPath\AD_Comprehensive_HealthCheck_$DomainName_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
    
    Write-Host "`nGenerating comprehensive HTML report..." -ForegroundColor Cyan
    
    # Start HTML content
    $HTML = Get-HTMLHeader -DomainName $DomainName -ForestName $ForestName
    
    # ================= EXECUTIVE SUMMARY =====================
    $HTML += @"
<div class="card">
<div class="card-header">
<h2>Executive Summary</h2>
</div>
<div class="card-body">
<p>This comprehensive report provides a complete assessment of the Active Directory environment for <strong>$DomainName</strong> across all $($DCs.Count) Domain Controllers.</p>

<div class="summary-grid">
<div class="summary-card total">
<div class="label">Domain Controllers</div>
<div class="count">$($DCs.Count)</div>
</div>
<div class="summary-card $(if ($criticalCount -eq 0) {'healthy'} else {'critical'})">
<div class="label">Critical Issues</div>
<div class="count">$criticalCount</div>
</div>
<div class="summary-card $(if ($warningCount -eq 0) {'healthy'} else {'warning'})">
<div class="label">Warnings</div>
<div class="count">$warningCount</div>
</div>
<div class="summary-card $(if ($overallScore -ge 90) {'healthy'} elseif ($overallScore -ge 70) {'warning'} else {'critical'})">
<div class="label">Overall Score</div>
<div class="count">$overallScore%</div>
</div>
</div>

<div class="summary-grid">
<div class="summary-card total">
<div class="label">Total Users</div>
<div class="count">$(if ($userAnalysis) {$userAnalysis.Total} else {'N/A'})</div>
</div>
<div class="summary-card total">
<div class="label">Total Computers</div>
<div class="count">$(if ($computerAnalysis) {$computerAnalysis.Total} else {'N/A'})</div>
</div>
<div class="summary-card $(if ($gpoAnalysis.Statistics.Total) {'total'} else {'warning'})">
<div class="label">Total GPOs</div>
<div class="count">$(if ($gpoAnalysis) {$gpoAnalysis.Statistics.Total} else {'N/A'})</div>
</div>
<div class="summary-card $(if ($siteReplicationInfo -and $siteReplicationInfo.ReplicationFailures -eq 0) {'healthy'} else {'critical'})">
<div class="label">Replication Issues</div>
<div class="count">$(if ($siteReplicationInfo) {$siteReplicationInfo.ReplicationFailures} else {'N/A'})</div>
</div>
</div>

<h3>Environment Overview</h3>
<table class="data-table">
<tr><th>Property</th><th>Value</th></tr>
<tr><td>Domain Functional Level</td><td>$($DomainInfo.DomainMode)</td></tr>
<tr><td>Forest Functional Level</td><td>$($ForestInfo.ForestMode)</td></tr>
<tr><td>Domain SID</td><td>$($DomainInfo.DomainSID.Value)</td></tr>
<tr><td>Child Domains</td><td>$(($ForestInfo.Domains | Where-Object { $_ -ne $DomainName }).Count)</td></tr>
<tr><td>Sites</td><td>$(if ($siteReplicationInfo) {$siteReplicationInfo.Sites.Count} else {'N/A'})</td></tr>
<tr><td>Trust Relationships</td><td>$(if ($trustRelationships) {$trustRelationships.Count} else {'N/A'})</td></tr>
</table>
</div>
</div>
"@

    # ================= DOMAIN CONTROLLER HEALTH ==============
    $HTML += "<div class='card'><div class='card-header'><h2>Domain Controller Health & Performance</h2></div><div class='card-body'><table class='data-table'><thead><tr><th>DC Name</th><th>Site</th><th>Roles</th><th>CPU Usage</th><th>Memory Usage</th><th>Disk Free (C:)</th><th>Uptime</th><th>Status</th></tr></thead><tbody>"
    
    foreach ($dc in $dcHealth) {
        $diskPercent = if ($dc.DiskTotalGB -gt 0) { [math]::Round(($dc.DiskFreeGB / $dc.DiskTotalGB) * 100, 1) } else { 0 }
        
        $HTML += @"
<tr>
<td><strong>$($dc.DCName)</strong></td>
<td>$($dc.Site)</td>
<td>$($dc.Roles)</td>
<td>
<div class="progress-container">
<div class="progress-bar progress-cpu" style="width: $($dc.CPU)%"></div>
</div>
<span>$($dc.CPU)%</span>
</td>
<td>
<div class="progress-container">
<div class="progress-bar progress-memory" style="width: $($dc.Memory)%"></div>
</div>
<span>$($dc.Memory)%</span>
</td>
<td>
<div class="progress-container">
<div class="progress-bar progress-disk" style="width: $diskPercent%"></div>
</div>
<span>$($dc.DiskFreeGB) GB free / $($dc.DiskTotalGB) GB total</span>
</td>
<td>$($dc.UptimeDays) days</td>
<td><span class="status $($dc.StatusClass)">$($dc.Status)</span></td>
</tr>
"@
    }
    
    $HTML += "</tbody></table></div></div>"

    # ================= AD SERVICES STATUS ====================
    $HTML += "<div class='card'><div class='card-header'><h2>Active Directory Services Status</h2></div><div class='card-body'><table class='data-table'><thead><tr><th>DC Name</th><th>NTDS</th><th>DNS</th><th>Netlogon</th><th>KDC</th><th>W32Time</th><th>Overall</th></tr></thead><tbody>"
    
    foreach ($service in $adServices) {
        $HTML += "<tr>
        <td><strong>$($service.DCName)</strong></td>
        <td><span class='status $(if($service.NTDS -eq 'Running'){'status-healthy'}else{'status-critical'})'>$($service.NTDS)</span></td>
        <td><span class='status $(if($service.DNS -eq 'Running'){'status-healthy'}else{'status-critical'})'>$($service.DNS)</span></td>
        <td><span class='status $(if($service.Netlogon -eq 'Running'){'status-healthy'}else{'status-critical'})'>$($service.Netlogon)</span></td>
        <td><span class='status $(if($service.KDC -eq 'Running'){'status-healthy'}else{'status-critical'})'>$($service.KDC)</span></td>
        <td><span class='status $(if($service.W32Time -eq 'Running'){'status-healthy'}else{'status-critical'})'>$($service.W32Time)</span></td>
        <td><span class='status $($service.OverallClass)'>$($service.Overall)</span></td>
        </tr>"
    }
    
    $HTML += "</tbody></table></div></div>"

    # ================= GPO ANALYSIS ==========================
    if ($gpoAnalysis) {
        $HTML += "<div class='card'><div class='card-header'><h2>Group Policy Analysis</h2></div><div class='card-body'>
        <div class='summary-grid'>
        <div class='summary-card total'><div class='label'>Total GPOs</div><div class='count'>$($gpoAnalysis.Statistics.Total)</div></div>
        <div class='summary-card $(if($gpoAnalysis.Statistics.Unlinked -eq 0){'healthy'}else{'warning'})'><div class='label'>Unlinked GPOs</div><div class='count'>$($gpoAnalysis.Statistics.Unlinked)</div></div>
        <div class='summary-card $(if($gpoAnalysis.Statistics.Stale -eq 0){'healthy'}else{'warning'})'><div class='label'>Stale GPOs</div><div class='count'>$($gpoAnalysis.Statistics.Stale)</div></div>
        <div class='summary-card $(if($gpoAnalysis.Statistics.Orphaned -eq 0){'healthy'}else{'critical'})'><div class='label'>Orphaned GPOs</div><div class='count'>$($gpoAnalysis.Statistics.Orphaned)</div></div>
        </div>
        
        <h3>Top 10 Largest GPOs</h3>
        <table class='data-table'>
        <thead><tr><th>GPO Name</th><th>Size (MB)</th><th>Links</th><th>Last Modified</th><th>Status</th></tr></thead>
        <tbody>"
        
        $topGPOs = $gpoAnalysis.GPOs | Sort-Object @{Expression={if($_.SizeMB -is [double]){$_.SizeMB}else{0}}; Ascending=$false} | Select-Object -First 10
        
        foreach ($gpo in $topGPOs) {
            $statusClass = if ($gpo.IsUnlinked) { "status-warning" } 
                          elseif ($gpo.IsStale) { "status-info" } 
                          elseif ($gpo.IsLarge) { "status-info" } 
                          else { "status-healthy" }
            
            $statusText = if ($gpo.IsUnlinked) { "Unlinked" } 
                         elseif ($gpo.IsStale) { "Stale" } 
                         elseif ($gpo.IsLarge) { "Large" } 
                         else { "OK" }
            
            $lastModified = if ($gpo.Modified) { $gpo.Modified.ToString('yyyy-MM-dd') } else { "Unknown" }
            
            $HTML += "<tr>
                <td>$($gpo.Name)</td>
                <td>$($gpo.SizeMB)</td>
                <td>$($gpo.LinkCount)</td>
                <td>$lastModified</td>
                <td><span class='status $statusClass'>$statusText</span></td>
            </tr>"
        }
        
        $HTML += "</tbody></table>"
        
        # Orphaned GPOs section
        if ($gpoAnalysis.OrphanedGPOs.Count -gt 0) {
            $HTML += "<h3>Orphaned GPOs (Require Cleanup)</h3>
            <table class='data-table'>
            <thead><tr><th>GPO ID</th><th>Size (MB)</th><th>Last Modified</th></tr></thead>
            <tbody>"
            
            foreach ($orphan in $gpoAnalysis.OrphanedGPOs) {
                $HTML += "<tr>
                    <td>$($orphan.ID)</td>
                    <td>$($orphan.SizeMB)</td>
                    <td>$($orphan.LastModified.ToString('yyyy-MM-dd'))</td>
                </tr>"
            }
            
            $HTML += "</tbody></table>"
        }
        
        $HTML += "</div></div>"
    }

    # ================= DNS HEALTH ============================
    if ($dnsHealth) {
        $HTML += "<div class='card'><div class='card-header'><h2>DNS Health Status</h2></div><div class='card-body'><table class='data-table'><thead><tr><th>DC Name</th><th>Total Zones</th><th>Forward Zones</th><th>Reverse Zones</th><th>AD Integrated</th><th>Stale Records</th><th>Scavenging</th><th>Status</th></tr></thead><tbody>"
        
        foreach ($dns in $dnsHealth) {
            $statusClass = if ($dns.StaleRecords -is [int] -and $dns.StaleRecords -gt 100) { "status-warning" } 
                          elseif ($dns.ScavengingEnabled -eq $true) { "status-healthy" } 
                          elseif ($dns.Status -eq "DNS Module Missing") { "status-info" }
                          else { "status-info" }
            
            $statusText = if ($dns.StaleRecords -is [int] -and $dns.StaleRecords -gt 100) { "Stale Records" } 
                         elseif ($dns.ScavengingEnabled -eq $true) { "Healthy" } 
                         elseif ($dns.Status -eq "DNS Module Missing") { "Module Missing" }
                         else { "Not Checked" }
            
            $HTML += "<tr>
                <td><strong>$($dns.DCName)</strong></td>
                <td>$($dns.Zones)</td>
                <td>$($dns.ForwardZones)</td>
                <td>$($dns.ReverseZones)</td>
                <td>$($dns.ADIntegrated)</td>
                <td>$($dns.StaleRecords)</td>
                <td>$(if($dns.ScavengingEnabled -eq $true){'Enabled'}elseif($dns.ScavengingEnabled -eq $false){'Disabled'}else{$dns.ScavengingEnabled})</td>
                <td><span class='status $statusClass'>$statusText</span></td>
            </tr>"
        }
        
        $HTML += "</tbody></table></div></div>"
    }

    # ================= AD SITES & REPLICATION ================
    if ($siteReplicationInfo) {
        $HTML += "<div class='card'><div class='card-header'><h2>AD Sites & Replication</h2></div><div class='card-body'>
        <div class='summary-grid'>
        <div class='summary-card total'><div class='label'>Total Sites</div><div class='count'>$($siteReplicationInfo.Sites.Count)</div></div>
        <div class='summary-card $(if($siteReplicationInfo.ReplicationFailures -eq 0){'healthy'}else{'critical'})'><div class='label'>Replication Issues</div><div class='count'>$($siteReplicationInfo.ReplicationFailures)</div></div>
        </div>
        
        <h3>Site List</h3>
        <table class='data-table'>
        <thead><tr><th>Site Name</th><th>Location</th><th>Description</th></tr></thead>
        <tbody>"
        
        foreach ($site in $siteReplicationInfo.Sites) {
            $HTML += "<tr>
                <td>$($site.Name)</td>
                <td>$($site.Location)</td>
                <td>$($site.Description)</td>
            </tr>"
        }
        
        $HTML += "</tbody></table>"
        
        # Replication summary
        if ($siteReplicationInfo.ReplicationSummary.Count -gt 0) {
            $HTML += "<h3>Recent Replication Summary</h3>
            <table class='data-table'>
            <thead><tr><th>Source Server</th><th>Partner Server</th><th>Last Attempt</th><th>Failures (24h)</th></tr></thead>
            <tbody>"
            
            foreach ($rep in $siteReplicationInfo.ReplicationSummary) {
                $HTML += "<tr>
                    <td>$($rep.Server)</td>
                    <td>$($rep.Partner)</td>
                    <td>$($rep.LastAttemptTime)</td>
                    <td>$($rep.FailureCount)</td>
                </tr>"
            }
            
            $HTML += "</tbody></table>"
        }
        
        $HTML += "</div></div>"
    }

    # ================= FSMO ROLES ============================
    if ($fsmoRoles) {
        $HTML += "<div class='card'><div class='card-header'><h2>FSMO Roles Status</h2></div><div class='card-body'><table class='data-table'><thead><tr><th>Role</th><th>Role Type</th><th>Current Holder</th><th>Online Status</th><th>Recommendation</th></tr></thead><tbody>"
        
        foreach ($role in $fsmoRoles) {
            $statusClass = if ($role.IsOnline) { "status-healthy" } else { "status-critical" }
            $statusText = if ($role.IsOnline) { "Online" } else { "Offline" }
            
            $recommendation = if ($role.Role -in @("PDC Emulator", "RID Master") -and -not $role.IsOnline) {
                "Critical: $($role.Role) is offline!"
            } elseif ($role.Role -eq "Infrastructure Master" -and $role.Holder -match "DC" -and $DCs.Count -gt 1) {
                "Ensure this role is NOT on a Global Catalog server"
            } else {
                "No action required"
            }
            
            $HTML += "<tr>
                <td><strong>$($role.Role)</strong></td>
                <td>$($role.RoleType)</td>
                <td>$($role.Holder)</td>
                <td><span class='status $statusClass'>$statusText</span></td>
                <td>$recommendation</td>
            </tr>"
        }
        
        $HTML += "</tbody></table></div></div>"
    }

    # ================= TRUST RELATIONSHIPS ====================
    if ($trustRelationships) {
        $HTML += "<div class='card'><div class='card-header'><h2>Trust Relationships</h2></div><div class='card-body'><table class='data-table'><thead><tr><th>Trust Name</th><th>Type</th><th>Direction</th><th>Transitive</th><th>Created</th></tr></thead><tbody>"
        
        foreach ($trust in $trustRelationships) {
            $HTML += "<tr>
                <td><strong>$($trust.Name)</strong></td>
                <td>$($trust.Type)</td>
                <td>$($trust.Direction)</td>
                <td>$($trust.Transitive)</td>
                <td>$($trust.Created.ToString('yyyy-MM-dd'))</td>
            </tr>"
        }
        
        $HTML += "</tbody></table></div></div>"
    }

    # ================= BACKUP STATUS =========================
    if ($backupStatus) {
        $HTML += "<div class='card'><div class='card-header'><h2>Backup Status</h2></div><div class='card-body'><table class='data-table'><thead><tr><th>DC Name</th><th>Last Backup</th><th>Days Since</th><th>Status</th><th>Action Required</th></tr></thead><tbody>"
        
        foreach ($backup in $backupStatus) {
            $statusClass = if ($backup.DaysSinceLastBackup -le 1) { "status-healthy" } 
                          elseif ($backup.DaysSinceLastBackup -le 3) { "status-warning" } 
                          else { "status-critical" }
            
            $statusText = if ($backup.DaysSinceLastBackup -le 1) { "Recent" } 
                         elseif ($backup.DaysSinceLastBackup -le 3) { "Warning" } 
                         else { "Critical" }
            
            $action = if ($backup.DaysSinceLastBackup -gt 7) { "Immediate backup required" }
                     elseif ($backup.DaysSinceLastBackup -gt 3) { "Schedule backup soon" }
                     else { "No action" }
            
            $lastBackupText = if ($backup.LastBackup) { $backup.LastBackup.ToString('yyyy-MM-dd HH:mm') } else { "No recent backups" }
            
            $HTML += "<tr>
                <td><strong>$($backup.DCName)</strong></td>
                <td>$lastBackupText</td>
                <td>$($backup.DaysSinceLastBackup)</td>
                <td><span class='status $statusClass'>$statusText</span></td>
                <td>$action</td>
            </tr>"
        }
        
        $HTML += "</tbody></table></div></div>"
    }

    # ================= USER ACCOUNT ANALYSIS ================
    if ($userAnalysis) {
        $HTML += "<div class='card'><div class='card-header'><h2>User Account Analysis</h2></div><div class='card-body'>
        <div class='summary-grid'>
        <div class='summary-card total'><div class='label'>Total Users</div><div class='count'>$($userAnalysis.Total)</div></div>
        <div class='summary-card healthy'><div class='label'>Enabled</div><div class='count'>$($userAnalysis.Enabled)</div></div>
        <div class='summary-card warning'><div class='label'>Disabled</div><div class='count'>$($userAnalysis.Disabled)</div></div>
        <div class='summary-card $(if($userAnalysis.Locked -eq 0){'healthy'}else{'critical'})'><div class='label'>Locked</div><div class='count'>$($userAnalysis.Locked)</div></div>
        </div>
        
        <table class='data-table'>
        <thead><tr><th>Metric</th><th>Count</th><th>Status</th><th>Recommendation</th></tr></thead>
        <tbody>
        <tr><td>Password Never Expires</td><td>$($userAnalysis.PasswordNeverExpires)</td><td><span class='status $(if($userAnalysis.PasswordNeverExpires -eq 0){'status-healthy'}else{'status-warning'})'>$(if($userAnalysis.PasswordNeverExpires -eq 0){'Compliant'}else{'Non-Compliant'})</span></td><td>$(if($userAnalysis.PasswordNeverExpires -eq 0){'No action'}else{'Review and update password policies'})</td></tr>
        <tr><td>Password Expired</td><td>$($userAnalysis.PasswordExpired)</td><td><span class='status $(if($userAnalysis.PasswordExpired -eq 0){'status-healthy'}else{'status-warning'})'>$(if($userAnalysis.PasswordExpired -eq 0){'OK'}else{'Warning'})</span></td><td>$(if($userAnalysis.PasswordExpired -eq 0){'No action'}else{'Notify users to change passwords'})</td></tr>
        <tr><td>Inactive 30+ days</td><td>$($userAnalysis.Inactive30)</td><td><span class='status $(if($userAnalysis.Inactive30 -eq 0){'status-healthy'}else{'status-info'})'>Monitor</span></td><td>$(if($userAnalysis.Inactive30 -eq 0){'No action'}else{'Monitor for extended inactivity'})</td></tr>
        <tr><td>Inactive 60+ days</td><td>$($userAnalysis.Inactive60)</td><td><span class='status $(if($userAnalysis.Inactive60 -eq 0){'status-healthy'}else{'status-warning'})'>$(if($userAnalysis.Inactive60 -eq 0){'OK'}else{'Review'})</span></td><td>$(if($userAnalysis.Inactive60 -eq 0){'No action'}else{'Investigate inactivity'})</td></tr>
        <tr><td>Inactive 90+ days</td><td>$($userAnalysis.Inactive90)</td><td><span class='status $(if($userAnalysis.Inactive90 -eq 0){'status-healthy'}else{'status-critical'})'>$(if($userAnalysis.Inactive90 -eq 0){'OK'}else{'Cleanup Required'})</span></td><td>$(if($userAnalysis.Inactive90 -eq 0){'No action'}else{'Disable or remove accounts'})</td></tr>
        </tbody>
        </table>
        </div></div>"
    }

    # ================= COMPUTER ACCOUNT ANALYSIS ============
    if ($computerAnalysis) {
        $HTML += "<div class='card'><div class='card-header'><h2>Computer Account Analysis</h2></div><div class='card-body'>
        <div class='summary-grid'>
        <div class='summary-card total'><div class='label'>Total Computers</div><div class='count'>$($computerAnalysis.Total)</div></div>
        <div class='summary-card healthy'><div class='label'>Servers</div><div class='count'>$($computerAnalysis.Servers)</div></div>
        <div class='summary-card healthy'><div class='label'>Workstations</div><div class='count'>$($computerAnalysis.Workstations)</div></div>
        <div class='summary-card warning'><div class='label'>Disabled</div><div class='count'>$($computerAnalysis.Disabled)</div></div>
        <div class='summary-card healthy'><div class='label'>Domain Controllers</div><div class='count'>$($computerAnalysis.DCs)</div></div>
        </div>
        
        <table class='data-table'>
        <thead><tr><th>Inactivity Period</th><th>Count</th><th>Status</th><th>Action</th></tr></thead>
        <tbody>
        <tr><td>30+ days inactive</td><td>$($computerAnalysis.Inactive30)</td><td><span class='status $(if($computerAnalysis.Inactive30 -eq 0){'status-healthy'}else{'status-info'})'>Monitor</span></td><td>$(if($computerAnalysis.Inactive30 -eq 0){'No action'}else{'Review'})</td></tr>
        <tr><td>60+ days inactive</td><td>$($computerAnalysis.Inactive60)</td><td><span class='status $(if($computerAnalysis.Inactive60 -eq 0){'status-healthy'}else{'status-warning'})'>$(if($computerAnalysis.Inactive60 -eq 0){'OK'}else{'Warning'})</span></td><td>$(if($computerAnalysis.Inactive60 -eq 0){'No action'}else{'Investigate'})</td></tr>
        <tr><td>90+ days inactive</td><td>$($computerAnalysis.Inactive90)</td><td><span class='status $(if($computerAnalysis.Inactive90 -eq 0){'status-healthy'}else{'status-critical'})'>$(if($computerAnalysis.Inactive90 -eq 0){'OK'}else{'Stale'})</span></td><td>$(if($computerAnalysis.Inactive90 -eq 0){'No action'}else{'Cleanup required'})</td></tr>
        </tbody>
        </table>
        </div></div>"
    }

    # ================= RECOMMENDATIONS ======================
    $HTML += "<div class='card'><div class='card-header'><h2>Comprehensive Recommendations</h2></div><div class='card-body'><h3>Priority Actions (1-3 days):</h3><ul>"
    
    if ($criticalCount -gt 0) {
        $HTML += "<li><strong>CRITICAL:</strong> Address $criticalCount critical DC issues immediately</li>"
    }
    
    if ($siteReplicationInfo -and $siteReplicationInfo.ReplicationFailures -gt 0) {
        $HTML += "<li><strong>CRITICAL:</strong> Resolve $($siteReplicationInfo.ReplicationFailures) replication failures</li>"
    }
    
    if ($gpoAnalysis -and $gpoAnalysis.Statistics.Orphaned -gt 0) {
        $HTML += "<li><strong>SECURITY:</strong> Clean up $($gpoAnalysis.Statistics.Orphaned) orphaned GPOs from SYSVOL</li>"
    }
    
    if ($backupStatus -and ($backupStatus | Where-Object { $_.DaysSinceLastBackup -gt 7 }).Count -gt 0) {
        $HTML += "<li><strong>RECOVERY:</strong> Perform immediate backups on DCs without recent backups</li>"
    }
    
    $HTML += "</ul><h3>Medium Priority Actions (1-2 weeks):</h3><ul>"
    
    if ($userAnalysis -and $userAnalysis.PasswordNeverExpires -gt 0) {
        $HTML += "<li><strong>SECURITY:</strong> Review $($userAnalysis.PasswordNeverExpires) accounts with non-expiring passwords</li>"
    }
    
    if ($userAnalysis -and $userAnalysis.Inactive90 -gt 0) {
        $HTML += "<li><strong>HOUSEKEEPING:</strong> Clean up $($userAnalysis.Inactive90) user accounts inactive for 90+ days</li>"
    }
    
    if ($computerAnalysis -and $computerAnalysis.Inactive90 -gt 0) {
        $HTML += "<li><strong>ASSET MANAGEMENT:</strong> Remove $($computerAnalysis.Inactive90) computer accounts inactive for 90+ days</li>"
    }
    
    if ($gpoAnalysis -and $gpoAnalysis.Statistics.Unlinked -gt 0) {
        $HTML += "<li><strong>OPTIMIZATION:</strong> Review $($gpoAnalysis.Statistics.Unlinked) unlinked GPOs for removal</li>"
    }
    
    $HTML += "</ul><h3>Long-term Improvements (1 month):</h3><ul>"
    
    if (-not $recycleBinStatus.Enabled) {
        $HTML += "<li><strong>RECOVERY:</strong> Enable Active Directory Recycle Bin</li>"
    }
    
    if ($passwordPolicy -and $passwordPolicy.MinLength -lt 14) {
        $HTML += "<li><strong>SECURITY:</strong> Increase minimum password length to 14 characters</li>"
    }
    
    $HTML += @"
<li><strong>AUTOMATION:</strong> Implement automated AD health monitoring</li>
<li><strong>DOCUMENTATION:</strong> Update AD recovery and maintenance procedures</li>
<li><strong>TRAINING:</strong> Conduct AD administration best practices training</li>
</ul>
<h3>Next Steps:</h3>
<ol>
<li>Review this report with IT leadership within 24 hours</li>
<li>Create action plan with priorities and owners</li>
<li>Schedule daily follow-ups on critical items</li>
<li>Schedule comprehensive reassessment in 30 days</li>
<li>Consider third-party AD management tools for advanced monitoring</li>
</ol>
</div></div>
"@

    # Add footer
    $HTML += Get-HTMLFooter
    
    # Save HTML report
    $HTML | Out-File $ReportFile -Encoding UTF8
    
    Write-Host "`n=========================================" -ForegroundColor Green
    Write-Host "COMPREHENSIVE REPORT GENERATED SUCCESSFULLY!" -ForegroundColor Green
    Write-Host "=========================================" -ForegroundColor Green
    Write-Host "Company: $($CompanyInfo.Name)" -ForegroundColor Yellow
    Write-Host "Author: $($CompanyInfo.Author)" -ForegroundColor Yellow
    Write-Host "Practice: $($CompanyInfo.Practice)" -ForegroundColor Yellow
    Write-Host "Report saved to: $ReportFile" -ForegroundColor Yellow
    
    Write-Host "`nExecutive Summary:" -ForegroundColor Cyan
    Write-Host "  Domain Controllers: $($DCs.Count)" -ForegroundColor White
    Write-Host "  Critical Issues: $criticalCount" -ForegroundColor $(if ($criticalCount -eq 0) { "Green" } else { "Red" })
    Write-Host "  Warnings: $warningCount" -ForegroundColor $(if ($warningCount -eq 0) { "Green" } else { "Yellow" })
    Write-Host "  Overall Health Score: $overallScore%" -ForegroundColor $(if ($overallScore -ge 90) { "Green" } elseif ($overallScore -ge 70) { "Yellow" } else { "Red" })
    Write-Host "  Total Users: $(if ($userAnalysis) {$userAnalysis.Total} else {'N/A'})" -ForegroundColor White
    Write-Host "  Total Computers: $(if ($computerAnalysis) {$computerAnalysis.Total} else {'N/A'})" -ForegroundColor White
    Write-Host "  Total GPOs: $(if ($gpoAnalysis) {$gpoAnalysis.Statistics.Total} else {'N/A'})" -ForegroundColor White
    Write-Host "  AD Sites: $(if ($siteReplicationInfo) {$siteReplicationInfo.Sites.Count} else {'N/A'})" -ForegroundColor White
    
    # Open report in browser if requested
    if ($OpenReport -and (Test-Path $ReportFile)) {
        Write-Host "`nOpening report in default browser..." -ForegroundColor Cyan
        Start-Process $ReportFile
    }
    
}
catch {
    Write-Host "`nERROR: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Script execution failed." -ForegroundColor Red
    exit 1
}

Write-Host "`nScript completed successfully!" -ForegroundColor Green
Write-Host "Report prepared by: $($CompanyInfo.Author)" -ForegroundColor Cyan
Write-Host "Support: $($CompanyInfo.SupportEmail)" -ForegroundColor Cyan
Write-Host "$($CompanyInfo.Name) - $($CompanyInfo.Practice)" -ForegroundColor Cyan 

Save the script at any location in the domain controller. Open PowerShell with administrative privilege and go to directory where the script is saved.

Run the script

.\V4.ps1

below details will appear in the powershell console.

Report is generated successfully and save in OS drive with name ADHealthReports. Open the report and review.

Download the sample report.

In this article i have explained component services and roles which need to be run and check in active directory. how to run and generate a brief report of the domain controllers to view and take action on time.

Did you enjoy the article? you also may want to understand more about how to check and block NTLM authentication in active directory which will force users to authenticate via Kerberos.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top