Search code examples
powershelleventswmiwql

How to Subscribe to Windows File Modification Events


I am trying to subscribe to the event of a specific file being modified using WQL with this query:

SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA "CIM_DataFile" AND TargetInstance.Drive="C:" AND TargetInstace.Path="\\test\\filewatching\\"

I can register the event successfully (see output at the bottom) and can see it is bound with the script that I registered with (the consumer).

However, when I modify a file in C:\test\filewatching\ the script does not run.


Here is the code for registering for a File Creation Event in the same folder, and this works:

#WQL

$query = @"
SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA "Cim_DirectoryContainsFile" AND TargetInstance.GroupComponent="Win32_Directory.Name='C:\\test\\filewatching'"
"@
$instanceFilter = ([WMICLASS]"\\$Computername\root\subscription:__EventFilter").CreateInstance()
$instanceFilter.QueryLanguage = 'WQL'
$instanceFilter.Query = $query
$instanceFilter.Name = 'EventFilterNameHere'
$instanceFilter.EventNameSpace = 'root/CIMV2'
$result = $instanceFilter.Put()

# Consumer

$script = 
@"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\test\filewatching\Log.log", 8, True)
objFile.WriteLine "New File Created"
objFile.Close
"@
$instanceConsumer = ([wmiclass]"\\$Computername\root\subscription:ActiveScriptEventConsumer").CreateInstance()
$instanceConsumer.Name = 'ConsumerNameHere'
$instanceConsumer.ScriptingEngine = 'VBScript'
$instanceConsumer.ScriptFilename = '' 
$instanceConsumer.ScriptText = $script
$instanceConsumer.Put()

# Binding

[object]$Filter = (Get-WMIObject -Computername $Computername -Namespace root\Subscription -Class __EventFilter | Sort Name)
[object]$Consumer = (Get-WMIObject -Computername $Computername -Namespace root\Subscription -Class __EventConsumer | Sort Name)

$instanceBinding = ([wmiclass]"\\$Computername\root\subscription:__FilterToConsumerBinding").CreateInstance()
$instanceBinding.Filter = $Filter
$instanceBinding.Consumer = $Consumer
$instanceBinding.Put()

So I know there is nothing wrong with my PowerShell. This makes me think that my query must be wrong.

I have tried a few tweaks of the above InstanceModificationEvent query, but with no success. (these tweaks include stuff like changing C: to C or removing \s from the TargetInstace.Path).


I need this subscription to be permanent and it can't reset if the PC is turned on or off - this is why I am using WQL. If anyone can suggest another way of achieving the same or how I can fix my query I will be eternally grateful!


Output after running the script:

Path          : \\WIN7-IT3\root\subscription:ActiveScriptEventConsumer.Name="Co
                nsumerNameHere"
RelativePath  : ActiveScriptEventConsumer.Name="ConsumerNameHere"
Server        : WIN7-IT3
NamespacePath : root\subscription
ClassName     : ActiveScriptEventConsumer
IsClass       : False
IsInstance    : True
IsSingleton   : False

Path          : \\WIN7-IT3\root\subscription:__FilterToConsumerBinding.Consumer
                ="\\\\WIN7-IT3\\ROOT\\Subscription:ActiveScriptEventConsumer.Na
                me=\"ConsumerNameHere\"",Filter="\\\\WIN7-IT3\\ROOT\\Subscripti
                on:__EventFilter.Name=\"EventFilterNameHere\""
RelativePath  : __FilterToConsumerBinding.Consumer="\\\\WIN7-IT3\\ROOT\\Subscri
                ption:ActiveScriptEventConsumer.Name=\"ConsumerNameHere\"",Filt
                er="\\\\WIN7-IT3\\ROOT\\Subscription:__EventFilter.Name=\"Event
                FilterNameHere\""
Server        : WIN7-IT3
NamespacePath : root\subscription
ClassName     : __FilterToConsumerBinding
IsClass       : False
IsInstance    : True
IsSingleton   : False

Querying WMI subscriptions:

Get-WmiObject -Namespace root\Subscription -Class __Eventfilter
Get-WMIObject -Namespace root\Subscription -Class __EventConsumer
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
Get-WMIObject -Namespace root\Subscription -Class ActiveScriptEventConsumer

Output after querying:

__GENUS          : 2
__CLASS          : __EventFilter
__SUPERCLASS     : __IndicationRelated
__DYNASTY        : __SystemClass
__RELPATH        : __EventFilter.Name="EventFilterNameHere"
__PROPERTY_COUNT : 6
__DERIVATION     : {__IndicationRelated, __SystemClass}
__SERVER         : WIN7-IT3
__NAMESPACE      : ROOT\Subscription
__PATH           : \\WIN7-IT3\ROOT\Subscription:__EventFilter.Name="EventFilterNameHere"
CreatorSID       : {1, 5, 0, 0...}
EventAccess      : 
EventNamespace   : root/CIMV2
Name             : EventFilterNameHere
Query            : SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 
                   "CIM_DataFile" AND TargetInstance.Drive="C:" AND 
                   TargetInstace.Path="\\test\\filewatching\\"
QueryLanguage    : WQL
PSComputerName   : WIN7-IT3

__GENUS          : 2
__CLASS          : ActiveScriptEventConsumer
__SUPERCLASS     : __EventConsumer
__DYNASTY        : __SystemClass
__RELPATH        : ActiveScriptEventConsumer.Name="ConsumerNameHere"
__PROPERTY_COUNT : 8
__DERIVATION     : {__EventConsumer, __IndicationRelated, __SystemClass}
__SERVER         : WIN7-IT3
__NAMESPACE      : ROOT\Subscription
__PATH           : \\WIN7-IT3\ROOT\Subscription:ActiveScriptEventConsumer.Name="ConsumerNameHere"
CreatorSID       : {1, 5, 0, 0...}
KillTimeout      : 0
MachineName      : 
MaximumQueueSize : 
Name             : ConsumerNameHere
ScriptFilename   : 
ScriptingEngine  : VBScript
ScriptText       : Set objFSO = CreateObject("Scripting.FileSystemObject")
                   Set objFile = objFSO.OpenTextFile("c:\test\filewatching\Log.log", 8, True)
                   objFile.WriteLine "New File Created"
                   objFile.Close
PSComputerName   : WIN7-IT3

__GENUS                 : 2
__CLASS                 : __FilterToConsumerBinding
__SUPERCLASS            : __IndicationRelated
__DYNASTY               : __SystemClass
__RELPATH               : __FilterToConsumerBinding.Consumer="\\\\WIN7-IT3\\ROOT\\Subscription:ActiveS
                          criptEventConsumer.Name=\"ConsumerNameHere\"",Filter="\\\\WIN7-IT3\\ROOT\\Su
                          bscription:__EventFilter.Name=\"EventFilterNameHere\""
__PROPERTY_COUNT        : 7
__DERIVATION            : {__IndicationRelated, __SystemClass}
__SERVER                : WIN7-IT3
__NAMESPACE             : ROOT\Subscription
__PATH                  : \\WIN7-IT3\ROOT\Subscription:__FilterToConsumerBinding.Consumer="\\\\WIN7-IT
                          3\\ROOT\\Subscription:ActiveScriptEventConsumer.Name=\"ConsumerNameHere\"",F
                          ilter="\\\\WIN7-IT3\\ROOT\\Subscription:__EventFilter.Name=\"EventFilterName
                          Here\""
Consumer                : \\WIN7-IT3\ROOT\Subscription:ActiveScriptEventConsumer.Name="ConsumerNameHer
                          e"
CreatorSID              : {1, 5, 0, 0...}
DeliverSynchronously    : False
DeliveryQoS             : 
Filter                  : \\WIN7-IT3\ROOT\Subscription:__EventFilter.Name="EventFilterNameHere"
MaintainSecurityContext : False
SlowDownProviders       : False
PSComputerName          : WIN7-IT3

__GENUS          : 2
__CLASS          : ActiveScriptEventConsumer
__SUPERCLASS     : __EventConsumer
__DYNASTY        : __SystemClass
__RELPATH        : ActiveScriptEventConsumer.Name="ConsumerNameHere"
__PROPERTY_COUNT : 8
__DERIVATION     : {__EventConsumer, __IndicationRelated, __SystemClass}
__SERVER         : WIN7-IT3
__NAMESPACE      : ROOT\Subscription
__PATH           : \\WIN7-IT3\ROOT\Subscription:ActiveScriptEventConsumer.Name="ConsumerNameHere"
CreatorSID       : {1, 5, 0, 0...}
KillTimeout      : 0
MachineName      : 
MaximumQueueSize : 
Name             : ConsumerNameHere
ScriptFilename   : 
ScriptingEngine  : VBScript
ScriptText       : Set objFSO = CreateObject("Scripting.FileSystemObject")
                   Set objFile = objFSO.OpenTextFile("c:\test\filewatching\Log.log", 8, True)
                   objFile.WriteLine "New File Created"
                   objFile.Close
PSComputerName   : WIN7-IT3

Solution

  • Stumbled across this page soon after posting this question, from which I worked out that the query I need is:

    SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'CIM_DataFile' AND TargetInstance.Name='c:\\test\\filewatching\\tester.txt'

    Notice that contrary to several other posts online, what worked for me was actually to omit the Path and Drive parts of TargetInstance and only included the full path and name for the file I want to watch.

    This works for me because I will only be watching 1 specific file. If you need to watch multiple files using this method you will need to register multiple subscriptions.

    The full code for registering a File Modification watcher:

    #WQL
    
    $query = @"
    SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'CIM_DataFile' AND TargetInstance.Name='c:\\test\\filewatching\\tester.txt'
    "@
    $instanceFilter = ([WMICLASS]"\\$Computername\root\subscription:__EventFilter").CreateInstance()
    $instanceFilter.QueryLanguage = 'WQL'
    $instanceFilter.Query = $query
    $instanceFilter.Name = 'EventFilterNameHere'
    $instanceFilter.EventNameSpace = 'root/CIMV2'
    $result = $instanceFilter.Put()
    
    # Consumer
    
    $script = 
    @"
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile("c:\test\filewatching\Log.log", 8, True)
    objFile.WriteLine "New File Created"
    objFile.Close
    "@
    $instanceConsumer = ([wmiclass]"\\$Computername\root\subscription:ActiveScriptEventConsumer").CreateInstance()
    $instanceConsumer.Name = 'ConsumerNameHere'
    $instanceConsumer.ScriptingEngine = 'VBScript'
    $instanceConsumer.ScriptFilename = '' 
    $instanceConsumer.ScriptText = $script
    $instanceConsumer.Put()
    
    # Binding
    
    [object]$Filter = (Get-WMIObject -Computername $Computername -Namespace root\Subscription -Class __EventFilter | Sort Name)
    [object]$Consumer = (Get-WMIObject -Computername $Computername -Namespace root\Subscription -Class __EventConsumer | Sort Name)
    
    $instanceBinding = ([wmiclass]"\\$Computername\root\subscription:__FilterToConsumerBinding").CreateInstance()
    $instanceBinding.Filter = $Filter
    $instanceBinding.Consumer = $Consumer
    $instanceBinding.Put()