zurückInhaltvorwärts Perl Einführung: Das Win32API::Net Modul

12.4. Das Win32API::Net Modul

12.4.1 Einleitung

Das Modul Win32API::Net gehört zum Standardumfang der letzten paar ActivePerl Builds und braucht deswegen wahrscheinlich nicht nachinstalliert zu werden. Falls dies doch der Fall sein sollte, kann man dies entweder über den PPM (Perl Paket Manager) oder über WWW.Activestate.com nachgeholt werden, wo man ein entsprechendes Zip-Archiv downloaden kann, das auch eine Anleitung enthält, wie man das Modul installiert.

Das Modul Win32API::Net ermöglicht über Perl den Zugriff auf Funktionen, die die Windows NT Lanmanager API zu Verfügung stellt, um User und Gruppen zu verwalten. Wir werden hier aus Platzgründen nicht alle Funktionen besprechen. Dies ist auch gar nicht notwendig, da man sich hoffentlich nach Lektüre dieses Kapitels die übrigen Funktionen leicht selbst erarbeiten kann.

Welche Funktionen stellt das Modul also bereit? Es handelt sich z.B. um Funktionen, mit Hilfe dessen man

Für eine erschöpfende Aufzählung aller Funktionen sollten man sich die ausgezeichnete Dokumentation anschauen, die mit dem Modul installiert wird. Die Dokumentation enthält auch Beispiele zu einigen Funktionen.

Die Struktur dieses Kapitels orientiert sich an Aufgaben, die immer mal wieder auftauchen, wenn man mit Windows NT Server oder Workstation 4.0 arbeitet. Es sollte nicht unerwähnt bleiben, daß viele der Funktionen, die wir hier besprechen, nur nutzbar sind, wenn sie unter einem Konto mit entsprechenden Rechten laufen.

12.4.2 Welche bzw. Wieviele User existieren auf Server XY?

Diese Frage taucht immer wieder mal auf und sind oft auch der Ausgangspunkt für weitere administrative oder statistische Aufgaben. Stelle Dir vor, Du hast einen Server mit X Usern, die kein Homeverzeichnis zugewiesen bekommen haben. Normalerweise kannst Du, wenn Du den User anlegst, sofort ein Userverzeichnis erstellen, aber wenn Du das aus irgendwelchen Gründen nicht getan hast (oder gerade von einem anderen Server migrierst), stehst Du vor einem Problem. Es sei denn Du gehörst zu den Leuten, die gerne manuell 1000 Userkonten nachbessern. Stelle Dir dann noch vor Du müßtest - nachdem Du die Userverzeichnisse erzeugt hast - noch die entsprechenden Rechte auf die Verzeichnisse vergeben und diese dann noch freigeben. "Klar", hören wir jetzt einige sagen, "da nehmen wir ein paar Tools aus dem Ressource Kit". OK, aber bedenkt folgendes:

  1. Das Ressource Kit kostet Geld (kann man meistens ignorieren, die Firma zahlt ;-))
  2. Mit einem Perl Skript erreicht man maximale Flexibilität und ist nicht von irgendeinem Tool abhängig, das oft nur einen Teil der gewünschten Funktionalität bietet
  3. Bestehende Skripts können modifiziert und wechselnden Aufgaben, Umgebungen angepaßt werden, Tools nicht.
  4. Man erspart sich die Suche nach einem passenden Tool für "seine Aufgabe"
  5. Selbst programmieren ist männlicher ;-)

Die Funktion, die man benötigt, um Konten aus der Benutzerdatenbank eines Servers zu lesen, heißt

UserEnum($lServer, \@lArray, $lFilter)

Mit dieser Funktion kann man all die Konten aus Server herauslesen, die einem bestimmten Kriterium genügen. Die Funktion benötigt drei Argumente. Das erste, $lServer, spezifiziert den Server oder die Workstation, auf dessen Benuterdatenbank man zugreifen möchte. Das kann eine lokale Machine sein, aber auch ein entfernter Server (auf dem natürlich NICHT Perl installiert werden muß).

