System.IO.FileSystemWatcher only notifies once on edited files
Asked Answered
H

2

6

The script uses file system watcher to monitor a honeypot folder, and report back any changes (edit, rename, delete or create), then performs some actions.

The actions work fine when creating, renaming and deleting.

But when editing, I can only make the script trigger the actions once. So for example, if a test device tries to edit a file on honeypot folder, the actions are triggered. But is the same device tries to edit again the same file or a different file, the watcher for editing seems to not work because the actions are not triggered.

So I tried to reset the script every 5 minutes via task scheduler (start the script every 5 minutes), but still same results.

Here's the code:

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "\\vmserver2\_Do_Not_Delete_Or_Rename"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true

### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = {
    $path = $Event.SourceEventArgs.FullPath
    $changeType = $Event.SourceEventArgs.ChangeType

    $logline = "$(Get-Date), $changeType, $path"
    #Add-content "D:\log.txt" -value $logline
    #write-host $logline

    $targetdevice = Get-SmbOpenFile |
        select clientusername, clientcomputername, path |
        where {$_.Path -like  'E:\Data\Archive\_Do_Not_Delete_Or_Rename' }

    $targetIP = $targetdevice.clientcomputername
    $targetUser = $targetdevice.clientusername

    Send-ToEmail -email "[email protected]" $targetIP
    $targetUser
}

### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcher "Created" -Action $action
Register-ObjectEvent $watcher "Changed" -Action $action
Register-ObjectEvent $watcher "Deleted" -Action $action
Register-ObjectEvent $watcher "Renamed" -Action $action
while ($true) {sleep 5}

I'm pretty new to powershell so I don't understand while the watchers for the rest of events work and only editing does not work.

Hephzibah answered 7/7, 2018 at 13:56 Comment(2)
What version of PowerShell are you using? I was unable to re-create the issue myself using PowerShell 5. You can get information by copy and pasting the value of the $PSVersionTable variable.Metathesize
sorry I got no longer access to this server, the client had a windows 2012R2 updated ok without issues. I could use the script on a windows 10 device,but on a the server the script crashes. Hope that helpsHephzibah
J
4

Your core logic is sound.

If you simplify your action block to only do the Write-host part, it should always work. I believe your problem is that on the second call, you get a terminating error that you didn't catch and in return, it stop the events from triggering.

Try replacing your action scriptblock with the following

$action = {
  try {
    $path = $Event.SourceEventArgs.FullPath
    $changeType = $Event.SourceEventArgs.ChangeType

    $logline = "$(Get-Date), $changeType, $path"
    #Add-content "D:\log.txt" -value $logline
    #write-host $logline

    $targetdevice = Get-SmbOpenFile |
        select clientusername, clientcomputername, path |
        where {$_.Path -like  'E:\Data\Archive\_Do_Not_Delete_Or_Rename' }

    $targetIP = $targetdevice.clientcomputername
    $targetUser = $targetdevice.clientusername

    Send-ToEmail -email "[email protected]" $targetIP
    $targetUser
  }
  catch {
      Write-Host 'an error was thrown :(... Fortunately, it was caught.'
  }
}

This should correct your issue of the changed event triggering only once.

Here's an example using 3 watchers that check for a file change over the same directory. You will notice that after the counter reached 5, only 2 of the 3 watchers continue to work properly. The one that do not produce any errors watcherNoError and the one that produce a terminating error but was caught in a Try catch watcherErrorsTryCatch.

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$PathToWatch = "\\127.0.0.1\c$\_"

$watcherNoError = New-Object System.IO.FileSystemWatcher -Property @{Path = $PathToWatch;Filter = '*.*';IncludeSubdirectories=$true;EnableRaisingEvents = $true}
$watcherWithErrors = New-Object System.IO.FileSystemWatcher -Property @{Path = $PathToWatch;Filter = '*.*';IncludeSubdirectories=$true;EnableRaisingEvents = $true}
$watcherErrorsTryCatch = New-Object System.IO.FileSystemWatcher -Property @{Path = $PathToWatch;Filter = '*.*';IncludeSubdirectories=$true;EnableRaisingEvents = $true}

$Global:Counter = @{
    watcherNoError = 0 
    watcherWithErrors = 0 
    watcherErrorsTryCatch = 0
}
### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = {

    $path = $Event.SourceEventArgs.FullPath
    $changeType = $Event.SourceEventArgs.ChangeType



    Switch ($Event.MessageData.Name) {
        "NoErrors" {
            $Global:Counter.watcherNoError +=1
            $count = $Global:Counter.watcherNoError
        }
        "WithErrors" {
            $Global:Counter.watcherWithErrors +=1
            $count = $Global:Counter.watcherWithErrors
            if ($count -eq 5) {
                Write-Host 'A terminated errow will be thrown...' -ForegroundColor DarkMagenta
                Throw 'oh no !'
            }

        }
         "WithErrorsTryCatch"  {
            $Global:Counter.watcherErrorsTryCatch +=1
            $count = $Global:Counter.watcherErrorsTryCatch
            if ($count -eq 5) {
                try {
                    Throw 'oh no !'
                }
                catch {
                    Write-Host 'error was caught... You are safe ;)' -ForegroundColor Green
                }
            }

        }
    }

    $logline = "Count: $Count - $($event.MessageData.Name): $changeType, $path" 
    write-host $logline -ForegroundColor $Event.MessageData.ForegroundColor


} 

### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcherNoError "Changed" -Action $action -MessageData @{Name='NoErrors';ForegroundColor='Yellow'}
Register-ObjectEvent $watcherWithErrors "Changed" -Action $action -MessageData @{Name='WithErrors';ForegroundColor='DarkMagenta'}
Register-ObjectEvent $watcherErrorsTryCatch "Changed" -Action $action -MessageData @{Name='WithErrorsTryCatch';ForegroundColor='Green'}

while ($true) {sleep 5}




$action = {
  try {
    $path = $Event.SourceEventArgs.FullPath
    $changeType = $Event.SourceEventArgs.ChangeType

    $logline = "$(Get-Date), $changeType, $path"
    #Add-content "D:\log.txt" -value $logline
    #write-host $logline

    $targetdevice = Get-SmbOpenFile |
        select clientusername, clientcomputername, path |
        where {$_.Path -like  'E:\Data\Archive\_Do_Not_Delete_Or_Rename' }

    $targetIP = $targetdevice.clientcomputername
    $targetUser = $targetdevice.clientusername

    Send-ToEmail -email "[email protected]" $targetIP
    $targetUser
  }
  catch {
      Write-Host 'an error was thrown :(... Fortunately, it was caught.'
  }
}

VSCode output of previous sample

Additional note: Even if it was not the solution to your problem, you should still put a try / catch in that Action scriptblock as a terminating error will stop further processing on the next changes.

Jeer answered 31/1, 2019 at 3:44 Comment(0)
H
3

Either you'll need to use

Remove-Event $watcher "Changed"

at the end of the $Action scriptblock OR use

Unregister-Event $watcher "Changed"
Register-ObjectEvent $watcher "Changed -Action $action

at the end of the $Action scriptblock.

Honeymoon answered 8/7, 2018 at 5:23 Comment(1)
hi tested both solutions, and I'm afraid still only one shot when editing.The script seems to only allow actions when editing is detected once, but not twice, etcHephzibah

© 2022 - 2024 — McMap. All rights reserved.