
Oye, necesito crear una tarea de Windows que actúe de manera muy similar a los scripts de cierre de sesión en la Política de grupo (nuestro entorno no tiene AD). La tarea debe ejecutarse como el usuario que inició sesión para que el script pueda generar el nombre del usuario que la ejecuta en un .csv que rastrea las horas y fechas de cierre de sesión/inicio de sesión del usuario. No estoy seguro de que PowerShell sea la mejor manera de hacer esto, pero necesito poder crear una tarea como administrador, pero configurar al usuario para que ejecute el script como el usuario que ha iniciado sesión actualmente, quienquiera que sea en ese momento. Esto se implementará en varios cientos de máquinas.
Si hay otra forma de rastrear el cierre de sesión del usuario, estaría muy interesado en ese método, ya sea que involucre PowerShell o no. Anteriormente intenté ejecutar los scripts editando el registro para intentar configurar los scripts de cierre de sesión en GP, pero parecía ser más complejo de lo que esperaba inicialmente. Zenworks era otra opción, pero solo ejecutaba los scripts DESPUÉS de que el usuario había cerrado sesión, lo que provocaba que el script generara resultados incorrectos en el archivo .csv.
Como referencia, el siguiente enlace contiene a alguien con un problema similar pero sin algunos de los detalles a los que debo abordar.
Respuesta1
Editar: respuesta significativamente revisada debido a una pregunta actualizada.
Esta sigue siendo una mala idea para un script de cierre de sesión de propósito general; consulteesta preguntapara una explicación de por qué.
El argumento clave que debes considerar es /MO
. Este es el modificador. Según el documento de Microsoft deTAREAS, cuando el /SC
argumento es ONEVENT
, /MO
permite una cadena de consulta de evento XPath.
Este blog de Technet: Filtrado XML avanzado en el Visor de eventos de Windows- describe el formato y proporciona ejemplos útiles.
Según lo que realmente esté haciendo con los datos, en realidad no es necesario ejecutarlos en el contexto en el que el usuario cierra la sesión. Esto es bueno, ya que evita la posible condición de carrera inherente al intentar iniciar un nuevo proceso en medio de un cierre de sesión. También simplifica su consulta XPath:
*[System[EventID=4647]]
Todo lo que realmente necesita hacer es asegurarse de que la tarea iniciada por este evento reciba detalles sobre el evento que lo desencadenó. Las tareas programadas se almacenan como archivos xml y puede editar manualmente el XML para agregar elementos adicionales del esquema XML del Programador de tareas que no están expuestos a través de la interfaz de usuario. Le sugiero que primero cree una tarea con SCHTASKS o mediante la interfaz de usuario con todos los valores que pueda especificar. Entoncesexportara través de la interfaz de usuario o con:
schtasks /Query [/S <system> [/U <username> [/P [<password>]]]] `
/XML /TN <taskname> >event.xml
El artículo de TechnetActivar un script de PowerShell desde un evento de Windowsofrece una buena explicación y un ejemplo de cómo utilizarlo. Sin embargo, el atributo clave que agregaron fueConsultas de valor:
<ValueQueries>
<Value name="eventChannel">Event/System/Channel</Value>
<Value name="eventRecordID">Event/System/EventRecordID</Value>
<Value name="eventSeverity">Event/System/Level</Value>
</ValueQueries>
En lugar de utilizar EventRecordID para consultar el evento como lo demostraron, debería poder obtener los datos limitados que necesita directamente:
<Value name="logoffUsername">Event/EventData/Data[@Name='TargetUserName']</Value>
También es posible que desees incorporar TimeCreated, ya que debería ser más confiable y preciso que una función de tiempo llamada desde tu script:
<Value name="eventTime">Event/System/TimeCreated/@SystemTime</Value>
Esto le permitirá hacer referencia $(logoffUsername)
y (opcionalmente) $(eventTime)
dentro del script de Powershell ejecutado por la tarea.
En caso de que desee obtener otros valores, el esquema XML del evento está definido.en MSDN. También hice referenciaPublicación del blog de Michael Albert sobre cómo pasar parámetros de eventospara ejemplos de sintaxis.
Una vez que haya terminado de modificar la definición de la tarea, puedeimportarel mismo XML en todas las computadoras:
schtasks /Create [/S <system> [/U <username> [/P [<password>]]]]
/XML /TN
Nota: La pregunta original pedía crear un evento para un usuario en el contexto de ese usuario y ejecutarlo solo cuando ese usuario cerrara sesión. Esta es una mala idea debido a la posible condición de carrera mencionada anteriormente. Como referencia, esta fue la solución ofrecida.
$cred = Get-Credential
$password = $cred.GetNetworkCredential().Password
$command = <your command>
$myQuery = "*[System[EventID=4647] and EventData[Data [@Name='TargetUserName'] = '" + $env:username + "']]"
SCHTASKS /Create /TN "Logoff Monitor" /TR $command /SC ONEVENT `
/RL Highest /RU $cred.Username /RP $password `
/EC ScriptEvents /MO $myQuery
Respuesta2
Después de intentar hacer que esto funcionara con el programador de tareas, finalmente llegué a la conclusión de que mi idea inicial no se iba a implementar de manera tan simple y confiable como había esperado al principio. Alejándome de la programación de tareas pero manteniendo la idea de usar PowerShell, hice un script de PowerShell que Zenworks podía ejecutar al cerrar sesión del usuario para realizar un seguimiento de los detalles que consideraba relevantes. Ese script se puede encontrar aquí (es un script de PowerShell que se ejecuta a través de un archivo por lotes debido a problemas de compatibilidad):
@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.
@@:: Any non-PowerShell content must be preceeded by "@@"
@@setlocal
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF
if( -Not (Test-Path -Path C:\Windows\Logs\$env:computername.csv ) )
{
New-Item -Path C:\Windows\Logs\ -name "$env:computername.csv" -itemtype "file"
}
$events = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4647} -MaxEvents 1
foreach ($event in $events)
{
#Writes events to XML file
$eventXML = [xml]$event.ToXml()
#Converts Datetime of event into Two entries
$DateTime = '{0:MM/dd/yyyy hh:mm:ss}' -f ($event.timecreated)
$date = ($DateTime -split ' ')[0]
$time = ($DateTime -split ' ')[1]
#Formats Outputs of data to prep for export
$NewLine = "{0},{1},{2},{3},{4}" -f
"Logoff",
$env:computername,
$eventXML.Event.EventData.Data[1].'#text',
$Date,
$Time
#Exports Data to .csv
$NewLine | add-content -path C:\Windows\Logs\$env:computername.csv
}