Powershell Scripts

Daily Active Directory Monitoring Made Easy with PowerShell

If you’re a Domain Admin responsible for maintaining the health of your Active Directory environment, running dcdiag and repadmin commands daily can be time-consuming. To simplify this routine task, I’ve created a PowerShell script that automates these checks and generates a clear, HTML-based report. This makes daily AD monitoring faster, easier, and far more efficient — a real time-saver for any IT admin

Pre-requisites

  • GMSA Account to run the Scheduled Tasks
  • Domain Admin rights for the GMSA Account
  • Valid Email address and SMTP Address

Usage

Copy the Powershell code
Edit the email settings in the highlighted part
Run the Powershell

########################################################################################################
#       Author: Karanth
#       Modified : 19/6/2025
#       Usage : To gather Domain Controllers and their Status
########################################################################################################
###########################Define Variables#####################################################
<#
    .SYNOPSIS
    Get-ADHealth.ps1 
#>

# Defining the Variables 
$allTestedDomainControllers = [System.Collections.Generic.List[Object]]::new()
$allDomainControllers = [System.Collections.Generic.List[Object]]::new()
$now = Get-Date
$date = $now.ToShortDateString()
$reportTime = $now
$reportFileNameTime = $now.ToString("yyyyMMdd_HHmmss")
$reportemailsubject = "Active Directory Health Check for $allDomains"
$smtpsettings = @{
    To         = 'XXYY@xx.com '
    From       = 'XXYY@xx.com'
    Subject    = "$reportemailsubject - $date"
    SmtpServer = "XXYY@xx.com"
    Port       = "25"   
}

# Funtion to gather Domain Info
Function Get-AllDomains() {
    Write-Verbose "Running function Get-AllDomains"
    $allDomains = (Get-ADForest).Domains
    return $allDomains
}

# Funtion to gather Domain Controllers name 
Function Get-AllDomainControllers ($ComputerName) {
    Write-Verbose "Running function Get-AllDomainControllers"
    $allDomainControllers = Get-ADDomainController -Filter * -Server $ComputerName | Sort-Object HostName
    return $allDomainControllers
}

# Funtion to gather Domain Controllers Uptime 
Function Get-DomainControllerUptimeDays {
    param($ComputerName)
    Write-Verbose "Running function Get-DomainControllerUptimeDays"
    if (Test-Connection $ComputerName -Count 1 -Quiet) {
        try {
            $W32OS = Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop
            if ($W32OS) {
                $uptimeDays = ($W32OS | Select-Object -ExpandProperty LastBootUpTime)
                $lastBoot = $W32OS.ConvertToDateTime($uptimeDays)
                $uptime = (New-TimeSpan -Start $lastBoot -End (Get-Date)).Days
            } else {
                $uptime = "Data Error"
            }
        } catch {
            $uptime = 'WMI Failure'
        }
    }
    else {
        $uptime = 'Fail'
    }
    return $uptime
}

# Funtion to gather NETLOGON and NTDS Services 
Function Get-DomainControllerServices($ComputerName) {
    Write-Verbose "Running function DomainControllerServices"
    $thisDomainControllerServicesTestResult = [PSCustomObject]@{
        NTDSService     = $null
        NETLOGONService = $null
    }
    if ((Test-Connection $ComputerName -Count 1 -quiet) -eq $True) {
        if ((Get-Service -ComputerName $ComputerName -Name NTDS -ErrorAction SilentlyContinue).Status -eq 'Running') {
            $thisDomainControllerServicesTestResult.NTDSService = 'Success'
        }
        else {
            $thisDomainControllerServicesTestResult.NTDSService = 'Fail'
        }
        if ((Get-Service -ComputerName $ComputerName -Name netlogon -ErrorAction SilentlyContinue).Status -eq 'Running') {
            $thisDomainControllerServicesTestResult.NETLOGONService = 'Success'
        }
        else {
            $thisDomainControllerServicesTestResult.NETLOGONService = 'Fail'
        }
    }
    else {
        $thisDomainControllerServicesTestResult.NTDSService = 'Fail'
        $thisDomainControllerServicesTestResult.NETLOGONService = 'Fail'
    }
    return $thisDomainControllerServicesTestResult
}

