-1

Hey I need to create a windows task that acts much like the Logoff Scripts in Group Policy (our environment does not have AD) do. The task needs to run as the logged in user so that the script can output the name of the user running it into a .csv that tracks user logoff/login times and dates. I'm not sure that powershell is the absolute best way to do this but I need to be able to author task as an admin but set the user to run the script as the currently logged in user, whoever that might be at the time. This will be deployed to several hundred machines.

If there's another way to track user logoff I would be very much so interested in that method whether it involves powershell or not. Previously I've tried making the scripts run by editing the registry to attempt to set Logoff scripts in GP but that seemed to be more complex that I had initially expected. Zenworks was another option but it only ran the scripts AFTER the user had logged out which cause the script to output incorrect results into the .csv.

For reference the link below contains someone with a similar problem but without some of the specifics I need to target.

https://stackoverflow.com/questions/33390035/windows-creating-a-task-for-an-event-from-the-command-line

2
  • what's your specific problem? what have you tried and why did it fail?
    – flolilo
    Oct 4, 2017 at 16:13
  • @flolilolilo The problem mostly stems from not having AD. If I had AD, I could just add a logoff script through GP. I've tried making changes to the registry but I lack knowledge in powershell scripting so it makes it difficult to develop a more reasonable fix. I've also tried using Zenworks (our package deployment platform) to run a script on a user logoff event but it always triggers too late (The scripts grabs the currently logged in user's user name and some other metrics and put it into a .csv file. Zenworks triggers the script after the user has already logged out.).
    – nyshanin
    Oct 4, 2017 at 17:49

2 Answers 2

0

Edit: Significantly revised answer due to updated question.
This is still a bad idea for a general purpose logoff script - see this question for an explanation of why.

The key argument you need to look at is /MO. This is the modifier. Per the Microsoft document of SCHTASKS, when the /SC argument is ONEVENT, then /MO allows an XPath event query string.

This Technet Blog - Advanced XML filtering in the Windows Event Viewer - describes the format and provides useful examples.

Based on what you're actually doing with the data, you don't actually need to execute within the context of the user logging off. This is good, since it avoids the potential race condition inherent in trying to launch a new process in the middle of a logoff. It also simplifies your XPath query:

*[System[EventID=4647]]

All you really need to do is make sure that the task fired off from this event receives details about the event that triggered it. Scheduled tasks are stored as xml files, and you can manually edit the XML to add additional elements of the Task Scheduler XML schema that aren't exposed through the UI. I suggest you create a task first with SCHTASKS or through the UI with all the values you can specify. Then export it through the UI or with:

schtasks /Query [/S <system> [/U <username> [/P [<password>]]]] `
/XML /TN <taskname> >event.xml

The Technet article Trigger a PowerShell Script from a Windows Event offers a good explanation and example of how to use this. However, the key attribute they added was ValueQueries:

<ValueQueries>
    <Value name="eventChannel">Event/System/Channel</Value>
    <Value name="eventRecordID">Event/System/EventRecordID</Value>
    <Value name="eventSeverity">Event/System/Level</Value>
</ValueQueries>

Rather than using the EventRecordID to query back to the event as they demonstrated, you should be able to pull in the limited data you need directly:

<Value name="logoffUsername">Event/EventData/Data[@Name='TargetUserName']</Value>

You might also want to pull in the TimeCreated, as it should be more reliably accurate than a time function called from within your script:

<Value name="eventTime">Event/System/TimeCreated/@SystemTime</Value>

This will allow you to reference $(logoffUsername) and (optionally) $(eventTime) within the Powershell script executed by the task.

In case you wanted to pull in other values the Event XML schema is defined on MSDN. I also referenced Michael Albert's blog post on passing event parameters for syntax examples.

Once you have finished modifying the task definition, you can import the same XML on all computers:

schtasks /Create [/S <system> [/U <username> [/P [<password>]]]]

/XML /TN

Note: The original question called for creating an event for a user under the context of that user, and running it only when that user logged off. This is a bad idea due to the potential race condition mentioned above. For reference, this was the solution offered.

$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
8
  • You don’t specify which one of those 5 responses is relevant. Honestly until you quote and cite the current relevant information in your answer I think adding the quotation for you is not wise. So feel free to improve this answer and I will add the link for you.
    – Ramhound
    Oct 4, 2017 at 0:03
  • I only want to track Security\Microsoft Windows security auditing\4647. AD isn't a thing in my environment and I've failed miserably to get any answers about making changes to the registry in order to add a logoff script through GP so it seems that tasks are the most reasonable way to accomplish my goal.
    – nyshanin
    Oct 4, 2017 at 12:53
  • @nyshanin AD does not need to be in place in order to implement logoff scripts. You can set them in the local policy as well.
    – BryKKan
    Oct 4, 2017 at 16:47
  • @BryKKan Is there a way to easily make changes to local policy on many machines?
    – nyshanin
    Oct 4, 2017 at 17:48
  • I haven't used it myself yet, but the old version is no longer supported, so I'd suggest looking at this tool: blogs.technet.microsoft.com/secguide/2016/01/21/…
    – BryKKan
    Oct 4, 2017 at 18:52
0

After attempting to make this work with taskscheduler I finally came to the conclusion that my initial idea wasn't going to implemented as simply and reliably as I had first hoped. Moving away from tasksched but still retaining the idea to use powershell, I made a powershell script that zenworks could run at User Logoff in order to track the details that I considered relevant. That script can be found here (It's a powershell script run through a batch file due to compatibility issues):

@@:: 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
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .