VBscript to determine group membership recursively

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 :-)
LVL 1
metropolitanhotelAsked:
Who is Participating?
 
RobSampsonConnect With a Mentor Commented:
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
 
RobSampsonCommented:
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
 
metropolitanhotelAuthor Commented:
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
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

 
RobSampsonCommented:
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
 
metropolitanhotelAuthor Commented:
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
 
RobSampsonCommented:
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
 
RobSampsonCommented:
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
 
metropolitanhotelAuthor Commented:
That is absolutely brilliant Rob, thanks :-)
0
 
RobSampsonCommented:
No problem.  It actually would be pretty useful, I might use that myself...

Rob.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.