# Funtion to gather DCDIAG results 
Function Get-DomainControllerDCDiagTestResults($ComputerName) {
    Write-Verbose "Running function Get-DomainControllerDCDiagTestResults"
    $DCDiagTestResults = [PSCustomObject]@{
        ServerName         = $ComputerName
        Connectivity       = $null
        Advertising        = $null
        FrsEvent           = $null
        DFSREvent          = $null
        SysVolCheck        = $null
        KccEvent           = $null
        KnowsOfRoleHolders = $null
        NetLogons          = $null
        ObjectsReplicated  = $null
        Replications       = $null
        RidManager         = $null
        Services           = $null
        
    }
    if ((Test-Connection $ComputerName -Count 1 -quiet) -eq $True) {
        $params = @(
            "/s:$ComputerName",
            "/test:Connectivity",
            "/test:Advertising",
            "/test:FrsEvent",
            "/test:DFSREvent",
            "/test:SysVolCheck",
            "/test:KccEvent",
            "/test:KnowsOfRoleHolders",
            "/test:NetLogons",
            "/test:ObjectsReplicated",
            "/test:Replications",
            "/test:RidManager",
            "/test:Services"
        )
        $DCDiagTest = (Dcdiag.exe @params) -split ('[\r\n]')
        $TestName = $null
        $TestStatus = $null
        $DCDiagTest | ForEach-Object {
            switch -Regex ($_) {
                "Starting test:" {
                    $TestName = ($_ -replace ".*Starting test:").Trim()
                }
                "passed test|failed test" {
                    $TestStatus = if ($_ -match "passed test") { "Passed" } else { "Failed" }
                }
            }
            if ($TestName -and $TestStatus) {
                $DCDiagTestResults.$TestName = $TestStatus
                $TestName = $null
                $TestStatus = $null
            }
        }
    }
    else {
        foreach ($property in $DCDiagTestResults.PSObject.Properties.Name) {
            if ($property -ne "ServerName") {
                $DCDiagTestResults.$property = "Failed"
            }
        }
    }
    return $DCDiagTestResults
}

