2

I am a novice when it comes to scripting so please bear with me. I am trying to create a script that will monitor a bait file that is added to all file shares on a server. When the script sees that the file was modified it will block access to the user that made the modification and send an email. The script seems to work ok other than the FileSystemWatcher. It will only monitor the last share. I have seen a similar post on here but was getting confused with the answer. Can someone please help me with the task of creating a FileSystemWatcher for each bait file? I would also like any input as to how I might improve upon the script in other ways. Your help is greatly appreciated.

$to = "joe@blow.com"
$File = "test.txt" 
$FilePath = "C:\temp"
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider

## SEND MAIL FUNCTION
function sendMail($s, $to) {
    $smtpServer = "mail.nowhere.com"
    $smtpFrom = "alert@nowhere.com"
    $smtpTo = $to

    $messageSubject = $s[0]
    $message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
    $message.Subject = $messageSubject
    $message.IsBodyHTML = $false
    $message.Body = $s[1]

    $smtp = New-Object Net.Mail.SmtpClient($smtpServer)
    $smtp.Send($message)
}

## Get a list of shares and Perform tasks on each location.
$cryptopaths = Get-WmiObject -Class win32_share -filter "Type=0 AND name like '%[^$]'" | ForEach ($_.Path) {
    $cryptopath = $_.Path

    ## Copy the bait file to the share location
    Copy $FilePath\$File $cryptopath\$File -Force

    ##Get files hash
    Try {
        $Origin = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$FilePath\$File")))
        $Copy = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$CryptoPath\$File")))

    }
     ##Error on reading hash
        Catch {
            echo "error reading $CryptoPath\$File" 
        }

    ## If files don't match, then Send messaged and quit
    if (Compare-Object $Origin $Copy){
        ## files don't match
        $subject = "Error logged on $CryptoPath\$File by $env:username on $env:computername"
        $body = "The original file does not match the witness file.  Aborting monitor script."
        $email =@($subject,$body)
        sendMail -s $email -to "ben22@nowhere.com"
        Exit
        }




    ## CREATE WATCHER ON DIRECTORY
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $CryptoPath
    $watcher.Filter = $File
    $watcher.IncludeSubdirectories = $false
    $watcher.EnableRaisingEvents = $false
    $watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor [System.IO.NotifyFilters]::FileName
}


## Execute Watcher
while($TRUE){
    $result = $watcher.WaitForChanged([System.IO.WatcherChangeTypes]::Changed `
        -bor [System.IO.WatcherChangeTypes]::Renamed `
        -bor [System.IO.WatcherChangeTypes]::Deleted `
        -bor [System.IO.WatcherChangeTypes]::Created, 1000);
    if ($result.TimedOut){
        continue;
    }

    if ($result.Name -eq $File) {
        ### Make sure the files do not match
        try {
            $FileCheck = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$CryptoPath\$File")))
            if (Compare-Object $Origin $FileCheck){
                ## files don't match
                $body = "Witness file $FilePath\$File on $env:computername has been modified."
                }
        }
        catch {
            ## file deleted
            $body = "Witness file $FilePath\$File on $env:computername has been deleted"
            }
        finally {
            ## Deny owner of changed file access to shares and disconnect their open sessions. Send email alert
            Get-Acl "$CryptoPath\$File" | foreach ($_.Owner) {
            Get-SmbShare | Block-SmbShareAccess –AccountName $_.Owner
            Close-SmbSession –ClientUserName $_.Owner
            }
            $subject = "EMERGENCY ON FILE SERVER -- $FilePath\$File by $env:username on $env:computername"
            $email =@($subject,$body)
            sendMail -s $email -to "ben22@nowhere.com"
            sendMail -s $email -to "5555555555@txt.bell.ca"
            Exit

        }

    }

}
Community
  • 1
  • 1

1 Answers1

2

The problem is that you create FileSystemWatcher instances in a loop (ForEach ($_.Path) {}), but you assign them to the same variable $watcher, overwriting the previous reference each time. Once outside the loop, you work with the $watcher variable, which references the last FileSystemWatcher instance you created and that's why you are receiving notifications for the last file only.

To get this working, you should use a type that allows storing multiple references -- that is, an array. Example:

$watchers = @();
...
$watcher = New-Object System.IO.FileSystemWatcher;
...
$watchers += $watcher;

Also, I would propose to use event handler/callback/delegate-based approach instead of waiting for a change using WaitForChanged(), because waiting for multiple file system watchers would call for a parallelized solution (well, ideally). Use Register-ObjectEvent to register an event handler and see this example in particular: http://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b.

PowerShellPack also has a Start-FileSystemWatcher cmdlet that wraps this all up nicely, but I'm not sure about the status of PowerShellPack in general. It should be part of the Windows 7/8 Resource Kit, though.

PeterK
  • 3,667
  • 2
  • 17
  • 24