Der nächste Parameter, \@lArray, ist eine Referenz auf ein Array. Das eigentliche Array enthält nach dem Aufruf von UserEnum() die Namen all derjenigen User, die dem Kriterium $lFilter genügen. Was ist eine Referenz? Kurz ausgedrückt, eine Referenz R ist eine Variable, die die Speicheraddresse einer anderen Variablen V enthält und Informationen darüber von welchem Typ die Variable V ist. In Perl erzeugt man eine Referenz auf eine Variable, indem man der Variablen ein \ voranstellt. Man muß also unterscheiden zwischen dem eigentlichen Array - dargestellt durch @array - und einer Referenz auf dieses Array - dargestellt durch \@array. Hierüber braucht man sich im folgenden keine weiteren Gedanken zu machen, aber man sollte den sehr wichtigen Unterschied im Hinterkopf behalten und bei Gelegenheit, die entsprechenden Kapitel im Camel Book nachlesen. Die Funktion bekommt also, umgangssprachlich gesprochen, gesagt, wo sie das Array @lArray im Speicher findet, in das sie die Usernamen ablegen soll. Dieses "wo" ist die Adresse des arrays @lArray im Speicher eures Computers. Diese Adresse wird durch die Referenzvariable \@lArray übermittelt, die die Speicheradresse von @lArray enthält.

Der nächste Parameter, $lFilter, ist einer von fünf möglichen Flags (konstante Größen mit einer bestimmten Bedeutung). Dieser Flag spezifiziert, welchen Typ von Konto man erfassen möchste. Es gibt, wie Ihr sicherlich wißt, verschiedene Typen von Windows NT Konten: Zum einen normale Benutzerkonten und zum anderen Spezialkonnten, wie z.B. die Computerkonten, die man erzeugt, indem man eine NT Workstation einer NT Domäne hinzufügt. Diese Konten sind auch nicht im Benutzermanager sichtbar. Man kann sie aber dennoch mit dieser Funktion auslesen, wenn man möchte. Wir verweisen an dieser Stelle auf die Dokumentation und verwenden hier durchgängig als Flag FILTER_NORMAL_ACCOUNT, da wir nur mit normalen Benutzerkonten arbeiten.

Also, laßt uns sofort loslegen. Das folgende als Subroutine geschriebene Skript liest alle normalen Benutzerkonten von einem Server aus. Weil es so schön ist, werden zur Wiederholung auch alle User sofort in eine Datei geschrieben. Dies erledigt die Subroutine UserFileDump(). Ein ähnliches Beispiel findet sich in der Dokumentation zu dem Modul. Vor der Ausführung des Skripts sollte man natürlich vorher die Zeilennummern entfernen. Ja, ja, ich sage es nur um ganz sicher zu gehen.

1.	use Win32API::Net;

2.	$gServer="\\\\babcom";

3.	userEnumerate($gServer);
 
4.	sub userEnumerate {
5.	    my($lServer) = @_;
6.    	    my(@lArray, $lZaehler, $lUser, $lFilter);

7.	    $lFilter=Win32API::Net::FILTER_NORMAL_ACCOUNT();
8.	    $lZaehler=0;
      
9.	    print "Lese Usernamen von Server $lServer...\n";
     
10.	    Win32API::Net::UserEnum($lServer, \@lArray, $lFilter) || die "Kann Informationen nicht von Server $lServer lesen.";
      
11.	    foreach $lUser (@lArray) {
12.	      $lZaehler++;
13.	      &UserFileDump($lUser);
14.	    }
      
15.	    print "$lZaehler User gefunden.\n";
16.	 } # Ende  userEnumerate()

17.	 sub UserFileDump {
18.	    my($lUser) = @_;
19.     my $lElement,$lGroup, $lDumpFile;

20.     $lDumpFile="user.txt";

21.	    print "schreibe User $lUser in Datei $lDumpFile... ";
  	 
22.     open (USRDMP,">>$lDumpFile") || die "Kann Datei $lDumpFile nicht oeffnen.";
23.     print USRDMP "$lUser\n" || die "Kann nicht in Datei $lDumpFile schreiben.;
24.     close(USRDMP) || die "Kann Datei $lDumpFile nicht schliessen";

25.     print "fertig!\n";
26.  } # Ende UserFileDump()

Bei Ausführung sollte man in etwa folgendes sehen (Ja, der Henning hat schon eine interessante Benutzerdatenbank auf seinem Testserver):

