Archiv der Kategorie: Tipps

Tipp: Json-Daten flexibel verarbeiten

Das JavaScript Object Notation-Format, kurz JSON, kommt immer mehr in Mode, da Webservice-Aufrufe auch im Admin-Alltag eine Rolle spielen.

Das folgende Beispiel ist aus der Notwendigkeit entstanden, den Inhalt einer JSON-Datei, die in einem Webverzeichnis liegt, verarbeiten zu müssen. Mehr oder weniger ad-hoc.

Die URL der Datei ist http://www.activetraining.de/Nanohub/Kurs1001.json.

Das Herunterladen erledigt Curl:

Hinter Curl steckt natürlich nicht das vielseitige Allround-Kommandozeilentool, sondern lediglich das Invoke-WebRequest-Cmdlet. Die Rückgabe ist daher nicht Text, sondern ein WebResponseObject-Objekt. Der heruntergeladene Inhalt steckt in der Content-Property, aber als Byte-Folge.

Gesucht ist daher eine „Trick“, um aus der Byte-Folge Text zu machen. Wer sich ein wenig mit .NET auskennt weiß, dass es dafür die Encoding-Klasse gibt, die verschiedene „Encoder“ über statische Properties zur Verfügung stellt. Eine gute Wahl ist immer der Default-Encoder. Ein wenig ungewöhnlich ist (jedenfalls für mich;), dass jede statische Encoder-Eigenschaft für ein Objekt steht. Unter anderem bietet jedes Objekt eine GetText-Methode, die eine Byte-Folge in einen Text umwandelt.

Der folgende Aufruf macht aus einer Byte-Folge daher Text:

Das Ganze soll allerdings in eine Function eingebaut werden, so dass sich der Byte-Inhalt direkt nach dem Aufruf von Curl in Text umwandeln lässt.

Auftritt PowerShell-Konsole und dem genialen PSReadLine-Modul, das ab Windows Server 2016 und Windows 10 bekanntlich fest dabei ist und ansonsten per Install-Module PSReadline -Force nachgeladen wird. Dank PSReadline ist es kein Problem, eine komplette Function-Definition in die Konsole einzugeben:

Das Beste daran ist, dass die komplette Function-Definition komfortabel editieren kann. Einfach per [Pfeil oben]-Taste und den üblichen Tasten für die Textbearbeitung. Einfach und genial.

Die Umwandlung des heruntergeladenen JSON in Objekte sieht damit wie folgt aus:

Dank PSReadline lassen sich auch Function-Definitionen in der Konsole komfortabel bearbeiten

Dank PSReadline lassen sich auch Function-Definitionen in der Konsole komfortabel bearbeiten

Meine ersten Schritte mit Docker und wie man einen Linux-Admin von der PowerShell überzeugen kann (Teil 1)

Docker ist das Hype-Wort der Stunde. Zum allergrößten Teil steckt hinter Docker natürlich eine solide Technik und wenig Hype, aber wer die Ankündigungen der Hersteller, inklusive Microsoft, in den letzten 1-2 Jahren verfolgt hat, wird festgestellt haben, dass das Wort Container bzw. Docker als wichtigste Container-Technik Pflichtbestandteil von strategischen Ankündigungen geworden ist. Die IT-Branche braucht alle paar Jahren ihr „next bing thing“ und das ist auch ja grundsätzlich in Ordnung.

Docker ist eine Container-Technik. Eine Container ist eine virtuelle Umgebung, in der eine Anwendung alle für die Ausführung benötigten „Abhängigkeiten“ in Gestalt der entsprechenden Dateien inklusive Dateisystem und Netzwerkschnittstelle vorfindet. Im Unterschied zur Virtualisierung ist der Aufwand deutlich geringer, da alle Container unter einem bestimmten Betriebssystem ausführen – ein Container kann aber auch sein eigenes Betriebssystem mitbringen. Container bieten daher zahlreicher Vorteile für die Bereitstellung, von der vor allem Webanwendungen und generell Anwendungen ohne eigene Benutzeroberfläche profitieren. Sie benötigen weniger Arbeitsspeicher, weniger Festplattenspeicher, starten schneller als eine VM, sind offenbar deutlich besser skalierbar, können im Verbund (Stichwort: Kubernetes, das nächste Hype-Wort nach Container) eingesetzt, lassen sich schnell erstellen. Sozusagen eine Wegwerfverpackung für eine Anwendung.

