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

 

by GoN | Published: Jun 2026 | Last Updated:

miércoles, 17 de junio de 2026

WINDOWS. WSUS. Script refresh

 


Propósito:

En ocasiones o los PC tardan en entrar en el Wsus o el Wsus en ver los PCs, con lo que he crea un script, para ejecutar en los PCs, para refrescar esta conexión e intentar que se vean sin problemas.

Crea un log en C:\Windows\Temp\wsus_task.log en el que puedes verificar la ejecución de los pasos.

Es una herramienta en la que estoy muy contento, pero cuando le pasa algo no es fácil saber que le esta pasando.

Pasos:

El Script:


 # ================================
# WSUS FULL RESET - ENTERPRISE 
# ================================

$log = "C:\Windows\Temp\wsus_task.log"

# --- LOG ---
function Write-Log {
    param([string]$msg, [string]$level = "INFO")
    $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    Add-Content -Path $log -Value "$timestamp [$level] - $msg"
}

# --- EJECUCIÓN COMANDOS (CLAVE) ---
function Run-Command {
    param(
        [string]$name,
        [scriptblock]$command
    )

    Write-Log "$name - START"

    try {
        $output = & $command 2>&1
        if ($output) {
            foreach ($line in $output) {
                Write-Log "$name OUTPUT: $line"
            }
        }
        Write-Log "$name - OK"
    }
    catch {
        Write-Log "$name - ERROR: $($_.Exception.Message)" "ERROR"
    }
}

Write-Log "===== INICIO SCRIPT WSUS (FULL RESET PRO) ====="

# --- INFO ---
Write-Log "Equipo: $env:COMPUTERNAME"

# --- RED ---
Run-Command "Flush DNS" { ipconfig /flushdns }
Run-Command "Registro DNS" { ipconfig /registerdns }
Run-Command "Ping WSUS" { ping SRVSUSN }

Run-Command "Test WSUS 8531" {
    $test = Test-NetConnection NombreServidorWsus -Port 8531 -WarningAction SilentlyContinue
    "TcpTestSucceeded=$($test.TcpTestSucceeded) RemoteAddress=$($test.RemoteAddress)"
}

# --- TIEMPO ---
Run-Command "Sincronizar hora" { w32tm /resync }

# --- SERVICIOS STOP ---
Run-Command "Stop wuauserv" { Stop-Service wuauserv -Force }
Run-Command "Stop bits" { Stop-Service bits -Force }

# --- LIMPIEZA ---
# GON a medio plazo quitar
Run-Command "Delete SoftwareDistribution" {
    if (Test-Path "C:\Windows\SoftwareDistribution") {
        Remove-Item "C:\Windows\SoftwareDistribution" -Recurse -Force
    } else {
        "No existe SoftwareDistribution"
    }
}

#GON a medio plazo quitar
Run-Command "Delete catroot2" {
    if (Test-Path "C:\Windows\System32\catroot2") {
        Remove-Item "C:\Windows\System32\catroot2" -Recurse -Force
    } else {
        "No existe catroot2"
    }
}

# --- RESET WSUS ---
Run-Command "Reset identidad WSUS" {
    $wuReg = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate"
    Remove-ItemProperty -Path $wuReg -Name "AccountDomainSid" -ErrorAction SilentlyContinue
    Remove-ItemProperty -Path $wuReg -Name "PingID" -ErrorAction SilentlyContinue
#GON a medio plazo quitar SusClientId
    Remove-ItemProperty -Path $wuReg -Name "SusClientId" -ErrorAction SilentlyContinue
    Remove-ItemProperty -Path $wuReg -Name "SusClientIdValidation" -ErrorAction SilentlyContinue
}

# --- START SERVICIOS ---
Run-Command "Start wuauserv" { Start-Service wuauserv }
Run-Command "Start bits" { Start-Service bits }

Start-Sleep -Seconds 10

# --- GPO ---
Run-Command "GPO Update" { gpupdate /force }

Start-Sleep -Seconds 10

# --- VALIDACIÓN WSUS ---
Run-Command "Leer config WSUS" {
    $wsus = Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
    "WUServer=$($wsus.WUServer)"
    "WUStatusServer=$($wsus.WUStatusServer)"
}

