Was ich Bruce (Payette) noch hätte fragen können…

Die PoshConf 2017 liegt schon wieder über eine Woche zurück, ein kurzer Bericht folgt in Kürze. Es ist immer wieder ein Erlebnis Bruce Payette, einer Köpfe hinter der PowerShell-Skriptsprache zu treffen. Theoretisch kann man ihn alles fragen was man jemals über die PowerShell wissen wollte, es aber niemanden fragen konnte. Theoretisch. Praktisch gehen die 3 Tage doch schnell vorüber und in dem dicht gefüllten Konferenzprogramm bleibt dann noch zu wenig Zeit. Das nächste Mal schreibe ich mir die Fragen vorher auf bzw. sammele sie in diesem Blog.

Hier ist schon die erste Frage:

Warum braucht ein PowerShell-Ausdruck immer dieses eine Paar runder Klammern extra?

Hier ein Beispiel. Der folgende Befehl soll per Write-Progress den Wert einer Variablen ausgeben und sie vor der Ausgabe um eins erhöhen. Das erledigt der folgende Befehl:

Es stört mich jedes Mal, dass ich es erst nach ein paar Mal ausprobieren hinbekomme, weil ich jedes Mal das zweite Paar runder Klammern weglasse, dass eigentlich überflüssig sein sollte. Eigentlich. Wahrscheinlich hat es was mit den verschiedenen Modi des PowerShell-Interpreters zu tun.

Aus aktuellem Anlass: SMB1 deaktivieren

Zur Zeit treibt der WannaCry-Trojaner sein Unwesen und richtet teilweise erheblichen Schaden an, da die Infrastruktur im öffentlichen Raum (in erster Linie offenbar Krankenhäuser in Großbritannien) vorübergehend nur eingeschränkt funktionsfähig ist. Über den Ursprung des Trojaners möchte ich nicht spekulieren, genauso wenig, ob es unter Windows mehr Sicherheitslücken gibt wie unter Linux&Co. Ein Fakt ist, dass Microsoft einen enormen Aufwand betreibt, um durch das zeitnahe zur Verfügung stellen von Patches den durch die Verwendung von Windows eingetretenen Schaden zu begrenzen. Allerdings darf man nicht Ursache und Wirkung verwechseln. Die Verbreitung wird leider durch den Umstand verursacht, dass es offenbar immer noch Menschen gibt, die E-Mail-Anhänge doppelt anklicken.

Die Verbreitung im LAN geschieht offenbar über eine Lücke im SMB1-Protokoll. Dieses lässt sich per PowerShell sehr einfach deaktivieren:

Die PowerShell muss dazu als Administrator gestartet werden. Ein Get-SmbServerConfiguration fragt die aktuelle Einstellung ab. Dank PS-Remoting ist es theoretisch eine Kleinigkeit, diese Abfrage in der gesamten Domäne durchzuführen. Wenn man dann noch mit Pscribo einen kleinen Report anfertigt, aus dem hervorgeht, dass auf 100% der Computern in der Domäne SMB1 deaktiviert wurde, dürfte das Ganze beim Vorgesetzten einen guten Eindruck machen (natürlich vorausgesetzt, dass SMB1 nicht doch für irgendetwas Unternehmenskritisches benötigt wird;)

ipconfig /displaydns-Output mit ConvertFrom-String verarbeiten

Ipconfig ist ein vielseitiges Tool. Ich benutze es immer noch, auch wenn z.B. Get-NetIPAddress einen etwas übersichtlicheren Output produziert.

Ein Pedant zum Schalter /displaydns gibt es bei den PowerShell-Commmands nicht. Leider ist die Ausgabe etwas unübersichtlich. Da man sich im Allgemeinen nur für die Hostnamen interessiert, mit denen irgendwelche Prozesse auf dem PC eine Verbindung herstellen, wäre es praktisch, wenn es eine Möglichkeit gäbe zu erreichen, dass nur diese Namen ausgegeben werden.

Mit dem vielseitigen ConvertFrom-String-Cmdlet, das ab der Version 5.0 zur Verfügung steht, gibt es eine solche Möglichkeit. Das Cmdlet ist leistungsfähig, aber etwa spärlich dokumentiert und bereits deutlich anspruchsvoller als z. B. Select-String.

