Archiv für den Monat: September 2018

Kleine Tipps für Zwischendurch: PowerShell Core-Module aus der PowerShell Gallery auflisten

PowerShell Core ist die Zukunft. Möglichst viele der Module, die Teil von Windows und Windows Server sind, sollen früher oder später auch für PowerShell Core verfügbar sein.

Modul-Autoren sollen im Modulmanifest den Eintrag CompatiblePSEditions verwenden und dort die Editionen „PSDesktop“ und „PSCore“ eintragen. Dieser Eintrag wird aber erst ab WMF 5.1 verstanden.

Aktuell ist es nicht ganz so einfach wie es sein könnte herauszufinden, ob ein Modul der PowerShell Gallery auch für PowerShell Core in Frage kommt. Diese Information verbirgt sich in der unscheinbaren Tags-Eigenschaft eines Modul.

In der PowerShell Dokumentation wird das Prinzip der Abfrage bereits ausführlich beschrieben:

https://docs.microsoft.com/en-us/powershell/gallery/how-to/finding-items/searching-by-psedition

Zwei kleine Beispiele sollen die Abfrage per Find-Module in der Praxis zeigen.

Etwas komfortabler ist der folgende Befehl, der zu allen Modulen der PowerShell Gallery die Editionen angibt, unter denen sie verwendet werden können.

Der folgende Befehl gruppiert die ganze Ausgabe nach der Eigenschaft Edition:

Wenn die Abfrage stimmt, sind von den über 3.200 Modulen der PowerShell Gallery nur etwas über 200 Module mit einem PSEdition-Information versehen. Und da man in die Tags-Eigenschaft alles eintragen kann, gibt es auch unterschiedliche Schreibweisen.

Wer bei den installierten Modulen die Edition abfragen möchte, verwendet dazu die CompatiblePSEditions-Eigenschaft:

oder

Wenn in der PowerShell-Konsole oder ISE nichts ausgegeben wird, liegt es an der PSModulePath-Umgebungsvariablen. Nur bei PowerShell Core ist das Verzeichnis c:\Program files\powershell\6\Modules mit dabei.

Die Beispiele zu meinem Vortrag auf der Basta! 2018

Die Basta! (der Name stand ursprünglich einmal für „Basic Tage“ – BASIC ist eine Programmiersprache, die vor allem in den 90er Jahren populär war – das Ausrufezeichen ist vermutlich aus Marketinggründen angehängt worden) ist zwar eine Entwicklerkonferenz, die zwei Mal im Jahr stattfindet (und das bereits seit über 20 Jahren), doch wie bei jeder Konferenz haben auch Randthemen ihren Platz.

Die Beispiele (in Gestalt einer Reihe von PowerShell-Skripten) gibt es unter dem folgenden Link:

Basta2018_PowerShellSkripte

Mehr zu dem Vortrag in Kürze (während ich diese Zeilen schreibe liegt er nämlich noch in der Zukunft und diese ist bekanntlich etwas schwierig vorauszusagen).

Convert a hashtable into its string representation

Recently I had to find a technique to update a module manitest file (psd1). Using the Update-ModuleManifest cmdlet is the official way to achieve this. Although using this command is really simple it completely overwrites the current psd1 file and inserts all the „junk“ that I want to keep out from that file.

Another approach is to load the psd1 file with the Import-LocalizedData cmdlet which results to a hashtable.

Updating single entries is easy:

But how do you turn the the whole hashtable with all its keys and values into a string again? Not so easy. Luckily I found an old blog entry by June Blender (when she was still working for Sapien Technologies):

https://www.sapien.com/blog/2014/10/21/a-better-tostring-method-for-hash-tables/

But June restricted her function to simple hashtables with no values that are hashtables themselves.

So I have extend her function so that it works also with hashtable that contains other hashtable values which is necessary to convert a hashtable that represents a psd1 file. Ny function calls itself recursively which which is not hard to accomplish in PowerShell. The difficult part can be to find an exit condition or threat the last value differently. For this reason I am not using foreach but the for command with an index variable to enumerate all the values. For the last value I add „}“ to complete the hashtable string.

As PowerShell expert Kirk Munro suggested in his comments to Junes blog post converting the hashtable to json might be a simpler solution. I have not tried this yet although it probably could have saved me a lot of typing and trying. But hey, why choosing a simple solution if you can spend a couple of hours fiddling with Powershell script code;)

So this should work. I have tested the function with a specific psd1 file. If not please leave a comment or send me a mail.

PowerShell Core 6.1 mit interessanten Neuerungen

