;; 
;;=====================================================================================----- 
;; 
;;FUNCTION       GroupMember() 
;; 
;;ACTION         List or Modify a Group's contents 
;; 
;;AUTHOR         Glenn Barnas 
;; 
;;VERSION        1.0 - 2010/01/05 
;; 
;;HISTORY        1.0 - 2010/01/05 - Initial Release 
;; 
;;SYNTAX         GroupMember(Action, Target, Group [, Object]) 
;; 
;;PARAMETERS     Action - REQUIRED - String 
;;               - Defines what to do 
;;                * LIST - Return list of all member IDs 
;;                * LISTU - Return list of member User IDs 
;;                * LISTG - Return list of member Group IDs 
;;                  ADD - Add the list of names to the group 
;;                  DELETE - Remove the list of names from the group 
;;                  REPLACE - Replace the group members with the list of DNs 
;;                  CLEAR - Remove all users from a group 
;; 
;;                * LIST actions can be prefixed with "N" to return full NTDS strings, 
;;                 or "O" to return object pointers instead of simple member names. 
;; 
;;               Target - REQUIRED - String 
;;               - the target computer or domain 
;; 
;;               Group  - REQUIRED - String 
;;               - The group DN string 
;; 
;;               Object - OPTIONAL - Array 
;;               - Array of object(s) within the group to modify. The Object is 
;;                required for all actions except LIST and CLEAR. 
;; 
;;REMARKS        Utility UDF to read/write group membership objects.  
;;		 See the AdGroupMember UDF for similar capabilities for managing AD 
;;               groups via LDAP 
;; 
;;RETURNS        Array of data for Query, Status for Modify actions (1=Success) 
;;               Array of User/Group strings for Action=LIST 
;;               Array of UserID strings for LISTU 
;;               Array of GroupID strings for LISTU 
;;               If the LIST action is prefaced with "N", an array of NTDS 
;;               strings is returned. 
;;               If the LIST action is prefaced with "O", an array of object 
;;               pointers is returned. 
;;               Boolean - 1 (success) or 0 (fail) on modify actions 
;; 
;;DEPENDENCIES   NTDS 
;; 
;;TESTED WITH    W2K, WXP, W2K3 
;; 
;;EXAMPLES       ; find all members of the local administrators group 
;;               $aMembers = GroupMember('LIST', @WKSTA, 'Administrators') 
;;               For Each $Member in $aMembers 
;;                 $Member ? 
;;               Next 
;; 
;;               ; get list of user accounts in the local Domain Admins group 
;;               $aMemberUsers = GroupMember('NLISTU', @DOMAIN, 'Domain Admins') 
;; 
;;               ; return an array of object pointers to member groups in the local Administrators group 
;;               $aMemberGroups = GroupMember('OLISTG', @WKSTA, 'Administrators') 
;;               For Each $Obj in $aMemberGroups 
;;                 $Obj.Name ? 
;;               Next 
; 
Function GroupMember($_Action, $_Target, $_Group, OPTIONAL $_aObjects)
 
  Dim $_objGroup				; Group object pointer 
  Dim $_Container				; Container reference 
  Dim $_NtdsProp				; NTDS Action Property value 
  Dim $_Filter					; Filter parameter for reads 
  Dim $_Type					; type of data to return 
  Dim $_aMembers				; Members array 
  Dim $_aTmp, $_I				; temp array, index pointer 
 
  $GroupMember = 0				; be pessimistic (assume failure) 
  $_I = -1
 
  $_objGroup = GetObject('WinNT://' + $_Target + '/' + $_Group)
  If @ERROR Exit @ERROR EndIf			; exit now if not valid 
 
  $_Type = 0					; return simple member string 
  Select
   Case Left($_Action, 5) = 'NLIST'
    $_Action = SubStr($_Action, 2)
    $_Type = 1					; return full NTDS string 
   Case Left($_Action, 5) = 'DLIST'
    $_Action = SubStr($_Action, 2)
    $_Type = 2					; return object pointer 
   Case Left($_Action, 5) = 'OLIST'
    $_Action = SubStr($_Action, 2)
    $_Type = 3					; return object pointer 
  EndSelect
 
  ; set the action property and validate the optional filter parameter 
  Select
   Case $_Action = 'LIST'			; list users & groups 
    $_NtdsProp = 0
    $_Filter = 3				; binary - User & Group flag 
   Case $_Action = 'LISTU'			; list users 
    $_NtdsProp = 0
    $_Filter = 1				; binary - User flag 
   Case $_Action = 'LISTG'			; list groups 
    $_NtdsProp = 0
    $_Filter = 2				; binary - Group flag 
   Case $_Action = 'DELETE'			; delete from group 
    $_NtdsProp = 1
   Case $_Action = 'CLEAR'			; delete all group members 
    $_NtdsProp = 2
   Case $_Action = 'ADD'			; add objects to group 
    $_NtdsProp = 3
   Case $_Action = 'REPLACE'			; replace group contents (Clear & Add) 
    $_NtdsProp = 4
  EndSelect
 
 
 ; considered a Select/Case statement group here, but multiple Ifs permitted 
 ; combining the code for REPLACE via CLEAR + ADD 
  If $_NtdsProp = 0				; QUERY (*LIST*) 
    For Each $_Member in $_objGroup.members
      ; collect user data 
      If ($_Filter & 1) And $_Member.class = 'User'
        $_I = $_I + 1
        ReDim Preserve $_aTmp[$_I]		; expand the array 
        Select
         Case $_Type = 0
          $_aTmp[$_I] = $_Member.Name		; add user name 
         Case $_Type = 1
          $_aTmp[$_I] = $_Member.AdsPath	; add NTDS path 
         Case $_Type = 3
          $_aTmp[$_I] = $_Member		; add object pointer 
        EndSelect
      EndIf
 
      ; collect group data 
      If ($_Filter & 2) And $_Member.class = 'Group'
        $_I = $_I + 1
        ReDim Preserve $_aTmp[$_I]		; expand the array 
        Select
         Case $_Type = 0
          $_aTmp[$_I] = $_Member.Name		; add user name 
         Case $_Type = 1
          $_aTmp[$_I] = $_Member.AdsPath	; add NTDS path 
         Case $_Type = 2
          $_aTmp[$_I] = $_Member.CN		; add DN String 
         Case $_Type = 3
          $_aTmp[$_I] = $_Member		; add object pointer 
        EndSelect
      EndIf
    Next
 
    $_objGroup = 0				; discard the object reference 
    $GroupMember = $_aTmp			; return the array of objects 
    Exit 0
  EndIf ; LIST 
 
 
  ; delete all specified objects from the group if they exist. 
  If $_NtdsProp = 1				; DELETE objects 
    For Each $_Object In $_aObjects
      If InSTR($_Object, '\')
        $_Object = Split($_Object, '\')
        If UBound($_Object) <> 1 Exit 13 EndIf	; must be Container\Object format! 
        $_Container = $_Object[0]		; container 
        $_Object = $_Object[1]			; object 
      Else
        $_Container = $Target			; container 
      EndIf
      ; only delete if object exists in group 
      For Each $_Member in $_objGroup.Members
      ; must determine member class (user or group) 
        If InStr($_Member.AdsPath, 'WinNT://' +  $_Container + '/' + $_Object)
          $_objGroup.remove('WinNT://' + $_Container + '/' + $_Object)
          If @ERROR Exit @ERROR EndIf		; exit if error occurs 
        EndIf
      Next
    Next
 
    $_objGroup = 0				; discard the object reference 
    $GroupMember = 1				; return success 
    Exit 0
  EndIf
 
 
  ; delete all existing objects from the defined group 
  ; also preps for REPLACE by clearing the existing group content 
  If $_NtdsProp = 2 Or $_NtdsProp = 4		; CLEAR object 
    ; enumerate the objects and delete them 
    For Each $_Member in $_objGroup.members
      $_objGroup.remove($_Member.AdsPath)
      If @ERROR Exit @ERROR EndIf		; exit if error occurs 
    Next
  EndIf
 
 
  ; add all objects to the group. Succeed if the object already exists. 
  If $_NtdsProp = 3 Or $_NtdsProp = 4		; ADD objects 
    For Each $_Member In $_aObjects
      If InSTR($_Member, '\')
        $_Member = Split($_Member, '\')
        If UBound($_Member) <> 1 Exit 13 EndIf	; must be Container\Object format! 
        $_Container = $_Member[0]		; container 
        $_Member = $_Member[1]			; object 
      Else
        $_Container = $Target			; container 
      EndIf
      $_objGroup.Add('WinNT://' + $_Container + '/' + $_Member)
      If @ERROR
        If @ERROR <> -2147352567		; ignore "User Exists" error 
          Exit @ERROR				; exit if error occurs 
        EndIf
      EndIf
    Next
  EndIf ; ADD 
 
  $_objGroup = 0				; discard the object reference 
  $GroupMember = 1				; return success 
  Exit 0
 
EndFunction