Die Idee ist, dass aus meinen Mix aus Mustertext und vereinfachter regulärer Ausdrucks-Syntax ein Muster für einen unregelmäßig strukturierten Text zur Verfügung gestellt wird.

Das folgende Muster ist für die Ipconfig-Ausgabe mit dem Schalter displaydns gedacht:

Wichtig sind die geschweifte Klammer, über die einem Element des Textes ein Name gegeben wird. Der Mustertext wird in einer Textdatei gespeichert.

Der folgende Befehl gibt nur die Hostnamen aus dem DisplayDns-Output aus:

Mit dem ConvertFrom-String-Cmdlet kann man viel Zeit verbringen. Vieles habe ich nur per „Trial and error“ herausgefunden. Der Aufwand lohnt sich, denn wenn ein Muster funktioniert erhält man damit ein mächtiges Werkzeug für die Textauswertung.

Azure VMs konfigurieren per Custom Script Extension

Die Technik der Custom Script Extension gibt es bei Azure schon einige Jahre. Dahinter steckt ein Agent, der ein zuvor festgelegtes Skript auf die VM „anwendet, in dem es in ein Download-Verzeichnis gespeichert und dann ausgeführt wird. Das Skript kann ein PowerShell-Skript sein, es kann aber auch eine Exe-Datei oder etwas Anderes sein. Eine Custom Script Extension ist für spezielle Aktivitäten gedacht, die nach der Bereitstellung einer VM ausgeführt werden sollen. Sie können aber auch zu einem beliebigen Zeitpunkt ausgeführt werden.

Die sparsame Verwendung der Mehrzahlform deutet es dezent an, es kann pro VM nur eine Custom Script Extension geben. Über sie können aber mehrere Skripte zusammengefasst werden, von denen ein Skript automatisch ausgeführt wird.

Die Custom Script Extension ist z.B. für die automatisierte Installation von Anwendungen in einer VM gedacht.

Das soll ein kleines Beispiel veranschaulichen

Ausgangspunkt ist eine Exe-Datei, die in einem Azure Storage-Blob liegt:

$PackageUri = „https://standardspeicher.blob.core.windows.net/packages/SumatraPDF-3.1.2-64-install.exe“

Das ist aber keine Voraussetzung. Grundsätzlich spielt der Aufenthaltsort der Exe-Datei keine Rolle. Irgendwo in der Cloud oder in einem Ftp-Verzeichnis.

Gesucht ist ein PowerShell-Skript, dass diese Exe-Datei herunterlädt und ausführt. Das folgende Skript erledigt das:

Am Ende wird die Exe-Datei zusammen mit dem Schalter /S per Invoke-Expression ausgeführt. Nicht optimal, aber per Start-Process erhielt ich auch mit Admin-Credentials ein „Access Denied“ beim Versuch die Exe-Datei zu starten.

Das Skript selber befindet sich auch in einem Azure Storage-Account:

$FileUri = „https://standardspeicher.blob.core.windows.net/ps1blob/Azure_InstallAppForVM.ps1“

Das Skript, das die Custom Script Extension hinzufügt, ist wie folgt aufgebaut:

Die entscheidende Rolle spielt das Set-AzureRmVMCustomScriptExtension-Cmdlet aus dem AzureRm.Compute-Modul. Es richtet die Custom Sript Extension für die angegebene VM, die dazu ausführen muss, ein.

Der Run-Parameter legt das Skript fest, das ausgeführt werden soll (bei einem einzigen Skript ist er vermutlich nicht erforderlich – ich hatte keine Lust mehr das auszuprobieren;).

Ein

ruft die Custom Script Extension über ihren Namen ab.

In der Praxis müsste man die Installation z.B. über eine CSV-Datei steuern, in der die Namen, Urls usw. der zu installierenden Anwendungen ethalten sind.

Insgesamt ist die Custom Script Extension eine flexible Technik, die, anders als Azure DSC, auch keine zusätzlichen Kosten verursacht.

Azure VM per PowerShell und DSC konfigurieren

*** Rohentwurf – wird in den nächsten Tagen noch einmal überarbeitet ***

DSC ist eine flexible Angelegenheit, das gilt besonders im Zusammenhang mit der Konfiguration einer Azure VM. Per DSC lässt sich eine beliebige Konfiguration auf beliebige VMs anwenden mit minimalem Aufwand, da sich die Azure-Plattform um die „Feinheiten“ kümmert.

