Jetzt testen

Ereignisbehandlung mit PowerShell und .NET (Teil 1)

Inhaltsverzeichnis

Post Featured Image

Das Oxford-Wörterbuch definiert ein Ereignis als „eine Sache, die passiert, besonders etwas Wichtiges“. In der Computerwelt assoziieren die meisten Leute Ereignisse mit Protokollierung, aber Ereignisse gehen darüber hinaus. Tatsächlich ist ein Protokoll nichts anderes als die Ausgabe eines Ereignisses.

Dieser Blog-Beitrag behandelt zwei Arten von Ereignissen, PowerShell-Engine-Ereignisse und .NET-Objekt-Ereignisse (klicken Sie hier für Teil 2 der Serie, Ereignisbehandlung mit PowerShell und WMI).

Lesen Sie weiter, wenn Sie erfahren möchten, wie Sie sowohl PowerShell-Engine-Ereignisse als auch .NET Object-Ereignisse behandeln können und welche PowerShell-Befehle Ihnen dabei helfen. 

Die Grundlagen: Von PowerShell-Klassen und Objekten zu Ereignissen

Bevor wir uns mit den Details von PowerShell-Ereignissen beschäftigen, müssen wir zunächst Klassen und Objekte verstehen.

  • Eine Klasse ist eine Sammlung von Eigenschaften und Methoden.
  • Ein Objekt ist eine Instanz der Klasse, die für den Zugriff auf Eigenschaften und Methoden verwendet wird.

Einfach, oder? Lassen Sie uns dies anhand eines realen Beispiels vereinfachen.

Ich bin ein Objekt der Klasse meiner Eltern. Meine Eltern haben bestimmte Eigenschaften, wie schwarze Haare, braune Augen usw. und bestimmte Methoden wie Kochen, Tanzen usw.

Nehmen wir ein einfaches Beispiel für eine in PowerShell definierte Klasse:

class PrintName {#Define a class
    [Int] $count #Property
    [Void] WriteHello([string] $Name){ #Function
        Write-Host "Hello $Name"
    }
}
$Obj = [PrintName]::new() #Instantiate an Object

$Obj.count = 1 #Assign value for count property
Write-Host "Property Value " $obj.count
$Obj.WriteHello('Sonny') #Call GetProcessPath method
"`n"

Diese liefert die folgende Ausgabe:

Property Value 1
Hello Sonny

Im obigen Beispiel haben wir eine Klasse namens „PrintName“ mit einer Eigenschaft namens „count“ und einer Methode namens „WriteHello“ erstellt. Aber warum sprechen wir über Klassen?

Nun, um Ereignisse zu verstehen, müssen wir zuerst die Klassenstruktur verstehen, denn ein Ereignis ist nichts anderes als eine spezielle Art von Methode.

Speziell, weil andere Teile des Codes Ereignisse abonnieren können (dies wird auch als Ereignis-Subskription bezeichnet).

                  

PowerShell Engine Ereignisse

Damit andere Programme über bestimmte Aktionen oder Zustandsänderungen durch ein bestimmtes Programm informiert werden können, müssen wir Ereignisse in diesem Programm erstellen.

So können andere Programme diese Ereignisse abonnieren und werden informiert, wenn bestimmte Aktionen oder Zustandsänderungen in Ihrem Programm stattfinden (keine Sorge, sobald wir über .NET Framework-Ereignisse besprechen, werden die Dinge klarer).

PowerShell bietet verschiedene Cmdlets für die Arbeit mit Ereignissen. Beginnen wir mit dem New-Event Cmdlet (Abbildung 1), das, wie der Name schon sagt, ein neues Ereignis erstellt.

Ein neues Ereignis erstellen: das New-Event cmdlet

PowerShell bietet verschiedene Cmdlets für die Arbeit mit Ereignissen. Beginnen wir mit dem Cmdlet „New-Event“ (Abbildung 1), mit dem, wie der Name schon sagt, ein neues Ereignis erstellt wird.

New-Event 

    [-SourceIdentifier]  

    [[-Sender] ] 

    [[-EventArguments] <psobject[]>] 

    [[-MessageData] ] 

    []</psobject[]>

Source: Microsoft

 Das Cmdlet "New-Event" erstellt ein neues PowerShell-Ereignis

Abbildung 1: Das New-Event Cmdlet erstellt ein neues Event

Anzeigen der Ereigniswarteschlange: Das Get-Event Cmdlet

