Wednesday, May 23, 2012

Passing event details as arguments through a scheduled task to the action program

In this post I am going to schedule a task that runs a program when a certain event occurs, and pass details of that event as command line parameters to the action program


This is a simple windows script that prints back any command line arguments passed to it

args.vbs

if wscript.arguments.count > 0 then
 for i = 0 to wscript.arguments.count -1
  wscript.echo i & " " & wscript.arguments.item(i)
 next
else
 wscript.echo "no arguments"
end if
wscript.stdin.readline


In the Event Viewer I go to the event I wish to attach my task to. I chose event 4778 which (in Windows 2008 R2) occurs when a user reconnects to an existing session.

Log Name:      Security
Source:        Microsoft-Windows-Security-Auditing
Date:          5/22/2012 11:26:35 AM
Event ID:      4778
Task Category: Other Logon/Logoff Events
Level:         Information
Keywords:      Audit Success
User:          N/A
Computer:      Server1.XXXXX.XXX
Description:
A session was reconnected to a Window Station.

Subject:
 Account Name:  administrator
 Account Domain:  XXXXX
 Logon ID:  0x4832b

Session:
 Session Name:  RDP-Tcp#0

Additional Information:
 Client Name:  XXX
 Client Address:  XXX.XXX.XXX.XXX

This event is generated when a user reconnects to an existing Terminal Services session, or when a user switches to an existing desktop using Fast User Switching.
I am going to pass the Date, Event ID, Computer, Account Name, Accoount Domain, Client Name and  Client Address to our script.
First we need to create the task.
From the Actions pane in the Event Viewer click: Attach Task To This Event


Continue the Wizard and choose for Action "Start a program"


I set the script host executable cscript.exe as the action program and gave it my script host as argument


A success confirmation message appears after finishing the wizard


Now to extract the event details and pass them to the script we have to modify the xml of the scheduled task, and add xquery path strings to the details we want to extract.
Export the task from the Task Scheduler.


The exported xml should be like this:

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="
http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2012-05-23T08:42:50.2747257</Date>
    <Author>XXXXX\administrator</Author>
  </RegistrationInfo>
  <Triggers>
    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Security"&gt;&lt;Select Path="Security"&gt;*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4778]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
    </EventTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>XXXXX\administrator</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <Duration>PT10M</Duration>
      <WaitTimeout>PT1H</WaitTimeout>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\cscript.exe</Command>
      <Arguments>C:\ayman\args.vbs</Arguments>
    </Exec>
  </Actions>
</Task>



Paste the code below inside the EventTrigger node

<ValueQueries>
  <Value name="AccountDomain">Event/EventData/Data[@Name='AccountDomain']</Value>
  <Value name="AccountName">Event/EventData/Data[@Name='AccountName']</Value>
  <Value name="ClientAddress">Event/EventData/Data[@Name='ClientAddress']</Value>
  <Value name="ClientName">Event/EventData/Data[@Name='ClientName']</Value>
  <Value name="Computer">Event/System/Computer</Value>
  <Value name="EventID">Event/System/EventID</Value>
  <Value name="Logged">Event/System/TimeCreated/@SystemTime</Value>
</ValueQueries>


And add the following to Arguments inside Actions -> Exec

'$(AccountName)' '$(AccountDomain)' '$(ClientName)' '$(ClientAddress)' '$(EventID)' '$(Computer)' '$(Logged)'

So that the final xml looks like this

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="
http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2012-05-23T08:42:50.2747257</Date>
    <Author>XXXXX\administrator</Author>
  </RegistrationInfo>
  <Triggers>
    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Security"&gt;&lt;Select Path="Security"&gt;*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4778]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
      <ValueQueries>
        <Value name="AccountName">Event/EventData/Data[@Name='AccountName']</Value>
        <Value name="ClientName">Event/EventData/Data[@Name='ClientName']</Value>
 <Value name="AccountDomain">Event/EventData/Data[@Name='AccountDomain']</Value>
 <Value name="ClientAddress">Event/EventData/Data[@Name='ClientAddress']</Value>
 <Value name="EventID">Event/System/EventID</Value>
 <Value name="Computer">Event/System/Computer</Value>
 <Value name="Logged">Event/System/TimeCreated/@SystemTime</Value>
      </ValueQueries>
    </EventTrigger>

  </Triggers>
<Principals>
    <Principal id="Author">
      <UserId>XXXXX\administrator</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <Duration>PT10M</Duration>
      <WaitTimeout>PT1H</WaitTimeout>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\cscript.exe</Command>
      <Arguments>C:\ayman\args.vbs '$(AccountName)' '$(AccountDomain)' '$(ClientName)' '$(ClientAddress)' '$(EventID)' '$(Computer)' '$(Logged)'</Arguments>
    </Exec>

  </Actions>
</Task>


Delete the task created by the wizard earlier and import the xml we edited instead.

Finally we need to fire the event, in my case I am going to disconnect and connect back to my remote desktop session.
Upon connecting I get the script output.