Im Folgenden stelle ich ein sehr einfaches Beispiel vor, bei dem lediglich ein Verzeichnis mit einer Datei und einem bestimmten Inhalt angelegt werden. Da der Name des Verzeichnisses und der Inhalt der Datei über Konfigurationsdaten festgelegt werden, wird es auch bei diesem Beispiel etwas interessanter.

Voraussetzung sind:
>Ein Azure-Konto
>Eine Windows VM (theoretisch funktioniert es auch mit einer Linux VM)
>Ein Azure-Automationskonto, das zuvor gegebenenfalls angelegt werden muss

Schritt 1: Die DSC-Konfiguration wird in einer Ps1-Datei lokal gespeichert

Und was ist mit den Konfigurationsdaten? Die kommen etwas später ins Spiel.

Schritt 2: Anlegen einer DSC-Konfiguration im Automations-Konto
Dieser Schritt ist sehr einfach, denn es wird lediglich die Ps1-Datei hochgeladen.

Schritt 3: Die Konfiguration wird in eine Mof-Datei kompiliert und damit ein Knoten angelegt
Dieser Schritt wird bei einfachen Konfiguration direkt im Azure-Portal durchgeführt. Eine Ausnahme liegt vor, wenn Konfigurationsdaten im Spiel sind. In diesem Fall muss die Kompilierung (Stand: Mail 2017) noch per PowerShell-Cmdlets lokal ausgeführt werden.

Das erledigt die folgende Befehlsfolge:

Der Name der VM lautet in diesem Beispiel „TestVM2“.

Die Bereitstellung dauert ein paar Minuten. Ging alles gut, werden am Ende aufgetretene Meldungen ausgegeben. Fehler sollten keine dabei sein, lediglich ein Warnung, die besagt, dass das Microsoft.PowerShell.Managagement-Snapim nicht geladen wurde. Dies ist aber nur eine Warnung, kein Fehler.

Ob das Kompilieren erfolgreich war, erfährt man aus dem Azure-Portal.

Schritt 4: Der Knoten wird einer VM zugeordnet
Im letzten Schritt muss lediglich der Pull Server-Knoten einer VM zugeordnet werden. Dieser Schritt besteht aus weiteren Teilschritten und wird im Portal nicht wirklich intuitiv angeboten:

>Hinzufügen einer Azure VM über +Azure-VM hinzufügen
>Auswahl der VM und Bestätigen mit OK
>Registrieren des Knotens und Bestätigen der Konfigurationsdaten und Bestätigen mit OK
>Erstellen

Ist dies erledigt, dauert es ca. 15 Minuten bis die Konfiguration angewendet wird.

Azure VM per PowerShell und CustomScript Extension vorkonfigurieren

*** noch im Rohmodus – der Beitrag wird in Kürze noch einmal überarbeitet ***

Möchte man man eine Azure VM skriptgesteuert vorbereiten, z.B. bestimmte Anwendungen vorabinstallieren, gibt es dafür grundsätzlich zwei Alternativen, die keine Tools anderer Hersteller oder Microsoft SCCM voraussetzen:

>Azure DSC
>Eine Extension in Gestalt der CustomScriptExtension, die entweder beim Anlegen der VM oder nachträglich als Erweiterung ausgewählt wird