Sobald ein Ereignis erstellt wurde, wird es der Ereigniswarteschlange hinzugefügt. Um die Ereigniswarteschlange anzuzeigen, verwenden wir das Cmdlet Get-Event. Abbildung 2 zeigt, dass sich zwei Ereignisse in der Warteschlange befinden.

Get-Event
   [[-SourceIdentifier] ]
   []
Get-Event
   [-EventIdentifier] 
   []

Quelle: Microsoft

 Die Ausgabe des Cmdlets Get-Event zeigt, dass sich derzeit zwei Ereignisse in der Ereigniswarteschlange befinden

Abbildung 2: Das New-Event Cmdlet ruft Ereignisse in der PowerShell-Ereigniswarteschlange für die aktuelle Sitzung ab

Entfernen von Ereignissen aus der Warteschlange: Das Remove-Event cmdlet

Wie entfernen wir nun Ereignisse aus der Warteschlange? Wir verwenden das Remove-Event Cmdlet, wie in Abbildung 3 dargestellt.

Remove-Event
      [-SourceIdentifier] 
      [-WhatIf]
      [-Confirm]
      []
Remove-Event
      [-EventIdentifier] 
      [-WhatIf]
      [-Confirm]
      []

Quelle: Microsoft

Das Cmdlet "Remove-Event" löscht Ereignisse aus der Ereigniswarteschlange

Abbildung 3: Das Remove-Event Cmdlet löscht Ereignisse aus der PowerShell-Ereigniswarteschlange

Wie wir sehen können, ist nur noch ein Ereignis in der Ereigniswarteschlange vorhanden.

Bevor wir fortfahren, leeren wir die Ereigniswarteschlange, indem wir das folgende Cmdlet ausführen

Remove-Event -SourceIdentifier *

Nun befinden sich keine Ereignisse in unserer Ereigniswarteschlange.

Abonnieren eines Ereignisses: Die Cmdlets Register-EngineEvent und Get-EventSubscriber

Ereignisse sind nur dann nützlich, wenn jemand sie abonniert hat. Um ein Ereignis zu abonnieren, verwenden wir das Register-EngineEvent Cmdlet, wie in Abbildung 4 gezeigt. Genau wie Ereignisse wird die Ereignis-Subskription der Sitzung hinzugefügt.

Um die aktuellen Subskriptionen einer Sitzung anzuzeigen, verwenden Sie das Cmdlet Get-EventSubscriber. Die beiden Cmdlets Register-EngineEvent und Get-EventSubscriber sind in Abbildung 4 dargestellt.

Register-EngineEvent
        [-SourceIdentifier] 
        [[-Action] ]
        [-MessageData ]
        [-SupportEvent]
        [-Forward]
        [-MaxTriggerCount ]
        []

Quelle: Microsoft

Get-EventSubscriber
   [[-SourceIdentifier] ]
   [-Force]
   []
Get-EventSubscriber
   [-SubscriptionId] 
   [-Force]
   []

Source: Microsoft

 Abonnieren von " MyEvent" mit dem Cmdlet "Register-EngineEvent

Abbildung 4: Mit dem Register-EngineEvent Cmdlet abonieren wir das Ereignis "MyEvent"

Auslösen von Ereignissen in PowerShell

Im obigen Beispiel erstellen wir ein neues Abonnement für ein Ereignis mit dem Namen „MyEvent“.

Lassen Sie uns nun das Ereignis mit dem Namen „MyEvent" auslösen. Wie in Abbildung 5 zu sehen ist, wird beim Auslösen des Ereignisses mit dem Namen „MyEvent“ der Ereignisabonnent aufgerufen und der Aktionsskriptblock ausgeführt. 

 Der Aktionsblock wird ausgeführt, wenn ein neues Ereignis mit dem Namen " MyEvent" aufgerufen wird

Abbildung 5: Der Aktionsscriptblock wird ausgeführt, wenn ein neues Ereignis mit dem Namen "MyEvent" ausgeführt wird
 

Abmelden von einem Ereignis: Das Unregister-Event Cmdlet

Um die Subskription aus der aktuellen Sitzung zu entfernen, verwenden wir das Cmdlet Unregister-Event, wie in Abbildung 6 gezeigt.

Unregister-Event
          [-SourceIdentifier] 
          [-Force]
          [-WhatIf]
          [-Confirm]
          []
Unregister-Event
          [-SubscriptionId] 
          [-Force]
          [-WhatIf]
          [-Confirm]
          []

Quelle: Microsoft

Abbrechen eines Ereignisabonnements mit dem Cmdlet "Unregister-Event" in PowerShell

Abbildung 6: Aufhebung der Registrierung des Eventabonnements

                            
  • ErrorDataReceived
  • Exited und
  • OutputDataReceived

Um auf Klassenmitglieder zuzugreifen, müssen wir entweder ein neues Objekt instanziieren oder vorhandene Objekte verwenden. Sobald wir ein Objekt haben, können wir es verwenden, um die Ereignisse zu abonnieren.

Aufhebung der Registrierung des Eventabonnements

Abbildung 7: Aufhebung der Registrierung des Eventabonnements

Beispiel: Process.Exited-Ereignisse in PowerShell behandeln

PowerShell bietet das Cmdlet Register-ObjectEvent zum Abonnieren von .NET Framework-Ereignissen.

Nehmen wir als Beispiel an, dass wir das Ereignis „Exiting“ der Klasse Process abonnieren möchten. Dazu benötigen wir zunächst ein Objekt der Klasse Process. Mit diesem Objekt können wir dann die Member-Ereignisse der Process-Klasse abonnieren.

Das Ereignis „Exiting“ wird aufgerufen, wenn ein Prozess beendet wird. In unserem Beispiel werden wir das Objekt Taschenrechner-Prozess verwenden, um das Ereignis „Exiting“ zu abonnieren.

Jeder Prozess, der unter Windows läuft, ist ein Objekt. Um eine Liste der Prozessobjekte unter Windows abzurufen, können wir das Cmdlet Get-CimInstance verwenden. Wie in Abbildung 8 zu sehen ist, gibt das Cmdlet Get-CimInstance die Objekte aus, und durch Kombination mit dem Cmdlet Get-Member können wir die Klassenmitglieder anzeigen.

Get-CimInstance
   [-ClassName] 
   [-ComputerName <string[]>]
   [-KeyOnly]
   [-Namespace ]
   [-OperationTimeoutSec ]
   [-QueryDialect ]
   [-Shallow]
   [-Filter ]
   [-Property <string[]>]
   []</string[]></string[]>

Quelle: Microsoft

 Verwenden des Cmdlets Get-CimInstance zum Anzeigen laufender Prozesse

Abbildung 8: Verwenden des Cmdlets Get-CimInstance zum Anzeigen laufender Prozesse

Für unser Beispiel starten wir in unserem Code den Taschenrechner-Prozess. Wir haben immer die Möglichkeit, ein bereits vorhandenes Prozessobjekt zu verwenden.

Im folgenden Code starten wir zunächst den Taschenrechner-Prozess und erfassen das Objekt in der Variablen $CalcProcessObj.

$CalcProcessObj = [System.Diagnostics.Process]::Start("calc.exe")
Register-ObjectEvent -InputObject $CalcProcessObj -EventName Exited -Action { cmd.exe /c ping -n 2 8.8.8.8 }

Als Nächstes verwenden wir das Cmdlet Register-ObjectEvent, um das Ereignis „Exited“ für den Taschenrechnerprozess zu abonnieren.

Register-ObjectEvent
        [-InputObject] 
        [-EventName] 
        [[-SourceIdentifier] ]
        [[-Action] ]
        [-MessageData ]
        [-SupportEvent]
        [-Forward]
        [-MaxTriggerCount ]
        []

Source: Microsoft

Wenn der Taschenrechner-Prozess beendet wird, wird unser Aktionsblock ausgeführt. Der Aktionsblock enthält einen einfachen Befehl zum Senden von 2 Pings an die IP-Adresse 8.8.8.8.

Abbildung 9 zeigt die Ausgabe, dass wir das Ereignis erfolgreich abonniert haben. Ausgabe nach dem Ausführen des Cmdlets „Register-ObjectEvent

Abbildung 9: Ausgabe nach dem Ausführen des Cmdlets „Register-ObjectEvent

Nach dem Beenden des Taschenrechner-Prozesses können wir sehen, dass der Scriptblock ausgeführt wurde und 2 Pings an die IP-Adresse 8.8.8.8 gesendet wurden (Abbildung 10).

Wireshark zeigt 2 Pings, die an die IP-Adresse 8.8.8.8 gesendet wurden

Abbildung 10: Wireshark zeigt 2 Pings, die an die IP-Adresse 8.8.8.8 gesendet wurden

Wie wir bereits gesehen haben, können wir das Cmdlet Get-EventSubscriber verwenden, um eine Liste aller Ereignisabonnements abzurufen.

Die Ausgabe in unserem Beispiel ist in Abbildung 11 zu sehen.

Fig-11-List-of-event-subscribers-for-current-session

