Tim33 Posted May 28, 2014 Share Posted May 28, 2014 For that you did a very good job! If you need my environment for additional tests, just leave me a message. Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 28, 2014 Share Posted May 28, 2014 How can I pull the lastLogonTimestamp AD attribute via _AD_GetObjectsInOU? The script does not error, it simply returns blank fields in the array. _AD_Open($LS_AD_User & "@" & $LS_AD_Domain, $LS_AD_Password, $LS_AD_DNSDomain, $LS_AD_HostServer, $LS_AD_Configuration) _ArrayDisplay(_AD_GetObjectProperties("CN=Username,OU=Users,DC=Domain,DC=com")) _ArrayDisplay(_AD_GetObjectsInOU("DC=Domain,DC=com", "(Mail=*)", 2, "GivenName, Mail, sAMAccountName, SN, lastLogon, lastLogonTimestamp", "sAMAccountName")) _AD_Close() This is obviously a test script, but _AD_GetObjectProperties returns the attributes with values, _AD_GetObjectsInOU does not. Thanks again for all your help. Link to comment Share on other sites More sharing options...
water Posted May 29, 2014 Author Share Posted May 29, 2014 _AD_GetObjectsInOU returns the attribute "as is". LastLogonTimeStamp is a 64 bit integer. You need to decode it. Function _AD_GetObjectProperties shows how to decode attribute LastLogonTimeStamp. Details can be found here. "Many attributes in Active Directory have a data type (syntax) called Integer8. These 64-bit numbers (8 bytes) often represent time in 100-nanosecond intervals. If the Integer8 attribute is a date, the value represents the number of 100-nanosecond intervals since 12:00 AM January 1, 1601." My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 29, 2014 Share Posted May 29, 2014 I understand the need to decode LastLogonTimeStamp to be an actual date and wrote a function to do just that, but why is it that the column returns empty, not an integer, nothing at all from _AD_GetObjectsInOU? From _AD_GetObjectProperties I get the formatted date, on the exact same user as is returned in _AD_GetObjectsInOU. Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 29, 2014 Share Posted May 29, 2014 To be clear...I do not care if what is returned is an Integer8 or a formatted date, I just need a value returned. I can decode it later. Link to comment Share on other sites More sharing options...
water Posted May 29, 2014 Author Share Posted May 29, 2014 I think _ArrayDisplay just can't display the value. Can you try VargetType on the returned value and check what you get? I can't test at the moment. My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 29, 2014 Share Posted May 29, 2014 I Did: _AD_Open($LS_AD_User & "@" & $LS_AD_Domain, $LS_AD_Password, $LS_AD_DNSDomain, $LS_AD_HostServer, $LS_AD_Configuration) $IS_Users = _AD_GetObjectsInOU("DC=domain,DC=com", "(&(sAMAccountType=805306368)(memberOf=CN=IS_Users,OU=Groups,DC=domain,DC=com)(Mail=*))", 2, "lastLogon, lastLogonTimestamp", "sAMAccountName") MsgBox(4096, "Test", 'Last Logon: ' & VarGetType($IS_Users[1][0]) & @CRLF & 'Last Logon Timestamp: ' & VarGetType($IS_Users[1][1])) _AD_Close() It returned: Last Logon: Object Last Logon Timestamp: Object Link to comment Share on other sites More sharing options...
water Posted May 29, 2014 Author Share Posted May 29, 2014 So this is correct. You then can translate this object into a date by using the function I referred to above. My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 29, 2014 Share Posted May 29, 2014 (edited) I hate to seem stupid, but some of your code is fairly foreign to me, as I do not work with objects on a normal basis. Is there a way you can assist me in getting this setup so I can get the right information? I would think it could be useful to persons other than myself and might be helpful to include in the UDF. Edited May 29, 2014 by JoshuaBarnette Link to comment Share on other sites More sharing options...
water Posted May 30, 2014 Author Share Posted May 30, 2014 (edited) When you look for "lastlogontimestamp" you will find this code in function _AD_GetObjectProperties to translate the value: If $vPropertyValue.LargeInteger.LowPart = 0 And $vPropertyValue.LargeInteger.HighPart = 0 Then $aObjectProperties[$iCount3][1] = "1601/01/01 00:00:00" Else Local $sTemp = DllStructCreate("dword low;dword high") DllStructSetData($sTemp, "Low", $vPropertyValue.LargeInteger.LowPart) DllStructSetData($sTemp, "High", $vPropertyValue.LargeInteger.HighPart) Local $sTemp2 = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($sTemp)) Local $sTemp3 = _Date_Time_SystemTimeToTzSpecificLocalTime(DllStructGetPtr($sTemp2)) $aObjectProperties[$iCount3][1] = _Date_Time_SystemTimeToDateTimeStr($sTemp3, 1) EndIf Edited May 30, 2014 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 - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 30, 2014 Share Posted May 30, 2014 I know I will be showing my ignorance here, but here goes....Below is what I have been trying to use to adapt the GetObjectPropeties function to the GetObjectInOU function, Line 1330 returns $iCount1 as blank. expandcollapse popupFunc _AD_GetObjectsInOU($sOU = "", $sFilter = "(name=*)", $iSearchScope = 2, $sDataToRetrieve = "sAMAccountName", $sSortBy = "sAMAccountName", $bCount = False) ; Data Type Mapping between Active Directory and LDAP ; http://msdn.microsoft.com/en-us/library/aa772375(VS.85).aspx Local Const $ADSTYPE_DN_STRING = 1 Local Const $ADSTYPE_CASE_IGNORE_STRING = 3 Local Const $ADSTYPE_BOOLEAN = 6 Local Const $ADSTYPE_INTEGER = 7 Local Const $ADSTYPE_OCTET_STRING = 8 Local Const $ADSTYPE_UTC_TIME = 9 Local Const $ADSTYPE_LARGE_INTEGER = 10 Local Const $ADSTYPE_NT_SECURITY_DESCRIPTOR = 25 Local Const $ADSTYPE_UNKNOWN = 26 If $sOU = "" Then $sOU = $sAD_DNSDomain Else If _AD_ObjectExists($sOU, "distinguishedName") = 0 Then Return SetError(1, 0, "") EndIf Local $iCount2, $aDataToRetrieve, $aTemp If $sDataToRetrieve = "" Then $sDataToRetrieve = "sAMAccountName" $sDataToRetrieve = StringStripWS($sDataToRetrieve, 8) $__oAD_Command.Properties("Searchscope") = $iSearchScope $__oAD_Command.CommandText = "<LDAP://" & $sAD_HostServer & "/" & $sOU & ">;" & $sFilter & ";" & $sDataToRetrieve $__oAD_Command.Properties("Sort On") = $sSortBy Local $oRecordSet = $__oAD_Command.Execute If @error Or Not IsObj($oRecordSet) Then Return SetError(2, @error, "") Local $iCount1 = $oRecordSet.RecordCount If $iCount1 = 0 Then If $bCount Then Return SetError(3, 0, 0) Return SetError(3, 0, "") EndIf If $bCount Then Return $iCount1 If StringInStr($sDataToRetrieve, ",") Then $aDataToRetrieve = StringSplit($sDataToRetrieve, ",") Local $aObjects[$iCount1 + 1][$aDataToRetrieve[0]] $aObjects[0][0] = $iCount1 $aObjects[0][1] = $aDataToRetrieve[0] $iCount2 = 1 $oRecordSet.MoveFirst Do For $iCount1 = 1 To $aDataToRetrieve[0] If IsArray($oRecordSet.Fields($aDataToRetrieve[$iCount1]).Value) Then $aTemp = $oRecordSet.Fields($aDataToRetrieve[$iCount1]).Value $aObjects[$iCount2][$iCount1 - 1] = _ArrayToString($aTemp) Else $aObjects[$iCount2][$iCount1 - 1] = $oRecordSet.Fields($aDataToRetrieve[$iCount1]).Value EndIf Next $oRecordSet.MoveNext $iCount2 += 1 Until $oRecordSet.EOF #Region ; Translate Objects To Values For $r = 1 To $aObjects[0][0] For $c = 0 To $aObjects[0][1] - 1 If IsObj($aObjects[$r][$c]) Then MsgBox(4096, "Test Is Object", "Array Row: " & $r & " And Column: " & $c & " Is An Object.") $oObject = $aObjects[$r][$c] $oObject.GetInfo() Local $iCount1 = $oObject.PropertyCount() MsgBox(4096, "Test Is Object", "Array Row: " & $r & " And Column: " & $c & " Has " & $iCount1 & " Properties.") For $iCount2 = 0 To $iCount1 - 1 $oItem = $oObject.Item($iCount2) If Not ($sProperties = ",," Or StringInStr($sProperties, "," & $oItem.Name & ",") > 0) Then ContinueLoop $oPropertyEntry = $oObject.GetPropertyItem($oItem.Name, $ADSTYPE_UNKNOWN) If Not IsObj($oPropertyEntry) Then Return SetError(2, 0, $oItem.Name) Else For $vPropertyValue In $oPropertyEntry.Values If $oItem.ADsType = $ADSTYPE_CASE_IGNORE_STRING Then $aObjects[$r][$c] = $vPropertyValue.CaseIgnoreString ElseIf $oItem.ADsType = $ADSTYPE_INTEGER Then If $oItem.Name = "sAMAccountType" Then For $iCount4 = 0 To 11 If $vPropertyValue.Integer = $aSAMAccountType[$iCount4][1] Then $aObjects[$r][$c] = $aSAMAccountType[$iCount4][0] ExitLoop EndIf Next ElseIf $oItem.Name = "userAccountControl" Then $aObjects[$r][$c] = $vPropertyValue.Integer & " = " For $iCount4 = 0 To 20 If BitAND($vPropertyValue.Integer, $aUAC[$iCount4][0]) = $aUAC[$iCount4][0] Then $aObjects[$r][$c] &= $aUAC[$iCount4][1] & " - " EndIf Next If StringRight($aObjects[$r][$c], 3) = " - " Then $aObjects[$r][$c] = StringTrimRight($aObjects[$r][$c], 3) Else $aObjects[$r][$c] = $vPropertyValue.Integer EndIf ElseIf $oItem.ADsType = $ADSTYPE_LARGE_INTEGER Then If $oItem.Name = "pwdLastSet" Or $oItem.Name = "accountExpires" Or $oItem.Name = "lastLogonTimestamp" Or $oItem.Name = "badPasswordTime" Or $oItem.Name = "lastLogon" Or $oItem.Name = "lockoutTime" Then If $vPropertyValue.LargeInteger.LowPart = 0 And $vPropertyValue.LargeInteger.HighPart = 0 Then $aObjects[$r][$c] = "1601/01/01 00:00:00" Else Local $sTemp = DllStructCreate("dword low;dword high") DllStructSetData($sTemp, "Low", $vPropertyValue.LargeInteger.LowPart) DllStructSetData($sTemp, "High", $vPropertyValue.LargeInteger.HighPart) Local $sTemp2 = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($sTemp)) Local $sTemp3 = _Date_Time_SystemTimeToTzSpecificLocalTime(DllStructGetPtr($sTemp2)) $aObjects[$r][$c] = _Date_Time_SystemTimeToDateTimeStr($sTemp3, 1) EndIf Else $aObjects[$r][$c] = __AD_LargeInt2Double($vPropertyValue.LargeInteger.LowPart, $vPropertyValue.LargeInteger.HighPart) EndIf ElseIf $oItem.ADsType = $ADSTYPE_OCTET_STRING Then $xAD_Dummy = DllStructCreate("byte[56]") DllStructSetData($xAD_Dummy, 1, $vPropertyValue.OctetString) ; objectSID etc. See: http://msdn.microsoft.com/en-us/library/aa379597(VS.85).aspx ; objectGUID etc. See: http://www.autoitscript.com/forum/index.php?showtopic=106163&view=findpost&p=767558 If _Security__IsValidSid(DllStructGetPtr($xAD_Dummy)) Then $aObjects[$r][$c] = _Security__SidToStringSid(DllStructGetPtr($xAD_Dummy)) ; SID Else $aObjects[$r][$c] = _WinAPI_StringFromGUID(DllStructGetPtr($xAD_Dummy)) ; GUID EndIf ElseIf $oItem.ADsType = $ADSTYPE_DN_STRING Then $aObjects[$r][$c] = $vPropertyValue.DNString ElseIf $oItem.ADsType = $ADSTYPE_UTC_TIME Then Local $iDateTime = $vPropertyValue.UTCTime $aObjects[$r][$c] = StringLeft($iDateTime, 4) & "/" & StringMid($iDateTime, 5, 2) & "/" & StringMid($iDateTime, 7, 2) & _ " " & StringMid($iDateTime, 9, 2) & ":" & StringMid($iDateTime, 11, 2) & ":" & StringMid($iDateTime, 13, 2) ElseIf $oItem.ADsType = $ADSTYPE_BOOLEAN Then If $vPropertyValue.Boolean = 0 Then $aObjects[$r][$c] = "False" Else $aObjects[$r][$c] = "True" EndIf ElseIf $oItem.ADsType = $ADSTYPE_NT_SECURITY_DESCRIPTOR Then $oValue = $vPropertyValue.SecurityDescriptor $aObjects[$r][$c] = "Control:" & $oValue.Control & ", " & _ "Group:" & $oValue.Group & ", " & _ "Owner:" & $oValue.Owner & ", " & _ "Revision:" & $oValue.Revision Else $aObjects[$r][$c] = "Has the unknown ADsType: " & $oItem.ADsType EndIf Next EndIf Next EndIf Next Next #EndRegion Else Local $aObjects[$iCount1 + 1] $aObjects[0] = UBound($aObjects) - 1 $iCount2 = 1 $oRecordSet.MoveFirst Do If IsArray($oRecordSet.Fields($sDataToRetrieve).Value) Then $aTemp = $oRecordSet.Fields($sDataToRetrieve).Value $aObjects[$iCount2] = _ArrayToString($aTemp) Else $aObjects[$iCount2] = $oRecordSet.Fields($sDataToRetrieve).Value EndIf $oRecordSet.MoveNext $iCount2 += 1 Until $oRecordSet.EOF #Region ; Translate Objects To Values For $r = 1 To $aObjects[0] If IsObj($aObjects[$r]) Then $oObject = $aObjects[$r] $oObject.GetInfo() Local $iCount1 = $oObject.PropertyCount() For $iCount2 = 0 To $iCount1 - 1 $oItem = $oObject.Item($iCount2) If Not ($sProperties = ",," Or StringInStr($sProperties, "," & $oItem.Name & ",") > 0) Then ContinueLoop $oPropertyEntry = $oObject.GetPropertyItem($oItem.Name, $ADSTYPE_UNKNOWN) If Not IsObj($oPropertyEntry) Then Return SetError(2, 0, $oItem.Name) Else For $vPropertyValue In $oPropertyEntry.Values If $oItem.ADsType = $ADSTYPE_CASE_IGNORE_STRING Then $aObjects[$r] = $vPropertyValue.CaseIgnoreString ElseIf $oItem.ADsType = $ADSTYPE_INTEGER Then If $oItem.Name = "sAMAccountType" Then For $iCount4 = 0 To 11 If $vPropertyValue.Integer = $aSAMAccountType[$iCount4][1] Then $aObjects[$r] = $aSAMAccountType[$iCount4][0] ExitLoop EndIf Next ElseIf $oItem.Name = "userAccountControl" Then $aObjects[$r] = $vPropertyValue.Integer & " = " For $iCount4 = 0 To 20 If BitAND($vPropertyValue.Integer, $aUAC[$iCount4][0]) = $aUAC[$iCount4][0] Then $aObjects[$r] &= $aUAC[$iCount4][1] & " - " EndIf Next If StringRight($aObjects[$r], 3) = " - " Then $aObjects[$r] = StringTrimRight($aObjects[$r], 3) Else $aObjects[$r] = $vPropertyValue.Integer EndIf ElseIf $oItem.ADsType = $ADSTYPE_LARGE_INTEGER Then If $oItem.Name = "pwdLastSet" Or $oItem.Name = "accountExpires" Or $oItem.Name = "lastLogonTimestamp" Or $oItem.Name = "badPasswordTime" Or $oItem.Name = "lastLogon" Or $oItem.Name = "lockoutTime" Then If $vPropertyValue.LargeInteger.LowPart = 0 And $vPropertyValue.LargeInteger.HighPart = 0 Then $aObjects[$r] = "1601/01/01 00:00:00" Else Local $sTemp = DllStructCreate("dword low;dword high") DllStructSetData($sTemp, "Low", $vPropertyValue.LargeInteger.LowPart) DllStructSetData($sTemp, "High", $vPropertyValue.LargeInteger.HighPart) Local $sTemp2 = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($sTemp)) Local $sTemp3 = _Date_Time_SystemTimeToTzSpecificLocalTime(DllStructGetPtr($sTemp2)) $aObjects[$r] = _Date_Time_SystemTimeToDateTimeStr($sTemp3, 1) EndIf Else $aObjects[$r] = __AD_LargeInt2Double($vPropertyValue.LargeInteger.LowPart, $vPropertyValue.LargeInteger.HighPart) EndIf ElseIf $oItem.ADsType = $ADSTYPE_OCTET_STRING Then $xAD_Dummy = DllStructCreate("byte[56]") DllStructSetData($xAD_Dummy, 1, $vPropertyValue.OctetString) ; objectSID etc. See: http://msdn.microsoft.com/en-us/library/aa379597(VS.85).aspx ; objectGUID etc. See: http://www.autoitscript.com/forum/index.php?showtopic=106163&view=findpost&p=767558 If _Security__IsValidSid(DllStructGetPtr($xAD_Dummy)) Then $aObjects[$r] = _Security__SidToStringSid(DllStructGetPtr($xAD_Dummy)) ; SID Else $aObjects[$r] = _WinAPI_StringFromGUID(DllStructGetPtr($xAD_Dummy)) ; GUID EndIf ElseIf $oItem.ADsType = $ADSTYPE_DN_STRING Then $aObjects[$r] = $vPropertyValue.DNString ElseIf $oItem.ADsType = $ADSTYPE_UTC_TIME Then Local $iDateTime = $vPropertyValue.UTCTime $aObjects[$r] = StringLeft($iDateTime, 4) & "/" & StringMid($iDateTime, 5, 2) & "/" & StringMid($iDateTime, 7, 2) & _ " " & StringMid($iDateTime, 9, 2) & ":" & StringMid($iDateTime, 11, 2) & ":" & StringMid($iDateTime, 13, 2) ElseIf $oItem.ADsType = $ADSTYPE_BOOLEAN Then If $vPropertyValue.Boolean = 0 Then $aObjects[$r] = "False" Else $aObjects[$r] = "True" EndIf ElseIf $oItem.ADsType = $ADSTYPE_NT_SECURITY_DESCRIPTOR Then $oValue = $vPropertyValue.SecurityDescriptor $aObjects[$r] = "Control:" & $oValue.Control & ", " & _ "Group:" & $oValue.Group & ", " & _ "Owner:" & $oValue.Owner & ", " & _ "Revision:" & $oValue.Revision Else $aObjects[$r] = "Has the unknown ADsType: " & $oItem.ADsType EndIf Next EndIf Next EndIf Next #EndRegion EndIf $__oAD_Command.Properties("Sort On") = "" ; Reset sort property Return $aObjects EndFunc ;==>_AD_GetObjectsInOU I am sure this has something to go with the object only being the LargeInteger and not a property list, but as I said before I have never worked much with Objects. Link to comment Share on other sites More sharing options...
water Posted May 30, 2014 Author Share Posted May 30, 2014 I just tried it myself and got an invalid result as well. I have always used _AD_GetObjectsInOU to return the FQDNs of objects and then called _AD_GetObjectProperties to get their translated properties. Why do you need to do it in a single function? Performance? My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted May 30, 2014 Share Posted May 30, 2014 Remember the Windows 8 slowness we were talking about the other day? This is faster, it runs in under 10 seconds on Windows 7 and Windows 8. Also I can filter based on MemberOf here, etc, thus leaving me to make only one AD call versus 115 for the full membership of the Information Services group. The performance this way is phenomenal, also if I ask for a Property that isn't set for an object, it returns a blank field versus me having to loop through every user's _AD_GetObjectProperties to ensure that all the properties are returned, etc. I really think this call, if it can be fixed to get the TimeStamps could seriously improve performance overall. Link to comment Share on other sites More sharing options...
AdamUL Posted May 31, 2014 Share Posted May 31, 2014 JoshuaBarnette, I had a situation similar to you a few years ago, and I wrote >_AD_GetLastLogonTimeStamp by editing _AD_GetPasswordExpired.  This function works like _AD_GetLastLoginDate and _AD_GetPasswordExpired.  This may help with what you are trying to do.  I also like the idea of the time stamps in a readable format in the return from _AD_GetObjectsInOU, but I haven't looked into how much coding that would require.   Adam Link to comment Share on other sites More sharing options...
water Posted May 31, 2014 Author Share Posted May 31, 2014 I have been thinking about this issue. The UDF function _AD_GetObjectsInOU has it's main goal to return objects the user searches for plus some of the properties. So the UDF function will never translate any properties like _AD_GetObjectProperties does. But feel free to write such a function yourself when performance is a goal. Unfortunately I don't have much spare time at the moment to make _AD_GetObjectsInOU translate the properties. But on the other hand I'm interested in any solution you might post here for other users. My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
JoshuaBarnette Posted June 3, 2014 Share Posted June 3, 2014 Water, I have spent a couple of days off and between yesterday and today, I have combined both functions into one and hope you will be pleased with the updated function. This combines the aspects of _AD_GetObjectsInOU and _AD_GetObjectProperties you get the speed of GetObjectInOU and the versatility of GetObjectProperties. I have test and see great results. Please let me know what you think and if you might include these changes in the next UDF revision? expandcollapse popup; #FUNCTION# ==================================================================================================================== ; Name...........: _AD_GetObjectsInOU ; Description ...: Returns a filtered array of objects and attributes for a given OU or just the number of records if $bCount is True. ; Syntax.........: _AD_GetObjectsInOU($sOU[, $sFilter = "(name=*)"[, $iSearchScope = 2[, $sDataToRetrieve = "sAMAccountName"[, $sSortBy = "sAMAccountName"[, $bCount = False]]]]]) ; Parameters ....: $sOU - The OU to retrieve from (FQDN) (default = "", equals "search the whole AD tree") ; $sFilter - Optional: An additional LDAP filter if required (default = "(name=*)") ; $iSearchScope - Optional: 0 = base, 1 = one-level, 2 = sub-tree (default) ; $sDataToRetrieve - Optional: A comma-seperated list of attributes to retrieve (default = "sAMAccountName"). ; |More than one attribute will create a 2-dimensional array ; $sSortBy - Optional: name of the attribute the resulting array will be sorted upon (default = "sAMAccountName"). ; |To completely suppress sorting (even the default sort) set this parameter to "". This improves performance when doing large queries ; $bCount - Optional: If set to True only returns the number of records returned by the query (default = "False") ; Return values .: Success - Number of records retrieved or a one or two dimensional array of objects and attributes in the given OU. First entry is for the given OU itself ; Failure - "", sets @error to: ; |1 - Specified OU does not exist ; |2 - No records returned from Active Directory. $sDataToRetrieve is invalid (attribute may not exist). @extended is set to the error returned by LDAP ; |3 - No records returned from Active Directory. $sFilter didn't return a record ; Author ........: Jonathan Clelland, Sundance ; Modified.......: water, JoshuaBarnette ; Remarks .......: The default filter returns an array including one record for the OU itself. To exclude the OU use a different filter that doesn't include the OU ; e.g. "(&(objectcategory=person)(objectclass=user)(name=*))" ;+ ; To make sure that all properties you specify in $sDataToRetrieve exist in the AD you can use _AD_ObjectExistsInSchema. ;+ ; The following examples illustrate the use of the escaping mechanism in the LDAP filter: ; (o=Parens R Us \28for all your parenthetical needs\29) ; (cn=*\2A*) ; (filename=C:\5cMyFile) ; (bin=\00\00\00\04) ; (sn=Lu\c4\8di\c4\87) ; The first example shows the use of the escaping mechanism to represent parenthesis characters. ; The second shows how to represent a "*" in a value, preventing it from being interpreted as a substring indicator. ; The third illustrates the escaping of the backslash character. ; The fourth example shows a filter searching for the four-byte value 0x00000004, illustrating the use of the escaping mechanism to ; represent arbitrary data, including NUL characters. ; The final example illustrates the use of the escaping mechanism to represent various non-ASCII UTF-8 characters. ; Related .......: _AD_GetAllOUs ; Link ..........: ; Example .......: Yes ; =============================================================================================================================== Func _AD_GetObjectsInOU($sOU = "", $sFilter = "(name=*)", $iSearchScope = 2, $sDataToRetrieve = "sAMAccountName", $sSortBy = "sAMAccountName", $bCount = False) Local $oObject Local $oItem, $oPropertyEntry, $oValue, $iCount3, $xAD_Dummy ; Data Type Mapping between Active Directory and LDAP ; http://msdn.microsoft.com/en-us/library/aa772375(VS.85).aspx Local Const $ADSTYPE_DN_STRING = 1 Local Const $ADSTYPE_CASE_IGNORE_STRING = 3 Local Const $ADSTYPE_BOOLEAN = 6 Local Const $ADSTYPE_INTEGER = 7 Local Const $ADSTYPE_OCTET_STRING = 8 Local Const $ADSTYPE_UTC_TIME = 9 Local Const $ADSTYPE_LARGE_INTEGER = 10 Local Const $ADSTYPE_NT_SECURITY_DESCRIPTOR = 25 Local Const $ADSTYPE_UNKNOWN = 26 Local $aSAMAccountType[12][2] = [["DOMAIN_OBJECT", 0x0],["GROUP_OBJECT", 0x10000000],["NON_SECURITY_GROUP_OBJECT", 0x10000001], _ ["ALIAS_OBJECT", 0x20000000],["NON_SECURITY_ALIAS_OBJECT", 0x20000001],["USER_OBJECT", 0x30000000],["NORMAL_USER_ACCOUNT", 0x30000000], _ ["MACHINE_ACCOUNT", 0x30000001],["TRUST_ACCOUNT", 0x30000002],["APP_BASIC_GROUP", 0x40000000],["APP_QUERY_GROUP", 0x40000001], _ ["ACCOUNT_TYPE_MAX", 0x7fffffff]] Local $aUAC[21][2] = [[0x00000001, "SCRIPT"],[0x00000002, "ACCOUNTDISABLE"],[0x00000008, "HOMEDIR_REQUIRED"],[0x00000010, "LOCKOUT"],[0x00000020, "PASSWD_NOTREQD"], _ [0x00000040, "PASSWD_CANT_CHANGE"],[0x00000080, "ENCRYPTED_TEXT_PASSWORD_ALLOWED"],[0x00000100, "TEMP_DUPLICATE_ACCOUNT"],[0x00000200, "NORMAL_ACCOUNT"], _ [0x00000800, "INTERDOMAIN_TRUST_ACCOUNT"],[0x00001000, "WORKSTATION_TRUST_ACCOUNT"],[0x00002000, "SERVER_TRUST_ACCOUNT"],[0x00010000, "DONT_EXPIRE_PASSWD"], _ [0x00020000, "MNS_LOGON_ACCOUNT"],[0x00040000, "SMARTCARD_REQUIRED"],[0x00080000, "TRUSTED_FOR_DELEGATION"],[0x00100000, "NOT_DELEGATED"], _ [0x00200000, "USE_DES_KEY_ONLY"],[0x00400000, "DONT_REQUIRE_PREAUTH"],[0x00800000, "PASSWORD_EXPIRED"],[0x01000000, "TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION"]] If $sOU = "" Then $sOU = $sAD_DNSDomain Else If _AD_ObjectExists($sOU, "distinguishedName") = 0 Then Return SetError(1, 0, "") EndIf Local $iCount2, $aDataToRetrieve, $aTemp If $sDataToRetrieve = "" Then $sDataToRetrieve = "sAMAccountName" $sDataToRetrieve = StringStripWS($sDataToRetrieve, 8) $__oAD_Command.Properties("Searchscope") = $iSearchScope $__oAD_Command.CommandText = "<LDAP://" & $sAD_HostServer & "/" & $sOU & ">;" & $sFilter & ";ADsPath" $__oAD_Command.Properties("Sort On") = $sSortBy Local $oRecordSet = $__oAD_Command.Execute If @error Or Not IsObj($oRecordSet) Then Return SetError(2, @error, "") Local $rCount = $oRecordSet.RecordCount If $rCount = 0 Then If $bCount Then Return SetError(3, 0, 0) Return SetError(3, 0, "") EndIf If $bCount Then Return $rCount If StringInStr($sDataToRetrieve, ",") Then $aDataToRetrieve = StringSplit($sDataToRetrieve, ",") Local $aObjects[$rCount + 1][$aDataToRetrieve[0]] $aObjects[0][0] = $rCount $aObjects[0][1] = $aDataToRetrieve[0] $crCount = 1 ; Current Record $oRecordSet.MoveFirst Do Local $sLDAPEntry = $oRecordSet.fields(0).Value $oObject = __AD_ObjGet($sLDAPEntry) ; Retrieve the COM Object $oObject.GetInfo() Local $iCount1 = $oObject.PropertyCount() For $iCount2 = 0 To $iCount1 - 1 $oItem = $oObject.Item($iCount2) $pIndex = _ArraySearch($aDataToRetrieve, $oItem.Name) If @Error Then ContinueLoop $oPropertyEntry = $oObject.GetPropertyItem($oItem.Name, $ADSTYPE_UNKNOWN) If Not IsObj($oPropertyEntry) Then Return SetError(2, 0, $oItem.Name) Else For $vPropertyValue In $oPropertyEntry.Values If $oItem.ADsType = $ADSTYPE_CASE_IGNORE_STRING Then $aObjects[$crCount][$pIndex - 1] = $vPropertyValue.CaseIgnoreString ElseIf $oItem.ADsType = $ADSTYPE_INTEGER Then If $oItem.Name = "sAMAccountType" Then For $iCount4 = 0 To 11 If $vPropertyValue.Integer = $aSAMAccountType[$iCount4][1] Then $aObjects[$crCount][$pIndex - 1] = $aSAMAccountType[$iCount4][0] ExitLoop EndIf Next ElseIf $oItem.Name = "userAccountControl" Then $aObjects[$crCount][$pIndex - 1] = $vPropertyValue.Integer & " = " For $iCount4 = 0 To 20 If BitAND($vPropertyValue.Integer, $aUAC[$iCount4][0]) = $aUAC[$iCount4][0] Then $aObjects[$crCount][$pIndex - 1] &= $aUAC[$iCount4][1] & " - " EndIf Next If StringRight($aObjects[$crCount][$pIndex - 1], 3) = " - " Then $aObjects[$crCount][$pIndex - 1] = StringTrimRight($aObjects[$crCount][$pIndex - 1], 3) Else $aObjects[$crCount][$pIndex - 1] = $vPropertyValue.Integer EndIf ElseIf $oItem.ADsType = $ADSTYPE_LARGE_INTEGER Then If $oItem.Name = "pwdLastSet" Or $oItem.Name = "accountExpires" Or $oItem.Name = "lastLogonTimestamp" Or $oItem.Name = "badPasswordTime" Or $oItem.Name = "lastLogon" Or $oItem.Name = "lockoutTime" Then If $vPropertyValue.LargeInteger.LowPart = 0 And $vPropertyValue.LargeInteger.HighPart = 0 Then $aObjects[$crCount][$pIndex - 1] = "1601/01/01 00:00:00" Else Local $sTemp = DllStructCreate("dword low;dword high") DllStructSetData($sTemp, "Low", $vPropertyValue.LargeInteger.LowPart) DllStructSetData($sTemp, "High", $vPropertyValue.LargeInteger.HighPart) Local $sTemp2 = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($sTemp)) Local $sTemp3 = _Date_Time_SystemTimeToTzSpecificLocalTime(DllStructGetPtr($sTemp2)) $aObjects[$crCount][$pIndex - 1] = _Date_Time_SystemTimeToDateTimeStr($sTemp3, 1) EndIf Else $aObjects[$crCount][$pIndex - 1] = __AD_LargeInt2Double($vPropertyValue.LargeInteger.LowPart, $vPropertyValue.LargeInteger.HighPart) EndIf ElseIf $oItem.ADsType = $ADSTYPE_OCTET_STRING Then $xAD_Dummy = DllStructCreate("byte[56]") DllStructSetData($xAD_Dummy, 1, $vPropertyValue.OctetString) ; objectSID etc. See: http://msdn.microsoft.com/en-us/library/aa379597(VS.85).aspx ; objectGUID etc. See: http://www.autoitscript.com/forum/index.php?showtopic=106163&view=findpost&p=767558 If _Security__IsValidSid(DllStructGetPtr($xAD_Dummy)) Then $aObjects[$crCount][$pIndex - 1] = _Security__SidToStringSid(DllStructGetPtr($xAD_Dummy)) ; SID Else $aObjects[$crCount][$pIndex - 1] = _WinAPI_StringFromGUID(DllStructGetPtr($xAD_Dummy)) ; GUID EndIf ElseIf $oItem.ADsType = $ADSTYPE_DN_STRING Then $aObjects[$crCount][$pIndex - 1] = $vPropertyValue.DNString ElseIf $oItem.ADsType = $ADSTYPE_UTC_TIME Then Local $iDateTime = $vPropertyValue.UTCTime $aObjects[$crCount][$pIndex - 1] = StringLeft($iDateTime, 4) & "/" & StringMid($iDateTime, 5, 2) & "/" & StringMid($iDateTime, 7, 2) & _ " " & StringMid($iDateTime, 9, 2) & ":" & StringMid($iDateTime, 11, 2) & ":" & StringMid($iDateTime, 13, 2) ElseIf $oItem.ADsType = $ADSTYPE_BOOLEAN Then If $vPropertyValue.Boolean = 0 Then $aObjects[$crCount][$pIndex - 1] = "False" Else $aObjects[$crCount][$pIndex - 1] = "True" EndIf ElseIf $oItem.ADsType = $ADSTYPE_NT_SECURITY_DESCRIPTOR Then $oValue = $vPropertyValue.SecurityDescriptor $aObjects[$crCount][$pIndex - 1] = "Control:" & $oValue.Control & ", " & _ "Group:" & $oValue.Group & ", " & _ "Owner:" & $oValue.Owner & ", " & _ "Revision:" & $oValue.Revision Else $aObjects[$crCount][$pIndex - 1] = "Unknown ADsType" & $oItem.ADsType EndIf Next EndIf Next $oRecordSet.MoveNext $crCount += 1 Until $oRecordSet.EOF Else Local $aObjects[$rCount + 1] $aObjects[0] = $rCount $crCount = 1 ; Current Record $oRecordSet.MoveFirst Do Local $sLDAPEntry = $oRecordSet.fields(0).Value $oObject = __AD_ObjGet($sLDAPEntry) ; Retrieve the COM Object $oObject.GetInfo() Local $iCount1 = $oObject.PropertyCount() For $iCount2 = 0 To $iCount1 - 1 $oItem = $oObject.Item($iCount2) $sDataToRetrieve = "," & $sDataToRetrieve & "," If Not StringInStr($sDataToRetrieve, "," & $oItem.Name & ",") Then ContinueLoop $oPropertyEntry = $oObject.GetPropertyItem($oItem.Name, $ADSTYPE_UNKNOWN) If Not IsObj($oPropertyEntry) Then Return SetError(2, 0, $oItem.Name) Else For $vPropertyValue In $oPropertyEntry.Values If $oItem.ADsType = $ADSTYPE_CASE_IGNORE_STRING Then $aObjects[$crCount] = $vPropertyValue.CaseIgnoreString ElseIf $oItem.ADsType = $ADSTYPE_INTEGER Then If $oItem.Name = "sAMAccountType" Then For $iCount4 = 0 To 11 If $vPropertyValue.Integer = $aSAMAccountType[$iCount4][1] Then $aObjects[$crCount] = $aSAMAccountType[$iCount4][0] ExitLoop EndIf Next ElseIf $oItem.Name = "userAccountControl" Then $aObjects[$crCount] = $vPropertyValue.Integer & " = " For $iCount4 = 0 To 20 If BitAND($vPropertyValue.Integer, $aUAC[$iCount4][0]) = $aUAC[$iCount4][0] Then $aObjects[$crCount] &= $aUAC[$iCount4][1] & " - " EndIf Next If StringRight($aObjects[$crCount], 3) = " - " Then $aObjects[$crCount] = StringTrimRight($aObjects[$crCount], 3) Else $aObjects[$crCount] = $vPropertyValue.Integer EndIf ElseIf $oItem.ADsType = $ADSTYPE_LARGE_INTEGER Then If $oItem.Name = "pwdLastSet" Or $oItem.Name = "accountExpires" Or $oItem.Name = "lastLogonTimestamp" Or $oItem.Name = "badPasswordTime" Or $oItem.Name = "lastLogon" Or $oItem.Name = "lockoutTime" Then If $vPropertyValue.LargeInteger.LowPart = 0 And $vPropertyValue.LargeInteger.HighPart = 0 Then $aObjects[$crCount] = "1601/01/01 00:00:00" Else Local $sTemp = DllStructCreate("dword low;dword high") DllStructSetData($sTemp, "Low", $vPropertyValue.LargeInteger.LowPart) DllStructSetData($sTemp, "High", $vPropertyValue.LargeInteger.HighPart) Local $sTemp2 = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($sTemp)) Local $sTemp3 = _Date_Time_SystemTimeToTzSpecificLocalTime(DllStructGetPtr($sTemp2)) $aObjects[$crCount] = _Date_Time_SystemTimeToDateTimeStr($sTemp3, 1) EndIf Else $aObjects[$crCount] = __AD_LargeInt2Double($vPropertyValue.LargeInteger.LowPart, $vPropertyValue.LargeInteger.HighPart) EndIf ElseIf $oItem.ADsType = $ADSTYPE_OCTET_STRING Then $xAD_Dummy = DllStructCreate("byte[56]") DllStructSetData($xAD_Dummy, 1, $vPropertyValue.OctetString) ; objectSID etc. See: http://msdn.microsoft.com/en-us/library/aa379597(VS.85).aspx ; objectGUID etc. See: http://www.autoitscript.com/forum/index.php?showtopic=106163&view=findpost&p=767558 If _Security__IsValidSid(DllStructGetPtr($xAD_Dummy)) Then $aObjects[$crCount] = _Security__SidToStringSid(DllStructGetPtr($xAD_Dummy)) ; SID Else $aObjects[$crCount] = _WinAPI_StringFromGUID(DllStructGetPtr($xAD_Dummy)) ; GUID EndIf ElseIf $oItem.ADsType = $ADSTYPE_DN_STRING Then $aObjects[$crCount] = $vPropertyValue.DNString ElseIf $oItem.ADsType = $ADSTYPE_UTC_TIME Then Local $iDateTime = $vPropertyValue.UTCTime $aObjects[$crCount] = StringLeft($iDateTime, 4) & "/" & StringMid($iDateTime, 5, 2) & "/" & StringMid($iDateTime, 7, 2) & _ " " & StringMid($iDateTime, 9, 2) & ":" & StringMid($iDateTime, 11, 2) & ":" & StringMid($iDateTime, 13, 2) ElseIf $oItem.ADsType = $ADSTYPE_BOOLEAN Then If $vPropertyValue.Boolean = 0 Then $aObjects[$crCount] = "False" Else $aObjects[$crCount] = "True" EndIf ElseIf $oItem.ADsType = $ADSTYPE_NT_SECURITY_DESCRIPTOR Then $oValue = $vPropertyValue.SecurityDescriptor $aObjects[$crCount] = "Control:" & $oValue.Control & ", " & _ "Group:" & $oValue.Group & ", " & _ "Owner:" & $oValue.Owner & ", " & _ "Revision:" & $oValue.Revision Else $aObjects[$crCount] = "Unknown ADsType" & $oItem.ADsType EndIf Next EndIf Next $oRecordSet.MoveNext $crCount += 1 Until $oRecordSet.EOF EndIf $__oAD_Command.Properties("Sort On") = "" ; Reset sort property Return $aObjects EndFunc ;==>_AD_GetObjectsInOU Link to comment Share on other sites More sharing options...
water Posted June 3, 2014 Author Share Posted June 3, 2014 Thanks a lot for this modification! I will have a look as soon as I find some spare time. Maybe I will make it an optional feature that can be switched on/off by the user. My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
Iceman682 Posted June 12, 2014 Share Posted June 12, 2014 (edited) Hi Water Is there a way to recurse someone's surname and populate their domain windows username, just like when adding domain users to the local Administrators or Power Users security groups? Many thanks in advance Iceman Edited June 12, 2014 by Iceman682 Link to comment Share on other sites More sharing options...
water Posted June 12, 2014 Author Share Posted June 12, 2014 Have you tried _AD_GetObjectsInOU? My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki  Link to comment Share on other sites More sharing options...
Iceman682 Posted June 12, 2014 Share Posted June 12, 2014 Thanks for push in the right direction Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now