Archiv des Autors: PemoAdmin

PowerShell-Tipp: Variablen in WMI-Abfrage einbauen

Der folgende Tipp ist sehr allgemein und eher harmlos, aber praktisch. WMI-Filter verwenden bekanntlich eine andere Syntax, u.a. müssen Zeichenketten in einem Vergleich in Apostrophe gesetzt werden.

Get-CimInstance -ClassName Win32_Service -Filter "Name='WinRM'"

Das funktioniert (natürlich) auch mit einer Variablen:

$Dienst = "WinRM"
Get-CimInstance -ClassName Win32_Service -Filter "Name='$Dienst'"

Anders als man es als PowerShell-Neuling vielleicht vermuten könnte, wird eine Variable auch dann ersetzt, wenn sie in Apostrophen steht.

PowerShell-Know how: Die Rolle der psbase-Eigenschaft

Jedes PowerShell-Objekt besitzt eine psobject– und eine psbase-Property. Beide sind also immer da, werden aber relativ selten für irgendwelche Abfragen benötigt. Während psobject das Objekt beschreibt, das von PowerShell für jedes Objekt als eine Art „Rahmenobjekt“ angelegt wird (auch „Adapter“ genannt, da hier das klassische Adapter-Entwurfsmuster angewendet wird), erlaubt psbase eine Abfrage der Members des „eingebauten“ Objekts.

Und wann braucht man das?

Zum Beispiel, wenn man die Members der (abstrakten) Array-Klasse erhalten will, auf der PowerShell-Arrays basieren.

$l = 11,22,33<br>$l.psbase | Get-Member -MemberType Property

Das gilt auch für Listen aller Art.

$lg = [System.Collections.Generic.List[Int]::new()<br>$lg.add(11)<br>$lg.psbase | Get-Member

Die Rückgabe ist allerdings kein RuntimeType-Objekt, sondern ein PsObject-Objekt.

psobject und psbase sind elementare Bestandteile des PowerShell-Typssystems, das auf dem .NET-Typsystem aufsetzt und zu mehr Flexibilität führen soll (das Thema dynamisches Typisieren kam durch Programmiersprachen wie Ruby und Python damals gerade erst in Mode). Leider aber auch zu etwas Verwirrung (da nehme ich mich selber nicht aus;). Eine gute Zusammenfassung gibt der „Vater“ der PowerShell, Jeffrey Snover, in einem Blogeintrag aus dem Jahr 2006 (!):

Blog-Tipp: Die ACL eines Systemdienstes abfragen

Es ist meines Wissens nicht möglich, die Zugriffsberechtigungen für einen Systemdienst abzufragen. Der folgende Befehl gibt daher nur vermeintlich, die ACL des WinRM-Dienstes zurück (der dazu ausführen muss), sondern die ACL der Datei svchost.exe.

Get-Ciminstance win32_service -filter "Name='Winrm'" | Select-Object @{n="Id";e={$_.ProcessId}} | Get-Process | Get-ACL

Ein Pendant zu sc.exe sdshow winrm gibt es daher nicht.

Ein sehr guter Blogbeitrag von PowerShell-Experten Ron Edwards zeigt, wie es gehen könnte:

Das Beispiel macht aber auch deutlich, dass es insgesamt eine aufwändige Angelegenheit ist, da API-Funktionen des Betriebssystems eingebunden werden müssen. Für ad hoc-Abfragen kommt eine Umsetzung daher nicht in Frage.

Bleibt also nur sc.exe mit dem sdshow-Parameter, das aber nur die DACL zurückgibt. Und wie macht man aus dem SDDL-Text eine „lesbare“ Ausgabe? Ganz einfach mit dem ConvertFrom-SddlString-Cmdlet.

sc.exe sdshow winrm | Select-Object -Skip 1 | ConvertFrom-SddlString

Wer spontan darauf kommt, warum das Select-Object-Cmdlet mit -Skip 1 erforderlich ist, ist ein heißer Anwärter auf ein „PoshAdmin“-T-Shirt falls es das eines Tages geben sollte;)

PowerShell für Anfänger – die praktische psobject-Property

Es ist eine der größeren Herausforderungen für viele PowerShell-Anfänger den Unterschiedzwischen Select-Object und Where-Object zu verstehen. Das liegt weniger an der Namensgebung, sondern in erster Linie daran, dass am Anfang überhaupt nicht klar ist, was es konkret bedeutet, dass Ausgaben immer aus „Objekten“ bestehen.

Die nächste Hürde besteht darin, den Unterschied zwischen dem Namen einer Eigenschaft und ihrem Wert zu verstehen.

Ein

Get-Process -ID $PID  | Select-Object -Property *Time*

gibt vom aktuellen PowerShell-Prozess alle Eigenschaften mit ihren Werten zurück, in deren Name das Wort „Time“ enthalten ist.

Wie aber müsste die Abfrage lauten, wenn alle Eigenschaften ausgegeben werden sollen, die z.B. keinen Wert besitzen?

Anders, als man es vielleicht vermuten würde, ist eine solche Abfrage mit Where-Object zunächst nicht möglich. In der vereinfachten Syntax muss der linke Vergleichswert immer der Name einer Property sein.

Ein

Get-Process -ID $PID | Where-Object * -ne $null

funktioniert nicht, da für den Property-Parameter kein Platzhalter eingesetzt werden darf (das musste ich allerdings erst noch einmal selber überprüfen;).

Möchte man für eine Abfrage auf die „Beschaffenheit“ (Medaten) eines Objekts zugreifen, also z.B. auf die Namen, Typen und Werte der Properties, kommt die „unsichtbare“ psobject-Property ins Spiel, über die jedes (!) Objekt verfügt. Siegibt das „PowerShell-Objekt“ zurück. Das ist jenes Objekt, von dem sich jedes von der Powershell angelegte Objekt ableitet.

Das PowerShell-Objekt besitzt folgende Properties:

>BaseObject

>IntermediateBaseObject

>Members

>Method

>Properties

>TypeNames

Der folgende Befehl gibt alle Properties des Process-Objekts zurück.

(Get-Process -ID $PID).psobject.properties

Diesselben Informationen liefert zwar auch Get-Member, in diesem Fall lassen sie sich aber flexibler weiterverarbeiten.

Die folgende Abfrage gibt die Namen und (damit die Ausgabe nicht so „nackt“ ist) auch die Typen aller Eigenschaften des PowerShell-Prozess-Objekts zurück, die keinen Wert besitzen.

(Get-Process -ID $PID).psobject.properties.where{$_.Value -eq $null} | Se<br>lect-Object Name,TypeNameOfValue
Abb. Dank der psobject-Property ist auch ein Filtern nach den Namen und Werten von Eigenschaften möglich.

Praktisch ist diese Technik bei Abfragen mit Get-WmiObject, da sichd die zahlreichen Eigenschaften, die mit __ beginnen, ausblenden lassen.

(Get-WmiObject -Class Win32_Process -Filter "ProcessId=$PID").psobject.Pr<br>operties.Where{$_.Name -notlike "<em>_*" -and $</em>.Value -ne $null} | Select Name,Value | Sort Name

Auch wenn der Tippaufwand für diese Abfrage insgesamt doch recht groß ist, werden endlich nur jene Properties ausgeben, die keine Spezial-WMI-Properties sind, und die einen Wert besitzen.

Fazit: Bei der PowerShell besitzt jedes Objekt eine psobject-Eigenschaft. Sie liefert sozusagen das „Rahmenobjekt“, in das jedes Objekt eingebaut wird. Mit seiner properties-Eigenschaft lassen sich neben den Namen auch die Werte von Eigenschaften abfragen was in manchen Situationen ganz praktisch sein kann. Auch wenn man diese Eigenschaft nie verwenden wird, sollte man wissen, dass es sie gibt.

PowerShell für Anfänger – Wie liest man die Parametersyntax eines Cmdlets?

Jedes PowerShell-Command besitzt eine Reihe von Parametern, die immer in einer einheitlichen Schreibweise als Teil der „Syntax“ ausgegeben werden (z.B. per Get-Command und dem Syntax-Parameter).