# --- DETECCIÓN ---
Run-Command "DetectNow COM" {
    (New-Object -ComObject Microsoft.Update.AutoUpdate).DetectNow()
}

Start-Sleep -Seconds 10

# --- RESET AUTH ---
Run-Command "Reset Authorization" {
    wuauclt /resetauthorization /detectnow
}

# --- REPORT ---
Run-Command "Report WSUS" {
    wuauclt /reportnow
}

Start-Sleep -Seconds 15

# --- MODERNO ---
Run-Command "UsoClient Scan" {
    UsoClient StartScan
}


# --- DESCARGA ---
Run-Command "UsoClient Download" {
    UsoClient StartDownload
}

Start-Sleep -Seconds 30

# --- INSTALACIÓN ---
Run-Command "UsoClient Install" {
    UsoClient StartInstall
}


Start-Sleep -Seconds 20

# --- RESTART SERVICIO ---
Run-Command "Restart wuauserv" {
    Restart-Service wuauserv -Force
}

Start-Sleep -Seconds 15

# --- VALIDACIÓN FINAL ---
Run-Command "Validar SusClientId" {
    $wuReg = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate"
    $sus = Get-ItemProperty $wuReg -ErrorAction SilentlyContinue
    "SusClientId=$($sus.SusClientId)"
}

# --- DEBUG LOG ---
Run-Command "Generar WindowsUpdate.log" {
    Get-WindowsUpdateLog | Out-File "C:\Windows\Temp\WindowsUpdate_debug.log"
}

 Write-Log "===== FIN SCRIPT ====="


by GoN | Published: Jun 2026 | Last Updated:

WINDOWS.AD. PC extensionAttribute. Ultimo usuario conectado


Propósito:

El escenario es una empresa con muchos PCs repartidos en cientos de localizaciones. Ausencia de una herramienta de inventario y la dificultad en ocasiones de identificar quien ha sido la última persona que ha utilizado ese PC.

El problema es que si pasa algo y tenemos que contactar, entre otras cosas para que lo encienda, sin información sería una tarea complicada.

Lo que voy a guardar es: usuario + timestamp último logon, en TAREA PROGRAMADA en LOGON que se ejecuta con contexto del usuario como SYSTEM.

Lo bueno es que guardaras esta información en el AD, con lo que podrás consultarla aún estando apagado el PC.

Pasos

FASE 1

 [ ] Ir a “Usuarios y equipos del Active Directory”

 


[ ] Ir a la OU donde están los PCs a gestionar

Interfaz de usuario gráfica, Aplicación

Descripción generada automáticamente

 

[ ] Delegar control….

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

Descripción generada automáticamente

Añadimos el usuario SELF

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

Descripción generada automáticamente

 [ ] Crear una tarea personalizada para delegar

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

Descripción generada automáticamente

 Seleccionar: Objetos Equipo

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

Descripción generada automáticamente

Elegimos el “extensionAttribute10”

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

Descripción generada automáticamente

 Interfaz de usuario gráfica, Aplicación

Descripción generada automáticamente

FASE 2:

Pon el script en un recurso donde lleguen todos los PC, lo normal el SYSVOL, para llamarlo desde la GPO.

Script 

#gon 20260616

Start-Sleep -Seconds 5

$Computer = $env:COMPUTERNAME

$User = (Get-WmiObject Win32_ComputerSystem).UserName

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

 

if ($User) {

 

    $Value = "$User - $Date"

 

    $searcher = New-Object DirectoryServices.DirectorySearcher

    $searcher.Filter = "(&(objectClass=computer)(name=$Computer))"

    $result = $searcher.FindOne()

 

    if ($result) {

        $entry = $result.GetDirectoryEntry()

        $entry.Put("extensionAttribute10", $Value)

        $entry.SetInfo()

    }

}

 En breve añadiré más cosas interesantes.

FASE 3, La GPO

 Interfaz de usuario gráfica, Aplicación

Descripción generada automáticamente

 FASE 4. Las verificaciones

En el DC:

 ()Para insertar un comentario de prueba:

Set-ADComputer pc05 -Replace @{extensionAttribute10="hola gon"}

()Para leer el valor del nuevo atributo

Get-ADComputer pc05 -Properties extensionAttribute10 | Select Name, extensionAttribute10

()Para mirar todos los PCs del AD:

 

by GoN | Published: Jun 2026 | Last Updated: