Per User Printer Assignment Logon Script in VMware View
One of my customers has been trying to figure out how they
are going to deal with printer assignment in their View environment. In truly stateless desktops (without Persona
Management, even), you have to completely depend on external printer
assignments, either through a logon script or through Active Directory. I prototyped both solutions for this
customer. In order to minimize our logon
times, we preinstalled our printer drivers in the parent image (for both
tests).
We found that a normal Windows 7 logon onto a stateless
desktop (where it’s always the first logon and so has to create a local profile
and apply group policy) takes about 20 seconds from the time the user hits
“connect”. Using Group Policy to
distribute printers increased that logon time to about 45 seconds… which was a
bit too much for my liking. Using a
logon script, on the other hand, does not delay the logon process (even if the
printers are not actually present immediately upon logon), and so this is the
direction that I recommended that the customer should go.
I’ve said it before and here it is again: I’m not a Windows
administrator. That said, I’ve seen my
share of logon scripts. Many of them
have basically been a big Case structure, where it checks each user’s group
membership against each case and, if your group membership matches the case,
you get the appropriate printer installed.
This certainly works, but it scales poorly. As you get more and more printers (a single
print server at this customer has about 100 printers on it), your case structure gets
larger and larger, which means that the script gets slower and slower. Another drawback is that when printers are
added or removed from the environment, the script must be updated to reflect
those changes. That introduces the
potential for human error to break the entire script, every time a printer is
changed. It also requires that one of
the customer’s scripting gurus (let’s be honest, if they’re not a guru, they
don’t want to touch such a high visibility script) make the time to update it
whenever the otherwise basic task of adding a printer occurs. All in all, a bad solution.
I know that I’m not particularly insightful and so there’s
no way that I’m the first person to identify those problems. What I may lack in insight, I make up in
google search syntax. Some poking around
uncovered a printer deployment logon script by Robert Gransbury, on Microsoft’s technet.
His approach is awesome, as it does away with that Case structure and
instead allows admins to add/remove printers by directly manipulating Active
Directory groups. Here’s how it works:
First, you need to prepare your Active Directory a little
bit. Create an OU called “Printer Paths”
(or at least, one with that string in its name). Add a new Security Group within that OU – for
organization’s sake, I named that group after one of the printers in the
environment. In that group’s
description, enter the UNC Path that the client would use to access that
printer (so the classic \\Print-Server\Server-Share-Name
syntax). Next, add an OU (not a Child of
the prior one!) called “Printer Defaults” and create another security group,
this time named “Default-<printer name>” with the same UNC Path
description.
What Robert’s script basically does is check all of the
groups of which the user is a member. If
those groups are not in the “Printer Paths” or “Printer Defaults” OUs, it
ignores them. If the groups are in the
“Printer Paths” OU, it adds a printer, using that group’s description as the
printer string. It then does the same
thing to find that user’s default printer, based on their membership of a
“Printer Defaults” group. It’s simple
and genius. It means that the script
does not need to be changed when printers are changed (or print servers, for
that matter). It means that administrators
can use a GUI (although, admittedly, a very basic one) to manage their
environment’s printers. It also means
that the script doesn’t have to compare every one of a user’s groups against a
giant Case structure of every printer in the organization. Instead, the group
has the printer’s information inbuilt.
Nice.
Of course, Robert’s script depends on the assumption that a
user is a direct member of a “Printer Paths” group (or, in practice, several of
them). It’s not a bad assumption, but my
customer has printer zones defined where every user in a given zone will be
given access to a whole list of printers.
As such, I modified Robert’s script to support that scenario. I just added another bit of logic into the
group check. If the group is part of the
“Printer Groups” OU, it checks to see what groups that group is a member of and
adds those groups’ descriptions as printers (since my Security Groups in the
“Printer Groups” OU are all members of one or more “Printer Paths” groups).
Anyway, the prototype for this logon script has been
successful, and with the printer drivers preinstalled in the desktop parent,
they install nice and swiftly. I figured
that I’d go ahead and post my modified version of Robert’s script, in case it
proves useful to anyone. As always, this
is a “use at your own risk” sort of affair (and please beware of unintended line breaks due to blog width issues!).
I’ve got it in place and it’s working for me, but I have no idea about
the intricacies of your environment and can offer no guarantee that this script
will do anything useful or beneficial for you.
'===========================================
'Dynamic Printer Deployment based on AD Group Membership
'Author: Robert Gransbury
'Source: Microsoft Technet (http://gallery.technet.microsoft.com/scriptcenter/bf160908-93e3-484c-944f-1c95004c5498)
'Modified By: Jason Coleman, 3/27/2012
'This script adds printers upon user login based on their AD Group membership.
'It looks for groups in the "Printer Paths" OU and attempts to install the printers in those groups' descriptions.
'It also looks for groups in the "Printer Groups" OU and installs printers based on what "Printer Paths" groups those groups are in.
'It assigns default printers by membership in the "Printer Defaults" OU groups. If an account is a member of more than one "Printer Defaults" group, unexpected results may occur.
'===========================================
on error resume next
Dim objADSystemInfo, objUser, objMemberOf, objChildMemberOf, objGroup, objChildGroup, objGroupEnum, objNetwork, objShell, objPrinter
Dim i, bTroubleFlag
Set objShell = CreateObject("WScript.Shell")
Set objNetwork = CreateObject("Wscript.Network")
WScript.sleep 25000
'Get current user info from active directory
Set objADSystemInfo = CreateObject("ADSystemInfo")
'bind to current user in active directory
Set objUser = GetObject("LDAP://" & objADSystemInfo.UserName)
'Tests to see if verbose messages should be displayed or not by looking at the user's description
if objuser.description = "printer.trouble" then
bTroubleFlag = true
msgbox "Troubleshooting Printer Logon Script"
end if
'Deletes any currently installed network Printers
Set objPrinter = objNetwork.EnumPrinterConnections
'Test to see if we have any printers mapped
If objPrinter.Count > 0 Then
'The Printer array is Printer name, printer path that is why it is step 2
for i=1 to objPrinter.Count Step 2
'test to make sure it is a network printer
if instr(objPrinter.Item(i),"\\") <> 0 then
if bTroubleFlag then
msgbox "Deleting:" & vbcrlf & objPrinter.Item(i)
end if
objNetwork.RemovePrinterConnection objPrinter.Item(i),true,true
end if
next
end if
'Get an array of group names that the user is a member of
objMemberOf = objUser.MemberOf
'Adds appropriate printers based on the user's group membership and sets default printer
for Each objGroup in objMemberOf
'Test to see if it is a printer group. all printer groups should be in this OU.
if (instr(objGroup,"OU=Printer Groups") <> 0) then
'If the group is in the Printer Groups OU, find that Group's parents and pass them to AddPrinter function
if bTroubleFlag then
msgbox "Found Printer Group: " & objGroup
end if
objChildMemberOf = (GetObject("LDAP://" & objGroup)).MemberOf
for Each objChildGroup in objChildMemberOf
if (objChildGroup = "") then
'if it is blank, it's because objChildMemberOf is a string because there was only 1 group that objGroup is a member of and so it did not return an array as expected
AddPrinter objChildMemberOf
else
AddPrinter objChildGroup
end if
next
else
'If the group is not in the Printer Groups OU, pass it to AddPrinter function for analysis
if bTroubleFlag then
msgbox "Found non Printer Group: " & objGroup
end if
AddPrinter objGroup
end if
next
'Sets default printer based on group membership within the Printer Defaults OU
for Each objGroup in objMemberOf
if (instr(objGroup,"OU=Printer Defaults") <> 0) then
set objGroupEnum = GetObject("LDAP://" & objGroup)
if bTroubleFlag then
msgbox "Setting Default:" & vbcrlf & "[" & objGroupEnum.name & "]" & vbcrlf & objGroupEnum.description
end if
objNetwork.SetDefaultPrinter objGroupEnum.description
set objGroupEnum = nothing
end if
next
if bTroubleFlag then
msgbox "Printer Logon Script Finished"
end if
Function AddPrinter(objIn)
'Function expects to be passed an LDAP string for a group
'Validates that an object is in the correct OU and attempt to find a UNC path in the description to install the printer.
if (instr(objIn,"OU=Printer Paths") <> 0) then
'Bind to the group to get is description. The description contain the path to the printer
set objGroupEnum = GetObject("LDAP://" & objIn)
if bTroubleFlag then
msgbox "Adding:" & vbcrlf & "[" & objGroupEnum.name & "]" & vbcrlf & objGroupEnum.description
end if
objNetwork.AddWindowsPrinterConnection objGroupEnum.description
set objGroupEnum = nothing
end if
end Function
Comments
Post a Comment
Sorry guys, I've been getting a lot of spam recently, so I've had to turn on comment moderation. I'll do my best to moderate them swiftly after they're submitted,