Skip to the main content.

ScriptRunner Blog

Meistere PowerShell WMI: überwache automatisiert System-Events

Inhaltsverzeichnis

Post Featured Image

Optimiere die Ereignisüberwachung mit PowerShell WMI! Erfahre, wie Systemaufgaben automatisiert und die Ereignisverwaltung mühelos rationalisiert werden können. Es gibt zwei Arten von WMI-Ereignissen, dies deckt beide ab, die extrinsische WMI-Ereignisbehandlung und die intrinsische.

 

Es gibt zwei Artikel zur Behandlung von Ereignissen mit PowerShell. Nutze .NET in PowerShell für ereignisgesteuertes Scripting ist der andere Artikel.

 

Was ist WMI-Eventing

WMI steht für Windows Management Instrumentation. WMI ist die Microsoft-Implementierung von CIM (Common Information Model). CIM ist ein Standard der DMTF (Distributed Management Task Force), der definiert, wie Systeminformationen dargestellt werden können. Kurz gesagt ist CIM ein herstellerneutrales Modell, das zur Darstellung von Systeminformationen verwendet wird. Microsofts Implementierung von CIM ist als WMI bekannt.

Man kann sich WMI als eine Datenbank vorstellen, die Informationen über das System speichert. Aber wie können wir diese Informationen einsehen und wo werden sie gespeichert? Auf einem Windows-System finden wir WMI wie unten dargestellt.

 


C:\Windows\System32\wbem\Repository

WMI-Standort

 

Wmic war das Tool der Wahl, um mit WMI zu interagieren, aber es ist inzwischen veraltet. Es ist zwar noch auf Windows-Systemen verfügbar, aber wenn man versucht, es zu verwenden, erscheint eine Meldung wie die hier gezeigte:

Screenshot der Windows-Eingabeaufforderung, der die WMIC-Hilfemeldung anzeigt, dass der Befehl veraltet ist

Veralteter WMIC-Befehl

 

Mit Blick auf die Zukunft empfiehlt Microsoft die Verwendung von PowerShell zur Interaktion mit WMI. PowerShell verfügt über eingebaute Cmdlets zur Interaktion mit WMI. Diese wurden WMI cmdlet genannt, sind jedoch seit PowerShell Version 3 zugunsten von CIM cmdlets veraltet. In diesem Artikel werden wir CIM-Cmdlets verwenden. Um die verfügbaren CIM-Cmdlets anzuzeigen, werden wir unser Lieblings-Cmdlet namens Get-Command verwenden.


tabindex="0"> Get-Command -noun *CIM*

 

Damit erhalten wir die folgende Liste von CIM-Cmdlets:



Get-CimAssociatedInstance
Get-CimClass
Get-CimInstance
Get-CimSession
Invoke-CimMethod
New-CimInstance
New-CimSession
New-CimSessionOption
Register-CimIndicationEvent
Remove-CimInstance
Remove-CimSession
Set-CimInstance

 

Der Vollständigkeit halber wollen wir auch einen Blick auf die in der PowerShell verfügbaren WMI-Cmdlets werfen und zwar mit folgendem Cmdlet:



Get-Command -noun *wmi*

 

Der Vollständigkeit halber schauen wir uns auch die WMI-Cmdlets an, die in PowerShell verfügbar sind, und zwar mit dem folgenden Cmdlet:



Get-Command -noun *wmi*

 

Das Ergebnis ist die folgende Liste:



Get-WmiObject
Invoke-WmiMethod
Register-WmiEvent
Remove-WmiObject
Set-WmiInstance

 

Es ist klar zu erkennen, dass es mehr CIM-Cmdlets als WMI-Cmdlets gibt. CIM-Cmdlets sind die Zukunft, daher werden wir nicht mehr von WMI-Cmdlets sprechen.

Wie bereits erwähnt, wird WMI verwendet, um Systeminformationen darzustellen, aber wie speichert es diese Informationen? Die Antwort ist einfach: WMI verwendet eine hierarchische Struktur, die von Namespaces ausgeht, um systemweite Informationen zu speichern und zu verwalten. Diesen Namespaces sind verschiedene Klassen untergeordnet, wobei jede Klasse spezifische Systeminformationen darstellt. So wird z. B. die Klasse WIN32_DiskDrive verwendet, um Informationen über Festplattenlaufwerke darzustellen. Wenn das System über ein Laufwerk verfügt, dann hat diese Klasse ein Objekt mit Eigenschaften und Methoden, die das Laufwerk für dieses System darstellen.

 

Wir können das Cmdlet Get-CIMInstance verwenden, um mit WMI zu interagieren und dieses Objekt anzuzeigen. Das Cmdlet Get-CIMInstance erfordert den Klassennamen als einen der Parameter. Wie unten zu sehen ist, gibt das Cmdlet Get-CIMInstance das Objekt für die Klasse Win32_DiskDrive zurück. Dieses Objekt hat Eigenschaften wie DeviceID, Caption, Partitions, Size, Model etc.

Screenshot der PowerShell ISE, die die Ausgabe des Cmdlets anzeigt

Win32_DiskDrive-Objekte

 

Nehmen wir ein anderes Beispiel: Angenommen – und das ist auf einem modernen Laptop höchst unwahrscheinlich – ich möchte Informationen über mein CD-ROM 😊. WMI hat die Klasse Win32_CDROMDrive, die zur Darstellung von CD-ROM-Informationen verwendet wird. Verwenden wir das Get-CIMInstance-Cmdlet, um das Objekt zu erhalten.

Screenshot der PowerShell ISE, der die (leere) Ausgabe des Cmdlets anzeigt

Keine Objekte für Win32_CDROMDrive

 

Da ich einen modernen Laptop benutze, habe ich kein CD-ROM-Laufwerk, daher hat unser Cmdlet kein Objekt zurückgegeben (wie unten gezeigt).
Zwei interessante Beobachtungen hier: Auch wenn das CD-ROM-Objekt nicht existiert, können wir das Objekt abfragen, ohne eine Fehlermeldung zu erhalten, und zweitens, auch wenn das CD-ROM nicht auf dem System vorhanden ist, hat WMI die Klasse Win32_CDROMDrive verfügbar.

In ähnlicher Weise können wir WMI nach Informationen über alle laufenden Prozesse auf einem System abfragen, wie unten gezeigt:

Screenshot der PowerShell ISE, die die Ausgabe des Cmdlets anzeigt

Objekte für die Klasse Win32_Process

 

Wenn du weitere Informationen wünscht oder erkunden möchtest, welche anderen Klassen in WMI
verfügbar sind, ist die Microsoft-Dokumentation zu WMI ein guter Ausgangspunkt.

Neben der Darstellung von Systeminformationen bietet WMI auch eine leistungsstarke Funktion namens Eventing. Im Wesentlichen bietet WMI die Möglichkeit, systemweite Ereignisse zu überwachen und den Benutzer zu benachrichtigen. Es gibt zwei Arten von Ereignissen in WMI: intrinsische und extrinsische Ereignisse.

 

Intrinsische WMI-Ereignisse

Intrinsische Ereignisse sind enger mit WMI selbst verbunden. Sie werden als Reaktion auf Änderungen in der WMI-Struktur ausgelöst. Wenn zum Beispiel ein neuer Prozess auf dem System erstellt wird, führt dies dazu, dass eine neue Instanz für die Klasse WIN32_Process erstellt wird, was zu einem Ereignis vom Typ __Instancecreationevent führt. Ein anderes Beispiel wäre eine neue Klasse, die in WMI erstellt wird, dies führt zu einem Ereignis vom Typ __ClassCreationEvent.

Genauso wie alles in WMI als Objekte dargestellt wird, werden auch Ereignisse als Objekte dargestellt, und jeder Ereignistyp hat eine zugehörige Klasse, wie unten aufgeführt. Es ist jedoch zu beachten, dass diese Objekte, die ein Ereignis repräsentieren, kurzlebig sind. Daher verwenden wir Pooling, wenn wir unseren Ereignisfilter erstellen, da wir sonst die Erstellung dieser Objekte verpassen könnten.

Nachfolgend sind verschiedene Arten von Intrinsic Events aufgeführt:


 
__NamespaceOperationEvent, 
__NamespaceModificationEvent,
__NamespaceDeletionEvent, 
__NamespaceCreationEvent,
__ClassOperationEvent, 
__ClassDeletionEvent, 
__ClassModificationEvent,
__ClassCreationEvent, 
__InstanceOperationEvent, 
__InstanceCreationEvent,
__MethodInvocationEvent, 
__InstanceModificationEvent,
__InstanceDeletionEvent, 
__TimerEvent

 

Extrinsische WMI-Ereignisse

Extrinsische Ereignisse werden auf der Grundlage von Änderungen auf Betriebssystemebene erzeugt. Dies ist ein wesentlicher Unterschied: Während intrinsische Ereignisse nach Änderungen innerhalb der WMI-Struktur suchen, suchen extrinsische Ereignisse nach Änderungen außerhalb von WMI auf Betriebssystemebene. So ist z. B. das Ereignis "Computer shutdown" ein Ereignis auf Betriebssystemebene und wird daher als extrinsisches Ereignis eingestuft. Extrinsische Ereignisse erfordern kein Pooling.

Nachfolgend sind verschiedene Arten von extrinsischen Ereignissen aufgeführt:



Win32_ComputerShutdownEvent,
Win32_IP4RouteTableEvent,
Win32_ProcessStartTrace,
Win32_ModuleLoadTrace,
Win32_ThreadStartTrace,
Win32_VolumeChangeEvent,
Msft_WmiProvider*,
RegistryKeyChangeEvent,
RegistryValueChangeEvent

Um mit diesen Ereignissen arbeiten zu können, müssen wir das Konzept der Filter verstehen, aber bevor wir über Filter sprechen, machen wir einen kurzen Abstecher zu WQL.

WQL ist die WMI-Abfragesprache und ist wie SQL. Sie verwendet die gleiche Syntax wie SQL, um WMI nach Informationen abzufragen. Weitere Informationen zu WQL finden sich auf der Microsoft Documentation Seite.

 

Verwendung von WQL für Ereignisfilter

Wir verwenden WQL, um Filter für Ereignisse zu erstellen. Einen Filter kann man sich als Suchbedingung vorstellen, um nach bestimmten Ereignissen zu suchen.

Nehmen wir ein Beispiel: Nehmen wir an, wir wollen ein Ereignis zur Erstellung eines neuen Prozesses erfassen, insbesondere den Prozess des Taschenrechners. Dazu müssen wir eine WQL-Abfrage erstellen, um den Filter für die Erfassung dieses Ereignisses zu definieren. In diesem Fall wollen wir die Erstellung einer neuen Instanz der Klasse WIN32_Process erfassen, wobei der Prozessname calc.exe ist, und sobald wir dieses Ereignis erfasst haben, wollen wir eine Aktion durchführen. In diesem speziellen Beispiel wollen wir "WMI is awesome" ausgeben.

Beginnen wir mit der Definition unseres Filters mittels WQL, in diesem Fall suchen wir nach dem Ereignis der Instanzerstellung, daher beginnt unsere Abfrage mit



Select * from __InstanceCreationEvent

Da es sich um ein Eigenes Ereignis handelt, müssen wir als Nächstes das Pooling-Intervall definieren, sodass unsere WQL-Abfrage folgendermaßen aussieht



Select * from __InstanceCreationEvent within 10

Als nächstes müssen wir definieren, nach welcher Klasseninstanz wir suchen. Also fügen wir diese Information zu unserer Abfrage hinzu



Select * from __InstanceCreationEvent within 10 where TargetInstance ISA "Win32_Process"

Als Nächstes müssen wir genau angeben, nach welcher Art von Instanz wir suchen, in diesem Fall suchen wir nach einer Prozessinstanz mit der Namenseigenschaft "calc.exe", daher sieht unsere WQL-Abfrage am Ende wie folgt aus.



Select * from __InstanceCreationEvent within 10 where TargetInstance ISA "Win32_Process" and TargetInstance.Name like "%calc%"

Nachdem wir nun unseren Filter definiert haben, müssen wir das Ereignis abonnieren und die Aktion festlegen, die ausgeführt werden soll, sobald das Ereignis beobachtet wird.

Zu diesem Zweck wird das Cmdlet Register-CimIndicationEvent verwendet. Das Cmdlet Register-CimIndicationEvent benötigt folgende Parameter, um ein Ereignis zu abonnieren (wie in Abb. 6 dargestellt)



-wqlQuery
-SourceIdentifier
-Action

Der Parameter -SouceIdentifier kann mit einem benutzerdefinierten Namen versehen werden.

Screenshot der WQL-Abfrage

WMI temporäres Ereignisabonnement

 

Wenn das Cmdlet erfolgreich ist, erhalten wir eine Meldung wie die unten abgebildete. Sie zeigt an, dass wir das Ereignis erfolgreich abonniert haben.

Screenshot der oben genannten WQL-Abfrage

 Ereignis erfolgreich abonniert

 

Wie wir beobachten können, bekomme ich, sobald ich den Prozess calc.exe starte, die Meldung "WMI is Awesome" auf dem Bildschirm angezeigt.

Dies funktioniert so lange gut, bis wir die PowerShell-Eingabeaufforderung beenden. Da dies ein temporäres Ereignisabonnement ist, hängt es von der Lebensdauer des Prozesses ab, unter dem es erstellt wurde, in unserem Fall die PowerShell-Eingabeaufforderung. Sobald wir den übergeordneten Prozess verlassen, verlieren wir unser Abonnement.

WMI bietet ein permanentes WMI-Ereignisabonnement, das unser Abonnement dauerhaft macht und den Systemneustart überdauert.

Das bringt uns zum nächsten wichtigen Konzept, das wir verstehen müssen, bevor wir mit der Erstellung permanenter Ereignisabonnements beginnen. Es gibt drei wichtige Konzepte zu verstehen

  • Ereignisfilter
  • Ereignis-Verbraucher
  • Bindung zwischen Ereignisfilter und Verbraucher

 

Ereignisfilter

Ereignisfilter haben wir bereits in unserem letzten Beispiel gesehen. Wie bereits erwähnt, werden Ereignisfilter verwendet, um bestimmte Ereignisse zu abonnieren. Man kann sie sich als Suchbedingung vorstellen, um nach bestimmten Ereignissen zu suchen. Es gibt jedoch einen Unterschied: Während wir in unserem letzten Beispiel den Filter definiert und in Form einer WQL-Abfrage an das Cmdlet Register-CimIndicationEvent übergeben haben, werden wir im Falle eines permanenten Ereignisabonnements unseren Filter in WQL erstellen, allerdings werden wir dann ein Objekt der Klasse __EventFilter initialisieren und unseren Filter als eine der Eigenschaften dieses Objekts übergeben.

 

Ereigniskonsument

Wenn unser Ereignisfilter den Kriterien entspricht, möchten wir eine bestimmte Aktion durchführen, wie wir es in unserem Beispiel für das temporäre Ereignisabonnement getan haben. Für das permanente Ereignisabonnement stehen uns fünf Aktionsarten zur Verfügung, die von fünf verschiedenen Verbraucherklassen bereitgestellt werden.

  • LogFileEventConsumer: Schreibt in eine Protokolldatei.
  • ActiveScriptEventConsumer: Führt ein Script aus.
  • NTEventLogEventConsumer: In das Windows-Ereignisprotokoll schreiben.
  • SMTPEventConsumer: Eine E-Mail senden.
  • CommandLineEventConsumer: Ausführung einer Befehlszeile.

Genauso wie bei den Ereignisfiltern müssen wir, sobald wir wissen, welche Aktion wir durchführen wollen, ein Objekt dieser Klasse initialisieren und mit Werten für bestimmte Eigenschaften versehen.

Sobald der Ereignisfilter und der Verbraucher definiert sind, müssen sie nur noch miteinander verbunden werden, und genau das erledigt die Filter-Verbraucher-Bindung für uns. Wir erstellen ein Objekt der Klasse __FilterToConsumerBinding und versorgen es mit Werten für die Objekte Filter und Consumer.

 

Beispiel: Einrichten eines permanenten WMI-Ereignis-Abonnements

Angenommen, wir möchten ein Ereignisprotokoll erstellen, wenn ein Benutzer ein USB-Gerät einsteckt. Das können wir ganz einfach mit WMI-Ereignissen erreichen.

Es ist ein dreistufiger Prozess:

  1. Zunächst definieren wir den Filter für ein Ereignis, das "Benutzer steckt USB-Stick ein"
  2. Dann definieren wir einen Verbraucher, der ein Ereignisprotokoll erstellt, wenn dieser Filter auf das Ereignis passt
  3. Schließlich verbindet die Filter-zu-Konsumenten-Bindung den Ereignisfilter mit dem rechtmäßigen Konsumenten

Die folgende Grafik zeigt eine schematische Darstellung dieses Prozesses.

Schematische Darstellung des Prozesses zur permanenten WMI-Event-Subscription

Prozess für die permanente WMI-Ereignisabonnierung

 

Das New-CimInstance Cmdlet wird verwendet, um eine Instanz der CIM-Klasse zu erstellen und kann zur Erstellung von Instanzen auf einem entfernten Computer verwendet werden, wenn es mit dem Parameter CimSession verwendet wird.

In unserem Skript verwenden wir das Cmdlet New-CIMInstance, um Instanzen für die Klassen EventFilter, EventConsumer und FilterToConsumer zu erstellen.

Wir bringen nun alles zusammen und erstellen unser erstes WMI-Ereignisabonnement (event subscription) :

Schritt 1: Zuerst definieren wir unsere WQL-Abfrage für unseren Filter:


$WQLQuery = 'SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA "Win32_USBControllerDevice"'

 

Wenn ein USB-Gerät an ein System angeschlossen wird, wird eine neue Instanz von WIN32_USBControllerDevice erstellt, daher suchen wir nach einem Instanzerstellungsereignis. Nun definieren wir unseren Filter wie unten gezeigt:



$WMIFilterInstance = New-CimInstance -ClassName __EventFilter -Namespace "rootsubscription" -Property @{Name="myFilter";
EventNameSpace="rootcimv2";
QueryLanguage="WQL";
Query=$WQLQuery
}

Wie wir sehen können, erstellen wir ein Objekt der Klasse __EventFilter und speichern es in der Variablen $WMIFilterInstance. Wir geben auch Werte für einige Eigenschaften dieses Objekts an, die wichtigsten sind Name und Query.

 

Schritt 2: Als nächstes definieren wir unseren Verbraucher, in unserem Fall wollen wir ein Ereignisprotokoll erzeugen, daher erstellen wir eine Instanz der NTEventLogEventConsumerKlasse.



$WMIEventConsumer = New-CimInstance -ClassName NTEventLogEventConsumer -Namespace "rootsubscription" -Property @{Name="USBLogging";
EventId = [uint32] 1; EventType = [uint32] 4; #EventType kann folgende Werte haben; Error 1, FailureAudit 16, Information 4, SuccesAudit 8, Warning 2
SourceName="PowerShell-Script-Log"; Category= [uint16] 1000 }
#Category wird nie wirklich verwendet, sondern kann einen beliebigen Wert haben und ist im Grunde dazu gedacht, mehr Informationen über das Ereignis zu liefern

