Quick Links

Files are in a constant state of flux on any operating system. Sometimes they get created, sometimes they get deleted, sometimes they get changed, and those are all normal things for an operating system to do. Sometimes when a file gets changed, it can cause instability in another application that depends on it, such as changing a port number in a properties file, a parameter in a deployment manifest, or even fixing code in production without going through source control.

As part of managing these operating systems, engineers need a way to watch what happens to these critical files and take action when something does. Enter the .NET FileSystemWatcher class and PowerShell.

In .NET and .NET Core, FileSystemWatcher is a class that lives in the System.IO namespace and monitors files. In PowerShell, this can be very useful, especially when it's combined with other PowerShell functions.

Instantiating the FileSystemWatcher

Instantiate this class in PowerShell by running

        $watcher = New-Object System.IO.FileSystemWatcher
    

. Once you do, you'll need to tell it which folder to watch. You do this by setting the Path parameter on the FileSystemWatcher object to the path of whichever folder you want it to watch. If you have a folder on Windows called WatchThisFolder under C:, you would set the FileSystemWatcher to that by running

        $watcher.Path = 'C:WatchThisFolder'
    

.

Because this class is also in .NET Core, you can do all this on a Linux or Mac OS system the same way you would on a Windows system. For example, if you had a folder on Ubuntu Linux called WatchThisFolder under your current user directory, you would run

        $watcher.Path = '/home/ubuntu/WatchThisFolder'
    

.

The rest of the code samples in this article work on either platform without any changes.

Triggering Events from the FileSystemWatcher

Now that you have a new FileSystemWatcher object, you can take a look under the hood and try to figure it out. To see the specific types of file system events that the FileSystemWatcher is watching for, enter

        $watcher | Get-Member -MemberType Event
    

. Get-Member shows everything that the object passed into it contains, and by adding the MemberType filter, you can see a certain category, in this case, events.

Those events are:

  • Changed
  • Created
  • Deleted
  • Disposed
  • Error
  • Renamed

When one or more of these FileSystemWatcher events is detected at the path the object is set to watch, the watcher object raises an external event, for which you can define actions.

Now that the watcher object knows what to watch, and you know what events it's watching for, you must set it to raise an event when an action is detected. Do this by running

        $watcher.EnableRaisingEvents = $true
    

. Think of this flag as an on/off switch for

        $watcher
    

: If the switch is off, nothing is going to happen if any changes are made. You can also tell it to look at files and folders nested under the one set in the path by changing the IncludeSubdirectories flag to true in the same way you did the EnableRaisingEvents flag.

Defining Actions to Take

Once the watcher object is set up, you must give it an action to perform once that change is detected. This can be as simple as writing to a system log, or as drastic as triggering a replacement of the instance with a clean pull of the source code. To get started, you need to define an action block in the code. Here's one just writes to the console:

$action = {
    

$path = $event.SourceEventArgs.FullPath

$name = $event.SourceEventArgs.Name

$changetype = $event.SourceEventArgs.ChangeType

Write-Host "File $name at path $path was $changetype at $(get-date)"

}

You can see this code is pulling variables from the event using the $event variable, which is created for you when the event is created by the watcher object. As soon as the event terminates, the variable also terminates, meaning that it's only going to have data relevant to that event.

Using the Register-ObjectEvent

So, you've got the FileSystemWatcher set up, and you've got actions that you want to take whenever something happens. Right now, these two are separate and don't know about each other. To get them working together, you must register the actions with the event. PowerShell has an entire cmdlet for this---the Register-ObjectEvent cmdlet. To use Register-ObjectEvent, you must pass it three things:

  • The FileSystemWatcher object
  • The event type to trigger the actions on
  • The actions you defined earlier

If you've set up everything the same way it's listed above, it will look something like this: Register-ObjectEvent $watcher 'Event Action' -Action $action. 'Event Action' can be replaced with any event from the FileSystemWatcher, but this will make for a nice demo.

Testing It Out

Now you can test it all out. In this section, you're going to create a new file in the specified directory, see the event action, then disable and deregister the event.

To create a new file and trigger the event, run the below line in PowerShell. This is on Ubuntu Linux, but if you're following along on Windows, replace /home/ubuntu/ with C: and / with .

New-Item -Path "/home/ubuntu/WatchThisFolder/newFile" -ItemType File

As soon as you enter that, the event fires and the action triggers.

File newFile at path /home/ubuntu/WatchThisFolder/newFile was Created at 09/26/2019 20:49:54

It fires every time you create a new file in that directory or anything nested under it, if you enabled the IncludeSubdirectories flag. Now that you know raising the event works, go and turn it off by running $watcher.EnableRaisingEvents = $false, then try creating a new file. The file should be created, but no event shows. This can be very useful if you are debugging and don't want to trigger anything while you are working in the file system.

Once you are done, run Get-EventSubscriber | Unregister-Event to unregister the event from the action. Now even if the EnableRaisingEvents flag is true and the action happens, the action won't fire.