Skript-Debugging für die PowerShell
In PowerShell-Skripte für die Automatisierung der IT-Aufgaben schleichen sich unweigerlich Fehler ein. Das macht den IT-Verantwortlichen nicht gleich zu einem schlechten Skripter, er muss jedoch Bugs identifizieren und ausmerzen, bevor sie in der Produktionsumgebung für Chaos sorgen. Im ersten Teil des Workshops geht es um die Besonderheiten des PowerShell-Debugging und verbreitete Fehlerquellen.
Kein Softwarecode ist von Anfang an perfekt, so sehr die Entwickler sich auch anstrengen mögen. Dies gilt insbesondere für Skriptcode in der PowerShell, der oft nicht von professionellen Programmierern erstellt wird, sondern von Administratoren zwischen all den anderen Aufgaben, die sie täglich zu erledigen haben. Manchmal sind die Fehler im Skript leicht festzustellen – aufgerufene Cmdlets oder Funktionen liefern Fehlermeldungen oder das Skript macht offensichtlich nicht das, was es soll. In anderen Fällen treten Fehler nur unter bestimmten Umständen auf, beispielsweise wenn dem ausführenden Account notwendige Berechtigungen fehlen oder gewisse Features des Betriebssystems nicht installiert sind. In jeder Situation müssen Sie den Fehler lokalisieren und beheben können, damit Ihr Skript sicher und zuverlässig seinen Dienst verrichten kann.
Für die meisten verbreiteten Programmiersprachen existieren spezielle Debugger, die sich in den fertig kompilierten Prozess einklinken und das Geschehen während der Ausführung oder zumindest bei Fehlern in Echtzeit untersuchen. Bei Skriptsprachen muss bei Anpassungen am Code nicht erst aufwendig kompiliert und gelinkt werden, daher verläuft die Fehlersuche oft weniger strukturiert, nach dem Prinzip von Versuch und Irrtum. Da werden in einzelne Codezweige diagnostische Ausgaben eingebaut, um festzustellen, welcher Zweig abgearbeitet wird und welche Werte einzelne relevante Variablen in diesem Zweig jeweils haben. Diese Ausgaben müssen nach erledigter Entstörung wieder entfernt werden.
Besonderheiten des PowerShell-Debugging
Falls Sie ein solches Debugging in Ihren Skripten erwägen, sollten Sie sich dennoch nicht auf die Ausgabemethoden wie Write-Host oder Write-Output verlassen, deren Ausgaben Sie nach erfolgtem Troubleshooting irgendwie unterdrücken müssen. Vielmehr sollten Sie gleich auf die Cmdlets zurückgreifen, die dafür vorgesehen sind: Write-Verbose und Write-Debug.
Standardmäßig erfolgt aus diesen Befehlen keine Konsolenausgabe, Sie können dies jedoch bei Bedarf aktivieren. Setzen Sie dafür entweder im Skript oder in der PowerShell-Sitzung vor Aufruf des Skripts die Systemvariable "$VerbosePreference" beziehungsweise "$DebugPreference" auf den Wert "Continue" und die Befehle schicken den ihnen übergebenen String jeweils an den Ausgabe-Stream 4 (Verbose) beziehungsweise 5 (Debug). So können Sie die Diagnostikausgaben im Skriptcode belassen und die Ausgaben nur bei der Fehlersuche einschalten. Falls das Setzen der Umgebungsvariablen unerwünscht oder unmöglich ist, können Sie das Verhalten auch mit Parametern steuern. Beginnen Sie dafür Ihr Skript oder Ihre Funktion mit
[CmdletBinding()] Param()
und Sie können auf die Standardparameter "-Verbose" und "-Debug" zurückgreifen, die die jeweilige Ausgabe aktivieren. Hier tritt ein Unterschied im Verhalten zwischen Write-Verbose und Write-Debug zu Tage: Der Switch "-Debug" stellt die Präferenz nicht auf "Continue", sondern auf "Inquire". Das führt dazu, dass Sie nach jeder diagnostischen Ausgabe aufgefordert werden, das Skript fortzusetzen oder anzuhalten. Wählen Sie hier die Option "H", bekommen Sie bereits den ersten Vorgeschmack auf den PowerShell-Debugger, denn nun ist die Ausführung mitten im Skript angehalten und alle Variablen haben die Werte, die Ihnen im Laufe des Skripts zugewiesen worden sind. Sie können die Werte durch Zuweisungen korrigieren und anschließend mit dem exit-Befehl die Frage nach dem Fortsetzen des Skripts wiederholt aufrufen, um sie diesmal mit "J" zu beantworten und die Ausführung fortzusetzen, oder mit der Antwort "B" das Skript zu beenden.
Heute stehen nicht nur Profi-Programmierern, sondern auch Gelegenheitsskriptern deutlich mächtigere Debugging-Werkzeuge zur Verfügung. Bevor wir uns diesen Möglichkeiten zuwenden, schauen wir uns zunächst einmal einige typische Skriptfehler an.
Verbreitete Fehlerquellen
Fehler in PowerShell-Skripten fallen üblicherweise in verschiedene Kategorien. Die erste davon beschreibt Logikfehler. So führt "-lt" statt "-gt" im Zusammenspiel mit "if" oder "Where-Object" dazu, dass der falsche Zweig abgearbeitet wird oder die falschen Objekte aus der Pipeline ausgewählt werden. Derselbe Fehler in einer Schleifen-Ausgangsbedingung (for, while oder do/until) kann dazu führen, dass die Schleife unendlich oft durchläuft. Logikfehler schleichen sich häufig dadurch ein, dass der Skripter im Laufe der Entwicklung die Vergleichswerte ins Gegenteil umdreht, die Vergleichsbedingung jedoch unverändert lässt. Um einen solchen Fehler zu erkennen, hilft nur scharfes Nachdenken, denn nur Sie als Autor des Skripts können wissen, was beabsichtigt war.
Als Nächstes sind Typfehler zu nennen: Die PowerShell verwendet zwar innerhalb der Sprache nur schwache Typen, die sie jedoch für die tatsächliche Verarbeitung in starke .NET-Typen konvertiert. Und hier gilt die Regel, dass bei allen Operationen, einschließlich der Vergleichsoperatoren, der erste Operand den Ergebnistyp bestimmt, sodass alle nachfolgenden Werte in diesen Typ umgewandelt werden. Das führt bisweilen zu Phänomenen, die sich auch aus der Diagnostik-Ausgabe nach Kernighan nicht gleich erklären lassen, ein Beispiel: Wir arbeiten mit den Variablen $a = 10, $b = "10" und $c = 9. Nun ergibt $a -gt $c "True", $b -gt $c "False", $a + $b "20" und $b + $a ergibt 1010 oder genauer "1010". Auf der Konsole sehen die Werte von $a und $b identisch aus, die Klarheit schafft erst die Anwendung der Methode ".GetType()". In diese Kategorie gehören beispielsweise auch die Fehler, die daraus entstehen, dass manche Cmdlets immer Arrays zurückgeben, während andere bei exakt einem Ergebnis ein Skalar des entsprechenden Typs liefern, bei mehreren Ergebnissen jedoch ein Array.
Base-0-Fehler treten bei der Arbeit mit Arrays (dazu gehören auch Strings, wenn Sie einzelne Zeichen oder Unterstrings ansprechen) in der PowerShell auf. Hierbei beginnt die Indizierung der Elemente stets bei 0. Die Skripter übersehen dies oft und versuchen, bei einem Array der Länge N auf das N-te Element zuzugreifen, obwohl das letzte Element in Wirklichkeit den Index N-1 trägt. Die String-Funktionen quittieren diese Versuche korrekterweise mit einem Fehler, beim Zugriff auf Arrays hingegen liefert die PowerShell bei einem nicht existierenden Element einfach stillschweigend "$null" zurück.
Auch Namen und Scopes von Variablen sind eine verbreitete Fehlerursache. Die PowerShell verlangt von Ihnen nicht, dass Sie Variablen vor der Verwendung initialisieren. Entdeckt die PowerShell beim Abarbeiten des Skripts einen neuen Variablennamen, weist diese dieser Variable $null zu und arbeitet damit, bis die Variable einen neuen Wert bekommt. Ungewollt entsteht diese Situation, wenn Sie sich bei einem Variablennamen verschreiben – die Syntaxprüfung der PowerShell-Editoren hilft hier nicht weiter, denn es ist ja ein aus Sicht der Sprache legitimer Vorgang. Die PowerShell-Erweiterung von VSCode überwacht immerhin den umgekehrten Fall, wenn Sie eine Variable mit einem Wert bestücken, aber niemals einsetzen. Gegen einen Tippfehler im Variablennamen hilft dies nur dann, wenn die Zeile mit dem Tippfehler die einzige Verwendung dieser Variablen darstellt.
Variablen falsch eingesetzt
Ein weiterer Fehlerfall, der noch schwieriger aufzuspüren ist, entsteht dann, wenn Variablen in unterschiedlichen Scopes, zum Beispiel innerhalb und außerhalb von Funktionen, erstellt oder verwendet werden. Nutzen Sie eine Variable innerhalb einer Funktion, ohne dass sie explizit initialisiert oder als Parameter der Funktion deklariert ist, schaut die PowerShell nach, ob es die Variable nicht bereits im globalen Scope gibt. Änderungen, die die Funktion am Wert der Variablen vornimmt, werden zwar nicht an den globalen Scope zurückgegeben, der Ausgangswert innerhalb der Funktion wird jedoch aus dem globalen Scope übernommen. Oft entstehen solche Fehler bei Zähler- oder Indexvariablen, die einfach "$i" oder "$n" heißen und in verschiedenen Scopes gleichnamig vorhanden sein können.
Ein Sonderfall dieses Phänomens ist das Entwickeln von Skripten in einer grafischen Umgebung (PowerShell ISE oder Visual Studio Code mit der PowerShell-Erweiterung) und deren anschließende Ausführung in einer reinen Shell oder innerhalb einer Anwendung. Die Werte aller Variablen bleiben nach der Ausführung in der Sitzung des grafischen Codeeditors erhalten und mehrere Skripte, die gleichzeitig zum Bearbeiten geöffnet sind, haben Zugriff auf diese Werte. Auf diese Weise kann es passieren, dass ein Skript eine Variable initialisiert und ein anderes Skript in derselben Sitzung diesen Wert verwendet, ohne dass es selbst dafür gesorgt hat, dass der Wert zugewiesen wird.
Um diesem Verhalten vorzubeugen, sollten Sie sich angewöhnen, Skripte in unterschiedlichen Instanzen der Entwicklungsumgebung zu öffnen. In der PowerShell ISE erreichen Sie dies innerhalb eines Fensters indem Sie mit "Strg + T" eine neue "PowerShell-Registerkarte" öffnen. Diese Registerkarten laufen zwar innerhalb desselben Windows-Prozesses, die PowerShell-Umgebungen sind jedoch voneinander isoliert. Innerhalb einer Registerkarte können Sie mehrere Skripte öffnen, die sich die PowerShell-Umgebung teilen. In Visual Studio Code hingegen hilft nur das Starten einer neuen VSCode-Instanz.
Infrastruktur nicht berücksichtigt
Welche Entwicklungsumgebung Sie auch immer verwenden, Sie sollten sich angewöhnen, jedes Skript vor der Freigabe in Produktion in einer frisch gestarteten Umgebung laufen zu lassen, um Querwirkungen aus anderen Skripten oder aus dem bisherigen Entwicklungsprozess zuverlässig auszuschließen. Annahmen über die Umgebung zu treffen ist ein sehr häufiges Phänomen und diese rangieren vom Typ des Betriebssystems (32 oder 64 Bit, Server oder Client) über die verwendete PowerShell-Version bis hin zum Vorhandensein gewisser Funktionen. Letzteres zeigt sich beispielsweise oft in Skripten, die zur Exchange-Verwaltung dienen und stillschweigend davon ausgehen, dass sowohl die Exchange Management Shell als auch die RSAT-Cmdlets für das Active Directory installiert sind. Auch Mutmaßungen zu den Berechtigungen (lokale Adminrechte, Domain Admin) oder die Konnektivität (Erreichbarkeit bestimmter interner Services oder sogar Internetdienste) fallen in dieses Problemfeld.
Je nachdem, ob das Skript in einer interaktiven Sitzung oder unbeaufsichtigt ausgeführt werden soll, können Sie bei fehlenden Voraussetzungen entweder eine Warnung ausgeben oder das Skript stillschweigend beenden. Die Direktive "#requires" am Anfang des Skripts ermöglicht, zumindest einen Teil der Voraussetzungsprüfungen (administrative Sitzung mit erhöhten Rechten, PowerShell-Version und -Edition, vorhandene Module) in harte Ausführungsbedingungen zu verwandeln.
Einige der schwer lokalisierbaren Fehler wie das Verwenden nicht initialisierter Variablen oder Indexfehler bei Arrays machen Sie sichtbar, indem Sie Ihr Skript im "Strict Mode" ausführen. Dies erreichen Sie mit dem Set-StrictMode-Cmdlet. Ab Version 3.0 sind Sie damit gegen die meisten Tippfehler und gegen Array-Indexfehler insofern geschützt, als dass sie nicht stillschweigend zu Nullwerten, sondern zu Fehlermeldungen führen.
Die PowerShell hat mehrere eingebaute Debugger. Einen sehr einfachen Code-Unterbrecher haben Sie bereits im Zusammenhang mit dem Write-Debug-Cmdlet kennengelernt. Einen weiteren Mini-Debugger aktivieren Sie mithilfe des Set-PSDebug-Befehls. Dieser bietet Ihnen drei Funktionalitäten, die oft bereits ausreichen, um einem Fehler auf die Schliche zu kommen.
- "-Strict" schaltet den StrictMode in der Version 1.0 ein, was lediglich das Verwenden nicht initialisierter Variablen abfängt.
- "-Step" hält die Ausführung nach jedem Befehl an, sodass Sie mit "H" denselben Mini-Debugger aktivieren können wie nach Write-Debug.
- "-Trace" aktiviert die ausführliche Protokollierung aller ausgeführter Befehle. Die zulässigen Werte sind "0" (deaktiviert), "1" (grundlegendes Tracing) oder "2" (erweitertes Tracing). Die Tracing-Nachrichten wandern in den Debug-Stream, beeinträchtigen also nicht die normalen Ausgaben des Skripts.
Das eingestellte Debugging-Verhalten gilt für die gesamte PowerShell-Sitzung, somit auch für die interaktiven Befehle einschließlich Starten des zu untersuchenden Skripts. Um das Verhalten auf den Ursprungszustand zurückzusetzen, verwenden Sie einfach Set-PSDebug -Off.
Ein sehr nützlicher Befehl beim Troubleshooten komplexer Skripte und Module ist Get-PSCallStack. Damit erhalten Sie Zugriff auf alle nachfolgenden Skript- und Funktionsaufrufe, die zu der Stelle führen, an der der Debugger das Skript unterbrochen hat. Sie können diesen Befehl auch in Ihrem eigenen Code verwenden, um beispielsweise in Logdateien festzuhalten, welches Skript oder welche Funktion die gerade protokollierte Meldung erzeugt. Der unmittelbare Aufruf Ihrer Logging-Funktion lautet dabei
(Get-PSCallStack)[-1].Command.
Doch der PowerShell-Debugger ist viel flexibler als die zwei betrachteten Beispiele. Ein guter Ausgangspunkt für das Erforschen der Debugging-Funktionalität ist der about_Debuggers-Artikel. Achten Sie bei allen das PowerShell-Debugging betreffenden Microsoft-Dokumenten darauf, dass Sie die Ansicht auf diejenige PowerShell-Version schalten, mit der Sie tatsächlich arbeiten, denn der Debugger hat einige Erweiterungen und Verbesserungen erfahren, insbesondere in der siebten Generation der PowerShell.
Debuggen in der Shell
Um in den Debugger zu gelangen, können Sie entweder ein laufendes Skript in der Konsole mit "Strg+Break" unterbrechen. Dies ist besonders dann nützlich, wenn das Skript unerwartet lange läuft und Sie eine Dauerschleife vermuten. Oder Sie setzen einen Breakpoint über den Befehl Set-PSBreakpoint. In beiden Fällen ändert sich der PowerShell-Prompt – er erhält den Prefix "[DBG]:". Damit wissen Sie, dass Sie sich im Debugger befinden. Hier können Sie mit den Befehlen "s" oder "StepInto", "v" oder "StepOver" beziehungsweise "o" oder "StepOut" die Ausführung schrittweise fortsetzen. Weitere nützliche Befehle sind "l" oder "list" zum Anzeigen der gerade ausgeführten Stelle im Skript, "k" zum Aufruf von Get-PSCallStack und "Enter" zur Wiederholung des letzten Debugger-Befehls.
Sind Sie mit dem Troubleshooting fertig, können Sie mit "q" beenden oder mit "c" beziehungsweise "continue" oder "exit" ohne weitere Unterbrechungen fortsetzen. Aber Achtung: Die PowerShell-Dokumentation ist an dieser Stelle irreführend und setzt "exit" mit dem Debugger-Befehl "q" gleich. Zudem finden Sie darin noch den Befehl "stop", der in keiner der aktuellen PowerShell-Versionen existiert.
Das Setzen von Breakpoints in der PowerShell ist sehr flexibel. Neben der Möglichkeit, einen bestimmten Ort, also eine Zeile und optional sogar eine Spalte, in einem Skript oder Modul anzugeben, an dem die Ausführung unterbrochen werden soll, können Sie mit dem Set-PSBreakpoint-Befehl
- Einen bestimmten Befehl, also ein Cmdlet oder eine Funktion, angeben, vor dessen Ausführung der Debugger einspringen soll. Dies geschieht mithilfe des Parameters "-Command", der übrigens auch Arrays akzeptiert, sodass Sie mehrere solche Breakpoints mit einem Befehl setzen können.
- Eine oder mehrere Variablen über den Parameter "-Variable" hinterlegen, sodass beim Zugriff auf diese Variablen der Debugger einspringt. Dabei erlaubt "-Mode" die Angabe des Zugriffs, also Read, Write oder ReadWrite, der die Unterbrechung bewirken soll. Falls also eine Variable falsche Werte aufweist, können Sie gezielt nach den Stellen forschen, an denen ihr diese Werte zugewiesen werden.
- Eine Aktion in Form eines Skriptblocks über "-Action" einbauen, die beim Erreichen des Breakpoints starten soll. Falls der Skriptblock nicht mit einem "break" endet, wird die Ausführung des Skripts tatsächlich nicht unterbrochen. Sie können den Skriptblock aber nutzen, um diagnostische Ausgaben zu tätigen und beispielsweise den Wert der gewünschten Variablen in eine Datei zu speichern. Geben Sie keine Aktion an, unterbricht die Ausführung und der Debugger-Prompt wird aktiviert.
Mit dem Get-PSBreakpoint-Befehl listen Sie die in der aktuellen Sitzung gesetzten Breakpoints auf. Die Cmdlets Enable-PSBreakpoint und Disable-PSBreakpoint erlauben es, Breakpoints zu aktivieren und zu deaktivieren, ohne sie löschen und neu anlegen zu müssen. Ein mit dem Set-PSBreakpoint-Cmdlet erstellter Breakpoint ist zunächst immer aktiv.
Debuggen im Editor
So mächtig der eingebaute PowerShell-Debugger auch ist, Skriptschreiber sind oft eher visuell veranlagt und kommen mit dem Debuggen an der Kommandozeile nicht immer gut zurecht. Um auch dieser Zielgruppe die Fehlersuche zu ermöglichen, halten alle IDEs, also Integrated Development Environments, auch einen grafischen Debugger bereit. Beliebte PowerShell-Editoren sind da keine Ausnahme.
Die mit Windows ausgelieferte PowerShell ISE ist im Großen und Ganzen nur ein Wrapper für den oben beschriebenen Konsolen-Debugger. Der Editor erlaubt lediglich das grafische Setzen, Löschen, Aktivieren und Deaktivieren von zeilenbasierten Breakpoints sowie die Hervorhebung der gerade ausgeführten Code-Zeile bei schrittweiser Ausführung. Ferner sind die meisten Debugger-Befehle mit Funktionstasten abgebildet, mit Ausnahme der Funktion zur Unterbrechung des laufenden Skripts. Diese heißt im englischen Originalmenü "Break All" und hört auf die Tastenkombination "Strg+B". Im deutschen Windows nennt sich der Menüpunkt "Alle trennen", die Tastenkombination ist im Menü nicht aufgeführt, funktioniert aber genauso gut wie in der englischen Fassung.
Eine deutliche Erweiterung der Funktionalität der ISE bietet das Modul "ISESteroids" des deutschen Entwicklers und PowerShell-Experten Dr. Tobias Weltner. Dieses erweitert die ISE um wichtige Funktionen wie Projektverwaltung, kontextsensitive Hilfe und auch Debugging-Werkzeuge wie Variableninspektion, Syntaxvorschläge und einiges mehr. Die Entwicklung des Moduls ist mittlerweile zum Stillstand gekommen, die aktuelle Version ist von 2019, nicht zuletzt deshalb, weil Visual Studio Code viele der Funktionen inzwischen adäquat abbildet. Wenn Sie jedoch in der ISE zu Hause sind und Ihre primäre Skriptumgebung die Windows-PowerShell ist, können Sie nach wie vor eine Lizenz für ISESteroids erwerben und für Ihre Skriptentwicklung nutzen.
Interessanterweise hat Microsoft nie den Debugging-Support für die PowerShell in das große Visual Studio eingebaut. Doch die kostenfreie kleine Schwester Visual Studio Code bietet mit der offiziellen PowerShell- und einigen weiteren Community-Erweiterungen exzellenten Support für das Schreiben und Troubleshooten von PowerShell-Skripten und -Modulen. Die Dokumentation bietet einen guten Überblick über die PowerShell-spezifischen Funktionen sowie einen Link zum zweiteiligen Blog von Keith Hill speziell über das Debuggen in VSCode. Neben der nativen PowerShell-Unterstützung bekommen Sie mit VSCode frei Haus weitere Features wie Git-Integration und Jupyter-Notebooks, die Ihre Möglichkeiten in puncto Verwalten und Verbreiten von Skriptcode noch einmal erweitern.
Spezialfälle im Debugging
Skripte, die für ein interaktives Ausführen in einer standardmäßigen Shell bestimmt sind, erlauben in der Regel schnelles und komfortables Debuggen mit guten Ergebnissen. Anders sieht es bei Skripten aus, die nur in einer bestimmten Umgebung überhaupt erst lauffähig sind, beispielsweise als Erweiterungsaktionen eines bestimmten Systems.
Ein Paradebeispiel hierfür sind Skripte, die in MDT- oder SCCM-Tasksequenzen laufen sollen und auf Objekte und Variablen der jeweiligen Umgebung angewiesen sind. In solchen Fällen müssen Sie oft die Klassen und Objekte, die außerhalb des Livesystems nicht vorhanden sind, emulieren, indem Sie sie vor oder während der Ausführung des Skripts in der Debugging-Umgebung erzeugen und entsprechenden Variablen zuweisen.
Ein weiterer Spezialfall ist das Debuggen von PowerShell-Skripten, die nicht in der Haupt-Shell laufen, sondern von dieser abgetrennt werden, um die asynchrone Ausführung mehrerer Skriptteile zu ermöglichen. Dies geschieht in der PowerShell entweder mittels Jobs oder mithilfe von Runspaces. Für beide Einsatzfälle hält die PowerShell entsprechende Cmdlets bereit, nämlich Debug-Job beziehungsweise Debug-Runspace, die Ihnen erlauben, den Konsolen-Debugger an einen laufenden Job beziehungsweise an einen aktiven Runspace anzuhängen. Das weitere Debuggen geschieht genau so wie das Debuggen des Hauptskripts an der Kommandozeile.
Fazit
Fehlerfreier Skriptcode ist ein Ziel, das ebenso wichtig anzustreben wie schwer zu erreichen ist. Besonders bei Skripten, die mit hohen Rechten und ohne Aufsicht laufen, sind Überraschungen sehr unangenehm und können zu Betriebsstörungen führen. Die PowerShell bietet einen eingebauten interaktiven Debugger, und die gängigen grafischen Entwicklungsumgebungen erweitern ihn um visuelle Hilfsmittel, die das Debugging um einiges erleichtern können. Machen Sie sich mit den vorgesehenen Debugging-Techniken vertraut, damit Ihnen das archaische "Debugging by printf()" erspart bleibt. (jp/ln)
Über den Autor: Evgenij Smirnov ist Senior Solutions Architect bei Semperis und Microsoft-MVP für Cloud & Datacenter Management sowie VMware Certified Implementation Expert für Datacenter-Virtualisierung.

Dieser Artikel erschien im IT-Administrator Sonderheft I/2024
"PowerShell – Infrastrukturen
automatisiert verwalten"
Das Sonderheft I/2024 liefert auf 205 Seiten praktisches Know-how zum Umgang mit der Microsoft-Skriptumgebung und deren Einsatz zur Automatisierung wichtiger Serversysteme. Das Autorenteam wirft unter anderem einen Blick auf die Methoden und Techniken, die die PowerShell für die Administration bereitstellt.
Bestellen Sie das Sonderheft hier als E-Paper
(Die gedruckte Ausgabe ist leider vergriffen)