Lese Usernamen von Server \\babcom...
schreibe User Administrator in Datei user.txt... fertig!
schreibe User ftp in Datei user.txt... fertig!
schreibe User Gast in Datei user.txt... fertig!
schreibe User IUSR_BABCOM in Datei user.txt... fertig!
4 User gefunden.

Natürlich bedarf das Skript keiner weiteren Erläuterung, da alle Programme trivialerweise selbstdokumentierend sind ;-). Ich erläutere das Skript nun zeilenweise, was zwar nicht schöner Stil ist, aber hoffentlich zur Übersichtlichkeit beiträgt.

Zeile 1:
Der use Befehl der das Modul Win32API::Net in das Skript einbindet. Diese Zeile ist unbedingt notwendig, da man sonst nicht auf die entsprechenden Funktionen des Moduls zugreifen kann.

Zeile 2:
Hier sollte man zwei Dinge beachten, wenn man sich in ähnliche Situationen Frustrationen ersparen will. Erstens, die UserEnum() Funktion des Moduls Win32API::Net möchte den Servernamen gerne in UNC Schreibweise, also mit zwei vorangestellten \. Zweitens, sollte man bedenken, daß \ den Perl Interpreter anweist, das nachfolgende Zeichen als Sonderzeichen zu interpretieren. Möchte man nur ein \ darstellen, schreibt man \\. Da wir hier zwei \ als Bestandteil des UNC Namens haben, schreiben wir also \\\\, um \\ darzustellen. Man kann viel einfacher den gleichen Effekt mit $gServer='\\babcom' statt durch $gServer="\\\\babcom" erzielen. Man beachte die einfachen Anführungsstriche im ersten Ausdruck.

Zeile 3:
Die Subroutine UserEnumerate() wird mit $gServer als Argument aufgerufen. Das g im Variablennamen steht übrigens für global, was anzeigen soll, daß es sich um eine globale Variable handelt.

Zeilen 5,6:
Die Variablen $lServer, @lArray, $lZaehler, $lUser und $lFilter werden durch die Funktion my() als lokale Variable deklariert, was ich noch einmal mit dem l in jedem Variablenname andeute. Man sollte sich angewöhnen, in Subroutinen benutzte Variablen durch my() zu kapseln und nicht alle Variablen als global deklarieren (der default Zustand). Auch sollte man globale Variablen nicht in Subroutinen manipulieren. Es schleichen sich sonst oft schwer zu findende Programmierfehler ein. Und was ist mit dem @_? Das is nicht schwer... Wir rufen unsere Funktion UserFileDump() mit einem Parameter auf. Die Parameter einer Subroutine werden von Perl in einem speziellen Array @_ abgespeichert: @_[0] enthält das erste an die Subroutine übergebene Element, @_[1] das zweite, etc. In unserem Fall enthält @_ nur ein Element, da wir ja nur ein Element übergeben. Mit my($lServer) = @_ wird $lServer als lokale Variable definiert und ihr wird @_[0] als Wert übergeben. $lServer enthält also jetzt den übergebenen Parameter. Schaut euch noch einmal das Kapitel 11.2ff an.

Zeile 7:
Diese Zeile sieht ein bißchen gefährlich aus. Sie ist es aber nicht. Es handelt sich hier um eine Variablendeklaration: Im Prinzip wird $lFilter auf den konstanten Wert FILTER_NORMAL_ACCOUNT gesetzt. Diesen Wert (man spricht von Flag) übergeben wir weiter unten der Funktion UserEnum(), damit sie weiß, daß nur normale Userkonten ausgelesen werden sollten.
Aber warum diese Schreibweise? Wenn man den Modulnamen Win32API::Net betrachtet, fällt sofort das :: auf. Dieses :: bedeutet, sehr schwammig formuliert, daß wir ein Modul Win32API haben, daß ein "Untermodul" Net enthält (etwas technischer kann man von einem Namensraum sprechen, der durch Win32API definiert wird, der wiederum einen weiteren Namensraum Net enthält). Dieses "Enthaltensein-in" wird durch :: zum Ausdruck gebracht. Will man auf Funktionen innerhalb des "Untermoduls" Net zugreifen, also auf Funktionen, die im "Net Untermodul enthalten sind", so benutzt man wieder das :: und schreibt ausführlich Win32API::Net::FILTER_NORMAL_ACCOUNT(), um sie aufzurufen.
"Einen Moment", höre ich einige sagen, "FILTER_NORMAL_ACCOUNT ist doch gar keine Funktion, sondern eine Konstante, also kann ich auch nicht das () dem FILTER_NORMAL_ACCOUNT nachstellen, das macht man doch nur bei Funktionen, die man aufrufen möchte. Also ist Win32API::Net::FILTER_NORMAL_ACCOUNT() doch eigentlich ein Aufruf der Funktion FILTER_NORMAL_ACCOUNT(), die im Net 'Untermodul' des Moduls Win32API enthalten ist.".
Im Prinzip stimmt das auch... FILTER_NORMAL_ACCOUNT ist aber von dem Programmierer des Moduls Win32API::Net als Funktion implementiert worden. Ruft man diese Funktion wie im Skript geschrieben durch Win32API::Net::FILTER_NORMAL_ACCOUNT() auf, hat dies zur Folge, daß die Variable $lFilter auf den Konstanten Wert FILTER_NORMAL_ACCOUNT gesetzt wird. Mann hat es also mit einer Funktion FILTER_NORMAL_ACCOUNT() zu tun, die als Wert die Konstante FILTER_NORMAL_ACCOUNT zurückgibt. Diese Konstante FILTER_NORMAL_ACCOUNT wird $lFilter als Wert zugewiesen.
Ich empfehle dringend für genaue Erklärungen das Camel Buch zu konsultieren. Das ist zwar nicht immer angenehm aber lehrreich. Im Prinzip reichen die Bemerkungen aus, um sich einen intuitiven Begriff zu bilden, mit dem man auch ganz gut arbeiten kann. Dennoch sollte man danach streben, den Dingen auf den Grund zu gehen.

Zeile 8:
$lZaehler ist nur eine Variable, die die Anzahl der ausgelesenen Userkonten enthält und hier mit 0 initialisiert wird.

Zeile 10:
Hier wird der Server $lServer via RPC kontaktiert und alle Benuterkonten von Typ $lFilter (also normale Userkonten) werden in das Array @lArray abgelegt. Falls die Anfrage nicht Erfolg hat, wird mittels der Funktion die() eine Fehlermeldung ausgegeben und das Skript unterbrochen. Das || steht für ein logisches ODER. Also: Versuche die Usernamen vom Server zu lesen ODER breche das Skript ab und gebe eine Fehlermeldung aus. Man sollte sich früh angewöhnen, mögliche Fehler abzufangen. Ich empfehle das Skript einmal mit falsche Servernamen zu füttern und die Fehlermeldungen zu beobachten. Schaut euch auch die Bemerkungen weiter oben zur Funktion UserEnum() an.

Zeilen 11-13:
Wenn Ihr euch bis in dieses Kapitel vorgekämpft habt, sollten diese Zeilen eigentlich nicht schwierig sein:

foreach $lUser (@lArray) {
	      $lZaehler++;
	      &UserFileDump($lUser);
	    }

$lUser durchläuft in dieser foreach Schleife alle Elemente von @lArray ab, hierbei wird der Zähler $lZaehler immer um eins höher gesetzt (man spricht von inkrementieren). Mann könnte statt $lZaehler++ auch $lZaehler=$lZaehler+1 schreiben, aber die erste Variante ist viel eleganter. Ist das letzte Element durchlaufen, enthält $lZaehler die Anzahl der Elemente von @lArray (dies geht eleganter, da Perl eine Funktion zur Verfügung stellt, die die Anzahl der Elemente eines Arrays angibt). Ferner wird für jeden Usernamen die Subroutine UserFileDump() aufgerufen. Wir machen also jetzt einen kleinen Sprung nach...

Zeilen 17-21:
Die Subroutine UserFileDump() macht nichts anderes als die Usernamen, die Übergeben werden, in die Datei user.txt zu schreiben. Die Zeilen hier sind nur die Definitionen bzw. Initialisierung der lokalen Variabeln. Übrigens, ich habe keinen Pfad angegeben, wo wird die Datei erzeugt? Gibt man keinen Pfad an, ist der default Pfad der current working directory, d.h. die Datei wird im selben Verzeichnis erzeugt, in dem sich das Skript befindet. Zeile 21 gibt nur eine kleine Statusmeldung aus.

