Search code examples
.netpowershellwindows-7wmi

How to Debug/Register a Permanent WMI Event Which Runs a Script on File Creation


I am trying to register a permanent WMI event on my machine, where if a file is created in the folder C:\test, a script should run.

I have successfully managed to register similar events where the action is to log a line to a file, but for some reason when trying to execute a script I just get no response.

Here is the query I am using:

$query=@"
Select * from __InstanceCreationEvent within 10
where targetInstance isa 'Cim_DirectoryContainsFile'
and targetInstance.GroupComponent='Win32_Directory.Name="C:\\\\test"'
"@

I have tried registering the event in a few different ways with no success, and was hoping someone could help me understand where I am going wrong.

Take for example the following script:

$computer = $env:COMPUTERNAME
$filterNS = "root\cimv2"
$wmiNS = "root\subscription"
$query = @"
Select * from __InstanceCreationEvent within 1
where targetInstance isa 'Cim_DirectoryContainsFile' 
and targetInstance.GroupComponent = 'Win32_Directory.Name="c:\\\\test"'
"@
$filterName = "NewFileFilter"
$scriptFileName = "C:\test\test.vbs"

$filterPath = Set-WmiInstance -Class __EventFilter `
    -ComputerName $computer -Namespace $wmiNS -Arguments `
    @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";
    Query=$query}

$consumerPath = Set-WmiInstance -Class ActiveScriptEventConsumer `
    -ComputerName $computer -Namespace $wmiNS `
    -Arguments @{name="MyConsumer"; ScriptFileName=$scriptFileName;
    ScriptingEngine="VBScript"}

Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `
    -Namespace $wmiNS -arguments @{Filter=$filterPath; Consumer=$consumerPath} |
    out-null

Where test.vbs is a one-liner: MsgBox("Hello!")

After running this, I can call the following:

Get-WmiObject -Namespace root\Subscription -Class __Eventfilter

Get-WMIObject -Namespace root\Subscription -Class __EventConsumer

Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding

And get the below output, which shows me that the event has indeed been registered:

__GENUS          : 2
__CLASS          : __EventFilter
__SUPERCLASS     : __IndicationRelated
__DYNASTY        : __SystemClass
__RELPATH        : __EventFilter.Name="NewFileFilter"
__PROPERTY_COUNT : 6
__DERIVATION     : {__IndicationRelated, __SystemClass}
__SERVER         : WIN7-IT3
__NAMESPACE      : ROOT\Subscription
__PATH           : \\WIN7-IT3\ROOT\Subscription:__EventFilter.Name="NewFileFilter"
CreatorSID       : {1, 5, 0, 0...}
EventAccess      : 
EventNamespace   : root\cimv2
Name             : NewFileFilter
Query            : Select * from __InstanceCreationEvent within 1
                   where targetInstance isa 'Cim_DirectoryContainsFile' 
                   and targetInstance.GroupComponent = 
                   'Win32_Directory.Name="c:\\\\test"'
QueryLanguage    : WQL
PSComputerName   : WIN7-IT3

__GENUS          : 2
__CLASS          : ActiveScriptEventConsumer
__SUPERCLASS     : __EventConsumer
__DYNASTY        : __SystemClass
__RELPATH        : ActiveScriptEventConsumer.Name="MyConsumer"
__PROPERTY_COUNT : 8
__DERIVATION     : {__EventConsumer, __IndicationRelated, __SystemClass}
__SERVER         : WIN7-IT3
__NAMESPACE      : ROOT\Subscription
__PATH           : \\WIN7-IT3\ROOT\Subscription:ActiveScriptEventConsumer.Name="MyCon
                   sumer"
CreatorSID       : {1, 5, 0, 0...}
KillTimeout      : 0
MachineName      : 
MaximumQueueSize : 
Name             : MyConsumer
ScriptFilename   : C:\test\test.vbs
ScriptingEngine  : VBScript
ScriptText       : 
PSComputerName   : WIN7-IT3

__GENUS                 : 2
__CLASS                 : __FilterToConsumerBinding
__SUPERCLASS            : __IndicationRelated
__DYNASTY               : __SystemClass
__RELPATH               : __FilterToConsumerBinding.Consumer="ActiveScriptEventConsum
                          er.Name=\"MyConsumer\"",Filter="__EventFilter.Name=\"NewFil
                          eFilter\""
__PROPERTY_COUNT        : 7
__DERIVATION            : {__IndicationRelated, __SystemClass}
__SERVER                : WIN7-IT3
__NAMESPACE             : ROOT\Subscription
__PATH                  : \\WIN7-IT3\ROOT\Subscription:__FilterToConsumerBinding.Cons
                          umer="ActiveScriptEventConsumer.Name=\"MyConsumer\"",Filter
                          ="__EventFilter.Name=\"NewFileFilter\""
Consumer                : ActiveScriptEventConsumer.Name="MyConsumer"
CreatorSID              : {1, 5, 0, 0...}
DeliverSynchronously    : False
DeliveryQoS             : 
Filter                  : __EventFilter.Name="NewFileFilter"
MaintainSecurityContext : False
SlowDownProviders       : False
PSComputerName          : WIN7-IT3

However, when I create a file in C:\test, nothing happens. The most frustrating this about this, is that I can create a similar event which uses the LogFileEventConsumer and this works perfectly (a line is logged to the specified file when a new file is added to C:\test).

Does anyone know what is going on here, or how I can debug this effectively? I have not found any way to get the binding to somehow output errors or log any kind of details about what is going on, and have not been able to work out what CIM WMI Studio is meant to do (it doesn't seem to work at all for me).

Any help is much appreciated, and please let me know if I can post any more details like any other code I have tried or any logs - thanks.


Solution

  • I was able to achieve this using the following script :

    $query = @"
    SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA "Cim_DirectoryContainsFile" AND TargetInstance.GroupComponent="Win32_Directory.Name='C:\\test'"
    "@
    
    #Set up hash table for splatting
    $wmiParams = @{
        Computername = $env:COMPUTERNAME
        ErrorAction = 'Stop'
        NameSpace = 'root\subscription'
    }
    
    # Filter
    #Creating a new event filter
    $wmiParams.Class = '__EventFilter'
    $wmiParams.Arguments = @{
        Name = 'WatchFiles'
        EventNamespace = 'root\CIMV2'
        QueryLanguage = 'WQL'
        Query = $query 
    }
    $filterResult = Set-WmiInstance @wmiParams
    
    # Consumer
    $wmiParams.Class = 'ActiveScriptEventConsumer'
    $wmiParams.Arguments = @{
        KillTimeout = 0
        MachineName = $env:COMPUTERNAME
        ScriptingEngine = 'VBScript'
        ScriptText = 
    @"
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile("c:\test\Log.log", 8, True)
    objFile.WriteLine "hellohellohellohellohellohello"
    objFile.Close
    "@
        ScriptFileName = $null
        Name = 'ActiveScriptEventConsumer'
    }
    $consumerResult = Set-WmiInstance @wmiParams
    
    # Binding
    $wmiParams.Class = '__FilterToConsumerBinding'
    $wmiParams.Arguments = @{
        Filter = $filterResult
        Consumer = $consumerResult
    }
    $bindingResult = Set-WmiInstance @wmiParams
    

    Notice the way I have formatted the $query filter, and that the only character that needs escaping is the \, due to the Here-String (@""@) I used.

    Some more things to note from here :

    When creating VBScript or JScript scripts to use with ActiveScriptEventCosumer, you need to be aware of some limitations:

    •ActiveScriptEventConsumer doesn't use the Windows Script Host (WSH), which is widely used in system administration scripts. This means that you can not use the WScript object or any of its properties and methods (like WScript.CreateObject, WScript.Sleep etc.).

    •The script can not generate any screen output, which means that you can not use the VBScript MsgBox function.

    •The script does not have network access.

    •The script can't use any user specific data, such as environment variables or network shares.

    This could explain the above failure, as the script I was referring to contained a MsgBox(), which cannot run in these circusmtances.