Am Anfang fand ich die Idee gut, dann war ich etwas skeptisch, inzwischen bin aber überzeugt, dass PowerShell Core die Zukunft der PowerShell ist. Seit einigen Monaten arbeite ich nahezu ausschließlich mit PowerShell Core was mir insofern leicht fällt, da ich kein Administrator bin, der beruflich die PowerSHell einsetzt und damit auf Module angewwiesen ist, die sich unter PowerShell Code nicht nutzen lassen, wenngleich dies nur noch auf wenige Module zutreffen dürfte.

Im Windows-Umfeld bringt PowerShell Core nur wenige Vorteile. Im Gegenteil, bedingt durch den Umstand, dass „Core“ bedeutet, dass PowerShell Code auf .NET Core und nicht .NET Framework basiert und und in .NET Core 2.0 einige APIs fehlten, die von PowerShell-Commands vorausgesetzt werden, waren bestimmte Aufrufe, etwa WMI, einfach nicht möglich. Unter Windows ist das natürlich kein Thema, denn man bleibt ganz einfach bei der Windows PowerShell.

Microsoft wäre nicht Microsoft, wenn es seine Anmwender/Kunden nicht zu irgendetwas „zwingen“ würde. Wir sollen alle in die Cloud gehen, also gibt es bei Windows Server mit jeder Version weniger Innovationen (wenngleich auch Windows Server 2019 interessante Neuerungen enthält). Da in Zukunft nur noch PowerShell Core weiterentwickelt wird, sollen Administratoren in Zukunft eher mit diseser Version arbeiten. Da bei .NET Core in der Vergangenheit wichtige APis fehlten, die es nur unter Windows gibt, wurden diese in Gestalt der „Compatibilty Packs“ inzwischen nachgereicht. Damit ist auch bei PowerShell Core z.B. WMI wieder möglich. Natürlich nur unter Windows.

Trotzdem fallen mir nur wenige Gründe ein, warum ein Administrator unter Windows anstelle der Windows PowerShell PowerShell Core verwenden sollte.

Nachdem im Januar 2018 die Version 6.0 freigegeben wurde, erschien im September 2018, also etwa mehr als ein halbes Jahr später, die Version 6.1.

Sie wird im Blog des PowerShell-Teams ausführlich vorgestellt:

https://blogs.msdn.microsoft.com/powershell/2018/09/13/announcing-powershell-core-6-1/

und vorallem

https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-core-61?view=powershell-6

Der Vollständigkeit halber habe ich die wichtigsten Neuerungen noch einmal stichwortartig zusammengefasst:

>Umstellung auf .NET Core 2.1
>Performance-Verbesserungen, z.B. bei Import-CSV
>Über die Eigenschaft PSEdition lässt sich bei Modulen abfragen, ob wie für Core, Desktop oder beide in Frage kommen. Diese Information muss aber in die Manifestdatei eingetragen werden, damit sie abgefragt werden kann
>Es gibt Cmdlets für den Umgang mit Markdown (sehr praktisch)
>Das Invoke-WebRequest-Cmdlet wurde verbessert
>Das Enable-PSSession-Cmdlet wurde verbessert
>Ein cd – führt zum letzten Verzeichnis zurück
>Ein cd– führt in das $Home-Verzeichnnis
>Für Update-Help muss die PowerShell nicht mehr als Administrator gestartet werden
>Ein [PSCustomObject]-Objekt besitzt jetzt eine Length- und Count-Eigenschaft

Bemerkenswert und im Grunde naheliegend ist, dass einige der Verbesserungen aus der Community stammen.

Tipps für Zwischendurch: Fenster auflisten mit der EnumWindows-API-Funktion

Vor kurzem fand ich nach der Suche nach einer Lösung auf Stackoverflow einen Link auf ein PowerShell-Skript, das ich vor vielen Jahren erstellt hatte. Leider hatte der Autor meinen Namen nicht erwähnt, sonst wäre ich bei Stackoverflow zu unverhofftem Ruhm gekommen. Außerdem verwies der Autor des Beitrags auf meinen alten Blog powershell-knowhow.de, den es schon länger nicht mehr gibt. Da ich das Skript von damals auf diversen Festplatten nicht wiederfinden konnte, musste ich mich noch einmal an die Arbeit machen. Wie sooft war das Ergebnis besser als das Original, da es deutlich kürzer geworden ist. Das ist generell ein Erfahrungswert, den ich aus über 10 Jahren PowerShell ziehen kann, und der vermutlich universell gilt. Um auf die kürzeste Lösung zu kommen, muss man sich in den Bereich, in dem die Lösung gesucht wird, bereits ziemlich gut auskennen.

Wie dem auch sei, die folgende Function Get-WindowData gibt Fensterhandle und Text zu jedem Top-Window-Fenster aus (und das sind bereits erstaunlich viele). Theoretisch lassen sich durch eine Art rekursiven Aufruf natürlich auch die Kindfenster eines Fensters auflisten, aber diese Mühe habe ich mir dann doch nicht gemacht.

Interesant und lehrreich an dem Beispiel ist die Leichtigkeit, mit der sich ein „Callback“, also eine Adresse auf eine Funktion, die von der API-Funktion pro Fenster aufgerufen wird, von PowerShell an eine Win32-API-Funktion übergeben lässt. Merkregel: Überall, wo ein Delegate erwartet wird, darf auch ein Scriptblock übergeben werden.

Kleine Tipps für Zwischendurch: Kopfzeile und restliche Zeilen einer Textdatei mit einem Befehl getrennt einlesen

Bei einer Textdatei sollen Kopfzeile und die restlichen Zeilen getrennt eingelesen werden. Aber mit einem einzigen Befehl.

Mit folgender Konstruktion ist mir das gelungen.

Zunächst ein paar Testdaten als Here-String:

Dann der Befehl, der die Textzeilen in eine erste Zeile und die restlichen Zeilen aufsplittet und das Ergebnis zwei Variablen zuweist:

Warum verwende ich kein ForEach-Object? In erster Linie weil ich es etwas „übertrieben“ finde, wenn ein Wiederholungsbefehl für einen einzigen Verarbeitungschritt verwendet werden muss. Auch wollte ich keine Variable verwenden. Dann wäre auch Folgendes möglich:

Die Variable Zeilen wird gebraucht, da ich nur so an die Länge des Array beim Zugriff auf dasselbige herankomme.

Es soll direkt nach dem Schema Textzeilen | Verarbeitung => Ergebnis funktionieren. Ohne Zwischenschritte und Variablen.

Was nicht jeder wissen dürfte, auch eim harmloser Scriptblock kann in einen Begin-, Process- und End-Block unterteilt werden. Möchte man in einen Scriptblock etwas pipen, geht das nur mit eimem vorangestellten &, das den Scriptblock ausführt.

Tja, was soll man davon halten? Es funktioniert, ok. Ist das Ganze intuitiv nachvollziehbar? Eher weniger. Auf der anderen Seite, im Vergleich zu einem Befehl in Bash oder Perl, der zum selben Resultat führt, ist die PowerShell-Variante regelrecht leicht verdaulich, denn es kommen weder reguläre Ausdrücke noch kryptische Variablen wie %%S vor;)

Tipps für Zwischendurch: COM-Methoden mit Enum-Werten aufrufen

Dieser Tipp ist etwas spezieller.

Ausgangspunkt ist eine COM-Dll (früher OLE Server oder COM Server genannt), die eine Methode Calculate anbietet, die drei Parameter besitzt. Der erste Parameter ist vom Typ eines Enum mit dem Namen „OpType“, die anderen beiden Parameter sind vom Typ int.

Das Anlegen eines COM-Objekts mit der ProgId ist einfach:

Der Aufruf von Calculate funktioniert zunächst nur, wenn für den Enum-Wert eine Zahl übergeben, z.B. eine 0.

Der Enum OpType ist zunächst nicht bekannt. Ein „Trick“ besteht darin, ihn per Enum-Befehl nachzubauen, den es ab Version 5.0 gibt.

Damit klappt auch der folgende Aufruf:

Tipps für Zwischendurch: Registry-Suche – besser ohne PowerShell

Das Durchsuchen der Registry ist nicht gerade die Stärke der PowerShell. Einfacher geht es mit dem reg-Kommando, dessen Syntax zudem übersichtlich per /? abrufbar ist.

Hier eine Kostprobe. Gesucht sind alle Einträge, die ein „MathLibV1“ enthalten.

Per PowerShell wäre eine solche Suche deutlich umständlicher und vor allem langsamer. Das beginnt bereits mit dem Umstand, dass es keinen Befehl gibt, der die Registry nach Werten durchsucht. Das mit Version 5.0 dazu gekommende Get-ItemPropertyValue-Cmdlet klingt praktisch, ist aber sehr limitiert, da der Name-Parameter keine Platzhalter akzeptiert.

Geht es nur um eine schnelle Suche, ist reg einfach unschlagbar.