← Windows Server Administration
PowerShell im Serverbetrieb
PowerShell ist das Werkzeug, mit dem ein Windows Server verwaltet wird — und die drei Stellen, an denen es im Betrieb schiefgeht, sind immer dieselben: eine Abfrage, die die Anzeige filtert statt der Eigenschaft; eine Fehlerbehandlung, die nie auslöst, weil der Fehler nicht beendend ist; und ein Lauf über zweihundert Hosts, der Erfolg meldet, obwohl er dreißig davon nie erreicht hat. Dieses Modul behandelt PowerShell als Handwerkszeug des Serverbetriebs; der Track powershell-automation führt dieselben Themen als eigene Disziplin weiter.
Lehrtext · 12 Abschnitte · zuletzt geprüft: 2026-09-03
Eine Shell, die keine Textzeilen weiterreicht
Wer von der Kommandozeile kommt, erwartet von einer Shell Text: Ein Befehl gibt Zeilen aus, das nächste Werkzeug filtert sie. PowerShell arbeitet anders — zwischen zwei Befehlen fließen Objekte mit benannten Eigenschaften. Das klingt nach einer Feinheit und ändert die Arbeitsweise vollständig, weil man nicht mehr rät, an welcher Spaltenposition ein Wert steht.
Objektpipeline — Ein Befehl gibt strukturierte Objekte weiter, keine formatierten Zeilen. Was auf dem Bildschirm erscheint, ist erst die letzte Stufe — eine Darstellung des Objekts, nicht das Objekt selbst.
powershell-automation behandelt PowerShell als eigene Disziplin — Module, Tests, Freigabeprozesse, Secret-Handhabung. Hier geht es um das, was ein Windows-Server-Administrator davon täglich braucht: die Objektpipeline lesen, Fehler richtig auffangen, einen Befehl kontrolliert auf viele Hosts bringen. Wer tiefer einsteigen will, findet die Fortsetzung dort.Der praktische Gewinn zeigt sich beim ersten Vergleich. Get-Service liefert Dienstobjekte, aus denen sich Name, Status, Starttyp und Abhängigkeiten einzeln abfragen lassen — ohne dass jemand eine Ausgabe zerschneidet. Dieselbe Abfrage über zwanzig Server liefert zwanzigmal dieselbe Struktur, sortierbar und vergleichbar. Genau daran hängt der Unterschied zwischen einem Handgriff und einem Verfahren: Ein Verfahren braucht ein Ergebnis, das sich weiterverarbeiten lässt.
Was auf dem Bildschirm steht, ist nicht das Objekt
Die Anzeige eines Cmdlets zeigt eine Auswahl der Eigenschaften — nicht alle. Wer nach etwas sucht, das nicht angezeigt wird, hält es leicht für nicht vorhanden. Das Gegenmittel ist ein einziges Cmdlet.
Get-Member- Zeigt alle Eigenschaften und Methoden eines Objekts. Der erste Befehl bei etwas Unbekanntem.
Select-Object -Property *- Zeigt alle Eigenschaftswerte statt der voreingestellten Auswahl.
Where-Object- Filtert nach Eigenschaften — nicht nach Zeichenketten in einer Ausgabe.
Format-Table,Format-List- Erzeugen Darstellung. Danach ist das Objekt weg; ein
Format-*gehört ans Ende einer Pipeline, nie in die Mitte.
Get-Service | findstr Stopped funktioniert scheinbar und ist trotzdem falsch: Es durchsucht die formatierte Anzeige. Ändert sich die Spaltenbreite, ändert sich das Ergebnis. Get-Service | Where-Object Status -eq Stopped fragt die Eigenschaft und ist unabhängig von der Darstellung. Derselbe Fehler in groß ist ein Skript, das den Erfolg eines Vorgangs an einer Textmeldung festmacht statt an einem Rückgabewert.Drei Fragen an einen unbekannten Befehl
Auf einem Server steht man regelmäßig vor einem Cmdlet, das man nicht kennt — aus einer Rolle, einem Herstellermodul oder einem fremden Skript. Drei Befehle klären es, ohne dass man etwas ausprobieren muss.
Get-Command -Noun WindowsFeature— welche Verben gibt es zu diesem Gegenstand?Get-Help <Cmdlet> -Full— Parameter, Beispiele und, entscheidend, ob es-WhatIfunterstützt.<Cmdlet> ... | Get-Member— was kommt eigentlich zurück?
Die dritte Frage wird am häufigsten übersprungen und ist im Serverbetrieb die wichtigste: Ein Skript, das den Rückgabewert eines Cmdlets weiterverarbeitet, muss wissen, ob dort ein einzelnes Objekt, eine Liste oder gar nichts ankommt. Install-WindowsFeature etwa liefert ein Ergebnisobjekt mit einem Erfolgskennzeichen — wer es ignoriert, prüft den Lauf gar nicht.
Ein fremdes Cmdlet aus einem Herstellermodul
Szenario
In einem übernommenen Skript steht ein Aufruf Set-AppNodeState -Node $n -State Drain. Das Modul stammt vom Hersteller der Anwendung, eine Dokumentation liegt nicht vor. Bevor du das Skript auf 30 Knoten anwendest, willst du wissen, was der Aufruf tut.
Anforderungen
- Keine Änderung an einem produktiven Knoten während der Prüfung.
Schritte
Get-Command Set-AppNodeState -Syntax— welche Parameter gibt es, und ist-WhatIfdarunter?Get-Help Set-AppNodeState -Full— gibt es Beispiele und eine Beschreibung der Zustände?Get-Command -Noun AppNodeState— existiert einGet--Gegenstück, mit dem sich der Zustand gefahrlos lesen lässt?- Das lesende Gegenstück auf einem Knoten ausführen und die Ausgabe durch
Get-Memberschicken. - Erst danach entscheiden, ob
-WhatIfals Sicherheitsnetz taugt — oder ob es den Parameter gar nicht gibt.
Merksatz: Die Antwort auf Schritt 1 entscheidet über den Rest: Fehlt -WhatIf, ist der Probelauf keine Option und der Test muss auf einem entbehrlichen Knoten stattfinden. Das ist eine Erkenntnis, die zwei Minuten kostet und einen Breitenlauf verhindert, den man nicht zurücknehmen kann.
Provider: dasselbe Verb für verschiedene Welten
PowerShell bildet nicht nur das Dateisystem als Laufwerk ab, sondern auch andere hierarchische Speicher. Get-ChildItem funktioniert deshalb an mehreren Stellen mit derselben Syntax.
C:\- Dateisystem. Elemente sind Dateien und Ordner.
HKLM:\- Registry. Elemente sind Schlüssel, Werte sind Eigenschaften daran.
Cert:\- Zertifikatspeicher. Elemente sind Speicher und Zertifikate.
WSMan:\- Konfiguration des Remoting-Dienstes.
Get-ChildItem Unterschlüssel und keine Werte. Wer einen Wert sucht, braucht Get-ItemProperty. Diese Unterscheidung ist der häufigste Grund, warum ein Skript einen vorhandenen Registry-Wert nicht findet und daraus schließt, er sei nicht gesetzt.`-WhatIf` ist keine Eigenschaft der Sprache
-WhatIf zeigt, was ein Befehl täte, ohne es zu tun. Es ist bequem und wird routinemäßig als Sicherheitsnetz vor einem Breitenlauf benutzt — mit einer Annahme, die nicht stimmt: dass es überall gilt.
Ein Cmdlet unterstützt -WhatIf nur, wenn es das ausdrücklich anmeldet. In einer eigenen Funktion geschieht das über [CmdletBinding(SupportsShouldProcess)] und einen Aufruf von $PSCmdlet.ShouldProcess(...) an der Stelle, an der die Änderung passiert. Ohne beides gibt es den Parameter nicht — und mit dem ersten allein gibt es ihn, ohne dass er etwas verhindert.
robocopy, sc.exe, ein Herstellerwerkzeug —, kennt dieses Programm -WhatIf nicht und führt aus. Ein Skript kann also mit -WhatIf laufen, harmlos aussehen und trotzdem Fakten schaffen. Der belastbare Test ist deshalb nicht der Schalter, sondern das Lesen der Zeilen: Welche davon rufen etwas auf, das PowerShell nicht kontrolliert?Fehler, die keine Ausnahme auslösen
PowerShell kennt zwei Arten von Fehlern, und der Unterschied entscheidet darüber, ob ein try/catch überhaupt etwas auffängt.
- Beendender Fehler
- Bricht die Anweisung oder das Skript ab. Wird von
try/catchgefangen. - Nicht beendender Fehler
- Meldet den Fehler und macht weiter. Wird nicht gefangen — der
catch-Block läuft nie. -ErrorAction Stop- Macht aus einem nicht beendenden Fehler einen beendenden — für diesen einen Aufruf.
$ErrorActionPreference- Setzt das Verhalten für die Sitzung oder das Skript.
try/catch um so einen Aufruf herum fängt nichts, läuft durch und schreibt am Ende seine Erfolgsmeldung. Genau so entstehen Breitenläufe, die auf 200 Hosts Erfolg melden und auf 30 davon nie etwas geändert haben. Wer einen Fehler behandeln will, muss ihn zuerst zu einem beendenden machen.Ein zweiter Punkt gehört dazu: $ErrorActionPreference global auf Stop zu setzen, wirkt wie eine elegante Abkürzung und hat einen Preis. Es macht auch solche Fehler beendend, die ein Cmdlet bewusst als Randfall meldet — etwa eine nicht gefundene Datei bei einer Suche über viele Pfade. Ein Skript bricht dann an einer Stelle ab, an der es weiterlaufen sollte. Der gezielte Schalter am einzelnen Aufruf ist mehr Schreibarbeit und trifft die Absicht genauer.
Externe Programme melden anders
Ein aufgerufenes .exe ist kein Cmdlet. Es wirft keine Ausnahme, füllt keinen Fehlerstrom nach PowerShell-Regeln und meldet sein Ergebnis über einen Rückgabewert des Betriebssystems.
$LASTEXITCODEenthält den Rückgabewert des zuletzt aufgerufenen externen Programms.$?sagt, ob der letzte Befehl aus Sicht von PowerShell erfolgreich war — bei externen Programmen ist das eine schwächere Aussage.- Ein
try/catchum einen.exe-Aufruf fängt nichts, solange der Aufruf selbst gelingt.
Besonders tückisch sind Programme, deren Rückgabewerte nicht der Konvention „null gleich Erfolg“ folgen. robocopy etwa benutzt die unteren Bits für Teilerfolge: Ein Wert ungleich null kann bedeuten, dass Dateien kopiert wurden — also genau das, was man wollte. Ein Skript, das stumpf auf null prüft, bricht dann bei einem erfolgreichen Lauf ab. Vor dem Prüfen steht die Frage, was die Rückgabewerte dieses Programms überhaupt bedeuten.
Daraus folgt eine Regel für jedes Skript, das ein externes Programm aufruft: Der Rückgabewert muss unmittelbar nach dem Aufruf gelesen und ausgewertet werden. Unmittelbar heißt wörtlich — schon ein Write-Host dazwischen kann $LASTEXITCODE überschreiben, und eine Auswertung drei Zeilen später prüft dann etwas anderes, als sie zu prüfen glaubt. Was geprüft werden soll, gehört in eine eigene Variable, direkt in der Zeile danach.
Die Ausführungsrichtlinie ist keine Sicherheitsgrenze
Die Ausführungsrichtlinie bestimmt, unter welchen Bedingungen PowerShell Skripte lädt und ausführt. Sie wird häufig als Schutzmaßnahme geführt, und die Dokumentation widerspricht dem ausdrücklich.
Praktisch relevant ist sie trotzdem, und zwar zusammen mit einer Eigenschaft aus dem Dateisystem: Unter RemoteSigned startet ein Skript nicht, das den Zone.Identifier-Datenstrom trägt — also aus dem Internet stammt. Unblock-File entfernt diesen Strom. Das ist die häufigste Begegnung mit der Richtlinie im Serverbetrieb, und sie ist keine Sicherheitsfrage, sondern eine Herkunftsfrage.
Für den Betrieb ergibt sich daraus eine nüchterne Haltung. Die Richtlinie auf Bypass zu setzen, um ein Problem zu lösen, gibt keine echte Kontrolle auf — aber es beseitigt einen Hinweis, der oft auf etwas anderes deutet: dass ein Skript aus einer Quelle stammt, die niemand geprüft hat. Wer die Richtlinie ändert, sollte das begründen können, und wer sie beibehält, sollte sie nicht für mehr halten, als sie ist.
Der Weg auf einen anderen Server
Remoting ist der Verwaltungsweg, auf den eine Server-Core-Entscheidung hinausläuft. Drei Formen sind zu unterscheiden, und sie verhalten sich verschieden.
Enter-PSSession- Interaktive Sitzung auf einem Host. Gut zum Nachsehen, schlecht zum Wiederholen.
Invoke-Command -ComputerName …- Führt einen Skriptblock auf einem oder vielen Hosts aus. Der Arbeitsweg.
New-PSSession+-Session- Hält Sitzungen offen. Sinnvoll, wenn mehrere Aufrufe auf dieselben Hosts gehen.
-ThrottleLimit- Begrenzt die Zahl gleichzeitiger Verbindungen — eine Ressourcenbremse auf dem aufrufenden Rechner.
$using: markiert ist. Der Fehler ist heimtückisch, weil ein leerer Wert oft keinen Fehler erzeugt, sondern eine andere Bedeutung hat — ein leerer Filter trifft alles, ein leerer Pfad zeigt auf das Arbeitsverzeichnis.Für den Einstieg genügt die mittlere Zeile. Invoke-Command mit einer Hostliste und einem Skriptblock deckt fast alles ab, was im Alltag ansteht, und hat gegenüber der interaktiven Sitzung zwei Vorteile: Der Aufruf lässt sich aufschreiben, und er läuft auf allen Hosts mit demselben Wortlaut. Die interaktive Sitzung ist das Werkzeug für den Einzelfall, bei dem man noch nicht weiß, wonach man sucht — sie hinterlässt aber nichts, was jemand wiederholen könnte.
Was aus einer Remotesitzung zurückkommt
Lebende .NET-Objekte lassen sich nicht über das Netz schicken. PowerShell serialisiert sie deshalb, überträgt die Darstellung und baut auf der eigenen Seite ein Objekt daraus.
Get-Process über Remoting liefert also Prozessobjekte, auf denen Kill() nicht existiert. Wer eine Methode braucht, ruft sie innerhalb des Skriptblocks auf dem Zielserver auf — dort ist das Objekt noch lebendig.Kurzcheck
Ein Skript holt mit Invoke-Command -ComputerName $Hosts { Get-Process w3wp } Prozesse von 20 Servern und ruft danach lokal $p.Kill() auf. Was passiert?
- Der Aufruf schlägt fehl, weil das zurückgegebene Objekt keine Methoden hat
- Der Prozess wird auf dem lokalen Rechner beendet, nicht auf dem Zielserver
- Der Aufruf wirkt, weil PowerShell die Methode über die offene Sitzung weiterleitet
Treffer. Richtig. Deserialisierte Objekte tragen Eigenschaften, keine Methoden. Das Beenden gehört in den Skriptblock, der auf dem Zielserver läuft.
Die Zielmenge ist ein Sicherheitsinput
Ein Befehl auf einem Server ist eine Änderung. Derselbe Befehl auf 200 Servern ist ein Change mit Ausfallradius — und der Unterschied steckt in einer Variablen, die oft aus einer Textdatei kommt.
Bevor ein Skriptblock über eine Liste läuft, gehören drei Fragen beantwortet: Woher stammt die Liste, wann wurde sie zuletzt geprüft, und was passiert, wenn ein Eintrag darin falsch ist? Eine Liste, die jemand aus einem Inventar exportiert hat, kann ausgemusterte Server enthalten — oder Server, die zu einem anderen Verantwortungsbereich gehören.
Invoke-Command auf 200 Hosts, von denen 30 nicht erreichbar sind, liefert Ergebnisse von 170 und Fehler von 30 — und wenn die Fehler nicht beendend sind, sieht die Zusammenfassung am Ende wie ein voller Erfolg aus. Ein Breitenlauf braucht deshalb zwingend eine Auswertung je Host: welcher Host hat geantwortet, welcher hat einen Fehler gemeldet, welcher gar nichts. Die Summe reicht nicht.Dieselbe Überlegung gilt für den Skriptblock selbst. Ein Filter, der auf einem Server elf Objekte trifft, kann auf einem anderen zweitausend treffen — etwa weil dort eine andere Rolle installiert ist. Der Probelauf auf einem repräsentativen Host beantwortet nicht nur, ob der Befehl funktioniert, sondern auch, wie viel er anfasst. Beides gehört geprüft, bevor die Liste länger wird als die Zahl der Server, die man an einem Nachmittag wiederherstellen könnte.
Der kontrollierte Breitenlauf
Aus alledem ergibt sich eine Reihenfolge, die im Serverbetrieb trägt. Sie kostet mehr Zeit als der direkte Aufruf und ist genau dort richtig, wo ein Fehler mehr als einen Server trifft.
-ErrorAction Stop) und den Rückgabewert externer Programme prüfen.→Auf einem Host schreibend ausführen und das Ergebnis fachlich prüfen.→Auf die Breite gehen, mit Auswertung je Host statt einer Summe.→Das Ergebnis je Host ablegen — das ist der Nachweis, nicht die Bildschirmausgabe.Jetzt anwenden
Diesen Stoff gibt es als Modul mit bewerteten Entscheidungs-Checks — dieselbe Einführung, danach die Übungen.
Zum Modul →Quellen & Aktualität9 Primärquellen · zuletzt geprüft:
- 01about_Execution_Policies
- 02about_Try_Catch_Finally
- 03about_Remote_Output
- 04Invoke-Command (Microsoft.PowerShell.Core)
- 05Everything you wanted to know about ShouldProcess
- 06Unblock-File (Microsoft.PowerShell.Utility)
- 07Get-Item (Microsoft.PowerShell.Management)
- 08Install-WindowsFeature (ServerManager)
- 09Registry Hives