# Funtion to gather Free space in C drive 
Function Get-DomainControllerOSDriveFreeSpaceGB {
    Param($ComputerName)
    Write-Verbose "Running function Get-DomainControllerOSDriveFreeSpaceGB"
    if (Test-Connection $ComputerName -Count 1 -Quiet) {
        try {
            $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop
            $osDriveLetter = $os.SystemDrive
            $osDrive = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $ComputerName `
                -Filter "DeviceID='$osDriveLetter'" -ErrorAction Stop
            $freeSpaceGB = [math]::Round($osDrive.FreeSpace / 1GB, 2)
        }
        catch {
            $freeSpaceGB = 'WMI Failure'
        }
    }
    else {
        $freeSpaceGB = 'Fail'
    }
    return $freeSpaceGB
}

# Funtion to gather Domain Controllers CPU Usage
Function Get-DomainControllerCPUUsage {
    Param($ComputerName)
    Write-Verbose "Running function Get-DomainControllerCPUUsage"
    if (Test-Connection $ComputerName -Count 1 -Quiet) {
        try {
            $avgProc = Get-WmiObject -Class win32_processor -ComputerName $ComputerName -ErrorAction Stop |
                       Measure-Object -Property LoadPercentage -Average |
                       Select-Object -ExpandProperty Average
            $cpuLoad = [math]::Round($avgProc, 2)
        }
        catch {
            $cpuLoad = 'WMI Failure'
        }
    }
    else {
        $cpuLoad = 'Fail'
    }
    return $cpuLoad
}

# Funtion to gather Memory Usage 
Function Get-DomainControllerMemoryUsage {
    Param($ComputerName)
    Write-Verbose "Running function Get-DomainControllerMemoryUsage"
    if (Test-Connection $ComputerName -Count 1 -Quiet) {
        try {
            $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop
            $total = $os.TotalVisibleMemorySize
            $free  = $os.FreePhysicalMemory
            $used  = $total - $free
            $memPercentUsed = [math]::Round(($used / $total) * 100, 2)
        }
        catch {
            $memPercentUsed = 'WMI Failure'
        }
    }
    else {
        $memPercentUsed = 'Fail'
    }
    return $memPercentUsed
}

# Funtion to put all the gathered information into HTML
Function New-ServerHealthHTMLTableCell() {
    param( $lineitem )
    $htmltablecell = $null
    switch ($($reportline."$lineitem")) {
        "Success" { $htmltablecell = "<td class=""pass"">$($reportline."$lineitem")</td>" }
        "Passed" { $htmltablecell = "<td class=""pass"">$($reportline."$lineitem")</td>" }
        "Pass" { $htmltablecell = "<td class=""pass"">$($reportline."$lineitem")</td>" }
        "Warn" { $htmltablecell = "<td class=""warn"">$($reportline."$lineitem")</td>" }
        "Fail" { $htmltablecell = "<td class=""fail"">$($reportline."$lineitem")</td>" }
        "Failed" { $htmltablecell = "<td class=""fail"">$($reportline."$lineitem")</td>" }
        "Could not test server uptime." { $htmltablecell = "<td class=""fail"">$($reportline."$lineitem")</td>" }
        default { $htmltablecell = "<td>$($reportline."$lineitem")</td>" }
    }
    return $htmltablecell
}
if (!($DomainName)) {
    Write-Host "No domain specified, using all domains in forest" -ForegroundColor Yellow
    $allDomains = Get-AllDomains
    $reportFileName = 'forest_health_report_' + (Get-ADForest).name + '_' + $reportFileNameTime + '.html'
}
else {
    Write-Host "Domain name specified on cmdline" -ForegroundColor Cyan
    $allDomains = $DomainName
    $reportFileName = 'dc_health_report_' + $DomainName + '_' + $reportFileNameTime + '.html'
}
foreach ($domain in $allDomains) {
    Write-Host "Testing domain" $domain -ForegroundColor Green
    $allDomainControllers = Get-AllDomainControllers $domain
    $allDomainControllers = @($allDomainControllers)
    $totalDCs = $allDomainControllers.Count
    $currentDCNumber = 0
    foreach ($domainController in $allDomainControllers) {
        $currentDCNumber++
        $stopWatch = [system.diagnostics.stopwatch]::StartNew()
        Write-Host "Testing domain controller ($currentDCNumber of $totalDCs) $($domainController.HostName)" -ForegroundColor Cyan
        $DCDiagTestResults = Get-DomainControllerDCDiagTestResults $domainController.HostName
        $svcResults = Get-DomainControllerServices $domainController.HostName
        $thisDomainController = [PSCustomObject]@{
            Server                            = ($domainController.HostName).ToLower()
            Site                              = $domainController.Site
            "IPv4 Address"                    = $domainController.IPv4Address
            "OS Free Space (GB)"              = Get-DomainControllerOSDriveFreeSpaceGB $domainController.HostName
            "CPU Usage (%)"                   = Get-DomainControllerCPUUsage $domainController.HostName
            "Memory Usage (%)"                = Get-DomainControllerMemoryUsage $domainController.HostName
            "Uptime (days)"                   = Get-DomainControllerUptimeDays $domainController.HostName
            "NTDS Service"                    = $svcResults.NTDSService
            "NetLogon Service"                = $svcResults.NETLOGONService
            "DCDIAG: Connectivity"            = $DCDiagTestResults.Connectivity
            "DCDIAG: Advertising"             = $DCDiagTestResults.Advertising
            "DCDIAG: FrsEvent"                = $DCDiagTestResults.FrsEvent
            "DCDIAG: DFSREvent"               = $DCDiagTestResults.DFSREvent
            "DCDIAG: SysVolCheck"             = $DCDiagTestResults.SysVolCheck
            "DCDIAG: KccEvent"                = $DCDiagTestResults.KccEvent
            "DCDIAG: FSMO "                   = $DCDiagTestResults.KnowsOfRoleHolders
            "DCDIAG: NetLogons"               = $DCDiagTestResults.NetLogons
            "DCDIAG: ObjectsReplicated"       = $DCDiagTestResults.ObjectsReplicated
            "DCDIAG: Replications"            = $DCDiagTestResults.Replications
            "DCDIAG: RidManager"              = $DCDiagTestResults.RidManager
            "DCDIAG: Services"                = $DCDiagTestResults.Services
        }
        $allTestedDomainControllers.Add($thisDomainController)
    }
}


# Modified HTML and table creation: Outlook-friendly
$htmlhead = @"
<html>
<body style='font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10pt;'>
<h1 style='font-size:20px;text-align:left;'>Domain Controller Health Check Report</h1>
<h3 style='font-size:14px;text-align:left;'>Generated: $reportTime</h3>
"@

$htmltableheader = @"
<h3 style='font-size:14px;color:#333;margin-bottom:3px;'>Domain Controller Health Summary</h3>
<h3 style='font-size:12px;margin-top:0;font-weight:normal;'>Forest: $((Get-ADForest).Name)</h3>
<table border='1' cellpadding='0' cellspacing='0' style='width:100%;border-collapse:collapse;font-size:10pt;'>
<tr style='background-color:#f2f2f2;'>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>Server</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>Site</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>IPv4 Address</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>OS Free Space (GB)</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>CPU Usage (%)</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>Memory Usage (%)</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>Uptime (days)</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>NTDS Service</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>NetLogon Service</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: Connectivity</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: Advertising</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: FrsEvent</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: DFSREvent</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: SysVolCheck</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: KccEvent</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: FSMO</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: NetLogons</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: ObjectsReplicated</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: Replications</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: RidManager</th>
  <th style='border:1px solid #ccc; padding:6px; color:#000; text-align:center;'>DCDIAG: Services</th>
</tr>
"@

Function New-ServerHealthHTMLTableCell() {
    param( $lineitem )
    $cellValue = $reportline."$lineitem"
    switch ($cellValue) {
        "Success" { return "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$cellValue</td>" }
        "Passed"  { return "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$cellValue</td>" }
        "Pass"    { return "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$cellValue</td>" }
        "Warn"    { return "<td style='border:1px solid #ccc;padding:6px; background-color:#FFD966; color:#000;text-align:center;'>$cellValue</td>" }
        "Fail"    { return "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;text-align:center;'>$cellValue</td>" }
        "Failed"  { return "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;text-align:center;'>$cellValue</td>" }
        "Could not test server uptime." { return "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;'>$cellValue</td>" }
        default   { return "<td style='border:1px solid #ccc;padding:6px;'>$cellValue</td>" }
    }
}

$serverhealthhtmltable = ""
$serverhealthhtmltable += $htmltableheader
foreach ($reportline in $allTestedDomainControllers) {
    $htmltablerow = "<tr>"
    $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$($reportline.Server)</td>"
    $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$($reportline.Site)</td>"
    $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$($reportline.'IPv4 Address')</td>"

    # OS Free Space
    $osFree = $reportline.'OS Free Space (GB)'
    if ($osFree -is [double] -or $osFree -is [int]) {
        if ($osFree -lt 20) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;text-align:center;'>$osFree</td>"   # Red (<20GB)
        } else {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$osFree</td>"   # Green (>=20GB)
        }
    } else {
        $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$osFree</td>"
    }
    # color coding CPU Usage
    $cpu = $reportline.'CPU Usage (%)'
    if ($cpu -is [double] -or $cpu -is [int]) {
        if ($cpu -le 75) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$cpu</td>"
        } elseif ($cpu -le 90) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#FFD966; color:#000;text-align:center;'>$cpu</td>"
        } else {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;text-align:center;'>$cpu</td>"
        }
    } else {
        $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$cpu</td>"
    }
    #color coding Memory Usage
    $mem = $reportline.'Memory Usage (%)'
    if ($mem -is [double] -or $mem -is [int]) {
        if ($mem -le 75) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$mem</td>"
        } elseif ($mem -le 90) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#FFD966; color:#000;text-align:center;'>$mem</td>"
        } else {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;text-align:center;'>$mem</td>"
        }
    } else {
        $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$mem</td>"
    }
    # color coding Uptime
    $uptimeDays = $reportline.'Uptime (days)'
    if ($uptimeDays -eq "CIM Failure" -or $uptimeDays -eq "Fail") {
        $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;'>$uptimeDays</td>"
    } elseif ($uptimeDays -is [int] -or $uptimeDays -is [double]) {
        if ($uptimeDays -le 31) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#6BBF59; color:#000;text-align:center;'>$uptimeDays</td>"
        } elseif ($uptimeDays -le 45) {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#FFD966; color:#000;text-align:center;'>$uptimeDays</td>"
        } else {
            $htmltablerow += "<td style='border:1px solid #ccc;padding:6px; background-color:#D9534F; color:#fff;text-align:center;'>$uptimeDays</td>"
        }
    } else {
        $htmltablerow += "<td style='border:1px solid #ccc;padding:6px;text-align:center;'>$uptimeDays</td>"
    }
    # Filling the Remaining items in HTML 
    $htmltablerow += (New-ServerHealthHTMLTableCell "NTDS Service")
    $htmltablerow += (New-ServerHealthHTMLTableCell "NetLogon Service")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: Connectivity")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: Advertising")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: FrsEvent")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: DFSREvent")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: SysVolCheck")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: KccEvent")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: FSMO ")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: NetLogons")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: ObjectsReplicated")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: Replications")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: RidManager")
    $htmltablerow += (New-ServerHealthHTMLTableCell "DCDIAG: Services")
    $htmltablerow += "</tr>"
    $serverhealthhtmltable += $htmltablerow
}
$serverhealthhtmltable += "</table>"

# Example explanation table
$explanationTable = @"
<h3 style='font-family: Arial, sans-serif; color: #0056b3;'>Column Reference</h3>
<table border='1' cellpadding='0' cellspacing='0' style='border-collapse:collapse; width:45%; font-family:Arial, sans-serif; font-size:12px;'>
  <thead>
    <tr style='background-color:#f2f2f2;'>
      <th style='border:1px solid #ccc; padding:6px; text-align:left;'>Field</th>
      <th style='border:1px solid #ccc; padding:6px; text-align:left;'>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Site</td><td style='border:1px solid #ccc; padding:6px;'>The AD site where the domain controller resides.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>IPv4 Address</td><td style='border:1px solid #ccc; padding:6px;'>The IP address of the domain controller.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>OS Free Space (GB)</td><td style='border:1px solid #ccc; padding:6px;'>Available disk space on the system drive (in GB).</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>CPU Usage (%)</td><td style='border:1px solid #ccc; padding:6px;'>Current CPU utilization of the server.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Memory Usage (%)</td><td style='border:1px solid #ccc; padding:6px;'>Current RAM utilization.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Uptime (days)</td><td style='border:1px solid #ccc; padding:6px;'>How many days the DC has been running since the last reboot.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>NTDS Service</td><td style='border:1px solid #ccc; padding:6px;'>Whether the AD DS (NTDS) service is running.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>NetLogon Service</td><td style='border:1px solid #ccc; padding:6px;'>Whether the NetLogon service is running.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Connectivity</td><td style='border:1px solid #ccc; padding:6px;'>Checks basic connectivity between DCs.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Advertising</td><td style='border:1px solid #ccc; padding:6px;'>Ensures the DC is advertising itself correctly.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>FrsEvent</td><td style='border:1px solid #ccc; padding:6px;'>Checks legacy File Replication Service errors.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>DFSREvent</td><td style='border:1px solid #ccc; padding:6px;'>Checks DFS Replication health for SYSVOL.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>SysVolCheck</td><td style='border:1px solid #ccc; padding:6px;'>Verifies SYSVOL availability.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>KccEvent</td><td style='border:1px solid #ccc; padding:6px;'>Checks KCC (replication topology).</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>FSMO</td><td style='border:1px solid #ccc; padding:6px;'>Confirms the DC knows FSMO role holders.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>NetLogons</td><td style='border:1px solid #ccc; padding:6px;'>Validates secure channel (Netlogon).</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>ObjectsReplicated</td><td style='border:1px solid #ccc; padding:6px;'>Confirms that AD objects replicate properly.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Replications</td><td style='border:1px solid #ccc; padding:6px;'>Checks replication health between DCs.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>RidManager</td><td style='border:1px solid #ccc; padding:6px;'>Verifies RID Master functionality.</td></tr>
    <tr><td style='border:1px solid #ccc; padding:6px;'>Services</td><td style='border:1px solid #ccc; padding:6px;'>Checks critical services (DNS, Kerberos, etc.).</td></tr>
  </tbody>
</table>
"@
$serverhealthhtmltable += $explanationTable

$htmltail = "<p style='font-size:11px; color:#555;'>* DNS test is performed using <code>Resolve-DnsName</code>. This cmdlet is only available from Windows Server 2012 onwards.</p>
</body>
</html>"

$htmlreport = $htmlhead + $serverhealthhtmltable

$htmlreport | Out-File $reportFileName -Encoding UTF8
Send-MailMessage @smtpsettings -Body $htmlreport -BodyAsHtml -Attachments $reportFileName -Encoding ([System.Text.Encoding]::UTF8) -ErrorAction Stop
Write-Host "Email sent successfully." -ForegroundColor Green

Comments Off on Daily Active Directory Monitoring Made Easy with PowerShell