Eigenbau
Die Windows PowerShell besitzt einen vorgegebenen Satz von Commandlets, aber der Administrator kann die vorhandenen um zusätzliche ergänzen. Eigene Commandlets sind entweder als Skriptdateien oder in .Net-DLLs implementiert.
Im dritten Teil des PowerShell-Tutorials geht es um die Techniken die immanenten Commandlets durch eigene zu ergänzen. Die einfache Methode, eigene Commandlets zu erstellen und damit die Fähigkeiten der Shell zu erweitern, sind in der PowerShell-Sprache geschriebene Funktionen, die der Entwickler in Skriptdateien verpackt. Solche Befehle heißen funktionsbasierte Commandlets und bestehen aus folgenden Teilen:
- Mindestens eine in der PowerShell-Sprache geschriebene Funktion, deren Name wie ein Commandlet aus Verb und Substantiv aufgebaut ist,
- eine Liste von Parametern,
- ein Unterblock mit dem Namen begin, den die PowerShell einmalig zu Beginn der Verarbeitung ausführt,
- ein Unterblock process, der für jedes Objekt in der Pipeline ausgeführt wird sowie
- ein Unterblock end, der einmalig zum Ende der Verarbeitung läuft.
Beachten muss man, dass die PowerShell process auch dann mindestens einmal aufruft, wenn die Pipeline leer ist.
Eigene Commandlets für LDAP-Server
Listing 1 zeigt die Implementierung der funktionsbasierten Commandlets Get-LDAPObject und Get-LDAPChildren, deren Aufgabe es ist, Einträge aus LDAP-Servern auszulesen. Ersteres prüft, ob das aktuelle Objekt $_ eine Zeichenkette ist. Falls ja, erzeugt das Commandlet eine zugehörige Instanz der .Net-Klasse System.DirectoryServices.DirectoryEntry und legt es in die Pipeline als Ausgabeobjekt. Die Zeichenkette kann der Administrator wahlweise als Parameter
Get-LDAPObject
"LDAP://E02/ou=Dozenten,dc=
it-visions,dc=local"
oder durch die Pipeline
"LDAP://E02/ou=Dozenten,dc=
it-visions,dc=local"
| Get-LDAPObject
übergeben.
Listing 1: Funktionsbasierte Commandlets
# Get single LDAP object
function Get-LDAPObject
{
param([string[]]$LDAPPath)
begin {}
process
{
if ($_)
{
if ($_ -is [string])
{
new-object system.directoryservices.directoryEntry($_)
}
else
{
throw "input must be [string]."
}
}
}
end
{
if ($LDAPPath)
{
foreach ($Path in $LDAPPath)
{
new-object system.directoryservices.directoryEntry($Path)
}
}
}
}
# Hilfsroutine
function getContainer([string] $path)
{
$con = new-object system.directoryservices.directoryEntry($path)
$con.PSBase.Children
}
# Get content of an LDAP container
function Get-LDAPChildren
{
param([string[]]$LDAPPath)
begin {}
process
{
if ($_)
{
if ($_ -is [string])
{
getContainer($_)
}
elseif ($_ -is [System.DirectoryServices.DirectoryEntry])
{
getContainer($_.PSBase.Path)
}
else
{
throw "Pipeline input must be [string] or
[System.DirectoryServices.DirectoryEntry]."
}
}
}
end
{
if ($LDAPPath)
{
foreach ($Path in $LDAPPath)
{
getContainer($Path)
}
}
}
}
# Define aliases for commandlet functions
Set-Alias LDO Get-LDAPObject
Set-Alias LDC Get-LDAPChildren
# Confirm installation
"Function-based commandlets for LDAP successfully installed!"
Get-LDAPChildren zum Auflisten des Inhalts eines Containers ist etwas aufwendiger, weil hier die Eingabe wahlweise ein Pfad oder ein Objekt des Typs System.DirectoryServices.DirectoryEntry sein kann. Folgende vier Syntaxformen sind demzufolge erlaubt:
Get-LDAPChildren
"LDAP://E02/ou=Dozenten,dc =
it-visions,dc=local"
______________________________
"LDAP://E02/ou=Dozenten,dc=
it-visions,dc=local"
| Get-LDAPChildren
Get-LDAPObject
______________________________
"LDAP://E02/ou=Dozenten,dc=
it-visions,dc=local"
| Get-LDAPChildren
______________________________
"LDAP://E02/ou=Dozenten,dc=
it-visions,dc=local"
| Get-LDAPObject
| Get-LDAPChildren
Die Rückgabe sind wieder Instanzen von System.DirectoryServices.DirectoryEntry, die andere Commandlets verarbeiten können. In den Downloads auf dem iX-FTP-Server ist eine ausführliche Version von Listing 1 vorhanden, in dem die Commandlets Add-LDAPObject zum Anlegen eines Verzeichnisobjekts und Remove-LDAPObject zum Löschen eines Verzeichnisobjekts realisiert sind.
Der Code aus Listing 1 ist in einer PowerShell-Skriptdatei mit der Dateinamenserweiterung ps1 (s. Teil 2 des Tutorials in iX 9/07) zu speichern. Diese Datei muss der Anwender in die Shell einbinden. Das geschieht mit dem Operator „.“ und dem vollständigen Pfad zur Skriptdatei (das Verfahren nennen die PowerShell-Entwickler „dot sourcing“), beispielsweise mit:
. H:\ix\LDAP_Commandlets.ps1
Der letzte Befehl in der Skriptdatei sorgt dafür, dass der Anwender einen Bestätigungstext erhält.
Den obigen Befehl legt man üblicherweise in der profile.ps1-Datei ab, damit er beim Start der Shell integriert wird. Wichtig ist, dass man das Skript nach jeder Quelltextänderung neu in die PowerShell einbinden muss. Danach sind alle in der Skriptdatei enthaltenen funktionsbasierten Commandlets wie normale Commandlets über die Konvention Verb-Substantiv (s. Teil 1, iX 8/07) verwendbar. Die Aliasdefinitionen am Ende der Skriptdatei mit Set-Alias ermöglichen, dass die Abkürzungen LDC und LDO anstelle der Commandlet-Namen zur Verfügung stehen.
Neue Befehle in beliebigen .Net-Sprachen erstellen
Statt der auf PowerShell-Skriptfunktionen basierten Commandlets kann man sie in einer beliebigen .Net-Programmierspache erstellen und in kompilierter Form als DLL (alias „Snap-In“) verbreiten. Dazu benötigt der Entwickler aber Grundkenntnisse des .Net Framework und einer der rund 70 .Net-basierten Programmiersprachen wie C#, VB.Net oder C++/CLI.
Zum Erstellen eines Commandlets sollte der Entwickler eine Variante von Visual Studio 2005 oder 2008 verwenden. Es reicht dabei, wenn man eine der kostenfreien Expressvarianten (Visual C# Express, Visual C++ Express oder Visual Basic Express) nutzt. Grundsätzlich ist eine Erstellung von Commandlets auch mit einem einfachen Texteditor und den im .Net Framework mitgelieferten Kommandozeilen-Compilern möglich. Dies ist jedoch deutlich mühsamer als die Bearbeitung im Visual Studio.
Den vollständigen Artikel finden Sie in der aktuellen Printausgabe.
Tutorialinhalt
- Teil I: Commandlets, Pipelines und Navigation
- Teil II: Skripte und WMI-Einbindung
- Teil III: Erweiterung durch eigene Commandlets
- Teil IV: Datenzugriff auf Datenbanken, Text- und XML-Dateien