Mit der PowerShell gibt es zunächst keine Berührungspunkte. PowerShell-Skripte sind bekanntlich keine Anwendungen, sie werden auf einfachste Weise bereitgestellt und es gibt nur selten Abhängigkeiten, die vor ihrer Ausführung aufgelöst werden müssten – Module und DSC-Ressourcen fallen mir natürlich ein. Die Ausführung eines Skripts in einem Container könnte daher eine Option sein, wenn mehrere Module verwendet oder eine Abhängigkeit zu einer bestimmten Version des .NET Framework oder in Zukunft auch .NET Core besteht. In diesem Fall gibt man einen Container weiter, aus dem das Skript gestartet wird.

Im Folgenden geht es um eine naheliegende Anwendung: Ich möchte die PowerShell unter Ububtu, Cent OS oder Suse ausprobieren, habe aber eine Lust für jedes OS eine VM einzurichten.

Die gute Nachricht ist, Microsoft bietet ein fertiges Image für einen Container an, in dem Ubuntu 16.04 und eine Beta der PowerShell 6.0 enthalten sind.

Wie das Ganze umgesetzt wird, ist auf der Projektseite der PowerShell Core beschrieben:

https://github.com/PowerShell/PowerShell/tree/master/docker

Die Umsetzung ist sehr einfach. Voraussetzung sind allerdings Windows 10 64 Bit bzw. Windows Server 2016, da sich Docker für Windows nur auf diesen Betriebssystemen installieren lässt.

Die Downloadadresse ist von Docker CE (Community Edition) ist:

https://store.docker.com/editions/community/docker-ce-desktop-windows

Eine weitere Einschränkung besteht darin, dass Docker auch unter Windows einen Linux-Kernel erfordert. Dieser wird bei Docker for Windows über eine Hyper V-VM zur Verfügung gestellt. Es muss daher möglich sein, Hyper V zu aktivieren. Wer bereits VMware Workstation verwendet, müsste VMware Workstation vorübergehend deinstallieren, damit Hyper V aktiviert werden kann.

Wurde Docker for Windows installiert, dauert es einen kurzen Augenblick ist der Dienst zur Verfügung steht. Anschließend steht das Docker-Befehlszeilentool zur Verfügung.

Der folgende Befehl lädt ein Docker-Image mit installierter PowerShell:

Tipp: Sollte es zwischendurch zu „Verbindungsproblemen“ kommen, die dazu führen, dass das Image nicht oder nur unvollständig geladen wird, die folgende kleine Änderung hat bei mir immer geholfen: Trage für den DNS-Server in der Netzwerkverbindung die Adresse 8.8.8.8 ein, der Google-DNS-Server bietet eine sehr schnelle Namensauflösung.

Etwas später startet die PowerShell Core unter Ubuntu in einem Container. Wie lässt sich das feststellen? Gar nicht, denn es ist ja gerade der Sinn und Zweck eines Containers, dass die Anwendung in ihrer vertrauten Umgebung ausführt.

PowerShell Core läuft unter Windows in einem Container mit Linux Ubuntu als OS

PowerShell Core läuft unter Windows in einem Container mit Linux Ubuntu als OS

Jetzt würde es mich doch interessieren, unter welcher Linux-Version die PowerShell ausführt. Als absoluter Linux-Noob fällt mir spontan keine Lösung ein. Unter Linux gibt es kein WMI, sonst wäre es sehr einfach, den Titel und die Version des OS per Win32_OperatingSystem abzufragen. Aber wozu gibt es das Internet? Eine kurze Recherche ergibt, dass man sich nur den Inhalt einer Datei ausgeben lassen muss:

Die Ausgabe besteht aus einer Reihe Zeilen, die alle gleich aufgebaut sind: Name = Wert. Für einen Linux-Admin ist das Zerlegen von Text mit Tools wie Awk und Sed sein tägliches Brot. Doch wenn selbst eine einfache Lösung aus zwei bis drei Zeilen Text besteht, der jede Menge „Spezialzeichen“ enthält, stellt sich die Frage, ob es nicht eine einfachere Lösung gibt. Die gibt es natürlich und besteht darin, den split-Operator im Rahmen einer ForEach-Wiederholung für jede Zeile auszuführen und das Ergebnis der besseren Weiterverarbeitbarkeit zu Liebe als Objekte auszugeben.

Der folgende PowerShell-Befehl macht genau das:

Oder

Ich bin mir sicher, dass Beispiele wie dieses auch bei Linux-Admins schnell ein gewisses Interesse an der PowerShell wecken dürften. Ob sie dabei in einem Container läuft oder nicht spielt dafür natürlich keine Rolle.

Der Inhalt einer Textdatei unter Linux per PowerShell in Objekte zerlegt

Der Inhalt einer Textdatei unter Linux per PowerShell in Objekte zerlegt

Bilder von einem USB-Stick auf eine Freigabe kopieren

Ausgangspunkt ist ein USB-Stick mit einer Reihe von Bilddateien, die auf verschiedene Ordner verteilt sind. Es sollen alle Bilder in einen (!) Ordner auf einem NAS kopiert werden.

Der naheliegende Versuch schlägt fehl:

Grund: Fehlende Authentifierung. Copy-Item besitzt aber einen Credential-Parameter, beim Kopieren von Dateien spielt er aber keine Rolle.

Lösung: Anlegen eines PSDrive

Damit steht das Laufwerk NAS: Neuer Anlauf.

Es funktioniert, aber es wird die Ordnerstruktur kopiert. Ich möchte aber, dass alle Bilder in ein Verzechnis kopiert werden.

Lösung: Anstelle vom Recurse-Parameter bei Copy-Item ein dir aka Get-ChidLitem und die Pipeline verwenden.

Active Directory – das Ablaufdatum eines Benutzerkontenkennworts abfragen

Für das Abfragen der kontenspezifischen Details eines Benutzerkontos gibt es bekanntlich das Search-ADAccount-Cmdlet. Damit lassen sich folgende Kontendetails abfragen:

>Ob ein Konto deaktiviert ist
>Ob ein Konto abgelaufen ist
>Ob sich der Benutzer ausgesperrt hat („locked out“), z.B. durch Mehrmalige Eingabe eines falschen Kennworts
>Ob das Kennwort abgelaufen ist
>Ob das Kennwort niemals abläuft
>Ob ein Konto innerhalb einer angegebenen Zeitspanne ablaufen wird
>Ob ein Konto innerhalb einer angegebenen Zeitspanne inaktiv war

Die letzten beiden Fragen beziehen den TimeSpan-Parameter mit ein. Es gilt die Konvention, dass die Übergabe einer Zahl als String als Tage interpretiert wird. Ansonsten wird der Parameterwert (wie in der Hilfe beschrieben) im Format 90.00:00:00 für 90 Tage übergeben.

Nicht dabei ist ein Parameter über das Abfragen des Ablaufdatums eines Kennworts. Aus gutem Grund, denn diese Eigenschaft hängt u.a. von den Kennwortrichtlinien ab und dem Umstand, dass für das Kennwort ein Ablaufdatum explizit festgelegt wurde was in der Regel nicht der Fall sein dürfte.

Die folgende Befehlsfolge gibt die Ablaufdaten sämtlicher (!) Benutzerkonto der Domäne aus, sofern ein solches existiert. Das Beispiel versteht sich in erster Linie als ein Vorschlag und ist noch nicht perfekt. Ich habe vor Jahren im PowerShell-Teamblog eine Function gefunden und sie endlich einmal selber umgesetzt. Über Kommentare und Verbesserungsvorschläge freue ich mich natürlich. Ich habe auch nicht im Internet geschaut, ob es vielleicht eine bessere Umsetzung gibt.

„Virtuelle Computer“ per Hosts-Datei erzeugen

In Schulungen habe ich nicht immer ein Netzwerkumgebung zur Verfügung. Um dennoch eine Abfrage gegen mehrere Computer halbwegs realistisch vorführen zu können, trage ich eine Reihe von Computernamen in die Hosts-Datei ein und ordne ihnen jeweils 127.0.0.1 zu.

Das klappt zwar mit Ping&Co, nicht aber per ComputerName-Parameter.

Eine kleine Namensauflösung per DNS-Resolve löst das in der Regel nicht existierende Problem und zeigt nebenbei dem Schüler was mit der PowerShell so alles möglich ist:

Angenommen, die Hosts-Datei enthält Einträge für PC1, PC2, PC3 usw. dann legt der folgende Befehl für jeden dieser nicht vorhandenen Computer eine CIM-Session an.

Und dann

Tipp: Mit Subst nicht mehr existierende Laufwerke mappen

Es kommt auch im Jahr 2017 noch vor, dass Anwendungen irgendwelche absoluten Pfade speichern und sollte das Laufwerk aus irgendeinem Grund nicht mehr existieren, lästige Fehlmeldungen anzeigen. Jedes Mal. Und das nicht bei irgendwelchen „Wald&Wiesen-Apps“, sondern z.B. auch bei Visual Studio.

Ich verstehe nicht, warum Microsoft Milliarden in Machine Learning investiert, aber Visual Studio nicht einfach lernen kann, dass z.B. ein Laufwerk H: nicht existiert, auch wenn der Laufwerksbuchstabe einem nicht existierenden USB-Laufwerk zugeordnet wurde.

Abhilfe schaft das Ur-Alt-Kommando Subst. Der folgende Befehl mapt den Pfad E:\ auf das nicht mehr existierende Laufwerk H:

Der Befehl kann in der PS-Befehlszeile direkt so eingegeben. In der Regel wird es aber einen Fehler geben, der besagt, dass H: ein ungültiger Parameter ist. Der Grund ist, dass der Laufwerksbuchstabe H: einem nicht existierenden USB-Laufwerk zugeordnet ist. Abhilfe schafft die Datenträgerverwaltung (einfach „Diskmgmt.msc“ eingeben), in der ihr bei dem Laufwerk einfach den Laufwerksbuchstaben löscht. Danach klappt es auch mit Subst und es gibt keine lästigen Fehlermeldungen nach jedem Windows-Start oder bei anderen Gelegenheiten.

Managed Service Accounts (MSAs) erstellen – per Befehlszeile und mit einem GUI-Tool

Ein Managed Service Account (MSA) ist, vereinfacht, ein Dienstkonto (also ein Benutzerkonto, das ausschließlich von Systemdiensten für die Anmeldung verwendet wird), das von Windows insofern verwaltet wird, dass sich der Admin nicht mehr um das Erneuern von Kennwörter kümmern muss.

MSAs wurden mit Windows Server 2008 R2 eingeführt.

Bis zur aktuellen Windows Server-Version können MSAs nur per PowerShell angelegt werden.

Das Anlegen eines MSA besteht aus drei Schritten:

1. Anlegen des MSA per New-ADServiceAccount-Cmdlet

Bei diesem Schritt wird das Konto auf einem DC angelegt.

2. Hinzufügen des MSA auf dem jeweiligen Computer, auf dem es später verwendet werden soll, per Add-ADComputerServiceAccount-Cmdlet

$ServiceAccountHost ist der Name des Computers, $ServiceAccount Name des MSA oder gleich das ganze Objekt, das den MSA repräsentiert.

3. Installieren des MSA auf dem Computer, auf dem es später verwendet werden soll, per Install-ADServiceAccount-Cmdlet.

Das war im Prinzip alles. In der Praxis kann allerdings eines „schief gehen“, so dass die im Grunde einfachen Cmdlets zu Fehlermeldungen führen.

Ein Aspekt ist, dass auf dem DC ein „Kds Rootkey“ angelegt werden muss, der sofort wirksam wird.

Ein weiterer Aspekt ist ein Bug, der inzwischen behoben werden sollte, durch den der Name eines MSA nicht länger als 16 Zeichen werden dürfte.

Alles ein wenig umständlich. Zum Glück gibt es ein geniales GUI-Tool von Cjwdev, mit dessen Hilfe das Anlegen eines MSA sehr einfach wird:

http://cjwdev.co.uk/Software/MSAGUI/Info.html

Die aktuelle Version ist 1.6.

Nuget-Packages direkt laden

Nuget-Packages sind Paketdateien, die in der Regel Assembly-Dateien enthalten, die von Entwicklern für ihre Projekte verwendet werden. Seit Visual Studio 2012 werden solches Packages über die Paketverwaltung oder über das PowerShell-Konsolenfenster per Install-Package hinzugefügt.

