Archiv der Kategorie: PowerShell-Praxis

PowerShell-Remoting mit SSH

PowerShell-Remoting mit SSH ist gut dokumentiert. Zwei sehr gute Artikel sollten für einen erfolgreichen Probelauf genügen:

https://www.thomasmaurer.ch/2020/04/enable-powershell-ssh-remoting-in-powershell-7/

und

https://4sysops.com/archives/enable-powershell-core-6-remoting-with-ssh-transport/

Ein dritter Artikel beschreibt die Verwendung der Public Key-Authentifizierung als Alternative zur Passwort-Eingabe:

https://4sysops.com/archives/powershell-remoting-with-ssh-public-key-authentication/

Was eventuell noch nicht jeder weiß, der sich für das Thema SSH mit PowerShell interessiert. Seit ein paar Monaten gibt es in der PowerShell Gallery ein kleines Modul des PowerShell-Teams, mit dessen einzigem Enable-SSHRemoting-Command das Einrichten von SSH auf dem Remote-Computer sehr einfach wird. Der Eintrag

PasswordAuthentication yes

muss allerdings auch in diesem Fall einkommentiert werden, falls eine Kennwort-Authentifizierung gewünscht ist. Für das erste Kennenlernen ist dies die einfachste Variante.

Das Modul gibt es unter

https://www.powershellgallery.com/packages/Microsoft.PowerShell.RemotingTools/0.1.0

Damit hat es sogar bei mir auf Anhieb funktioniert;)

Insgesamt sind damit folgende Schritte auf dem SSH-Server zu erledigen:

  1. PowerShell 7 installieren
  2. OpenSSH Client und OpenSSH Server installieren
  3. Enable-SSHRemoting ausführen oder den subsystem-Eintrag in der sshd-config-Datei „zu Fuß“ anlegen
  4. sshd-Dienst (nicht ssh) neu starten – unter Linux service ssh restart (eigentlich gar nicht so schwer;)

Damit kann per New-PSSession, Enter-PSSession und Invoke-Command unter einer PowerShell ab Version 6.0 SSH über den SSHTransport-Parameter verwendet werden:

#requires -version 
$hostname="Ubunti
#$hostname="172.23.25.12"
$Username="pemo"
$S1 = New-PSSession -Hostname $Hostname -Username $Username -SSHTransport -Subsystem powershell
Invoke-Command -ScriptBlock { Get-Process} -Session $S1
Remove-PSSession -Session $S1

Der Subsystem-Parameter wählt den Namen des auf dem SSH-Server angelegten subsystem-Eintrag in sshd_config aus. Das Passwort wird bei jedem (!) Aufruf abgefragt. Wer das nicht möchte, muss Public Key-Authentizierung verwenden. Wie das geht, wird in einem der genannten Blog-Artikel beschrieben.

Viel Erfolg!

Abb. Dank snap besteht die Installation der PowerShell unter Ubuntu aus einem Aufruf (sofern sie nicht bereits vorinstalliert ist)

Anlegen einer Custom Rule für Scriptanalyzer

Der PowerShell Scriptanalyzer ist das Werkzeug, um für PowerShell-Skripte mit einfachen Mitteln eine umfassende Qualitätsanalyse durchzuführen.

Eine einer Stärken ist seine Erweiterbarkeit. Durch das Definieren von Custom Rules lassen sich theoretisch beliebige Merkmale eines Skriptes testen (z.B. ob Kommentare irgendwelche unflätigen Begriffe enthalten;).

Grundsätzlich ist das Definieren einer solchen Custom Rule einfach, denn sie besteht nur aus einer Function, die in einer Psm1-Datei abgelegt werden muss. Beim Aufruf von Invoke-ScriptAnalyzer wird der Pfad der Psm1-Datei über den CustomRulePath-Parameter angegeben.

PowerShell-Experte Mathieu Buisson beschreibt in einem sehr guten Blog-Beitrag ein kleines Beispiel, das bei mir auf Anhieb funktioniert hat:

https://mathieubuisson.github.io/create-custom-rule-psscriptanalyzer/

Ich war für sein Beispiel, das man sich lediglich aus dem GitHub-Repo kopieren muss, sehr dankbar, da ich zuvor „stundenlang“ herumprobiert hatte, um eine eigene Regel zum Laufen zu bringen (ich hatte vor Jahren bereits einmal eine Custom Rule als Psm1-Datei erstellt, war aber zu bequem, das Beispiel von damals zu suchen). Was ich auch probiert hatte, das Ergebnis war eine Fehlermeldung, in der behauptet wurde, dass meine psm1-Datei keine Regeln enthielt. Wirklich nervig. Irgendwann kam ich auf die Lösung. Ich hatte offenbar übersehen, dass der Function-Parameter ScriptBlockAst heißen muss. Außerdem müssen die Typen der Input- und Output-Werte genau angegeben werden.

Die größte Herausforderung beim Erstellen einer Custom Rule sind aber nicht die kleinen Formalitäten, sondern der Umstand, dass man sich in Grundzügen mit den Abstract Syntax Trees (AST) der PowerShell auskennen muss.

Meine Regel prüft, ob ein Skript Cmdlets oder Functions enthält, die keinen WhatIf-Parameter anbieten. Warum kann das wichtig sein? Weil das Commands sind, die nicht von der globalen WhatIfPreference-Einstellung betroffen sind. Möchte man ein unbekanntes Skript in einer „Sandbox“ ausführen, in der nichts „Schlimmes“ passieren kann, wären diese Commands davon nicht betroffen.

Auch wenn man in der Regel davon ausgehen kann, dass ein Command ohne WhatIf-Parameter grundsätzlich nichts macht, was negative Folgen haben könnte, 100% sicher kann man sich nicht sein, da der Autor des Cmdlets oder der Functions das Implementieren von Whatif über SupportsShouldProcess = $true im Rahmen von [CmdletBinding()] nicht für erforderlich gehalten oder schlicht vergessen haben könnte.

Wer daher wissen will, welche Commands von einem WhatIf-Modus unbeeindruckt bleiben, sollte diese Regel ausprobieren.

Die gesamte Datei gibt es in meinem GitHub-Repo

https://github.com/pemo11/whatifrule

Ich werde die einzelnen Schritte, insbesondere den AST-Teil, in naher Zukunft noch ausführlicher beschreiben.

Den folgenden Kommentar wollte ich in dem Blog von Mathieu Buisson schreiben, aber aus irgendeinem Grund war das Kommentieren nicht möglich:

Your article is very helpful, thank you – but I think that you should mention that the parameter name has to be ScriptBlockAst otherwise the rule is not found by ScriptAnalyzer by my experience.

Tipp des Tages: Text-Dateien im BOM-Format finden

Die PowerShell ISE hat die etwas lästige Eigenart, dass Textdateien mit einem sog. BOM-Header gespeichert. Das bedeutet konkret, dass die Datei mit den Byte-Werten 255 und 254 beginnt oder FF EF als Hexadezimalzahlen. BOM steht für Byte Order Mark und soll einen Hinweis darauf geben, dass als Zeichencode UTF-8 verwendet wird (ich hoffe, ich gebe das korrekt wieder). Mehr dazu unter https://de.wikipedia.org/wiki/Byte_Order_Mark.

Dass eine einfache Angelegenheit erstaunlich kompliziert sein kann, macht eine ausführliche Abhandlung deutlich, die das Zusammenspiel von VS Code mit der PowerShell in Bezug auf die Zeichencodierung beschreibt:

https://docs.microsoft.com/en-us/powershell/scripting/dev-cross-plat/vscode/understanding-file-encoding?view=powershell-7

Ich muss leider zugegeben, dass ich immer wieder in Situationen komme, in denen in die Umlaute einer in VS-Code gespeicherten Ps1-Datei durch „seltsame“ Sonderzeichen ersetzt werden (die in der oben genannten Doku natürlich alle beschrieben werden).

Wie dem auch sei, mich hat es einfach einmal interessiert, welche Ps1-oder allgemein Textdateien einen solchen BOM-Header besitzen. Der folgende Befehl gibt zunächst für die Windows PowerShell alle iese Dateien aus, in dem er jeweils die ersten beiden Bytes der Datei liest und diese als Hex-Werte mit FFEF vergleicht:

Der obige Befehl funktioniert nur mit der Windows PowerShell, da es den Wert Byte für den Encoding-Parameter nicht bei PowerShell 7 gibt. Dafür gibt es mit dem AsByteStream-Parameter zwar ein flexibleres Konzept, allerdings darf der TotalCount-Parameter nicht mit diesem Parameter kombiniert werden, so dass ein Select-Object mit -First 2 erforderlich wird.

Der folgende Befehl entspricht dem letzten Befehl, nur dass er ab PowerShell 6 funktioniert:

PowerShell-Tipp: Variablen in WMI-Abfrage einbauen

Der folgende Tipp ist sehr allgemein und eher harmlos, aber praktisch. WMI-Filter verwenden bekanntlich eine andere Syntax, u.a. müssen Zeichenketten in einem Vergleich in Apostrophe gesetzt werden.

