Register-ObjectEvent Event Subscription Uptime - powershell

We have a data dump come from an external entity on a daily basis. Once we receive the file, a Object Event Listener triggers, moves the file into two separate locations (one live, one backup) and then calls a MS SQL stored procedure to import the file into the database.
The process and my script seem to function perfectly fine. The file moves, and the SQL is executed. However, every morning when I check to see if it triggered, nothing triggered. I check for event-subscribers by calling Get-EventSubscriber and there are no listeners registered. Once I register the listeners and move the file into the input location, everything runs fine.
I understand that the Event Subscriptions do not persist through reboots, but how long do they stay open? is it possible that something is closing them? or do they time out?
$watchFolder = "\\server\c$\path\to\inbound\"
$filter = "*.txt"
$fsw = New-Object IO.FileSystemWatcher $watchFolder, $filter -Property #{
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileMonitor -Action {
$filePath = $Event.SourceEventArgs.FullPath
$fileName = $Event.SourceEventArgs.Name
Write-Host "Source File: $fileName"
$success = copyFile($fileName, $filePath) #custom function to copy file to multiple destinations
if ($success) {
Write-Host "Starting SQL_JOB_NAME Job"
Set-Location SQLSERVER:\SQL\server\DEFAULT\Databases\msdb\
$execute_import = "EXEC dbo.sp_start_job N'SQL_JOB_NAME';"
Invoke-Sqlcmd -SuppressProviderContextWarning -Query $execute_import
} else {
Write-Host "Something failed along the way: `r`n"
}
}

As silly as it seems, I learned here that calling Register-ObjectEvent if the PowerShell process is closed, the ObjectEvent is also unregistered.
Because the nature of the project required us to have an always-on file listener, we decided that Powershell was not the correct implementation for us.
Instead we implemented a Windows service using the .NET libraries, which are the same used here in PowerShell.
Thanks to PetSerAl for his prompt answer.

Related

How do you automatically clean up all event subscriptions made in a PowerShell script when that script exits?

I'm setting up a FileSystemWatcher to watch a file for changes. This works. I also want to keep the script that sets this up running until the user manually terminates the script. This also works. However, I'd also like the event subscription on the FileSystemWatcher to get automatically cleaned up when the script exits (either normally or abnormally). This doesn't work, because event subscriptions are part of the session, and the script doesn't have its own session.
I tried creating a new session object inside the script and using it for the watcher setup and event registration, which seemed to do a great job cleaning up the event subscription on script termination, but it also seemed to cause all my console activity to get swallowed up in that child session.
How can I make it so that whenever the script exits (normally or abnormally), the event subscription is cleaned up automatically? (And doing this while maintaining visibility of my console output.)
In case the context matters, this is a simple ZIP file build script. I'm trying to add a "watch mode" to it so that when the ZIP is updated by another app, the ZIP is decompressed back to the folder from which it was created. So this script is meant to be executed from a PowerShell command line that remains active and is possibly used for other things before and after this script runs. In other words, the mighty hammer of Get-EventSubscriber | Unregister-Event is potentially a little too mighty, in addition to being another command that the script user would have to invoke on their own.
This is a condensed version of my script:
$watcher = New-Object System.IO.FileSystemWatcher ".", $fileName -Property #{
NotifyFilter = [IO.NotifyFilters]::LastWrite
}
Register-ObjectEvent $watcher -EventName Changed -Action {
Write-Host "File change detected."
# other things, scripts, etc
}
$watcher.EnableRaisingEvents = $true
Write-Host "Press Ctrl+C to stop watching the file."
while ($true)
{
if ([Console]::KeyAvailable)
{
$keyInfo = [Console]::ReadKey($true)
if ($keyInfo.Modifiers -eq [ConsoleModifiers]::Control -and $keyInfo.Key -eq [ConsoleKey]::C)
{
Exit
}
}
else
{
Start-Sleep 0.5
}
}
Try the following:
$watcher = New-Object System.IO.FileSystemWatcher $pwd, $fileName -Property #{
NotifyFilter = [IO.NotifyFilters]::LastWrite
}
# Register with a self-chosen source identifier.
$evtJob = Register-ObjectEvent -SourceIdentifier fileWatcher $watcher -EventName Changed -Action {
Write-Host "File change detected."
# other things, scripts, etc
}
$watcher.EnableRaisingEvents = $true
Write-Host "Press Ctrl+C to stop watching the file."
try {
# This blocks execution indefinitely while allowing
# events to be processed in the -Action script block.
# Ctrl-C aborts the script by default, which will execute
# the `finally` block.
Wait-Event -SourceIdentifier fileWatcher
}
finally {
# Clean up the event job, and with it the event subscription.
# Note: If the -Action script block produces output, you
# can collect it with Receive-Job first.
$evtJob | Remove-Job -Force
}

VSCode & Powershell: How to stop script and unregister object?

