How to Leverage .NET in PowerShell for event-driven scripting

Table of contents:

Extend PowerShell with .NET for powerful event automation. Learn how to monitor and handle system events like a pro! The article answers how to tap into .NET classes to monitor events and trigger automated responses, expanding PowerShells capabilities for efficient system management.

The Oxford dictionary defines an event as "a thing that happens, especially something important". In the computing world, most people associate events with logging, however events go beyond that. In fact, a log is nothing but an output from an event.

This blog post covers two types of events, PowerShell Engine Events and .NET Object Events (click here for the second part, Master PowerShell WMI: Automate system event monitoring effortlessly).

Keep on reading if you want to learn how to handle both PowerShell Engine Events and .NET Object Events and which PowerShell commandlets will get you there.

The basics: From PowerShell classes and objects to events

Before we delve into the details of PowerShell events, we first need to understand classes and objects.

  • A class is a collection of properties and methods.
  • An object is an instance of the class which is being used to access properties and methods.

Simple, right? Lets simplify this using a real-world example.

Im an object of my parents class. My parents have certain properties such as black hair, brown eyes etc. and certain methods such as cooking, dancing etc.

Lets take a simple example of a class defined in PowerShell:

#Define a class

class PrintName { 

   #Property 

[Int] $count  

#Function

[Void] WriteHello([string] $Name){  
        Write-Host "Hello $Name" 
    } 
}

#Instantiate an Object

$Obj = [PrintName]::new() 



#Assign value for count property

$Obj.count = 1   
Write-Host "Property Value " $obj.count 



#Call GetProcessPath method

$Obj.WriteHello('Sonny')  
"`n"

Which delivers the following output:

Property Value 1

Hello Sonny

In the example above we created a class called "PrintName" with one property called "count" and method called "WriteHello". But why are we talking about classes?

Well, to understand events we need to first understand class structure because an event is nothing but a special kind of method.

Special, because other pieces of code can subscribe to events (this is also known as event subscription).

PowerShell engine events

In order to enable other programs to be informed about certain actions or state changes by a specific program, we need to create events in that program.

Thus, other programs can subscribe to these events and get informed when certain actions or state changes happen in your program (dont worry, when we discuss .NET Framework events things would become clearer).

PowerShell provides different cmdlets for working with events. Lets start with the New-Event cmdlet, which, as the name suggests, creates a new Event.

Creating an event: New-Event cmdlet

PowerShell provides different cmdlets for working with events. Lets start with the New-Event cmdlet, which, as the name suggests, creates a new Event.

New-Event

    [-SourceIdentifier]

    [[-Sender] ]     

    [[-EventArguments] <psobject[]>]     

    [[-MessageData] ]     

    []</psobject[]>

Source: Microsoft

The New-Event cmdlet creates a new PowerShell event

Viewing the event queue: Get-Event cmdlet

Once an Event is created it is added to the event queue. To view the event queue, we use the Get-Event cmdlet. You can see below that there are two events in the queue.

Get-Event

    [[-SourceIdentifier] ]

    []
Get-Event

    [-EventIdentifier] 

    []

Source: Microsoft

The Get-Event cmdlet gets events in the PowerShell event queue for the current session

Removing events from the queue: Remove-Event cmdlet

Now how do we remove events from the queue? We use theRemove-Event cmdlet as shown below:

Remove-Event

    [-SourceIdentifier] 

    [-WhatIf]

    [-Confirm]

    []
Remove-Event

    [-EventIdentifier] 

    [-WhatIf]

    [-Confirm]

    []

Source: Microsoft

The Remove-Event cmdlet deletes events from the event queue in the current session.

As we can see, we only have one event left in the event queue.

Before we move on, lets clear the event queue by running the following cmdlet

Remove-Event -SourceIdentifier *

At this point there are no events in our event queue.

Subscribing to an event: Register-EngineEvent and Get-EventSubscriber cmdlet

Events are not useful unless someone has subscribed to them. In order to subscribe to an event, we use the Register-EngineEvent cmdlet as shown below. Just like events, event subscription is added to the session.

In order to view current subscriptions in a session, use the Get-EventSubscriber cmdlet. Both Register-EngineEvent and Get-EventSubscriber cmdlet are shown here.

Register-EngineEvent

    [-SourceIdentifier] 

    [[-Action] ]

    [-MessageData ]

    [-SupportEvent]

    [-Forward]

    [-MaxTriggerCount ]

    []

Source: Microsoft

Get-EventSubscriber

    [[-SourceIdentifier] ]

    [-Force]

    []
Get-EventSubscriber

    [-SubscriptionId] 

    [-Force]

    []

Source: Microsoft

By using the Register-EngingeEvent cmdlet we subscribe to MyEvent event

Raising events in PowerShell

In the example above we create a new subscription for an event called "MyEvent". Now lets raise the event named "MyEvent".

As we can see in now, when we raise the event named "MyEvent", the event subscriber is invoked and executes the Action Script Block.

The Action block is being executed when a new event named "MyEvent" is invoked

Unsubscribing from an event: Unregister-Event cmdlet

In order to remove the subscription from the current session we use the Unregister-Event cmdlet:

Unregister-Event

    [-SourceIdentifier] 

    [-Force]

    [-WhatIf]

    [-Confirm]

    []
Unregister-Event

    [-SubscriptionId] 

    [-Force]

    [-WhatIf]

    [-Confirm]

    []

Source: Microsoft

Unregistering the event subscriber

.NET framework events

