Hey, Scripting Guy!Chi sei?

Gli Scripting Guy di Microsoft

Scarica il codice per questo articolo: HeyScriptingGuy2007-082007_08.exe (152KB)

Non troppo tempo fa, lo Scripting Guy che ha scritto questo articolo stava guardando una partita di baseball alla TV. Alla fine della partita, ha deciso di leggere una rivista ma ha lasciato la TV accesa. Dopo aver letto la rivista, ha dato uno sguardo alla televisione e si è accorto che stavano trasmettendo un vecchio film ambientato durante la seconda guerra mondiale. ha alzato gli occhi proprio durante una delle scene più tipiche dei film di guerra: un giovane soldato americano è di sentinella notturna quando sente un rumore.

"Chi sei?" urla la sentinella.

"Sono io, il sergente Smith," risponde una voce.

"Sergente Smith? Non c'è nessun sergente Smith in questa unità."

"Sono nuovo; sono stato appena trasferito dalla compagnia A."

"Oh, davvero? Dalla compagnia A? Ok, Smith: chi ha vinto nel 1934 la World Series?"

"I New York Yankees."

Risposta sbagliata, "Smith"! (Quindi, come ci si può aspettare, è stato subito arrestato e portato in prigione). Ogni vero americano sa infatti che i St. Louis Cardinals, Dizzy Dean e la Gas House Gang, hanno conquistato la World Series nel 1934. Chi non è a conoscenza di questo può essere solo una cosa: una spia.

Se tra coloro che leggono l'articolo di questo mese c'è qualcuno che non sa che i St. Louis Cardinals hanno vinto la World Series nel 1934, possiamo dire solo una cosa: la festa è finita; sappiamo che sei una spia. In questo caso speriamo che tu abbia la decenza di costituirti presso l'ufficio dell'FBI più vicino. O per lo meno fai loro una chiamata, in casi del genere l'FBI procederà al tuo arresto.

Lo Scripting Guy che ha scritto questo articolo, si è reso conto che probabilmente avrebbe saputo rispondere alla seconda domanda della sentinella: Chi ha vinto la World Series nel 1966? I Baltimore Orioles La World Series del 1960? I Pittsburgh Pirates. La World Series del 1994? Attenzione! Una domanda trabocchetto: nel 1994 la World Series non è stata disputata.

Attualmente tuttavia avrebbe avuto parecchie difficoltà a rispondere alla prima domanda: chi sei? Oggi non è una domanda facile a cui rispondere, contrariamente agli anni 40. Dopo tutto, con Active Directory® gli utenti possono avere tutte le identità che desiderano, tra cui:

  • Il nome di battesimo (givenName).
  • Il cognome (sn).
  • Il nome di visualizzazione (displayName).
  • Il nome dell'entità utente (userPrincipalName).
  • Il nome di accesso (samAccountName).
  • Il nome distinto (distinguishedName).

Tutti questi nomi identificano la stessa persona e sono tutti nomi che, in base alle circostanze, è importante conoscere. E questo rappresenta un problema. La maggior parte degli utenti conosce il proprio nome e il proprio cognome. Ma alla domanda: "qual è il tuo nome distinto?" soltanto alcuni utenti sono in grado di rispondere, "è semplice! Sono CN=Ken.Myer, OU=Finance, DC=fabrikam, DC=com. Ma gli amici mi chiamano CN=Ken.Myer."

Come amministratore di sistema o persona addetta all'help desk, è necessario conoscere tali nomi. Ma in che modo è possibile ottenere tali informazioni? Un metodo potrebbe essere quello di appendere gli utenti a testa in giù e costringerli a confessare il nome distinto. Questo metodo potrebbe funzionare, ma attualmente nella maggior parte degli uffici del personale questa pratica non è ammessa. Pertanto, potrebbe essere necessario ricorrere al piano B: uno script. Che il tipo di script? Bene, per cominciare, cosa ne pensate dello script riportato nella figura 1?

Figure 1 Recupero degli attributi dell'utente con 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

