Hey, Scripting Guy!Wer ist da?

Die Scripting Guys von Microsoft

Laden Sie den Code für diesen Artikel herunter: HeyScriptingGuy2007-082007_08.exe (152KB)

Vor kurzem sah sich der Scripting Guy, der diesen Artikel geschrieben hat, im Fernsehen ein Baseballspiel an. Als das Spiel zu Ende war, las er ein Magazin, ließ aber das Fernsehgerät laufen. Nach Lektüre des Magazins bemerkte er, dass im Fernsehen gerade ein Kriegsfilm aus dem Zweiten Weltkrieg gezeigt wurde. In dem Augenblick war gerade eine dieser typischen Szenen zu sehen, die folgendermaßen ablaufen: Ein junger amerikanischer Soldat steht nachts Wache und hört plötzlich ein Geräusch.

„Wer ist da?“, brüllt der Wachhabende.

„Ich bin es, Gefreiter Smith“, antwortet eine Stimme.

„Gefreiter Smith? In unserer Truppe gibt es keinen Gefreiten Smith.“

„Ich bin neu hier. Ich wurde gerade erst aus Kompanie A hierher versetzt.“

„Ach, wirklich? Aus Kompanie A? Na schön, Smith: Wer hat das Endspiel der amerikanischen Baseballmeisterschaft von 1934 gewonnen?“

„Die New York Yankees.“

Falsch geraten, Freundchen „Smith“! (Smith wurde natürlich sofort verhaftet und in eine Zelle gesperrt.) Jeder gute Amerikaner, also jeder echte Amerikaner, weiß natürlich, dass die St. Louis Cardinals – Sie wissen schon, Dizzy Dean und die Gas House Gang – das Endspiel der amerikanischen Baseballmeisterschaft von 1934 gewonnen haben. Jeder, der das nicht weiß, kann nur Eines sein: ein Spion.

Wenn jemand, der den Artikel dieses Monats gelesen hat, nicht weiß, dass die St. Louis Cardinals das Endspiel der amerikanischen Baseballmeisterschaft von 1934 gewonnen haben, kann nur Folgendes daraus geschlossen werden: Das Spiel ist aus, denn Sie sind offensichtlich ein Spion. Wir hoffen, dass Sie genug Anstand haben, sich bei der nächsten Polizeidienststelle zu stellen. Oder rufen Sie zumindest dort an, denn Spione holt die Polizei in der Regel auch mit einem Dienstwagen ab.

Im Verlauf des Films wurde dem Scripting Guy, der diesen Artikel geschrieben hat, plötzlich klar, dass er ziemlich gute Chancen hatte, die zweite Frage des Wachpostens richtig zu beantworten: Wer hat das Endspiel der amerikanischen Baseballmeisterschaft von 1966 gewonnen? Die Baltimore Orioles. Und das Endspiel der amerikanischen Baseballmeisterschaft von 1960? Die Pittsburgh Pirates. Und das Endspiel der amerikanischen Baseballmeisterschaft von 1994? Ah! Das ist eine Trickfrage: 1994 gab es nämlich kein Endspiel der amerikanischen Baseballmeisterschaft.

Heutzutage dagegen hätte der Scripting Guy schon große Schwierigkeiten, gleich die allererste Frage zu beantworten: Wer ist da? Diese Frage ist heute nicht mehr so einfach zu beantworten wie in den Vierzigerjahren. Schließlich können schon allein bei Active Directory® die Benutzer mehrere verschiedene Identitäten besitzen, zum Beispiel die Folgenden:

  • ihren Vornamen („givenName“)
  • ihren Nachnamen („sn“)
  • ihren Anzeigenamen („displayName“)
  • ihren Benutzerprinzipalnamen („userPrincipalName“)
  • ihren Anmeldenamen („samAccountName“)
  • ihren Distinguished Name („distinguishedName“)

