O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
Windows Server Hacks
By Mitch Tulloch
March 2004
More Info

HACK
#53
Add Printers Based on Name of Computer
Here's a logon script you can use to solve a complicated problem in printer management: performing a logon task based on the name of the computer being logged into
The Code
[Discuss (2) | Link to this hack]

The Code

To get the code for the script, I suggest downloading the Logon.vbs file from the O'Reilly web site (http://www.oreilly.com/catalog/winsvrhks/), because it's too long to type from scratch. This version of the script was tested on Windows 2000 Service Pack 2 running Internet Explorer 6 Service Pack 1 with Microsoft Windows Script v5.6. It was also tested on Windows XP Professional participating in a small Active Directory domain.

WARNING

I have had a variation of this script in production for a long time now with great success. But, as always, either the differences in your environment or something I missed in editing the script for this hack might cause things behave unexpectedly.

Depending on your environment, the code requires:

For more information on using Internet Explorer for status messages, see "Using an IE Window to Display Progress" (http://www.myitforum.com/articles/11/view.asp?id=3489), "VBScript Forms (Part 1): Using Internet Explorer for Data Input/Output Forms" (http://www.myitforum.com/articles/11/view.asp?id=4390), and "Using IE to Browse for Files" (http://www.myitforum.com/articles/11/view.asp?id=4229).

Perform initial tasks

This section sets up the basics of the script. In this script, I also call a few subroutines/functions (listed further in later sections) that either gather information or perform my required tasks. I like using subroutines and functions, because it makes my code reusable or easily stored in a code library for future coding endeavors. This section accomplishes the following major tasks:

  • Call a subroutine that gathers basic system information

  • Exit the script if user is logged on locally to the server

  • Call subroutines to gather group memberships

The script also performs other tasks, as discussed in comments throughout the code.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' File:     Logon.vbs
' Updated:  April 2003
' Version:  2.1
' Author:   Dan Thomson, myITforum.com columnist
'           I can be contacted at dethomson@hotmail.com
'
' Usage:
'           This script can be directly assigned as a logon script for
'           Windows 2000 or greater clients. For older systems, this
'           script will need to be called from a logon batch file.
'
' Input:
'
' Requirements:
'           Win 9x, ME or NT 4:
'           - Active Directory Client Extensions
'             http://www.microsoft.com/windows2000/techinfo/howitworks/
'             activedirectory/adsilinks.asp
'           - Windows Script
'             http://msdn.microsoft.com/library/default.asp?url=/downloads
'             /list/webdev.asp
'           - A recent version of Internet Explorer
'
' Notes:
'           Tested on Windows 2000 Professional running Windows Script v5.6
'           and participating in an AD domain
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

On Error Resume Next

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Define Variables and Constants
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim objFileSys
Dim objIntExplorer
Dim objWshNetwork
Dim objWshShell
Dim strDomain           'Domain of the user
Dim strHomePage         'Homepage to be set for user
Dim strLogonPath        'Path to location from where the script is running
Dim strOSProdType       'OS Product type (WinNT, LanmanNT, ServerNT)
Dim strWorkstation      'Local Computer Name
Dim strUserGroups       'List of groups the user is a meber of
Dim intCounter          'General counter

Const UseNTServer = 0   'Sets whether this script runs when logging on locally
                        'to Windows Servers.
                        'Values are: 1 (Yes) OR 0 (No)

'Initialize common scripting objects
Set objFileSys    = CreateObject( "Scripting.FileSystemObject" )
Set objWshNetwork = CreateObject( "WScript.Network" )
Set objWshShell   = CreateObject( "WScript.Shell" )

'Pause script until user is fully logged on (applies only to Win 9x or ME)
'This will timeout after 10 seconds
strUser = ""
intCounter = 0
Do
  strUserID = objWshNetwork.Username
  intCounter = intCounter + 1
  Wscript.Sleep 500
Loop Until strUserID <> "" OR intCounter > 20

'Check for error getting username
If strUserID = "" Then
  objWshShell.Popup "Logon script failed - Contact the Helpdesk @ x 345", , _
    "Logon script", 48
  Call Cleanup
End If

'Setup IE for use as a status message window
Call SetupIE

'Display welcome message
Call UserPrompt ("Welcome " & strUserID)

'Add horizontal line as a 'break'
objIntExplorer.Document.WriteLn("<hr style=""width:100%""></hr>")

'Gather some basic system info
Call GetSystemInfo

If IsTerminalServerSession <> True Then
  'Exit if we are logging on locally to a server and the 
  'script is set to NOT run on servers
  IF UseNTServer = 0 AND (strOSProdType = "LanmanNT" OR strOSProdType = "ServerNT") Then
    objWshShell.Popup "Windows Server - Exiting Logon Script!", 10, _
    "Logon to " & strDomain, 16
    Call CleanUp
  End if
End If

'Get group memberships
strUserGroups = ""
Call GetLocalGroupMembership
Call GetGlobalGroupMembership

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Map drives, add shared printers and set default homepage
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Determining workstation settings

This section determines which settings should be applied to the workstation, based on the name of the workstation. Our environment has a nice naming convention: the building, room number, and station number are identified in the name. For example, the name Blg4Rm105-03 identifies a computer as being station 3, located in building 4, room 105. To determine which mappings get assigned to a computer, all I have to do is base my criteria on everything on the left of the dash (-).

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Part A
' This section performs actions based on computer name
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

'The left side of the computer name contains building and room information
If Instr( 1, strWorkstation, "-", 1) > 0 Then
  strWorkstation = _
    Left( strWorkstation, ( Instr( 1, strWorkstation, "-", 1)))
End If

Select Case UCase( strWorkstation )

  Case "BLD1RM101-"
    Call MapDrive ("U:", "MyShareSvr1", "MyShare1")
    Call AddPrinter ("Mydomain2", "MyPrtSvr2", "Bld1Rm101-HP4050")
    objWshNetwork.SetDefaultPrinter "\\MyPrtSvr2\Bld1Rm101-HP4050"
    strHomePage = "http://www.chesapeake.edu/academic_info/ " & _
    "acad_computing.asp"
  Case "BLD1RM202-"
    Call MapDrive ("U:", "MyShareSvr2", "MyShare2")
    Call AddPrinter ("Mydomain1", "MyPrtSvr1", "Bld1Rm202-HP4000")
    objWshNetwork.SetDefaultPrinter "\\MyPrtSvr1\Bld1Rm202-HP4000"
    strHomePage = "http://www.chesapeake.edu/library/default.asp"
  Case "BLD3RM104-"
    'This room uses TCP/IP printing instead of a print server. 
    'Only set homepage
    strHomePage = "http://www.chesapeake.edu/writing/wchome.htm"
  Case Else

End Select
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Setting the IE home page and final message

This section sets the IE home page (if specified), starts SMSls.bat, and posts a final message to the user. The script tests to determine if the user is a member of the Domain Admins or DoNotInstallSMS groups. If the user is a member of either of these groups, SMSls.bat is skipped. This is helpful if you want to get in quick to do a small task or to keep SMS off some of your more persnickety users' computers.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Set default homepage
If strHomePage <> "" Then
  Err.Clear
  objWshShell.RegWrite _
    "HKCU\Software\Microsoft\Internet Explorer\Main\Start Page", strHomePage
  If Err = 0 Then Call UserPrompt ("Set Internet home page to " & strHomePage)
End If

'Start SMSls.bat
'Do not run if a member of the Domain Administrators
'or in the global group DoNotInstallSMS
If InGroup("Domain Admins") OR InGroup("DoNotInstallSMS") Then
  Call UserPrompt ("Skipping SMSLS.BAT")
Else
  objWshShell.Run "%COMSPEC% /c " & strLogonPath & "\smsls.bat", 0, False
End If

'Add horizontal line as a 'break'
objIntExplorer.Document.WriteLn("<hr style=""width:100%""></hr>")

'Inform user that logon process is done
Call UserPrompt ("Finished network logon processes")

'Wait 10 seconds
Wscript.Sleep (10000)

'Close Internet Explorer
objIntExplorer.Quit( )

Call Cleanup
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

That's the end of the first major section of the script. The following subsections list and explain the various subroutines and functions.

Connecting to a shared network printer

The following routine is where the printer mapping occurs. It first verifies that the share is accessible and creates the mapping. If the share is not accessible or is not a valid print share, the user will be prompted with an error message that lets her know she should call the help desk.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Sub:      AddPrinter
'
' Purpose:  Connect to shared network printer
'
' Input:
'           strPrtServerDomain  Domain in which print server is a member
'           strPrtServer        Name of print server
'           strPrtShare         Share name of printer
'
' Output:
'
' Usage:
'           Call AddPrinter ("Mydomain2", "MyPrtSvr2", "Bld1Rm101-HP4050")
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub AddPrinter(strPrtServerDomain, strPrtServer, strPrtShare)

  On Error Resume Next

  Dim strPrtPath    'Full path to printer share
  Dim objPrinter    'Object reference to printer
  Dim strMsg        'Message output to user
  Dim blnError      'True / False error condition

  blnError = False

  'Build path to printer share
  strPrtPath = "\\" & strPrtServer & "\" & strPrtShare

  'Test to see if shared printer exists.
  'Proceed if yes, set error condition msg if no.
  Set objPrinter = GetObject _
    ("WinNT://" & strPrtServerDomain & "/" & strPrtServer & "/" & _ 
    strPrtShare)
  If IsObject( objPrinter ) AND _
  (objPrinter.Name <> "" AND objPrinter.Class = "PrintQueue") Then

     'Different mapping techniques depending on OS version
     If objWshShell.ExpandEnvironmentStrings( "%OS%" ) = "Windows_NT" Then
       Err.Clear
       'Map printer
       objWshNetwork.AddWindowsPrinterConnection strPrtPath
     Else
       'Mapping printers for Win9x & ME is a pain and unreliable.
     End If

  Else
    blnError = True
  End IF

  'Check error condition and output appropriate user message
  If Err <> 0 OR blnError = True Then
    strMsg = "Unable to connect to network printer. " & vbCrLf & _
             "Please contact the Helpdesk @ ext 345" & vbCrLf & _
             "and ask them to check the " & strPrtServer & " server." & _
             vbCrLf & vbCrLf & _
             "Let them know that you are unable to connect to the '" _
             & strPrtShare & "' printer"
    objWshShell.Popup strMsg,, "Logon Error !", 48
  Else
    Call UserPrompt ("Successfully added printer connection to " & _ 
    strPrtPath)
  End If

  Set objPrinter = Nothing

End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Mapping a drive to a shared folder

This routine is where the drive mapping occurs. It first removes any preexisting drive mapping that might be using the designated drive letter. Then, it verifies that the share is accessible and creates the mapping. If the share is not accessible, the user will be prompted with an error message that lets him know he should call the help desk.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Sub:      MapDrive
'
' Purpose:  Map a drive to a shared folder
'
' Input:
'           strDrive    Drive letter to which share is mapped
'           strServer   Name of server that hosts the share
'           strShare    Share name
'
' Output:
'
' Usage:
'           Call MapDrive ("X:", "StaffSvr1", "StaffShare1")
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub MapDrive( strDrive, strServer, strShare )

  On Error Resume Next

  Dim strPath       'Full path to printer share
  Dim blnError      'True / False error condition

  blnError = False

  'Disconnect Drive if drive letter is already mapped.
  'This assures everyone has the same drive mappings

  If objFileSys.DriveExists(strDrive) = True Then
    objWshNetwork.RemoveNetworkDrive strDrive, , True
  End If

  'Build path to share
  strPath = "\\" & strServer & "\" & strShare

  'Test to see if share exists. Proceed if yes, set error condition if no.
  If objFileSys.DriveExists(strPath) = True Then
    Err.Clear
    objWshNetwork.MapNetworkDrive strDrive, strPath
  Else
    blnError = True
  End If

  'Check error condition and output appropriate user message
  If Err.Number <> 0 OR blnError = True Then
    'Display message box informing user that the connection failed
    strMsg = "Unable to connect to network share. " & vbCrLf & _
             "Please contact the Helpdesk @ ext 345 and ask them " & _
             "to check the " & strServer & " server." & vbCrLf & _
             "Let them know that you are unable to connect to the " & _
             "'" & strPath & "' share"
    objWshShell.Popup strMsg,, "Logon Error !", 48
  Else
    Call UserPrompt ("Successfully added mapped drive connection to " & strPath)
  End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Gathering global group memberships

This routine is similar to the previous one, except it collects information about any global (rather than local) groups to which the user might belong. The names of the groups also get placed into the strUserGroups variable for future reference. Since some users might still be running Windows NT domains, I use the WinNT syntax instead of LDAP to perform the query, for cross-platform interoperability. This way is a little easier anyway.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Sub:      GetGlobalGroupMembership
'
' Purpose:  Gather all global groups the current user belongs to
'
' Input:
'
' Output:   Global group names are added to strUserGroups
'
' Usage:    Call GetGlobalGroupMembership
'
' Notes:    Use WinNT connection method to be backwards
'           compatible with NT 4 domains
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub GetGlobalGroupMembership

  On Error Resume Next

  Dim objNameSpace
  Dim objUser

  Const ADS_READONLY_SERVER = 4

  Set objNameSpace = GetObject( "WinNT:" )
  'Use the OpenDSObject method with the ADS_READONLY_SERVER
  'value to grab the "closest" domain controller

  'Connect to user object in the domain
  Set objUser = objNameSpace.OpenDSObject( _
    "WinNT://" & strDomain & "/" & strUserID, "", "", ADS_READONLY_SERVER)
  'Process each group
  For Each objGroup In objUser.Groups
    'Add group name to list
    strUserGroups = strUserGroups & objGroup.Name & ","
  Next
  Set objNameSpace = Nothing

End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Gathering basic information about the local system

Here is another routine that gathers specific information about the local computer, such as the user domain, workstation name, product type, and the path to the location from which the script is running.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Sub:      GetSystemInfo
'
' Purpose:  Gather basic information about the local system
'
' Input:
'
' Output:   strDomain, strOSProdType, strWorkstation, strLogonPath
'
' Usage:    Call GetSystemInfo
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub GetSystemInfo

  On Error Resume Next

  'Get domain name
  If objWshShell.ExpandEnvironmentStrings( "%OS%" ) = "Windows_NT" Then
    strDomain = objWshNetwork.UserDomain
  Else
    strDomain = objWshShell.RegRead( "HKLM\System\CurrentControlSet\" & _
                "Services\MSNP32\NetWorkProvider\AuthenticatingAgent" )
  End If

  'Get Product Type from Registry (WinNT, LanmanNT, ServerNT)
  strOSProdType = objWshShell.RegRead( _
    "HKLM\System\CurrentControlSet\Control\ProductOptions\ProductType")

  'Get computer name
  If IsTerminalServerSession = True Then
    'Set strWorkstation to the real name and not the name of the server
    strWorkstation = objWshShell.ExpandEnvironmentStrings( "%CLIENTNAME%" )
  Else
    strWorkstation = objWshNetwork.ComputerName
  End If

  'Get the path to the location from where the script is running
  strLogonPath = Left( Wscript.ScriptFullName, _
    ( InstrRev( Wscript.ScriptFullName, "\") -1))

End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Setting up IE for use as a status message window

I like to use Internet Explorer as a general status message screen for users. This routine gets Internet Explorer set up and ready for this purpose.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Sub:      SetupIE
'
' Purpose:  Set up Internet Explorer for use as a status message window
'
' Input:
'
' Output:
'
' Usage:    Call SetupIE
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub SetupIE

  On Error Resume Next

  Dim strTitle    'Title of IE window
  Dim intCount    'Counter used during AppActivate

  strTitle = "Logon script status"

  'Create reference to objIntExplorer
  'This will be used for the user messages. Also set IE display attributes
  Set objIntExplorer = Wscript.CreateObject("InternetExplorer.Application")
  With objIntExplorer
    .Navigate "about:blank"
    .ToolBar   = 0
    .Menubar   = 0
    .StatusBar = 0
    .Width     = 600
    .Height    = 350
    .Left      = 100
    .Top       = 100
  End With

  'Set some formating
  With objIntExplorer.Document
    .WriteLn ("<!doctype html public>")
    .WriteLn   ("<head>")
    .WriteLn    ("<title>" & strTitle & "</title>")
    .WriteLn     ("<style type=""text/css"">")
    .WriteLn      ("body {text-align: left; font-family: arial; _
                  font-size: 10pt}")
    .WriteLn     ("</style>")
    .WriteLn   ("</head>")
  End With

  'Wait for IE to finish
  Do While (objIntExplorer.Busy)
    Wscript.Sleep 200
  Loop

  'Show IE
  objIntExplorer.Visible = 1

  'Make IE the active window
  For intCount = 1 To 100
    If objWshShell.AppActivate(strTitle) Then Exit For
    WScript.Sleep 50
  Next

End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.