Se soltanto il sergente Smith avesse studiato lo script VBScript, eh? Questo script sfrutta un oggetto ADSI poco noto ma estremamente utile denominato ADSystemInfo. Si tratta di un piccolo e utile oggetto che può fornire una serie di informazioni sull'utente collegato al computer locale, sul computer locale stesso e sul dominio a cui appartiene. Ad esempio, vedere la figura 2.

Figure 2 Visualizzazione di tutte le informazioni sul dominio

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

Attualmente, tuttavia, l'unica proprietà di cui ci preoccupiamo è la proprietà UserName. Cosa c'è di così speciale nella proprietà UserName? Bene, si da il caso che corrisponda alla proprietà distinguishedName. E che cosa c'è di così speciale nel distinguishedName? Bene, il distinguishedName è simile al percorso di un file UNC: Proprio come un percorso UNC permette di identificare in modo univoco un file presente nella rete, il distinguishedName (ad esempio, il CN=Ken.Myer, OU=Finance, DC=fabrikam, DC=com) consente di identificare un account utente in Active Directory. A sua volta, consente di eseguire l'associazione all'account utente. Una volta stabilito tale collegamento, è possibile ottenere qualsiasi informazione sull'utente, tra cui la sua identità.

E questo è esattamente quello che viene eseguito dal primo script mostrato. Per iniziare, è necessario creare un'istanza dell'oggetto ADSystemInfo e quindi assegnare il valore della proprietà UserName a una variabile denominata strUser:

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

In questo caso non c'è molto altro da fare. Ad esempio, non è necessario specificare il nome utente, il dominio dell'utente o la UO in cui risiede l'account; ADSystemInfo fa tutto questo per noi. Una volta che il distinguishedName dell'utente è disponibile, è possibile associarlo al relativo account utilizzando questa riga di codice:

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

Quindi, dopo aver effettuato il collegamento, è possibile recuperare i valori degli attributi di Active Directory dell'account. Nello script di esempio, sono state recuperate alcune delle proprietà del nome utente ma è possibile recuperare allo stesso modo anche il numero di telefono, l'indirizzo dell'ufficio, l'indirizzo di posta elettronica e così via.

Fantastico. È necessario semplicemente distribuire lo script a tutti gli utenti in modo che non dovranno mai più chiedersi, "Chi sono io?". (Oppure, in questo modo possono ottenere in modo facile e veloce una risposta a tale domanda). Cosa pensate di questa domanda: chi sei? Una cosa è poter identificare l'utente collegato al computer locale. Ma come è possibile identificare l'utente collegato a un computer remoto? Come è facile immaginare, si tratta di un'attività particolarmente difficile.

Sfortunatamente non basta semplicemente associare lo script ADSystemInfo a un computer remoto. Questo perché l'oggetto ADSystemInfo può essere creato solo in locale. Questo lascia aperte tre possibilità:

  • Creare uno script di accesso che registra il nome dell'utente collegato in una posizione facilmente accessibile.
  • Utilizzare la classe WMI Win32_ComputerSystem e la proprietà UserName.
  • Effettuare altre operazioni.

La prima possibilità è allettante. L'oggetto ADSystemInfo può restituire il nome distinto del computer (la proprietà ComputerName) e il nome distinto dell'utente. Vedere lo script di esempio mostrato nella figura 3.

Figure 3 Script di accesso

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

Cosa stiamo facendo? In questa fase viene eseguito il recupero dei valori delle proprietà UserName e ComputerName, valori che andranno inseriti in due variabili (strUser e strComputer). Quindi, è necessario associare l'account utente in Active Directory (come fatto in precedenza), recuperare il valore dell'attributo displayName (come fatto in precedenza) e inserire tale valore in una variabile strUserName. Facile vero?

A questo punto è possibile utilizzare questa riga di codice per stabilire il collegamento all'account del computer Active Directory:

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

Dopo aver stabilito questo collegamento, è necessario assegnare il displayName dell'utente alla proprietà Description del computer e richiamare il metodo SetInfo per inserire tale modifica in Active Directory:

