miércoles, 17 de junio de 2026

 


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

$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:

miércoles, 27 de mayo de 2026

WINDOWS. GPO. Usuario bloqueado email

Propósito:

Por diferentes motivos las cuentas de nuestros usuarios del AD se bloquean, ya sea de manera automática, por culpa del propio usuario o cualquier otra circunstancia.

Esto al final acaba en tiempo de gestión para los administradores y poca visibilidad e información para los usuarios.

El script que voy a pegar podría hacerse de muchas maneras, en las que aportarían más información, pero he querido que sea sencillo y que no necesite mirar el visor de eventos (que tendría que ir a cada uno de nuestros DC si no están centralizados) y sin crear ningún fichero en el disco.

Al final acaba enviando un email al usuario e informándole que ha sido bloqueado. Te puedes preguntar ¿cómo lo va a leer si tiene el usuario del AD bloqueado?.. buena pregunta. En mi caso las cuentas de correo no están gestionadas por el AD, en otras cuentas de proveedor pongo la externa (no tienen cuenta de correo interna), hay muchos casos.

Esta claro que para que funcione la cuenta del usuario del AD debe tener documentado el email. Otra opción es que siempre que se bloquee, se envíe email solo a IT o con copia a IT.

La idea es que solo envíe una alerta por hora, para no ser muy pesado. Con lo que es importante configurar bien el programador de tareas.

Esto va muy bien sobre todo con proveedores, se dejan sesiones abiertas en los servidores durante mucho tiempo y cuando van a conectar porque les necesitamos no pueden. Con esta medida les llega un email para que cierren las sesiones y eviten este tipo de bloqueo.

En mi caso a los proveedores les caduca la contraseña automáticamente cada 24h, así que si no cierran sesión de un día para otro, esto les supone un bloqueo ya que tienen la sesión abierta con una contraseña caducada.

El resultado sería el siguiente email:


Pasos

El Script :

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

# SCRIPT: AD-Lockout-UserNotify.ps1

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

# FUNCION:

# Detecta usuarios bloqueados en Active Directory usando

# el atributo lockoutTime, consultando directamente contra

# el PDC Emulator para garantizar consistencia.

#

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


Import-Module ActiveDirectory


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

# VARIABLES DEL USUARIO

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

$SMTPServer = "smtp.MiDominio.com"

$SMTPPort   = 25

$From       = "Servodorenvio@MiDominio.com"


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

# OBTENCION DEL PDC

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

$domain = Get-ADDomain

$PDC = $domain.PDCEmulator


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

# CONFIGURACION

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

$now = Get-Date


$startWindow = $now.AddMinutes(-65)

$endWindow   = $now.AddMinutes(-2)


$lockoutDuration = 60


$startFileTime = $startWindow.ToFileTime()

$endFileTime   = $endWindow.ToFileTime()


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

# BUSQUEDA

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

$lockedUsers = Get-ADUser -Server $PDC `

-Filter {lockoutTime -gt 0} `

-Properties lockoutTime,mail,displayName,lastLogonDate,lastBadPasswordAttempt,passwordLastSet


foreach ($user in $lockedUsers) {


    if ($user.lockoutTime -gt 0 -and $user.mail) {


        $lockoutDate = [DateTime]::FromFileTime($user.lockoutTime)


        if ($user.lockoutTime -ge $startFileTime -and $user.lockoutTime -le $endFileTime) {


            if ($lockoutDate.AddMinutes($lockoutDuration) -gt $now) {


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

                # CONVERSIONES SEGURAS

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

                $LastLogon = if ($user.LastLogonDate) { $user.LastLogonDate } else { "No disponible" }

                $LastBadPwd = if ($user.LastBadPasswordAttempt) { $user.LastBadPasswordAttempt } else { "No disponible" }

                $PwdLastSet = if ($user.PasswordLastSet) { $user.PasswordLastSet } else { "No disponible" }


                $remaining = [math]::Round(($lockoutDate.AddMinutes($lockoutDuration) - $now).TotalMinutes)

                if ($remaining -lt 0) { $remaining = 0 }


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

                # ASUNTO CORPORATIVO

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

                $fecha = Get-Date -Format "yyyyMMdd - HH:mm"

                $Subject = "$fecha. SE. SEGURIDAD. Cuenta bloqueada: $($user.SamAccountName)"

$mail = $($user.mail)


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

                # EMAIL HTML

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

                $html = @"

<html>

<body style='font-family:Calibri;font-size:14px'>


<p>Hola $($user.displayName),</p>


<p><b>Su cuenta ha sido bloqueada por un proceso automatico.</b></p>


<p>Motivos habituales:</p>


<ul>

<li>Cambio de password reciente</li>

<li>Password caducado</li>

<li>Sesion activa con credenciales antiguas (muy probable)</li>

<li>Servicios o aplicaciones con password antiguo</li>

</ul>


<p>Revise los equipos donde haya iniciado sesion y vuelva a autenticarse.</p>


<p><b>Datos del bloqueo:</b></p>


<ul>

<li>Usuario: $($user.SamAccountName)</li>

<li>Fecha bloqueo: $lockoutDate</li>

<li>Tiempo restante bloqueo: $remaining minutos</li>

</ul>


<p><b>Informacion adicional:</b></p>


<ul>

<li>Ultimo logon: $LastLogon</li>

<li>Ultimo intento fallido: $LastBadPwd</li>

<li>Password cambiado: $PwdLastSet</li>

</ul>


<p>Contacte con IT de soporte@MiDominio.com si persiste.</p>


</body>

</html>

"@


                Send-MailMessage `

                -SmtpServer $SMTPServer `

                -Port $SMTPPort `

                -From $From `

                -To $mail `

                -Subject $Subject `

                -Body $html `

                -BodyAsHtml


            }

        }

    }

}


 


La tarea programada :




Probar con un usuario:


Para probar y ver pon pantalla y si hay algín caso

Import-Module ActiveDirectory

$pdc = (Get-ADDomain).PDCEmulator

$lockedUsers = Search-ADAccount -LockedOut -Server $pdc

if ($lockedUsers) {

    Write-Host "Hay usuarios bloqueados"

    $lockedUsers | ForEach-Object {
        Write-Host $_.SamAccountName
    }

} else {

    Write-Host "No hay usuarios bloqueados"

}


by GoN | Published: May 2026 | Last Updated: