Professor Windows - August 2002
Scripting Your Windows 2000 Network, Part 2
Bob Wells, Programming Writer, Microsoft Corporation
After understanding what scripting is and what main technologies are involved, topics which were mentioned in Scripting Your Windows 2000 Network – Part 1, Part 2 will go more practically into various code samples that I find useful when I'm out there in the field, working with our partners and customers.
Part 2 assumes you have acquired some hands-on experience with sample scripts, as well as knowledge and understanding of the main technologies involved (VBS, WSH), along with technologies used inside some of the scripts (ADSI and WMI). To read more, see the following links:
Kicking Off: Reading Environment Variables
Let's kick off with a simple script, which may be handy in many environments. Reading environment variables is a very common procedure when using logon scripts and batch files. You can use variables such as %COMPUTERNAME%, %WINDIR% etc to map network drives, connect to printers and other relevant actions you may wish to perform in your logon/logoff scripts. One example of how to access those variables programmatically from script is by using the Wscript.Shell object.
You create a Wscript.Shell (WshShell) object whenever you want to run a program locally, manipulate the contents of the registry, create a shortcut, or access a system folder. The WshShell object provides the Environment collection. This collection allows you to handle environmental variables (such as WINDIR, PATH, or PROMPT).
For example (Note: To test this script, please change the DC names inside the script according to servers in your domain environment):
' Start Dim wshShell ' Create a new Windows Scripting Host Shell object Set wshShell = CreateObject("Wscript.Shell") ' Set it to read the environment variables Set EnvVar = wshShell.Environment("PROCESS") ' Re-direct LPT1: to the appropriate printer according to the authenticating DC name If EnvVar.Item("LogonServer") = "DC1" then wshShell.Run "net use lpt1: \\DC1\Printer1" Else wshShell.Run "net use lpt1: \\DC2\Printer2" End If ' -End
This script will run the net use command to connect LPT1: to the appropriate printer according to the LogonServer variable. When the authenticating DC is \\DC1, printer1 will be mapped, and for any other DC, printer2 will be mapped. You can use this generic script to get any environment variable, e.g. Computername, TEMP, WinDir etc, simply by replacing the EnvVar.Item line with the required variable. For example: To read the TEMP directory location, use EnvVar.Item("TEMP").
Working with the registry is common and will quite often be helpful to save information into the registry and read it when needed. Deleting from and writing to/from the registry can be achieved from .vbs files using the RegWrite and RegDelete methods of the Wscript.Shell object. Reading registry data can be achieved by the RegRead method.
You can also use WMI when accessing the registry. Using WMI might seem less intuitive when first compared to Wscript.Shell, yet it provides more control and greater functionality, e.g. the ability to enumerate keys and values.
The TechNet Script Center has many useful samples that demonstrate how to use both WSH and WMI to access and manipulate registry data. For those samples, please see: http://www.microsoft.com/technet/scriptcenter/registry/default.asp.
Using WMI to Fetch Operating System Information
In the opening sample we saw how you can read information from the environment variables, yet a lot of the information you might want to manipulate and use is located in other repositories and interfaces. You can use WMI to fetch any kind of system information you can think of, from disks and partitions through event viewer data and up to services, shares and anything else you have in your operating system environment and applications.
To read more about WMI, see: http://msdn.microsoft.com/library/en-us/dnclinic/html/scripting06112002.asp
The following is a sample script that displays various information taken from the at which machine it runs. This script should work on systems running Windows 2000 or above:
' Start ' Using WMI to read Win32_OperatingSystem information For Each os in GetObject("winmgmts:").InstancesOf("Win32_OperatingSystem") ' Display the OS information in "chunks": each VBCRLF line ' means go down one line to the next one, and each WScript.Echo statement means ' display a new message box. WScript.Echo "Version Info:" & VBCRLF & _ "============" & VBCRLF & _ " Version: ", os.Caption, os.Version & VBCRLF & _ " Build: ", os.BuildNumber, os.BuildType & VBCRLF & _ " CSD Version: ", os.CSDVersion & VBCRLF & _ " Serial Number: ", os.SerialNumber & VBCRLF & _ " Manufacturer: ", os.Manufacturer WScript.Echo "Memory Info:" & VBCRLF & _ "===========" & VBCRLF & _ " Free Physical Memory: ", os.FreePhysicalMemory & VBCRLF & _ " Free Space in Paging Files: ", os.FreeSpaceInPagingFiles & VBCRLF & _ " Size Stored in Paging Files: ", os.SizeStoredInPagingFiles & VBCRLF & _ " Free Virtual Memory: ", os.FreeVirtualMemory & VBCRLF & _ " Total Virtual Memory Size: ", os.TotalVirtualMemorySize & VBCRLF & _ " Total Visible Memory Size", os.TotalVisibleMemorySize WScript.Echo "Time Info:" & VBCRLF & _ "=========" & VBCRLF & _ " Current Time Zone: ", os.CurrentTimeZone & VBCRLF & _ " Install Date: ", os.InstallDate & VBCRLF & _ " Last Bootup Time: ", os.LastBootUpTime & VBCRLF & _ " Local Date & Time: ", os.LocalDateTime WScript.Echo "Process Info:" & VBCRLF & _ "============" & VBCRLF & _ " Foreground App Boost: ", os.ForegroundApplicationBoost & VBCRLF & _ " Maximum #Processes: ", os.MaxNumberOfProcesses & VBCRLF & _ " Maximum Memory Size for Processes: ", os.MaxProcessMemorySize & VBCRLF & _ " #Processes: ", os.NumberOfProcesses WScript.Echo "User Info:" & VBCRLF & _ "=========" & VBCRLF & _ "#Users: ", os.NumberOfUsers & VBCRLF & _ "Registered User: ", os.RegisteredUser WScript.Echo "Locale Info:" & VBCRLF & _ "===========" & VBCRLF & _ "Code Set: ", os.CodeSet & VBCRLF & _ "Country Code: ", os.CountryCode & VBCRLF & _ "Locale: ", os.Locale WScript.Echo "System Info:" & VBCRLF & _ "===========" & VBCRLF & _ "Boot Device: ", os.BootDevice & VBCRLF & _ "Name: ", os.CSName & VBCRLF & _ "Status: ", os.Status & VBCRLF & _ "System Device: ", os.SystemDevice & VBCRLF & _ "System Directory: ", os.SystemDirectory & VBCRLF & _ "Windows Directory: ", os.WindowsDirectory Next ' End
Setting Information for Multiple Objects
One of the most useful operations scripting can be utilized for is setting information to multiple objects in a domain environment. Think about it, specifically for the following sample : When we used to open up User Manager in NT, we could easily pick up a list of users and edit their properties all together. Now, don't get me wrong here, of course Active Directory brings many improvements compared to the NT environment, and exceeds the old management tools by all means.Still, this simple action is required by many administrators. The good news is that multiple select and edit has been added into Windows XP/.NET Active Directory Users & Computers MMC Snap-ins. Let's see how this can be done via script. For the following sample, we'd like to use a script to automatically populate the home directory and home drive letter to multiple users in a certain Organization Unit (OU) in a Windows 2000 Active Directory domain.
This sample uses ADSI throughout the script. To read more about ADSI, see: http://msdn.microsoft.com/library/en-us/adsi/adsi/adsi_scripting_tutorial.asp.
Here's the sample script:
' -Start ' Setting Home Directory for multiple users in a specific OU Dim oContainer ' Bind to the destination Organizational Unit or container (modify this line according to ' your own domain tree information) Set oContainer=GetObject("LDAP://OU=MyUsers,DC=yosdom,DC=com") ' Running the ModifyUsers Subroutine on the container ModifyUsers(oContainer) ' Clean up Set oContainer = Nothing ' Display a message box upon operation complete MsgBox "Finished :O)" ' Close WScript.Quit Sub ModifyUsers(oTopLevelContainer) ' oTopLevelContainer is the oContainer object Dim oObj ' Go into a loop for every object in the container For Each oObj in oTopLevelContainer ' We use "Select Case" to apply different code/actions to different values of an item. In ' this case, we check every object's Class to apply the actions according to the Object's ' class, e.g. If it's a user object, it goes into the 'Case "user"' section. Select Case oObj.Class ' if it's a user object, then set it's home directory and ' home drive properties (modify the Server name and home drive ' letter according to your needs.) Case "user" oObj.Put "HomeDirectory", "\\server1\users\" + oObj.SamAccountName oObj.Put "HomeDrive", "m:" ' Save changes to the user object oObj.Setinfo ' If it's a container/OU, go into the ModifyUsers loop for every object there Case "organizationalUnit" , "container" ModifyUsers(oObj) End select ' Goes into the next available child object under the container Next End Sub ' End
The results of such a script will be that all the users in the OU called MyUsers in domain yosdom.com will get the home directory and home drive letter set accordingly. This means Home Drive will be M:, and the home directory will be set according to the user login name, e.g. \\Server1\users\YossiS.
When looking inside the script, notice that the ModifyUsers Subroutine uses a Select Case statement to check for the object class and act accordinglyFor example, if it finds a down-level container or another OU under MyUsers, it'll go into that container/OU and run the script for all the users inside this level as well. If it's a user object, it'll set the appropriate settings as described above. You can, for example, change the following line in the script and see what happens:
What this will do is run on the entire domain tree and set the appropriate home drive and home directory to all user objects if finds.
A similar and more advanced script sample that uses this kind of a Subroutine to automate the creation of user shares can be found at How to Create User Shares for All Users in a Domain with ADSI (234746).
Appending to a Log File
When running a long script operation, maintaining a text log file can be very useful to parse afterwards, both as a means to see how your script operation went, as well as a debug mechanism to figure out where your script is failing. You can use Scripting.FileSystemObject object to create and append to your own custom log file.
If you need to add, move, change, create, or delete folders (directories) and files, you can use the FileSystemObject (FSO) object model, which is explained in the following link: http://msdn.microsoft.com/library/en-us/script56/html/FSOoriFileSystemObject.asp.
For example, let's take the above script where we read environment variables and performed the 'net use' command accordingly, and incorporate a Log Appending Subroutine into the original script (Note: To test this script, please change the DC names inside the script according to servers in your domain environment):
' Start Dim wshShell ' Create a new Windows Scripting Host Shell object Set wshShell = CreateObject("Wscript.Shell") ' Set it to read the environment variables Set EnvVar = wshShell.Environment("PROCESS") ' Log/Append an entry into a local text file stating which DC authenticated the user ' Note: Calling 'Now' outputs the exact time/date on this machine in real-time Add2Log Now & ": User was authenticated by DC " & EnvVar.Item("LogonServer") ' Now back to the original script action: Re-direct LPT1 to the appropriate printer ' according to the authenticating DC name If EnvVar.Item("LogonServer") = "DC1" then ' Map LPT1 to the printer \\DC1\Printer1 If the DC name is DC1 wshShell.Run "net use lpt1: \\DC1\Printer1" Else ' If the DC name is not DC1, then Map LPT1 to the printer \\DC2\Printer2 wshShell.Run "net use lpt1: \\DC2\Printer2" End If ' Log/Append another entry into the local text file stating the script is completed Add2Log Now & ": Script completed running." ' Close wscript.quit ' This is the Log Appending Subroutine Sub Add2Log (txt) ' txt is the text we send into the subroutine ' Declare the log file name Const Myfile = "C:\MyLog.txt" ' Log file name ' Open it for Append Const ForAppending = 8 ' Append mode ' Declare the FileSystemObject and File variables Dim fso, file ' Create a new FileSystemObject object Set fso = CreateObject("Scripting.FileSystemObject") ' Open the file and force creation, if it doesn't exist already Set file = fso.OpenTextFile(MyFile, ForAppending, TRUE) file.WriteLine (txt) ' append log ' Clean up Set file = Nothing End Sub ' End
Go to c:\ and open up MyLog.txt. You'll find the entries similar to the following:
7/16/2002 6:43:35 PM: User was authenticated by DC \\DCName
7/16/2002 6:43:37 PM: Script completed running.
This script consists of two main parts: the 'append to log' Subroutine (Add2Log Sub) and the first script lines that trigger the log appending Subroutine. Whenever you run a line inside the script that starts with 'Add2Log' followed by a text between " " marks, the text will go into the Add2Log section. It will append the text you specified to the next line inside the text file, in this case, c:\MyLog.txt.
If you want more sample scripts, I encourage you go over the related links and explore the samples shown there, especially the TechNet Script Center. Happy Scripting!
TechNet Script Center (almost 400 ready-to-go script samples in various areas!)
For any feedback regarding the content of this column, please write to Microsoft TechNet. Please be aware that a response is not guaranteed.