objComputer.Description = strUserName
objComputer.SetInfo

Perché eseguire questa operazione? La risposta è semplice. Ad esempio, se Ken Myer accede al computer atl-ws-01. Quale sarà il valore della proprietà Description del computer atl-ws-01? Esatto: Ken Myer, ovvero la persona attualmente collegata al computer. E chi è collegato al computer atl-ws-01? Basta controllare la proprietà Description.

Ora, in linea generale, questo scenario funziona bene e funziona bene anche con uno script di disconnessione che cancella i valori della proprietà Description ogni volta che l'utente chiude la connessione. Tuttavia, questa non è una soluzione infallibile. Perché no? Gli script di accesso non vengono sempre eseguiti. Ad esempio, solitamente non vengono eseguiti quando gli utenti eseguono l'accesso utilizzando RAS. Allo stesso modo, uno script di accesso non viene eseguito se l'utente chiude la propria connessione di rete, accede utilizzando credenziali memorizzate nella cache e quindi ristabilisce la connessione alla rete utilizzando tale computer. Oppure, cosa accade se un utente spegne il computer senza eseguire la disconnessione? Lo script di disconnessione non viene eseguito. In questo caso Ken Myer risulta collegato al computer atl-ws-01 anche se tale computer non è più in esecuzione. In altre parole, questa è una tecnica utile, ma...

E per quanto riguarda l'opzione 2 con la classe Win32_ComputerSystem? Spesso questo metodo funziona, ma il problema con la classe Win32_ComputerSystem è rappresentato dal fatto che talvolta non restituisce il nome dell'utente collegato, specialmente nel caso di utenti senza diritti di amministratore (e in particolar modo per i computer che eseguono Windows® 2000). Lo script riportato nella figura 4 probabilmente indicherà l'utente collegato al computer, ma questo non è sicuro al cento per cento.

Figure 4 Individuazione dell'utente collegato con 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

Fra l'altro, la proprietà UserName viene restituita nel formato dominio\nomeutente. In altre parole: FABRIKAM\kenmyer.

Una precisazione: Anche se non viene restituito un nome, questo non significa che sia presente un utente collegato al computer. Quando Ken Myer si disconnette dal computer atl-ws-01, il suo nome viene trattenuto come il valore della proprietà UserName, questo valore viene sostituito quando un altro utente esegue l'accesso al computer.

Beh!

Ma attenzione, questo non ci rende delle spie. Di seguito è riportato un altro metodo che è possibile utilizzare. Se qualcuno esegue l'accesso a un computer, viene eseguito il processo Explorer.exe. In generale, se il processo Explorer.exe non viene eseguito, significa che nessuno ha effettuato l'accesso al computer. Poiché il processo Explorer.exe viene eseguito in base alle credenziali dell'utente collegato, è quasi sempre possibile determinare l'utente collegato utilizzando uno script simile a quello riportato nella figura 5.

Figure 5 Determinazione del proprietario di 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

Come è possibile notare, in questo caso si stabilisce il collegamento al servizio WMI su un computer remoto (atl-ws-01, per la precisione). Quindi, si utilizza questa riga di codice per ottenere una raccolta di tutte le istanze della classe Win32_Process con il nome Explorer.exe:

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

E adesso? Come detto, se il processo Explorer.exe non viene eseguito, ci sono buone probabilità che nessuno abbia eseguito l'accesso al computer. Come è possibile sapere se il processo Explorer.exe viene eseguito? Un modo molto semplice è controllare il valore della proprietà Count della raccolta. Se il valore della proprietà Count è uguale a 0, la raccolta è vuota e l'unico caso in cui può essere presente una raccolta vuota si verifica quando non vi sono istanze di Explorer.exe in esecuzione su atl-ws-01. In tal caso, viene visualizzato un messaggio che specifica che nessun utente è collegato al computer:

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

Se il valore della proprietà Count non è uguale a 0, viene impostato un ciclo per la raccolta dei processi con il nome Explorer.exe (supponendo che la raccolta conterrà invariabilmente un solo elemento). Per ogni istanza di Explorer.exe dovrà essere richiamato il metodo GetOwner per determinare in base a quale account viene eseguito il processo Explorer.exe:

objProcess.GetOwner strUser, strDomain

Tenere presente che è necessario passare al metodo GetOwner un paio di parametri out: StrUser e strDomain. I parametri out sono delle semplici variabili assegnate a un metodo; il metodo assegnerà quindi i valori a tali parametri. In questo caso, al parametro strUser verrà assegnato il nome di accesso dell'utente collegato (kenmyer) e al parametro strDomain verrà assegnato il nome di dominio dell'utente collegato (FABRIKAM). Tutto quello che bisogna fare è restituire i valori di questi due parametri out:

Wscript.Echo strDomain & "\" & strUser

Sapete? Questo è abbastanza positivo. Ma è possibile fare un passo in avanti. Quando si utilizza il metodo GetOwner, viene restituito il nome di accesso (samAccountName) dell'utente collegato al computer. Questa è una cosa positiva ma, come detto in precedenza, gli utenti hanno infatti ogni genere di nome oltre al proprio samAccountName. Per rispondere del tutto alla domanda "Chi sei?" sarebbe utile conoscere il displayName dell'utente. Ma non è possibile determinare tale nome utilizzando il metodo GetOwne, vero?

No, non è possibile. Tuttavia, è possibile acquisire il valore di samAccountName, inserirlo in uno script di ricerca di Active Directory e individuare e associare l'account utente al nome di accesso (attività facilitata dal fatto che samAccountNames deve essere univoco in un dominio). Quindi, dopo aver eseguito l'associazione all'account utente è possibile recuperare i valori di qualsiasi proprietà di Active Directory, compreso il displayName.

Non c'è tempo per spiegare in dettaglio lo script riportato nella figura 6; per ulteriori informazioni sulla ricerca in Active Directory, vedere l'argomento relativo alla "tecnologia RFID". (In inglese). Basta dire che questo script determina il nome di accesso dell'utente collegato, ricerca in Active Directory l'utente con quel nome di accesso (samAccountName), esegue l'associazione all'account utente in questione e restituisce il displayName dell'utente. E tutto con una mano legata dietro la schiena!

Figure 6 Associazione all'utente collegato

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

Dunque, cosa abbiamo imparato oggi? (Oppure, quali sono i nostri "takeaway" di oggi? Come direbbero in Microsoft. Per far impazzire lo Scripting Guy, è sufficiente rivolgersi a lui dicendo cose del tipo, "abbiamo bisogno di classificare i takeaway principali, gli elementi d'azione e i non obiettivi delle parti coinvolte"). In primo luogo, le nostre conoscenze sono ora sufficienti per ottenere informazioni sull'utente collegato a un computer, sia remoto che locale. Ma, cosa più importante, ora sappiamo cosa fare nel caso in cui dovessimo ritrovarci, a causa di un salto spaziotemporale, all'epoca della seconda guerra mondiale: bisogna avere a disposizione una copia di questo articolo (per poter rispondere alla domanda "Chi sei?") e, in ogni caso, bisogna sempre avere a disposizione i nomi dei vincitori delle varie edizioni della World Series. Dopotutto, in qualsiasi momento potrebbero chiedervi "Chi ha vinto la World Series nel 1903?"

Nota: i Boston Red Sox. Si tratta della prima edizione della World Series. Chi ha vinto la World Series del 1904? Che significa non lo so? Ci scusi un attimo, dobbiamo fare una telefonata.

Gli Scripting Guy di Microsoftlavorano o, per meglio dire, sono stipendiati da Microsoft. Quando non si dedicano al baseball (o ad altre attività) come giocatori, allenatori o semplici spettatori, gestiscono il TechNet Script Center. Consultare la pagina www.scriptingguys.com.

© 2008 Microsoft Corporation e CMP Media, LLC. Tutti i diritti riservati. È vietata la riproduzione completa o parziale senza autorizzazione.