1. Network Drives
Connect network drives
Batch: connect network drives
NET USE G: \\CompanyServer\Dept /PERSISTENT:No
IF ERRORLEVEL 1 (
ECHO Error mapping drive G:
)
NET USE H: \\CompanyServer\%UserName% /PERSISTENT:No
IF ERRORLEVEL 1 (
ECHO Error mapping drive H:
)
KiXtart: connect network drives
USE G: "\\CompanyServer\Dept"
If @ERROR <> 0
"Error @ERROR mapping drive G:@CRLF"
EndIf
USE H: "\\CompanyServer\@HOMESHR"
If @ERROR <> 0
"Error @ERROR mapping drive H:@CRLF"
EndIf
VBScript: connect network drives
Set wshNetwork = CreateObject( "WScript.Network" )
On Error Resume Next
With wshNetwork
.MapNetworkDrive "G:", "\\CompanyServer\Dept"
If Err Then
WScript.Echo "Error " & Err & " mapping drive G:"
WScript.Echo "(" & Err.Description & ")"
End If
.MapNetworkDrive "H:", "\\CompanyServer\" & .UserName
If Err Then
WScript.Echo "Error " & Err & " mapping drive H:"
WScript.Echo "(" & Err.Description & ")"
End If
End With
On Error Goto 0
Set wshNetwork = Nothing
Instead of “annoying” the user with the details of mapping drives, consider logging error messages and the results to a log file on the local computer.
In case of errors, set a variable named Error
and, at the end of the login script, display a message telling the user to contact the helpdesk.
KiXtart: connect drives with logging
; It doesn't hurt to make sure the C:\temp folder exists
MD "C:\temp"
; Redirect messages to a log file, display
; a message dialog if redirection fails
If RedirectOutpu( "C:\temp\login.log", 1 ) <> 0
$Msg = "Error logging the results.@CRLF"
$Msg = $Msg + "Please notify the helpdesk.@CRLF"
$Msg = $Msg + "For now, results will be displayed on screen."
$RC = MessageBox( $Msg, "Log File Error", 64, 300 )
EndIf
$Error = 0
; Map drive G: to the department share
USE G: "\\CompanyServer\Dept"
If @ERROR <> 0
"Error @ERROR while trying to map drive G:@CRLF"
$Error = $Error + 1
EndIf
; Map drive H: to the user's home share
USE H: "\\CompanyServer\@HOMESHR"
If @ERROR <> 0
"Error @ERROR while trying to map drive H: to the homedir@CRLF"
$Error = $Error + 1
EndIf
; List all mappings
USE List
; End redirection
$RC = RedirectOutput( "" )
; Warn the user if (an) error(s) occurred
If $Error > 0
$Msg = "$Error error(s) occurred during login.@CRLF"
$Msg = $Msg + "The errors are logged to be "
$Msg = $Msg + "reviewed by the helpdesk staff.@CRLF"
$Msg = $Msg + "Please notify the helpdesk.@CRLF"
$RC = MessageBox( $Msg, "Login Error", 64 )
EndIf
VBScript: connect drives with logging
Set wshNetwork = CreateObject( "WScript.Network" )
Set objFSO = CreateObject( "Scripting.FileSystemObject" )
' It doesn't hurt to make sure the C:\temp folder exists
If Not objFSO.FolderExists( "C:\temp" ) Then
Set objTempFolder = objFSO.CreateFolder( "C:\temp" )
Set objTempFolder = Nothing
End If
On Error Resume Next
' Open a log file, display a message dialog in case of error
Set objLogFile = objFSO.CreateTextFile( "C:\temp\login.log", True, False )
If Err Then
strMsg = "Error logging the results." & vbCrLf _
& "Please notify the helpdesk." & vbCrLf _
& "For now, results will be displayed on screen."
MsgBox strMsg, "Log File Error", 64
End If
intError = 0
With wshNetwork
' Map drive G: to the department share
.MapNetworkDrive "G:", "\\CompanyServer\Dept"
If Err Then
objLogFile.WriteLine "Error " & Err & " mapping drive G:"
objLogFile.WriteLine "(" & Err.Description & ")"
intError = intError + 1
End If
' Map drive H: to the user's home share
.MapNetworkDrive "H:", "\\CompanyServer\" & .UserName
If Err Then
objLogFile.WriteLine "Error " & Err & " mapping drive H:"
objLogFile.WriteLine "(" & Err.Description & ")"
intError = intError + 1
End If
End With
On Error Goto 0
' List all drive mappings
With wshNetwork.EnumNetworkDrives
For i = 0 To .Count - 2 Step 2
objLogFile.WriteLine .Item(i) & " " & .Item(i+1)
Next
End With
' Close the log file
objLogFile.Close
Set objLogFile = Nothing
' Warn the user if (an) error(s) occurred
If intError > 0 Then
strMsg = intError & " error(s) occurred during login." & vbCrLf _
& "The errors are logged to be reviewed " _
& "by the helpdesk staff." & vbCrLf _
& "Please notify the helpdesk."
MessageBox strMsg, "Login Error", 64
EndIf
End If
Set objFSO = Nothing
Set wshNetwork = Nothing
Often network drives are mapped based on group membership (or, for AD domains, on OU):
Batch: connect drives based on group membership
NET GROUP Marketing /DOMAIN | FINDSTR /R /I /B /C:"%UserName%$" >NUL
IF NOT ERRORLEVEL 1 (
NET USE G: \\Server\Marketing /PERSISTENT:No
)
Note: | Though this will usually work, it may fail if ampersands, carets, percent or dollar signs are used in group or user names. Not recommended! |
KiXtart: connect drives based on group membership
If InGroup( "Marketing" )
USE M: "\\CompanyServer\Marketing"
EndIf
VBScript: connect drives based on group membership
In VBScript this is a little more complicated, though hard-coding the domain name would simplify things:
strGroup = "Marketing"
blnMember = False
Set objSysInfo = CreateObject( "WinNTSystemInfo" )
strUserName = objSysInfo.UserName
strDomain = objSysInfo.DomainName
Set objSysInfo = Nothing
Set objUser = GetObject( "WinNT://" & strDomain & "/" & strUserName )
Set colGroups = objUser.Groups
For Each objGroup in colGroups
If LCase( objGroup.Name ) = LCase( strGroup ) Then
blnMember = True
End If
Next
Set colGroups = Nothing
set objUser = Nothing
The code shown is for NT as well as AD groups, and even for local groups on computers in a workgroup.
For AD domains, use ADSI.
Disconnect network drives
If users are allowed to map their own drives, you may want to consider disconnecting drives before mapping them:
Batch: reconnect drives
NET USE G: /DELETE /Y
NET USE G: \\CompanyServer\Dept /PERSISTENT:No
KiXtart: reconnect drives
USE G: /DELETE
USE G: "\\CompanyServer\Dept"
VBScript: reconnect drives
wshNetwork.RemoveNetworkDrive "G:", True
wshNetwork.MapNetworkDrive "G:", "\\CompanyServer\Dept"
2. Network Printers
Connect network printers
Batch: connect DOS style network printers
NET USE LPT1 \\Server\HPLJ4 /PERSISTENT:No
IF ERRORLEVEL 1 (
ECHO Error connecting printer HP LaserJet 4
)
KiXtart: connect network printers
If Not AddPrinterConnection( "\\CompanyServer\LaserJet Marketing" ) = 0
"Error @ERROR while trying to connect to LaserJet Marketing@CRLF"
EndIf
KiXtart: connect DOS style network printers
USE LPT1: "\\Server\HPLJ4"
If @ERROR <> 0
"Error @ERROR while trying to connect to HPLJ4@CRLF"
EndIf
VBScript: connect network printers
Set wshNetwork = CreateObject( "WScript.Network" )
On Error Resume Next
wshnetwork.AddWindowsPrinterConnection "\\CompanyServer\LaserJet Marketing"
If Err Then
WScript.Echo "Error " & Err.Number & " while trying to connect to LaserJet Marketing"
WScript.Echo "(" & Err.Description & ")"
End If
On Error Goto 0
Set wshNetwork = Nothing
VBScript: connect DOS style network printers
Set wshNetwork = CreateObject( "WScript.Network" )
On Error Resume Next
wshNetwork.AddPrinterConnection "LPT1", "\\Server\HPLJ4", False
If Err Then
WScript.Echo "Error " & Err.Number & " while trying to connect to HPLJ4"
WScript.Echo "(" & Err.Description & ")"
End If
On Error Goto 0
Set wshNetwork = Nothing
Like network drives, printer connections will usually depend on OU or group membership.
The same techniques discussed for network drives apply for network printers too.
Disconnect network printers
Disconnecting network printers is much like disconnecting network drives:
Batch: disconnect DOS style printers
NET USE LPT1 /DELETE /Y
IF ERRORLEVEL 1 (
ECHO Error disconnecting printer port LPT1
)
KiXtart: disconnect printers
If Not DelPrinterConnection( "\\CompanyServer\LaserJet Marketing" ) = 0
"Error @ERROR while trying to drop LaserJet Marketing@CRLF"
EndIf
KiXtart: disconnect DOS style printers
USE LPT1: /DELETE
If @ERROR <> 0
"Error @ERROR while trying to drop LPT1@CRLF"
EndIf
VBScript: disconnect all printer types
Set wshNetwork = CreateObject( "WScript.Network" )
On Error Resume Next
wshnetwork.RemovePrinterConnection "\\CompanyServer\LaserJet Marketing", True, False
If Err Then
WScript.Echo "Error " & Err.Number & " while trying to drop LaserJet Marketing"
WScript.Echo "(" & Err.Description & ")"
End If
On Error Goto 0
Set wshNetwork = Nothing
Set the default printer
Another useful function is SetDefaultPrinter( )
which, you may have guessed, sets the current user’s default printer.
It is available in KiXtart as well as in VBScript.
In NT batch there is no simple way to set the default printer.
It could be done by manipulating the registry, but this isn’t recommended.
You may want to consider using prnmngr.vbs -t
to set the default printer in a batch file.
Prnmngr.vbs
is located in Windows’ System32 directory.
KiXtart: set default printer
If SetDefaultPrinter ( "\\Server\HP LaserJet 4" ) <> 0
"Error @ERROR while trying to set the default printer to HP LaserJet 4@CRLF"
EndIf
VBScript: set default printer
Set wshNetwork = CreateObject( "WScript.Network" )
On Error Resume Next
wshnetwork.SetDefaultPrinter "\\Server\HP LaserJet 4"
If Err Then
WScript.Echo "Error " & Err.Number & " while trying to make HP LaserJet 4 the default printer"
WScript.Echo "(" & Err.Description & ")"
End If
On Error Goto 0
Set wshNetwork = Nothing
3. Log Computer Access
Though auditing is the preferred way to log access to computers, it does have one disadvantage: you can check on the computer who accessed it and when, but not the other way around.
So what do we do if we want to know which computers were accessed by a particular user?
To efficiently search this information, we need to store it in a central location, we don’t want to access each computer’s security event log separately.
And how are we going to collect this information?
Since the login script is forced to run each time a user logs in, it is perfectly suited to log each (interactive) access to any computer in the domain.
There are several options:
- a single log file containing all login information of all users on all computers for every date
- a log file per user
- a log file per computer (less practical)
- a log file per date
- any combination of the options 2..4
Depending on the number of users (and logins) I would recommend using a log file per date, or per user per date.
The log files need to be stored in directories per date, on a server where all Authenticated Users have Write permissions. The directory per date can be created by a scheduled task on the server, but it may be easier and safer to let login script check if it exists and create it if not.
So let’s have a look at some code to create a log file per user per day.
In the following code, a log file with the user name is created/used, and the computer name, current date and current time are logged.
If you want to use a single “common” log file for all users per day, make sure you also log the current user name.
Batch: log computer access
:: Strip the leading day of the week from the date
FOR %%A IN (%Date%) DO SET Today=%%A
:: Remove the date delimiters
SET Today=%Today:/=%
SET Today=%Today:-=%
:: Create a directory for today if it does not exist
IF NOT EXIST \\Server\Logs\%Today% MD \\Server\Logs\%Today%
:: Log the computer name and the date and time in a file with the user's name
>> \\Server\Logs\%Today%\%UserName%.log ECHO %ComputerName%,%Date%,%Time%
Note: | The directories created are not “sortable”, i.e. their names depend on the date format used on the computer running the login script. Either force the date format using a group policy, or use one of the SortDate scripts to get today’s date in YYYYMMDD format. |
KiXtart: log computer access
; Get the current date in YYYYMMDD format
$Today = "@YEAR" + Right( "0@MONTHNO", 2 ) + Right( "0@MDAYNO", 2 )
; Create the directory if it doesn't exist
If Exist( "\\Server\Logs\$Today\*.*" ) = 0
MD "\\Server\Logs\$Today"
EndIf
; Log current computer access
If RedirectOutput( "\\Server\Logs\$Today\@USERID.log" ) = 0
"@WKSTA,@DATE,@TIME@CRLF"
$RC = RedirectOutput( "" )
EndIf
VBScript: log computer access
Const ForAppending = 8
Const TristateFalse = 0
' Get today's date in YYYYMMDD format and time in HHmmss format
strToday = CStr( 10000 * Year( Now ) + 100 * Month( Now ) + Day( Now ) )
lngNow = 1000000 + 10000 * Hour( Now ) + 100 * Minute( Now ) + Second( Now )
strNow = Right( CStr( lngNow ), 6 )
' Get the current user and computer names
Set wshNetwork = CreateObject( "WScript.Network" )
strUser = wshNetwork.UserName
strComputer = wshNetwork.ComputerName
Set wshNetwork = Nothing
' Create the directory if it doesn't exist
Set objFSO = CreateObject( "Scripting.FileSystemObject" )
With objFSO
strFolder = .BuildPath( "\\Server\Logs", strToday )
If Not .FolderExists( strFolder ) Then
.CreateFolder strFolder
End If
strLog = .BuildPath( strFolder, strUser & ".log" )
Set objLog = .OpenTextFile( strLog, ForAppending, True, TristateFalse )
objLog.WriteLine strComputer & "," & strToday & "," & strNow
objLog.Close
Set objLog = Nothing
End With
Set objFSO = Nothing
4. Log Computer Status
Besides the computer name, user name and time of login, you can choose from a long list of properties to add to the login log.
How about logging the IP and MAC addresses?
Log IP and MAC addresses
Batch: log IP and MAC address (single adapter)
FOR /F "tokens=1,2 delims=:" %%A IN ('IPCONFIG /ALL ˆ| FIND "Address"') DO (
FOR /F "tokens=1,2" %%C IN ("%%~A") DO FOR %%E IN (%%~B) DO SET %%C%%D=%%E
)
>> \\Server\Logs\%Today%\%UserName%.log ECHO.%IPAddress%,%PhysicalAddress:-=%
Notes: | (1) | Though this code snippet will usually work, it depends too much on the Windows language and version to be reliable. Use only in an environment with identical Windows installations. |
(2) | The variable Today should be set before running the code displayed above. |
|
(3) | Instead of writing the AntiVirus status to a separate line, it is recommended to combine all properties that need to be logged into a single line. |
Batch: log IP and MAC addresses (single adapter, XP Pro SP2 or later)
SETLOCAL ENABLEDELAYEDEXPANSION
SET WMIPath=Path Win32_NetworkAdapter
SET WMIQuery=WHERE "AdapterType LIKE 'Ethernet%%' AND MACAddress>'' AND NOT PNPDeviceID LIKE 'ROOT\\%%'"
FOR /F "tokens=*" %%A IN ('WMIC %WMIPath% %WMIQuery% Get MACAddress /Format:List ^| FIND "="') DO SET %%A
SET WMIPath=Path Win32_NetworkAdapterConfiguration
SET WMIQuery=WHERE "MACAddress='%%MACAddress%%'"
FOR /F "tokens=*" %%A IN ('WMIC %WMIPath% %WMIQuery% Get IPAddress /Format:List ^| FIND "="') DO (
FOR /F "tokens=2 delims==" %%B IN ("%%~A") DO (
IF NOT "%%~B"=="" (
FOR /F "tokens=1 delims={}" %%C IN ("%%~B") DO (
SET IPAddress=!IPAddress!,%%~C
)
)
)
)
>> \\Server\Logs\%Today%\%UserName%.log ECHO.%IPAddress:~1%,%MACAddress::=%
ENDLOCAL
Notes: | (1) | This code snippet requires Windows XP Professional SP2 or later. |
(2) | The variable Today should be set before running the code displayed above. |
|
(3) | Instead of writing these properties to a separate line, it is recommended to combine all properties that need to be logged into a single line. |
KiXtart: log IP and MAC addresses
; Read the first IP address
$IP = Join( Split( @IPAddress0, " " ), "" )
; Check if there are more, and join them all using semicolons
For $i = 1 To 3
$RC = Execute( "If @@IPAddress$i > '' $$IP = $$IP + Chr(59) + Join( Split( @@IPAddress$i, ' ' ), '' )" )
Next
; Log the results
If RedirectOutput( "\\Server\Logs\$Today\@USERID.log" ) = 0
"$IP,@ADDRESS@CRLF"
$RC = RedirectOutput( "" )
EndIf
Notes: | (1) | The variable $Today needs to be set before running the code displayed above. |
(2) | Instead of writing these properties to a separate line, it is recommended to combine all properties that need to be logged into a single line. |
VBScript: log IP and MAC addresses
' Query all network adapters that have a MAC address
strQuery = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE MACAddress > ''"
Set objWMIService = GetObject( "winmgmts://./root/CIMV2" )
Set colItems = objWMIService.ExecQuery( strQuery, "WQL", 48 )
For Each objItem In colItems
If IsArray( objItem.IPAddress ) Then
strIP = strIP & ";" & Join( objItem.IPAddress, ";" )
strMAC = strMAC & ";" & Replace( objItem.MACAddress, ":", "" )
End If
Next
Set colItems = Nothing
Set objWMIService = Nothing
' Log the result
Set objLog = .OpenTextFile( strLog, ForAppending, True, TristateFalse )
objLog.WriteLine Mid( strIP, 2 ) & "," & Mid( strMAC, 2 )
objLog.Close
Set objLog = Nothing
Notes: | (1) | The variables objFSO and strLog and the constant ForAppending need to be set before running the code snippet displayed above. |
(2) | Instead of writing these properties to a separate line, it is recommended to combine all properties that need to be logged into a single line. |
Log AntiVirus status
Now let’s get some more advanced status readings. How about, for example, the status of the AntiVirus software installed?
Batch: log AntiVirus status (Windows XP SP2/SP3 only)
SET NameSpace=/Namespace:\\root\SecurityCenter
SET AVPath=Path AntiVirusProduct
SET AVProperties=displayNameˆˆ,onAccessScanningEnabledˆˆ,productUptoDateˆˆ,versionNumber
FOR /F "tokens=*" %%A IN ('WMIC %NameSpace% %AVPath% Get %AVProperties% /Format:List ˆ| FIND "="') DO (>NUL SET %%A)
>> \\Server\Logs\%Today%\%UserName%.log ECHO.%displayName%,%versionNumber%,%onAccessScanningEnabled%,%productUptoDate%
Notes: | (1) | The first 3 lines, setting environment variables, are used to limit the length of the WMIC command line. You are free to integrate them directly into the WMIC command. If you do, replace each set of double carets by a single caret. |
(2) | The variable Today should be set before running the code displayed above. |
|
(3) | Instead of writing the AntiVirus status to a separate line, it is recommended to combine all properties that need to be logged into a single line. | |
(4) | This WMIC command requires Windows XP Professional SP2 or SP3. It will not work in Windows Vista and later. |
KiXtart: log AntiVirus status (Windows XP SP2/SP3 only)
; Read the AV software status
$objWMISvc = GetObject( "winmgmts:{impersonationLevel=impersonate}!//./root/SecurityCenter" )
$colItems = $objWMISvc.ExecQuery( "SELECT * FROM AntiVirusProduct", "WQL", 48 )
For Each $objItem In $colItems
$Msg = $objItem.displayName + "," + $objItem.versionNumber
If $objItem.onAccessScanningEnabled = 0
$Msg = $Msg + ",FALSE,"
Else
$Msg = $Msg + ",TRUE,"
EndIf
If $objItem.productUptoDate = 0
$Msg = $Msg + "FALSE@CRLF"
Else
$Msg = $Msg + "TRUE@CRLF"
EndIf
Next
; Log the result
If RedirectOutput( "\\Server\Logs\$Today\@USERID.log" ) = 0
$Msg
$RC = RedirectOutput( "" )
EndIf
Notes: | (1) | The variable $Today needs to be set before running the code displayed above. |
(2) | Instead of writing the AntiVirus status to a separate line, it is recommended to combine all properties that need to be logged into a single line. | |
(3) | This WMIC command requires Windows XP Professional SP2 or SP3. It will not work in Windows Vista and later. |
VBScript: log AntiVirus status (Windows XP SP2/SP3 only)
' Query the AV status
Set objWMISvc = GetObject( "winmgmts:{impersonationLevel=impersonate}!//./root/SecurityCenter" )
Set colItems = objWMISvc.ExecQuery( "SELECT * FROM AntiVirusProduct" )
For Each objItem in colItems
With objItem
strMsg = .displayName & "," & .versionNumber
If .onAccessScanningEnabled Then
strMsg = strMsg & ",TRUE,"
Else
strMsg = strMsg & ",FALSE,"
End If
If .productUptoDate Then
strMsg = strMsg & "TRUE"
Else
strMsg = strMsg & "FALSE"
End If
End With
Next
Set colItems = Nothing
Set objWMISvc = Nothing
' Log the result
Set objLog = .OpenTextFile( strLog, ForAppending, True, TristateFalse )
objLog.WriteLine strMsg
objLog.Close
Set objLog = Nothing
Notes: | (1) | The variables objFSO and strLog and the constant ForAppending need to be set before running the code snippet displayed above. |
(2) | Instead of writing the AntiVirus status to a separate line, it is recommended to combine all properties that need to be logged into a single line. | |
(3) | This WMIC command requires Windows XP Professional SP2 or SP3. It will not work in Windows Vista and later. |
More Properties to Log
Besides the status of the AntiVirus software, there are more properties that can be useful to log, like the computer’s last reboot, hardware properties like CPU type or amount of physical memory, local printers…
Well, you get the idea.
Browse the script samples on this site, or other sites, for more details.
Warning: | Useful as this may be, you need to limit the number of logged properties, or the login process may take way too much time. Hardware properties could be logged in separate files per computer and limited to one log per week, for example. |
5. Update user or computer settings
Of course, when you can use login scripts to check settings, why not use it to correct or modify settings?
Many settings can be managed using group policies, but sometimes it may be easier to use an addition to the login script.
Make sure these modifications:
- are required on this particular computer (avoid running them more than once, don’t run on a server)
- don’t take too much time
- can be accomplished with the user’s credentials
- don’t require a reboot
6. Tips and best practices to prevent common mistakes
The most common mistake I’ve seen in login scripts is bloating: too many small additions that add up to a script that takes a quarter of an hour or even more to run.
How many hours of lost productivity every day are acceptable?
Some basic guidelines:
- always remember: login scripts run with the user’s credentials, and the user’s profile!
- don’t use login scripts for jobs that can be done by Scheduled Tasks (you may want to check if the task is scheduled on this particular computer, though)
- skip parts of the login script if possible: test if a condition is met, if so skip to the next section, if not connect, check, modify or log…
- group as many actions as possible, so you don’t need to check for group or OU membership or other conditions over and over again
- if a lot of checking and logging is done, consider grouping these actions in a “once per day” section at the end of the script
- avoid using login scripts as a poor man’s software distribution mechanism (though it is perfectly acceptable to configure user settings, or to perform emergency security updates)
- consider using a group policy to logoff all users during non-office hours, to make sure the login script runs at least once every working day
- check if the login script is running on a desktop computer or a Citrix or Terminal Server
- on a server the login script should not be allowed to do anything, especially not map network drives or printers!
- if the user is a (domain) administrator, the login script should not do anything, except maybe log the status (administrators shouldn’t have a login script in the first place)
KiXtart: abort if user is Administrator
; Get the current date in YYYYMMDD format
$Today = "@YEAR" + Right( "0@MONTHNO", 2 ) + Right( "0@MDAYNO", 2 )
; Create the directory if it doesn't exist
If Exist( "\\Server\Logs\$Today\*.*" ) = 0
MD "\\Server\Logs\$Today"
EndIf
; Log current computer access
If RedirectOutput( "\\Server\Logs\$Today\@USERID.log" ) = 0
"@WKSTA,@USERID,@DATE,@TIME,@PRIV@CRLF"
$RC = RedirectOutput( "" )
EndIf
; Administrators should quit now
If @PRIV = "ADMIN"
Quit 1
EndIf
Note: | This code won’t discriminate between local Administrators or Domain Admins. |
Related Stuff:
- Logon Script FAQ
- The Network section of my VBScript Scripting Techniques pages, especially the part on retrieving names
- Directory Service command line tools
- Kixtart Login Scripts
Alıntı:
http://www.robvanderwoude.com/loginscripts.php