In einem PowerShell-Skript benötigt man im Allgemeinen keine Packages. Wie immer gibt es Ausnahmen. Eine solche Ausnahme ist das sehr nützliche HtmlAgilityPack, das aus einer Assembly-Datei besteht, mit deren Hilfe sich die Inhalte einer Html-Seite per XPath und vertrauten XML-Methoden wie SelectNodes und SelectSingleNode ansprechen lassen.

Lee Holmes beschreibt die Technik des „Html Scraping“ mit Hilfe des HtmlAgilityPack sehr schön unter der folgenden Adresse:

http://www.leeholmes.com/blog/2010/03/05/html-agility-pack-rocks-your-screen-scraping-world/

Ich habe darüber auch bereits etwas geschrieben, aber wer den Artikel von Lee Holmes liest, weiß damit alles Wissenswerte und kann sich das Lesen anderer Blog-Einträge erst einmal sparen.

Bis auf eine Kleinigkeit eventuell: Normalerweise lädt man die Assembly-Datei von der Codeplex-Projektseite herunter. Es gibt zwei Gründe, die dagegen sprechen: Das Codeplex-Portal soll irgendwann in naher Zukunft eingestellt werden. Die Methode ist nicht besonders elegant, wozu gibt es das PackageManagement-Modul seit PowerShell 5.0?

Damit auch Nuget-Packages geladen werden, muss eine Package Source für die Adresse https://www.nuget.org/api/v2 existieren (dies ist zwar die „alte“ Adresse, sie funktioniert natürlich nach wie vor). Das Get-PackageSource-Cmdlet fragt diese ab:

Sollte die Package Source wider Erwarten noch nicht dabei sein, muss sie zuerst per Register-PackageSource angelegt werden:

Dabei muss auch der Providername, in diesem Fall Nuget angegeben werden. Das Ergebnis ist eine neue Package Source mit dem Namen „NugetP“.

Damit kann das HtmlAgilityPack-Package per Install-Package-Cmdlet heruntergeladen werden:

Abgelegt wird das „Package“ mit seinen Dateien unter C:\Program Files\PackageManagement\NuGet\Packages. Die entscheidende Datei HtmlAgilitypack.dll befindet sich in einem der zahlreichen Unterverzeichnisse, z.B. unter HtmlAgilityPack.1.4.9.5\lib\Net45. Sie wird per Add-Type-Cmdlet und dessen Path-Parameter geladen.

Eine Alternative ist der direkte Aufruf von Nuget.exe, das zuerst per dir lokalisiert werden muss.

Sollte das aus irgendeinem Grund nicht funktionieren, kann es erforderlich sein, die Nuget-Paketquelle zuerst zu aktivieren:

Ein Nuget sources list listet alle vorhandenen Sources auf, ein Nuget list alle verfügbaren Pakete.

Windows 7 – von 2 auf 5.1 – mit Zwischenschritt über die Version 5.0

Vor einiger Zeit hatte ich beschrieben, wie man unter Windows 7 in einem Schritt von Version 2.0 auf 5.0 aktualisiert:

http://poshadmin.de/?p=616

Möchte man gleich auf die aktuelle Version 5.1 umsteigen, was ich empfehle, ist wieder etwas mehr Aufwand erforderlich, denn auch hier muss zuerst auf Version 5.0 aktualisiert werden. Erst danach kann das Update für die Version 5.1 installiert werden.

Es ist interessant, dass das Update für Windows 7 als Zip-Datei zur Verfügung gestellt wird, das aus einer Ps1- und der Msu-Dateu besteht. Offiziell soll man die Ps1-Datei starten, die eine Reihe von Tests durchführt, um zu entscheiden, ob das Update durchgeführt werden kann usw. Wer WMF 5.0 bereits installiert hat, startet die Msu-Datei direkt.

Sollte die Installationsdatei wider Erwarten doch in einer Update-Endlosschleife höngen bleiben, liegt dies an der Datei WSUSSCAN.cab. In diesem Fall sieht es fast so aus, als müsste die ganze Prozedur auch für das 5.1-Update für jede einzelne (!) Cab-Datei wiederholt werden. Erst wenn die Neustart-Meldung erscheint, wurden alle Teil-Updates installiert.

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.