Discussion:
Logon Script to map network drives based on users memberships in security groups
(too old to reply)
RBoyle
2009-10-16 21:41:47 UTC
Permalink
I need to find a real simple way to log users using logon scripts to
my windows 2003 domain, attaching them to my users AD account and
relating the script to what security group my users are in.

For instance: if my user is in the "IT$ Management" security group he
should have the following happen to him:

net use x:/ /delete
net use y:/ /delete
net use z:/ /delete

net use x:\\netwincp\myFiles1\directoryName1
net use y:\\netwincp\myFiles2\directoryName2
net use z:\\netwincp\myFiles3\directoryNmae3

** Based off of being in the specific security groups that i'm going
to mapout I want to elimnate whatever network drive that was
previously mapped and then re-map the dirves i have laid ou **

Can someone give me a quick example of how to write one of these out
so I can put a test one in action for my domain.

Thanks guys - much appriciated in advance.

Ryan J. Boyle
Richard Mueller [MVP]
2009-10-16 22:39:14 UTC
Permalink
Post by RBoyle
I need to find a real simple way to log users using logon scripts to
my windows 2003 domain, attaching them to my users AD account and
relating the script to what security group my users are in.
For instance: if my user is in the "IT$ Management" security group he
net use x:/ /delete
net use y:/ /delete
net use z:/ /delete
net use x:\\netwincp\myFiles1\directoryName1
net use y:\\netwincp\myFiles2\directoryName2
net use z:\\netwincp\myFiles3\directoryNmae3
** Based off of being in the specific security groups that i'm going
to mapout I want to elimnate whatever network drive that was
previously mapped and then re-map the dirves i have laid ou **
Can someone give me a quick example of how to write one of these out
so I can put a test one in action for my domain.
Thanks guys - much appriciated in advance.
A batch file program cannot use ADSI to retrieve information from Active
Directory, so it cannot check group membership, unless you use a third party
tool like KiXtart or the IfMember utility that was in the Windows NT
resource kit.

A VBScript program can map network drives and check group membership. Some
recommendations in this link:

http://www.rlmueller.net/LogonScriptGuidelines.htm

From the link above, the simplest script to map drives according to group
membership could be similar to:
==========
Option Explicit
Dim objNetwork, objSysInfo, strUserDN, strAdsPath, objGroup

Set objNetwork = CreateObject("Wscript.Network")

' Retrieve user DN.
Set objSysInfo = CreateObject("ADSystemInfo")
strUserDN = objSysInfo.UserName
strAdsPath = "LDAP://" & strUserDN

' Bind to group object.
Set objGroup = GetObject("LDAP://TestGroup,ou=Sales,dc=MyDomain,dc=com")

' Test for membership in the group.
If (objGroup.IsMember(strAdsPath) = True) Then
' Map a drive.
objNetwork.MapNetworkDrive "K:", "\\MyServer\GroupShare"
End If
==========
You should use the full Distinguished Name of the group. You would repeat
the last few steps for each group to be checked. If there are many groups to
check, the link above shows some more efficient methods to make the logon
script faster.

If you want to handle cases where the drive letter is already mapped, due to
persistent connections, you can use the MapDrive function near the bottom of
the page in the above link. Otherwise, you can use code to trap the possible
error, remove the existing mapping, and try again. For example (in part):
=========
' Test for membership in the group.
If (objGroup.IsMember(strAdsPath) = True) Then
' Map a drive. Trap error if letter in use.
On Error Resume Next
objNetwork.MapNetworkDrive "K:", "\\MyServer\GroupShare"
If (Err.Number <> 0) Then
On Error GoTo 0
' Remove existing mapping.
objNetwork.RemoveNetworkDrive "K:", True, True
' Try again.
objNetwork.MapNetworkDrive "K:", "\\MyServer\GroupShare"
End If
On Error GoTo 0
End If
======
Tips to setup logon scripts and a discussion of limitations see this link:

http://www.rlmueller.net/LogonScriptFAQ.htm

I hope this helps.
--
Richard Mueller
MVP Directory Services
Hilltop Lab - http://www.rlmueller.net
--
Cynic
2009-10-17 12:43:57 UTC
Permalink
Post by RBoyle
I need to find a real simple way to log users using logon scripts to
my windows 2003 domain, attaching them to my users AD account and
relating the script to what security group my users are in.
You could also use the WinNT provider to retrieve a user's group
membership and use a loop and select case block to assign drives. This
requires less code and is a little easier to extend in the future:

strUserID = objNetwork.UserName

objUser = GetObject("WinNT://"&strUserID&",user")

for each group in objUser.Groups
select case group
case "groupName"
' map drive here
end select
next

The shortfall of this approach is that it doesn't use the
distinguished name of the groups, as Ryan suggests. If you have groups
in different OUs that have the same canonical name, it won't work
properly. You could do the same thing using ADSI, as Ryan suggests:

set objUser = GetObject("LDAP://"&strUserDN)

for each group in objUser.memberOf
select case group
case "groupDN"
' map drive here
end select
next
Richard Mueller [MVP]
2009-10-17 17:00:20 UTC
Permalink
Post by RBoyle
I need to find a real simple way to log users using logon scripts to
my windows 2003 domain, attaching them to my users AD account and
relating the script to what security group my users are in.
You could also use the WinNT provider to retrieve a user's group
membership and use a loop and select case block to assign drives. This
requires less code and is a little easier to extend in the future:

strUserID = objNetwork.UserName

objUser = GetObject("WinNT://"&strUserID&",user")

for each group in objUser.Groups
select case group
case "groupName"
' map drive here
end select
next

The shortfall of this approach is that it doesn't use the
distinguished name of the groups, as Ryan suggests. If you have groups
in different OUs that have the same canonical name, it won't work
properly. You could do the same thing using ADSI, as Ryan suggests:

set objUser = GetObject("LDAP://"&strUserDN)

for each group in objUser.memberOf
select case group
case "groupDN"
' map drive here
end select
next
------------

I agree that the WinNT provider is easier to use. Don't take offense by what
follows, but I want to express my "philosophy" on the matter. Consider this
a matter of style if you like.

Logon scripts are important for the user experience. The code may be in use
for years. While the code is easier to develop (and perhaps read) when you
use the WinNT provider, it has the following drawbacks.

1. It is slower.
2. It only supports features available in NT networks. Many fewer attributes
are exposed.
3. It is blind to the hierarchy of AD. It does not recognize OU's or nested
domain groups.
4. It does not recognize computer group membership (in case you want to map
printers accordingly).

The first point is probably the only relevant one. Using the WinNT provider
is convenient for the coder, but results in slower code for the users.
Anyone coding logon scripts should be able to determine the Distinguished
Names of objects. Personally, I have found that shorter code is not
necessarily faster, more reliable, or easier to maintain. I like lots of
comments, white space, and indenting in my scripts so they can be understood
by anyone reading them.

Finally, I need to point out that the following will raise an error if the
user is not a member of at least 3 groups (2 groups if you use the WinNT
provider):

For Each objGroup In objUser.memberOf