Was mir am Anfang in Schulungen etwas schwer fiel zu erklären war, was die eckigen Kammern bei den Parameternamen zu bedeuten haben. Mal gibt es welche, mal gibt es sie nicht.

Der Hintergrund ist natürlich der Umstand, dass es bei den meisten Commands Pflichtparameter gibt, der Name eines Parameters aber auch entfallen kann, wenn es ein Positionsparameter ist.

Hier die (eigentlich sehr einfache) Regel:

>Ist nur der Name des Parameters in eckige Klammern gesetzt, ist der Parameter ein Pflichtparameter und ein Positonsparameter – sein Name kann daher entfallen.

>ist der gesamte Parameter in eckige Klammen gesetzt, ist der Parameter kein Pflichtparameter und kann komplett entfallen.

>Die eckigen Klammern am Ende des Datentyps besitzen eine andere Bedeutung. Sie geben an, dass auch mehrere Parameterwerte per Komma getrennt übergeben werden können.

Eine Frage zum Verständnis: Wie viele Pflichtparameter besitzt das ForEach-Object-Cmdlet und darf der Name des Process-Parameters weggelassen werden? Die Auflösung erfolgt in Kürze.

Die Antwort: Einer und Ja. Es gibt aber glaube niemanden, der jemals den Process-Parameter hingeschrieben hat;)

Neues zur PowerShell (Stand: Mai 2020)

Auch wenn alles im PowerShell-Teamblog zu lesen ist (der unter neuer und etwas gewöhnungsbedürftiger Optik vor einiger Zeit umgezogen ist – die neue Adresse ist devblogs.microsoft.com/powershell), hier eine Kurzzusammenfassung:

>PowerShell 7.0 wurde im Januar freigegeben, die Version 7.1 liegt bereits als Preview vor. Die wichtigste „Neuerung“ ist die Umstellung auf das kommende .NET 5.0, das .NET Framework und .NET Core vereinen wird. Darüber hinaus gibt es aber zahlreiche kleinere Verbesserungen bei einzelnen Cmdlets und Fehlerkorrekturen.

>PowerShellGet 3.0

Es gibt eine komplett überarbeitete Version des mit Version 5.0 eingeführten „Package Managers Managers“ für die Paketverwaltung für Module, Skripte und DSC-Ressourcen. Das ist positiv, da die erste Version von PowerShellGet einige Schwächen hatte.

>Out-GridView für die Konsole

Das ist eine sehr interessante Neuerung. Eine GridView-Ausgabe für die Konsole. Damit lassen sich die Rückgabewerte einer Abfrage deutlich besser darstellen als per Format-Table. Aktuell gibt es die ConsoleGuiTools nur für Linux und MacOS, in Kürze wird es sie natürlich auch für Windows geben (bevor jetzt jemand vermutet, dass dies ein weiterer Beleg dafür ist, dass Microsoft heimlich plant, Windows durch Linux zu ersetzen – technisch wäre es vermutlich mit vertretbarem Aufwand möglich – Schuld ist in diesem Fall lediglich ein harmloser Bug in der externen Datei Gui.cs, die ein portable GUI-Bibliothek im Stile einer MS-DOS-GUI zur Verfügung stellt – wer Programme wie Midnight Command kennt, weiß was gemeint ist;).

>Secret Mangement – was steckt dahinter?

Wenn ein Blog-Eintrag eines weltweiten Konzerns mit „We are excited to release…“ beginnt, muss man eigentlich nicht weiterlesen, denn das ist in den USA seit Jahrzehnten bekanntlich Standard-Marketing-Englisch. Aber wenn es jemand aus dem PowerShell-Team schreibt, lese ich natürlich (ganz aufgeregt;) weiter. Und es geht um Secrets. Wir wissen ja alle, dass der Umgang mit SecureStrings und PSCredentials nicht der Weisheit letzter Schluss sein kann (und die Version 5.0 eingeführten CMS-Cmdlets, die das Verschlüsseln von Texten mit Zertifikaten ermöglichen sollen, dürfte niemand benutzen). Und tatsächlich geht es mit diesem Modul genau in diese Richtung. Ein älterer Blog-Eintrag, den ich aber bislang noch nicht gelesen hatte, geht ausführlicher auf die Hintergründe des Secret Management-Moduls ein:

und

Die Kurzzusammenfassung: Mit dem Modul wird es möglich, beliebige „Geheimnisse“ (z.B. Texte aber auch vorhandene SecureStrings) sicher lokal zu speichern. Diese Technik löst (natürlich) nicht PSCredentials und SecureStrings ab, aber es wird mit ihr möglich, einen SecureString deutlich sicherer und konsistenter abzulegen als es aktuell der Fall ist. Und: Wie es in dem zweiten Blog-Beitrag von Paul Higinbotham beschrieben wird: Der vom SecretManagement-Modul verwendete Default-Vault speichert die Secrets im User Context, also nichts Neues, aber dank Vault Extensions könnte ein Secret so gespeichert werden, dass es übertragbar wird, z.B. für eine Anmeldung an eine Webseite aus einer App heraus (?).

Aktuell ist das SecretManagement-Modul noch Preview – wer es installieren möchte, muss bei Install-Module den (mir bislang unbekannten) Parameter AllowPreview verwenden:

Install-Module Microsoft.PowerShell.SecretManagement -Repository PSGallery -AllowPrerelease

Das grundsätzliche Problem mit SecureStrings wird also nicht „out of the box“ gelöst, wenngleich es grundsätzlich möglich ist – das hätte Blog-Autorin Sidney Smith eigentlich erwähnen sollen. Auf diesen Umstand weist auch Klaus Schulte in seinem Kommentar am Schluss des Blogs hin. Schade, dass solche Beiträge (man muss leider sagen wie üblich) nicht vom PowerShell-Team weiter kommentiert werden. Ein konstruktiver Dialog mit dem „Anwender/Kunden“ kommt damit nicht zustande. Es bleibt bei der Verkündigung einer Neuerung (anders sieht es natürlich im Projektportal aus, wo zwischen den Entwicklern und Usern eine rege Diskussion entsteht, wenn es um „Issues“ wie vermeintliche Fehler oder Verbesserungsvorschläge geht). Interessant ist aber der Hinweis von Klaus Schulte auf KeePass und das PoshKKeePass-Modul, das ich bislang noch nicht verwendet habe.

Zusammenfassung: Die Entwicklung der PowerShell geht in die richtige Richtung. Leider ist die „Kunden-Kommunikation“ des PowerShell-Teams ausbaufähig. Sowohl quantitativ als auch qualitativ. Ich bezweifele, dass sich die Mehrheit der PowerShell-Anwender die Zeit nimmt und versucht, über die unregelmäßigen und leider oft etwas unvollständigen Blog-Einträge auf dem laufenden zu bleiben und sich die gegebenenfalls fehlenden Puzzlestücke zusammenzusuchen. Die entscheidende Frage, warum man eine bestimmte Neuerung im Admin-Umfeld wirklich braucht, wird leider so gut wie nie adressiert.

Positiv ist, wieviele der kleinen Änderungen und Fehlerkorrekturen aus der Community stammen (dabei frage ich mich allerdings immer, wo die Leute die Zeit hernehmen, die für das Einarbeiten in den umfangreichen Quellcode und das Testen einer Änderung erforderlich ist bevor man überhaupt einen Pull Request starten kann).

Bei PowerShell 7.0 sind für mich die wichtigsten Innovationen das Remoting per SSH und die Parallelisierung bei ForEach-Object durch den neuen Parallel-Parameter. Auch das neue PowerShellGet-Modul, das Secret Management-Modul und die portablen GUI-Oberflächen, die zu 100% in der Konsole umgesetzt werden, wirken attraktiv und werden die Möglichkeiten, Leistungsfähigkeit und damit auch die Reichweite von PowerShell-Skripten verbessern.

PowerShell-Challenge der Woche