18 comments:

  1. This is very helpful. Thank you. I have just one further question. A log I am interested in had a data section that has no labels. Such as this:


    192.168.4.70
    DC\jdoe
    2

    How would I write a ValueQuery fir the IP address?
    I tried "Event/EventData/Data[1]" and it does not work.
    Thanks in advance for any help.

    ReplyDelete
    Replies
    1. Hello Nick,

      Look in the XML view (from details tab) to find the structure of your event, and then you can query the field you want. If there is no specific element for the IP, you will have to parse it yourself inside your script.

      Delete
  2. Thanks for this great tutorial! I'm using this with Windows Server Backup to report to Zabbix if the backup was done well.

    ReplyDelete
  3. Thanks! I was happy to find this.

    ReplyDelete
  4. This paragraph will assist the internet users for setting up
    new blog or even a weblog from start to end.

    Here is my homepage ... diets that work

    ReplyDelete
  5. This is very interesting, You're a very skilled blogger. I have joined your rss feed and look forward to seeking more of your fantastic post. Also, I've shared
    your website in my social networks!

    Also visit my web blog Crack Passwords like a Boss With GPU-Based Cluster

    ReplyDelete
  6. It's a shame you don't have a donate button! I'd most certainly donate to this superb blog! I suppose for now i'll settle for bookmarking and adding your RSS feed
    to my Google account. I look forward to fresh updates and will share this website with
    my Facebook group. Talk soon!

    Here is my blog - compare car insurance

    ReplyDelete
  7. Excellent weblog right here! Also your web site quite a bit up fast!
    What web host are you using? Can I am getting your associate
    link to your host? I want my website loaded up as fast as yours lol

    Check out my web page; linkbucks

    ReplyDelete
  8. Very interesting. My additional question: In a special task I use 2 event-triggers, from both events I want to pass the same label to my script or batch-job. Has the query-section to be put twice into the xml-file?

    ReplyDelete
  9. I really enjoyed your article; seeing the example helped a lot. But I've encountered an issue when trying to passing a single character in the event's Event/EventData/Data location. I've set up a "variable" in the ValueQueries section, and reference it in the Actions/Exec/Arguments field of a scheduled task. With test data (none of which was a single character), there was no issue, and the command (a batch file to log the args passed) showed everything was coming thru perfectly. But when real data (a single character (1) ) was in the Data field, the variable replacement doesn't occur, and the "variable" string, "$(EventInfo)", is passed to the batch file, and printed. I've tested with alpha vs. numeric vs. punctuation. Any single character causes the failure, but when the character is doubled, the value comes thru perfectly.

    I would appreciate any advice, as I have Googled, to no avail.

    ReplyDelete
  10. This is really cool. I'm not really down with XML, is this easy to replicate?

    I'd like a "tornado warning" message! "x Days since last unnatural disaster"

    To start, if I could get the values out of event 100 when it occurs, that'd be sweet

    ReplyDelete
  11. This comment has been removed by the author.

    ReplyDelete
  12. Hi Ayman,
    It is an excellent post.

    I am facing an issue where I am unable to pass "Event/EventData/Data[1]" as the parameter to one of my programs. The ideal way would be to pass the value in the array of "Data" elements but I am happy to even pass the whole "Data" object or "EventData" object as well. Unfortunately I am unable to even get hold of those elements or their values.

    My event XML has an Array of 4 Data elements without name attribute to it, sorry couldn't paste the XML file here due to blogspot restrictions.

    ValueQueries element in my Task XML has following values:

    Event/EventData/Data[1]
    Event/EventData/Data[2]
    Event/EventData/Data[4]

    Arguments element in my Task XML has following parameters passed:
    C:\test\myexec.exe $(Number) $(Type) $(Url)

    When I schedule this task, I get following error:
    Error Value: 2147943726

    In case you wish to see the XML files then please let me know your email address.

    Please help and thanks in advance.

    --
    ASR

    ReplyDelete
  13. This comment has been removed by the author.

    ReplyDelete
  14. I too, have the issue with the event having four values with no name. And only the first one gets passed to the command.
    See my excerpt below:

    -
    LogonDenied
    Client Frontend EXCHANGE
    Ntlm
    123.249.56.65



    I want just the forth parameter (IP Address) from the event logs.
    But I am only getting LogonDenied (from first parameter.
    Any ideas how to achieve this?
    I have tried Event/EventData/Data[0] ... Event/EventData/Data[3] , etc.

    ReplyDelete
  15. Dear Ayman, thanks a lot. I wonder why the information is not in the oficcial MS documentation.

    ReplyDelete
  16. very nice, but I am little confused about csscript.exe, is it c# code? and what logic is inside the c# code

    ReplyDelete
  17. I'm using performance monitor to write an event and then use that to trigger a task that needs to pass the data from the "countername" variable to the task. I successfully tested this passing the eventid as an argument, but I can't get it to pass the counter. This is the code I'm using to extract the EventID from event viewer. Event/EventData/Data[@Name='EventID']. But when I change EventID to countername, it just passes a null value. Anyone know how to extract this variable from event viewer?

    ReplyDelete