Das funktioniert (natürlich) auch mit einer Variablen:

Anders als man es als PowerShell-Neuling vielleicht vermuten könnte, wird eine Variable auch dann ersetzt, wenn sie in Apostrophen steht.

Blog-Tipp: Die ACL eines Systemdienstes abfragen

Es ist meines Wissens nicht möglich, die Zugriffsberechtigungen für einen Systemdienst abzufragen. Der folgende Befehl gibt daher nur vermeintlich, die ACL des WinRM-Dienstes zurück (der dazu ausführen muss), sondern die ACL der Datei svchost.exe.

Ein Pendant zu sc.exe sdshow winrm gibt es daher nicht.

Ein sehr guter Blogbeitrag von PowerShell-Experten Ron Edwards zeigt, wie es gehen könnte:

Das Beispiel macht aber auch deutlich, dass es insgesamt eine aufwändige Angelegenheit ist, da API-Funktionen des Betriebssystems eingebunden werden müssen. Für ad hoc-Abfragen kommt eine Umsetzung daher nicht in Frage.

Bleibt also nur sc.exe mit dem sdshow-Parameter, das aber nur die DACL zurückgibt. Und wie macht man aus dem SDDL-Text eine „lesbare“ Ausgabe? Ganz einfach mit dem ConvertFrom-SddlString-Cmdlet.

Wer spontan darauf kommt, warum das Select-Object-Cmdlet mit -Skip 1 erforderlich ist, ist ein heißer Anwärter auf ein „PoshAdmin“-T-Shirt falls es das eines Tages geben sollte;)

PowerShell-Know how als Teil der Windows-Problemlöser

Auf diesen Blog-Eintrag wollte ich schon seit Jahren hinweisen, da ich ihn sehr gut finde:

https://chentiangemalc.wordpress.com/2011/05/08/windows-7-can-teach-you-powershellinbuilt-wealth-of-scripts/

Der Autor hat sich die Mühe gemacht, die vielen PowerShell-Skripte, die unter der Haube der zahlreichen Diagnosehilfen von Windows ihren Dienst verrichten, aufzulisten und ihren Inhalt zu untersuchen.

Auch wenn, wie es PowerShell-Experte Jeffrey Hicks es in einem Kommentar korrekt anmerkt, einige (oder eher viele?) der vorgestellten Techniken veraltet bzw. nicht ganz optimal sind, zum Lernen der verschiedenen PowerShell-Techniken sind die Skripts dann doch gut geeignet.

Auf alle Fälle ist es erstaunlich, welche Fülle an PowerShell-Know how abseits der offiziellen Module bereits bei Windows 7 im Windows-Verzeichnis dabei war.

Systemdienste vom Typ Kernel Driver auflisten und Spezialfall npcap

Das Auflisten von Systemdiensten ist mit der PowerShell eigentlich sehr einfach, doch wie sooft lauern auch kleine „Stolperfallen“. Insbesondere der Netzwerkdienste npcap, der u.a. von Wireshark verwendet wird, fällt so richtig aus der Rolle und kann einem „gestressten“ Admin Nerven und vor allem Zeit kosten.

Eigentlich ist alles ganz einfach. Ein Get-Service listet alle vorhanden Systemdienste auf, die Eigenschaft ServiceType gibt an, ob es sich um einen prozessbasierten Dienst oder einen „Kernel Service“ handelt.

Der folgende Aufruf gibt für den Dienst npcap (der über Nmap isntalliert wird) auch die korrekte Bezeichnung zurück.

Npcap wird als Dienst vom Typ „KernelDriver“ ausgegeben.

Die erste „Überraschung“ entsteht beim Gruppieren aller Dienste nach der ServiceType-Property.

Der Grund für die Überraschung: „KernelDriver“ erscheint nicht als Gruppenname, wird also nicht abgefragt. Der Dienst ist auch in keiner der Gruppen enthalten. Stattdessen erscheinen als Gruppennamen Zahlen wie 224, 208 oder 240, bei denen es sich um zusammengesetzte Zweierpotenzen handelt (eventuell setzt sich der Wert für ServiceType bei einigen Diensten aus mehreren Zahlenwerten zusammen – dafür spricht, dass für den ServiceType dieser Dienste bei einer WMI-Abfrage per Win32_Service als „Unknown“ eingetragen wird).

Es bleibt mysteriös, denn die folgende Abfrage gibt nichts zurück;

Auf einmal gibt es keinen Dienst mit dem Namen npcap mehr (der Platzhalter funktioniert natürlich beim Name-Parameter von Get-Service). Dies könnte eventuell mit der Art und Weise zu tun haben, wie npcap in der Registry unter HKey_Local_Machine\System\CurrentControlSet\Services eingetragen wurde.

Eine Abfrage der Systemdienste funktioniert etwas besser per WMI:

Hier ist auch die Verwendung von Platzhaltern kein Problem:

Warum aber ein get-service npca* nichts zurückgibt, bleibt im Moment ein (weiteres) ungelöstes Rätsel (genauso, dass sich der Dienst auf meinem PC mit Windows 8.1 nicht starten lässt).

Praxistipp: Abfrage der zuletzt installierten Systemdienste

Anders als bei Anwendungen gibt es bei Diensten keinen Installationszeitpunkt, der sich über den Dienst selber abfragen ließe. Wenn man herausbekommen möchte, ob eine Anwendung auch einen Systemdienst installiert hat, geht dies sehr einfach über die Abfrage des System-Ereignisprotokolls. Am besten mit „Service Controll Manager“ als Quelle und eventuell auch gleich der InstanceId 1073748869.

Da ich vor einiger Zeit wieder einmal Wireshark intalliert hatte, wollte ich herausfinden, welcher Systemdienst während der Installation installiert wurde (ok, es ist natürlich Npcap, aber Kontrolle ist bekanntlich besser…)

Der folgende Befehl liefert schon ein erstes Ergebnis:

Für die Praxis wichtige Details zur Installationsort liefert die Eigenschaft ReplacementStrings:

PowerShell 7.0 unter Ubuntu – dank snap alles ganz einfach

Die PowerShell unter Linux zu installieren, ist grundsätzlich kein Problem, auf der Downloadseite ist die Installation bestens für alle Versionen und Varianten dokumentiert und gelingt daher auch Unix-NOOBS wie mir (wobei ich Unix seit den 80er Jahren kenne und eine noch ausgepackte Box mit Linux Suse 4.2 bei mir im Regal steht – das Interesse war also immer da;).

Dank snap gelingt die Installation inzwischen mit einem Aufruf – es muss kein Repo mehr registriert werden.

Der Aufruf sieht wie folgt aus:

sudo snap install powerShell --classic

Die Angabe „–classic“ ist aus für mich nicht mehr nachvollziehbaren Gründen offenbar erforderlich. Anschließend kann die PowerShell 7.0 (bzw. eine aktuellere Version) durch Eingabe von pwsh gestartet werden.

Auch bei der neuen PowerShell muss die Hilfe per Update-Help aktualisiert werden. Hier kommt es aber offenbar auf den Parameter -UICulture mit en-us als Parameterwert an. Anschließend zeigt die Hilfe dann auch die Beispiele an und alles sieht richtig gut aus. Auch wenn ich natürlich etwas voreingenommen bin, bin ich mir sicher, dass die PowerShell eine Bereicherung im (gigantischen) Linux-Ökosystem ist.

Praxistipp: Feststellen, ob alle Dateien mit einer Zahlenendung vorhanden sind

Die Anforderung: Ein Verzeichnis enthält viele Dateien, die zuvor (einzeln) aus dem Internet heruntergeladen wurde. Jede Datei endet mit einer aus drei Ziffern bestehenden Zahl, also z.B. Datei1.zip.001, Datei1.zip.002, Datei1.zip.003 usw. Da die Dateien alle per Mausklick aus einem per OneDrive Business (Sharepoint) freigegebenen Webverzeichnis heruntergeladen wurden, soll sichergestellt werden, dass tatsächlich alle Dateien heruntergeladen wurden. Dazu muss geprüft werden, ob alle Zahlen von 1..n am Ende des Dateinamens in der Zahlenfolge von 1..n enthalten sind.

Das ist natürlich eine ideale Gelegenheit, die eigenen PowerShell-Kenntnisse zu überprüfen.

Da in dem Befehl nicht nur eine Hashtable, sondern auch ein (Mini-) Regex vorkommen, ist die Schreibweise am Anfang vielleicht etwas speziell, aber wie immer ist sie eigentlich ganz einfach.

Das folgende Beispiel geht davon aus, dass sich im Verzeichnis F:\VIS 2020 Dateien mit den Endungen .001 bis .242 befinden. Sollte eine Datei fehlen, wird sie ausgegeben.

In der ersten Version meiner „genialen Idee“ hatte ich noch einen Denkfehler eingebaut. Jetzt sollte das Ergebnis aber stimmen und es werden nur die Nummern ausgegeben, für die es keine Datei gibt.