viernes, 26 de junio de 2026

WINDOWS. AD Health Dashboard

 



Propósito:

Verificar el estado de mi AD. 

Pasos:

Código


#############################################################################

# Script: AD_Health_Fusion_Final.ps1

# Descripcion:

# Script avanzado para monitorizacion de Active Directory:

# - Ping DC

# - Servicios criticos (Netlogon, NTDS, DNS)

# - Replicacion REAL (repadmin)

# - Tests DCDIAG clave (Replications, Netlogons, Advertising, FSMOCheck)

# - Dashboard HTML

# - Envio email SOLO si hay errores

#

# Disenado para entornos grandes minimizando impacto

#############################################################################

 

# ==========================

# VARIABLES DEL USUARIO

# ==========================

$SMTPServer = "smtp01.midominio.com"

$SMTPPort   = 25

$From       = "miserver@midominio.com"

$To         = "test@test.com"

 

$Fecha = Get-Date -Format "yyyy-MM-dd - HH:mm"

$Errores = @()

 

$Subject = "$Fecha - AD Health Monitor"

 

$ReportPath = "C:\Temp\AD_Health.html"

 

# ==========================

# HTML INICIO

# ==========================

$HTML = @"

<html>

<head>

<style>

body { font-family: Arial; }

table { border-collapse: collapse; width: 100%; }

th { background-color: #2F5496; color: white; }

td { border: 1px solid #ccc; padding: 5px; text-align: center; }

.ok { background-color: #C6EFCE; }

.error { background-color: #FFC7CE; }

</style>

</head>

<body>

<h2>AD Health Dashboard v2</h2>

<table>

<tr>

<th>DC</th>

<th>Ping</th>

<th>Netlogon</th>

<th>NTDS</th>

<th>DNS</th>

<th>Repadmin</th>

<th>DCDIAG</th>

</tr>

"@

 

# ==========================

# OBTENER DCs

# ==========================

$DCs = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName

 

# ==========================

# FUNCION SERVICIOS

# ==========================

function Get-ServiceState {

    param($dc,$name)

 

    try {

        $svc = Get-Service -ComputerName $dc -Name $name -ErrorAction Stop

        if ($svc.Status -eq "Running") {

            return @("OK","ok")

        } else {

            $Errores += "$dc servicio $name STOP"

            return @("STOP","error")

        }

    } catch {

        $Errores += "$dc servicio $name ERROR"

        return @("ERROR","error")

    }

}

 

# ==========================

# ANALISIS POR DC

# ==========================

foreach ($dc in $DCs) {

 

    # PING

    if (Test-Connection $dc -Count 1 -Quiet) {

        $PingStatus = "OK"; $PingClass = "ok"

    } else {

        $PingStatus = "FAIL"; $PingClass = "error"

        $Errores += "$dc Ping FAIL"

    }

 

    # SERVICIOS

    $Netlogon = Get-ServiceState $dc "Netlogon"

    $NTDS     = Get-ServiceState $dc "NTDS"

    $DNS      = Get-ServiceState $dc "DNS"

 

    # ==========================

    # REPLICACION (REPADMIN REAL)

    # ==========================

    $rep = repadmin /showrepl $dc | Out-String

 

    if ($rep -match "error|fail") {

        $RepStatus = "ERROR"

        $RepClass = "error"

        $Errores += "$dc Repadmin error"

    } else {

        $RepStatus = "OK"

        $RepClass = "ok"

    }

 

    # ==========================

    # DCDIAG CLAVE

    # ==========================

    $dcdiag = dcdiag /s:$dc /test:Replications /test:Netlogons /test:Advertising /test:FSMOCheck | Out-String

 

    if ($dcdiag -match "failed|error") {

        $DCDIAGStatus = "ERROR"

        $DCDIAGClass = "error"

        $Errores += "$dc DCDIAG error"

    } else {

        $DCDIAGStatus = "OK"

        $DCDIAGClass = "ok"

    }

 

    # ==========================

    # HTML

    # ==========================

    $HTML += "<tr>"

    $HTML += "<td>$dc</td>"

    $HTML += "<td class='$PingClass'>$PingStatus</td>"

    $HTML += "<td class='$($Netlogon[1])'>$($Netlogon[0])</td>"

    $HTML += "<td class='$($NTDS[1])'>$($NTDS[0])</td>"

    $HTML += "<td class='$($DNS[1])'>$($DNS[0])</td>"

    $HTML += "<td class='$RepClass'>$RepStatus</td>"

    $HTML += "<td class='$DCDIAGClass'>$DCDIAGStatus</td>"

    $HTML += "</tr>"

}

 

$HTML += "</table>"

 

# ==========================

# RESUMEN ERRORES

# ==========================

#lo envia solo si sale errores

if ($Errores.Count -gt 0) {

 

    $HTML += "<h3>Errores detectados ($($Errores.Count))</h3><ul>"

 

    foreach ($e in $Errores) {

        $HTML += "<li>$e</li>"

    }

 

    $HTML += "</ul>"

}

 

$HTML += "</body></html>"

 

$HTML | Out-File $ReportPath -Encoding UTF8

 

# ==========================

# EMAIL SOLO SI ERRORES

# ==========================

if ($Errores.Count -gt 0) {

 

    $Subject = "$Fecha - AD Health Alert - $($Errores.Count) elementos"

 

    Send-MailMessage -SmtpServer $SMTPServer `

        -Port $SMTPPort `

        -From $From `

        -To $To `

        -Subject $Subject `

        -Body $HTML `

        -BodyAsHtml

}



[] Tarea programada

Interfaz de usuario gráfica, Texto, Aplicación, Correo electrónico

Descripción generada automáticamente

No funciona con el usuario system, hay que poner con uno con privilegios en el AD

Lo ideal sería ejecutarlo cada hora y que solo envía si hay errores. Yo lo hago una vez al día porque en principio no tengo problemas de sincronización y durante el día si lo tuviese seguro que tendría algún otro indicador que me alertaría.

 

Interfaz de usuario gráfica, Texto, Aplicación

Descripción generada automáticamente

 

Interfaz de usuario gráfica, Texto, Aplicación

Descripción generada automáticamente

 

Resultado

 Interfaz de usuario gráfica

Descripción generada automáticamente

 ===============================================================

Mejoras v2

He conseguido que se ejecute con el usuario SYSTEM con los siguiente controles, no son exactamente los mismos pero los suficiente para tener un diagnóstico claro.


y con este nuevo código:



#############################################################################
# AD_Health
#############################################################################

$SMTPServer = "smtP.MIDOM.com"
$SMTPPort   = 25
$From       = "YO@MIDOM.com"
$To         = "GON@MIDOM.com"

$Fecha = Get-Date -Format "yyyy-MM-dd - HH:mm"

$script:Errores = @()

$ReportPath = "C:\Temp\AD_Health.html"

#############################################################################
# FUNCION DCDIAG
#############################################################################

function Test-DCDiagCheck {

    param(
        [string]$DC,
        [string]$TestName
    )

    try {

        $Output = dcdiag /s:$DC /test:$TestName 2>&1 | Out-String

        if ($Output -match "(?i)failed test|fatal|error")
        {
            $script:Errores += "$DC $TestName ERROR"
            return @("ERROR","error")
        }

        return @("OK","ok")
    }
    catch
    {
        $script:Errores += "$DC $TestName EXCEPTION"
        return @("ERROR","error")
    }
}

#############################################################################
# HTML
#############################################################################

$HTML = @"
<html>
<head>
<style>
body { font-family: Arial; }
table { border-collapse: collapse; width:100%; }
th { background:#2F5496; color:white; }
td { border:1px solid #ccc; padding:5px; text-align:center; }
.ok { background:#C6EFCE; }
.error { background:#FFC7CE; }
</style>
</head>
<body>

<h2>AD Health Dashboard</h2>

<table>

<tr>
<th>DC</th>
<th>Ping</th>
<th>Repadmin</th>
<th>Replications</th>
<th>NetLogons</th>
<th>Advertising</th>
<th>FSMO</th>
<th>SYSVOL</th>
</tr>
"@

#############################################################################
# DCS
#############################################################################

$DCs = Get-ADDomainController -Filter * |
       Sort-Object HostName |
       Select-Object -ExpandProperty HostName

#############################################################################
# ANALISIS
#############################################################################

foreach ($DC in $DCs)
{
    #########################################################################
    # PING
    #########################################################################

    if (Test-Connection -ComputerName $DC -Count 1 -Quiet)
    {
        $PingStatus = "OK"
        $PingClass  = "ok"
    }
    else
    {
        $PingStatus = "FAIL"
        $PingClass  = "error"

        $script:Errores += "$DC Ping FAIL"
    }

    #########################################################################
    # REPADMIN
    #########################################################################

    try
    {
        $RepOutput = repadmin /showrepl $DC 2>&1 | Out-String

        if (
            $RepOutput -match "(?i)fails" -or
            $RepOutput -match "(?i)error" -or
            $RepOutput -match "(?i)8453" -or
            $RepOutput -match "(?i)1722"
        )
        {
            $RepStatus = "ERROR"
            $RepClass = "error"

            $script:Errores += "$DC Repadmin ERROR"
        }
        else
        {
            $RepStatus = "OK"
            $RepClass = "ok"
        }
    }
    catch
    {
        $RepStatus = "ERROR"
        $RepClass = "error"

        $script:Errores += "$DC Repadmin EXCEPTION"
    }

    #########################################################################
    # TESTS DCDIAG
    #########################################################################

    $Replications = Test-DCDiagCheck $DC "Replications"
    $NetLogons    = Test-DCDiagCheck $DC "NetLogons"
    $Advertising  = Test-DCDiagCheck $DC "Advertising"
    $FSMO         = Test-DCDiagCheck $DC "FSMOCheck"
    $SYSVOL       = Test-DCDiagCheck $DC "SysVolCheck"

    #########################################################################
    # HTML
    #########################################################################

    $HTML += "<tr>"
    $HTML += "<td>$DC</td>"
    $HTML += "<td class='$PingClass'>$PingStatus</td>"
    $HTML += "<td class='$RepClass'>$RepStatus</td>"
    $HTML += "<td class='$($Replications[1])'>$($Replications[0])</td>"
    $HTML += "<td class='$($NetLogons[1])'>$($NetLogons[0])</td>"
    $HTML += "<td class='$($Advertising[1])'>$($Advertising[0])</td>"
    $HTML += "<td class='$($FSMO[1])'>$($FSMO[0])</td>"
    $HTML += "<td class='$($SYSVOL[1])'>$($SYSVOL[0])</td>"
    $HTML += "</tr>"
}

#############################################################################
# RESUMEN
#############################################################################

$HTML += "</table>"

if ($script:Errores.Count -gt 0)
{
    $HTML += "<h3>Errores detectados ($($script:Errores.Count))</h3>"
    $HTML += "<ul>"

    foreach ($ErrorItem in $script:Errores)
    {
        $HTML += "<li>$ErrorItem</li>"
    }

    $HTML += "</ul>"
}
else
{
    $HTML += "<h3 style='color:green'>Sin errores detectados</h3>"
}

$HTML += "</body></html>"

#############################################################################
# GUARDAR
#############################################################################

New-Item -ItemType Directory -Path "C:\Temp" -Force | Out-Null

$HTML | Out-File $ReportPath -Encoding UTF8

#############################################################################
# MAIL
#############################################################################

$Subject = "$Fecha - AD Health Report v9 - $($script:Errores.Count) elementos"

Send-MailMessage `
    -SmtpServer $SMTPServer `
    -Port $SMTPPort `
    -From $From `
    -To $To `
    -Subject $Subject `
    -Body $HTML `
    -BodyAsHtml

Mola!!


by GoN | Published: Jun 2026 | Last Updated: Jul 2026