PowerShell unter Linux installieren

Der folgende Beitrag ist eher eine „Note to my self“, damit ich beim nächsten Mal danach nicht suchen muss – auf der PowerShell Projektportalseite ist alles sehr schön und vollkommen ausreichend beschrieben:

https://docs.microsoft.com/de-de/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-6#ubuntu-1604

Aber vielleicht doch etwas zu ausführlich.

Also, mit der Linux-VM per SSH verbinden. Danach geht es wie folgt weiter:

Schritt 1: Herunterladen der Schlüssel (keys), damit das Microsoft-Repo vom Paketmanager als vertrauenswürdig behandelt wird.

Schritt 2: Registrieren der Microsoft-Schlüssel

Schritt 3: Aktualisieren aller Pakete

Schritt 4: Jetzt wird das PowerShell-Paket installiert

Es wird automatisch die aktuellste stabile Version installiert.

Schritt 5: Ging alles gut, PowerShell starten

Ein $PSVersionTable gibt aus, um welche Version es sich tatsächlich handelt. 6.2, 6.3 womöglich sogar 6.4:)

Wurde die Microsoft-Paketquelle registriert, lässt führt ein Update der Pakete auch dazu, dass PowerShell aktualisiert wird. Bzw. PowerShell kann wie jedes andere Package auch aktualisiert werden.

Erstellen eines Zertifikats per Inf-Datei und ein kleiner Unterschied zwischen der ISE und Visual Studio Code

Visual Studio Code ist ein genialer Universaleditor mit unzähligen Erweiterungen. Eine davon macht Code zu einem mehr als vollwertigen Editor für PowerShell-Skripte inklusive Eingabehilfen und einer deutlich besseren Debugger-Unterstützung. Die einzige kleine Einschränkung gegenüber der ISE ist, dass bei „Code“ eine Befehlsfolge in einer Datei gespeichert werden muss, damit sie als PowerShell-Skript ausgeführt werden kann.

Eine weitere Eigenheit von Code hat mich vor einiger Zeit etwas Nerven gekostet. Ein Skript führte zu einem eigentlich harmlosen „Die Zeichenfolge hat kein Abschlusszeichen“-Fehler, dessen Ursache ich einfach nicht finden konnte. In der ISE wurde dasselbe Skript problemlos ausgeführt. Am Ende stellt sich heraus, dass es genau die BOM-Bytes waren, die bei der ISE automatisch einer Textdatei vorangestellt werden. Nachdem ich das Skript auch in Code als „UTF-8 with BOM“ gespeichert hatte, funktioniert es dort genauso.

Das Skript ist eine kleine Demo des Protect-CMS-Cmdlets, das in der Windows PowerShell die Verschlüsselung von Zeichenketten mit Hilfe eines für die Dokumentesignierung geeigneten Zertifikats erledigt. Das Zertifikat wird mit Hilfe einer Inf-Datei und dem Befehlszeilentool certreg.exe angelegt. In die Inf-Datei muss lediglich ein Wert für subject eingetragen werden.

Visual Basic-Code in einem PowerShell-Skript ausführen

Visual Basic ist ein wenig die vergessene Programmiersprache bei Microsoft. In den 90er Jahren hatte das damals noch alte Visual Basic einen großen Anteil daran, dass sich Windows 3.0 und seine Nachfolger als Betriebssystemoberfläche so schnell verbreiten konnte. Mit der Einführung von .NET Framework im Jahr 2002 war Visual Basic als Visual Basic.NET zwar noch vertreten, das neue Visual Basic hatte aber nicht mehr viel mit dem vertrauten Visual Basic gemein. Die Folge war, dass es immer mehr auf ein Abstellgleis geriet, wenngleich die Sprache immer noch ein fester Bestandteil des .NET Framework als auch von .NET Core ist und auch weiterentwickelt wird. Auch bei .NET Core 3.0 wird VB dabei sein, auch wenn es nicht für alle Anwendungstypen eingesetzt werden kann:

https://blogs.msdn.microsoft.com/vbteam/2018/11/12/visual-basic-in-net-core-3-0/

Anders sieht es bei PowerShell Core aus. Mit der aktuellen Version 6.1 wird VB im Zusammenhang mit dem Add-Type-Cmdlet nicht mehr unterstützt. Auch wenn es nur wenige Gründe geben dürfte, in einem PowerShell-Skript Visual Basic-Code zu verwenden ist es trotzdem etwas schade, da eine Option weniger zur Auswahl steht.

Das folgende Beispiel ist daher nur für die Windows PowerShell gedacht. Es startet Word per später Bindung, lädt eine Docx-Datei und speichert sie als Pdf-Datei. Das setzt Word ab Version 2010 (?) voraus, damit diese Option überhaupt zur Verfügung steht.

17 ist der „Code“ (der Wert der Konstanten, die für das Dateiformat steht) für Pdf. Alle Pfade bitte anpassen, damit es auch funktioniert.

Kleine Tipps für Zwischendurch – Strings und Arrays per Select-String durchsuchen

Der Match-Operator ist praktisch, er gibt aber nur an, ob eine Zeichenkette in einer anderen Zeichenkette enthalten ist, die Übereinstimmung erfährt man nicht. Konkretes Beispiel: Ich wollte abfragen, ob die Path-Umgebungsvariable einen Pfad enthält, in dem ein bestimmter Name, z.B. „python“ enthalten ist.

Wenn ein

ein $true liefert bedeutet das lediglich, dass es einen Verzeichnispfad gibt, wie genau heißt erfährt man nicht.

Für diesen Zweck gibt es das vielseitige Select-String-Cmdlet, das auch ohne reguläre Ausdrücke nützlich ist.

Der folgende Befehl gibt alle Verzeichnispfade aus, in denen z.B. „python“ enthalten ist.

Tipp der Woche: Strings mit einem Zeichen auffüllen

Nach diesem kleinen Trick (ich behaupte einmal, dass er relativ undokumentiert ist) habe ich einige Jahre „gesucht“. Ich hatte in einmal in einem PowerShell-Skript oder einem Beispiel, das damals noch für die Version 1.0 gemacht war entdeckt, die Syntax nicht verstanden und dann aus den Augen verloren. Ausgerechnet bei einer Code Golf-Challenge auf Stackoverflow habe ich ihn per Zufall wieder entdeckt.

Die folgende Schreibweise erzeugt einen String, der aus z.B. 20 * besteht:

Eigentlich ganz logisch. Etwas kniffliger wird es, wenn z.B. eine 10×10-Matriz mit einem einzelnen Zeichen erzeugt werden soll. Das geht wie folgt:

Oder wenn man einmal wirklich zu viel Zeit hat:

String-Spielereien mit der PowerShell-Syntax, die kein Mensch wirklich braucht

Code like C# (oder so ähnlich)

Die PowerShell-Skriptsprache ist kein C# und soll es auch nicht sein. Trotzdem haben die Erfinder der Sprache ein paar syntaktische Ähnlichkeiten eingebaut, die Entwicklern, die in der Regel C# verwenden (wo sind alle die VB-Entwickler geblieben?), das Eingewöhnen etwas erleichtern sollen. Dazu gehört z.B. die Option Konstruktorargumente in runden Klammern übergeben zu können und die Kleinigkeit, dass eine Befehlszeile mit einem Semikolon enden kann, auch wenn dieses nicht benötigt wird.

Beim Anlegen von Objekten werden C#-Entwickler den new-Operator vermissen. Das Pendant ist das etwas sperrige New-Object-Cmdlet oder seit Version 5 die statische New-Methode des Type-Objekts. Mit Hilfe eines Alias und der optionalen Klammerschreibweise für Konstruktorargumente wird die PowerShell-Syntax der C#-Syntax dann doch verblüffend ähnlich.

Die Semikolons sind natürlich überflüssig. Was es definitiv nicht gibt ist ein Pendant zum using-Befehl in C# für den automatischen Aufruf von Dispose(). Solche Feinheiten dürften in einem PowerShell-Skript eher selten eine Rolle spielen. Und Variablen müssen wirklich nicht deklariert werden, sie sind aber trotzdem typsiert.

Module in einer Remote-Session global importieren am Beispiel von Office 365/Exchange Online

Der Zugriff auf Exchange bzw. Exchange Online (Office 365) erfolgt im Rahmen einer Remote-Session. Im ersten Schritt wird die Remote-Session angelegt, im zweiten Schritt wird ein Modul im Rahmen der Remote-Session lokal importiert, so dass alle Cmdlets aus dem Modul lokal als (Proxy-) Functions zur Verfügung stehen.

Hier ein kleines Beispiel.

Das ist alles in der offiziellen PowerShell/Office 365-Dokumentation ausführlich dokumentiert, z.B.

https://docs.microsoft.com/de-de/office365/enterprise/powershell/manage-office-365-with-office-365-powershell

Was aber, wenn man das Importieren des Remote-Moduls in ein Modul auslagern möchte? Auf einmal steht das Remote-Modul außerhalb des Moduls nicht mehr zur Verfügung.

Ich muss zugegeben, dass ich auf die Lösung nicht auf Anhieb gekommen bin (oft bin ich auch zu bequem, um es wirklich zu probieren). Sie besteht darin, dass temporäre Modul per Import-Module mit dem Parameter Global oder Scope global zu importieren. Dadurch steht es anschließend auch außerhalb des angelegten Moduls zur Verfügung.

Kleine Tipps für Zwischendurch: Datumszeitangaben im UTC-Format konvertieren

Datumskonvertierungen sind ein Thema für sich, insbesondere, wenn Spezialitäten wie UTC-Zeitangaben (Universal Time Coordinate) wie „20181010T110000“ ins Spiel kommen. Solche Datumswerte kommen z.B. in Ics-Dateien vor, die als Einladung zu einem Meeting verschickt werden.

PowerShell ist dank dem .NET Framework-Unterbau sehr vielseitig was den Umgang mit Datumsformat angeht, man muss eventuell nur ein wenig suchen bzw. experimentierfreudig sein.

So hat es eine Weile gedauert bis ich herausgefunden hatte wie die obige Zeichenfolge in einen DateTime-Wert konvertiert wird:

Das Geheimnis besteht darin, dass die zweite Zeichenfolge den Aufbau der ersten Zeichenfolge so abbilden muss, dass ein gültiger Datumszeitwert resultiert.

Das „T“ stört natürlich ein wenig. Der vielseitige replace-Operator lässt es unaufällig verschwinden:

Einen Access Token für Microsoft Graph erhalten

Mein neues Schwerpunktthema ist Office 365-Administration per PowerShell&Co und in diesem Themenkontext vor allem die reichhaltige Microsoft Graph-API.

Das für das Ausführen von Abfragen erforderliche Access Token zu erhalten ist nicht ganz so einfach. Das liegt weniger an der Technik, sondern eher daran, dass die Thematik (u.a. OAUth 2.0) ungewohnt ist und viele Wege nach Rom führen, von denen einige auch in Sackgassen führen.

Eine sehr gute Anleitung hat Microsoft Mitarbeiter Paul Kotylo in seinem Blog beschrieben:

https://blogs.technet.microsoft.com/cloudlojik/2018/06/29/connecting-to-microsoft-graph-with-a-native-app-using-powershell/

Wer die Schritte der Reihe nach ausführt (und keine Fehler macht) kommt zu einem Erfolg und kann Abfragen wie die folgende ausführen:

Die auf ODATA basierende Abfragesyntax wird hier beschrieben:

Ob eine Abfrage funktioniert hängt auch davon ab, ob für die App die erforderliche Berechtigung gesetzt wurde.

Ich werden das Thema in einigen Wochen an dieser Stelle ausführlicher mit einer Schritt für Schritt-Anleitung vorstellen.

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.