So far, we have discussed PowerShell engine event. Now lets switch our focus to .NET framework events. A complete reference for .NET can be found at .NET API browser.

Lets start by taking a simple example of the Process class which is part of the System.Diagnostic namespace.

As we can see, the Process class has constructor, properties, methods and events. Collectively they are also known as class members. The Process class also has three events:

  • ErrorDataReceived
  • Exited and
  • OutputDataReceived

In order to access class members, we need to either instantiate a new Object or use existing objects. Once we have an object, we can use it to subscribe to the events.

Exited event for System.Diagnostic.Process class in .NET

Example: Handling Process.Exited events in PowerShell

PowerShell provides the Register-ObjectEvent cmdlet to subscribe to .NET framework events.

Lets take an example, suppose that we want to subscribe to the exiting event of Process class. In order to do so, we first need an Object of Process class. We can then use the object to subscribe to the Member events of the Process class.

The exiting event is invoked when a process exits. In our example we will use the calculator process object to subscribe to the exiting event.

Any process running on Windows is an Object. To get a list of process objects on windows we can use the Get-CimInstance cmdlet. As you can see, the Get-CimInstance returns the objects and by combining it with the Get-Member cmdlet we can view the class members.

Get-CimInstance 

    [-ClassName] 

    [-ComputerName <string[]>] 

    [-KeyOnly] 

    [-Namespace ] 

    [-OperationTimeoutSec ] 

    [-QueryDialect ] 

    [-Shallow ] 

    [-Filter ] 

    [-Property <string[]>] 

    []

Source: Microsoft

Using Get-CimInstance cmdlet to view running processes

For our example we launch the calculator process in our code. We always have the option to use an already existing process object. In the code below we are first starting the calculator process and capturing the object in $CalcProcessObj variable.

$CalcProcessObj = [System.Diagnostics.Process]::Start("calc.exe") Register-ObjectEvent -InputObject 

$CalcProcessObj -EventName Exited -Action { cmd.exe /c ping -n 2 8.8.8.8 }

Next, we use the Register-ObjectEvent cmdlet to subscribe to the exited event for the calculator process.

 Register-ObjectEvent

    [-InputObject] 

    [-EventName] 

    [[-SourceIdentifier] ]

    [[-Action] ]

    [-MessageData ]

    [-SupportEvent]
 
    [-Forward]
 
    [-MaxTriggerCount ]
  
    [] 

Source: Microsoft

When the calculator process exits our action block will be executed. The Action block contain a simple command to send 2 pings to IP address 8.8.8.8.

This is the output that we have successfully subscribed to the event:

Output after running the Register-ObjectEvent cmdlet

Once we exit the calculator process, we can see that the Action Script block was executed and resulted in 2 pings being sent to IP address 8.8.8.8

Wireshark showing 2 pings sent to IP address 8.8.8.8

As we have seen before we can use the Get-EventSubscriber cmdlet for getting a list of all event subscriptions.

The output in our example is shown here:

Retrieving a list of event subscribers for the current session with Get-EventSubscriber cmdlet

Similarly, we can use the Unregister-Event cmdlet to unregister an event subscription (dont you love the verb-noun representation of cmdlets), as can be seen here:

Unregistering an event subscriber with the Unregister-Event cmdlet

Example: Handling FileSystemWatcher Class Events in PowerShell

Lets take another example of FileSystemWatcher class which provide some interesting Events that we can subscribe to, such as file or directory creation, deletion etc.

Suppose we want to subscribe to file/directory creation event.

We follow the same steps from our last example:

  • first we need to get the object
  • then we use the Register-ObjectEvent cmdlet to subscribe to the event

Events for FileSystemWatcher class in .NET

Lets first create an Object of FileSystemWatcher class

$testobj1 = New-Object -TypeName System.IO.FileSystemWatcher

Next, we give our object the directory path to watch for file/directory creation

$testobj1.Path = "C:\Users\Public"

Lastly, we use the Register-ObjectEvent cmdlet to subscribe to the event.

Register-ObjectEvent -InputObject $testobj1 -EventName Created -Action { cmd.exe /c ping -n 2 8.8.8.8 }

Example: Handling System.Diagnostics.EventLog Class Events in PowerShell

Heres another example: the System.Diagnostics.EventLog class provides an Event called EntryWritten which is raised when a log entry is written.

We follow the same steps as before: we instantiate an object, set the Log property for the object (= which log we want to watch) and then use the Register-ObjectEvent cmdlet to subscribe to the EntryWritten event.

$testobj1 = New-Object -TypeName System.Diagnostics.EventLog


$testobj1.Log = "Security"



Register-ObjectEvent -InputObject $testobj1 -EventName EntryWritten -Action { Write-Host "Meow" }

Please note: this code requires admin level permission as we are working with logging.

EntryWritten event for EventLog class

For this demonstration I switched from VS Code to ISE. We subscribed to the EntryWritten event for Security logs. Our action blocks write Meow each time the event is invoked.

Subscribing for EntryWritten event

Conclusion

There are few things which we need to keep in mind:

  • Events, event subscriptions, and the event queue exist only in the current session. If we close the current session, the event queue is discarded, and the event subscription is canceled.
  • There are two separate cmdlets to work with PowerShell engine events (Register-EngineEvent) and .NET Framework events (Register-ObjectEvent)

In the next part we will be discussing, how to handle WMI eventing with PowerShell.

Your ultimate PowerShell Cheat Sheet

Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.The poster is available for download and in paper form.

Get your poster here!

Related links