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:

Con 


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

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

if (Test-Path $log) {
    if ((Get-Item $log).Length -gt 5MB) {
        Clear-Content $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 ---
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 v2 ====="

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

# ===================================================
# RED
# ===================================================

Run-Command "Flush DNS" {
    ipconfig /flushdns
}

Run-Command "Registro DNS" {
    ipconfig /registerdns
}

Run-Command "Ping WSUS" {
    ping miservidorsus.com
}

Run-Command "Test WSUS 8531" {

    $test = Test-NetConnection miservidorsus.com `
        -Port 8531 `
        -WarningAction SilentlyContinue

    "TcpTestSucceeded=$($test.TcpTestSucceeded)"
    "RemoteAddress=$($test.RemoteAddress)"
}

# --- NUEVO : VALIDACION HTTPS/TLS WSUS ---
Run-Command "Validacion HTTPS WSUS" {

    try {

        $response = Invoke-WebRequest `
            -Uri "https://miservidorsus:8531" `
            -UseBasicParsing `
            -TimeoutSec 15

        "StatusCode=$($response.StatusCode)"
        "HTTPS CONNECTIVITY OK"
    }
    catch {

        "HTTPS ERROR: $($_.Exception.Message)"
    }
}

# ===================================================
# TIEMPO
# ===================================================

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

# ===================================================
# STOP SERVICIOS UPDATE
# ===================================================

Run-Command "Stop servicios update" {

    Stop-Service wuauserv -Force -ErrorAction SilentlyContinue
    Stop-Service bits -Force -ErrorAction SilentlyContinue
    Stop-Service cryptsvc -Force -ErrorAction SilentlyContinue
    Stop-Service usosvc -Force -ErrorAction SilentlyContinue

}

# ===================================================
# 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 UPDATE
# ===================================================

Run-Command "Start servicios update" {

    Start-Service cryptsvc -ErrorAction SilentlyContinue
    Start-Service bits -ErrorAction SilentlyContinue
    Start-Service wuauserv -ErrorAction SilentlyContinue
    Start-Service usosvc -ErrorAction SilentlyContinue

}

Run-Command "Estado servicios" {

    Get-Service `
        wuauserv,
        bits,
        cryptsvc,
        usosvc |
    Select-Object Name, Status

}

Start-Sleep -Seconds 10

# ===================================================
# GPO
# ===================================================

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

Start-Sleep -Seconds 10

# ===================================================
# VALIDACION WSUS
# ===================================================

Run-Command "Leer config WSUS" {

    $wsus = Get-ItemProperty `
        "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"

    "WUServer=$($wsus.WUServer)"
    "WUStatusServer=$($wsus.WUStatusServer)"
}

# --- NUEVO : HISTORIAL DETECCION ---
Run-Command "Last Detection Result" {

    $path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Detect"

    if (Test-Path $path) {

        Get-ItemProperty $path |
        Select-Object LastSuccessTime, LastError

    }
    else {

        "Detection history not found"

    }
}

# --- NUEVO : HISTORIAL INSTALACION ---
Run-Command "Last Install Result" {

    $path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install"

    if (Test-Path $path) {

        Get-ItemProperty $path |
        Select-Object LastSuccessTime, LastError

    }
    else {

        "Install history not found"

    }
}

# ===================================================
# DETECCION
# ===================================================

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 WSUS
# ===================================================

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

# --- NUEVO : EVENTOS WINDOWS UPDATE ---
Run-Command "Windows Update Events" {

    Get-WinEvent `
        -LogName "Microsoft-Windows-WindowsUpdateClient/Operational" `
        -MaxEvents 30 `
        -ErrorAction SilentlyContinue |
        Select-Object `
            TimeCreated,
            Id,
            LevelDisplayName,
            Message
}

Start-Sleep -Seconds 15

# ===================================================
# USOCLIENT
# ===================================================

Run-Command "UsoClient Scan" {
    UsoClient StartScan
}

Run-Command "Interactive Scan" {
    UsoClient StartInteractiveScan
}

# ===================================================
# DESCARGA
# ===================================================

Run-Command "UsoClient Download" {
    UsoClient StartDownload
}

Start-Sleep -Seconds 30

# ===================================================
# INSTALACION
# ===================================================

Run-Command "UsoClient Install" {
    UsoClient StartInstall
}

Start-Sleep -Seconds 20

# ===================================================
# RESTART WUAUSERV
# ===================================================

Run-Command "Restart wuauserv" {
    Restart-Service wuauserv -Force
}

Start-Sleep -Seconds 15

# ===================================================
# VALIDACION SUSCLIENTID
# ===================================================

Run-Command "Validar SusClientId" {

    $wuReg = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate"

    $sus = Get-ItemProperty `
        $wuReg `
        -ErrorAction SilentlyContinue

    "SusClientId=$($sus.SusClientId)"
}

# --- NUEVO : DIAGNOSTICO IDENTIDAD WSUS ---
Run-Command "WSUS Identity Check" {

    $wuReg = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate"

    $data = Get-ItemProperty `
        $wuReg `
        -ErrorAction SilentlyContinue

    "SusClientId=$($data.SusClientId)"
    "SusClientIdValidation=$($data.SusClientIdValidation)"
    "PingID=$($data.PingID)"
}

# --- NUEVO : UPDATE ORCHESTRATOR ---
Run-Command "Update Orchestrator Status" {

    Get-Service usosvc `
        -ErrorAction SilentlyContinue |
    Select-Object Name, Status, StartType

}

# ===================================================
# GENERACION LOG WINDOWS UPDATE
# ===================================================

Run-Command "Generar WindowsUpdate.log" {

    Get-WindowsUpdateLog |
    Out-File "C:\Windows\Temp\WindowsUpdate_debug.log"

}

# ===================================================
# RESUMEN FINAL
# ===================================================

Run-Command "WSUS Health Summary" {

    "========== SERVICES =========="

    Get-Service `
        wuauserv,
        bits,
        cryptsvc,
        usosvc |
    Select-Object Name, Status

    ""

    "========== WSUS CONFIG =========="

    $wsus = Get-ItemProperty `
        "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" `
        -ErrorAction SilentlyContinue

    "WUServer=$($wsus.WUServer)"
    "WUStatusServer=$($wsus.WUStatusServer)"

    ""

    "========== CLIENT ID =========="

    $client = Get-ItemProperty `
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" `
        -ErrorAction SilentlyContinue

    "SusClientId=$($client.SusClientId)"
}

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

 Si la cosa no se soluciona probrar


Stop-Service wuauserv
Stop-Service bits

Remove-ItemProperty `
-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" `
-Name SusClientId `
-ErrorAction SilentlyContinue

Remove-ItemProperty `
-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" `
-Name SusClientIdValidation `
-ErrorAction SilentlyContinue

Remove-ItemProperty `
-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" `
-Name PingID `
-ErrorAction SilentlyContinue

Remove-ItemProperty `
-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" `
-Name AccountDomainSid `
-ErrorAction SilentlyContinue

Rename-Item `
C:\Windows\SoftwareDistribution `
SoftwareDistribution.old

Start-Service bits
Start-Service wuauserv

wuauclt /resetauthorization /detectnow



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

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:

 
*****************************************************************************************************

La evolución de lo anterior

Nos verifica servicios críticos, nos da la Ip y la MAC del PC

Resultado:

PC001-DOMINIO\usuario-2026-07-01 09:15:00-192.168.10.25-00:11:22:33:44:55

Si detecta errores:

PC001-DOMINIO\usuario-2026-07-01 09:15:00-192.168.10.25-00:11:22:33:44:55|cyserver:NOK-Stopped,CPHBWatcher:NOK-NotFound


Servicios en el PC como:



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)

{

    # Obtener IP y MAC principal

    $NIC = Get-WmiObject Win32_NetworkAdapterConfiguration |

           Where-Object {

               $_.IPEnabled -eq $true -and

               $_.MACAddress

           } |

           Select-Object -First 1

 

    $IP = ""

 

    if ($NIC)

    {

        $IP = $NIC.IPAddress |

              Where-Object { $_ -match '^\d{1,3}(\.\d{1,3}){3}$' } |

              Select-Object -First 1

 

        $MAC = $NIC.MACAddress

    }

    else

    {

        $MAC = ""

    }

 

    $Value = "$Computer-$User-$Date-$IP-$MAC"

 

    $services = @(

        "cyserver",

        "xdrhealth",

        "GoNBadService",

        "CPHBWatcher"

    )

 

    $nokList = @()

 

    # Dar tiempo al arranque de servicios

    Start-Sleep -Seconds 90

 

    foreach ($s in $services)

    {

        try

        {

            $svc = Get-Service -Name $s -ErrorAction Stop

 

            if ($svc.Status -ne "Running")

            {

                $nokList += "${s}:NOK-$($svc.Status)"

            }

        }

        catch

        {

            $nokList += "${s}:NOK-NotFound"

        }

    }

 

    if ($nokList.Count -gt 0)

    {

        $Value = "$Value|" + ($nokList -join ",")

    }

 

    $searcher = New-Object DirectoryServices.DirectorySearcher

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

 

    $result = $searcher.FindOne()

 

    if ($result)

    {

        $entry = $result.GetDirectoryEntry()

 

        $entry.Properties["extensionAttribute10"].Value = $Value

 

        $entry.CommitChanges()

    }

}

Como tengo la información en el AD, si un PC no tiene arrancado o instalado el antivirus, puedo enviar un email a Help Desk para que lo revise. Genero una tarea programada a las 11:00 de la mañana, cuando en principio ya se han logeado mis usuarios y nutrido la información. Solo verifica los del día en curso.

<#

.SYNOPSIS

Revision diaria de servicios criticos.

 

.DESCRIPTION

El script revisa el atributo extensionAttribute10 de todos los equipos

de Active Directory.

 

Solo considera equipos que:

 

- Han actualizado extensionAttribute10 durante el dia actual.

- Contienen la cadena NOK.

 

Si existen equipos afectados:

 

- Genera un CSV.

- Genera un correo HTML.

- Adjunta el CSV.

- Envia el correo.

 

Si no existen equipos afectados:

 

- No envia correo.

- Finaliza sin generar alertas.

 

Disenado para ejecucion mediante Tarea Programada.

 

#>

 

Import-Module ActiveDirectory -ErrorAction Stop

 

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

# VARIABLES DEL USUARIO

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

$SMTPServer = "smtp.xxxx.com"

$SMTPPort   = 25

$From       = "alerta@midom.com"

$To         = "HelpDesk@midom.com"

$Subject    = ""

 

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

# CONFIGURACION

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

$Hoy = Get-Date -Format "yyyy-MM-dd"

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

 

$RutaInforme = "C:\Scripts\Informes"

 

if (!(Test-Path $RutaInforme))

{

    New-Item -Path $RutaInforme -ItemType Directory -Force | Out-Null

}

 

$CSVFile = Join-Path $RutaInforme "ServiciosCriticos_NOK_$Hoy.csv"

 

try

{

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

    # OBTENCION DE EQUIPOS

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

    $EquiposConProblemas = Get-ADComputer `

        -LDAPFilter "(extensionAttribute10=*)" `

        -Properties extensionAttribute10 |

        Where-Object {

            $_.extensionAttribute10 -like "*$Hoy*" -and

            $_.extensionAttribute10 -like "*NOK*"

        } |

        Select-Object @{

                Name = "Equipo"

                Expression = { $_.Name }

            },

            @{

                Name = "Detalle"

                Expression = { $_.extensionAttribute10 }

            } |

        Sort-Object Equipo

 

    $TotalEquipos = $EquiposConProblemas.Count

 

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

    # SOLO SI HAY NOK

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

    if ($TotalEquipos -gt 0)

    {

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

        # EXPORTACION CSV

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

        $EquiposConProblemas |

        Export-Csv `

            -Path $CSVFile `

            -Delimiter ";" `

            -NoTypeInformation `

            -Encoding UTF8

 

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

        # ASUNTO

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

        $Subject = "$FechaActual - Equipos con problemas al arrancar servicios criticos - $TotalEquipos elementos"

 

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

        # TABLA HTML

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

        $TablaHTML = $EquiposConProblemas |

            ConvertTo-Html -Fragment

 

        $Body = @"

<html>

 

<head>

 

<style>

 

body{

    font-family: Arial;

    font-size: 10pt;

}

 

table{

    border-collapse: collapse;

}

 

table, th, td{

    border: 1px solid black;

    padding: 5px;

}

 

th{

    background-color: #D9EAD3;

}

 

</style>

 

</head>

 

<body>

 

<h2>Revision diaria de servicios criticos</h2>

 

<p>

De los PC que hoy se han conectado, se han encontrado

<b>$TotalEquipos</b> equipos con problemas al arrancar servicios criticos.

</p>

 

<p>

Fecha de ejecucion: $(Get-Date)

</p>

 

$TablaHTML

 

<br>

 

<p>

Se adjunta el detalle completo en formato CSV.

</p>

 

</body>

 

</html>

"@

 

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

        # ENVIO EMAIL

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

        Send-MailMessage `

            -SmtpServer $SMTPServer `

            -Port $SMTPPort `

            -From $From `

            -To $To `

            -Subject $Subject `

            -Body $Body `

            -BodyAsHtml `

            -Attachments $CSVFile `

            -Encoding UTF8

    }

}

catch

{

    $ErrorMessage = $_.Exception.Message

 

    $Subject = "$FechaActual - Error revision servicios criticos"

 

    $Body = @"

<html>

<body>

 

<h2>Error durante la ejecucion</h2>

 

<p>

Se ha producido un error durante la revision diaria.

</p>

 

<p>

$ErrorMessage

</p>

 

</body>

</html>

"@

 

    Send-MailMessage `

        -SmtpServer $SMTPServer `

        -Port $SMTPPort `

        -From $From `

        -To $To `

        -Subject $Subject `

        -Body $Body `

        -BodyAsHtml `

        -Encoding UTF8

}

Con este resultado, que podría ser un ticket automático:




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