04.06.2009, 18:03 Uhr

Erweiterungen für die PowerShell

Die PowerShell ist auf Erfolgskurs. Seit ihrer offiziellen Freigabe im Herbst 2006 wurde sie als kostenlose Erweiterung für Windows XP, Windows Server 2003/2008 und Vista mehrere Hundertausend Mal heruntergeladen und dürfte bei vielen Administratoren inzwischen ein geschätzter Helfer zur Lösung kleinerer wie grösserer Herausforderungen des administrativen Alltags sein.
Einer der Pluspunkte der PowerShell ist ihre Erweiterbarkeit. Cmdlets und Provider lassen sich jederzeit hinzufügen und damit die "Reichweite" der Shell deutlich erweitern. Eines der beliebtesten "Add-Ons" sind die PowerShell Community Extensions, ein Open Source-Projekt der sehr aktiven "PowerShell-Community", das ca. 80 mehr oder weniger nützliche Cmdlets, sowie einen Provider für das Active Directory hinzufügt [1]. Inzwischen sind knapp ein Dutzend solcher Erweiterungen verfügbar, die meisten kostenlos oder relativ preiswert, was ein Beleg dafür sein könnte, dass sich Geschäftsmodelle für "Utilities" heutzutage nur schwer etablieren lassen, wenn man nicht ein Apfel im Logo trägt.Selber machen ist die DeviseWer kein passendes Cmdlet oder keinen passenden Provider findet, kann selber aktiv werden. Voraussetzung dafür sind Visual Studio 2005/2008, eine frei verfügbare (und optionale) Vorlage sowie gute Kenntnisse in C# oder VB.NET und im Umgang mit den Klassen des .NET Frameworks. Allzu schwer ist die Aufgabe nicht, doch der Zeitaufwand, um zu einem fehlerfreien Cmdlet oder gar Provider zu kommen darf wie immer nicht unterschätzt werden. Die Hintergründe, die bei der Umsetzung eine Rolle spielen, sind im PowerShell SDK ausführlich beschrieben - hier gibt es auch Beispiele für einzelne Cmdlets und Provider. Beide Erweiterungen werden stets in einem Snapin (eine Assembly-Bibliothek, die folglich die Erweiterung .Dll trägt) verpackt, das vor seinem ersten Einsatz in die Registry eingetragen werden muss (was Adminrechte voraussetzt), damit es per Add-PsSnapin-Cmdlet geladen werden kann. Voraussetzung für die Umsetzung ist das SDK, das Teil des großen Windows SDK ist, allerdings nicht. Auch die Professional Edition von Visual Studio wird nicht benötigt, die kostenlosen Express Editionen genügen.Ein "Hallo, Welt"-CmdletWie einfach die Umsetzung eines Cmdlets sein kann, soll ein kleines Beispiel deutlich machen, das zu einem Get-Time-Cmdlet führt, durch das die aktuelle Uhrzeit als Zeichenkette in die Pipeline gelegt wird (sie kann mit dem Cmdlet aber nicht geändert werden). Kaum zu glauben, aber ein solches Cmdlet ist bei der PowerShell nicht vorhanden (aus dem Autor nicht bekannten Gründen hat der DisplayHint-Parameter des Get-Date-Cmdlets in einer Zeichenkette keine Wirkung, so dass es bereits ein "Uhrzeit: $(""{0:HH:mm}"" -f (get-date))" sein muss, damit die Uhrzeit in einer Zeichenkette erscheint). Der erste Schritt besteht darin, die von Alan Bradley, einem der zahlreichen PowerShell-Enthusiasten, zusammenstellte Visual Studio-Vorlage zu installieren, so dass in VS 2005/2008 entsprechende Vorlagen zur Verfügung stehen [2]. In einem Blog-Eintrag beschreibt Alan ausführlich, wie die Vorlage eingesetzt wird [3]. Anschließend sollte nach dem Start der IDE unter "Windows PowerShell" auch bei einem deutschsprachigen Visual Studio eine neue Vorlage "Windows PowerShell" zur Verfügung stehen, die die Grundlage für ein PowerShell-Snapin darstellt. Auch wenn diese Vorlagen nicht zwingend erforderlich sind, geht es mit ihnen gerade am Anfang deutlich einfacher. Technisch basiert ein Cmdlet auf einer Klasse, die sich entweder von der Klasse Cmdlet oder der Klasse PsCmdlet ableitet, die sich beide im Namespace System.Management.Automation befinden. Für einfache Cmdlets ist die Cmdlet-Klasse als Basisklasse die etwas bessere Wahl, da bei PsCmdlet stärkere Abhängigkeiten zum jeweiligen PowerShell-Host bestehen, die in der Regel nicht benötigt werden.Abbildung 1: Auch für PowerShell-Cmdlets gibt es eines Visual Studio-VorlageDas Projekt soll "GetTimeSnapin" heißen und wird nach dem Start gleich gespeichert. Die Snapin-Klasse enthält keinen "aktiven" Code, ihre Properties legen lediglich die Eckdaten des Snapins fest. Hier kommt es vor allem auf die Name-Property an, denn sie legt den Namen des Snapins fest, der später bei Add-PsSnapin angegeben werden muss und in diesem Fall ebenfalls "GetTimeSnapin" lautet. Das unscheinbare RunInstaller(true)-Attribut sorgt dafür, dass daraus eine Installer-Klasse wird, so dass die Assembly später mit Installutil.exe registriert werden kann. Da das Projekt am Anfang noch keine Cmdlet-Vorlage enthält, wird diese über Projekt|Neues Element hinzufügen hinzugefügt - sie erhält den Namen "GetTime", wenngleich dieser keine direkte Rolle spielt. Auch die Cmdlet-Klasse ist mit einem Attribut ausgestattet. Es legt unter anderem den Namen des Cmdlets fest, der stets nach dem Schema Verb-Hauptwort gebildet wird. Da das Cmdlet "Get-Time" lauten soll, sieht die Definition der Klasse wie folgt aus: [Cmdlet(VerbsCommon.Get, "Time")] public class GetTime : Cmdlet Die über VerbsCommon angebotenen Verben sind lediglich Vorschläge, auch der Verbname kann frei gewählt werden. Pipeline-ProduktionDa ein Cmdlet seine Ausgaben grundsätzlich in die Pipeline legt, muss diese Ausgabe auch irgendwo erfolgen. Dieses Irgendwo ist im Allgemeinen die ProcessRecord-Methode, in der die Pipeline-Ausgabe über einen Aufruf von WriteObject erfolgt:protected override void ProcessRecord() {WriteObject(DateTime.Now.ToString("HH:mm:ss"));}Damit ist das Cmdlet im Prinzip bereits fertig. Im Prinzip deswegen, weil die Assembly-Bibliothek per Installutil.exe (einem Kommandozeilentool der .NET-Laufzeit) nach jeder Änderung erneut registriert werden muss. Dies kann z.B. über eine Postbuild-Aktion erledigt werden:%systemroot%\Microsoft.NET\Framework\v2.0.50727\installutil.exe "$(TargetPath)" Wird das Projekt fehlerfrei erstellt, wird das Snapin anschließend registriert, so dass es in der PowerShell (die dazu nicht geschlossen werden muss) über ein Get-PsSnapin -Registered aufgelistet und über ein Add-PsSnapin GetTimeSnapin hinzugefügt wird. Ging alles gut, sollte sich das Get-Time-Cmdlet wie jedes andere Cmdlet aufrufen lassen. Debuggen in Visual StudioDas Debuggen eines Cmdlets kann natürlich direkt in Visual Studio erfolgen. Dazu muss lediglich erreicht werden, dass mit dem Start des Projekts per [F5] die PowerShell gestartet und das Snapin geladen wird. Ersteres wird erreicht, in dem in den Projekteigenschaften im Register "Debuggen" bei "Externes Programm starten" der vollständige Pfad von PowerShell.exe eingetragen wird. Bei "Befehlszeilenargumente" wird ein PowerShell-Skript angegeben, wobei das Argument einen bestimmten Aufbau besitzen muss: -NoExit -C . \InstallSnapin.ps1' . steht für das Verzeichnis, in dem sich die Skriptdatei InstallSnapin.ps1 befindet (Listing 1). Der Pfad wird in einfache Apostrophe gesetzt und dem Ganzen gehen ein Punkt und ein Leerzeichen voraus, damit das Skript "dot sourced" aufgerufen wird. Der Schalter -C übergibt die Befehlszeile an die PowerShell und der Schalter -NoExit sorgt dafür, dass die PowerShell danach nicht beendet wird. Zum Schluss wird bei Arbeitsverzeichnis erneut der Verzeichnispfad eingestellt, in dem sich die Skriptdatei befindet. Wird ein Haltepunkt z.B. auf die WriteObject-Methode gesetzt und das Projekt gestartet, werden nacheinander das Projekt erstellt, die PowerShell gestartet, die Skriptdatei InstallSnapin.ps1 ausgeführt, das Snapin registriert, geladen und das Cmdlet geladen, so dass der Haltepunkt erreicht wird und die Ausführung des Cmdlets im Debugger fortgesetzt werden kann (wobei in diesem konkreten Fall nicht mehr allzu viel passiert). Da das Skript in Listing 1 davon ausgeht, dass sich die Snapin-Dll im Benutzerverzeichnis befindet, sollte als Postbuild-Aktion der folgende Befehl ausgeführt werden: copy "$(TargetPath)" %userprofile%Installutil.exe muss an dieser Stelle nicht ausgeführt werden, da dies im Rahmen des PowerShell-Skripts geschieht.function Install-Snapin{param([string]$DllPath=$(throw "Bitte DLL-Pfad eingeben"), [string]$SnapinName) set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil.exe if(test-path -path $DllPath){installutil /u $Dllpathinstallutil $Dllpathwrite-host "Snapin wurde installiert."if ($SnapinName -ne "") { add-pssnapin $SnapinName write-host "... und hinzugefügt." }}else { write-error "Datei gibt es nicht" } }Write-Host "Snapin wird hinzugefügt - weiter mit Taste"Read-HostInstall-Snapin GetTimeSnapin.dll GetTimeSnapin Listing 1: Das PowerShell-Skript registriert und lädt das Snapin, so dass das Cmdlet anschließend zur Verfügung steht Jedes Cmdlet sollte eine Hilfe besitzen, auch wenn es nur die Uhrzeit anzeigt. PowerShell-Hilfedateien liegen in einem speziellen XML-Format vor, das MAML heißt. Am einfachsten wird die Hilfedatei mit einem kleinen Tool, dem Cmdlet Help-Editor erstellt [4].Advanced Functions als AlternativeDa nur wenige Administratoren über die für das Erstellen eigener Cmdlets erforderlichen Programmierkenntnisse und vor allem über die erforderliche Zeit verfügen dürften, gibt es bei der kommenden Version 2.0 der PowerShell, die voraussichtlich mit Windows 7 im Oktober dieses Jahres ihr Debut geben wird, eine Alternative in Gestalt der "Advanved Functions". Eine Advanced Funktion ist ein Skript, das die Funktionalität eines Cmdlets abbildet. Anders als bei einer regulären Funktion können z.B. Parameterdetails festgelegt und eine Hilfe eingebaut werden.Links[1] http://www.codeplex.com/Pscx[2] http://psvs2008.codeplex.com/[3] http://www.gangleri.net/2009/04/21/BuildingPowerShellCmdletsWithVisualStudio2008.aspx [4] http://www.wassimfayed.com/PowerShell/CmdletHelpEditor.zip [5] http://www.powershell-ag.de Peter Monadiemi


Das könnte Sie auch interessieren