Abbildung 11: Abrufen einer Liste von Ereignisabonnenten für die aktuelle Sitzung mit dem Cmdlet Get-EventSubscriber

Auf ähnliche Weise können wir mit dem Cmdlet Unregister-Event die Registrierung eines Ereignisabonnements aufheben (lieben Sie nicht auch die Verb-Nomen-Darstellung von Cmdlets? 😊), wie in Abbildung 12 zu sehen ist.

Fig-12-unregistering-an-event-subscriber

Abbildung 12: Aufheben der Registrierung eines Ereignisabonnenten mit dem Cmdlet „Unregister-Event“

Beispiel: FileSystemWatcher-Klassenereignisse in PowerShell behandeln

 

Nehmen wir ein weiteres Beispiel der FileSystemWatcher-Klasse, die einige interessante Ereignisse bereitstellt (siehe Abbildung 13), welche wir abonnieren können, z. B. das Erstellen oder Löschen von Dateien oder Verzeichnissen usw.

Angenommen, wir möchten das Ereignis zum Erstellen von Dateien/Verzeichnissen abonnieren. Wir folgen den gleichen Schritten wie in unserem letzten Beispiel:

  • Zuerst müssen wir das Objekt abrufen,
  • dann verwenden wir das Cmdlet Register-ObjectEvent, um das Ereignis zu abonnieren

 Ereignisse für die Klasse FileSystemWatcher in .NET

Abbildung 13: Ereignisse für die Klasse FileSystemWatcher in .NET

Erstellen wir zunächst ein Objekt der Klasse FileSystemWatcher

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

Als Nächstes geben wir unserem Objekt den Verzeichnispfad, der auf die Erstellung von Dateien/Verzeichnissen überwacht werden soll

$testobj1.Path = "C:\Users\Public"
Schließlich verwenden wir das Cmdlet Register-ObjectEvent, um das Ereignis zu abonnieren.
Register-ObjectEvent -InputObject $testobj1 -EventName Created -Action { cmd.exe /c ping -n 2 8.8.8.8 }

 

Beispiel: System.Diagnostics.EventLog-Klassenereignisse in PowerShell behandeln

Hier ein weiteres Beispiel: Die System.Diagnostics.EventLog-Klasse bietet ein Ereignis namens „EntryWritten“, das ausgelöst wird, wenn ein Protokolleintrag geschrieben wird.

Wir folgen denselben Schritten wie zuvor: Wir instanziieren ein Objekt, legen die Log-Eigenschaft für das Objekt fest (= welches Protokoll wir beobachten möchten) und verwenden dann das Cmdlet Register-ObjectEvent, um das Ereignis „EntryWritten“ zu abonnieren.

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

$testobj1.Log = "Security"

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

Bitte beachten Sie: Dieser Code erfordert Administratorrechte, da wir mit der Protokollierung arbeiten.

 Ereignisse für die Klasse FileSystemWatcher in .NET

Abbildung 14: Ereignisse für die Klasse FileSystemWatcher in .NET

Für diese Demonstration habe ich von VS Code zu ISE gewechselt. Abbildung 15 zeigt, wie wir das Ereignis „EntryWritten“ für Sicherheitsprotokolle abonniert haben.

Unsere Aktionsblöcke schreiben jedes Mal „Miau“, wenn das Ereignis aufgerufen wird.

Abonnieren des Ereignisses „EntryWritten“
 
Abbildung 15: Abonnieren des Ereignisses „EntryWritten“

Fazit

Es gibt einige Dinge, die wir beachten müssen

  • Ereignisse, Ereignissubskriptionen und die Ereigniswarteschlange existieren nur in der aktuellen Sitzung. Wenn wir die aktuelle Sitzung schließen, wird die Ereigniswarteschlange verworfen, und die Ereignissubskription wird abgebrochen.
  • Es gibt zwei separate Cmdlets für die Arbeit mit PowerShell-Engine-Ereignissen (Register-EngineEvent) und .NET Framework-Ereignissen (Register-ObjectEvent).

Im zweiten Teil der Serie besprechen wir, wie WMI-Ereignisse mit PowerShell gehandhabt werden können.


Volle Kontrolle und vollständige Nachverfolgbarkeit

Sie können Ihre Überwachungsfunktionen noch besser nutzen, indem Sie den ScriptRunner Report/Audit DB Connector einsetzen. Damit können Sie alle Ihre Berichte über die Scriptausführung in einem zentralen Repository speichern und erhalten eine vollständige Nachvollziehbarkeit aller Prozesse.


 

Über den Autor: