Geht es nach Microsoft, ist die PowerShell das Mittel der Wahl in Sachen Windows-Automatisierung. Gleichzeitig wird die klassische Kommandozeile stetig aus dem Sichtfeld gezogen1 und gezielt unattraktiver gemacht z.B. durch Wegnahme von WMI-Abfragen.2
Wenn Umfragen schamlos Bash, Shell und PowerShell zusammenwerfen3 und die Fachpresse der PowerShell eine hohe Attraktivität nachplappert, habe ich meine Zweifel an diesem Narrativ. Zahlreiche Beispiele aus der Praxis untermauern die Zweifel und führen bei mir zu einer anderen Schlußfolgerung.
Die PowerShell ist tot
Sie war es vielleicht von Anfang an.4 Denke ich zurück, erinnere ich mich dunkel, wie ich diese zunächst als Abfallprodukt wahrnahm. Ein Dialekt mehr in der damals noch wachsenden .NET Sprachenfamilie. Ein Tool für Softwareentwickler, weniger für Administratoren. Niemand zieht ohne Not eine zusätzliche Abstraktionsebene in sein Netz. Nicht in Version 1.0 und schon gar nicht von Microsoft, das begriffen auch die tyischen Windows-Admins.
Das erkannte auch Microsoft und sprang in rascher zeitlicher Folge auf Versionsnummer 2.0, gefolgt von der zwangsweisen und ungefragten Beglückung im Windows Server 2008 R2.
Zur besseren Verdeutlichung, wie kaputt Windows ist: Diese PowerShell Version 2.0 schlummert seitdem in jedem neuen Windows parallel zur jeweils aktuellen Programmversion. Eine Deinstallation ist nach Analyse des BSI nicht möglich.5 So arbeitet Microsoft: Lieber sich mit doppeltem Murcks durchfuddeln, als ein sauberes Paketmanagement aufzusetzen.
Es kam, wie es kommen musste. Die PowerShell entwickelte sich prächtig - für Hacker und Ransomware-Autoren6. Jetzt gab es einen Grund, sich als Admin doch mit der PowerShell zu beschäftigen. Ein perfider Move durch die Hintertür.
Aktuell ist es wieder eleganter, Systeme mit .BAT Dateien in der Kommandozeile anzugreifen.7 Die heutige “Generation-Googlemail” kennt die 80er und 90er nicht mehr.
Bei meiner Recherche zum Blog durfte ein Blick auf den TIOBE-Index8 nicht fehlen. Dort ist die PowerShell Stand Anfang April 2022 abgeschlagen auf Platz 41 noch hinter VBScript (34) oder Visual Basic 6.0 (17).9 Solchen Rankings möchte ich aber nicht zu viel Bedeutung beimessen. Die Beispiele aus der Praxis sprechen für sich.
Von einfachen Skripten…
Gilt es auf einem Windows-System alle offenen Ports zu bestimmen, führt netstat10 schnell zum Ziel. Ein Befehl, ein paar kurze, knackige Parameter, fertig. Die Ausgabe kann (mit Einschränkungen) gepiped11 und z.B. seitenweise mit more ausgegeben werden.
Die PowerShell braucht zwei Befehle: Get-NetTCPConnection12 und Get-NetUDPEndpoint.13 Mit entsprechenden Parametern ausgeschmückt läuft das PowerShell Gegenstück zu netstat über mehrere Zeilen. Die akademische Frage warum das eine Connection und das andere Endpoint heißt kann ich als Softwareentwickler zwar nachvollziehen. Ein mehr robust und pragmatisch denkender Admin, der schnell eine Ausgabe seiner Ports will, schüttelt bei so einem Quatsch den Kopf.
Und so beobachte ich manchmal Menschen, wie diese stoisch die Gedenksekunde der ngen14 beim Öffnen der PowerShell erdulten, nur um dann klassische Kommandozeilen-Befehle wie netstat, ping oder nslookup einzutippen. Technisch betrachtet ist das krank.
…über GUI-Abfragen…
Im Zuge einer AD-Migration brauchte ich vor einiger Zeit grafische Ein- und Ausgaben. Dateien sollten von einem alten Userkonto zu einem neuen wandern. Nicht wenn ein Admin es will, sondern wenn der Anwender es für sich entscheidet.
In der Theorie kann die PowerShell auf System.Windows.Forms15 zugreifen und GUI Ein- und Ausgaben realisieren. Praktisch habe ich das Ganze schnell abgebrochen als absehbar wurde, was da an Spagetticode16 und Quirks zusammen kommt.
Ich bin zu alt für solche Zeitverschwendung. Eine halbe Stunde in Auto-It17 später und die Lösung stand. Mit UPX18 als Standalone Executable ohne Abhängigkeiten noch am gleichen Tag deployed.
…bis zu Sofortbildern
Von einem anderen Kunden gab es kürzlich die Anfrage, ob ich ein Screenshot-Programm kennen würde, das Bildschirmaufnahmen nicht per Tastatur-Hotkey sondern mit genau einem Mausklick macht. Ein Anwender darf nicht mit “Speichern unter” Interaktionen beschäftigt werden.
Sinn und Zweck des Ganzen ist die Dokumentation einer Siemens WinCC-Visualisierung, wo dem Bediener keine physische Tastatur zur Verfügung steht. Die Dateinamen müssen natürlich dem ISO-860119 Standard entsprechen.
Die PowerShell könnte das. Im Vergleich zum Auto-It Einzeiler aber in keinem wirtschaftlich vertretbaren Maßstab. Am Ende lag der Hauptaufwand bei der Erzeugung des Zeitstempels:
;
; Screenshot per Mausklick aufnehmen mit ISO 8601 Zeit-Datumstempel
; by Tomas Jakobs, 2022
; Lizenz: GPL3
;
#include <AutoItConstants.au3>
#include <WinAPI.au3>
#include <Date.au3>
#include <ScreenCapture.au3>
#include <WindowsConstants.au3>
#include <file.au3>
Local $aMyDate, $aMyTime
_DateTimeSplit(_NowCalc(), $aMyDate, $aMyTime)
If not @error Then
; Kurze Pause damit ggf. Startmenü nach Aufruf geschlossen ist
sleep(100)
; Erzeugung des ISO 8601 Zeitstempels für den Dateinamen
; Aufgrund Einschränkungen durch NTFS Dateistreams Namenskonvention
; müssen die Zeit-Delimeter mit Bindestrichen statt Doppelpunkten sein.
; Alles in Lokalzeit der jeweiligen Systemuhr.
$Zeitstempel = $aMyDate[1] & "-"
; Wenn Monat einstellig, dann füge Präfix 0 hinzu
if StringLen($aMyDate[2])=1 then
$Zeitstempel = $Zeitstempel & "0" & $aMyDate[2] & "-"
else
$Zeitstempel = $Zeitstempel & $aMyDate[2] & "-"
EndIf
; Wenn Tag einstellig, dann füge Präfix 0 hinzu
if StringLen($aMyDate[3])=1 then
$Zeitstempel = $Zeitstempel & "0" & $aMyDate[3] & "T"
else
$Zeitstempel = $Zeitstempel & $aMyDate[3] & "T"
EndIf
; Wenn Stunde einstellig, dann füge Präfix 0 hinzu
if StringLen($aMyTime[1])=1 then
$Zeitstempel = $Zeitstempel & "0" & $aMyTime[1] & "-"
else
$Zeitstempel = $Zeitstempel & $aMyTime[1] & "-"
EndIf
; Wenn Minute einstellig, dann füge Präfix 0 hinzu
if StringLen($aMyTime[2])=1 then
$Zeitstempel = $Zeitstempel & "0" & $aMyTime[2] & "-"
else
$Zeitstempel = $Zeitstempel & $aMyTime[2] & "-"
EndIf
; Wenn Sekunde einstellig, dann füge Präfix 0 hinzu
if StringLen($aMyTime[3])=1 then
$Zeitstempel = $Zeitstempel & "0" & $aMyTime[3]
else
$Zeitstempel = $Zeitstempel & $aMyTime[3]
EndIf
; Speicherort auf Desktop im Unterordner Screenshots
$FolderName = @DesktopDir & "\Screenshots"
$FileName = $FolderName & "\" & $Zeitstempel & ".jpg"
; Erzeuge Ordner, wenn noch nicht vorhanden
if not FileExists($FolderName) Then DirCreate($FolderName)
; Überschreibe (d.h. lösche Datei mit identischem Zeitstempel)
if FileExists($FileName) then FileDelete($FileName)
; Erzeuge neue Datei
_ScreenCapture_Capture($FileName)
EndIf
Die in der Taskleiste abgelegte Verknüpfung zur Standalone-EXE erstellt wie gewünscht mit genau einem Mausklick (dem Programmstart) den Screenshot.
Konsequenz
Nur drei Beispiele, wo die PowerShell als Werkzeug versagt und Kommandozeile oder Auto-It leistungsfähiger sind.
Natürlich gibt’s auch nützliche PowerShell-Skripte, die fehlende Grundfunktionen eines Systems nachrüsten. Ein Fail2Ban für Exchange-Server zum Beispiel.20 Schaut man sich das Ausgangsproblem an wird deutlich, das Script ist keine Lösung - es doktert nur etwas an den Symptomen. Einen Exchange betreibt man nicht mit Portweiterleitungen direkt am Internet. Never ever! Macht man es richtig, braucht man auch dieses Script nicht.
Das ahnt auch der Autor und schränkt das Skript auf “kleine Umgebungen” ein, was es nicht besser macht. Die Daten dort sind nicht weniger wichtig als in großen. Es sind die vielen kleinen zu Bot-Netzen orchestrierten Server, die uns alle mit Spam und Malware gefährden.
Ich habe ein Problem mit der PowerShell, mit dem .NET Framework in seiner heutigen Implementierung. Es versprach, managed Code in einer CLR21 sicherer zu machen und COM zu ersetzen. Vom Zeitpunkt an, die Trümmer darunter22 damit erschliessen zu wollen, ist dieses Versprechen nicht mehr zu halten. Wer für alles offen ist, kann nicht ganz dicht sein.
Jeder lebt in und mit seinen eigenen Geschichten. Und während ich diese Zeilen schreibe installieren woanders viele im gutem Glauben an die Zukunft ADs, Server und Desktops. Das ist der Teil, den ich regelmäßig am unverständlichsten finde. Als passendes Meme fällt mir dazu nur dieses Filmzitat ein:23
Ich sehe tote Menschen. Sie wissen nicht, dass sie tot sind.
Mit Erscheinen des Windows Subsystem for Linux24 gibt es einen weiteren Grund, keinen Cent mehr auf ein totes Pferd zu setzen. Eine neue Generation an Admins ist dabei, mit Bash alles automatisieren zu wollen. Ich sage, lasst die Werbeplattform und Verhaltensdatenwanze Windows links liegen und nutzt das WSL besser zur Migration auf freie Software.
In diesem Sinne,
Tomas Jakobs
Update vom 28.08.2024
Nach zwei Jahren habe ich es endlich geschafft, das Repo zum Codeberg zu tragen:
https://codeberg.org/tomas-jakobs/one-click-screenshot
https://answers.microsoft.com/en-us/windows/forum/all/right-click-on-start-gave-powershell-instead-of/5b58788b-bae8-4a15-b0b7-badab75e0227 ↩︎
https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmic ↩︎
https://heise.de/news/Hacker-scripten-und-lieben-die-Shell-Anonyme-Umfrage-beim-Chaos-Computer-Club-6659866.html ↩︎
https://bsi.bund.de/DE/Service-Navi/Publikationen/Studien/SiSyPHuS_Win10/AP8/SiSyPHuS_AP8_node.html ↩︎
https://forbes.com/sites/forbestechcouncil/2021/09/10/how-hackers-use-powershell-and-how-to-take-action/ ↩︎
https://docs.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconnection ↩︎
https://docs.microsoft.com/en-us/powershell/module/nettcpip/get-netudpendpoint ↩︎
https://docs.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator ↩︎
https://docs.microsoft.com/de-de/dotnet/api/system.windows.forms.form ↩︎
https://frankysweb.de/exchange-ip-nach-fehlgeschlagenen-logins-sperren/ ↩︎
https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux ↩︎