Alle diese Namen identifizieren dieselbe Person, und alle diese Namen sind Namen, die abhängig von den jeweiligen Umständen wichtig sein können. Genau hier liegt aber das Problem. Die meisten Benutzer kennen ihren Vornamen und ihren Nachnamen. Doch wenn Sie einen Benutzer fragen, „Wie lautet Ihr Distinguished Name?“, werden nur wenige in der Lage sein, zu antworten, „Na, das ist doch ganz einfach! Ich bin CN=Ken.Myer, OU=Finance, DC=fabrikam, DC=com. Aber alle meine Freunde nennen mich einfach nur CN=Ken.Myer.“

Als Systemadministrator oder Helpdeskmitarbeiter müssen Sie diese Namen ebenfalls kennen. Doch wie gelangen Sie in den Besitz dieser Informationen? Nun, eine Methode wäre, die Benutzer an Fleischerhaken aufzuhängen, bis sie Ihnen endlich ihren Distinguished Name verraten. Das würde bestimmt funktionieren, aber die meisten Personalabteilungen raten heutzutage von solchen Praktiken ab. Daher müssen Sie unter Umständen auf Plan B zurückgreifen: ein Skript. Welche Art von Skript? Wie wäre es denn für den Anfang mit dem Skript in Abbildung 1?

Figure 1 Abrufen von Benutzerattributen mit ADSystemInfo

On Error Resume Next

Set objSysInfo = CreateObject("ADSystemInfo")
strUser = objSysInfo.UserName

Set objUser = GetObject("LDAP://" & strUser)
WScript.Echo "First Name: " & objUser.givenName
WScript.Echo "Last Name: " & objUser.sn
WScript.Echo "Display Name: " & objUser.displayName
WScript.Echo "User Principal Name: " & objUser.userPrincipalName
WScript.Echo "SAM Account Name: " & objUser.sAMAccountName
WScript.Echo "Distinguished Name: " & objUser.distinguishedName

Wenn Gefreiter Smith doch nur VBScript gelernt hätte ... Dieses Skript nutzt ein nur wenig bekanntes, aber äußerst nützliches ADSI-Objekt namens „ADSystemInfo“. Dies ist ein hübsches kleines Objekt, das alle möglichen Informationen über den Benutzer, der im Augenblick am lokalen Computer angemeldet ist, sowie über den lokalen Computer selbst und die Domäne zurückgibt, der er angehört. Werfen Sie beispielsweise einen Blick auf Abbildung 2.

Figure 2 Anzeigen verschiedener Arten von Domäneninformationen

On Error Resume Next

Set objSysInfo = CreateObject("ADSystemInfo")

Wscript.Echo "User name: " & objSysInfo.UserName
Wscript.Echo "Computer name: " & objSysInfo.ComputerName
Wscript.Echo "Site name: " & objSysInfo.SiteName
Wscript.Echo "Domain short name: " & objSysInfo.DomainShortName
Wscript.Echo "Domain DNS name: " & objSysInfo.DomainDNSName
Wscript.Echo "Forest DNS name: " & objSysInfo.ForestDNSName
Wscript.Echo "PDC role owner: " & objSysInfo.PDCRoleOwner
Wscript.Echo "Schema role owner: " & objSysInfo.SchemaRoleOwner
Wscript.Echo "Domain is in native mode: " & objSysInfo.IsNativeMode

Heute soll es aber nur um die Eigenschaft „UserName“ gehen. Was ist an „UserName“ denn so besonders? Nun ja, es entspricht zufällig der Eigenschaft „distinguishedName“. Was ist an „distinguishedName“ so besonders? Nun, „distinguishedName“ ähnelt einem UNC-Dateipfad: So, wie ein UNC-Pfad ermöglicht, eine Datei an einer beliebigen Stelle im Netzwerk eindeutig zu identifizieren, ermöglicht „distinguishedName“ (z. B. „CN=Ken.Myer, OU= Finance, DC=fabrikam, DC=com“), ein Benutzerkonto in Active Directory eindeutig zu identifizieren. Dies wiederum ermöglicht es, eine Bindung zu diesem Benutzerkonto herzustellen. Sobald diese Verbindung hergestellt ist, können über diesen Benutzer alle gewünschten Informationen abgerufen werden, einschließlich der Information, wer er ist.

