Monday, July 01, 2013

Streamlining The Logon Script

I am a firm believer in using groups to define rights and access, some people go over the top on this because they lack logical thinking, or the time to determine the logic required, but rights and access should ALWAYS (read as 'as much as possible) be handled by group membership.

To that end whether through the GPO, or the NETLOGON method of calling a logon script, group membership can determine what resources you connect to (i.e. printers and drive letters), as archaic as the drive letter is, it is still a logical manner for people to find their stuff. This topic may get covered by my other blog "My Life in I.T.," (http://bankingonthechaos.wordpress.com/) from a higher altitude, this blog covers what I believe is a streamlined logon script that can be implemented with simplicity and should run fairly quickly.

@echo off
:: net time /setsntp:192.168.10.99
:: net time /domain:mydomain /set /yes

cscript [full UNC path to script]LoginScript.vbs

Firstly, I tried to conform to the ideals of my current employer when I started this process, so the use of a command (.cmd) batch file is simply fluff, the commands, all two of them, might be best executed through calls from VBScript, but frankly they're remarked out ('::') anyway. Pointless lines in a pointless file. I don't have the rights to a test environment at present so I simply mimicked the NETLOGON methodology because the "powers that be" don't like to share information.

So, we can ignore the .cmd file and call cscript.exe, the command-line variant of wscript.exe, to execute the logon script.

The Script: LoginScript.vbs
' ====== Meatballs, the Logon Script in VBScript
' ====== By: C. Stevens, April 12th, 2013
' ====== Last Modified: 20130412 - evens
On Error Resume Next
set objShell = WScript.CreateObject( "WScript.Shell" )
Set objNetwork = CreateObject("WScript.Network")
Dim groupListD
Meatballs ' --------------- This is the main routine
Set objShell = Nothing
Set objNetwork = Nothing
WScript.Quit ' -------------------------------- END
This is simply clean, a good start, and a defined variable. We now define the routine I call Spaghetti, it's the substance of the meal, er, script. It's the functional part of making things easy to use, and re-use. This is what get's customized to your needs.
Sub Spaghetti
wscript.echo "-------------------------------------------"
wscript.echo "Making drives/printer connections..."
' ===== LOGIN MAPPINGS: START
' ---- DO NOT MODIFY ABOVE THIS LINE ----
 ~ STUFF ~
' ---- DO NOT MODIFY BELOW THIS LINE ----
' ===== LOGIN MAPPINGS: END
End Sub
We'll get to the "Stuff" later. This is like adding Parmesan Cheese to the meal, it's last and added to taste. What any great plate of Spaghetti needs is Meatballs! In reality you start with great meatballs, the pasta is just an excuse to have them, so you may notice we actually start by calling Meatballs, when then adds Spaghetti, or your past of choice.

Sub Meatballs
 ADSPath = EnvString("userdomain") & "/" & EnvString("username")
 Set userPath = GetObject("WinNT://" & ADSPath & ",user")
 wscript.echo ADSPath & " group membership:"
 wscript.echo "-------------------------------------------"
 If IsEmpty(groupListD) then
  Set groupListD = CreateObject("Scripting.Dictionary")
  groupListD.CompareMode = TextCompare
  For Each listGroup in userPath.Groups
   groupListD.Add listGroup.Name, "-"
   wscript.echo " " & listGroup.Name, "-"
  Next
 End if

 Spaghetti ' calls the Spaghetti routine. 

End Sub
 Meatballs' purpose is to retrieve the list of groups the user has from AD, build the list into a dictionary item called groupListD, then call the Spaghetti routine to work out the Stuff we need to connect for the user. Before we can top this meal off with cheese, we need some helpers in place, subroutines and functions that we'll use in Spaghetti to attach to the resources.

' *****************************************************
' This function returns a particular environment variable's value.
' for example, if you use EnvString("username"), it would return
' the value of %username%.
Function EnvString(variable)
  variable = "%" & variable & "%"
  EnvString = objShell.ExpandEnvironmentStrings(variable)
End Function

' *****************************************************
' This function connects a particular drive letter to the 
' specified UNC path. If the drive letter is already 
' connected to another UNC connection it will disconnect it
' and then try again using the function 'driveAlreadyConnected'
Sub ConnectDrive(driveLetter, UNCpath)
 ' Map network drive script
 driveLetter=left(driveLetter,1) & ":"
 if driveAlreadyConnected(driveLetter) then
  'wscript.echo "Already Connected to " & driveLetter
  objNetwork.RemoveNetworkDrive driveLetter
 end if
 objNetwork.MapNetworkDrive driveLetter, UNCPath, True
 if driveAlreadyConnected(driveLetter) then
  wscript.echo driveLetter & "... OK"
 else
  wscript.echo driveLetter & "... Failed!"
 end if
end Sub

' *****************************************************
' This function will check if the drive letter is in use and
' attempt to disconnect it, allowing for the desired connection
' be replace it. It is used by the ConnectDrive routine.
Function driveAlreadyConnected(strDL)
 Set CheckDrive = objNetwork.EnumNetworkDrives()
 driveAlreadyConnected = False
 For intDrive = 0 To CheckDrive.Count - 1 Step 2
  If CheckDrive.Item(intDrive) = strDL Then
   driveAlreadyConnected = True
  end if
 Next 
end Function
Don't sweat the small stuff...

The Stuff we ignored earlier fits between those two lines:
' ---- DO NOT MODIFY ABOVE THIS LINE ----
 and
' ---- DO NOT MODIFY BELOW THIS LINE ---- 
This is where the logic your organisation needs, your group memberships for the current user, will determine what resources they will be connected to. In the following example the group membership is by location. Frankly you could determine the location by IP Address if you have good information to go by, but in this example we'll use group membership. For the user's that are members of the Halifax group this is their code.
' HALIFAX
If CBool(groupListD.Exists(" Halifax Users")) then
 wscript.echo "Connecting Drive (HAL) G:, S:, KL..."
 ConnectDrive "G:", "\\halfile.mydomain.com\Groups"
 ConnectDrive "S:", "\\halfile.mydomain.com\Shared"
 ConnectDrive "K:", "\\FS01.mydomain.com\Shared"
 ConnectDrive "L:", "\\FS01.mydomain.com\Group"
end if
You can see how it works, it is downright simple. the wscript.echo is unnecessary and more for you and I reading the code, or during testing. The IF... THEN is the key to determining membership. It reads as, "if the groupListD list created earlier using Meatballs, contains the group "Halifax Users", then connect these drives."

You can add sections like this for each location, and it the beauty of Meatballs is it only checks with AD once, to grab the list of groups, the rest is simply a searching a local variable array (dictionary object).
' MONTREAL
If CBool(groupListD.Exists(" Montreal Users")) then
 wscript.echo "Connecting Drive (MTL) G:, S:, K:, L:, N:..."
 ConnectDrive "G:", "\\mtlfile.mydomain.com\Group"
 ConnectDrive "S:", "\\mtlfile.mydomain.com\Shared"
 ConnectDrive "K:", "\\FS01.mydomain.com\Shared"
 ConnectDrive "L:", "\\FS01.mydomain.com\Group"
 ConnectDrive "N:", "\\halfile.mydomain.com\Shared"
end if

' TORONTO (320 Bay Street Users)
If CBool(groupListD.Exists(" 320 Bay Users")) then
 wscript.echo "Connecting Drive (TOR) G: and S:..."
 ConnectDrive "G:", "\\FS01\Group"
 ConnectDrive "S:", "\\FS01\Shared"
end if
If you have specialty connections for certain roles that are not location based, but still based on group membership, add them in the same manner:
' ------ SPECIAL Connections
' ------ Human Resources (I: Drive)
If CBool(groupListD.Exists("Human Resources")) then
 wscript.echo "Connecting Drive H:..."
 ConnectDrive "H:", "\\FS01\HR"
end if
' --------------------------------------------- SPECIAL PROJECTS(M: Drive)
If CBool(groupListD.Exists("SPECIALProjects RW")) then
 wscript.echo "Connecting Drive M:..."
 ConnectDrive "M:", "\\FS01\CSTSpecial"
end if
If CBool(groupListD.Exists("SPECIALProjects RO")) then
 wscript.echo "Connecting Drive M:..."
 ConnectDrive "M:", "\\FS01\CSTSpecial"
end if

You can even add printers, though I do hope you have print servers in place in this case. You can make them location based selecting a local print server versus a remote one, or even the printer that's local to the site. Take some time to determine location based on other information, perhaps even the logon server and you can ensure the local printer is connected rather than one back home.

' ------ Connecting Printers -------------
' Example... These connections can take a while and slow down 
' the "logon" process.
' Note: This will not duplicate printers for the user.
objNetwork.AddWindowsPrinterConnection "\\PrintServer01\torORANGE01P"
objNetwork.SetDefaultPrinter "\\PrintServer01\torORANGE01P"

Anyway, that's my logon script in VB based on group memberships

No comments:

There is no individual ownership when you are part of a team, it's the sum of the parts that makes you the RESILIENT team you need to be.