Einige Dinge, die man beachten sollte, sind die EventID und EventType Eigenschaften, auch der sourceName wird verwendet, um die Quelle des Ereignisses zu definieren.

 

Schritt 3: Schließlich binden wir unseren Filter mit dem Verbraucher.



$WMIWventBinding = New-CimInstance -ClassName __FilterToConsumerBinding -Namespace "rootsubscription" -Property @{Filter = [Ref] $WMIFilterInstance;
Consumer = [Ref] $WMIEventConsumer
}

Wie wir sehen können, müssen wir dem zuvor erstellten Objekt einen Wert für die Filter- und Consumer-Eigenschaft übergeben, um die Filter-Consumer-Bindung zu erstellen. Das vollständige Skript, das ich hier verwende, liegt in meinem GitHub-Repository

Nach der Ausführung erstellt dieses Skript ein dauerhaftes Ereignisabonnement. Wenn ein Benutzer das nächste Mal ein USB-Gerät anschließt, wird ein Eintrag im Windows-Ereignisprotokoll erstellt, wie in den Abbildungen unten gezeigt.

 

Screenshot, der ein neues Ereignis im Windows-Ereignisprotokoll zeigt

Windows-Ereignisprotokoll

 

Screenshot: Details des Ereignisses im Windows Ereignisprotokoll

Ereignisprotokoll mit Ereignis-ID und Quelle

 

Entfernen eines Ereignisabonnements

Ein letzter Hinweis, bevor wir diesen Blogbeitrag beenden: Wenn wir ein dauerhaftes Ereignisabonnement entfernen wollen, müssen wir die folgenden drei Befehle ausführen:



Get-CimInstance -ClassName __EventFilter -namespace rootsubscription -filter "name='myFilter'" | Remove-CimInstance

Get-CimInstance -ClassName NTEventLogEventConsumer -Namespace rootsubscription -filter "name='FolderWriteLogging'" | Remove-CimInstance

Get-CimInstance -ClassName __FilterToConsumerBinding -Namespace rootsubscription -Filter "Filter = ""__eventfilter.name='myFilter'""" | Remove-CimInstance

Das Cmdlet Get-CimInstance gibt das Objekt für die mit dem Parameter ClassName angegebene Klasse zurück, das wir zum Entfernen an das Cmdlet Remove-CimInstance übergeben. Weitere Informationen zu GetCimInstance finden wir in der Microsoft-Dokumentation.

 

Zusammenfassung

Wir können  WMI-Ereignisse zur Überwachung verschiedener Änderungen im System nutzen. WMI ist eine leistungsstarke Technologie, die jedoch unter Systemadministratoren noch unbekannt ist. PowerShell hat die Interaktion und Arbeit mit WMI durch die Bereitstellung nützlicher Cmdlets vereinfacht. Ich hoffe, dass dieser Artikel die Leser dazu anregt, mehr über WMI zu erfahren und PowerShell zu nutzen, um diese Technologie für interessante Projekte einzusetzen.


 

 

Zusammenhängende Posts

7 min read

AD‑Benutzer, ‑Gruppen und ‑Computer mit PowerShell hinzufügen

In der PowerShell lassen sich relativ unkompliziert Benutzerkonten sowie Gruppen erstellen, Benutzer zu Gruppen...

6 min read

Scripten in Dauerschleife: Einführung in PowerShell-Schleifen

Schleifen sind die Lebensader der meisten Programmiersprachen, und sie spielen eine integrale Rolle in jedem Code. Die...

4 min read

Wie erstelle ich PFX-Dateien mit PowerShell?

Du musst eine PFX-Datei erstellen, da du "nur" ein .CER, .CRT-File und einen RSA-Key erhalten hast beim Kauf eines...

Über den Autor: