Propósito
Pasos
Las GPO se aplican cada 90 minutos, si alguien consiguiese añadirse al grupo de administradores locales del PC, a los 90 minutos se borraría.
by GoN | Published: April 2026 | Last Updated:
Gonsystem: Windows - Linux - Servers - Redes - Cisco - Comunicaciones - Monitorización - Seguridad - Seguridad
Propósito
Pasos
Las GPO se aplican cada 90 minutos, si alguien consiguiese añadirse al grupo de administradores locales del PC, a los 90 minutos se borraría.
by GoN | Published: April 2026 | Last Updated:
Siguiendo las BP, vamos a crear una GPO que bloquea el acceso de los administradores de dominio para que puedan hacer RDP o logon en los PCS, así evitamos movimientos laterales y que la contraseña de un administrador se pueda quedar en el cache de alguna estación de trabajo.
Propósito
Una opción más para mantener nuestro Active Directory al día es asegurarnos de que no existan equipos que ya no estén en uso. Deberíamos procurar que los PCs estén lo más actualizados posible: últimas actualizaciones, parches, configuraciones de seguridad (GPOs), patrones de antivirus, etc. Además, un equipo que no se conecta o que ya no está activo sigue consumiendo licencias innecesariamente.
Para tener un control mínimo, he preparado este script que se ejecuta una vez por semana y informa de los equipos que llevan más de 30 y 60 días sin contactar con nuestro AD.
Pasos
|
#
========================== # VARIABLES DEL USUARIO #
========================== $SMTPServer =
"smtp.com" $SMTPPort = 25 $From =
"HelpDesk@Miempresa.com" $To =
"alertas@Miempresa.com" $Subject = "OU Computers que
llevan tiempo sin conectar" # CONFIGURACIÓN #
========================== Import-Module
ActiveDirectory $Hoy =
(Get-Date) $Dias30 =
$Hoy.AddDays(-30) $Dias60 =
$Hoy.AddDays(-60) # OBTENER EQUIPOS AD #
========================== $Equipos =
Get-ADComputer -Filter * -Properties Description, LastLogonTimestamp,
CanonicalName | Select-Object Name, Description,
@{Name="OU";Expression={($_.CanonicalName -replace
"^[^/]+/","")}},
@{Name="LastLogon";Expression={[DateTime]::FromFileTime($_.LastLogonTimestamp)}} # FILTRADOS #
========================== $Entre30y60 =
$Equipos | Where-Object { $_.LastLogon -le $Dias30 -and $_.LastLogon -gt
$Dias60 } $Mas60 = $Equipos | Where-Object {
$_.LastLogon -le $Dias60 } #
GENERAR TABLAS HTML # ========================== function Convert-ToHtmlTable {
param($Data, $Titulo)
if ($Data.Count -eq 0) { return
"<h3>$Titulo</h3><p>No hay equipos en este
rango.</p>" }
$Data | Select-Object Name, Description, OU, LastLogon | ConvertTo-Html -Fragment
-PreContent "<h3>$Titulo</h3>" } $Html60 = Convert-ToHtmlTable -Data $Mas60
-Titulo "Equipos sin conectar mas de 60 dias" #
EMAIL HTML # ========================== $Body = @" <html> <head> <style> table { border-collapse: collapse;
width: 100%; } th, td { border: 1px solid #999;
padding: 6px; text-align: left; } th { background-color: #f2f2f2; } h3 { font-family: Arial; } </style> </head> <body> <h2>Informe
de equipos que llevan tiempo sin conectar</h2> <p>Generado desde SRVDC04
$(Get-Date -Format "dd/MM/yyyy HH:mm")</p> $Html30_60 <br> $Html60 </body> </html> "@ # ENVÍO DEL EMAIL # ========================== Send-MailMessage -From $From -To
$To -Subject $Subject -Body $Body -BodyAsHtml `
-SmtpServer $SMTPServer -Port $SMTPPort |
El resultado vendría a ser un email así:
hiberfil.sys.
Pasos
Crear una GPO "FastStartup_Disable"
by GoN | Published: Mar 2026 | Last Updated:
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"
Propósito
[01] Preparar el AD
Debemos tener la plantilla preparada para la gestión del LAPS:
|
Copy
from: C:\Windows\PolicyDefinitions\LAPS.admx C:\Windows\PolicyDefinitions\<language>\LAPS.adml
|
Copy
to: \\<domain>\SYSVOL\<domain>\Policies\PolicyDefinitions\ \\<domain>\SYSVOL\<domain>\Policies\PolicyDefinitions\<language>\ |
Verifica que el esquema de AD tiene los atributos del
nuevo LAPS
Ejecuta en tu DC:
COMANDO: Get-ADObject -SearchBase
"CN=Schema,CN=Configuration,DC=Midomino,DC=com" -LDAPFilter
"(lDAPDisplayName=msLAPS-EncryptedPassword)"
COMANDO: Get-ADObject -SearchBase (Get-ADRootDSE).schemaNamingContext -LDAPFilter "(|(name=*laps*)(name=*admPwd*))" | Select-Object name
Si no lo tiene hay que prepararlo
Es recomendable que el usuario que lo vaya a configurar pertenezca a los grupos:
· Scheme Administrator / Organization Administrator
Para que la configuración no de problemas se debe hacer desde un Controlador de Dominio que tenga el módulo de LAPS instalado.
ç Empezamos. NO debes ejecutarlo si:
El esquema ya fue actualizado previamente (solo se hace una vez por bosque).
Tu dominio ya tiene los atributos de LAPS nuevo (Windows Server 2019/2022 + actualizaciones ya los incluyen).
Estás usando LAPS Legacy (el antiguo) y no piensas migrar.
Esta configuración podría cambiar en función a la evolución que de Microsoft a este servicio. Mucho cuidado en configurar el LAPS sin haber tenido en cuenta los puntos anteriores.
Preparamos el AD
C:\> Update-LapsADSchema
Debemos crear 2 nuevos grupos de usuarios, uno que solo podrá leer las contraseñas y otro para gestionar el LAPS
[] Permiso de lectura
COMMAND: PS C:\> Set-LapsADReadPasswordPermission -Identity "OU=LAPS,DC=midominio,DC=com" -AllowedPrincipals @("laps\LapsPasswordReadersGroup")
[] Permitir acceso a contraseñas cifradas (Password Encryption Allowed Principals)
Para visualizar contraseñas cuando LAPS está configurado con cifrado, los usuarios deben pertenecer a un grupo autorizado.
COMMAND: Set-LapsADPasswordEncryptionAllowedPrincipals
-Identity "OU=LAPS,DC=midominio,DC=com" -AllowedPrincipals
"Domain Admins","Grupo_Laps_Management"
Members of the Domain Admins group already have password query permission by default.
[] Consultar permisos extendidos (Query Extended Rights Permissions)
Este comando permite verificar qué grupos o usuarios tienen permisos avanzados sobre la OU, incluyendo lectura de contraseñas y escritura de atributos gestionados por LAPS.
COMMAND: Find-LapsADExtendedRights -Identity
Hay que autorizar a un grupo del AD de usuarios para poder leer las contraseñas del LAPS
Este será el día a día cuando debamos modificarlo
En cada OU que queramos que se le aplique la GPO hay que autorizarla por comandos
[03] Script para consultar la OU autorizadas por LAPS
Se recorre el AD y mira todas las OU, saca las que con el comando anterior han sido autorizadas
|
Import-Module
ActiveDirectory
#
GUID del atributo especÃfico $lapsEncryptedGuid
= [guid]"f3531ec6-6330-4f8e-8d39-7a671fbac605"
#
Obtener todas las OUs del dominio $OUs = Get-ADOrganizationalUnit
-Filter *
$ousWithWritePermission = foreach
($OU in $OUs) {
# Leer ACL de la OU usando ADSI $entry =
[ADSI]"LDAP://$($OU.DistinguishedName)"
$acl = $entry.ObjectSecurity
# Filtrar ACEs con WriteProperty sobre el
GUID especÃfico $writeAce = $acl.Access | Where-Object { ($_.ActiveDirectoryRights -band
[System.DirectoryServices.ActiveDirectoryRights]::WriteProperty) -and $_.ObjectType -eq $lapsEncryptedGuid
}
if ($writeAce) { foreach ($ace in $writeAce) { [PSCustomObject]@{ OU = $OU.DistinguishedName Trustee = $ace.IdentityReference Inherited = $ace.IsInherited } } } }
#
Mostrar lista única de OUs con permisos WRITE if ($ousWithWritePermission) {
$ousWithWritePermission | Sort-Object OU -Unique | Format-Table -AutoSize } else {
Write-Output "No se encontraron OUs con permisos WRITE sobre ms-LAPS-Encrypted-Password-Attributes." } |
|
Write-Host "Ejecutando v3.1..." Write-Host "Hay que asegurarse que
las OUs donde hay equipos LAPS este autorizadas con:
Set-LapsADComputerSelfPermission y que recorre todas las Sub-OUS, este proceso
puede durar varios minutos. " # --- Configuración --- $ou = "OU=Equipos,DC=midominio,DC=com" # Fecha y hora para el nombre del archivo $timestamp =
Get-Date -Format "yyyyMMdd_HHmm" $csvPath =
"C:\scripts\Computer_LAPS_with_assigned_passwd_$timestamp.csv" # --- Obtener equipos de la OU --- $computers =
Get-ADComputer -SearchBase $ou -Filter * -Properties Description, MemberOf,
DistinguishedName # --- Lista de resultados --- $resultados = @() foreach ($computer in $computers) {
try {
# Intenta obtener la contraseña con el nuevo esquema de LAPS
$laps =
Get-LapsADPassword -Identity $computer.Name -ErrorAction Stop if ($laps) { # Obtener OU desde el DN $computerOU =
($computer.DistinguishedName -split ",",2)[1] # Obtener grupos del AD $groups = $computer.MemberOf | ForEach-Object { ($_ -split ",")[0]
-replace "^CN=" } $resultados += [PSCustomObject]@{ ComputerName = $computer.Name Description = $computer.Description OU = $computerOU ADGroups = ($groups -join ";") PasswordSet = $true Expiration = $laps.ExpirationTimestamp } }
}
catch {
# No se añade a la lista
} } # --- Exportar a CSV --- $resultados |
Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8 Write-Host "Archivo CSV generado correctamente: $csvPath" Write-Host "Total de filas exportadas: $($resultados.Count)" |
COMANDO: Get-LapsDiagnostics
Get-LapsDiagnostics:
all data for this run was written to the following zip file:
[06] Como comprobar si un PC tiene LAPS
COMANDO: Get-ADComputer PC001 -Properties msLAPS-PasswordExpirationTime, msLAPS-EncryptedPassword
Si esos atributos tienen valores, LAPS está aplicado
y funcionando.
Si aparecen vacíos o null, el equipo no ha
aplicado la GPO o no ha podido escribir en AD.
[07] GPO
|
Detecta TODAS las OUs donde LAPS está realmente aplicado
Comprueba permisos LAPS (lectura, cifrado, self‑permission)
Detecta inconsistencias
Genera un informe claro en pantalla
Write-Host "=== AUDITORIA LAPS EN OUs CON EQUIPOS ===" -ForegroundColor Cyan
# Ruta del script para guardar el CSV en la misma carpeta
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
# Fecha y hora para el archivo
$timestamp = (Get-Date).ToString("yyyyMMdd_
# Detectar atributos disponibles en el esquema
$schema = (Get-ADObject -SearchBase (Get-ADRootDSE).
$hasNewLAPS = $schema -contains "msLAPS-
$hasLegacyLAPS = $schema -contains "ms-Mcs-AdmPwdExpirationTime"
Write-Host "Atributos detectados:" -ForegroundColor Yellow
Write-Host " - Windows LAPS: $hasNewLAPS"
Write-Host " - LAPS Legacy: $hasLegacyLAPS"
Write-Host ""
# Construir lista de propiedades validas
$props = @()
if ($hasNewLAPS) { $props += "msLAPS-
if ($hasLegacyLAPS) { $props += "ms-Mcs-AdmPwdExpirationTime" }
# Obtener todas las OUs
$OUs = Get-ADOrganizationalUnit -Filter *
$results = @()
$OUsSinPermisos = @()
$OUsPeligrosas = @()
foreach ($ou in $OUs) {
$ouDN = $ou.DistinguishedName
# Obtener equipos de la OU
try {
$computers = Get-ADComputer -Filter * -SearchBase $ouDN -Properties $props -ErrorAction Stop
}
catch {
continue
}
# Saltar OUs sin equipos
if ($computers.Count -eq 0) { continue }
# 1. Intentar obtener permisos LAPS sin romper el script
try {
$rights = Find-LapsADExtendedRights -Identity $ouDN -ErrorAction Stop
}
catch {
$rights = $null
}
# 2. Detectar si hay equipos con atributos LAPS
$lapsActive = $false
foreach ($c in $computers) {
foreach ($p in $props) {
if ($c.$p) { $lapsActive = $true; break }
}
if ($lapsActive) { break }
}
# 3. SELF permission
$selfPermission = $false
if ($rights) {
if ($rights.ExtendedRightHolders -match "SELF") { $selfPermission = $true }
}
# 4. Detectar automaticamente todos los grupos con permisos LAPS
$gruposLAPS = @()
if ($rights) {
foreach ($g in $rights.ExtendedRightHolders) {
if ($g -notmatch "SELF") {
$gruposLAPS += $g
}
}
}
# Registrar OUs sin permisos correctos
if (-not $selfPermission -or $gruposLAPS.Count -eq 0) {
$OUsSinPermisos += $ouDN
}
# Registrar OUs con demasiados grupos (riesgo)
if ($gruposLAPS.Count -gt 3) {
$OUsPeligrosas += $ouDN
}
# Resultado
$results += [PSCustomObject]@{
OU = $ouDN
PermisosLAPS = if ($rights) { "Si" } else { "No" }
SELF = if ($selfPermission) { "Si" } else { "No" }
GruposConPermisos = if ($gruposLAPS.Count -gt 0) { ($gruposLAPS -join "; ") } else { "Ninguno" }
EquiposConLAPS = if ($lapsActive) { "Si" } else { "No" }
NumeroEquipos = $computers.Count
}
}
# Mostrar tabla
$results | Sort-Object OU | Format-Table -AutoSize
# Exportar CSV en la misma carpeta del script
$exportPath = "$scriptPath\LAPS_Auditoria_$
$results | Sort-Object OU | Export-Csv $exportPath -NoTypeInformation -Encoding UTF8
Write-Host "`nCSV exportado en: $exportPath" -ForegroundColor Green
# Mostrar recomendaciones al final
Write-Host ""
Write-Host "=== RECOMENDACIONES ===" -ForegroundColor Cyan
Write-Host "`n1) OUs sin permisos LAPS correctos:" -ForegroundColor Yellow
if ($OUsSinPermisos.Count -eq 0) {
Write-Host " - Todas las OUs estan correctamente configuradas."
} else {
$OUsSinPermisos | ForEach-Object { Write-Host " - $_" }
}
Write-Host "`n2) Como aplicar permisos LAPS a todas las OUs con PCs:" -ForegroundColor Yellow
Write-Host " Set-
Write-Host " Set-
Write-Host " Set-
Write-Host "`n3) OUs con permisos peligrosos (demasiados grupos con acceso):" -ForegroundColor Yellow
if ($OUsPeligrosas.Count -eq 0) {
Write-Host " - No se han detectado OUs con exceso de grupos."
} else {
$OUsPeligrosas | ForEach-Object { Write-Host " - $_" }
}
Write-Host "`n=== FIN DE AUDITORIA ===" -ForegroundColor Cyan |