Das ist genau das, was im ersten hier gezeigten Skript erfolgt. Zunächst wird eine Instanz des Objekts „ADSystemInfo“ erstellt und danach dem Wert der Eigenschaft „UserName“ eine Variable namens „strUser“ zugewiesen:

Set objSysInfo = CreateObject("ADSystemInfo")
strUser = objSysInfo.UserName

Beachten Sie, dass hier gar nicht viel zu tun ist. Zum Beispiel muss weder der Benutzername noch die Benutzerdomäne oder die Organisationseinheit angegeben werden, in der sich das Benutzerkonto befindet, denn das erledigt alles „ADSystemInfo“. Sobald der „distinguishedName“ des Benutzers vorliegt, kann mithilfe der folgenden Codezeile eine Bindung zu seinem bzw. ihrem Benutzerkonto hergestellt werden:

Set objUser = GetObject("LDAP://" & strUser)

Wenn die Verbindung hergestellt ist, können auch hier die Werte aller Active Directory-Attribute dieses Kontos abgerufen werden. Im Beispielskript wurden lediglich einige der Namenseigenschaften des Benutzers abgerufen, aber es hätten genauso gut auch seine Telefonnummer, sein Firmenstandort, seine E-Mail-Adresse usw. abgerufen werden können.

Das ist großartig. Sie müssen lediglich allen Benutzern dieses Skript aushändigen, und danach müssen sich Ihre Benutzer nie wieder die Frage stellen, wer sie denn eigentlich sind. (Sollten sie sich diese Frage doch einmal stellen, haben sie die Antwort sofort parat.) Doch was ist nun mit einer ganz ähnlichen Frage: Wer ist da? Es ist ja schön und gut, den Benutzer identifizieren zu können, der am lokalen Computer angemeldet ist. Doch wie um alles in der Welt kann festgestellt werden, wer an einem Remotecomputer angemeldet ist? Wie Sie vielleicht bereits ahnen, ist dieses Problem etwas schwieriger zu lösen.

Dasselbe haben wir ebenfalls gedacht. Sie können nicht einfach das ADSystemInfo-Skript auf einen Remotecomputer anwenden. Dies ist deshalb nicht möglich, weil das Objekt „ADSystemInfo“ nur lokal erstellt werden kann. Damit bleiben drei Möglichkeiten:

  • Erstellen Sie ein Anmeldeskript, das den Namen des angemeldeten Benutzers in einem sofort zugänglichen Speicherort aufzeichnet.
  • Verwenden Sie die WMI-Klasse „Win32_ComputerSystem“ und die Eigenschaft „UserName“.
  • Setzen Sie eine völlig andere Methode ein.

Die erste Möglichkeit klingt recht einladend. Wie Sie vielleicht bemerkt haben, kann das Objekt „ADSystemInfo“ sowohl den Distinguished Name des Computers (die Eigenschaft „ComputerName“) als auch den Distinguished Name des Benutzers zurückgeben. Betrachten Sie nun das Beispielskript in Abbildung 3.

Figure 3 Anmeldeskript

On Error Resume Next

Set objSysInfo = CreateObject("ADSystemInfo")

strUser = objSysInfo.UserName
strComputer = objSysInfo.ComputerName

Set objUser = GetObject("LDAP://" & _
    strUser)
strUserName = objUser.displayName

Set objComputer = GetObject("LDAP://" & _
    strComputer)
objComputer.Description = strUserName
objComputer.SetInfo

Wie funktioniert es? Zunächst werden die Werte der Eigenschaften „UserName“ und „ComputerName“ abgerufen und in einem Variablenpaar („strUser“ und „strComputer“) gespeichert. Danach wird die Bindung zum Benutzerkonto in Active Directory hergestellt (genau wie zuvor), der Wert des Attributs „displayName“ abgerufen (genau wie zuvor) und dieser Wert in einer Variablen namens „strUserName“ gespeichert. Ganz einfach, nicht wahr?

Anschließend kann mithilfe der folgenden Codezeile eine Verbindung zum Active Directory-Computerkonto hergestellt werden:

Set objComputer = GetObject("LDAP://" & _
    strComputer)

