Hé, vous le scripteur !Qui êtes-vous ?

Les scripteurs Microsoft

Télécharger le code de cet article: HeyScriptingGuy2007-082007_08.exe (152KB)

Il y n'a pas si longtemps, le scripteur qui écrit cet article regardait un match de base-ball à la télé. Une fois le match terminé, il décida de lire un magasine mais laissa la télévision allumée. Alors qu'il terminait son magasine, il leva les yeux vers la télé et se rendit compte qu'il était en train de regarder un vieux film sur la deuxième guerre mondiale. En fait, il leva les yeux juste au moment où les acteurs entamaient un scénario 1000 fois éprouvé : un jeune GI américain effectue son tour de garde de nuit lorsqu'il entend un bruit.

« Qui va là ? » aboie la sentinelle.

« Ce n'est que moi, le sergent Smith », répond une voix.

« Sergent Smith ? Il n'y a pas Sergent Smith dans ce bataillon ».

« Je suis nouveau ici ; je viens d'être transféré de la compagnie A ».

« Ah, vraiment ? La compagnie A ? OK, Smith : Qui a gagné les World Series de 1934 ? »

« Les Yankees de New York ».

Mauvaise réponse, « Smith » ! (Qui, comme vous pouvez le deviner, est immédiatement arrêté et jeté en prison.) Tout bon Américain, tout Américain qui se respecte, sait que les St. Louis Cardinals, vous savez, Dizzy Dean et le Gas House Gang, ont gagné les World Series de 1934. Quiconque ne sait pas ça ne peut être qu'une chose : un espion.

Si une personne lisant cet article ne savait pas que les St. Louis Cardinals ont gagné les World Series de 1934, hé bien, une chose est sûre : votre compte est bon ; nous savons que vous êtes un espion. Nous espérons que vous aurez la décence de vous rendre au bureau du FBI le plus proche, ou au moins de les appeler. Quand il s'agit d'espions, le FBI assure le service ramassage et de livraison.

Alors que le Scripteur qui écrit cet article regardait le film, il se rendit compte qu'il aurait d'assez bonnes chances de répondre à la deuxième question de la sentinelle : Qui a gagné les World Series de 1966 ? Les Baltimore Orioles. World Series de 1960 ? Pittsburgh Pirates. World Series 1994 ? Ha ! Une question piège : il n'y a pas eu de World Series en 1994.

Cependant, à l'heure actuelle, il aurait eu beaucoup de problèmes à répondre à la toute première question : Qui êtes-vous ? Ce n'est pas une question aussi facile aujourd'hui qu'elle pourrait l'avoir été dans les années 40. Après tout, rien qu'avec Active Directory®, les utilisateurs peuvent avoir un nombre incalculable d'identités différentes, notamment :

  • leur prénom (givenName).
  • leur nom (sn).
  • leur nom d'affichage (displayName).
  • leur nom principal utilisateur (userPrincipalName).
  • leur nom d'ouverture de session (samAccountName).
  • leur nom unique (distinguishedName).

Tous ces noms identifient la même personne, et tous sont des noms qui, selon les circonstances, sont importants à connaître. Et c'est un gros problème. La plupart des utilisateurs connaissent leur prénom et leur nom. Mais si vous demandez à un utilisateur, « Quel est votre nom unique ? », peu d'entre eux pourront répondre, « Ben, c'est facile ! Je suis CN=Ken.Myer, UO=Finance, DC=fabrikam, DC=com. Mais mes amis m'appellent CN=Ken.Myer. »

En tant qu'administrateur système ou membre du support technique, vous devez vous aussi connaître ces noms. Mais comment êtes-vous censé obtenir ces informations ? Hé bien, une approche serait de pendre vos utilisateurs à des crochets de boucher jusqu'à ce qu'ils vous révèlent leur nom unique. Cela fonctionnerait probablement, mais la plupart des départements des ressources humains déconseillent ces pratiques de nos jours. Il vous faudra donc avoir recours au plan B : un script. Quel genre de script ? Hé bien, pour les débutants, pourquoi pas le script de la figure 1 ?

Figure 1 Récupération des attributs de l'utilisateur avec 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

Ohlala, si seulement le sergent Smith avait étudié VBScript, hein ? Ce script exploite un objet ADSI peu connu (mais extrêmement utile) nommé ADSystemInfo. Il s'agit d'un petit objet bien pratique qui peut renvoyer toutes sortes d'informations sur l'utilisateur actuellement connecté à l'ordinateur local, de même que sur l'ordinateur local lui-même et le domaine auquel il appartient. Par exemple, examinez la figure 2.

Figure 2 Afficher toutes sortes d'informations de domaine

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

Pour le moment, cependant, la seule propriété qui nous intéresse est la propriété UserName. Qu'est-ce que UserName a de si spécial ? Et bien, il se trouve qu'elle correspond à la propriété distinguishedName. Et qu'y a distinguishedName de spécial ? Hmm, distinguishedName s'apparente à un chemin d'accès UNC : tout comme un chemin UNC nous permet d'identifier de manière univoque un fichier quelque part sur notre réseau, distinguishedName (par exemple, CN=Ken.Myer, UO=Finance, DC=fabrikam, DC=com) nous permet d'identifier de façon unique un compte utilisateur dans Active Directory. Ensuite, cela nous permet de nous connecter à ce compte utilisateur. Et une fois cette connexion établie, nous pouvons récupérer n'importe quelle information sur un utilisateur, y compris son identité.

Et c'est exactement ce que nous faisons dans le premier script que nous vous avons présenté. Pour commencer, nous créons une instance de l'objet ADSystemInfo et attribuons ensuite la valeur de la propriété UserName à une variable nommée strUser :

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

Notez que nous n'avons pas à faire grand chose. Par exemple, nous n'avons pas besoin de spécifier le nom d'utilisateur, le domaine de l'utilisateur ou l'UO où réside le compte utilisateur ; ADSystemInfo fait tout cela pour nous. Une fois que nous avons le distinguishedName de l'utilisateur, nous pouvons ensuite nous connecter à son compte utilisateur en utilisant cette ligne de code :

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

Une fois encore, après avoir établi la connexion, nous pouvons renvoyer les valeurs de n'importe quel attribut Active Directory de ce compte. Dans notre exemple de script, nous avons simplement renvoyé certaines des propriétés du nom de l'utilisateur, mais nous aurions pu tout aussi facilement récupérer le numéro de téléphone, l'emplacement du bureau, l'adresse de messagerie etc.

C'est génial. Il vous suffit donc de transmettre ce script à tous vos utilisateurs et ils n'auront plus jamais à se demander, « Qui suis-je ? » (ou, s'ils sont indécis, ils disposeront d'un moyen rapide et facile de le déterminer). Mais qu'en est-il de la question apparentée : Qui êtes-vous ? C'est une chose de pouvoir identifier l'utilisateur connecté à l'ordinateur local. Mais comment diable peut-on déterminer qui a ouvert une session sur un ordinateur distant ? Comme on pouvait s'y attendre, il s'agit là d'un problème plus épineux.

Et, désolé, nous y avons pensé nous aussi, mais malheureusement, on ne peut pas simplement diriger notre petit script ADSystemInfo vers un ordinateur distant. La raison en est que l'objet ADSystemInfo peut uniquement être créé localement. Cela nous laisse trois possibilités :

  • Créer un script d'ouverture de session qui enregistre le nom de l'utilisateur connecté dans un certain emplacement facile d'accès.
  • Utiliser la classe WMI Win32_ComputerSystem et la propriété UserName.
  • Faire autre chose.

La première option semble plutôt séduisante. Comme vous l'avez peut-être remarqué, l'objet ADSystemInfo peut renvoyer le nom unique de l'ordinateur (la propriété ComputerName) ainsi que celui de l'utilisateur. Observez, si vous le voulez bien, l'exemple de script proposé à la figure 3.

Figure 3 Script d'ouverture de session

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

Que faisons-nous ici ? Pour commencer, nous prenons les valeurs des propriétés UserName et ComputerName et les enregistrons dans une paire de variables (strUser et strComputer). Nous nous connectons ensuite au compte utilisateur dans Active Directory (tout comme nous le faisions avant), nous récupérons la valeur de l'attribut displayName (comme avant) et nous enregistrons cette valeur dans une variable nommée strUserName. C'est plutôt simple, non ?

Nous sommes ensuite en mesure d'utiliser cette ligne de code pour nous connecter au compte ordinateur Active Directory :

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

après avoir établi cette connexion, nous attribuons le displayName de l'utilisateur à la propriété Description de l'ordinateur et appelons ensuite la méthode SetInfo pour intégrer cette modification à Active Directory :

objComputer.Description = strUserName
objComputer.SetInfo

Pourquoi faisons-nous cela ? C’est simple : supposons que Ken Myer ouvre une session sur l'ordinateur atl-ws-01. Devinez ce que sera la propriété Description de la valeur atl-ws-01 ? Vous l'avez deviné : Ken Myer, la personne actuellement connectée à l'ordinateur. Vous voulez savoir qui a ouvert une session sur atl-ws-01 ? Il vous suffit de vérifier la propriété Description.

Bon, ce scénario fonctionne plutôt bien en général, et même mieux si vous avez un script de déconnexion qui efface la propriété Description chaque fois que l'utilisateur ferme la session. Ce n'est toutefois pas une solution infaillible. Pourquoi ? Hé bien, les scripts d'ouverture de session ne fonctionnent pas toujours. Par exemple, ils ne s'exécutent généralement pas lorsque les gens ouvrent une session en utilisant RAS. De la même manière, le script d'ouverture de session ne s'exécutera pas si un utilisateur débranche sa connexion réseau, ouvre une session à l'aide d'informations d'authentification mises en cache et rebranche ensuite son ordinateur au réseau. Et supposons qu'un utilisateur éteigne son ordinateur sans fermer la session. Cela signifie que son script de fermeture de session ne s'exécutera jamais. Dans ce cas, Ken Myer apparaît toujours connecté à atl-ws-01 bien que la machine ne soit même pas allumée. Par conséquent, cette technique est très utile, mais...

Donc qu'en est-il de l'option 2, c'est-à-dire utiliser la classe Win32_ComputerSystem ? Encore une fois, cela fonctionnera souvent, mais le problème avec la classe Win32_ComputerSystem, c'est qu'elle ne renvoie pas toujours le nom de l'utilisateur connecté, surtout pour les utilisateurs qui n'ont pas de droits d'administrateur (et, surtout, pour les ordinateurs exécutant Windows® 2000). Le script de la figure 4 vous dira probablement qui a ouvert une session sur un ordinateur mais, une fois encore, cela n'est pas garanti.

Figure 4 Savoir qui est connecté avec WMI

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

En outre, dans ce cas-ci, la propriété UserName est renvoyée au format domaine\nom_d'utilisateur. En d'autres termes : FABRIKAM\kenmyer.

Oh, nous allions oublier : même si un nom est renvoyé, cela ne signifie pas qu'un utilisateur est effectivement connecté à l'ordinateur. Lorsque Ken Myer se déconnecte de atl-ws-01, son nom est conservé comme valeur de la propriété UserName et n'est pas remplacé tant qu'une autre personne n'ouvre pas une session.

Mouais.

Attendez ; cela ne fait pas de nous des espions. Voici une autre approche possible. Si quelqu'un est connecté à un ordinateur, il est probable que le processus Explorer.exe soit en cours d'exécution. En général, si Explorer.exe n'est pas en cours d'exécution, personne n'est connecté à la machine. Et comme Explorer.exe s'exécute à l'aide des informations d'authentification de l'utilisateur connecté, nous pouvons presque toujours déterminer qui a ouvert une session sur un ordinateur en utilisant un script semblable à celui de la figure 5.

Figure 5 Déterminer le propriétaire d'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

Comme vous pouvez le voir, dans le cas présent, nous nous connectons au service WMI sur un ordinateur distant (atl-ws-01, pour être exact). Nous utilisons ensuite la ligne de code suivante pour récupérer une collection de toutes les instances de la classe Win32_Process portant le nom Explorer.exe:

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

Et maintenant ? Hé bien, comme nous l'avons vu précédemment, si Explorer.exe ne s'exécute pas, il y a d'assez bonnes chances pour que personne ne soit connecté à l'ordinateur. Comment savoir si Explorer.exe est en cours d'exécution ? Une méthode très simple consiste à vérifier la valeur de la propriété Count de la collection. Si Count est égal à 0, nous avons une collection vide, et la seule explication pour que nous ayons une collection vide, c'est qu'il n'y ait pas d'instance Explorer.exe en cours d'exécution sur atl-ws-01. Si c'est le cas, nous renvoyons un message indiquant que personne n'est connecté à l'ordinateur :

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

Si Count est différent de 0, nous configurons une boucle For Each de sorte à parcourir la collection de processus ayant pour nom Explorer.exe (et oui, nous supposons que notre collection contiendra invariablement un seul élément). Pour chaque instance d'Explorer.exe, nous appelons ensuite la méthode GetOwner pour déterminer sous le compte de qui Explorer.exe s'exécute :

objProcess.GetOwner strUser, strDomain

Notez que nous transmettons des paramètres out à GetOwner : StrUser et strDomain. Les paramètres out sont simplement des variables que nous nommons et fournissons à une méthode ; la méthode leur attribuera ensuite des valeurs. Dans le cas présent, strUser recevra le nom d'ouverture de session de l'utilisateur connecté (kenmyer) et strDomain le nom de domaine de l'utilisateur connecté (FABRIKAM). Il nous suffit ensuite de renvoyer les valeurs de ces deux paramètres out :

Wscript.Echo strDomain & "\" & strUser

Vous savez quoi ? Cela marche plutôt bien. Mais nous pouvons aller un peu plus loin. Lorsque nous utilisons la méthode GetOwner, nous recevons en retour le nom d'ouverture de session (samAccountName) pour l'utilisateur connecté à l'ordinateur. C'est bien, mais, comme nous l'avons vu, les utilisateurs ont toutes sortes de noms en plus de leur samAccountName. Pour véritablement répondre à la question « Qui êtes-vous ? », il serait bon de connaître, disons, le nom d'affichage de l'utilisateur. Mais nous ne pouvons pas connaître les autres noms en utilisant GetOwner, n'est-ce pas ?

Non, nous ne le pouvons pas. Toutefois, nous pouvons utiliser le samAccountName, le placer dans un script de recherche Active Directory, puis localiser et nous connecter au compte utilisateur avec ce nom d'ouverture de session (une tâche facilitée par le fait que samAccountNames doit être unique dans un domaine). Et une fois connectés au compte utilisateur, nous pouvons retrouver les valeurs de n'importe quelle propriété Active Directory, y compris displayName.

Nous n'avons pas le temps de détailler le script de la figure 6. Pour en savoir plus sur les recherches dans Active Directory, consultez l'article « Hé mec, où est mon imprimante ? ». Qu'il me suffise de dire que ce script détermine le nom d'ouverture de session de l'utilisateur connecté, recherche dans Active Directory l'utilisateur possédant ce nom d'ouverture de session (samAccountName), se connecte au compte utilisateur en question et ensuite renvoie le nom d'affichage de l'utilisateur. Et tout ça avec une main attachée derrière le dos !

Figure 6 Connexion à l'utilisateur connecté

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

Donc quels sont les key takeaways pour aujourd'hui ? (À propos, c'est ainsi les gens de Microsoft parlent. Si vous voulez rendre un Scripteur complètement dingue, allez vers lui et dites quelque chose comme, « Nous avons besoin de trier les key takeaways, les action items et les non-goals pour tous les stakeholders ».) Bien, nous savons maintenant d'une part comment obtenir les informations concernant l'utilisateur connecté à un ordinateur (local ou distant). Plus important, nous savons que faire si nous sommes pris dans une spirale spatio-temporelle et que nous nous retrouvons à l'époque de la deuxième guerre mondiale : veillez à avoir une copie de cet article (pour pouvoir répondre à la question « Qui êtes-vous ? ») et, quoi que vous fassiez, ayez toujours avec vous une liste des vainqueurs des World Series. Après tout, on ne peut jamais savoir quand quelqu'un demandera, « Qui a gagné les World Series de 1903 ? »

N'oubliez pas : Les Boston Red Sox. C'étaient d'ailleurs les tous premiers World Series. Maintenant, selon vous qui a gagné les World Series de 1904 ? Comment ça, vous ne savez pas ? Excusez-nous une seconde ; nous avons un coup de fil à passer....

Les scripteurs Microsoft travaillent pour (enfin, sont employés par) Microsoft. Lorsqu’ils ne sont pas en train de jouer au base-ball, d’entraîner une équipe ou de regarder un match (ou de se livrer à de multiples autres activités), ils dirigent le Script Center TechNet. Vous pouvez le vérifier vous-même sur www.scriptingguys.com.

© 2008 Microsoft Corporation et CMP Media, LLC. Tous droits réservés. Toute reproduction, totale ou partielle, est interdite sans autorisation préalable.