Gesucht ist ein Befehl, der alle Common-Parameters eines Commands ausgibt, also z.B. Verbose, ErrorAction, WarningVariable usw.

Ich würde dazu die Metadaten eines Parameter-Objekts abfragen, die per Get-Command, dem Commandnamen und der Parameters-Property geliefertert werden.Die Parameters-Property ist eine „hässliche“ Hashtable, also muss sowohl die Parameters- als auch die Values-Properties expandiert werden.

Der folgende Befehl liefert alle Parameter einzeln:

Get-Command Get-Printjob | Select-Object -ExpandProperty Parameters | Select-Object -ExpandProperty Values

Wie an der Ausgabe zu erkennen ist, kommt bei einigen der Parameter-Objekte in der Attributes-Eigenschaft ein „CommonParameters“ vor. Dahinter steckt die TypeId-Property. DIe Frage ist daher, wie man nur solche Parameter erhält, bei denen das der Fall ist.

Das ist mein Versuch, der nach einer halben Stunde herumprobieren, herauskam (am Beispiel des Get-PrintJob-Commands):

Get-Command get-printjob | Select-Object -ExpandProperty Parameters | Select-Object -ExpandProperty Values | Where-Object { ($_.Attributes.TypeId.Where{$_ -match "Common"}).Count -eq 0 }

Es gibt sicher kürzere und elegantere Wege die Namen der Common-Parameters zu erhalten, zumal nicht alle Common Parameters einen System.Management.Automation.Internal.CommonParameters-Typ besitzen. Warum auch immer;)

Die Challenge ist damit nicht gelöst und lautet: Wie erhalte ich die Namen der Common Parameters eines Commands aus den Metadaten des Commands?

Natürlich sind die Common Parameters immer gleich und lassen sich allgemein über die Properties der Klasse System.Management.Automation.Internal.CommonParameters abfragen. Aber die Challange soll, sofern überhaupt möglich, über die Metadaten eines Commands gelöst werden.

Das „Geheimnis“ von Set-Culture

Wie viele Stunden habe ich schon damit verbracht herauszufinden, wie sich während der Ausführung eines PowerShell-Befehls die „Kultur“ vorübergehend ändern lässt? Die Frage kann natürlich außer mir niemand beantworten. Ich kenne die Antwort auch nicht, aber es waren einige Stunden (mehr als 3). Dabei ist alles sehr einfach.

Set-Culture setzt die Kultur auf die angegebene Einstellung für das aktuelle Benutzerkonto. Aber nicht in der aktuellen PowerShell-Sitzung, sondern erst in der nächsten. Ganz witzig ist auch, dass sich unmittelbar nach der Ausführung des Befehls sich das Datum im SysTray-Feld der Taskleiste an die neue Kultur anpasst. Praktisch ohne Verzögerung, was für einen PowerShell schon etwas ungewöhnlich ist.

Fazit: Das Set-Culture-Cmdlet funktioniert auch ohne irgendwelche Klimmzüge, aber nicht so wie man es vielleicht erwarten würde.

PowerShell-Know how als Teil der Windows-Problemlöser

Auf diesen Blog-Eintrag wollte ich schon seit Jahren hinweisen, da ich ihn sehr gut finde:

https://chentiangemalc.wordpress.com/2011/05/08/windows-7-can-teach-you-powershellinbuilt-wealth-of-scripts/

Der Autor hat sich die Mühe gemacht, die vielen PowerShell-Skripte, die unter der Haube der zahlreichen Diagnosehilfen von Windows ihren Dienst verrichten, aufzulisten und ihren Inhalt zu untersuchen.

Auch wenn, wie es PowerShell-Experte Jeffrey Hicks es in einem Kommentar korrekt anmerkt, einige (oder eher viele?) der vorgestellten Techniken veraltet bzw. nicht ganz optimal sind, zum Lernen der verschiedenen PowerShell-Techniken sind die Skripts dann doch gut geeignet.

Auf alle Fälle ist es erstaunlich, welche Fülle an PowerShell-Know how abseits der offiziellen Module bereits bei Windows 7 im Windows-Verzeichnis dabei war.