Sobald diese Verbindung hergestellt ist, wird der „displayName“ des Benutzers der Eigenschaft „Description“ des Computers zugewiesen und danach die Methode „SetInfo“ aufgerufen, um diese Änderung in Active Directory einzutragen:

objComputer.Description = strUserName
objComputer.SetInfo

Was ist der Sinn dieser Vorgehensweise? Die Antwort ist ganz einfach: Nehmen Sie einmal an, Ken Myer meldet sich am Computer „atl-ws-01“ an. Was glauben Sie, welchen Wert die Eigenschaft „Description“ des Computers „atl-ws-01“ annehmen wird? Ganz genau: „Ken Myer“, also genau die Person, die an diesem Computer angemeldet ist. Möchten Sie wissen, wer sich am Computer „atl-ws-01“ angemeldet hat? Fragen Sie einfach die Eigenschaft „Description“ ab.

Im Allgemeinen funktioniert dieses Szenario recht gut – es funktioniert sogar noch besser, wenn Sie ein Abmeldeskript einrichten, das bei jeder Abmeldung des Benutzers den Inhalt der Eigenschaft „Description“ löscht. Dies ist jedoch keine völlig sichere Lösung. Warum nicht? Anmeldeskripte werden nämlich nicht immer ausgeführt. Sie werden beispielsweise dann nicht ausgeführt, wenn sich jemand mithilfe von RAS anmeldet. Ein Anmeldeskript wird auch dann nicht ausgeführt, wenn ein Benutzer seine bzw. ihre Netzwerkverbindung unterbricht, sich mithilfe zwischengespeicherter Anmeldeinformationen anmeldet und danach den Computer wieder ans Netzwerk anschließt. Nehmen Sie außerdem einfach einmal an, dass ein Benutzer seinen Computer ausschaltet, ohne sich abzumelden. Dies bedeutet, dass sein Abmeldeskript nie ausgeführt wird. In diesem Fall ist Ken Myer angeblich weiterhin am Computer „atl-ws-01“ angemeldet, obwohl der Computer noch nicht einmal in Betrieb ist. Es handelt sich hier also um ein nützliches Verfahren, aber ...

Wie steht es also mit der zweiten Möglichkeit, der Verwendung der Klasse „Win32_ComputerSystem“? Auch diese Methode funktioniert in vielen Fällen, aber das Problem liegt bei der Klasse „Win32_ComputerSystem“ darin, dass sie nicht immer den Namen des angemeldeten Benutzers zurückgibt, insbesondere bei Benutzern, die nicht über Administratorrechte verfügen, und bei Computern, auf denen Windows® 2000 ausgeführt wird. Aus dem Skript in Abbildung 4 können Sie wahrscheinlich ersehen, wer an einem Computer angemeldet ist, aber auch hier ist die Richtigkeit der Information nicht garantiert.

Figure 4 Finden Sie mit WMI heraus, wer angemeldet ist

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_ComputerSystem")

For Each objItem in colItems
  Wscript.Echo objItem.UserName
Next

Übrigens wird in diesem Fall der Wert der Eigenschaft „UserName“ im Format „Domäne\Benutzername“ abgerufen. Anders ausgedrückt: als „FABRIKAM\kenmyer“.

Hier noch ein weiterer Hinweis: Selbst wenn ein Name ausgegeben wird, bedeutet dies nicht unbedingt, dass tatsächlich ein Benutzer am Computer angemeldet ist. Wenn sich Ken Myer beim Computer „atl-ws-01“ abmeldet, wird sein Name als Wert der Eigenschaft „UserName“ beibehalten und erst bei der Anmeldung eines anderen Benutzers ersetzt.

Oje.

Aber warten Sie mal: Das macht uns noch lange nicht zu Spionen. Es gibt noch ein weiteres Verfahren, das eingesetzt werden kann. Wenn ein Benutzer an einem Computer angemeldet ist, wird mit Sicherheit der Prozess „Explorer.exe“ ausgeführt. Wird Explorer.exe nicht ausgeführt, ist normalerweise niemand am Computer angemeldet. Da Explorer.exe unter den Anmeldeinformationen des angemeldeten Benutzers ausgeführt wird, kann mithilfe eines Skripts wie in Abbildung 5 fast immer ermittelt werden, wer an einem Computer angemeldet ist.

Figure 5 Ermitteln des Besitzers von Explorer.exe

strComputer = "atl-ws-01"

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colItems = objWMIService.ExecQuery _
  ("Select * from Win32_Process Where Name = 'explorer.exe'")

If colItems.Count = 0 Then
  Wscript.Echo "No one is logged on to the computer."
Else
  For Each objProcess in colItems
    objProcess.GetOwner strUser, strDomain
    Wscript.Echo strDomain & "\" & strUser
  Next
End If

In diesem Fall wird eine Verbindung zum WMI-Dienst auf einem Remotecomputer (atl-ws-01) hergestellt. Danach wird die folgende Codezeile verwendet, um eine Sammlung aller Instanzen der Klasse „Win32_Process“ mit dem Namen „Explorer.exe“ abzurufen:

Set colItems = objWMIService.ExecQuery _
  ("Select * from Win32_Process Where " & _
  "Name = 'explorer.exe'")

Was nun? Wenn Explorer.exe nicht ausgeführt wird, dann ist es, wie bereits erwähnt, sehr wahrscheinlich, dass niemand am Computer angemeldet ist. Wie lässt sich erkennen, ob Explorer.exe ausgeführt wird? Eine ganz einfache Möglichkeit besteht darin, den Wert der Eigenschaft „Count“ der Sammlung zu überprüfen. Wenn „Count“ den Wert 0 hat, liegt eine leere Sammlung vor, und eine leere Sammlung kann es nur dann geben, wenn auf atl-ws-01 keine Instanzen von Explorer.exe ausgeführt werden. Wenn dies der Fall ist, geben Sie die Meldung zurück, dass niemand am Computer angemeldet ist:

Wscript.Echo "No one is logged on " & _
"to the computer."

Wenn „Count“ nicht den Wert 0 hat, durchlaufen Sie mithilfe einer „For Each“-Schleife die Sammlung der Prozesse mit dem Namen „Explorer.exe“ (es wird davon ausgegangen, dass die Sammlung nur einziges Element enthält). Anschließend wird für jede Instanz von Explorer.exe die Methode „GetOwner“ aufgerufen, um zu ermitteln, unter welchem Konto Explorer.exe ausgeführt wird:

objProcess.GetOwner strUser, strDomain

Beachten Sie, dass der Methode „GetOwner“ ein Paar von Out-Parametern übergeben wird: „strUser“ und „strDomain“. Out-Parameter sind einfach nur Variablen, denen Sie einen Namen zuweisen und die Sie einer Methode übergeben. Danach weist die Methode diesen Out-Parametern Werte zu. In diesem Fall wird der Variablen „strUser“ der Anmeldename des angemeldeten Benutzers (kenmyer) und der Variablen „strDomain“ der Domänenname für den angemeldeten Benutzer (FABRIKAM) zugewiesen. Anschließend müssen lediglich die Werte dieser beiden Out-Parameter zurückgegeben werden:

Wscript.Echo strDomain & "\" & strUser

Wissen Sie was? Das ist recht gut. Sie können aber noch einen Schritt weiter gehen. Wenn Sie die Methode „GetOwner“ verwenden, erhalten Sie den Anmeldenamen (den „samAccountName“) des Benutzers, der am Computer angemeldet ist. Das ist nicht schlecht, aber wie bereits erwähnt, besitzen Benutzer neben ihrem „samAccountName“ noch viele weitere Namen. Um die Frage „Wer ist da?“ wahrheitsgemäß beantworten zu können, wäre es hilfreich, beispielsweise den „displayName“ des Benutzers zu kennen. Diese anderen Namen können aber mithilfe von „GetOwner“ nicht ermittelt werden, oder?

Nein, das ist nicht möglich. Sie können aber den „samAccountName“ in ein Active Directory-Suchskript stecken und anschließend das Benutzerkonto mit diesem Anmeldenamen suchen und eine Bindung dazu herstellen. Diese Aufgabe wird dadurch erleichtert, dass ein „samAccountName“ innerhalb einer Domäne eindeutig sein muss. Sobald die Bindung zum Benutzerkonto hergestellt wurde, können auch hier die Werte aller Active Directory-Attribute einschließlich der Eigenschaft „displayName“ abgerufen werden.

Das Skript in Abbildung 6 kann an dieser Stelle nicht ausführlich erläutert werden. Weitere Informationen zu Active Directory-Suchvorgängen finden Sie im Artikel He, wo ist mein Drucker?. Es genügt zu sagen, dass dieses Skript den Anmeldenamen des angemeldeten Benutzers ermittelt, Active Directory nach dem Benutzer mit diesem Anmeldenamen („samAccountName“) durchsucht, eine Bindung zum betreffenden Benutzerkonto herstellt und anschließend den „displayName“ des Benutzers zurückgibt. Und das alles mit links!

Figure 6 Bindung zum angemeldeten Benutzer

strComputer = "atl-ws-01"

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colItems = objWMIService.ExecQuery _
  ("Select * from Win32_Process Where Name = 'explorer.exe'")

If colItems.Count = 0 Then
  Wscript.Echo "No one is logged on to the computer."
Else
  For Each objProcess in colItems
    objProcess.GetOwner strUser,strDomain
  Next
End If

Const ADS_SCOPE_SUBTREE = 2

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection

objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 

objCommand.CommandText = "SELECT displayName FROM " & _
  "'LDAP://DC=wingroup,DC=fabrikam,DC=com' WHERE " & _
    "objectCategory='user' " & _
    "AND samAccountName = '" & strUser & "'"
Set objRecordSet = objCommand.Execute

objRecordSet.MoveFirst

Do Until objRecordSet.EOF
  Wscript.Echo objRecordSet.Fields("displayName").Value
  objRecordSet.MoveNext
Loop

Was ist also die Ausbeute von heute? (Die Leute von Microsoft reden übrigens wirklich so. Wenn Sie den Scripting Guy so richtig in den Wahnsinn treiben wollen, brauchen Sie nur etwas Ähnliches zu sagen wie: „Die Prinzipalresultate müssen ergebnisorientiert selektioniert werden“.) Nun, zum einen wissen Sie jetzt, wie Sie Informationen über den an einem Computer angemeldeten Benutzer abrufen können – unabhängig davon, ob es sich um einen lokalen Computer oder einen Remotecomputer handelt. Was noch viel wichtiger ist: Sie wissen jetzt, wie Sie vorgehen müssen, falls Sie mal plötzlich durch ein Zeitloch fallen und sich im Zweiten Weltkrieg wiederfinden: Achten Sie darauf, dass Sie eine Ausgabe dieses Artikels bei sich haben (damit Sie die Frage „Wer ist da?“ richtig beantworten können), und tragen Sie immer eine Liste der Endspielgewinner der amerikanischen Baseballmeisterschaften mit sich herum. Man kann ja schließlich nie wissen, wann jemand die Frage stellt: „Wer hat das Endspiel der amerikanischen Baseballmeisterschaft von 1903 gewonnen?“

Hinweis: Die Boston Red Sox. Das war übrigens das allererste Endspiel der amerikanischen Baseballmeisterschaft. Wer hat denn nun das Endspiel der amerikanischen Baseballmeisterschaft von 1904 gewonnen? Was soll das heißen, Sie wissen es nicht? Würden Sie uns bitte für eine Sekunde entschuldigen? Wir müssen mal kurz telefonieren ...

Die Scripting Guys von Microsoft arbeiten für (und sind Angestellte von) Microsoft. Wenn sie nicht gerade ihrem Hobby, dem Baseball (oder verschiedenen anderen Aktivitäten) nachgehen, betreiben sie das TechNet-Skriptcenter. Besuchen Sie es unter www.scriptingguys.com.

© 2008 Microsoft Corporation und CMP Media, LLC. Alle Rechte vorbehalten. Die nicht genehmigte teilweise oder vollständige Vervielfältigung ist nicht zulässig.