Jump to content
water

Active Directory UDF

Recommended Posts

water

Hi Thomas,

Having a function _AD_GetAccountsExpired with a date + time? option would be very nice feature. An additional plus would be if it could filter $sUser so we could use smith* to select all accounts that start with smith.

Thanks,

Tom

Hi Tom,

at the moment it is not possible to do this in one go. Every AD function has a single purpose.

As _AD_GetAccountsExpired returns an array containing the FQDN you can either split the FQDN and check the CN=xxx part or you can get the name of each user by calling _AD_GetObjectAttribute.

Thomas


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
tom95521

Hi Tom,

at the moment it is not possible to do this in one go. Every AD function has a single purpose.

As _AD_GetAccountsExpired returns an array containing the FQDN you can either split the FQDN and check the CN=xxx part or you can get the name of each user by calling _AD_GetObjectAttribute.

Thomas

Yes, that is easy enough to do by looping in the array and testing.

Thanks,

Tom

Share this post


Link to post
Share on other sites
water

Having a function _AD_GetAccountsExpired with a date + time? option would be very nice feature.

Hi Tom,

here is the _AD_GetAccountsExpired function with two new parameters. You can define a start and end date/time. All accounts that expire within this "window" are returned.

Note: the 2nd parameter is the end date/time of the window and the 3rd parameter is the start date/time.

; #FUNCTION# ====================================================================================================================
; Name...........: _AD_GetAccountsExpired
; Description ...: Returns an array with FQDNs of expired accounts (user, computer).
; Syntax.........: _AD_GetAccountsExpired([$sAD_Class = "user"[ ,$sAD_DTEExpire = ""[ ,$sAD_DTSExpire = ""]]])
; Parameters ....: $sAD_Class - Optional: Specifies if expired user accounts or computer accounts should be returned (default = "user").
;   "user" - Returns objects of category "user"
;   "computer" - Returns objects of category "computer"
;   $sAD_DTEExpire - YYYY/MM/DD HH:MM:SS (local time) returns all accounts that expire between $sAD_DTSExpire and the specified date/time (default = "" = Now)
;   $sAD_DTSExpire - YYYY/MM/DD HH:MM:SS (local time) returns all accounts that expire between the specified date/time and $sAD_DTEExpire (default = "1601/01/01 00:00:00)
; Return values .: Success - One-based two dimensional array of FQDNs of expired accounts
;   |1 - FQDNs of expired accounts
;   |2 - account expired YYYY/MM/DD HH:NMM:SS UTC
;   |3 - account expired YYYY/MM/DD HH:NMM:SS local time of calling user
;   Failure - "", sets @error to:
;   |1 - No expired accounts found
;   |2 - Specified date/time is invalid
;   |3 - Invalid value for $sAD_Class. Has to be "user" or "computer"
; Author ........: Thomas Rupp
; Modified.......:
; Remarks .......:
; Related .......: _AD_IsAccountExpired, _AD_SetAccountExpire
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _AD_GetAccountsExpired($sAD_Class = "user",$sAD_DTEExpire = "", $sAD_DTSExpire = "")

    If $sAD_Class <> "user" And $sAD_Class <> "computer" Then Return SetError(3,0,0)
    ; process end date/time
    If $sAD_DTEExpire = "" Then
        $sAD_DTEExpire = _Date_Time_GetSystemTime() ; Get current date/time (UTC)
        $sAD_DTEExpire = _Date_Time_SystemTimeToDateTimeStr($sAD_DTEExpire, 1) ; convert to format yyyy/mm/dd hh:mm:ss
    ElseIf Not _DateIsValid($sAD_DTEExpire) Then
        Return SetError(2,0,0)
    Else
        $sAD_DTEExpire = _Date_Time_EncodeSystemTime(StringMid($sAD_DTEExpire,6,2),StringMid($sAD_DTEExpire,9,2),StringLeft($sAD_DTEExpire,4), _ ; encode input
            StringMid($sAD_DTEExpire,12,2),StringMid($sAD_DTEExpire,15,2),StringMid($sAD_DTEExpire,18,2))
        Local $sAD_DTEExpireUTC = _Date_Time_TzSpecificLocalTimeToSystemTime(DllStructGetPtr($sAD_DTEExpire)) ; convert local time to UTC
        $sAD_DTEExpire = _Date_Time_SystemTimeToDateTimeStr($sAD_DTEExpireUTC, 1) ; convert to format yyyy/mm/dd hh:mm:ss
    EndIf
    ; process start date/time
    If $sAD_DTSExpire = "" Then $sAD_DTSExpire = "1600/01/01 00:00:00"
    If Not _DateIsValid($sAD_DTSExpire) Then
        Return SetError(2,0,0)
    Else
        $sAD_DTSExpire = _Date_Time_EncodeSystemTime(StringMid($sAD_DTSExpire,6,2),StringMid($sAD_DTSExpire,9,2),StringLeft($sAD_DTSExpire,4), _ ; encode input
            StringMid($sAD_DTSExpire,12,2),StringMid($sAD_DTSExpire,15,2),StringMid($sAD_DTSExpire,18,2))
        Local $sAD_DTSExpireUTC = _Date_Time_TzSpecificLocalTimeToSystemTime(DllStructGetPtr($sAD_DTSExpire)) ; convert local time to UTC
        $sAD_DTSExpire = _Date_Time_SystemTimeToDateTimeStr($sAD_DTSExpireUTC, 1) ; convert to format yyyy/mm/dd hh:mm:ss
    EndIf
    Local $iAD_DTEExpire = _DateDiff("s", "1601/01/01 00:00:00", $sAD_DTEExpire) * 10000000 ; convert end date/time to Integer8
    Local $iAD_DTSExpire = _DateDiff("s", "1601/01/01 00:00:00", $sAD_DTSExpire) * 10000000 ; convert start date/time to Integer8
    Local $iAD_Temp, $sAD_Temp
    Local $sAD_DTStruct = DllStructCreate("dword low;dword high")
    ; -1 to remove rounding errors
    Local $sAD_Query = "<LDAP://" & $sAD_HostServer & "/" & $sAD_DNSDomain & ">;(&(objectCategory=person)(objectClass=" & $sAD_Class & ")" & _
        "(!accountExpires=0)(accountExpires<=" & Int($iAD_DTEExpire)-1 & ")(accountExpires>=" & Int($iAD_DTSExpire)-1 &"));distinguishedName,accountExpires;subtree"
    Local $oAD_RecordSet = $oAD_Connection.Execute($sAD_Query)
    If Not IsObj($oAD_RecordSet) Or $oAD_RecordSet.RecordCount = 0 Then Return SetError(1, 0, "")
    Local $aAD_FQDN[$oAD_RecordSet.RecordCount + 1][3]
    $aAD_FQDN[0][0] = $oAD_RecordSet.RecordCount
    Local $iAD_Count = 1
    While Not $oAD_RecordSet.EOF
        $aAD_FQDN[$iAD_Count][0] = $oAD_RecordSet.Fields(0).Value ; distinguishedName
        $iAD_Temp = $oAD_RecordSet.Fields(1).Value ; accountExpires
        DllStructSetData($sAD_DTStruct, "Low", $iAD_Temp.LowPart)
        DllStructSetData($sAD_DTStruct, "High", $iAD_Temp.HighPart)
        $sAD_Temp = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($sAD_DTStruct))
        $aAD_FQDN[$iAD_Count][1] = _Date_Time_SystemTimeToDateTimeStr($sAD_Temp, 1) ; accountExpires as UTC
        $sAD_Temp = _Date_Time_SystemTimeToTzSpecificLocalTime(DllStructGetPtr($sAD_Temp))
        $aAD_FQDN[$iAD_Count][2] = _Date_Time_SystemTimeToDateTimeStr($sAD_Temp, 1) ; accountExpires as local time
        $iAD_Count += 1
        $oAD_RecordSet.MoveNext
    WEnd
    $aAD_FQDN[0][0] = UBound($aAD_FQDN) - 1
    Return $aAD_FQDN

EndFunc ;==>_AD_GetAccountsExpired

What do you think?


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
tom95521

Hi Thomas,

I did not have time to test today, but the function looks great. I will test tomorrow in our AD.

Thanks,

Tom

Share this post


Link to post
Share on other sites
water

I missed to attach the example script for _AD_GetAccountsExpired:

#AutoIt3Wrapper_AU3Check_Parameters= -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#AutoIt3Wrapper_AU3Check_Stop_OnWarning=Y
#include <AD.au3>

; Open Connection to the Active Directory
_AD_Open()

Global $aDisabled[1]
; *****************************************************************************
; Example 1
; Get a list of expired user accounts
; *****************************************************************************
$aDisabled = _AD_GetAccountsExpired()
If @error > 0 Then
    MsgBox(64, "Active Directory Functions - Example 1", "No expired user accounts could be found")
Else
    _ArrayDisplay($aDisabled, "Active Directory Functions - Example 1 - Expired User Accounts")
EndIf

; *****************************************************************************
; Example 2
; Get a list of user accounts that expire end of this year
; *****************************************************************************
$aDisabled = _AD_GetAccountsExpired("user", @YEAR & "/12/31")
If @error = 0 Then
    _ArrayDisplay($aDisabled, "Active Directory Functions - Example 2 - Expired User Accounts")
ElseIf @error = 1 Then
    MsgBox(64, "Active Directory Functions - Example 2", "No expired user accounts could be found")
Else
    MsgBox(64, "Active Directory Functions - Example 2", "Invalid parameters provided")
EndIf

; *****************************************************************************
; Example 3
; Get a list of user accounts that expire between january and october this year
; *****************************************************************************
$aDisabled = _AD_GetAccountsExpired("user", @YEAR & "/10/31", @YEAR & "/01/01")
If @error = 0 Then
    _ArrayDisplay($aDisabled, "Active Directory Functions - Example 3 - Expired User Accounts")
ElseIf @error = 1 Then
    MsgBox(64, "Active Directory Functions - Example 3", "No expired user accounts could be found")
Else
    MsgBox(64, "Active Directory Functions - Example 3", "Invalid parameters provided")
EndIf

; *****************************************************************************
; Example 4
; Get a list of expired computer accounts
; *****************************************************************************
$aDisabled = _AD_GetAccountsExpired("computer")
If @error > 0 Then
    MsgBox(64, "Active Directory Functions - Example 4", "No expired computer accounts could be found")
Else
    _ArrayDisplay($aDisabled, "Active Directory Functions - Example 4 - Expired Computer Accounts")
EndIf

; Close Connection to the Active Directory
_AD_Close()

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
tom95521

Hi Thomas,

This is great. I ran your test script. I can use it to find soon to be expired AD accounts. I will use it in my next AD library card program.

Thanks!

Tom

Share this post


Link to post
Share on other sites
water

Glad to hear.

Will be part of the upcoming version 0.40.


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
mnorland

Glad to hear.

Will be part of the upcoming version 0.40.

This seems like the most logical place to give feedback / comments / suggestions / questions about the AD UDF you've written (awesome work, by the way!). This is, by the way, basically directed at water - though I encourage anyones comments/thoughts.

I'm working on a script for our employees home machines (e.g. not domain machines), when on the VPN, to check their credentials and then map network drives. I have it working, but had to disable the checks I was using to test if their password is expired or if their account was disabled. It appears that the problem is because _AD_IsPasswordExpired and _AD_IsObjectLocked eventually call _AD_GetPasswordInfo() which doesn't make use of $oAD_Connection at all. In doing some searches, it looks like there are probably some other functions that do direct .Get()'s on hand-rolled LDAP queries instead of going through the existing connection.

My question is twofold - one, is there any reason (beyond historic / that's how it was written) it's done this way, and two should I endeavor to rewrite _AD_GetPasswordInfo for you, or would you rather make the changes yourself? As it stands I can pull out the bits I need with _AD_GetObjectAttribute calls for what little I need, but it probably won't be much more effort to change it over if you'd rather.

Thanks,

-Martin

Share this post


Link to post
Share on other sites
water

This seems like the most logical place to give feedback / comments / suggestions / questions about the AD UDF you've written (awesome work, by the way!). This is, by the way, basically directed at water - though I encourage anyones comments/thoughts.

I'm working on a script for our employees home machines (e.g. not domain machines), when on the VPN, to check their credentials and then map network drives. I have it working, but had to disable the checks I was using to test if their password is expired or if their account was disabled. It appears that the problem is because _AD_IsPasswordExpired and _AD_IsObjectLocked eventually call _AD_GetPasswordInfo() which doesn't make use of $oAD_Connection at all. In doing some searches, it looks like there are probably some other functions that do direct .Get()'s on hand-rolled LDAP queries instead of going through the existing connection.

My question is twofold - one, is there any reason (beyond historic / that's how it was written) it's done this way, and two should I endeavor to rewrite _AD_GetPasswordInfo for you, or would you rather make the changes yourself? As it stands I can pull out the bits I need with _AD_GetObjectAttribute calls for what little I need, but it probably won't be much more effort to change it over if you'd rather.

Thanks,

-Martin

Hi Martin,

there are two reasons why this is done the way it is:

  • historical: I started with just converting the adfunctions to a UDF adding documentation, examples and a help file. Later new functions were added.
  • technical: _AD_GetPasswordInfo directly connects to the PDC and therefore doesn't use $oAD_Connection. The PDC handles the login requests and "knows" about the locked out or disabled accounts, expired passwords etc. without having to wait for synchronization to occur.
My knowledge of AD is quite limited (I'm learning as I'm working on this UDF) and I only have readonly access to our AD.

If you have enhancements for the UDF I will be glad to incorporate them!

Thomas


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
water

I enhanced the _AD_Open function to get more information if the connection to the AD could not be established.

When you provide a userid/password _AD_Open tries to test the credentials and returns an array with extended information in case of an error (for more details please see the heading of the _AD_Open function, return value: x).

Unfortunately this only works with Windows 7 (and later). For Windows XP and lower you only get @error = 8.

If you want to test it with Windows Vista (it should work but I can't test it) then you have to change the line containing

If @OSVersion = "Win_7" Then
to
If @OSVersion = "Win_VISTA" Then

Anyone willing to test and post the results?

Edit: I combined everything in the following attachment. Just run the following code:

#include <AD.au3>
$R = _AD_OpenEx("domain\userid","password")
If @error <> 0 Then
    If @error <= 8 Then
        MsgBox(0,"AD Test Script","Error Code: " & @error & ", Extended: " & @extended & " returned by _AD_Open")
    Else
        If IsArray($R) Then _ArrayDisplay($R)
    EndIf
EndIf
_AD_Close()
Exit

; #FUNCTION# ====================================================================================================================
; Name...........: _AD_Open
; Description ...: Opens a connection to the Active Directory.
; Syntax.........: _AD_Open([$sAD_UserIdParam = "", $sAD_PasswordParam = ""[, $sAD_DNSDomainParam = "", $sAD_HostServerParam = "", $sAD_ConfigurationParam = ""]])
; Parameters ....: $sAD_UserIdParam - Optional: UserId to authenticate with. This has to be a valid domain user
;   $sAD_PasswordParam - Optional: Password to authenticate with
;   $sAD_DNSDomainParam - Optional: Active Directory domain name if you want to connect to a different domain
;   $sAD_HostServerParam - Optional: Name of the connected Domain Controller if you want to connect to a different domain
;   $sAD_ConfigurationParam - Optional: Configuration naming context if you want to connect to a different domain
; Return values .: Success - 1
;   Failure - 0 or array, sets @error to:
;   |1 - Installation of the custom error handler failed. @extended set to error code of ObjEvent
;   |2 - Creation of the COM object to the AD failed. @extended set to error code of ObjCreate
;   |3 - Open the connection to AD failed. @extended set to error code received by the COM error handler.
;   |   Happens when the User doesn't have query / modify access
;   |4 - Creation of the RootDSE object failed. @extended set to error code received by the COM error handler.
;   |   Happens when the connection to the domain isn't successful. @extended is set to -2147023541 (0x8007054B)
;   |5 - Creation of the DS object failed. @extended set to error code received by the COM error handler
;   |6 - Parameter $sAD_HostServerParam and $sAD_ConfigurationParam have to be specified when $sAD_DNSDomainParam is specified
;   |7 - Parameter $sAD_PasswordParam has to be specified when $sAD_UserIdParam is specified
;   |8 - OpenDSObject method failed. @extended set to error code received by the COM error handler.
;   |   On Windows XP or lower this shows that $sAD_UserIdParam and/or $sAD_PasswordParam are invalid
;   |x - For Windows 7 systems: Win32 error code (decimal). Returns an array with the following information:
;   |1 - ADSI error code (decimal)
;   |2 - Unicode string that describes the ADSI error
;   |3 - name of the provider that raised the error
;   |4 - Win32 error code extracted from element[2]
;   |5 - description of the Win32 error code as returned by _WinAPI_FormatMessage
; Author ........: Jonathan Clelland
; Modified.......: Thomas Rupp
; Remarks .......: To close the connection to the Active Directory, use the _AD_Close function.
;+
;   _AD_Open will use the alternative credentials $sAD_UserIdParam and $sAD_PasswordParam if passed as parameters.
;   $sAD_UserIdParam has to be in one of the following formats (assume the samAccountName = DJ)
;   * Windows Login Name e.g. "DJ"
;   * NetBIOS Login Name e.g. "<DOMAIN>\DJ"
;   * User Principal Name e.g. "DJ@domain.com"
;   All other name formats have not been successfully tested (see section "Link").
;+
;   If you want to connect to a different domain (not the domain your computer is a member of) or your computer is no domain member
;   then please provide $sAD_DNSDomainParam, $sAD_HostServerParam and $sAD_ConfigurationParam as FQDN as well as $sAD_UserIdParam and $sAD_PasswordParam.
;   Example:
;   $sAD_DNSDomainParam = "DC=subdomain,DC=example,DC=com"
;   $sAD_HostServerParam = "servername.subdomain.example.com"
;   $sAD_ConfigurationParam = "CN=Configuration,DC=subdomain,DC=example,DC=com"
;+
;   The COM error handler will be set up if there isn't an error handler already active.
;   But be aware that some functions will not work correctly any more because they handle error codes ($iAD_COMError) that are set by the error handler.
;+
;   If you specify $sAD_UserIdParam and the OS is Windows 7 then _AD_Open will try to verify the userid/password (see return values).
; Related .......: _AD_Close
; Link ..........: http://msdn.microsoft.com/en-us/library/cc223499(PROT.10).aspx (Simple Authentication), http://msdn.microsoft.com/en-us/library/aa746471(VS.85).aspx (ADO)
; Example .......: Yes
; ===============================================================================================================================
Func _AD_OpenEx($sAD_UserIdParam = "", $sAD_PasswordParam = "", $sAD_DNSDomainParam = "", $sAD_HostServerParam = "", $sAD_ConfigurationParam = "")

    ; A COM error handler will only be set up if there isn't an error handler already active.
    If ObjEvent("AutoIt.Error") = "" Then
        $oAD_MyError = ObjEvent("AutoIt.Error", "_AD_ErrorHandler") ; Install a custom error handler
        If @error <> 0 Then Return SetError(1, @error, 0)
    EndIf
    $iAD_COMError = 0
    $oAD_Connection = ObjCreate("ADODB.Connection") ; Create COM object to AD
    If Not IsObj($oAD_Connection) Or @error <> 0 Then Return SetError(2, @error, 0)
    ; ConnectionString Property (ADO): http://msdn.microsoft.com/en-us/library/ms675810.aspx
    $oAD_Connection.ConnectionString = "Provider=ADsDSOObject" ; Set Service providertype
    If $sAD_UserIdParam <> "" Then
        If $sAD_PasswordParam = "" Then Return SetError(7, 0, 0)
        $oAD_Connection.Properties("User ID") = $sAD_UserIdParam ; Authenticate User
        $oAD_Connection.Properties("Password") = $sAD_PasswordParam ; Authenticate User
        ; If userid is the Windows login name then set the flag for secure authentifiction
        If StringInStr($sAD_UserIdParam, "\") = 0 And StringInStr($sAD_UserIdParam, "@") = 0 Then
            $oAD_Connection.Properties("ADSI Flag") = $ADS_SECURE_AUTH
        Else
            $oAD_Connection.Properties("ADSI Flag") = 0x0
        EndIf
        $sAD_UserId = $sAD_UserIdParam
        $sAD_Password = $sAD_PasswordParam
    EndIf
    ; ADO Open Method: http://msdn.microsoft.com/en-us/library/ms676505.aspx
    $oAD_Connection.Open()
    If @error <> 0 Then Return SetError(3, @error, 0)
    ; Connect to another Domain if the Domain parameter is provided
    If $sAD_DNSDomainParam <> "" Then
        If $sAD_HostServerParam = "" Or $sAD_ConfigurationParam = "" Then Return SetError(6, 0, 0)
        $oAD_RootDSE = ObjGet("LDAP://" & $sAD_HostServerParam & "/RootDSE")
        If Not IsObj($oAD_RootDSE) Or @error <> 0 Then Return SetError(4, @error, 0)
        $sAD_DNSDomain = $sAD_DNSDomainParam
        $sAD_HostServer = $sAD_HostServerParam
        $sAD_Configuration = $sAD_ConfigurationParam
    Else
        $oAD_RootDSE = ObjGet("LDAP://RootDSE")
        If Not IsObj($oAD_RootDSE) Or @error <> 0 Then Return SetError(4, @error, 0)
        $sAD_DNSDomain = $oAD_RootDSE.Get("defaultNamingContext") ; Retrieve the current AD domain name
        $sAD_HostServer = $oAD_RootDSE.Get("dnsHostName") ; Retrieve the name of the connected DC
        $sAD_Configuration = $oAD_RootDSE.Get("ConfigurationNamingContext") ; Retrieve the Configuration naming context
    EndIf
    $oAD_OpenDS = ObjGet("LDAP:")
    If Not IsObj($oAD_OpenDS) Or @error <> 0 Then Return SetError(5, @error, 0)
    ; Check userid/password if provided
    If $sAD_UserIdParam <> "" Then
    Local $oAD_Temp = $oAD_OpenDS.OpenDSObject("LDAP://" & $sAD_HostServer, $sAD_UserId, $sAD_Password, $ADS_SERVER_BIND)
    If Not IsObj($oAD_Temp) Or @error <> 0 Then ; login error occurred - get extended information
            If @OSVersion = "Win_7" Then        ; Delivers detailed error information for Windows 7 and later, sets @error for all other OS.
                Local $aAD_Errors = _AD_GetLastErrorEx()
                If $aAD_Errors[4] <> 0 Then
                    If $iAD_Debug = 1 Then ConsoleWrite("_AD_Open: " & _ArrayToString($aAD_Errors, @CRLF, 1) & @CRLF)
                    If $iAD_Debug = 2 Then MsgBox(64, "Active Directory Functions - Debug Info - _AD_Open", _ArrayToString($aAD_Errors, @CRLF, 1))
                    If $iAD_Debug = 3 Then FileWrite("AD_Debug.txt", @YEAR & "." & @MON & "." & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " " & @CRLF & _
                        "-------------------" & @CRLF & "_AD_Open: " & _ArrayToString($aAD_Errors, @CRLF, 1) & @CRLF & _
                        "========================================================" & @CRLF)
                    Return Seterror(Dec($aAD_Errors[4]), 0, $aAD_Errors)
                Endif
            Else
                Return SetError(8, @error, 0)
            EndIf
    Endif
    Endif
    Return 1

EndFunc ;==>_AD_Open

; #INTERNAL_USE_ONLY#============================================================================================================
; Name...........: _AD_GetLastError
; Description ...: Uses the ADsGetLastError function to retrieve the calling thread's last-error code value.
; Syntax.........: _AD_GetLastError()
; Parameters ....: None
; Return values .: Success - An array containing the following values:
; |1 - ADSI error code (decimal)
; |2 - Unicode string that describes the error
; |3 - name of the provider that raised the error
; Failure - 0, sets @error to the return values of DLLCall
; Return values .:
; Author ........: Thomas Rupp
; Modified.......:
; Remarks .......: This function is used internally
; Related .......:
; Link ..........: http://msdn.microsoft.com/en-us/library/cc231199(PROT.10).aspx (Win32 Error Codes), http://forums.sun.com/thread.jspa?threadID=703398
; Example .......:
; ===============================================================================================================================
Func _AD_GetLastErrorEx()

    Local $aAD_LastError[6] = [5]
    Local $EC = DllStructCreate("DWord")
    Local $ED = DllStructCreate("wchar[256]")
    Local $PN = DllStructCreate("wchar[256]")
    DllCall("Activeds.dll", "DWORD", "ADsGetLastError", "ptr", DllStructGetPtr($EC), "ptr", DllStructGetPtr($ED), "DWORD", 256, "ptr", DllStructGetPtr($PN), "DWORD", 256)
    If @error <> 0 Then Return SetError(@error, @extended, "")
    $aAD_LastError[1] = DllStructGetData($EC, 1) ; error code (decimal)
    $aAD_LastError[2] = DllStructGetData($ED, 1) ; Unicode string that describes the error
    $aAD_LastError[3] = DllStructGetData($PN, 1) ; name of the provider that raised the error
    Local $sAD_Error = StringTrimLeft($aAD_LastError[2], StringInStr($aAD_LastError[2], "AcceptSecurityContext", 2))
    $sAD_Error = StringTrimLeft($sAD_Error, StringInStr($sAD_Error, " data", 2) + 5)
    $aAD_LastError[4] = StringTrimRight($sAD_Error, StringLen($sAD_Error) - StringInStr($sAD_Error, ", vece", 2) + 1)
    _WinAPI_FormatMessage($__WINAPICONSTANT_FORMAT_MESSAGE_FROM_SYSTEM, 0, Dec($aAD_LastError[4]), 0, $aAD_LastError[5], 4096, 0)
    Return $aAD_LastError

EndFunc ;==>_AD_GetLastError

Example of error array (on a german system - row 5 means: user not found):

Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
supersonic

Hi water,

I've tested it, but I always get @error = 0.

Greets,

-supersonic.

Share this post


Link to post
Share on other sites
water

Hi supersonic,

  • what OS do you run? The new feature only works for Windows 7. On Windows XP you should get @error = 8 (for this to work please download the changed code again).

    It might work for Vista but then you have to change the script according to my original posting

  • If you run Windows 7 how did you specify the userid (Domain\userid, userid or userid@microsoft.com)?
Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
supersonic

Hi water,

I run the example script as you provided:

$R = _AD_Open("DE01\Administrator","password")
If @error <> 0 Then
    If @error <= 7 Then
        MsgBox(0,"AD Test Script","Error Code: " & @error & " returned by _AD_Open")
    Else
        If IsArray($R) Then _ArrayDisplay($R)
    EndIf
EndIf
_AD_Close()
Exit
Edited by supersonic

Share this post


Link to post
Share on other sites
water

Hi supersonic,

I combined everything in one script. Coulde you please download the code again from this post?

So if you are running Windows XP you should at least get @error=8 when an invalid userid or password is provided.

Thanks


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
supersonic

Hi water!

I tried running the script under W7 from your post again. Nothing happens.

BTW: Please change '_AD_GetLastError' in line 117 to '_AD_GetLastErrorEx'.

What can I do?

Greets,

-supersonic.

Share this post


Link to post
Share on other sites
water

Hi supersonic,

BTW: Please change '_AD_GetLastError' in line 117 to '_AD_GetLastErrorEx'.

Done.

What can I do?

Could you please insert
#AutoIt3Wrapper_Run_Debug_Mode=y
before
$R = _AD_OpenEx("domain\userid","password")
and post the console output?

Thanks for testing!


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
supersonic

Of course! ;)

0001: 0-0: #include ".\..\AUTOIT\Include\Water\AD\AD.au3"
0002: 0-0: #AutoIt3Wrapper_Run_Debug_Mode=y
0003: 0-0: $R = _AD_OpenEx("DOMAIN\Administrator", "password")
0071: 0-0:     If ObjEvent("AutoIt.Error") = "" Then
0072: 0-0:         $oAD_MyError = ObjEvent("AutoIt.Error", "_AD_ErrorHandler") ; Install a custom error handler
0073: 0-0:         If @error <> 0 Then Return SetError(1, @error, 0)
0074: 0-0:     EndIf
0075: 0-0:     $iAD_COMError = 0
0076: 0-0:     $oAD_Connection = ObjCreate("ADODB.Connection") ; Create COM object to AD
0077: 0-0:     If Not IsObj($oAD_Connection) Or @error <> 0 Then Return SetError(2, @error, 0)
0079: 0-0:     $oAD_Connection.ConnectionString = "Provider=ADsDSOObject" ; Set Service providertype
0080: 0-0:     If $sAD_UserIdParam <> "" Then
0081: 0-0:         If $sAD_PasswordParam = "" Then Return SetError(7, 0, 0)
0082: 0-0:         $oAD_Connection.Properties("User ID") = $sAD_UserIdParam ; Authenticate User
0083: 0-0:         $oAD_Connection.Properties("Password") = $sAD_PasswordParam ; Authenticate User
0085: 0-0:         If StringInStr($sAD_UserIdParam, "\") = 0 And StringInStr($sAD_UserIdParam, "@") = 0 Then
0088: 0-0:             $oAD_Connection.Properties("ADSI Flag") = 0x0
0089: 0-0:         EndIf
0090: 0-0:         $sAD_UserId = $sAD_UserIdParam
0091: 0-0:         $sAD_Password = $sAD_PasswordParam
0092: 0-0:     EndIf
0094: 0-0:     $oAD_Connection.Open()
0095: 0-0:     If @error <> 0 Then Return SetError(3, @error, 0)
0097: 0-0:     If $sAD_DNSDomainParam <> "" Then
0105: 0-0:         $oAD_RootDSE = ObjGet("LDAP://RootDSE")
0106: 0-0:         If Not IsObj($oAD_RootDSE) Or @error <> 0 Then Return SetError(4, @error, 0)
0107: 0-0:         $sAD_DNSDomain = $oAD_RootDSE.Get("defaultNamingContext") ; Retrieve the current AD domain name
0108: 0-0:         $sAD_HostServer = $oAD_RootDSE.Get("dnsHostName") ; Retrieve the name of the connected DC
0109: 0-0:         $sAD_Configuration = $oAD_RootDSE.Get("ConfigurationNamingContext") ; Retrieve the Configuration naming context
0110: 0-0:     EndIf
0111: 0-0:     $oAD_OpenDS = ObjGet("LDAP:")
0112: 0-0:     If Not IsObj($oAD_OpenDS) Or @error <> 0 Then Return SetError(5, @error, 0)
0114: 0-0:     If $sAD_UserIdParam <> "" Then
0115: 0-0:     Local $oAD_Temp = $oAD_OpenDS.OpenDSObject("LDAP://" & $sAD_HostServer, $sAD_UserId, $sAD_Password, $ADS_SERVER_BIND)
0116: 0-0:     If Not IsObj($oAD_Temp) Or @error <> 0 Then ; login error occurred - get extended information
0131: 0-0:     Endif
0132: 0-0:     Return 1
0004: 0-0: If @error <> 0 Then
0011: 0-0: _AD_Close()
0012: 0-0: Exit

Share this post


Link to post
Share on other sites
water

That helps a lot!

The difference is that line

Local $oAD_Temp = $oAD_OpenDS.OpenDSObject("LDAP://" & $sAD_HostServer, $sAD_UserId, $sAD_Password, $ADS_SERVER_BIND)
seems to return a different result.

Could you please insert

ConsoleWrite(IsObj($oAD_Temp) & "-" & @error & "-" & @extended & @CRLF)
after this line and post the output of the ConsoleWrite?

I get 0-0-0 if I provide an invalid userid/password.

BTW: What version is your Domain Controller? Windows 2003, Windows 2008?


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
supersonic

Strange, strange...

Now it works!? ;)

But I haven't change anything...

The results are:

0-0-0 (invalid password and/or user, error code 8, extended code 0),

1-0-0 (valid password and user).

We are currently using MS-Windows 2003 R2 as domain controllers.

Edited by supersonic

Share this post


Link to post
Share on other sites
water

Strange, strange...

Now it works!? ;)

But I haven't change anything...

You didn't change anything but by inserting ConsoleWrite the script now checks the @error of ConsoleWrite which is always 0.

But I think I found the bug:

Could you please change the line

If Not IsObj($oAD_Temp) Or @error <> 0 Then
to
If @error <> 0 Or IsObj($oAD_Temp) = 0 Then

BTW: As you get returncode 8 I assume you are running Windows XP?

Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2018-12-03 - Version 1.4.11.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (2018-10-31 - Version 1.3.4.1) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
PowerPoint (2017-06-06 - Version 0.0.5.0) - Download - General Help & Support
Excel - Example Scripts - Wiki
Word - Wiki
 
Tutorials:

ADO - Wiki

 

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.

  • Similar Content

    • water
      By water
      Extensive library to control and manipulate Microsoft Active Directory.

      Threads: Development - General Help & Support - Example Scripts - Wiki
      Previous downloads: 30467
       
      Known Bugs: (last changed: 2018-12-03)
      None
        Things to come: (last changed: 2018-12-03)
      None
      BTW: If you like this UDF please click the "I like this" button. This tells me where to next put my development effort
    • antmar904
      By antmar904
      I'm trying to read all cells used in column "C" in excel to an array but not sure how.
       
      Local $NameArray = _Excel_RangeRead($oWorkbook, $oWorkbook.Activesheet, $oWorkbook.Range["C"].End)  
    • Blois
      By Blois
      Hey Guys,
      Good?
      I'm ned help to consult in other domain. My three domain contains any domains.
      How do I get this query done?
       
      Tks for the Help!
       
    • water
      By water
      Hello all!
      As I have just read access to my companies Active Directory I need some users willing to test the rewritten _AD_ModifyAttribute function.
      My goal is to have the function handle single and multi value attributes the same way and support CLEAR, UPDATE, APPEND and DELETE for the attributes.
      First step is to test how the function handles single value attributes:
      Please modify the following script to specify the object (I suggest a dummy user in your test AD environment - the function might still be buggy).
      Then please run the script and post the restults!
      If everything works as expected we will test multi value attributes. AD attributes: http://www.rlmueller.net/UserAttributes.htm
      #include <AD.au3> _AD_Open() $sObject = "user-to-modify" ; <== NEEDS TO BE CHANGED BY YOU! $sAttribute = "Description" ; CLEAR - single value attribute _AD_ModifyAttribute($sObject, $sAttribute, "Original value", 2) ; Set the original value If @error Then Exit MsgBox(0, "Single value - Error!", "CLEAR: Set original value returned @error = " & @error & ", @extended = " & @extended) _AD_ModifyAttributeEX($sObject, $sAttribute, "", 1) If @error Then Exit MsgBox(0, "Single value - Error!", "CLEAR returned @error = " & @error & ", @extended = " & @extended) $sReturnValue = _AD_GetObjectAttribute($sObject, $sAttribute) If @error Then Exit MsgBox(0, "Single value - Error!", "CLEAR: Query new value returned @error = " & @error & ", @extended = " & @extended) MsgBox(0, "Success!", "Value after CLEAR: " & $sReturnValue & @CRLF & "Expected value: ''") ; UPDATE - single value attribute _AD_ModifyAttribute($sObject, $sAttribute, "Original value", 2) ; Set the original value If @error Then Exit MsgBox(0, "Single value - Error!", "UPDATE: Set original value returned @error = " & @error & ", @extended = " & @extended) _AD_ModifyAttributeEX($sObject, $sAttribute, "UPDATE", 2) If @error Then Exit MsgBox(0, "Single value - Error!", "UPDATE returned @error = " & @error & ", @extended = " & @extended) $sReturnValue = _AD_GetObjectAttribute($sObject, $sAttribute) If @error Then Exit MsgBox(0, "Single value - Error!", "UPDATE: Query new value returned @error = " & @error & ", @extended = " & @extended) MsgBox(0, "Success!", "Value after UPDATE: " & $sReturnValue & @CRLF & "Expected value: 'UPDATE'") ; APPEND - single value attribute - APPEND should work the same way as UPDATE _AD_ModifyAttribute($sObject, $sAttribute, "Original value", 2) ; Set the original value _AD_ModifyAttributeEX($sObject, $sAttribute, "APPEND", 3) $sReturnValue = _AD_GetObjectAttribute($sObject, $sAttribute) If @error Then Exit MsgBox(0, "Single value - Error!", "APPEND returned @error = " & @error & ", @extended = " & @extended) MsgBox(0, "Success!", "Value after APPEND: " & $sReturnValue & @CRLF & "Expected value: 'APPEND'") ; DELETE - single value attribute - DELETE should work the same way as CLEAR _AD_ModifyAttribute($sObject, $sAttribute, "Original value", 2) ; Set the original value _AD_ModifyAttributeEX($sObject, $sAttribute, "DELETE", 4) $sReturnValue = _AD_GetObjectAttribute($sObject, $sAttribute) If @error Then Exit MsgBox(0, "Single value - Error!", "DELETE returned @error = " & @error & ", @extended = " & @extended) MsgBox(0, "Success!", "Value after DELETE: " & $sReturnValue & @CRLF & "Expected value: ''") _AD_Close() Exit ; #FUNCTION# ==================================================================================================================== ; Name...........: _AD_ModifyAttribute ; Description ...: Modifies an attribute of the given object to the value specified. ; Syntax.........: _AD_ModifyAttribute($sObject, $sAttribute[, $vValue = ""[, $iOption = 1]]) ; Parameters ....: $sObject - Object (user, group ...) to add/delete/modify an attribute (sAMAccountName or FQDN) ; $sAttribute - Attribute to add/delete/modify ; $vValue - Optional: Value(s) to modify the attribute with. Use a blank string ("") to remove all values (default). ; +$vValue can be a single value (as a string) or a multi-value (as a zero-based one-dimensional array) ; $iOption - Optional: Indicates the mode of modification: Clear, Update, Append, Delete. ; |1 - CLEAR: remove all value(s) from the attribute (default when $vValue = "" or Default) ; |2 - UPDATE: replace the current value(s) with the specified value(s) ; |3 - APPEND: append the specified value(s) to the existing values(s) ; |4 - DELETE: delete the specified value(s) from the object ; Return values .: Success - 1 ; Failure - 0, sets @error to: ; |1 - $sObject does not exist ; |2 - Parameter $iOption is invalid. needs to be in the range1 to 4. ; |x - Error returned by SetInfo method (Missing permission etc.) ; Author ........: Jonathan Clelland ; Modified.......: water ; Remarks .......: ; Related .......: _AD_GetObjectAttribute, _AD_GetObjectProperties, _AD_AddEmailAddress ; Link ..........: http://msdn.microsoft.com/en-us/library/aa746353(VS.85).aspx (ADS_PROPERTY_OPERATION_ENUM Enumeration) ; Example .......: Yes ; =============================================================================================================================== Func _AD_ModifyAttributeEX($sObject, $sAttribute, $vValue = "", $iOption = 1) Local $aValue[1] If $vValue = Default Then $vValue = "" If IsArray($vValue) Then $aValue = $vValue Else ; Move the string value to the array $aValue[0] = $vValue EndIf If $iOption = Default Then $iOption = 1 If $iOption < 1 Or $iOption > 4 Then Return SetError(2, 0, 0) If Not _AD_ObjectExists($sObject) Then Return SetError(1, 0, 0) Local $sProperty = "sAMAccountName" If StringMid($sObject, 3, 1) = "=" Then $sProperty = "distinguishedName" ; FQDN provided $__oAD_Command.CommandText = "<LDAP://" & $sAD_HostServer & "/" & $sAD_DNSDomain & ">;(" & $sProperty & "=" & $sObject & ");ADsPath;subtree" Local $oRecordSet = $__oAD_Command.Execute ; Retrieve the ADsPath for the object Local $sLDAPEntry = $oRecordSet.fields(0).Value Local $oObject = __AD_ObjGet($sLDAPEntry) ; Retrieve the COM Object for the object $oObject.GetInfo Switch $iOption Case 1 $oObject.PutEx(1, $sAttribute, 0) ; CLEAR: remove all the property value(s) from the object Case 2 $oObject.PutEx(2, $sAttribute, $aValue) ; UPDATE: replace the current value(s) with the specified value(s) Case 3 $oObject.PutEx(3, $sAttribute, $aValue) ; APPEND: append the specified value(s) to the existing values(s) Case 4 $oObject.PutEx(4, $sAttribute, $aValue) ; DELETE: delete the specified value(s) from the object EndSwitch $oObject.SetInfo If @error Then Return SetError(@error, 0, 0) Return 1 EndFunc ;==>_AD_ModifyAttributeEX  
    • Trinnon
      By Trinnon
      I have a question about the @error logging features in _AD_CreateUser.  Hopefully I am just missing something obvious.
      In my app I am creating a user if it does not exist then manipulating some attributes. 
      If the user does exist I would then call another function to remove groups from the user and modify some attributes.
      My question is...
      If the user already Exists, the _AD_CreateUser option gives $iValue = 0 and @error = 0.
      How can @error = 1 for the condition that the user already exists?
       
      I copied a small ship of the code in question along with my full .au3. 
      I am using AD UDF 1.4.9.0 (Water, thanks for the awesome work on this!!!).
      $iValue = _AD_CreateUser ($sOU, $sUser, $sCN)
      If $iValue = 1 Then
            _FileWriteLog ($Log, "Func UserCheck() - User '" & $sUser & "' successfully created ==> Calling UserAttribsNewUser Function.")
            Call ("NewUser")
      ElseIf @error = 1 Then
            _FileWriteLog ($Log, $sUser & " already exists ==> Calling UserAttribsExistingUser Function.")
            Call ("ExistingUser")
       
      AccountSettingConfigurations-Test1.au3
       
×