miércoles, 17 de junio de 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

No hay comentarios: