Logon scripts can over time get complex and messy. Here’s an example of an alternative script that is a “one for all” script that can be used across a whole enterprise. The script uses group membership and the location of the user account in AD to work out which drive mappings and welcome window to give to the user.
Let’s assume there are two sites in the company to keep it simple, one in London and another in New York.
The OU structure is as follows:
John wants the following drives mapped:
P: drive mapped to \\nyserver1\Public
M: drive mapped to \\nyserver2\IT
Sarah wants the following drives mapped:
P: drive mapped to \\nyserver1\Public
M: drive mapped to \\lonserver2\IT
We create three groups:
A group named DriveMapping-nyserver1-Public and add both Sarah and John
The group description is set as: P,\\nyserver1.acme.corp\public
A group named DriveMapping-nyserver2-IT and add John
The group description is set as: M,\\nyserver2.acme.corp\IT
A group named DriveMapping-lonserver2-IT and add Sarah
The group description is set as: M,\\lonserver2.acme.corp\IT
On \\acme.corp\netlogon\images we save an image for each of the sites, London and New York: London.jpg and New York.jpg. These are used in the logon welcome window. There’s also one named default.jpg in case the script can’t work out which site they’re from.
Now this is how the script works:
- When the user logs in the script runs
- The script works out who that user is (their logon name)
- The script queries active directory and looks at their user account
- It looks at the location of the username in active directory and works out what site they belong to, i.e. Sarah is in London
- It uses this information to select the correct welcome screen image
- The welcome screen is then shown with this site-specific image
- It then proceeds to start mapping drives. From the user account it gets the list of groups
- If the group name they’re a member of matches the name “DriveMappings-*” then it looks at the description of the group and uses that information to map the drive
- It does this for each group that the user is a member of
- Once completed it waits 20 seconds and then closes automatically
The benefits of doing it this way?
- You don’t need to edit the logon script to map a new drive for a user. Either add them to an existing group or create a new one with the correct name (DriveMapping-lonserver1-IT for instance) and the description field filled out.
- It’s easy to add and remove users to groups to make sure they get the drives mapped they want
- You can see, just by looking at their group membership what drives they are getting mapped.
- You can create one logon script and give it to everyone in the company!
- It still works when you’re connecting remotely from offsite
Here’s the script:
'================================================================================ '= = '= NAME: logon.vbs = '= = '= DESCRIPTION: Map user drives based on group membership = '= = '= VERSION HISTORY: = '= = '= 1.0 02/06/2011 Stuart Jordan Script rewritten with specific per = '= drive groups only used for mapping = '= = '================================================================================ 'Don't prompt if there are any errors. Users don't want to see these! On Error Resume Next Dim WshNetwork, adsPath,strUser, strMappedDrives Const ADS_READONLY_SERVER = 4 'This is the start path from which we look for the object in LDAP strLDAPPath="dc=acme,dc=corp" strDomainName="ACME" 'This is the drive letter we're using for the user's homedrive strHomedriveDriveLetter="H" 'This just contains a list of drives mapped to be outputted to the screen strMappedDrives="Drives mapped: " 'This is how long the IE window stays open in seconds after all the drives are mapped intCloseWait=20 'This records the time at the start of the script 'We'll check at the end and then output the difference startTime=Timer() 'Quit the logon script if we've logged onto a server if isServer(".") then wscript.quit end if 'Get the Username of the person logging in Set WSHNetwork = WScript.CreateObject("WScript.Network") strUser = "" While strUser = "" strUser = UCase(WSHNetwork.UserName) Wend 'Get the user object from Active Directory. adsPath = "WinNT://" & strDomainName & "/" & strUser Set dso = GetObject("WinNT:") Set objUser = dso.OpenDSObject(adsPath,"","", ADS_READONLY_SERVER) 'Set the logon script image as the one that matches the user's 'Home site, e.g. London. We get this from the OU the user account 'is in. Move the user account and this will change. strLogonScriptImage=getHomeSiteFromUserName(strUser) & ".jpg" 'Create a new internet explorer object set InternetExplorer = new IE 'Create the Internet explorer window InternetExplorer.Initialize 'Write something in the first line below the image InternetExplorer.SetMessageLine1 "You are logged on as " & strDomainName & "\" & strUser 'For each group the user is a member of For Each Group In objUser.groups 'Check to see if this group is a drive mapping group if string_compare("DriveMappings-",Group.Name) then 'If it is then lets process it. 'The description field is of the form A,\\server\share 'where A is the drive letter, server and share the destination 'mapping. Split creates an array using a separator, in this case 'a comma. arrDescField=split(Group.Description,",") 'The first cell contains the drive letter to be mapped strTargetDrive=arrDescField(0) & ":" 'The second cell contains the share to be mapped strTargetPath=arrDescField(1) 'Check to see whether the drive specified is out homedrive letter (i.e. U) if string_compare(strHomedriveDriveLetter,strTargetDrive) then 'Treat U drive as a special case as it's specific to the user logging in InternetExplorer.SetMessageLine3 "Drive " & strTargetDrive & " will be mapped to " & strTargetPath & strUser 'Actually map the drive using the information we collected MapDrive strTargetDrive,strTargetPath & "\" & strUser 'If it's not then process as normal else InternetExplorer.SetMessageLine3 "Drive " & strTargetDrive & " will be mapped to " & strTargetPath 'Actually map the drive using the information we collected MapDrive strTargetDrive,strTargetPath end if InternetExplorer.SetMessageLine2 strMappedDrives end if Next 'Get the time at this point EndTime=Timer() 'Work out the durating between EndTime and StartTime in milliseconds Duration=int((EndTime-StartTime)*1000) 'Output a completion status on line 3. Note the double minus in calculating the duration. 'This is so that we always round the value up so we don't return 0 seconds. Neat huh? InternetExplorer.SetMessageLine3 "Completed in " & -int(-Duration/1000) & " seconds." 'This bit waits for a set period before closing the window automatically do while intCloseWait > 0 'A countdown appears on messageline4. When it reaches 0 seconds the "do while" exits InternetExplorer.SetMessageLine4 "This screen will close automatically in " & intCloseWait & " seconds." wscript.sleep 1000 intCloseWait = intCloseWait - 1 loop 'Now exit IE InternetExplorer.quit 'And clear our InternetExplorer object set InternetExplorer=nothing 'Quit the script wscript.quit '========================================================================== '= Subs and functions 'This class object is used to create and manipulate our Internet Explorer window class IE 'These are our variables but since they're private we can only change them 'by calling PUBLIC functions within the class private m_MessageLine1,m_MessageLine2,m_MessageLine3,m_MessageLine4 private m_IE, m_objDocument 'These public functions allow us to update the m_messageline1 variable which contains the 'output in the IE window on messageline1 public function SetMessageLine1(strNewMessageText) m_MessageLine1=strNewMessageText UpdateMessageLine1() end function public function SetMessageLine2(strNewMessageText) m_MessageLine2=strNewMessageText UpdateMessageLine2() end function public function SetMessageLine3(strNewMessageText) m_MessageLine3=strNewMessageText UpdateMessageLine3() end function public function SetMessageLine4(strNewMessageText) m_MessageLine4=strNewMessageText UpdateMessageLine4() end function 'Private functions to update the variables private function UpdateMessageLine1() m_IE.document.all.MessageLine1.innerHTML=m_MessageLine1 end function private function UpdateMessageLine2() m_IE.document.all.MessageLine2.innerHTML=m_MessageLine2 end function private function UpdateMessageLine3() m_IE.document.all.MessageLine3.innerHTML=m_MessageLine3 end function private function UpdateMessageLine4() m_IE.document.all.MessageLine4.innerHTML=m_MessageLine4 end function 'This is a public sub that creates the IE window inside which the HTML page will be shown public Sub initialize() On Error Resume Next Set m_IE = CreateObject("InternetExplorer.Application") With m_IE 'These are the attributes of the window created by IE. .navigate "about:blank" .visible= True .resizable=0 'Height of the IE window .height=543 'Width of the IE window .width=620 .toolbar=0 .statusbar=0 .menubar=0 .visible=1 'Position these number of pixels in from the top left of the screen .Left=20 .Top=20 .document.parentWindow.opener = "me" End With Do while m_IE.Busy ' wait for page to load Wscript.Sleep 100 Loop 'Call the private sub to load the html content in the window loadHTML() End Sub 'This sub forms the HTML page shown in the IE window shown during logon private sub loadHTML() Set m_objDocument = m_IE.document m_objDocument.Open m_objDocument.Writeln "<html>" m_objDocument.Writeln "<body SCROLL='no' onclick=self.close() leftmargin='0' marginheight='0' marginwidth='0' topmargin='0'>" m_objDocument.Writeln "<table width='620' border='0' cellspacing='0' cellpadding='0'>" m_objDocument.Writeln "<tr>" 'This is the image that's loaded in the IE window. Note that it loads an image that is particular to the site that 'the user account is found in (or more strictly the branch OU) m_objDocument.Writeln "<td><img src='file://acme.corp/netlogon/images/" & strLogonScriptImage & "' alt='' height='457' width='620' border='0'></td>" m_objDocument.Writeln "</tr>" m_objDocument.Writeln "</table>" m_objDocument.Writeln "<table width='620' border='0' cellspacing='0' cellpadding='0'>" 'These are the message lines referenced above when were outputting text to the IE window m_objDocument.Writeln "<tr><td><font face='Verdana' size='1' ID='MessageLine1'></font></td></tr>" m_objDocument.Writeln "<tr><td><font face='Verdana' size='1' ID='MessageLine2'></font></td></tr>" m_objDocument.Writeln "<tr><td><font face='Verdana' size='1' ID='MessageLine3'></font></td></tr>" m_objDocument.Writeln "<tr><td><font face='Verdana' color='blue' size='1' ID='MessageLine4'></font></td></tr>" m_objDocument.Writeln "</table>" m_objDocument.Writeln "</html>" m_objDocument.Close end sub public sub quit() m_IE.quit end sub end class 'Actually maps the network drives Sub MapDrive(strDrive,strShare) On Error Resume Next WSHNetwork.MapNetworkDrive strDrive, strShare If Err.Number Then 'wscript.echo Err.Number & " so removing drive first" err.clear WSHNetwork.RemoveNetworkDrive strDrive WSHNetwork.MapNetworkDrive strDrive, strShare If Err.Number Then 'wscript.echo "Removing drive still failed with error " & Err.Number & ":" & Err.Description end if End If strMappedDrives = strMappedDrives & strDrive & " " End Sub 'Checks to see if the machine we're running the logon script on is a "server" because 'we don't really want to map drives automatically on servers. function isServer(strtmpComputer) boolReturn=0 on error resume next ' WMI connection to Root CIM Set objWMIService = GetObject("winmgmts:\\" & strtmpComputer & "\root\cimv2") 'Get list of items (there is only one in this case) Set colItems = objWMIService.ExecQuery("Select DomainRole from Win32_ComputerSystem") for each objComputer in colItems 'This returns a number depending on the role in the domain intRole=objComputer.DomainRole '0 (0x0) standalone Workstation '1 (0x1) Member Workstation '2 (0x2) Standalone Server '3 (0x3) Member Server '4 (0x4) Backup Domain Controller '5 (0x5) Primary Domain Controller next 'If the system is 2,3,4 or 5 then it is a server. Otherwise we'll assume it's a workstation. select case intRole case 2,3,4,5 boolReturn=1 case else boolreturn=0 end select isServer = boolReturn end function 'This is used to check is a string is found inside another 'It's a bit like strcmp but works for regex and where 'the expression is found at the start of the string. private function string_compare(expression,targetstring) set oreg= new regexp oReg.pattern=expression oReg.IgnoreCase = TRUE if ("" = expression OR "" = targetstring) then boolSearchResult=0 end if if oReg.test (targetstring) then boolSearchResult=1 else boolSearchResult=0 end if string_compare=boolSearchResult end function 'Do a LDAP lookup to find the location of the user account 'in active directory. Used to find the home site of the 'particular user. function getCanonicalNameFromUsername(strSamAccountName) dim strReturn,arrCN strSamAccountName=LCase(strSamAccountName) 'Setup for the LDAP connection Set objConnection = CreateObject("ADODB.Connection") objConnection.Open "Provider=ADsDSOObject;" Set objCommand = CreateObject("ADODB.Command") objCommand.ActiveConnection = objConnection 'Let's find the person strType="User" objCommand.CommandText = "<LDAP://" & strLDAPPath & ">;(&(objectCategory=" & strType & ")" & _ "(samAccountName=" & strSamAccountName & "));canonicalName;subtree" 'Run the query Set objRecordSet = objCommand.Execute if objRecordset.RecordCount = 1 then 'For some reason the canonicalName object is returned as an array 'with the first array cell containing the actual canonicalName arrCN=objRecordset.Fields("canonicalName") 'Ubound is the reference of the last cell in the array (zero in this case) 'but added for completeness strReturn=arrCN(Ubound(arrCN)) 'wscript.echo "Found " & strSamAccountName & " at " & strReturn else wscript.echo "Failed to find " & strSamAccountName end if getCanonicalNameFromUsername=strReturn end function 'Uses the canonicalName to work out the home site of the user so 'that the correct image can be shown in the logon script 'window function getHomeSiteFromUserName(strtmpUsername) dim strReturn 'The site name is contained in the third set of / slashes 'acme.corp/Europe/London/OU1/OU2/username 'So let's split the canonicalName using the /'s with an 'level per cell arrCN=split(getCanonicalNameFromUsername(strtmpUsername),"/") if Ubound(arrCN) > 1 then strReturn=arrCN(2) else 'Let's default to default if we can't work out what site the 'user account is in strReturn="default" end if getHomeSiteFromUserName=strReturn end function