Jump to content

Active Directory UDF - Help & Support (II)


water
 Share

Recommended Posts

chaoticyeshua,

can you please post your latest version of function _AD_HasRequiredRights__GivenMembers?

I would like to replace the permission checking functions in the UDF with your version.

In addition I want to modify the example script (permission checking, progress bar) so it can be used by other memberrs too.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Can you please post how you call _AD_GetOUTreeView? There might be a way to improve performance.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Hi Water! Sorry for the delay.

Here's _AD_HasRequiredRights__GivenMembers. The only thing I really changed from the original is that you provide the groups the user is in to the function instead of it pulling those groups every time the function is called. It actually saved a lot of time.

Func _AD_HasRequiredRights__GivenMembers($sAD_Object, $iAD_Right = 983551, $sAD_User = @UserName, $aAD_MemberOf = "")
   
If StringMid($sAD_Object, 3, 1) <> "=" Then $sAD_Object = _AD_SamAccountNameToFQDN($sAD_Object) ; sAMAccountName provided
Local $aAD_TrusteeArray, $sAD_TrusteeGroup, $sAD_TrusteeArrayFQDN
Local $oAD_Object = __AD_ObjGet("LDAP://" & $sAD_HostServer & "/" & $sAD_Object)
If IsObj($oAD_Object) Then
  Local $oAD_Security = $oAD_Object.Get("ntSecurityDescriptor")
  Local $oAD_DACL = $oAD_Security.DiscretionaryAcl
  For $oAD_ACE In $oAD_DACL
   $aAD_TrusteeArray = StringSplit($oAD_ACE.Trustee, "\")
   $sAD_TrusteeGroup = $aAD_TrusteeArray[$aAD_TrusteeArray[0]]
   If (UBound($aAD_TrusteeArray) - 1 ) = 2 And $aAD_TrusteeArray[2] = $sAD_User And BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
   If (UBound($aAD_TrusteeArray) - 1 ) = 2 Then
      $sAD_TrusteeArrayFQDN = _AD_SamAccountNameToFQDN($aAD_TrusteeArray[2])
   EndIf
   For $iCount1 = 0 To UBound($aAD_MemberOf) - 1
       If StringInStr($aAD_MemberOf[$iCount1], $sAD_TrusteeArrayFQDN) And _
      BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
    If StringInStr($aAD_MemberOf[$iCount1], "CN=" & $sAD_TrusteeGroup & ",") And _
      BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
   Next
  Next
EndIf
Return 0
EndFunc

As for how I call _AD_GetOUTreeView, I've hardly changed the code you gave me at all and I am just running the script directly essentially as you gave it to me. Later on, I will call it only if it detects that a given computer object doesn't exist in AD already. The problem is that I work for a university, and we have close to 150 top level OUs (basically one for each department). In other words, unless the _AD_HasRequiredRights can somehow be further optimized, I don't think much else can be done about it.

Edited by chaoticyeshua
Link to comment
Share on other sites

Could you please set the following variables like this:

Global $iScope = 1               ; Scope for the LDAP search to calculate the count. 1 = Only the OU, 2 = OU + sub-OUs
Global $bCount = False           ; True = Display the object count right to the OU
Global $bDisplay = False         ; True = Display objects in the OU as well (selection = $sCategory)
It removes calculating the number of objects in the OUs and should enhance performance a lot.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

This means we will need to do some time measurement to see where we need to improve the code.

I'm going to implement your permission checking function and do some tests.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Just built everything together and did some "performance" tests:

91 OUs including 20 top level OUs

Total run time without permission check: 2.97 seconds

Total run time with permission check:

5.8 seconds (only the 20 top OUs were checked because I have no permissions at all)

4.9 seconds to do the permission checks

I will check if performance of the permission checking routine can be improved.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Most time is spent doing the pre-windows-2000 check.

If I remove this check the processing time drops from 5000 milliseconds to 500.

Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

How about this:

Every SamAccountName is translated once to the FQDN and then stored in a Dictionary. The second time it can be found there and reduces the processing time.

#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=n
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <AD.au3>
#include <TreeviewConstants.au3>
#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <GuiTreeView.au3>

Global $sTitle = "Active Direcory OU Treeview"
#region ### START Koda GUI section ### Form=
Global $hMain = GUICreate($sTitle, 743, 683, -1, -1)
Global $hTree = GUICtrlCreateTreeView(6, 6, 600, 666, -1, $WS_EX_CLIENTEDGE)
Global $bExit = GUICtrlCreateButton("Exit", 624, 8, 97, 33)
Global $bExpand = GUICtrlCreateButton("Expand all", 624, 56, 97, 33)
Global $bCollapse = GUICtrlCreateButton("Collapse all", 624, 104, 97, 33)
Global $bSelect = GUICtrlCreateButton("Select OU", 624, 152, 97, 33)
Global $hProgress = GUICtrlCreateProgress(624, 200, 97, 20)
GUISetState(@SW_SHOW)
#endregion ### END Koda GUI section ###
;------------------------------
; Make your changes below
;------------------------------
Global $sOU = "" ; FQDN of the OU where to start
Global $sCategory = "computer" ; Category to select when counts should be displayed or full LDAP query
Global $sText = " (Computer: %)" ; Text to use for the counts to display. % is replaced with the count
Global $iScope = 1 ; Scope for the LDAP search to calculate the count. 1 = Only the OU, 2 = OU + sub-OUs
Global $bCount = False ; True = Display the object count right to the OU
Global $bDisplay = False ; True = Display objects in the OU as well (selection = $sCategory)
Global $iTestTimerTotal
Global $oDict = ObjCreate("Scripting.Dictionary")
;------------------------------
; Make your changes above
;------------------------------
$iTestTimerTotal = TimerInit()
Global $aTreeView = _AD_GetOUTreeView($sOU, $hTree, False, $bCount, $bDisplay, $sCategory, $sText, $iScope, $hProgress)
If @error <> 0 Then MsgBox(16, "Active Direcory OU Treeview", "Error creating list of OUs starting with '" & $sOU & "'." & @CRLF & _
        "Error returned by function _AD_GetALLOUs: @error = " & @error & ", @extended =  " & @extended)
MsgBox(0, "", "Total: " & TimerDiff($iTestTimerTotal))
Sleep(1000)
GUICtrlDelete($hProgress)
While 1
    $Msg = GUIGetMsg()
    Switch $Msg
        Case $GUI_EVENT_CLOSE, $bExit
            Exit
        Case $bExpand
            _GUICtrlTreeView_Expand($hTree)
        Case $bCollapse
            _GUICtrlTreeView_Expand($hTree, 0, False)
        Case $bSelect
            $hSelection = _GUICtrlTreeView_GetSelection($hTree)
            $sSelection = _GUICtrlTreeView_GetText($hTree, $hSelection)
            For $i = 1 To $aTreeView[0][0]
                If $hSelection = $aTreeView[$i][2] Then ExitLoop
            Next
            $sOU = $aTreeView[$i][1]
            MsgBox(64, $sTitle & " - Selected OU", "Name: " & $sSelection & @CRLF & "FQDN: " & $sOU)
    EndSwitch
WEnd

; #FUNCTION# ====================================================================================================================
; Name...........: _AD_GetOUTreeView
; Description ...: Fills a given TreeView control with all OUs starting with a given OU.
; Syntax.........: _AD_GetOUTreeView($sOU, $hTreeView[, $bIsADOpen = False[, $bCount = False[, $bDisplay = False[, $sCategory = ""[, $sText = " (%)"[, $iSearchScope = 1[, $hProgress = Default]]]]]]])
; Parameters ....: $sOU       - FQDN of the OU where to start. "" displays all OUs
;                 $hTreeView    - Handle of the TreeView where the OUs will be displayed. Has to be created in advance
;                 $bIsADOpen    - Optional: Pass as True if the connection to the AD has already been made by the calling script (default = False)
;                 $bCount      - Optional: True will display the count of objects in the OU right to the OU name (default = False)
;                                 The LDAP query to determine the count is built from $sCategory
;                 $bDisplay  - Optional: True will display the objects in the OU below the OU itself (default = False)
;                 $sCategory    - Optional: Category to search for or the complete LDAP search string e.g. "computer" and "(objectcategory=computer)" are equivalent
;                                 The number of found objects is added to the OUs name (default = "" = don't count objects)
;                 $sText        - Optional: Text to display the count. Must contain a % which will be replaced by the actual number (default = " (%)")
;                 $iSearchScope - Optional: Search scope for function _AD_GetObjectsInOU: 0 = base, 1 = one-level, 2 = sub-tree (default = 1)
;                 $hProgress    - Optional: ControlID of a progress bar to show during creation of the TreeView (default = Default)
; Return values .: Success - 1
;                 Failure - Returns 0 and sets @error:
;                 |x - Function _AD_Open or _AD_GetAllOUs failed. @error and @extended are set by _AD_Open or _AD_GetAllOUs
; Author ........: water based on code by ReFran - http://www.autoitscript.com/forum/topic/84119-treeview-read-to-from-file
; Modified.......: water including ideas by HaeMHuK
; Remarks .......: Use $iSearchScope = 1 to get the number of objects of a single OU.
;                 Use $iSearchScope = 2 to get the number of objects in the OU plus all sub-OUs.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _AD_GetOUTreeView($sOU, $hTreeView, $bIsADOpen = False, $bCount = False, $bDisplay = False, $sCategory = "", $sText = " (%)", $iSearchScope = 1, $hProgress = Default)

    Local $iCount, $aObjects
    If $bIsADOpen = False Then
        _AD_Open()
        If @error Then Return SetError(@error, @extended, 0)
    EndIf
    $sSeparator = "\"
    Local $aOUs = _AD_GetAllOUs($sOU, $sSeparator)
    If @error <> 0 Then Return SetError(@error, @extended, 0)
    Local $aTreeView[$aOUs[0][0] + 1][3] = [[$aOUs[0][0], 3]]
    Local $aResult = $aTreeView
    Local $iIndexTV = 0, $sIgnoreOU = ""
    Local $aMemberOf = _AD_GetUserGroups(@UserName) ; Dummy can be removed
    For $i = 1 To $aOUs[0][0]
        If $sIgnoreOU = "" Or ($sIgnoreOU <> "" And StringInStr($aOUs[$i][1], $sIgnoreOU, -1) = 0) Then ; OU to ignore if not set or OU to ignore is set and entry to check isn't a sub-OU of the ignored OU then check permissions
            If _AD_HasRequiredRights__GivenMembers($aOUs[$i][1], 0, @UserName, $aMemberOf) = 0 Then ; User doesn't have needed permissions. Ignore this OU and all sub-OUs   ==>
                $sIgnoreOU = $aOUs[$i][1]
            Else
                $sIgnoreOU = ""
            EndIf
        EndIf
        If $sIgnoreOU = "" Then
            $iIndexTV = $iIndexTV + 1
            $aTemp = StringSplit($aOUs[$i][0], $sSeparator)
            $aTreeView[$iIndexTV][0] = StringFormat("%" & $aTemp[0] - 1 & "s", "") & "#" & $aTemp[$aTemp[0]]
            $aTreeView[$iIndexTV][1] = $aOUs[$i][1]
        EndIf
    Next
    ReDim $aTreeView[$iIndexTV + 1][3]
    $aTreeView[0][0] = $iIndexTV
    _GUICtrlTreeView_BeginUpdate($hTreeView)
    Local $ah[50], $sLDAPString
    If $sCategory <> "" And StringIsAlNum($sCategory) Then
        $sLDAPString = "(objectcategory=" & $sCategory & ")"
    Else
        $sLDAPString = $sCategory
    EndIf
    $iOutIndex = 0
    For $iIndex = 1 To $aTreeView[0][0]
        If $hProgress <> Default Then GUICtrlSetData($hProgress, $iIndex * 100 / $aTreeView[0][0])
        $iOutIndex += 1
        $aObjects = ""
        $sLine = StringSplit(StringStripCR($aTreeView[$iIndex][0]), @TAB)
        $iLevel = StringInStr($sLine[1], "#")
        If $iLevel = 0 Then ExitLoop
        If ($bCount Or $bDisplay) And $sLDAPString <> "" Then
            If $bDisplay Then
                $aObjects = _AD_GetObjectsInOU($aTreeView[$iIndex][1], $sLDAPString, 1, "samaccountname,distinguishedname", "", False)
                If @error Then
                    $iCount = 0
                Else
                    $iCount = $aObjects[0][0]
                EndIf
            Else
                $iCount = _AD_GetObjectsInOU($aTreeView[$iIndex][1], $sLDAPString, $iSearchScope, "samaccountname,distinguishedname", "", True)
            EndIf
        EndIf
        $sTemp = ""
        If $bCount And $sLDAPString <> "" Then $sTemp = StringReplace($sText, "%", $iCount)
        If $iLevel = 1 Then
            $ah[$iLevel] = _GUICtrlTreeView_Add($hTreeView, 0, StringMid($sLine[1], $iLevel + 1) & $sTemp)
        Else
            $ah[$iLevel] = _GUICtrlTreeView_AddChild($hTreeView, $ah[$iLevel - 1], StringMid($sLine[1], $iLevel + 1) & $sTemp)
        EndIf
        $aResult[$iOutIndex][0] = $aTreeView[$iIndex][0]
        $aResult[$iOutIndex][1] = $aTreeView[$iIndex][1]
        $aResult[$iOutIndex][2] = $ah[$iLevel]
        ; Add the objects of the OU to the TreeView
        If IsArray($aObjects) Then
            ReDim $aResult[UBound($aResult, 1) + $aObjects[0][0]][UBound($aResult, 2)]
            For $iIndex2 = 1 To $aObjects[0][0]
                $iOutIndex += 1
                If StringRight($aObjects[$iIndex2][0], 1) = "$" Then $aObjects[$iIndex2][0] = StringLeft($aObjects[$iIndex2][0], StringLen($aObjects[$iIndex2][0]) - 1)
                $aResult[$iOutIndex][0] = $aObjects[$iIndex2][0]
                $aResult[$iOutIndex][1] = $aObjects[$iIndex2][1]
                $aResult[$iOutIndex][2] = _GUICtrlTreeView_AddChild($hTreeView, $ah[$iLevel], $aObjects[$iIndex2][0])
            Next
        EndIf
    Next
    If $bIsADOpen = False Then _AD_Close()
    _GUICtrlTreeView_EndUpdate($hTreeView)
    $aResult[0][0] = UBound($aResult, 1) - 1
    $aResult[0][1] = UBound($aResult, 2)
    Return $aResult

EndFunc   ;==>_AD_GetOUTreeView

Func _AD_HasRequiredRights__GivenMembers($sAD_Object, $iAD_Right = 983551, $sAD_User = @UserName, $aAD_MemberOf = "")
    If StringMid($sAD_Object, 3, 1) <> "=" Then $sAD_Object = _AD_SamAccountNameToFQDN($sAD_Object) ; sAMAccountName provided
    Local $aAD_TrusteeArray, $sAD_TrusteeGroup, $sAD_TrusteeArrayFQDN
    Local $oAD_Object = __AD_ObjGet("LDAP://" & $sAD_HostServer & "/" & $sAD_Object)
    If IsObj($oAD_Object) Then
        Local $oAD_Security = $oAD_Object.Get("ntSecurityDescriptor")
        Local $oAD_DACL = $oAD_Security.DiscretionaryAcl
        For $oAD_ACE In $oAD_DACL
            $aAD_TrusteeArray = StringSplit($oAD_ACE.Trustee, "\")
            $sAD_TrusteeGroup = $aAD_TrusteeArray[$aAD_TrusteeArray[0]]
            If (UBound($aAD_TrusteeArray) - 1) = 2 And $aAD_TrusteeArray[2] = $sAD_User And BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
            If (UBound($aAD_TrusteeArray) - 1) = 2 Then
                ; Check if the entry exists in the Dictionary
                If $oDict.Exists($aAD_TrusteeArray[2]) Then
                    $sAD_TrusteeArrayFQDN = $oDict.Item($aAD_TrusteeArray[2])
                Else
                    $sAD_TrusteeArrayFQDN = _AD_SamAccountNameToFQDN($aAD_TrusteeArray[2])
                    $oDict.Add($aAD_TrusteeArray[2], $sAD_TrusteeArrayFQDN)
                Endif
            EndIf
            For $iCount1 = 0 To UBound($aAD_MemberOf) - 1
                If StringInStr($aAD_MemberOf[$iCount1], $sAD_TrusteeArrayFQDN) And _
                        BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
                If StringInStr($aAD_MemberOf[$iCount1], "CN=" & $sAD_TrusteeGroup & ",") And _
                        BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
            Next
        Next
    EndIf
    Return 0
EndFunc   ;==>_AD_HasRequiredRights__GivenMembers

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

But still rather slow!

Could you insert

Return 1
after
Func _AD_HasRequiredRights__GivenMembers($sAD_Object, $iAD_Right = 983551, $sAD_User = @UserName, $aAD_MemberOf = "")

If you first run the unmodified version of the script with a user who has full permissions on every OU and then run the changed version we know how much time we "lose" with permission checking.

Then we can decide if it makes sense to make permission checking even faster.

Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

If I put "Return 1" immediately under the Func as you suggested:

Total: 10407.0055566789

Total: 10384.6118935539

Total: 10496.474043506

So it's taking about a minute total on top of that for permission checking. Which about resembles what a co-worker of mine attempted with a Powershell script.

Edited by chaoticyeshua
Link to comment
Share on other sites

If you like I can add some counters so we can see where we spend the time. Then we can check if we can improve performance of certain parts of the code.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

This checks the permissions but always returns "success" even if the user doesn't have proper permissions.

The counters displayed at the end show that most time is spend in the loop checking the trustees.

It loops 894000 times and spends 6.85 seconds of 9.2 seconds in permission checking and 9.83 seconds of overall processing time.

If we could reduce the loop processing time we could gain a lot.

Which numbers do you get?

#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=n
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <AD.au3>
#include <TreeviewConstants.au3>
#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <GuiTreeView.au3>
Global $sTitle = "Active Direcory OU Treeview"
#region ### START Koda GUI section ### Form=
Global $hMain = GUICreate($sTitle, 743, 683, -1, -1)
Global $hTree = GUICtrlCreateTreeView(6, 6, 600, 666, -1, $WS_EX_CLIENTEDGE)
Global $bExit = GUICtrlCreateButton("Exit", 624, 8, 97, 33)
Global $bExpand = GUICtrlCreateButton("Expand all", 624, 56, 97, 33)
Global $bCollapse = GUICtrlCreateButton("Collapse all", 624, 104, 97, 33)
Global $bSelect = GUICtrlCreateButton("Select OU", 624, 152, 97, 33)
Global $hProgress = GUICtrlCreateProgress(624, 200, 97, 20)
GUISetState(@SW_SHOW)
#endregion ### END Koda GUI section ###
;------------------------------
; Make your changes below
;------------------------------
Global $sOU = "" ; FQDN of the OU where to start
Global $sCategory = "computer" ; Category to select when counts should be displayed or full LDAP query
Global $sText = " (Computer: %)" ; Text to use for the counts to display. % is replaced with the count
Global $iScope = 1 ; Scope for the LDAP search to calculate the count. 1 = Only the OU, 2 = OU + sub-OUs
Global $bCount = False ; True = Display the object count right to the OU
Global $bDisplay = False ; True = Display objects in the OU as well (selection = $sCategory)
Global $iTimerTotal, $iTimerOU, $iTimerGroups, $iTimerTV, $iTimerPerm = 0, $iTimerLoop = 0
Global $iCountSamFQDN = 0, $iCountLoop = 0
Global $oDict = ObjCreate("Scripting.Dictionary")
;------------------------------
; Make your changes above
;------------------------------
$iTimerTotal = TimerInit()
Global $aTreeView = _AD_GetOUTreeView($sOU, $hTree, False, $bCount, $bDisplay, $sCategory, $sText, $iScope, $hProgress)
If @error <> 0 Then MsgBox(16, "Active Direcory OU Treeview", "Error creating list of OUs starting with '" & $sOU & "'." & @CRLF & _
  "Error returned by function _AD_GetALLOUs: @error = " & @error & ", @extended =  " & @extended)
MsgBox(0, "", "Total: " & TimerDiff($iTimerTotal) & @CRLF & _
  "Get OUs: " & $iTimerOU & @CRLF & _
  "Get Groups: " & $iTimerGroups & @CRLF & _
  "Check Permissions: " & $iTimerPerm & @CRLF & _
  "  SamToFQDN called: " & $iCountSamFQDN & @CRLF & _
  "  Time in Loop: " & $iTimerLoop & @CRLF & _
  "  Count Loop: " & $iCountLoop & @CRLF & _
  "Display TreeView: " & $iTimerTV)
Sleep(1000)
GUICtrlDelete($hProgress)
While 1
 $Msg = GUIGetMsg()
 Switch $Msg
  Case $GUI_EVENT_CLOSE, $bExit
   Exit
  Case $bExpand
   _GUICtrlTreeView_Expand($hTree)
  Case $bCollapse
   _GUICtrlTreeView_Expand($hTree, 0, False)
  Case $bSelect
   $hSelection = _GUICtrlTreeView_GetSelection($hTree)
   $sSelection = _GUICtrlTreeView_GetText($hTree, $hSelection)
   For $i = 1 To $aTreeView[0][0]
    If $hSelection = $aTreeView[$i][2] Then ExitLoop
   Next
   $sOU = $aTreeView[$i][1]
   MsgBox(64, $sTitle & " - Selected OU", "Name: " & $sSelection & @CRLF & "FQDN: " & $sOU)
 EndSwitch
WEnd
; #FUNCTION# ====================================================================================================================
; Name...........: _AD_GetOUTreeView
; Description ...: Fills a given TreeView control with all OUs starting with a given OU.
; Syntax.........: _AD_GetOUTreeView($sOU, $hTreeView[, $bIsADOpen = False[, $bCount = False[, $bDisplay = False[, $sCategory = ""[, $sText = " (%)"[, $iSearchScope = 1[, $hProgress = Default]]]]]]])
; Parameters ....: $sOU       - FQDN of the OU where to start. "" displays all OUs
;                 $hTreeView    - Handle of the TreeView where the OUs will be displayed. Has to be created in advance
;                 $bIsADOpen    - Optional: Pass as True if the connection to the AD has already been made by the calling script (default = False)
;                 $bCount      - Optional: True will display the count of objects in the OU right to the OU name (default = False)
;                                 The LDAP query to determine the count is built from $sCategory
;                 $bDisplay  - Optional: True will display the objects in the OU below the OU itself (default = False)
;                 $sCategory    - Optional: Category to search for or the complete LDAP search string e.g. "computer" and "(objectcategory=computer)" are equivalent
;                                 The number of found objects is added to the OUs name (default = "" = don't count objects)
;                 $sText        - Optional: Text to display the count. Must contain a % which will be replaced by the actual number (default = " (%)")
;                 $iSearchScope - Optional: Search scope for function _AD_GetObjectsInOU: 0 = base, 1 = one-level, 2 = sub-tree (default = 1)
;                 $hProgress    - Optional: ControlID of a progress bar to show during creation of the TreeView (default = Default)
; Return values .: Success - 1
;                 Failure - Returns 0 and sets @error:
;                 |x - Function _AD_Open or _AD_GetAllOUs failed. @error and @extended are set by _AD_Open or _AD_GetAllOUs
; Author ........: water based on code by ReFran - [url="http://www.autoitscript.com/forum/topic/84119-treeview-read-to-from-file"]http://www.autoitscript.com/forum/topic/84119-treeview-read-to-from-file[/url]
; Modified.......: water including ideas by HaeMHuK
; Remarks .......: Use $iSearchScope = 1 to get the number of objects of a single OU.
;                 Use $iSearchScope = 2 to get the number of objects in the OU plus all sub-OUs.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _AD_GetOUTreeView($sOU, $hTreeView, $bIsADOpen = False, $bCount = False, $bDisplay = False, $sCategory = "", $sText = " (%)", $iSearchScope = 1, $hProgress = Default)
 Local $iCount, $aObjects
 If $bIsADOpen = False Then
  _AD_Open()
  If @error Then Return SetError(@error, @extended, 0)
 EndIf
 $sSeparator = "\"
 $iTimerOU = TimerInit()
 Local $aOUs = _AD_GetAllOUs($sOU, $sSeparator)
 If @error <> 0 Then Return SetError(@error, @extended, 0)
 $iTimerOU = TimerDiff($iTimerOU)
 Local $aTreeView[$aOUs[0][0] + 1][3] = [[$aOUs[0][0], 3]]
 Local $aResult = $aTreeView
 Local $iIndexTV = 0, $sIgnoreOU = ""
 $iTimerGroups = TimerInit()
 Local $aMemberOf = _AD_GetUserGroups(@UserName) ; Dummy can be removed
 $iTimerGroups = TimerDiff($iTimerGroups)
 For $i = 1 To $aOUs[0][0]
  If $hProgress <> Default Then GUICtrlSetData($hProgress, $i * 100 / $aOUs[0][0] / 2) ; 50 of the progress by processing the OUs
  If $sIgnoreOU = "" Or ($sIgnoreOU <> "" And StringInStr($aOUs[$i][1], $sIgnoreOU, -1) = 0) Then ; OU to ignore if not set or OU to ignore is set and entry to check isn't a sub-OU of the ignored OU then check permissions
   If _AD_HasRequiredRights__GivenMembers($aOUs[$i][1], 0, @UserName, $aMemberOf) = 0 Then ; User doesn't have needed permissions. Ignore this OU and all sub-OUs
    $sIgnoreOU = $aOUs[$i][1]
   Else
    $sIgnoreOU = ""
   EndIf
  EndIf
  If $sIgnoreOU = "" Then
   $iIndexTV = $iIndexTV + 1
   $aTemp = StringSplit($aOUs[$i][0], $sSeparator)
   $aTreeView[$iIndexTV][0] = StringFormat("%" & $aTemp[0] - 1 & "s", "") & "#" & $aTemp[$aTemp[0]]
   $aTreeView[$iIndexTV][1] = $aOUs[$i][1]
  EndIf
 Next
 ReDim $aTreeView[$iIndexTV + 1][3]
 $aTreeView[0][0] = $iIndexTV
 $iTimerTV = TimerInit()
 _GUICtrlTreeView_BeginUpdate($hTreeView)
 Local $ah[50], $sLDAPString
 If $sCategory <> "" And StringIsAlNum($sCategory) Then
  $sLDAPString = "(objectcategory=" & $sCategory & ")"
 Else
  $sLDAPString = $sCategory
 EndIf
 $iOutIndex = 0
 For $iIndex = 1 To $aTreeView[0][0]
  If $hProgress <> Default Then GUICtrlSetData($hProgress, 50 + $iIndex * 100 / $aTreeView[0][0] / 2) ; 50 of the progress by processing the result
  $iOutIndex += 1
  $aObjects = ""
  $sLine = StringSplit(StringStripCR($aTreeView[$iIndex][0]), @TAB)
  $iLevel = StringInStr($sLine[1], "#")
  If $iLevel = 0 Then ExitLoop
  If ($bCount Or $bDisplay) And $sLDAPString <> "" Then
   If $bDisplay Then
    $aObjects = _AD_GetObjectsInOU($aTreeView[$iIndex][1], $sLDAPString, 1, "samaccountname,distinguishedname", "", False)
    If @error Then
     $iCount = 0
    Else
     $iCount = $aObjects[0][0]
    EndIf
   Else
    $iCount = _AD_GetObjectsInOU($aTreeView[$iIndex][1], $sLDAPString, $iSearchScope, "samaccountname,distinguishedname", "", True)
   EndIf
  EndIf
  $sTemp = ""
  If $bCount And $sLDAPString <> "" Then $sTemp = StringReplace($sText, "%", $iCount)
  If $iLevel = 1 Then
   $ah[$iLevel] = _GUICtrlTreeView_Add($hTreeView, 0, StringMid($sLine[1], $iLevel + 1) & $sTemp)
  Else
   $ah[$iLevel] = _GUICtrlTreeView_AddChild($hTreeView, $ah[$iLevel - 1], StringMid($sLine[1], $iLevel + 1) & $sTemp)
  EndIf
  $aResult[$iOutIndex][0] = $aTreeView[$iIndex][0]
  $aResult[$iOutIndex][1] = $aTreeView[$iIndex][1]
  $aResult[$iOutIndex][2] = $ah[$iLevel]
  ; Add the objects of the OU to the TreeView
  If IsArray($aObjects) Then
   ReDim $aResult[UBound($aResult, 1) + $aObjects[0][0]][UBound($aResult, 2)]
   For $iIndex2 = 1 To $aObjects[0][0]
    $iOutIndex += 1
    If StringRight($aObjects[$iIndex2][0], 1) = "$" Then $aObjects[$iIndex2][0] = StringLeft($aObjects[$iIndex2][0], StringLen($aObjects[$iIndex2][0]) - 1)
    $aResult[$iOutIndex][0] = $aObjects[$iIndex2][0]
    $aResult[$iOutIndex][1] = $aObjects[$iIndex2][1]
    $aResult[$iOutIndex][2] = _GUICtrlTreeView_AddChild($hTreeView, $ah[$iLevel], $aObjects[$iIndex2][0])
   Next
  EndIf
 Next
 If $bIsADOpen = False Then _AD_Close()
 _GUICtrlTreeView_EndUpdate($hTreeView)
 $aResult[0][0] = UBound($aResult, 1) - 1
 $aResult[0][1] = UBound($aResult, 2)
 $iTimerTV = TimerDiff($iTimerTV)
 Return $aResult
EndFunc   ;==>_AD_GetOUTreeView
Func _AD_HasRequiredRights__GivenMembers($sAD_Object, $iAD_Right = 983551, $sAD_User = @UserName, $aAD_MemberOf = "")
 Local $iPerm = TimerInit()
 Local $iLoop = TimerInit()
 If StringMid($sAD_Object, 3, 1) <> "=" Then $sAD_Object = _AD_SamAccountNameToFQDN($sAD_Object) ; sAMAccountName provided
 Local $aAD_TrusteeArray, $sAD_TrusteeGroup, $sAD_TrusteeArrayFQDN
 Local $oAD_Object = __AD_ObjGet("LDAP://" & $sAD_HostServer & "/" & $sAD_Object)
 If IsObj($oAD_Object) Then
  Local $oAD_Security = $oAD_Object.Get("ntSecurityDescriptor")
  Local $oAD_DACL = $oAD_Security.DiscretionaryAcl
  For $oAD_ACE In $oAD_DACL
   $aAD_TrusteeArray = StringSplit($oAD_ACE.Trustee, "\")
   $sAD_TrusteeGroup = $aAD_TrusteeArray[$aAD_TrusteeArray[0]]
   If (UBound($aAD_TrusteeArray) - 1) = 2 And $aAD_TrusteeArray[2] = $sAD_User And BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
   If (UBound($aAD_TrusteeArray) - 1) = 2 Then
    ; Check if the entry exists in the Dictionary
    If $oDict.Exists($aAD_TrusteeArray[2]) Then
     $sAD_TrusteeArrayFQDN = $oDict.Item($aAD_TrusteeArray[2])
    Else
     $iCountSamFQDN = $iCountSamFQDN + 1
     $sAD_TrusteeArrayFQDN = _AD_SamAccountNameToFQDN($aAD_TrusteeArray[2])
     $oDict.Add($aAD_TrusteeArray[2], $sAD_TrusteeArrayFQDN)
    EndIf
   EndIf
   $iLoop = TimerInit()
   For $iCount1 = 0 To UBound($aAD_MemberOf) - 1
    $iCountLoop = $iCountLoop + 1
    If StringInStr($aAD_MemberOf[$iCount1], $sAD_TrusteeArrayFQDN) And _
      BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then
     $iTimerPerm = $iTimerPerm + TimerDiff($iPerm)
     Return 1
    ElseIf StringInStr($aAD_MemberOf[$iCount1], "CN=" & $sAD_TrusteeGroup & ",") And _
      BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then
     $iTimerPerm = $iTimerPerm + TimerDiff($iPerm)
     Return 1
    EndIf
   Next
   $iTimerLoop = $iTimerLoop + TimerDiff($iLoop)
  Next
 EndIf
 $iTimerPerm = $iTimerPerm + TimerDiff($iPerm)
 Return 1 ; 1: Test. Change to 0 for production
EndFunc   ;==>_AD_HasRequiredRights__GivenMembers
Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Running the script you provided above as you gave it to me, here are the numbers returned:

Total: 258437.521704194

Get OUs: 4004.1720678115

Get Groups: 297.634247911434

Check Permissions: 245035.587381845

SamToFQDN called: 330

Time in Loop: 85837.8615153769

Count Loop: 5208609

Display TreeView: 1259.03492161772

Link to comment
Share on other sites

How about this one? I moved some code out of the loop:

#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=n
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <AD.au3>
#include <TreeviewConstants.au3>
#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <GuiTreeView.au3>

Global $sTitle = "Active Direcory OU Treeview"
#region ### START Koda GUI section ### Form=
Global $hMain = GUICreate($sTitle, 743, 683, -1, -1)
Global $hTree = GUICtrlCreateTreeView(6, 6, 600, 666, -1, $WS_EX_CLIENTEDGE)
Global $bExit = GUICtrlCreateButton("Exit", 624, 8, 97, 33)
Global $bExpand = GUICtrlCreateButton("Expand all", 624, 56, 97, 33)
Global $bCollapse = GUICtrlCreateButton("Collapse all", 624, 104, 97, 33)
Global $bSelect = GUICtrlCreateButton("Select OU", 624, 152, 97, 33)
Global $hProgress = GUICtrlCreateProgress(624, 200, 97, 20)
GUISetState(@SW_SHOW)
#endregion ### END Koda GUI section ###
;------------------------------
; Make your changes below
;------------------------------
Global $sOU = "" ; FQDN of the OU where to start
Global $sCategory = "computer" ; Category to select when counts should be displayed or full LDAP query
Global $sText = " (Computer: %)" ; Text to use for the counts to display. % is replaced with the count
Global $iScope = 1 ; Scope for the LDAP search to calculate the count. 1 = Only the OU, 2 = OU + sub-OUs
Global $bCount = False ; True = Display the object count right to the OU
Global $bDisplay = False ; True = Display objects in the OU as well (selection = $sCategory)
Global $iTimerTotal, $iTimerOU, $iTimerGroups, $iTimerTV, $iTimerPerm = 0, $iTimerLoop = 0
Global $iCountSamFQDN = 0, $iCountLoop = 0
Global $oDict = ObjCreate("Scripting.Dictionary")
;------------------------------
; Make your changes above
;------------------------------
$iTimerTotal = TimerInit()
Global $aTreeView = _AD_GetOUTreeView($sOU, $hTree, False, $bCount, $bDisplay, $sCategory, $sText, $iScope, $hProgress)
If @error <> 0 Then MsgBox(16, "Active Direcory OU Treeview", "Error creating list of OUs starting with '" & $sOU & "'." & @CRLF & _
        "Error returned by function _AD_GetALLOUs: @error = " & @error & ", @extended =  " & @extended)
MsgBox(0, "", "Total: " & TimerDiff($iTimerTotal) & @CRLF & _
        "Get OUs: " & $iTimerOU & @CRLF & _
        "Get Groups: " & $iTimerGroups & @CRLF & _
        "Check Permissions: " & $iTimerPerm & @CRLF & _
        "  SamToFQDN called: " & $iCountSamFQDN & @CRLF & _
        "  Time in Loop: " & $iTimerLoop & @CRLF & _
        "  Count Loop: " & $iCountLoop & @CRLF & _
        "Display TreeView: " & $iTimerTV)
Sleep(1000)
GUICtrlDelete($hProgress)
While 1
    $Msg = GUIGetMsg()
    Switch $Msg
        Case $GUI_EVENT_CLOSE, $bExit
            Exit
        Case $bExpand
            _GUICtrlTreeView_Expand($hTree)
        Case $bCollapse
            _GUICtrlTreeView_Expand($hTree, 0, False)
        Case $bSelect
            $hSelection = _GUICtrlTreeView_GetSelection($hTree)
            $sSelection = _GUICtrlTreeView_GetText($hTree, $hSelection)
            For $i = 1 To $aTreeView[0][0]
                If $hSelection = $aTreeView[$i][2] Then ExitLoop
            Next
            $sOU = $aTreeView[$i][1]
            MsgBox(64, $sTitle & " - Selected OU", "Name: " & $sSelection & @CRLF & "FQDN: " & $sOU)
    EndSwitch
WEnd

; #FUNCTION# ====================================================================================================================
; Name...........: _AD_GetOUTreeView
; Description ...: Fills a given TreeView control with all OUs starting with a given OU.
; Syntax.........: _AD_GetOUTreeView($sOU, $hTreeView[, $bIsADOpen = False[, $bCount = False[, $bDisplay = False[, $sCategory = ""[, $sText = " (%)"[, $iSearchScope = 1[, $hProgress = Default]]]]]]])
; Parameters ....: $sOU       - FQDN of the OU where to start. "" displays all OUs
;                 $hTreeView    - Handle of the TreeView where the OUs will be displayed. Has to be created in advance
;                 $bIsADOpen    - Optional: Pass as True if the connection to the AD has already been made by the calling script (default = False)
;                 $bCount      - Optional: True will display the count of objects in the OU right to the OU name (default = False)
;                                 The LDAP query to determine the count is built from $sCategory
;                 $bDisplay  - Optional: True will display the objects in the OU below the OU itself (default = False)
;                 $sCategory    - Optional: Category to search for or the complete LDAP search string e.g. "computer" and "(objectcategory=computer)" are equivalent
;                                 The number of found objects is added to the OUs name (default = "" = don't count objects)
;                 $sText        - Optional: Text to display the count. Must contain a % which will be replaced by the actual number (default = " (%)")
;                 $iSearchScope - Optional: Search scope for function _AD_GetObjectsInOU: 0 = base, 1 = one-level, 2 = sub-tree (default = 1)
;                 $hProgress    - Optional: ControlID of a progress bar to show during creation of the TreeView (default = Default)
; Return values .: Success - 1
;                 Failure - Returns 0 and sets @error:
;                 |x - Function _AD_Open or _AD_GetAllOUs failed. @error and @extended are set by _AD_Open or _AD_GetAllOUs
; Author ........: water based on code by ReFran - http://www.autoitscript.com/forum/topic/84119-treeview-read-to-from-file
; Modified.......: water including ideas by HaeMHuK
; Remarks .......: Use $iSearchScope = 1 to get the number of objects of a single OU.
;                 Use $iSearchScope = 2 to get the number of objects in the OU plus all sub-OUs.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _AD_GetOUTreeView($sOU, $hTreeView, $bIsADOpen = False, $bCount = False, $bDisplay = False, $sCategory = "", $sText = " (%)", $iSearchScope = 1, $hProgress = Default)

    Local $iCount, $aObjects
    If $bIsADOpen = False Then
        _AD_Open()
        If @error Then Return SetError(@error, @extended, 0)
    EndIf
    $sSeparator = "\"
    $iTimerOU = TimerInit()
    Local $aOUs = _AD_GetAllOUs($sOU, $sSeparator)
    If @error <> 0 Then Return SetError(@error, @extended, 0)
    $iTimerOU = TimerDiff($iTimerOU)
    Local $aTreeView[$aOUs[0][0] + 1][3] = [[$aOUs[0][0], 3]]
    Local $aResult = $aTreeView
    Local $iIndexTV = 0, $sIgnoreOU = ""
    $iTimerGroups = TimerInit()
    Local $aMemberOf = _AD_GetUserGroups(@UserName) ; Dummy can be removed
    $iTimerGroups = TimerDiff($iTimerGroups)
    For $i = 1 To $aOUs[0][0]
        If $hProgress <> Default Then GUICtrlSetData($hProgress, $i * 100 / $aOUs[0][0] / 2) ; 50 of the progress by processing the OUs
        If $sIgnoreOU = "" Or ($sIgnoreOU <> "" And StringInStr($aOUs[$i][1], $sIgnoreOU, -1) = 0) Then ; OU to ignore if not set or OU to ignore is set and entry to check isn't a sub-OU of the ignored OU then check permissions
            If _AD_HasRequiredRights__GivenMembers($aOUs[$i][1], 0, @UserName, $aMemberOf) = 0 Then ; User doesn't have needed permissions. Ignore this OU and all sub-OUs
                $sIgnoreOU = $aOUs[$i][1]
            Else
                $sIgnoreOU = ""
            EndIf
        EndIf
        If $sIgnoreOU = "" Then
            $iIndexTV = $iIndexTV + 1
            $aTemp = StringSplit($aOUs[$i][0], $sSeparator)
            $aTreeView[$iIndexTV][0] = StringFormat("%" & $aTemp[0] - 1 & "s", "") & "#" & $aTemp[$aTemp[0]]
            $aTreeView[$iIndexTV][1] = $aOUs[$i][1]
        EndIf
    Next
    ReDim $aTreeView[$iIndexTV + 1][3]
    $aTreeView[0][0] = $iIndexTV
    $iTimerTV = TimerInit()
    _GUICtrlTreeView_BeginUpdate($hTreeView)
    Local $ah[50], $sLDAPString
    If $sCategory <> "" And StringIsAlNum($sCategory) Then
        $sLDAPString = "(objectcategory=" & $sCategory & ")"
    Else
        $sLDAPString = $sCategory
    EndIf
    $iOutIndex = 0
    For $iIndex = 1 To $aTreeView[0][0]
        If $hProgress <> Default Then GUICtrlSetData($hProgress, 50 + $iIndex * 100 / $aTreeView[0][0] / 2) ; 50 of the progress by processing the result
        $iOutIndex += 1
        $aObjects = ""
        $sLine = StringSplit(StringStripCR($aTreeView[$iIndex][0]), @TAB)
        $iLevel = StringInStr($sLine[1], "#")
        If $iLevel = 0 Then ExitLoop
        If ($bCount Or $bDisplay) And $sLDAPString <> "" Then
            If $bDisplay Then
                $aObjects = _AD_GetObjectsInOU($aTreeView[$iIndex][1], $sLDAPString, 1, "samaccountname,distinguishedname", "", False)
                If @error Then
                    $iCount = 0
                Else
                    $iCount = $aObjects[0][0]
                EndIf
            Else
                $iCount = _AD_GetObjectsInOU($aTreeView[$iIndex][1], $sLDAPString, $iSearchScope, "samaccountname,distinguishedname", "", True)
            EndIf
        EndIf
        $sTemp = ""
        If $bCount And $sLDAPString <> "" Then $sTemp = StringReplace($sText, "%", $iCount)
        If $iLevel = 1 Then
            $ah[$iLevel] = _GUICtrlTreeView_Add($hTreeView, 0, StringMid($sLine[1], $iLevel + 1) & $sTemp)
        Else
            $ah[$iLevel] = _GUICtrlTreeView_AddChild($hTreeView, $ah[$iLevel - 1], StringMid($sLine[1], $iLevel + 1) & $sTemp)
        EndIf
        $aResult[$iOutIndex][0] = $aTreeView[$iIndex][0]
        $aResult[$iOutIndex][1] = $aTreeView[$iIndex][1]
        $aResult[$iOutIndex][2] = $ah[$iLevel]
        ; Add the objects of the OU to the TreeView
        If IsArray($aObjects) Then
            ReDim $aResult[UBound($aResult, 1) + $aObjects[0][0]][UBound($aResult, 2)]
            For $iIndex2 = 1 To $aObjects[0][0]
                $iOutIndex += 1
                If StringRight($aObjects[$iIndex2][0], 1) = "$" Then $aObjects[$iIndex2][0] = StringLeft($aObjects[$iIndex2][0], StringLen($aObjects[$iIndex2][0]) - 1)
                $aResult[$iOutIndex][0] = $aObjects[$iIndex2][0]
                $aResult[$iOutIndex][1] = $aObjects[$iIndex2][1]
                $aResult[$iOutIndex][2] = _GUICtrlTreeView_AddChild($hTreeView, $ah[$iLevel], $aObjects[$iIndex2][0])
            Next
        EndIf
    Next
    If $bIsADOpen = False Then _AD_Close()
    _GUICtrlTreeView_EndUpdate($hTreeView)
    $aResult[0][0] = UBound($aResult, 1) - 1
    $aResult[0][1] = UBound($aResult, 2)
    $iTimerTV = TimerDiff($iTimerTV)
    Return $aResult

EndFunc   ;==>_AD_GetOUTreeView

Func _AD_HasRequiredRights__GivenMembers($sAD_Object, $iAD_Right = 983551, $sAD_User = @UserName, $aAD_MemberOf = "")
    Local $iPerm = TimerInit()
    Local $iLoop = TimerInit()
    If StringMid($sAD_Object, 3, 1) <> "=" Then $sAD_Object = _AD_SamAccountNameToFQDN($sAD_Object) ; sAMAccountName provided
    Local $aAD_TrusteeArray, $sAD_TrusteeGroup, $sAD_TrusteeArrayFQDN
    Local $oAD_Object = __AD_ObjGet("LDAP://" & $sAD_HostServer & "/" & $sAD_Object)
    If IsObj($oAD_Object) Then
        Local $oAD_Security = $oAD_Object.Get("ntSecurityDescriptor")
        Local $oAD_DACL = $oAD_Security.DiscretionaryAcl
        For $oAD_ACE In $oAD_DACL
            If BitAND($oAD_ACE.AccessMask, $iAD_Right) <> $iAD_Right Then
                ConsoleWrite("**" & @LF)
                ContinueLoop
            EndIf
            $aAD_TrusteeArray = StringSplit($oAD_ACE.Trustee, "\")
            $sAD_TrusteeGroup = "CN=" & $aAD_TrusteeArray[$aAD_TrusteeArray[0]] & ","
            If $aAD_TrusteeArray[0] = 2 Then
                If $aAD_TrusteeArray[2] = $sAD_User And BitAND($oAD_ACE.AccessMask, $iAD_Right) = $iAD_Right Then Return 1
                ; Check if the entry exists in the Dictionary
                If $oDict.Exists($aAD_TrusteeArray[2]) Then
                    $sAD_TrusteeArrayFQDN = $oDict.Item($aAD_TrusteeArray[2])
                Else
                    $iCountSamFQDN = $iCountSamFQDN + 1
                    $sAD_TrusteeArrayFQDN = _AD_SamAccountNameToFQDN($aAD_TrusteeArray[2])
                    $oDict.Add($aAD_TrusteeArray[2], $sAD_TrusteeArrayFQDN)
                EndIf
            EndIf
            $iLoop = TimerInit()
            For $iCount1 = 0 To UBound($aAD_MemberOf) - 1
                $iCountLoop = $iCountLoop + 1
                If StringInStr($aAD_MemberOf[$iCount1], $sAD_TrusteeArrayFQDN) Then
                    $iTimerPerm = $iTimerPerm + TimerDiff($iPerm)
                    Return 1
                ElseIf StringInStr($aAD_MemberOf[$iCount1], $sAD_TrusteeGroup) Then
                    $iTimerPerm = $iTimerPerm + TimerDiff($iPerm)
                    Return 1
                EndIf
            Next
            $iTimerLoop = $iTimerLoop + TimerDiff($iLoop)
        Next
    EndIf
    $iTimerPerm = $iTimerPerm + TimerDiff($iPerm)
    Return 1 ; 1: Test. Change to 0 for production
EndFunc   ;==>_AD_HasRequiredRights__GivenMembers

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Sorry having to do this through my phone's remote desktop for a while....

Total: 251937.709114971

Get OUs: 3888.85102403275

Get Groups: 236.36206708617

Check Permissions: 239218.078879474

SamToFQDN called: 330

Time in Loop: 77195.5899914161

Count Loop: 5211244

Display TreeView: 1009.50628504835

Link to comment
Share on other sites

Didn't get much faster :(

Was my last post for today. Will think about this problem again tomorrow.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...