The "For Each" statement expects a variant array, but objUser.memberOf is
Empty if the memberOf collection has no values, and is datatype "String" if
there is only one value. The memberOf attribute exposed by the LDAP provider
never includes the "primary" group of the user. The statement will raise an
error unless memberOf has at least two values (group DN's), not counting the
"primary" group (usually "Domain Users").

There are several ways to handle this. One is to test the datatype of
objUser.memberOf with the TypeName function. For example:
===========
arrGroups = objUser.memberOf
If IsEmpty(arrGroups) Then
' Member of no groups (except "primary"), do nothing.
ElseIf (TypeName(arrGroups) = "String") Then
' Member of one group (besides "primary").
Select Case arrGroups
Case "<group DN>"
' Map drives and/or printers.
End Select
Else
' arrGroups is Variant().
For Each strGroup In arrGroups
Select Case strGroup
Case "<group DN>"
' Map drives and/or printers.
End Select
Next
End If
=======
Notice the "Select Case" structure is repeated. Kind of ugly, but does not
slow down the script at all. Still, I would prefer using the GetEx method to
retrieve the memberOf collection, although this requires trapping the error
raised if there are no values. For example:
========
On Error Resume Next
arrGroups = objUser.GetEx("memberOf")
If (Err.Number <> 0) Then
On Error GoTo 0
' Member of no groups (except "primary"), do nothing.
Else
On Error GoTo 0
' Member of one or more groups. arrGroups is Variant()
' even if there is only one value (because GetEx was used).
For Each strGroup In arrGroups

Select Case strGroup
Case "<group DN>"
' Map drives and/or printers.
End Select
Next
End If
=======
See this link for more discussion:

http://www.rlmueller.net/MemberOf.htm

Finally, the code looks cleaner if you use the Groups method of the user
object:
========
For Each objGroup In objUser.Groups
Select Case objGroup.sAMAccountName
Case "<group pre-Windows 2000 name>"
' Map drives and/or printers.
End Select
Next
=========
I used the sAMAccountName attribute of the group object, since I assume the
LDAP provider. The point, however, is that the provider must bind to all of
the groups the user is a direct member of. This may not matter much, but if
the user is a member of many groups it will slow the script down. It would
be more efficient to retrieve the memberOf collection of DN values. The
slowest operation in logon scripts is binding to objects over the network in
Active Directory. Binding to local objects, like wshNetwork or the
dictionary object, is much faster.
--
Richard Mueller
MVP Directory Services
Hilltop Lab - http://www.rlmueller.net
--
RBoyle
2009-10-17 22:04:20 UTC
Permalink
On Oct 17, 1:00 pm, "Richard Mueller [MVP]" <rlmueller-
Post by Cynic
Post by RBoyle
I need to find a real simple way to log users using logon scripts to
my windows 2003 domain, attaching them to my users AD account and
relating the script to what security group my users are in.
You could also use the WinNT provider to retrieve a user's group
membership and use a loop and select case block to assign drives. This
strUserID = objNetwork.UserName
objUser = GetObject("WinNT://"&strUserID&",user")
for each group in objUser.Groups
  select case group
    case "groupName"
       ' map drive here
  end select
next
The shortfall of this approach is that it doesn't use the
distinguished name of the groups, as Ryan suggests. If you have groups
in different OUs that have the same canonical name, it won't work
set objUser = GetObject("LDAP://"&strUserDN)
for each group in objUser.memberOf
  select case group
    case "groupDN"
    ' map drive here
  end select
next
------------
I agree that the WinNT provider is easier to use. Don't take offense by what
follows, but I want to express my "philosophy" on the matter. Consider this
a matter of style if you like.
Logon scripts are important for the user experience. The code may be in use
for years. While the code is easier to develop (and perhaps read) when you
use the WinNT provider, it has the following drawbacks.
1. It is slower.
2. It only supports features available in NT networks. Many fewer attributes
are exposed.
3. It is blind to the hierarchy of AD. It does not recognize OU's or nested
domain groups.
4. It does not recognize computer group membership (in case you want to map
printers accordingly).
The first point is probably the only relevant one. Using the WinNT provider
is convenient for the coder, but results in slower code for the users.
Anyone coding logon scripts should be able to determine the Distinguished
Names of objects. Personally, I have found that shorter code is not
necessarily faster, more reliable, or easier to maintain. I like lots of
comments, white space, and indenting in my scripts so they can be understood
by anyone reading them.
Finally, I need to point out that the following will raise an error if the
user is not a member of at least 3 groups (2 groups if you use the WinNT
For Each objGroup In objUser.memberOf
The "For Each" statement expects a variant array, but objUser.memberOf is
Empty if the memberOf collection has no values, and is datatype "String" if
there is only one value. The memberOf attribute exposed by the LDAP provider
never includes the "primary" group of the user. The statement will raise an
error unless memberOf has at least two values (group DN's), not counting the
"primary" group (usually "Domain Users").
There are several ways to handle this. One is to test the datatype of
===========
arrGroups = objUser.memberOf
If IsEmpty(arrGroups) Then
    ' Member of no groups (except "primary"), do nothing.
ElseIf (TypeName(arrGroups) = "String") Then
    ' Member of one group (besides "primary").
    Select Case arrGroups
        Case "<group DN>"
            ' Map drives and/or printers.
    End Select
Else
    ' arrGroups is Variant().
    For Each strGroup In arrGroups
        Select Case strGroup
            Case "<group DN>"
                ' Map drives and/or printers.
        End Select
    Next
End If
=======
Notice the "Select Case" structure is repeated. Kind of ugly, but does not
slow down the script at all. Still, I would prefer using the GetEx method to
retrieve the memberOf collection, although this requires trapping the error
========
On Error Resume Next
arrGroups = objUser.GetEx("memberOf")
If (Err.Number <> 0) Then
    On Error GoTo 0
    ' Member of no groups (except "primary"), do nothing.
Else
    On Error GoTo 0
    ' Member of one or more groups. arrGroups is Variant()
    ' even if there is only one value (because GetEx was used).
    For Each strGroup In arrGroups
        Select Case strGroup
            Case "<group DN>"
                ' Map drives and/or printers.
        End Select
    Next
End If
=======
http://www.rlmueller.net/MemberOf.htm
Finally, the code looks cleaner if you use the Groups method of the user
========
For Each objGroup In objUser.Groups
    Select Case objGroup.sAMAccountName
        Case "<group pre-Windows 2000 name>"
            ' Map drives and/or printers.
    End Select
Next
=========
I used the sAMAccountName attribute of the group object, since I assume the
LDAP provider. The point, however, is that the provider must bind to all of
the groups the user is a direct member of. This may not matter much, but if
the user is a member of many groups it will slow the script down. It would
be more efficient to retrieve the memberOf collection of DN values. The
slowest operation in logon scripts is binding to objects over the network in
Active Directory. Binding to local objects, like wshNetwork or the
dictionary object, is much faster.
--
Richard Mueller
MVP Directory Services
Hilltop Lab -http://www.rlmueller.net
--
Richard / Cynic thanks for your responses - much appricated for the
quick / detailed feedback:

Richard just a quick question on a script i'm going to attempt on
myself, then my co-workers and if that all works out then I'm going to
push it out to the rest of the organization:

I looked at what you had written in your first response -- the
following script;

----------------

Option Explicit
Dim objNetwork, objSysInfo, strUserDN, strAdsPath, objGroup


Set objNetwork = CreateObject("Wscript.Network")


' Retrieve user DN.
Set objSysInfo = CreateObject("ADSystemInfo")
strUserDN = objSysInfo.UserName
strAdsPath = "LDAP://" & strUserDN


' Bind to group object.
' Would this set objGroup & then test it to see if I was in the IT$
Department
' (R & W) security group in the Security Group OU, in the cph.local
domain?
' If I am in that group then it goes through and maps the drive
correct.

Set objGroup = GetObject("LDAP://IT$ Department (R & W),ou=Security
Groups,dc=cph.local,dc=com")


' Test for membership in the group.
If (objGroup.IsMember(strAdsPath) = True) Then
' Map a drive.
objNetwork.MapNetworkDrive "I:", "\\MyServer\IT$"
End If
==========

The last question i have is now how do I apply the vbs script to my
users -- do i go to the user i want to apply to in AD, go to the
Profile tab, and apply it to the Logon script portion of their user
profile.

Thanks guys - much appriciated in advance.

Ryan
Richard Mueller [MVP]
2009-10-17 23:58:25 UTC
Permalink
Richard / Cynic thanks for your responses - much appricated for the
quick / detailed feedback:

Richard just a quick question on a script i'm going to attempt on
myself, then my co-workers and if that all works out then I'm going to
push it out to the rest of the organization:

I looked at what you had written in your first response -- the
following script;

----------------

Option Explicit
Dim objNetwork, objSysInfo, strUserDN, strAdsPath, objGroup


Set objNetwork = CreateObject("Wscript.Network")


' Retrieve user DN.
Set objSysInfo = CreateObject("ADSystemInfo")
strUserDN = objSysInfo.UserName
strAdsPath = "LDAP://" & strUserDN


' Bind to group object.
' Would this set objGroup & then test it to see if I was in the IT$
Department
' (R & W) security group in the Security Group OU, in the cph.local
domain?
' If I am in that group then it goes through and maps the drive
correct.

Set objGroup = GetObject("LDAP://IT$ Department (R & W),ou=Security
Groups,dc=cph.local,dc=com")


' Test for membership in the group.
If (objGroup.IsMember(strAdsPath) = True) Then
' Map a drive.
objNetwork.MapNetworkDrive "I:", "\\MyServer\IT$"
End If
==========

The last question i have is now how do I apply the vbs script to my
users -- do i go to the user i want to apply to in AD, go to the
Profile tab, and apply it to the Logon script portion of their user
profile.

Thanks guys - much appriciated in advance.

Ryan
-----------

Yes, the code you posted should map the drive only if the current user is a
member of the "IT$ Department (R & W)" group.

There are two ways to configure logon scripts. One is to assign the script
on the "Profile" tab in ADUC for each user. Another is to use Group Policies
applied to the domain, OU's, or sites. The later is easier because the logon
script configured in a Group Policy applies to everyone in the
domain/OU/Site, so you don't need to configure each user. For more details,
see this link:

http://www.rlmueller.net/LogonScriptFAQ.htm
--
Richard Mueller
MVP Directory Services
Hilltop Lab - http://www.rlmueller.net
--
Cynic
2009-10-18 18:27:19 UTC
Permalink
Post by Richard Mueller [MVP]
I agree that the WinNT provider is easier to use. Don't take offense by what
follows, but I want to express my "philosophy" on the matter. Consider this
a matter of style if you like.
No offense taken, Richard. I agree with all of your points, some of
which I hadn't considered. I think our login scripts could benefit
from your suggestions as well!

Loading...