Beide Ansätzen besitzen, wie könnte es anders sein, ihre kleineren Vor- und Nachteile. Variante 1, die ich in seinem separaten Blog-Eintrag in naher Zukunft vorstellen werde, setzt etwas mehr Vorbereitungsaufwand voraus und kann aktuell nicht vollständig im Portal umgesetzt werden, ist aber flexibler, da sich eine beliebige Anzahl an VMs damit konfigurieren lassen. Variante 2 ist einfach, da lediglich ein vorhadenes PowerSHell-Skript geladen wird, dafür aber nicht so flexibel (ich bin mir im Moment nicht sicher, ob die Erweiterung per Azure-Cmdlets einer beliebigen Anzahl an VMs zugeordnet werden kann. Generell empfehle ich DSC, aber mehr nur ein paar VMs vorkonfigurieren möchte, kommt mit Variante 2 schneller zum Ziel.

Schritt 1: Auswahl der Erweiterungen für die VM

Im ersten Schritt meldet man sich am Azure-Portal aus, selektiert die bereits vorhandene VM und danach Erweiterungen.

Auswahl der Erweiterungen für eine VM

Schritt 2: Hinzufügen der CustomScriptExtension

Sollte die CustomScriptExtension noch nicht in der Liste der Erweiterungen enthalten sein, muss sie über das +-Zeichen hinzugefügt werden. Dabei wird ein vorhandenes Ps1-Skript hochgeladen. Theoretisch sollte es auch aus einem Storage Account geladen werden können.

Anlegen einer CustomScript Extension


Schritt 3: Anwenden der CustomScriptExtension
Wurde die CustomScriptExtension eingerichtet, erscheint sie in der Liste der Erweiterungen.

Die CustomScriptExtension wurde bereitgestellt

DSC in der Praxis – Anwendungen installieren über die Ressourcen Package und WindowsProcess

DSC lässt sich für viele Konfigurationsaufgaben einsetzen, z. B. auch für die Installation von Anwendungen. Warum nicht? Die Idee ist, dass man eine Liste von Exe- und Msi-Dateien zusammen mit Argumenten zusammenstellt, die auf einer Reihe von Nodes installiert werden sollen. Man schießt die DSC-Konfiguration per Start-DSCConfiguration ab („Make it so“), DSC verteilt diese auf die einzelnen Nodes und der LCM auf jedem Node arbeitet die Liste ab und installiert jede Anwendung durch Ausführen der Exe-Datei. Hört sich in der Theorie einfach an, die Umsetzung in der Praxis ist auch nicht allzu kompliziert, sieht man von den üblichen Stolpersteinen ab.

Einen guten Überblick für den Einstieg gibt ein Artikel vom PowerShell-Experte Adam Betram: https://4sysops.com/archives/installing-software-with-powershell-dsc. Er beschreibt, wie sich per DSC und der Package Ressource eine Anwendung (7-Zip) installieren lässt. In seinem Beispiel befindet sich die Msi-Datei aber in einem lokalen Verzeichnis. Die Frage ist, wie kommt sie dort hin? Entweder durch Kopieren aus einer Freigabe, die z.B. für ein Azure Storage-Blob angelegt wurde. In diesem Fall kommt aber die Berechtigungsproblematik ins Spiel und das Kennwort muss per Zertfikat verschlüsselt werden. Einfacher und unkomplizierter geht es per direkten Download der Datei. Da es dafür aber offenbar keine Ressource gibt, habe ich solche selber gebastelt.

Meine Lösung soll insgesamt etwas flexibler sein, in dem mehrere Apps installiert werden, deren Namen in den externen Konfigurationsdaten abgelegt sind. Außerdem sollen sich die Exe-Dateien in der Cloud befinden, z. B. in einem Azure Storage Account.

Das folgende Listing zeigt die gesamte DSC-Konfiguration, allerdings ohne die Konfigurationsdaten.

Die Konfigurationsdaten habe ich in einer separaten Psd1-Datei untergebracht.

Ausgeführt wird die Konfiguration per Start-DSCConfiguration:

Ich habe mich in dem Beispiel auf Exe-Installationsprogramme beschränkt, damit es übersichtlich bleibt. Für Msi-Pakete ist die Package-Ressource die etwas bessere Wahl.

Den Download der Exe-Dateien aus meinem Azure Storage-Blob übernimmt eine DSC-Ressource, die ich dafür geschrieben habe. Sie besteht aus zwei Dateien:

>xDownloadFileResource.psd1
>xDownloadFileResource.psm1

Die Psd1-Datei ist wie folgt aufgebaut:

Die Logik der Ressource ist in der Psm1-Datei enthalten:

Leg unter C:\Program Files\WindowsPowerShell\Modules ein Verzeichnis „xDownloadFileResource“ und darin ein Unterverzeichnis „1.0“ an und leg die Psd1- und die Psm1-Datei in diesem Verzeichnis ab.

Grafische Oberflächen für PowerShell-Skripte – ein WPF-Template

Ein PowerShell-Skript mit einer Benutzeroberflächen ausstatten ist grundsätzlich nicht schwer – die größte Herausforderung für einen Admin, der kein Entwickler ist, ist es, das Prinzip zu verstehen und einen Einstieg zu finden. Es gibt natürlich sehr viel Material im Web, unzählige Tutorials und Beispiele. Aber auch die Fülle des Angebots macht die Übersichtlichkeit nicht unbedingt besser, da viele Beispiele bereits einige Jahre alt und teilweise auch ein wenig umständlich sind.

Ich bevorzuge in der Regel WPF, da mir der Umstand, dass das Fenster in XML, genauer gesagt in XAML, definiert wird, etwas besser gefällt. Auch wenn weder Eingabehilfen noch keinen Designer gibt (sieht man von Visual Studio ab), ist die Umsetzung etwas einfacher als bei Windows Forms, wenngleich ich seit vielen Jahren mit WPF programmierer, so dass mir die ganze Syntax sehr vertraut ist (man muss lediglich daran denken, dass es auf die Groß-/Kleinschreibung ankommt).

Um nicht jedes Mal bei 0 beginnen und sich von Irgendwo her im Web ein XAML-Grundgerüst besorgen zu müssen, stelle ich im Folgenden ein Grundgerüst für ein PowerShell-Skript vor, das ein Fenster mit einem Label, einer Textbox und einem Button anzeigt. Nach einem Klick auf den Button wird der Inhalt der Textbox in einer Messagebox angezeigt und das Fenster wird geschlossen.

Das Template ist als Ausgangspunkt für eigene Dialogfelder sehr gut geeignet. Das erforderliche Know-how zu WPF findet man an vielen Stellen im Web, z. B. http://wpftutorial.net und http://www.wpf-tutorial.com. Auf beiden Seiten findet man viele kleine XAML-Beispiele für die Standard-Controls, die man 1:1 in die XAML-Definition des PowerShell-Skripts übernehmen kann.

Auch wenn das natürlich eine Menge Text ist, ist das Ganze am Ende doch eine relativ überschaubare Anlegenheit.

Die Farbwahl es zugegeben etwas gewagt, aber es sollte kein Problem sein, dass sich jeder passendere Farben ausdenkt.

Tipp: Wer hauptsächlich mit der ISE arbeitet, kann den gesamten Text per New-ISESnippet-Cmdlet als Textausschnitt ablegen und ihn dadurch als Ausschnitt verfügbar machen. In Visual Studio Code ist es etwas mehr Arbeit. Alles Weitere findet ihr unter der folgenden Adresse:

https://code.visualstudio.com/docs/editor/userdefinedsnippets

Abbildung: Das WPF Dialogbox-Template in Aktion

Das Microsoft PowerShell-Team kommt nach Deutschland (und viele andere PowerShell-Experten auch)

Wer einmal den Erfinder der PowerShell, Jeffrey Snover, persönlich kennenlernen möchte, hat dazu Anfang Mai in Hannover die Gelegenheit. Zusammen mit vielen anderen „PowerShell-Promis“ kommt der Vater der PowerShell zur PowerShell Conf Europe, die vom 3. bis 5. Mai in Hannover stattfindet.

Auf der Konferenz werden 3 Tage PowerShell-Know how von den bekanntesten Experten der weltweiten PowerShell-Community geboten. Mehr Know-how an einem Ort geht fast nicht mehr.

Weitere Infos zur Teilnahme gibt es unter psconf.eu.

PowerShell-Tipp: Dateien umbenennen

Eigentlich ist das Umbenennen von Dateien per PowerShell keiner besonderen Erwähnung wert, eigentlich aber doch. Vor kurzem musste ich im Rahmen der Überarbeitung meines Windows 10-Buches auf das Creators Update (eine Menge Arbeit, aber das nur nebenbei, vor allem, wenn man zu spät anfängt;) eine Ordner mit Dateien umbenennen. Ich war gerade dabei den Ordner mit F2-Taste drücken, neuen Namen eintragen, Enter-Taste drücken, F2-Taste drücken, neuen Namen eintragen usw. durchzugehen bis mir einfiel, dass es dafür doch die PowerShell gibt. Leider ist das Rename-Item-Cmdlet etwas seltsam, ich kann mir nie merken, ob man für den NewName-Parameter nur den Namen übergibt oder den gesamten Pfad, so dass es gerade für einen PowerShell-Neuling eine große Hürde darstellt.

Der folgende Befehl nennt alle Dateien im aktuellen Verzeichnis um, in dem er ein „PM_“ zu Beginn des Dateinamens entfernt:

Soll nicht nur der Name, sondern gleich der Pfad der Datei geändert werden, nimmt man dafür das Move-Item-Cmdlet.