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