PowerShell unter Linux installieren – bei einem traditionellen Provider als Alternative zu einer Azure-VM

Die PowerShell unter Linux zu installieren macht inzwischen richtig Spaß und setzt dank einer nahezu idiotensicheren Anleitung für die wichtigsten Linux-Disributionen unter https://github.com/PowerShell/PowerShell auch keinerlei Linux- oder Bash-Kenntnisse voraus. Ein wenig PowerShell-Kenntnisse genügen;)

Auch wenn es naheliegend ist, die Linux VM unter Azure anzulegen, diese Variante besitzt einen Nachteil, der sich mal mehr oder weniger auswirkt: Azure ist aus meiner Sicht relativ teuer. Eine einfache Linux-VM mit 2 Core CPU und 3.5 GB RAM kostet ca. 50€ pro Monat. Natürlich gibt es preiswertere Varianten, aber das Arbeiten soll ja auch etwas Spaß machen. Deutlich preiswerter sind die meisten traditionellen Provider. Hier gibt es eine virtuellen Linux-Server mit vollen Root-Rechten ab 9.99€ (etwa bei Host Europe, bei dem ich z.B. diesen Blog hoste).

Wer jahrelang den Komfort von Azure gewöhnt war, muss bei einem traditionellen Provider erst einmal umdenken. Man bestellt den Server, bekommt kurz danach die Auftragsbestätigung, muss dann einen Auftrag zur Startkonfiguration starten und bekommen dann etwas später die Zugangsdaten (eine gewisse Überraschung war es, dass es aufgrund interner Hardwareprobleme ein paar Tage gedauert hat – dies ist zwar die absolute Ausnahme, kann aber offenbar passieren).

Sobald man die Zugangsdaten in Gestalt einer IP-Adresse und einem generierten Kennwort für das root-Konto erhalten hat, geht es genauso weiter wie bei einer Azure-VM. Als SSH-Client verwende ich seit kurzem das hervorragende MobaXTerm (https://mobaxterm.mobatek.net).

Eine Besonderheit ist noch erwähnenswert: Auf einem nackten Linux-System fehlt am Anfang einiges, u.a. auch curl und das Package für den Download via HTTPS. Beide Packages müssen per apt-get install nachinstalliert werden:

Anschließend wird die PowerShell nach Anleitung installiert. Der Programmname lautet neuerdings pwsh und nicht mehr „powershell“.

Aktuell (Stand: November 2017) ist immer noch die Beta-9. Ich bin gespannt, ob die Version 1.0, wie es auf der PowerShell-Konferenz in Hannover im Mai angedeutet wurde, tatsächlich noch in diesem Jahr kommt.

PowerShell Core in einer Asp.Net Core-Anwendung hosten – die ersten Schritte

Gleich vorweg: Dies ist noch kein vollständiger Blog-Eintrag, es sind vielmehr nur mehr oder weniger zusammenhanglose Notizen, die allen weiterhelfen sollen, die an einem ähnlichen Projekt arbeiten und mir als eine Art „Notizzettel“ dienen sollen.

*** Letzte Aktualisierung: 11/11/2017 ***

Ich will in den nächsten Wochen ein umfangreicheres PowerShell-Skript über einen Websevice anbieten. Technisch ist das sehr einfach, wenn das Projekt auf ASP.NET basiert. Ich möchte das Projekt aber nur unter Asp.Net Core umsetzen, damit es theoretisch auch in einem Container unter Linux laufen kann. Warum nicht, auch wenn es für das Projekt im Moment keine konkreten Vorteile bietet?

Die Herausforderung besteht darin, dass PowerShell Core noch nicht immer nicht offiziell ist und es daher noch keine offiziellen Packages gibt. Ein Pendant zu System.Management.Automation ist das PowerShell Core SDK.

Einen guten Einstieg in die etwas komplizierte Thematik gibt der folgende Artikel:

https://github.com/PowerShell/PowerShell/tree/master/docs/host-powershell#net-core-sample-application

Im Folgenden beschreibe ich die Umsetzung des Projekts.

Ausgangspunkt ist ein leeres Verzeichnis. Vorausgesetzt werden VisuaL Studio Code (mit Visual Studio 2017 Community Edition ginge es zwar leichter, aber es soll ja nicht zu leicht sein) und die neueste Version des .NET Core SDKs, z.B. 2.02.

Die Downloaadresse ist https://www.microsoft.com/net/learn/get-started/windows

Schritt 1: Anlegen eines ASP.NET Core-Projekts vom Typ WebApi

Schritt 2:: Hinzufügen des PowerShell Core SDK-Package

*** Fortsetzung folgt ***

Nächstes Treffen der PowerShell User Group Stuttgart und Umgebung am 30.11.2017 Globalways/Gropiusplatz 2

Es gibt sie noch, die PowwerShell User Group Stuttgart, auch wenn es die letzten Monate eventuell etwas ruhig war um sie bzw. Facebook und Webseite nicht auf dem aktuellen Stand sind.

Und wann ist das nächste Treffen? Ganz einfach, bis auf Weiteres immer am letzten Donnerstag eines Monats. Wo erfahre ich mehr? Immer auf Meetup, also unter

https://www.meetup.com/de-DE/PowerShell-Usergroup-Stuttgart/

Eingeladen ist jeder, der sich für die PowerShell als ein enorm vielseitiges einsetzbares Werkzeug für die Adminstration unter Windows und neuerdings auch Linux interessiert.

Praxistipp: .NET Vesionsnummern auslesen mit einem Tool und einer Prise Regex

Es gibt mehrere Alternativen, um die Versionsnummern aller instlalierten .NET-Versionen zu erhalten. Meine „Lieblingsvariante“ ist immer noch ein kleines Tool mit dem Namen .NET Version Detector von einem belgischen Entwickler mit dem leicht zu merkenden Domännamen asoft.be:

http://www.asoft.be/prod_netver.html

Das kleine Tool zeigt alle vorhandenen Versionen nicht nur in einem Fenster an, sondern schreibt sie wenn das Tool in der Befehlszeile aufgerufen wird auch in eine Textdatei.

Mit einer Prise Regex erhält man die Versionsnummern zurück.

PowerShell-Praxis: Umgang mit mehrdimensionalen Arrays – ein „Schiffe versenken“-Algorithmus

Der Umgang mit Arrays besitzt bei der PowerShell seine Eigenheiten. Auf der seinen Seite genial einfach und komfortabel, doch sobald es um mehrdimensionale Arrays geht lässt der Komfort etwas nach und es wird speziell.

Eines gleich vorweg: Wer bislang eine Programmiersprache oder eine traditionelle Skriptsprache wie VBScript (WSH) verwendet hat und es einfach gewohnt ist mit zweidimensionalen Arrays zu arbeiten, z.B. um tabellarische Daten abbilden zu können: In 95% alle Fälle ist ein simples Array, das per [PSCustonmObject] angelegte Werte aufnimmt, die beliebige Werte aufnehmen, die deutlich einfachere und vor allem Powershell-typische Variante.

Da man aber niemand zwingend sollte, eine vertraut gewordene Gewohnheit aufgeben zu müssen, lassen sich zwei- oder mehrdimensionale Arrays auch bei PowerShell anlegen. Zwei Dinge muss man wissen: Wie ein mehrdimensionales Array angelegt und wie ein mehrdimensionales Array von einer Function zurückgegeben wird.

Mehrdimensionale Arrays anlegen


Ein mehrdimensionales Array wird per New-Object-Cmdlet angelegt. Da das Cmdlet offenbar mit der [,]-Schreibweise nicht klar kommt, muss die Typbezeichnung in Apostrophen gesetzt werden – dadurch fallen aber die äußeren eckigen Klammern weg.

Der folgende Befehl legt ein zweidimensionales Array mit 10 Feldern in der ersten und 2 Feldern in der zweiten Dimension an.

Der folgende Befehl legt ein dreidimensionales Array mit 2 Feldern in jeder Dimension an.

Die Rank-Eigenschaft gibt die Anzahl der Dimensionen an. Per GetLength-Methode erhält man die Größe einer einzelnen Dimension.

Ein mehrdimensionale Array in einer Function zurückgeben


Soll eine Function ein mehrdimensionale Array zurückgeben, muss dem Ausdruck bzw. der Variablen einfach ein Komma vorangestellt werden.

Ein Algorithmus für das Belegen eines Schiffe versenken-Spielfeldes

Ein genialer Algorithmus, der ein zweidimensionales Feld für ein Schiffe versenken-Spiel mit Schiffen belegt und dabei darauf achtet, dass sich keine „Verbände“ überlappen bzw. die Spielfeldgrenzen berücksichtigt werden.

Eine Erklärung folgt in Kürze – bei Fragen einfach fragen;)

Umgang mit generischen Listen (Teil 2)

Im ersten Teil „Umgang mit generischen Listen“ ging es um ein erstes Kennenlernen der generischen Liste. Beantwortet wurde auch die Frage warum man sie überhaupt braucht. Die Antwort war: Für das administrative Skripten bringen sie keine Vorteile und sollten daher auch nicht verwendet werden. Sie sind immer dann praktisch bzw. notwendig, wenn Programmcode aus einem C#-Programm in ein PowerShell-Skript umgesetzt werden soll, oder wenn eine Methode einer .NET-Assembly eine generische Liste als Parameterwert erwartet.

In diesem Teil geht es um die Typenbezeichnung, die die PowerShell bei einer generischen Liste verwendet.

Ausgangspunkt für das Beispiel ist ein von mir definierter Typ mit dem Namen WTToken.

Ob dieser Typ per class-Befehl definiert wird, aus einem C#-Programm stammt oder es ein ganz anderer Typ ist, etwa PSCustomObject, spielt keine Rolle.

Mit dem Typ wird als nächstes eine generische Liste per new()-Methode angelegt.

Damit gibt es eine Liste $Tokenlist, die nur Objekte vom Typ WTToken aufnehmen kann.

Übergebe ich eine andere Sorte von Wert, etwa eine Zahl, kommt es wie zu erwarten zu einer Fehlermeldung. Die Meldung selber ist aber etwas irritierend. Anstatt „Wrong type error“ lautet die Fehlermeldung: Für „Add“ und die folgende Argumenteanzahl kann keine Überladung gefunden werden: „1“.. ??? WTF;)

Die Fehlermeldung will uns Folgendes sagen: Es kann für den Typ, den der Wert (1 Argument) besitzt, der übergeben werden soll, keine Methodenvariante gefunden werden, die diesen Typ akzeptiert. Eigentlich ganz einfach.

Bis jetzt ist hoffentlich noch alles nachvollziehbar.

Im Folgenden wird es kurzzeitig etwas spezieller. Beim Herumexperimentieren mit generischen Listen erhielt ich die obige Fehlermeldung auch dann, wenn der Wert vom Typ WTToken war und damit passen sollte.

Nach ein wenig Herumprobieren kam ich eher per Zufall auf die Lösung. Die PowerShell fügt in die Typenbezeichnung auch den Pfad der Ps1-Datei ein, in der der Typ definiert wird. Vermutlich aus der Überlegung heraus, dass die Typenbezeichnung damit eindeutig wird, da es keine zwei identischen Pfade geben kann.

Legt man jetzt in der ISE ein neues Fenster an und kopiert den Skriptcode, der eben noch funktioniert hatte, in das neue Fenster, kommt es zu obigen Fehler, da die generische Liste einen Typ erwartet, in dem der Pfad des alten Skripts noch enthalten ist. Wird das Objekt in dem neuen Fenster angelegt, erhält sein Typnname nicht den alten Ps1-Pfad und es entsteht ein neuer Typ, der nicht mehr in die generische Liste eingefügt werden kann.

Ein „Problem“ ist dieses Verhalten in der Praxis natürlich nicht. Der Fehler kann nur beim Herumprobieren in der ISE auftreten, da hier Variablen globale Variablen sind und mit ihrem aktuellen Wert und vor allem Typ in jedem neuen Fenster automatisch verwendet werden.

Im Folgenden möchte ich noch einmal zeigen, wie sich das von mir beschriebene Verhalten nachvollziehen lässt.

Schritt 1:

Starte die ISE, gib den folgenden PowerShell-Code ein und speichere das Ganze in einer Datei, z.B. „Test.ps1“.

Führe das Ganze aus. Die Liste in der Variablen Tokenlist besitzt den Typ „System.Collections.Generic.List`1[[WTToken, ⧹E։⧹2017⧹Projekte⧹BoolscherService⧹test.ps1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]“ (der Ps1-Pfad lautet natürlich immer anders).

Schritt 2:

Lege in der ISE ein neues Fenster an und füge den folgenden Code ein

Führe das Ganze aus. Es kommt zu der besagten Fehlermeldung, da WToken jetzt eine Typenbezeichnung erhält, in der anstelle des Ps1-Pfades nur „\powershell“ enthalten ist. Auch wenn es nur eine Kleinigkeit ist, ist es damit ein anderer Typ.

PowerShell-Tipp: switch-Überprüfung mit continue abbrechen

Der switch-Befehl der PowerShell ist leistungsfähig, besitzt aber seine kleinere „Besonderheiten“. Eine davon ist, dass eine Überprüfung von mehreren Werten nicht per break, sondern per continue-Befehl abgebrochen wird. Während break die switch-Abfrage komplett abbricht, bricht continue nur den aktuellen Durchlauf ab. Zwingend erforderlich ist aber keiner der beiden Befehle. Es hängt von der Abfragelogik ab, ob nach der ersten Übereinstimmung nach weiteren Übereinstimmungen gesucht werden soll oder nicht.

Hier ein kleines Beispiel.

Ohne continue würden jeweils drei Meldungen ausgegeben werden. In der Hilfe ist das alles unter about_switch schön beschrieben;)

Eine kurze Einführung in die PowerShell für erfahrene Skripter

Wer sein Leben lang eine klassische Skriptsprache (VBScript, Perl usw.) verwendet hat und nun mit der PowerShell arbeiten möchte, hat man Anfang oft unnötige Probleme und Startschwierigkeiten. Da bei der PowerShell vertraute Formalismen wie eine explizite Variablendeklaration oder ein expliziter AUsgabebfehl keine ROlle spielen bzw. optional sind, wirkt die PowerShell-Syntax für „Umsteiger“ ungewohnt, unlogisch und man tendiert schnell zu alles nur noch negativ zu sehen.

Ich muss zugegeben, dass ich am Anfang auch meine kleineren Problemee hatte, aber nach ein paar Jahren kam ich dann ganz gut mit der PowerShell-Syntax klar;)

Angeregt wurde ich für diesen Blog-Eintrag durch einen längeren Thread in einem Forum auf Administrator.de, in der der Fragesteller (offensichtlich ein älterer Mensch mit sehr viel VBScript-Erfahrung) mit der Art und Weise wie in PowerShell Arrays verwendet nicht klar kam und in jeder „geht leider immer noch nicht“-Antwort auf am Anfang sehr hilfsbereite Antworten immer mehr der Frust über die vermeintlich „grausame“ PowerShell-Syntax anklang. Am Ende schlug die Hilfsbereitschaft natürlich in Kritik um und er musste sich ein „Du wetterst hier seit Tagen über
PowerShell – irgenwie alles sch… aber allem Anschein nach fehlen dir die elementarsten Grundlagen“. Der Fragesteller gab dann auch später zu, dass er noch sehr viel lesen, lesen und lesen müsste, aber das ist für mich weder die Lösung noch die Ursache der offenkunding vorhandenen Unzufriedenheit.

Das grundsätzliche Problem ist eine überzogene Erwartungshaltung („mit PowerShell soll ja alles einfacher werden“ – wird meines Wissens nirgendwo behauptet) und die (falsche) Annahme, dass Techniken, die z.B. bei VBScript seit 20 Jahren angewendet wurden, sich 1:1 auf PowerShell übertragen lassen. Und wenn dann noch eine gewisse Ungeduld hinzu kommt, ist die Unzufriedenheit groß. Und wenn dann der Unzufriendende eine Multiplikator-Funktion besitzt und sich in der Firma oder in Foren über die vermmeintlichen Schwächen der PowerShell auslässt….

Die PowerShell ist nicht perfekt, aber sie ist nicht nur enorm leistungsfähig (das hilft Anfängern im Allgemeinen wenig), sondern weitestgehend konsistent. Das Problem, sofern man es als solches betrachtet ist, dass die Syntax keine Rücksicht auf VBScript&Co genommen wurde, Bruce Payette (der Kopf der PowerShell-Spprache) vielleicht nicht ein ganz so großes Genie ist wie Guido van Rossum (Python) oder Larry Wall (Perl) und die Version 1.0 unter einem enormen Zeitdruck veröffentlicht wurde. Der Vater der PowerShell, Jeffrey Snover, hat es auf der PowerShell Konferenz 2017 in Hannover so umschrieben, dass der Vorgesetzte bei Microsoft das ganze Projekt am liebsten wieder eingestampt hätte und dem Team irgendwann ein Ultimatum gestellt hatte – entwweder ihr bringt jetzt eine Version 1.0 oder das Projekt ist tot. Keine ganz optimale Voraaussetzung und eventuell eine Erklärung für einige der Inkosistenzen.

Hier sind meine persönlichen Regeln/Empfehlungen für einen Einstieg, der sich gerade an Anwender richtet, die sehr viel Erfahrung mit VBScript und anderen formalen Skriptsprachen besitzen.

Regel 1: Variablen müssen nicht deklariert werden

Regel 2: Es gibt keine Notwendigkeit Write-Host zu benutzen

Regel 3: Der Umgang mit Arrays ist einfach, man darf nur nicht wie ein Programmierer denken

Regel 4: Der Umgang mit mehrdimensionalen Arrays ist inkosistent und ein Schwachpunkt der PowerShell-Syntax

Regel 5: Zweidimensionale Arrays werden im Allgemeinen nicht benötigt

Regel 6: Es gibt ein Pendant zu Option Explizit

Regel 7: Halte es einfach

Regel 8: Verwende die Eingabehilfen und vor allem PSREadline

Regel 9: Freunde Dich mit Subexpressions an – am besten sofort

Regel 10: Aktualisiere die Hilfe und schau Dir die Beispiele zu Cmdlets und PowerShell-Befehlen an

Regel 11: Schreibe immer zuerst einen Test

Ok, diese Regel ist nicht ganz ernst gemeint.

Ich werde zu allen Regeln in naher Zukunft noch einiges schhreiben. Ich wollte die Regel erst einmal los werden, da sie frustrierten PowerShell-Anfängern eventuell helfen können. Und noch etwas: Keine Panik und alles wird gut;)

Umgang mit generischen Listen (Teil 1)

Eine generische Collection ist der Fachbegriff für ein Array bzw. allgemein eine Liste, die nur eine bestimmte „Sorte“ von Werten aufnehmen kann. Zum Beispiel Zahlen (Int32), Zeichenketten (String) oder bestimmte Objekttypen. Der Vorteil ist, dass eine generische Liste immer nur eine Sorte von Werten besitzt. Einen zwigenden Grund gibt es für diesen Speziallfall im PowerShell-Alltag nicht. Dennoch ist es gut, dass man auch in PowerShell generische Listen anlegen kann, da es einige Methoden gibt, die diese Sorte von Parameterwert erwarten. Außerdem wäre es schade, wenn das Umsetzen von C#-Code auf PowerShell-Skript an einem solchen Detail scheitern würde.

Das Anlegen einer generischen Liste ist grundsätzlich einfach.

WTToken ist lediglich irgendein Typ, der z.B. als Klasse definiert wird.

Das Hinzufügen eines Elements geschieht per Add-Methode:

Das war einfach, jetzt wird es etwas anspruchsvoller und leider unnötig kompliziert. Der letzte Befehl fügt ein WTToken-Objekt zu einer Liste hinzu. Über den Konstruktor werden die Properties Typ und Name mit Werten belegt. Für die Property Wert gibt es keinen Konstruktorparameter, da diese Property nicht immer belegt werden soll. Möchte man der Property einen Wert zuweisen, müsste man formal zuerst eine Variable anlegen:

Das ist aber etwas umständlich. In C# gibt es dafür Objektinitialisierer mit einer einfachen Syntax:

Bei der PowerShell 6.0 gibt es diese Möglichkeit noch nicht („noch“ weil es ja nur eine Frage der Zeit ist, bis ein Vorschlag im Projektportal eingereicht bzw. eventuell sogar fertig als Pull Request eingereicht wird).

Eine eventuell naheliegende Schreibweise funktioniert leider nicht.

Der Grund ist, dass die Zuweisung zwar grundsätzlich funktioniert, dabei aber kein WTToken-Objekt resultiert, das der Add-Methode übergeben werden könnte. Auch ein weiteres in runde Klammern setzen bringt nichts, da in diesem Fall lediglich der zugewiesene Wert zurückgegeben wird. In diesem Punkt gibt es also noch Verbesserungsbedarf was das Thema Objektinitialisierer beim Anlegen eines Objekts über die statische New-Methode angeht.

Es gibt einen „Workaround“, doch leider ist dieser arg umständlich und damit keine echte Verbesserung. Dieser besteht darin, einen Scriptblock zu übergeben, in dem das Objekt angelegt wird. Da aber auch hier eine Variable benötigt wird, damit am Ende das Objekt zurückgegeben werden kann, ist das Ganze nicht besonders elegant. Im Folgenden geht es daher nur um das Prinzip bzw. ein Anschauungsbeispiel.

Wer bis hierhin mitgelesen hat (dafür erst einmal einen herzlichen Glückwunsch angesichts der etwas trockenen Thematik, es lief wohl gerade nichts Passendes im Fernsehen, auf Netflix oder irgendwo anders;), natürlich gibt es eine einfache Lösung: Das New-Object-Cmdlet und seinen Propert-Parameter, mit dessen Hilfe sich ein Objekt gleichzeitig anlegen und was einzelne Eigenschaften betrifft initialisieren lässt.

Vielleicht nicht ganz so elegant wie die moderne Schreibweise per New, aber auf alle Fälle nachvollziehbar und trotzdem alles in allem relativ einfach in der Umsetzung.

Tipp: Mehrdimensionale Arrays als Parameter übergeben

Grundsätzlich ist der Umgang mit mehrdimensionalen Arrays bei der PowerShell kein Problem, man muss sich allerdings an die etwas spezielle Schreibweise beim Anlegen eines mehrdimensionalen Arrays gewöhnen. Da hätten sich die Väter der PowerShell etwas überlegen können.

Der folgende Befehl legt ein leeres zweidimensionales Array vom Typ 2×10 an. Die erste Dimension ist damit 2, die zweite Dimension 10.

Wird ein mehrdimensionales Array einer Function als Parameter übergeben, sieht die Datentyp-Deklaration fast genauso aus.