I have a snippet that works fine. It registers an objectevent as follows (snippet):
Register-ObjectEvent $Watcher -EventName Created -SourceIdentifier FileCreated -Action {
It works fine, but I can't seem to get the script to fully stop and unregister. When I use "normal" methods to stop code (listed below), when I rerun the code (via F5) I get error:
Register-ObjectEvent : Cannot subscribe to the specified event. A subscriber with the source identifier 'FileCreated' already exists.
The only way I can unregister it (so far) is to terminate the powershell.exe process, which also kills the powershell run time within VSCode (throwing errors, forcing a manual restart).
I start the code by hitting F5
To stop the code, I have tried:
Shift-F5
Ctrl-C (in the powershell terminal area)
Nothing works except as described above.
What am I missing?
For debugging purposes only, where you often force-stop your script, there are 2 options. Either run the unregister-Event line manually so the event is unregistered or do add something like that near the top f your script. (at least before the Register-ObjectEvent )
Unregister-Event -SourceIdentifier FileCreated -EA 0
-EA 0 is the same as -ErrorAction SilentlyContinue and will prevent the script from throwing an error when you are on a new session and the event was never registered yet.
Note: This should not be in your production script and is only meant as a debugging help. (If you want to keep it on hand, I'd say comment it out at least on the prod. version of the script)
Your production version of the script should implement Unregister-Event properly though, something more akin to:
$FileWatcherReg = #{
InputObject = $Watcher
EventName = 'Created'
SourceIdentifier = 'FileCreated'
MessageData = #{WatchQueue = $WatchQueue; Timer = $WatchTimer }
Action = {
if ($null -ne $event) {
Write-Host "Path: $($Event.SourceArgs.FullPath) - ($($Event.Timegenerated))"
}
}
}
Register-ObjectEvent #FileWatcherReg
while ($true) {
Start-Sleep -Milliseconds 100
# Logic to exit the loop at some point.
}
# Exit script logic ...
Unregister-Event -SourceIdentifier FileCreated
Similar to Sage's example above, is this larger example. It structures the event clearly, and shows how by keeping the script running, you can then cancel the script, which will trigger the cleanup block.
So there is something to be said for this approach. (However, I continue to see the whole Register-ObjectEvent as kind of a TSR... your code stays resident... forever... waiting for the relevant event....)
The example:
https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/using-filesystemwatcher-correctly-part-2#

Windows Task Scheduler Powershell Script complete and restart

I have created a script that uses a FileSystemWatcher and creates a job. When a file is added to a folder it
Renames the newly added file
Moves the file to a new destination
Begins a downstream workflow
Once it’s done it ends the Job.
What I want to do is use Windows Task Scheduler to check that when the job completes to immediately run again. This way if one person drops a folder, that another person and can do the same.
ALSO If there is another way to have this work I am happy to use it.
I have tried to schedule the task to run but the lowest increment is 1 minute
I expect to have the filesystemwatcher running all the time. And once the script has finished I want it to start again.
You don't have to start job when the event (file creation) occur, you can directly move the file and do whatever you want using action parameter.
Also, you don't have to restart the event, it is will keep running unless you manually unregister it, so you don't have to use task scheduler at all.
Here's the code
# set the folder you want to watch
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "$home/Desktop/Folder"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
# start job when event is detected
$action = {
Start-Job -ScriptBlock {
# Step 1&2 (You can move the file and rename it with one step)
Move-Item $Event.SourceEventArgs.FullPath -Destination $newPath
} | Wait-Job
}
# Register and start the FileSystemWatcher
Register-ObjectEvent -InputObject $watcher -EventName Created -SourceIdentifier FileCreated -Action $action
Note
I used Start-Job in the code; However, you can omit it and put the code exactly in $action.
This code will register new FileSystemWatcher and will keep monitoring the folder for newly created file then move it to a new location.
To stop the watcher
Unregister-Event -SourceIdentifier FileCreated
Look here for more information about tracking changes to a folder using PowerShell

Register-ObjectEvent of System.IO.FileSystemWatcher not working as expected

I use a powershell script to watch a folder and get notification when a new file is created.
The script is the following
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\Users\Desktop\test\"
$watcher.Filter = "*.nrrd"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action =
{
$path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
Write-Host "The file '$path' was $changeType at '$(Get-Date)'" -fore green
}
### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcher "Created" -Action $action
while ($true) {sleep 5}
It works fine even if I create the file manually or with a .bat, PHP or even python.
The only way it does not work is when another user (or session?) is creating the file.
For example:
I created a folder on my computer, shared it with other computer over LAN.
When the file is created by me (or one of my script), the script is well working and the sentence is displayed.
If I use another computer on the LAN to add a file in the same folder, it does not work. Nothing is displayed.
(But if I copy/past the file created by the other user, the script is working, so is not a issue with the file itself).
I found this and this but the posts are outdated.
Do you have any workaround or idea to solve this?
UPDATE
The file created by hand (or copy/pasted) has owner DEBIAN\root but the original one have Unix User\www-data.
Maybe powershell (windows) is not able to "catch" this kind of user that's maybe why the event is not even created?
Try giving the shared folder's UNC path
$watcher.Path = "\\server\share"

DotNetZip ExtractProgress events in PowerShell

I'm extracting a ZIP file in PowerShell by using the DotNetZip library, and I want to display progress. I'm using the following code:
try {
$zip = [Ionic.Zip.ZipFile]::Read($ZipFileName)
Register-ObjectEvent `
-InputObject $zip `
-EventName ExtractProgress `
-SourceIdentifier ExtractProgress `
-Action {
[Console]::Beep()
Write-Host $Sender
Write-Host $SourceEventArgs
} | Out-Null
$zip.ExtractAll($Destination, 'OverwriteSilently')
}
finally {
Unregister-Event -SourceIdentifier ExtractProgress
$zip.Dispose()
}
My problem is that I don't see the events (no beep, no Write-Host) until the very end. I'm expecting to see progress events during the process.
Initially, I thought it was because Register-ObjectEvent queued the events, but the PowerShell help says that -Action is invoked immediately, without the event being queued.
If I write the equivalent code in a C# console application, then I see the progress events as each file is extracted, as expected, which means that (as far as I can tell) DotNetZip is doing the right thing. Note that the events are raised on the same thread that called ExtractAll.
What am I doing wrong?
(Windows 7 x64, PowerShell 2.0, configured to use .NET 4.0)