Zeilen 22-25:
Schaut euch noch einmal das Kapitel 10 an. Die folgende Zeilen sind wirklich leicht: Die Datei user.txt wird geöffnet bzw. vorher erzeugt falls nicht vorhanden. Dann wird der Username in die Datei geschrieben und die Datei wieder geschlossen. Beachtet auch hier die Verwendung von die(). Nach jedem Schreibvorgang in die Datei wird "fertig" ausgegeben. Anschließend geht es zurück zur Subroutine UserEnumerate() ...

Zeilen 15:
Die Anzahl der Usernamen, die aus der Benutzerdatenbank des Servers gelesen worden sind, wird ausgegeben. Das Skript endet.

Wow, das wars. Dies ganzen Erklärungen machen mich immer ganz fertig :-)

12.4.3 User und Gruppen mit einem Perl Skript erzeugen

Hier zeigen wir euch, wie man einen User auf einer NT Maschine (remote oder lokal) anlegt (Gruppen anlegen kann man mit demselben Modul, das besprechen wir ein anderes mal). Die Lösung, die wir hier vorstellen, läßt sich dann leicht als Grundlage für das Automatisieren verwenden, wenn man große Mengen von Usern anlegen will. Wir haben nicht viel Zeit (ist schon spät) also laßt uns sofort in medias res gehen. Ach ja, es schadet nie bei Microsoft auf die MSDN Seiten zu schauen, um Hinweise für die Win32 API Funktionen zu bekommen, auf die dieses Modul letztendlich beruht. Die Doku zu dem Modul ist aber gut (falls man Englisch kann, har har).

Wie Ihr sicherlich erraten habt, bindet man als erstes das benötigte Modul ein, also

use Win32API::Net;

als nächstes braucht man die entsprechende Methode aus diesem Modul, sie lautet UserAdd() und wird folgendermaßen aufgerufen. Eine Sache gleich vorweg: Schaut Euch auf jeden Fall auch die Dokumentation zu dem Modul an (ich wiederhole mich). Wenn Ihr verstanden hat, was die einzelnen Parameter von UserAdd() bedeuten, ist alles andere einfach.

Win32API::Net::UserAdd($Server, $Level, \%UserHash, $Error);

$Server, der am schwersten zu verstehende Parameter, enthält den Namen des Servers, auf dem der User angelegt werden soll. Dies wird in der Regel ein PDC sein, man kann aber auch hier den Namen einer Workstation oder eines Member Servers (also ein alleinstehender NT Server, kein Domänen - Controller) angeben. Ein kleiner Tipp: Prüft vorweg (geht auch mit Perl), ob der PDC momentat auch wirklich PDC ist. Vielleicht hat ein freundlicher Kollege aus irgendwelchen Gründen geraden einen BDC zum PDC gemacht, und der PDC ist gar nicht mehr PDC. Dann läuft das Skript vor die Wand (wenn man Domänen-User anlegen will) und man wundert sich (der Kollege auch, wenn man ohne Worte sofort losschreit). Man kann aber auch mittels des Net-Moduls den Namen des gerade aktuellen PDCs einer Domäne herausfinden.

Wir setzen

$Server="\\\\BABCOM"; # Hennings Super PDC! Achtung, niemals \\\\ vergessen.
                                        # Hier muß ein UNC Pfad gesetzt werden. Ansonsten
                                        # läuft das Skript vor die Wand. Die Fehlermeldung würde
                                        # auch niemanden diese Ursache vermuten lassen. Wie geschaffen
                                        # für Frust.

$Level ist ein wichtiger Parameter. Er legt fest, wieviele Informationen ich bei der Usererzeugung mit angeben will. Dieser Parameter wird bei vielen der Win32API::Net Funktionen verlangt. Was heißt das? Ganz klar: Wenn ich einen User erzeuge, muß ich einen Usernamen angeben (logisch!), ich muß auch ein Paßwort angeben. Das sind Infos, die ich auf jeden Fall angeben muß. Es gibt aber auch andere optionale Parameter, so z.B. vollständige Name des Users, eine Beschreibung für den User, der Pfad für das Login-Skript, der Pfad für das Profil Verzeichnis und und und. Ihr habt es erraten: Alle Infos, die ich Benutzermanager sehen kann, kann ich auch setzen. Vielleicht will ich aber nicht alle setzten, sondern nur einen Teil. Also, sage ich durch den $Level Parameter wieviele Infos ich setzen will. Es gibt drei Level: 1, 2, und 3 (wie bei Michael Schanze. Mmmmh, habe ich gerade mein Alter verraten?). Bei Level 1 muß ich die folgenden Infos setzen:

name, password, passwordAge, priv, homeDir, comment, flags, scriptPath

Diese Informationen muß ich auf jeden Fall setzen, wenn ich einen User erzeuge; nicht weil das Betriebssystem es verlangt (ich brauch ja nur den Usernamen und ein Paßwort), sondern weil UserAdd() (bzw, der zugrundeliegende Win32 API Aufruf) es so will. Was ist also, wenn ich einen User erzeugen will und mit Level 0 nicht auskomme, aber nicht alle Infos aus Level 2 benötige? Antwort: Pech gehabt! Ich muß sie auf jeden Fall setzen, kann sie aber so setzen, daß der Default erhalten bleibt. Einige Parameter aus den Levels können gar nicht gesetzt, sondern nur gelesen werden. Hier werden einfach Dummies eingesetzt, die von NT ignoriert werden. Sie müssen aber trotzdem gesetzt werden, sonst hat der Hash nicht die Form, die erwartet wird.

Level 2 ist schon ein bißchen mehr und Level 3 ist das volle Programm. Lest in der Doku nach, welche Level welche Infos velangen. Ich weiß, was Ihr denkt, ihr denkt, wir nehmen jetzt Level 1, aber wir sind verdammt hart, und nehmen die ganze Ladung:

$Level=3;

Jetzt kommt ein nicht so schwer zu verstehender Parameter. %UserHash ist ein Hash (auch associative array genannt) und enthält die gesamten Infos für den User, der erzeugt werden soll (also all das Zeug, das ich durch die Angabe von Level spezifiziert habe). Wer nicht weiß, was ein Hash ist, sollte sich schämen und das entsprechende Kapitel im Tutorial nachlesen. Aber Achtung! In der Parameterliste steht nicht %UserHash, sondern \%UserHash, also eine Referenz auf ein Hash. Es wird also nicht der Hash selbst, sondern ein Zeiger auf den Hash übergeben. Wer nicht weiß, was eine Referenz ist, sollte für eine erste Erklärung im Tutorial nachschlagen, aber auf jeden Fall ein gutes Buch konsultieren (jede Einführung in C wird das erklären, aber auch die Perl Einführungen, die bei O'Reilly erscheinen). Wer keine Lust hat zu lesen (böse, böse), sollte auf jeden Fall den Unterschied hiermit zur Kenntnis nehmen. Ist auch im folgenden nicht weiter wichtig. Wo waren wir gerade? Ah!

Tja, ihr wolltet Level 3 und jetzt kommt Level 3. Achtet im folgenden darauf, daß die Keys des Hashes (also passwordAge, logonServer etc) fest vorgegeben sind. Auch sollte klar sein, daß man nicht alle Infos auf einer Workstation setzen kann. Bei Level 3 wird mann es also schon mit einem PDC zu tun haben müssen, wenn man alles setzen will (und wir wollen, oder nicht?). Bei einer Workstation werden die Optionen ignoriert, die nicht gesetzt werden können.
$UserHash{passwordAge}=-1;    # -1 bedeutet, daß das Paßwort niemals abläuft 
$UserHash{priv}=Win32API::Net::USER_PRIV_USER(); # priv steht für privileges; der Wert hier bedeutet, daß es sich
                                                 # um einen normalen user handelt. Das Ding ist eigentliche eine Konstante
                                                 # in der API Implementierung. Ist aber für das Modul als Methode implementiert worden
                                                 # deshalb das ().
$UserHash{flags}=1;                # 1 bedeutet, daß das Konto gegenwärtig nicht abgelaufen (also aktiv) ist
$UserHash{scriptPath}="logon.bat"; # müssen wir das erklären?
$UserHash{badPwCount}=3;           # 3 mal darf der User versuchen sich einzuloggen, dann wird das Konto gesperrt (wegen Doofheit)
$UserHash{acctExpires}= -1;        # -1 bedeutet, daß das Konto niemals abläuft
$UserHash{codePage}=49;            # die code page; hier: Deutschland. Keine Ahnung, was läuft, wenn man auf einem englischen
                                   # Server eine deutsche code page setzt.
$UserHash{countryCode}= 49;        # Ländercode; hier: Deutschland
$UserHash{logonServer}="\\\\*";    # nur ein Dummy, da der Wert nicht gesetzt werden kann
$UserHash{numLogons}= 0;           # nur ein Dummy, da der Wert nicht gesetzt werden kann

# Schaut euch den Benutzermanager, die Doku und die Win32API Doku an. Es gibt 21 Felder, jede Stunde ein Bit
# Bit=0=User darf sich zu dieser Stunde nicht anmelden, Bit=1=Er darf es. Setze alle Bits auf 1 macht Dezimal 255
# für jedes Feld=User darf sich immer anmelden. Das ganze wird in einem Array abgespeichert. Nicht verzagen,
# das ist HIER wirklich nicht wichtig. Beachtet: Das ist hier eine Referenz auf ein Array.
@array=(255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);
$UserHash{logonHours}=\@array;


$UserHash{unitsPerWeek}= 0;          # Kann man gefahrlos auf 0 setzen ;-). Siehe Win32 API Doku. Die Erklärung in der Modul Doku ist unzureichend 
$UserHash{maxStorage}=10;            # Dummy, da Speicherplatzbeschränkungen für User bei NT nicht gesetzt werde können
$UserHash{lastLogon}=0;		     # wann der User zuletzt eingeloggt war, setzen wir hier einfach auf 0 (es gibt ihn ja noch nicht!)
$UserHash{lastLogoff}=0;             # wann der User sich zuletzt ausgeloggt hat.
$UserHash{workstations}="";          # von welchen Workstations sich der User aus einloggen kann. " " bedeutet alle (siehe Doku)
$UserHash{parms}= "Kilroy was here"; # String, der im Benutzermanager nicht sichtbar ist. Kann zu administrativen Zwecken
                                     # dienen, kann aber nur mit Skripten etc. ausgelesen werden. Hier kann
                                     # man sich super verewigen ("Ich war hier") :-)
$UserHash{authFlags}=0;              # Dummy, Wert kann nicht gesetzt werden, sondern nur gelesen werden (siehe Doku zum Modul)
$UserHash{passwordExpired}=0;        # 0= User muß Paßwort bei ersten Login nicht ändern, 1= er muß es
$UserHash{homeDirDrive}="U:";        # Laufwerksbuchstabe, der für das Homeverzeichnis des Users dient
$UserHash{primaryGroupId}=0x201;     # ID der primären Gruppe; wird von Microsoft in der API Doku empfohlen. Kann man
                                     # auch anders setzten. Dazu braucht man aber die Group ID der Gruppe (kann man auch mit Perl bekommen)
$UserHash{userId}=0;                 # Dummy, da nur gelesen werden kann

$UserHash{name}="MMARZ";                            # Benutzer-Name
$UserHash{fullName}="Marzorati, Marco";             # vollständiger Name im Benutzermanager
$UserHash{password}="strenggeheim";                 # Weiß ich nicht ;-)
$UserHash{comment}="Benutzer der Firma XYZ";        # Kommentar, der im Usermanager erscheint
$UserHash{usrComment}="Irgendeine Bemerkung";       # Kommentar, der NICHT im Usermanager erscheint, der aber gesetzt werden kann
$UserHash{profile}="\\\\Babcom\\Profile\\MMARZ";    # UNC Pfad zum Server, wo Profile abgespeichert werden (für roaming profiles)
$UserHash{homeDir}="\\\\Babcom\\Benutzer\\MMARZ";   # UNC Pfad zum Server, wo Benutzerverzeichnisse abgespeichert werden

So, alles klar bis jetzt? Ich hoffe, ich habe nichts vergessen. Übrigens, wer einen Terminalserver einsetzt, sollte sich das Win32::Lanman Modul angucken, da kann man weitere Terminalserver-spezifische Optionen setzen (z.B. Zusätzliche Profilpfade und Homeverzeichnisse).

Also, jetzt sind wir eigentlich fast fertig. Der letzte Parameter von Win32API::Net::UserAdd($Server, $Level, \%UserHash, $Error), den wir noch besprechen müssen, ist $Error. Dieser Parameter enthält mögliche Fehlermeldungen, wenn der Funktionsaufruf vor die Wand laufen sollte.

Darum stricken wir als nächstes noch eine kleine Subroutine um Win32API::Net::UserAdd(), um eine rudimentäre Fehlerbehandlung zu haben. Außerdem werdet ihr das Ding in euren Skripten sowieso kapseln.

sub UserCreate {
	my ($L_Server, $L_Level, %L_UserHash, $L_Error)=@_;
	if (Win32API::Net::UserAdd($L_Server, $L_Level, \%L_UserHash, $L_Error)) {
   		print ("User $L_UserHash{name} wurde erfolgreich angelegt\n");
        }   	
	else {
   		print ("Win32API::Net::UserAdd() konnte User $L_UserHash{name} nicht anlegen: error code is $L_Error\n");
	}
}

So, diese Subroutine wird mit

Win32API::Net::UserAdd($Server, $Level, %UserHash, $Error);

aufgerufen. Der neue Benutzer sollte dann sofort im Usermanager sichtbar sein (denkt daran, dieses Skript kann auf einer Workstation nicht alle gewählten Parameter setzen).

Hier noch einmal ein vollständiges Listing für die cut-and-paste Fraktion. Das Skript ist getestet worden, sollte also ohne Probleme laufen.

# Skript, das auf einem PDC einen User erzeugt 

use Win32API::Net;

# Parameter für den Aufruf von UserAdd()

$Server = "\\\\MARZORAM";
$Level  = 3;

# Definitionen für den Hash, der User-Infos enthält

$UserHash{passwordAge}=-1;
$UserHash{priv}=Win32API::Net::USER_PRIV_USER(); 
$UserHash{flags}=1;                
$UserHash{scriptPath}="logon.bat"; 
$UserHash{badPwCount}=3;           
$UserHash{acctExpires}= -1;      
$UserHash{codePage}=49;
$UserHash{countryCode}= 49;      
$UserHash{logonServer}="\\\\*";  
$UserHash{numLogons}= 0;         
@array=(255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);
$UserHash{logonHours}=\@array;
$UserHash{unitsPerWeek}= 0;      
$UserHash{maxStorage}=10;        
$UserHash{lastLogon}=0;		 
$UserHash{lastLogoff}=0;
$UserHash{workstations}="";       
$UserHash{parms}= "Kilroy was here";                    
$UserHash{authFlags}=0;         
$UserHash{passwordExpired}=0;   
$UserHash{homeDirDrive}="U:";   
$UserHash{primaryGroupId}=0x201;
$UserHash{userId}=0; 
$UserHash{name}="MMARZ";
$UserHash{fullName}="Marzorati, Marco";
$UserHash{password}="strenggeheim";
$UserHash{comment}="Benutzer der Firma XYZ";
$UserHash{usrComment}="Irgendeine Bemerkung"; 
$UserHash{profile}="\\\\Babcom\\Profile\\MMARZ"; 
$UserHash{homeDir}="\\\\Babcom\\Benutzer\\MMARZ";

&UserCreate($Server,$Level,%UserHash, $Error);


sub UserCreate {
	my ($L_Server, $L_Level, %L_UserHash, $L_Error)=@_; # Parameter in lokale Variablen lesen
	if (Win32API::Net::UserAdd($L_Server, $L_Level, \%L_UserHash, $L_Error)) {
   		print ("User $L_UserHash{name} wurde erfolgreich angelegt\n");
        }   	
	else {
   		print ("Win32API::Net::UserAdd() konnte User $L_UserHash{name} nicht anlegen: error code is $L_Error\n");
	}
}

Wir werden an dieser Stelle weitere Befehle aus dem Modul Win32API::Net besprechen, also schaut mal wieder vorbei.

12.4.4 In welchen Gruppen ist User XY Mitglied?

kommt noch!
Perl Einführung: Das Win32API::Net Modul zurückInhaltvorwärts