Watching the file system
We saw how to watch for WMI events http://msmvps.com/blogs/richardsiddaway/archive/2009/11/07/powershell-wmi-events.aspx. In this post we will look at watching the file system. This time we will use the .NET System.IO.FileSystemWatcher object which means we use Register-ObjectEvent instead of Register-WmiEvent.
| 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015
| ## folder to watch $folder = "C:\test" ## watch all files $filter = "*" ## events to watch $events = @("Changed", "Created", "Deleted", "Renamed") ## ## create watcher $fsw = New-Object -TypeName System.IO.FileSystemWatcher -ArgumentList $folder, $filter $fsw.IncludeSubDirectories = $true ## ## register events foreach ($event in $events){ Register-ObjectEvent -InputObject $fsw -EventName $event -SourceIdentifier "File System $event" } |
We can start by defining the folder we want to watch and which files. In this case I want to watch all files. We can restrict it to certain files e.g. $filter = “*.txt”
Wildcards work in the normal way for this filter. We could even restrict to a single file. The events we are interested in are defined.
After creating a System.IO.FileSystemWatcher object using the folder and filter in the construction we set the IncludeSubDirectories property so we are watching the whole path.
Finally we need to register an event for each event we are interested in. Notice how the SourceIdentifier changes to identify the particular event.
We can see the Eventsubscribers we have created.
PS> Get-EventSubscriber | Select SubscriptionId, EventName, SourceIdentifier | ft -a
SubscriptionId EventName SourceIdentifier
-------------- --------- ----------------
3 Changed File System Changed
4 Created File System Created
5 Deleted File System Deleted
6 Renamed File System Renamed
We now need to perform some actions on the files – create, change, rename, delete that we are monitoring
PS> Get-Event | group SourceIdentifier
Count Name Group
----- ---- -----
7 File System Changed {System.Management.Automation.PSEventArgs, System.Management.Automation.PSEventArgs,...
1 File System Renamed {System.Management.Automation.PSEventArgs}
1 File System Created {System.Management.Automation.PSEventArgs}
1 File System Deleted {System.Management.Automation.PSEventArgs}
If we look at this in more detail
PS> Get-Event | select EventIdentifier, SourceIdentifier, TimeGenerated
EventIdentifier SourceIdentifier TimeGenerated
--------------- ---------------- -------------
1 File System Changed 08/11/2009 11:46:39
2 File System Changed 08/11/2009 11:46:39
3 File System Renamed 08/11/2009 11:49:53
4 File System Changed 08/11/2009 11:49:53
5 File System Created 08/11/2009 11:53:18
6 File System Changed 08/11/2009 11:53:18
7 File System Changed 08/11/2009 11:53:31
8 File System Changed 08/11/2009 11:53:31
9 File System Deleted 08/11/2009 11:54:22
10 File System Changed 08/11/2009 11:54:22
Note that we get pairs of events – the second event of the pair is always a change event. If we just want to see the changes (and remove duplicates). Just be careful on this and check whether its the odd or even record you need if running multiple registered events.
| 001 002 003 004
| Get-Event -SourceIdentifier "File System Changed" | where {($_.EventIdentifier % 2) -eq 1} | foreach { "{0}, {1}, {2}" -f $_.SourceIdentifier, $_.SourceEventArgs.FullPath, $_.TimeGenerated } |
We can use Get-Event to pull the change events from the queue. The modulo arithmetic on the EventIdentifier ensures that we only get the first change record. We can then dump the file path and time of change.
In a similar way we can interrogate the other events
| 001 002 003 004
| Get-Event -SourceIdentifier "File System Created" | foreach { "{0}, {1}, {2}" -f $_.SourceIdentifier, $_.SourceEventArgs.FullPath, $_.TimeGenerated } |
| 001 002 003 004
| Get-Event -SourceIdentifier "File System Renamed" | foreach { "{0}, {1}, {2}, {3}" -f $_.SourceIdentifier, $_.SourceEventArgs.OldFullPath, $_.SourceEventArgs.FullPath, $_.TimeGenerated } |
| 001 002 003 004
| Get-Event -SourceIdentifier "File System Deleted" | foreach { "{0}, {1}, {2}" -f $_.SourceIdentifier, $_.SourceEventArgs.FullPath, $_.TimeGenerated } |
These four could be combined into a single script if required.
Combined with the process tracing we did previously we can now track what programs are started on a system and what files are accessed.