[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

VBscript to determine group membership recursively

Posted on 2007-07-24
9
Medium Priority
?
1,480 Views
Last Modified: 2011-09-20
I'm moving to a recursive group structure in AD that will be as follows:
-Site
  - Department
    - Job Function

Now, user accounts will only be added to the bottom group, their job function, and they inherit the group permissions etc for their department and site.  Now, I'm writing a fairly long login script in VBscript that does certain things based on either site or department, but rarely job function.  I have a small function that I found in someone else's loging script that does a great job of determining if a user is in a group, but it doesn't work for the scenario above, as the user would need to be a member of all 3 groups for it to apply to them.

The function I have is:

Function IsMember(sGroup)
    Dim oDict, oUser, oGroup

    If IsEmpty(oDict) Then
        Set oDict = CreateObject("Scripting.Dictionary")
        oDict.CompareMode = vbTextCompare
        Set oUser = GetObject("WinNT://" & strAdsPath & ",user")
        For Each oGroup In oUser.Groups
            oDict.Add oGroup.Name, "-"
        Next
        Set oUser = Nothing
    End If
    IsMember = CBool(oDict.Exists(sGroup))
    if IsMember and bolWriteLog then objLogFile.WriteLine(Now() & ": Success: User is member of " & sGroup)
End Function

What I need is one that can somehow tell that if the account is in a group called "Accounts - Managers", then that group in in the group "Accounts Department".  Perhaps there is another way to accomplish the same thing that I haven't thought of.

Thanks in advance :-)
0
Comment
Question by:metropolitanhotel
  • 6
  • 3
9 Comments
 
LVL 65

Expert Comment

by:RobSampson
ID: 19563334
Hi,

To be able to check the AD structure, you would need to obtain the ADsPath from an LDAP object, not a WinNT object.

You can use these three lines in a login script to return the ADsPath:
Set objSysInfo = CreateObject("ADSystemInfo")
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)
MsgBox objUser.ADsPath

Then you could just iterate through that to determine their tree structure.

Regards,

Rob.
0
 
LVL 1

Author Comment

by:metropolitanhotel
ID: 19563888
That's great thanks Rob, but it doesn't do exactly what I want.  I have put that into a function in my script and it gets around half my problem; it can determine if a user is in a site based on OU.  However, I still really need to be able to determine if groups are within other groups...

Take this example... We have certain applications that only certain groups can use.  At the moment we add accounts into a group, let's call it AccountsPackage, to give access to the users.  But in cases where a whole department should have access, it makes more sense just to add the department, AccountsDept, to the group let's call it AccountsPackage.  However, when the script checks what groups a member is in it doesn't return the parent group AccountsPackage, it only returns that the user is in AccountsDept.

The difference in the example from the original scenario i guess is that the applications are not represented in our OU structure, and as such the LDAP query is of no use.

I've started writing a function that looks up whether a group is in another group, but I think this may be a very roundabout  way of doing it:

Set objGroup = GetObject("LDAP://cn=testgrp,ou=testOU,dc=domain,dc=com")
If objGroup.IsMember("LDAP://cn=testgrp2,ou=testOU,dc=domain,dc=com") Then
    WScript.Echo "Is a Member!"
Else
    WScript.Echo "NOT a Member!"
End If

Thanks again :-)
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 19572241
Hi, I just wrote this function (which is pretty cool, thanks for making me look for that), which gets the groups that a user belongs to, and outputs them.
'=============
Set objNetwork = CreateObject("WScript.Network")
Set objSysInfo = CreateObject("ADSystemInfo")
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)

objMemberOf = objUser.GetEx("MemberOf")

' Retrieve ALL of the user groups that a user is a member of
strGroups = ""
For Each objGroup in objMemberOf
      strGroupName = Left(Mid(objGroup, InStr(objGroup, "CN=") + 3),InStr(Mid(objGroup, InStr(objGroup, "CN=") + 3), ",") - 1)
      If strGroups = "" Then
            strGroups = strGroupName
      Else
            strGroups = strGroups & VbCrLf & strGroupName
      End If
Next
MsgBox Replace(objUser.Name, "CN=", "") & " is a member of: " & VbCrLf & strGroups
'================

If you want, we can use the Split function to split by VbCrLf and then iterate through each group.

Regards,

Rob.
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:metropolitanhotel
ID: 19573391
I haven't used the split function before... I do need to iterate tjhrough each group and see what group it is a member of, and then recursively go through the parent groups one by one and see if they are in any groups.  The problem I'm having at the moment is that I don't know how to list the groups within a group, as objgroup.GetEx("MemberOf") doesn't seem to work on a group oibject, only on a useer object.

0
 
LVL 65

Expert Comment

by:RobSampson
ID: 19580415
I have just discovered that the GetEx("MemberOf") does work for Group objects....so I just have to figure out how to make it recursive..... I haven't worked much with recursion, so hopefully it won't take too long....

Regards,

Rob.
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 19580675
Hmmmm, I haven't done enough testing with this yet, but give it a shot......
I'll try some more on Monday....it's 5:30 Friday night now....
'=====================
Set objNetwork = CreateObject("WScript.Network")
Set objSysInfo = CreateObject("ADSystemInfo")
'MsgBox objSysInfo.UserName
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)
'Set objUser = GetObject("LDAP://" & "CN=TestComputers,OU=Test Computers,OU=Computers,OU=Civic Centre,OU=Sites,DC=maroondah,DC=local")
'Set objUser = GetObject("LDAP://" & "CN=TestGroup2,OU=Test Computers,OU=Computers,OU=Civic Centre,OU=Sites,DC=maroondah,DC=local")

strGroups = ""

GetMemberOfNames(objUser)

MsgBox Replace(objUser.Name, "CN=", "") & " is a member of: " & VbCrLf & strGroups

Sub GetMemberOfNames(objObjectToCheck)
      ' Retrieve ALL of the user groups that a user is a member of
      On Error Resume Next
      objMemberOf = objObjectToCheck.GetEx("MemberOf")
      If Err.Number = 0 Then
            On Error GoTo 0
            For Each objGroup in objMemberOf
                  strGroupName = Left(Mid(objGroup, InStr(objGroup, "CN=") + 3),InStr(Mid(objGroup, InStr(objGroup, "CN=") + 3), ",") - 1)
                  If strGroups = "" Then
                        strGroups = strGroupName
                  Else
                        strGroups = strGroups & VbCrLf & strGroupName
                  End If
                  Set objNextGroup = GetObject("LDAP://" & objGroup)
                  GetMemberOfNames(objNextGroup)
            Next
      Else
            Err.Clear
            On Error GoTo 0
      End If
End Sub
'===============

Regards,

Rob.
0
 
LVL 65

Accepted Solution

by:
RobSampson earned 1000 total points
ID: 19589401
Hi, here's another version of it that includes in indicator of how many levels the group is above the root ones.  Bear in mind that this works backwards, meaning that the more arrows there is, the *higher* the group is in the heirarchy.
'===============
Set objNetwork = CreateObject("WScript.Network")
Set objSysInfo = CreateObject("ADSystemInfo")
'MsgBox objSysInfo.UserName
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)
'Set objUser = GetObject("LDAP://" & "CN=TestComputers,OU=Test Computers,OU=Computers,OU=Civic Centre,OU=Sites,DC=maroondah,DC=local")
'Set objUser = GetObject("LDAP://" & "CN=TestGroup2,OU=Test Computers,OU=Computers,OU=Civic Centre,OU=Sites,DC=maroondah,DC=local")

strGroups = ""

intLevel = 0

GetMemberOfNames objUser, intLevel

strResults = Replace(objUser.Name, "CN=", "") & " is a member of: "
arrGroups = Split(strGroups, VbCrLf)
For intCount = LBound(arrGroups) To UBound(arrGroups)
      If strResults = "" Then
            strResults = arrGroups(intCount)
      Else
            strResults = strResults & VbCrLf & arrGroups(intCount)
      End If
Next

MsgBox strResults

Sub GetMemberOfNames(objObjectToCheck, intLevel)
      ' This function can get caught in a loop if there is a circular
      ' group membership.  There is a method of using a Dictionary object
      ' here: http://www.rlmueller.net/MemberOf.htm
      ' which checks if the group has been used before.
      
      intLevel = intLevel + 1
      ' Retrieve ALL of the user groups that a user is a member of
      On Error Resume Next
      objMemberOf = objObjectToCheck.GetEx("MemberOf")
      If Err.Number = 0 Then
            On Error GoTo 0
            For Each objGroup in objMemberOf
                  strGroupName = Left(Mid(objGroup, InStr(objGroup, "CN=") + 3),InStr(Mid(objGroup, InStr(objGroup, "CN=") + 3), ",") - 1)
                  If strGroups = "" Then
                        strGroups = String(intLevel, ">") & strGroupName
                  Else
                        strGroups = strGroups & VbCrLf & String(intLevel, ">") & strGroupName
                  End If
                  Set objNextGroup = GetObject("LDAP://" & objGroup)
                  GetMemberOfNames objNextGroup, intLevel
            Next
            intLevel = intLevel - 1
      Else
            intLevel = intLevel - 1
            Err.Clear
            On Error GoTo 0
      End If
End Sub
'================

Also, when you are checking group memberships, you won't want to use the arrows, to use something like:
If LCase(Replace(arrGroups(intCount), ">", "")) = LCase("groupname") Then
   ' do something for that group
End If

Regards,

Rob.
0
 
LVL 1

Author Comment

by:metropolitanhotel
ID: 19591993
That is absolutely brilliant Rob, thanks :-)
0
 
LVL 65

Expert Comment

by:RobSampson
ID: 19595831
No problem.  It actually would be pretty useful, I might use that myself...

Rob.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this article we want to have a look at the directory attributes which are used by Microsoft to store the so called Security Identifiers (SID). These SIDs plays an important role in delegating and granting permissions and in authentication of trus…
Over the years I have built up my own little library of code snippets that I refer to when programming or writing a script.  Many of these have come from the web or adaptations from snippets I find on the Web.  Periodically I add to them when I come…
Is your OST file inaccessible, Need to transfer OST file from one computer to another? Want to convert OST file to PST? If the answer to any of the above question is yes, then look no further. With the help of Stellar OST to PST Converter, you can e…
With just a little bit of  SQL and VBA, many doors open to cool things like synchronize a list box to display data relevant to other information on a form.  If you have never written code or looked at an SQL statement before, no problem! ...  give i…
Suggested Courses
Course of the Month20 days, 10 hours left to enroll

868 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question