Cloud Services

Cloudy Montag II: Eine Einführung in Pester

Im Laufe der Jahre habe ich die Erfahrung gemacht, dass es trotz eines gut getesteten Skripts und unabhängig davon, wie sorgfältig du deinen Code schreibst, einige Randfälle geben kann, die vergessen werden oder nur selten vorkommen. Das gilt besonders, wenn du deine Skripte manuell testest. Außerdem kann es sein, dass dein Implementierungsskript zwar fehlerfrei ausgeführt wurde, aber die ursprüngliche Geschäftsanforderung tatsächlich erfüllt hat?

Wenn du Pester noch nicht kennst, gibt dir dieser Artikel einen Überblick darüber, was Pester ist, was du damit erreichen kannst und warum es eine gute Idee ist, es zu lernen und in deiner Umgebung zu implementieren. Wenn du Pester bereits kennst, erhältst du in diesem Artikel einen kurzen Überblick darüber, wie wir es in der Abteilung Windows Operations & Engineering bei Swisscom implementiert haben.

In diesem Artikel erkläre ich zunächst die grundlegenden Konzepte hinter Pester und spreche über die verschiedenen Anwendungsfälle von Pester. Im zweiten Teil dieses Beitrags werde ich erläutern, wie wir Pester in meinem Team (Windows Operations & Engineering) bei Swisscom einsetzen. Ich werde zeigen, wie wir Pester nutzen, um ein großes Framework zu testen, das wir verwenden, und wie Pester uns hilft, die Compliance unserer Server zu messen.

Was ist Pester?

Pester ist ein Test Unit Framework für die PowerShell. Es gibt uns die Möglichkeit, Code zu schreiben, der das Testen unserer Skripte automatisiert. So wird automatisch überprüft, ob unser Skript tatsächlich wie erwartet funktioniert (oder nicht). Das ist eine großartige Funktion, die Entwickler schon seit Jahren nutzen, und PowerShell hat endlich sein Unit-Test-Framework bekommen: Pester.

Die Geschichte hinter Pester ist sehr interessant: Pester war ein Open-Source-Community-Projekt, das von Microsoft geforkt und mit Windows 10 ausgeliefert wurde. Das ist wirklich eine große Sache, denn Microsoft hat noch nie zuvor Community-Projekte offiziell in das Betriebssystem integriert. Und wir lieben es! 🙂

Pester Anwendungsfälle

Pester wird im Allgemeinen zur Beantwortung von zwei Hauptfragen verwendet:

Wie stelle ich sicher, dass der von mir geschriebene Code tatsächlich wie erwartet funktioniert?
Das bezeichnen wir im Allgemeinen als "Unit-Tests". Das Prinzip ist einfach: Du willst sichergehen, dass der Code in allen Fällen funktioniert. Dass alle Parameter getestet worden sind. Dass das Skript im Falle eines Fehlers mit einer bestimmten Fehlermeldung ordnungsgemäß beendet wird. Du willst auch sicherstellen, dass das Skript die Werte und Typen zurückgibt, die du erwartest, und dass 100% (oder vielleicht etwas weniger) deines Codes von deinen Tests abgedeckt werden. Auf diese Weise kannst du garantieren, dass dein Skript in der Produktion funktioniert und nicht versagt.

Die zweite Frage wäre:

Wie kann ich sicherstellen, dass das, was ich mit meinem Skript konfiguriert habe, auch wirklich das tut, was wir vorhaben? Das klingt vielleicht etwas vage, aber du hast vielleicht ein Skript, das einen Server so konfiguriert, dass er einige vordefinierte Standards umsetzt. Es setzt Registrierungsschlüssel, Firewall-Regeln, WSUS-Server, Backup-Einstellungen usw... Es ist eine gute Idee zu überprüfen, ob die Kommunikation mit externen Servern funktioniert, z. B. ob die implementierten Firewall-Regeln tatsächlich die richtigen Ports geöffnet haben und ob die Kommunikation zwischen deinem Server und der entfernten Ressource tatsächlich stattfindet. Das Öffnen der Ports ist eine Aktion, die unseren Kunden/Endkunden einen Dienst zur Verfügung stellt, und es ist sinnvoll, sicherzustellen, dass dieser Dienst tatsächlich funktioniert, bevor wir den Server an den Kunden ausliefern.

Das nennt man "Operative Validierung".

Im nächsten Teil zeige ich dir, wie wir es bei Swisscom in unseren täglichen Aufgaben im Bereich Operations umgesetzt haben.

Pester bei Swisscom

Nachdem wir nun einige der grundlegenden Konzepte von Unit-Tests behandelt haben, können wir uns ansehen, wie wir sie in unserem Team, Windows Operations & Engineering bei Swisscom, umgesetzt haben.

Unit-Tests bei Swisscom

In meinem derzeitigen Team (Windows Operations & Engineering) verwenden alle Ingenieure ein benutzerdefiniertes Build-Framework zum Schreiben unserer Skripte. Es hilft uns, unsere Skripte im gesamten Team einheitlich zu halten und zu garantieren, dass der Skripter bei jedem Skript, das er schreibt, die gleichen Erfahrungen macht (Protokollierungsverhalten, Konfigurationsdatei, Ausgaben usw.) und dass der Operator, der das Skript ausführt (falls vorhanden), ebenfalls die gleichen Erfahrungen macht, unabhängig davon, wer das Skript geschrieben hat.

Da mehrere Ingenieure auf mein Framework angewiesen sind, ist es wichtig, dass jede Änderung, die ich oder einer meiner Kollegen in das Repository einpflegt, fehlerfrei ist. An dieser Stelle beginnen wir, über das Schreiben von Unit-Tests mit Pester zu sprechen.

Um unser Framework zu testen, haben wir etwa 50 Tests, die verschiedene Dinge aus dem Framework testen. Dinge wie das Erstellen eines neuen Skripts, das Ausführen desselben, das Überprüfen, ob die Protokolldateien erstellt werden, ob die Protokolldatei unsere Standardausgabe enthält usw...

Die Ausgabe eines Pester-Tests sieht in etwa so aus wie auf dem Screenshot unten.

Abbildung 1 Ergebnisse des Pester-Tests

Lila steht für Informationen, grün bedeutet, dass der Test bestanden wurde, und rot, dass er nicht bestanden wurde.

Ich möchte betonen, dass ein roter Punkt (der darauf hinweist, dass dein Test fehlgeschlagen ist) eigentlich nichts Schlechtes ist. Es bedeutet einfach, dass der Pester Test etwas gefunden hat, das in deinem Skript korrigiert werden muss, und nicht ein Endbenutzer oder, noch schlimmer, ein Kunde, der den Fehler zuerst entdeckt hätte.

Eine vereinfachte Version eines der Tests, die ich geschrieben habe, findest du hier unter.

Der describe-Block fügt eine Informationsebene hinzu (der lila Teil der Ausgabe), und der Test selbst wird immer in einem it-Block durchgeführt. Wenn der it-Block geprüft wird, ist die Ausgabe grün. Andernfalls wird sie in rot ausgegeben. Der Text, der neben dem "describe"- oder "it"-Block hinzugefügt wird, ist informativ und kann alles sein, was dir hilft zu erklären, welchen Test du aufrufst und was er tut.

Im obigen Codeauszug prüfe ich einfach, ob die Variable $testFolder einen Unterordner namens "logs" enthält. Ich leite meinen Ausdruck an das Schlüsselwort "should" weiter. Dieses Schlüsselwort wertet die Testbedingung aus und gibt entweder einen Fehler aus oder bestätigt meinen Test.

Auch wenn die Syntax von pester leicht zu verstehen ist, heißt das nicht, dass man damit keine komplexen Aufgaben erledigen kann.

In diesem nächsten Codeauszug führen wir noch ein paar weitere Prüfungen durch.

Sie stellt sicher, dass das Framework, mit dem wir unsere Skriptvorlagen erstellen, das Skript tatsächlich in dem Format erzeugt, das wir erwarten, und dass es beim Aufruf des Skripts die entsprechende Meldung in der Protokolldatei erzeugt. In diesem speziellen Fall prüfe ich, ob zwei verschiedene Meldungen vorhanden sind. Wenn nur eine von ihnen vorhanden ist, schlägt der globale Test fehl.

Ich verwende Unit-Tests, um Code-Blöcke oder komplette Funktionen zu überprüfen. Im Allgemeinen versuche ich, nach dem folgenden Muster vorzugehen: Ein "describe"-Block, um eine Funktion zu testen, und ein "it"-Block für jede Funktion dieser Funktion, die ich testen möchte.

Operative Validierung bei Swisscom

Bei der operationellen Validierung schreibst du den Pester-Code nicht, um das von dir geschriebene Skript zu testen, sondern um zu prüfen, ob die Änderungen oder Implementierungen, die dein Skript vornimmt, tatsächlich deinem ursprünglichen Geschäftsbedarf entsprechen. Wenn du eine Firewall-Regel erstellst, um einen bestimmten Port zu öffnen, mit dem einzigen Ziel, auf einen Webserver zuzugreifen und Daten aus einer Datenbank zu lesen, willst du sicher sein, dass du tatsächlich mit diesem Webserver interagieren und die benötigten Daten abfragen kannst. Wenn du die Firewall-Regel erfolgreich eingerichtet und die richtigen Ports geöffnet hast, sich aber eine andere Firewall zwischen deinem Client-Server und deinem Webserver befindet oder das Netzwerkkabel einfach abgezogen wird, kannst du den beabsichtigten Dienst nicht anbieten, obwohl du die Firewall-Regel erfolgreich implementiert hast.

Bei Windows Operations & Engineering verwenden wir die Betriebsvalidierung, um sicherzustellen, dass unsere Server unseren Standards entsprechen (Sicherheit, Konfiguration usw.). So können wir garantieren, dass der von uns bereitgestellte Server mit unseren Standards übereinstimmt. Dieselben Tests werden in einem späteren Schritt verwendet, um sicherzustellen, dass keine Konfigurationsabweichung vorliegt.

Die Tests bestehen aus einer Reihe von Pester-Skripten, die auf einem oder mehreren Servern eingesetzt werden. Ein Skript führt die Pester-Tests aus und sammelt die Daten in einem Standard-XML-Format (NunitXML). Dieses Standard-XML ermöglicht es uns, die Ergebnisse unserer Tests zu automatisieren und daraus tolle Berichte zu erstellen!

Ich habe eine PowerShell-Klasse geschrieben, mit der wir entweder einen individuellen Bericht pro Server oder einen globalen Bericht erstellen können, der in verschiedenen Formaten (docx, html usw.) generiert werden kann.

Der individuelle Bericht basiert auf einem Open-Source-Projekt namens "ReportUnit", und du kannst dir unten ein Beispiel ansehen.

Abbildung 2 Einzelne Testergebnisse

Bei Bedarf können wir sehr schnell einen Bericht über einen bestimmten Server erstellen und sofort sehen, ob alles so ist, wie wir es erwarten. Aber obwohl der Bericht wirklich gut aussieht, ist er nicht der, den ich am meisten benutze.

Der Bericht, den ich am meisten benutze, ist der globale Bericht. Er ermöglicht es uns, auf einen Blick einen Überblick über unsere gesamte Umgebung zu erhalten. Der Bericht besteht aus 5/6 Seiten pro Server. Das bedeutet, dass er ziemlich umfangreich werden kann. Da er so umfangreich werden kann, zeige ich nur die interessantesten Teile dieser Berichte.

Here under you find an extract of one of the first pages of the report. It contains a table that lists all the servers that are in a result of failure. (which means that at least one or more test has failed). Using the ‘PercentageSuccess’ row we can quickly see the average rate of compliancy per server that we have.

Abbildung 3 Globale Testübersicht

So können wir schnell sehen, wo etwas falsch konfiguriert ist. Jeder Server enthält einen detaillierten Bericht über jeden Test. Er enthält den Erfolgsstatus des Tests, die Zeit, die er für die Ausführung gebraucht hat, und eine kurze Beschreibung. (Wie hier unten gezeigt).

Abbildung 4 Detaillierte Testübersicht

Durch die Verwendung von hellen Farben werden die fehlgeschlagenen Tests sofort sichtbar. Am Ende gibt es eine detaillierte Ansicht der fehlgeschlagenen Tests mit einer kurzen Beschreibung, wie im Screenshot unten zu sehen ist.

Abbildung 5 Detailansicht fehlgeschlagene Tests

Mit dieser Methode können wir den aktuellen Zustand unserer Server überprüfen und einen Server identifizieren, der seit der Bereitstellung falsch konfiguriert (oder verändert) wurde. Wenn dies eine große Anzahl von Servern betrifft, sehen wir es im globalen Bericht und können dann rechtzeitig eine Lösung für die gesamte Gruppe von Servern bereitstellen.

Ich würde gerne wissen, ob du Pester in deiner Umgebung implementiert hast und wenn ja, wie verwendest du es in deiner Umgebung? Nur Unit-Tests? Oder auch für die Betriebsvalidierung? Bitte teile deine Erfahrungen mit uns!

Ich hoffe, du hast diesen Artikel genauso gerne gelesen wie ich ihn geschrieben habe, und ich freue mich darauf, unten im Kommentarbereich von deiner Pester Implementierung zu lesen.

Stéphane Van Gulick

Stéphane Van Gulick

DevOps Engineer III

Mehr getIT-Beiträge

Bereit für Swisscom

Finde deinen Job oder die Karrierewelt, die zu dir passt. In der du mitgestalten und dich weiterentwickeln willst.

Was du draus machst, ist was uns ausmacht.

Zu den Karrierewelten

Zu den offenen Security Stellen