martes, 10 de febrero de 2026

WINDOWS. PS. Computers - Pcs dias sin conectar al dominio

 Propósito

Localizar los PCs/host que lleven cierto tiempo sin contactar contra nuestro AD, ya sea porque se han perdido, los han robado, se han roto, etc... el tema es que hay que sacarlos porque podrían estar consumiendo licencias, rompiendo nuestras estadísticas de parcheo, los antivirus sin actualizar, etc..

Pasos

Para localizarlos he creado 2 scripts

Script 1:

Descripcion: PCs sin conectar en 90 dias (se puede modificar fácilmente la cantidad de días). La idea sería ejecutarlo una vez al día para estar al corriente de nuestro inventario. Se podría añadir a una tarea programada.

# Parámetros
$DiasSinConectar = 90   # 3 meses aproximados
$FechaLimite = (Get-Date).AddDays(-$DiasSinConectar)

# Nombre del fichero con fecha y hora
$TimeStamp = Get-Date -Format "yyyyMMdd_HHmmss"
$FicheroSalida = "Equipos_NoConectan_Desde_3Meses_$TimeStamp.csv"

# Consulta a AD
Import-Module ActiveDirectory

Get-ADComputer -Filter * -Properties lastLogonTimestamp, Description |
    Select-Object Name,
                  Description,
                  @{Name='UltimoLogon'; Expression={[DateTime]::FromFileTime($_.lastLogonTimestamp)}} |
    Where-Object { $_.UltimoLogon -lt $FechaLimite } |
    Export-Csv -Path $FicheroSalida -NoTypeInformation -Encoding UTF8

Write-Host "Proceso finalizado. Archivo generado: $FicheroSalida"

Su salida sería algo así:


Script 2:

Descripcion. PC búsqueda manual, pide en pantalla cuantos días quiere que retroceda para comprobar.


# Cargar módulo de Active Directory
Import-Module ActiveDirectory

# Pedir días por pantalla
$DiasSinConectar = Read-Host "Introduce el número de días que llevan los PCs sin conectar"
$FechaLimite = (Get-Date).AddDays(-[int]$DiasSinConectar)

# Convertir timestamp y filtrar
$Equipos = Get-ADComputer -Filter * -Properties lastLogonTimestamp, Description |
    Select-Object Name,
                  Description,
                  @{Name='UltimoLogon'; Expression={[DateTime]::FromFileTime($_.lastLogonTimestamp)}} |
    Where-Object { $_.UltimoLogon -lt $FechaLimite }

# Mostrar por pantalla
Write-Host ""
Write-Host "Equipos encontrados que llevan más de $DiasSinConectar días sin conectar:" -ForegroundColor Cyan
$Equipos | Format-Table -AutoSize
Write-Host ""

# Número de equipos
$Total = $Equipos.Count

# Crear nombre del fichero con fecha/hora y número de PCs
$TimeStamp = Get-Date -Format "yyyyMMdd_HHmmss"
$NombreFichero = "PCs_SinConectar_${DiasSinConectar}dias_${Total}equipos_$TimeStamp.csv"

# Exportar CSV
$Equipos | Export-Csv -Path $NombreFichero -NoTypeInformation -Encoding UTF8

Write-Host "Exportación completada. Archivo generado: $NombreFichero" -ForegroundColor Green

La salida sería algo así


Dejo en tu mano borrarlos!
by GoN | Published: Feb 2026 | Last Updated:

WINDOWS. GPO. Longitud password 15

Propósito 

Cambiar la Default Domain Policy para que las contraseña tengan una longitud de 15 o más caracteres

Pasos

La sorpresa es que por defecto, si no tocamos nada, la GPO solo permite hasta 14 caracteres.


Para poder ampliarla necesitamos Habilitar la siguiente directiva "Reducir los limites de longitud mínima de la contraseña"



Y solucionado!!!



by GoN | Published: Feb 2026 | Last Updated:

lunes, 9 de febrero de 2026

WINDOWS. PS. PCs en la OU de computers

Propósito

Cuando añadimos un PC al dominio se va al contendor "Computers" que no es una OU a la que se puedan asignar GPOs.

Esto implica el riesgo que no se les apliquen ninguna GPO, con lo que hay esta atentos para moverlos a su OU correspondiente.

El script que aquí pondré lo ejecuto una vez al día (Tarea programada) y le envío copia al equipo de Help Desk. Básicamente mira si hay algún hosts en el contenedor de Computers y si lo hay envía un email informando.

No modifica nada, solo es informativa.

Pasos

Básicamente mira si hay algún hosts en el contenedor de Computers y si lo hay envía un email informando

 <#
   Busca equipos en el contenedor CN=Computers.
   Si hay equipos → envía correo con la tabla HTML en el CUERPO.
   Si no hay equipos → no envía correo.
#>

# ==========================
#  VARIABLES DEL USUARIO
# ==========================
$SMTPServer = "miservidor.midominio.com"
$SMTPPort   = 25
$From       = "Servidor1@midominio.com"
$To         = "itsecurity@midominio.com" , "HelpDesk@midominio.com",
$Subject    = "PCs en la Ou de Computers"

# Contenedor donde buscar (CORRECTO)
$SearchBase = "CN=Computers,DC=midominio,DC=com"

# ==========================
#  CARGAR MÓDULO AD
# ==========================
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
    Write-Error "El módulo ActiveDirectory no está disponible. Instala RSAT."
    exit 1
}
Import-Module ActiveDirectory

# ==========================
#  VALIDAR CONTENEDOR
# ==========================
try {
    $null = Get-ADObject -Identity $SearchBase -ErrorAction Stop
}
catch {
    Write-Error "ERROR: No se encontró el contenedor/OU: $SearchBase"
    exit 1
}

# ==========================
#  OBTENER EQUIPOS
# ==========================
$Computers = Get-ADComputer -SearchBase $SearchBase `
    -SearchScope OneLevel `
    -Filter * `
    -Properties Name, DNSHostName, Description, OperatingSystem, Enabled, LastLogonDate, whenCreated, whenChanged

$Count = $Computers.Count
Write-Host "Equipos encontrados: $Count"

if ($Count -eq 0) {
    Write-Host "Contenedor vacío. No se envía correo."
    exit 0
}

# ==========================
#  GENERAR HTML
# ==========================
$Sorted = $Computers | Sort-Object Name

$Table = $Sorted | Select-Object `
    @{n="Nombre (sAM)"; e={$_.Name}},
    @{n="Hostname (DNS)"; e={$_.DNSHostName}},
    @{n="Descripción"; e={ if ($_.Description) { $_.Description } else { "N/D" } }},
    @{n="SO"; e={$_.OperatingSystem}},
    @{n="Habilitado"; e={$_.Enabled}},
    @{n="Último logon"; e={ if ($_.LastLogonDate) { $_.LastLogonDate } else { "N/D" } }},
    @{n="Creado"; e={$_.whenCreated}},
    @{n="Modificado"; e={$_.whenChanged}} |
    ConvertTo-Html -As Table -Fragment

$Style = @"
<style>
body { font-family: Segoe UI, Arial; color:#222; }
table { border-collapse: collapse; width:100%; }
th, td { border: 1px solid #ccc; padding:6px; }
th { background:#f0f0f0; }
tr:nth-child(even) { background:#fafafa; }
.notice { font-weight:bold; color:#b00020; }
</style>
"@

$IntroText = "<p class='notice'>Estos PC deben moverse a la OU correspondiente</p>"
$Header    = "<h2>Equipos encontrados en: $SearchBase</h2>"
$BodyHtml  = ConvertTo-Html -Head $Style -Body "$IntroText $Header $Table"

# ==========================
#  ENVIAR CORREO
# ==========================
try {
    $Mail = New-Object System.Net.Mail.MailMessage
    $Mail.From = $From

    foreach ($recipient in $To) {
        if ($recipient.Trim()) { $Mail.To.Add($recipient.Trim()) }
    }

    $Mail.Subject   = $Subject + " (Total: $Count)"
    $Mail.IsBodyHtml = $true
    $Mail.Body      = $BodyHtml

    $SMTP = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort)
    $SMTP.Send($Mail)

    Write-Host "Correo enviado correctamente."
}
catch {
    Write-Error "Error enviando el correo: $($_.Exception.Message)"
    exit 1

}

El resultado vendría ser algo parecido a lo siguiente


Dificultad: Baja
Utilidad: Alta
by GoN | Published: Feb 2026 | Last Updated: