Active Directory UDF - General: Difference between revisions
mNo edit summary |
|||
(10 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
The Active Directory UDF offers functions to control and manipulate Microsoft Active Directory. This page describes the Active Directory UDF in general. | The Active Directory UDF offers functions to control and manipulate Microsoft Active Directory. This page describes the Active Directory UDF in general. | ||
== Concept == | == Concept == | ||
The Active Directory UDF is based upon this concept: | The Active Directory UDF is based upon this concept: | ||
Line 12: | Line 13: | ||
== Prerequisites == | == Prerequisites == | ||
You have to run version 3.3.6.0 of AutoIt or newer. | You have to run version 3.3.6.0 of AutoIt or newer. | ||
== Structure == | == Structure == | ||
Every script calls at least the following functions: | Every script calls at least the following functions: | ||
<syntaxhighlight lang="autoit"> | <syntaxhighlight lang="autoit"> | ||
_AD_Open() ; Open a connection to the Active Directory | |||
_AD_* ; Any function that queries or alters the Active Directory | |||
_AD_Close() ; Close the connection | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Open a connection == | == Open a connection == | ||
There are multiple ways to open a connection to the Active Directory. <br>Only one connection can be open at any time. This means you can't connect to two domains at the same time. You have to connect to the first domain, do all the work, close the connection and open the connection to the second domain. This is true for the Global Catalog as well. | There are multiple ways to open a connection to the Active Directory. <br>Only one connection can be open at any time. This means you can't connect to two domains at the same time. You have to connect to the first domain, do all the work, close the connection and open the connection to the second domain. This is true for the Global Catalog as well. | ||
=== To current domain === | === To current domain === | ||
No additional information is needed if you want to connect to the domain the computer is already connected to. _AD_Open uses the credentials of the currently logged in user to connect to the domain. | No additional information is needed if you want to connect to the domain the computer is already connected to. _AD_Open uses the credentials of the currently logged in user to connect to the domain. | ||
Line 38: | Line 41: | ||
The following examples are equivalent: | The following examples are equivalent: | ||
<syntaxhighlight lang="autoit"> | <syntaxhighlight lang="autoit"> | ||
$iResult = _AD_Open("DJ", "password of DJ") | |||
$iResult = _AD_Open("microsoft\DJ", "password of DJ") | |||
$iResult = _AD_Open("DJ@microsoft.com", "password of DJ") | |||
</syntaxhighlight> | </syntaxhighlight> | ||
If you want to connect to a specific Domain Controller in the domain then simply specify the HostServer. | If you want to connect to a specific Domain Controller in the domain then simply specify the HostServer. | ||
Example: | Example: | ||
<syntaxhighlight lang="autoit"> | <syntaxhighlight lang="autoit"> | ||
$iResult = _AD_Open("DJ", "password of DJ", "", "DC1.domain.com") ; setting alternate credentials | $iResult = _AD_Open("DJ", "password of DJ", "", "DC1.domain.com") ; setting alternate credentials | ||
Line 55: | Line 58: | ||
Examples of the needed parameters: | Examples of the needed parameters: | ||
<syntaxhighlight lang="autoit"> | <syntaxhighlight lang="autoit"> | ||
$sDNSDomainParam = Active Directory domain name e.g. "DC=subdomain,DC=example,DC=com" | |||
$sHostServerParam = Name of Domain Controller e.g. "servername.subdomain.example.com" or "subdomain.example.com" (access the domain root) | |||
$sConfigurationParam = Configuration naming context e.g. "CN=Configuration,DC=subdomain,DC=example,DC=com" | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Example:<br> | Example:<br> | ||
Line 93: | Line 96: | ||
=== Secure connection === | === Secure connection === | ||
==== To a Domain Controller ==== | ==== To a Domain Controller ==== | ||
If you want to use SSL and/or password encryption when you connect to a Domain Controller you have to set parameter 6 of _AD_Open.<br> | If you want to use SSL and/or password encryption when you connect to a Domain Controller you have to set parameter 6 of _AD_Open.<br> | ||
Line 106: | Line 110: | ||
==== To a Global Catalog ==== | ==== To a Global Catalog ==== | ||
If you want to use SSL for your connection to the Global Catalog use port 3269 instead of 3268. | If you want to use SSL for your connection to the Global Catalog use port 3269 instead of 3268. | ||
==== Set security settings from outside your script ==== | |||
Microsoft has changed the minimum security requirements for LDAP access as described [https://www.autoitscript.com/forum/topic/158419-active-directory-udf-help-support-iii/?do=findComment&comment=1448538 here]. | |||
If you do not want to modify all of your AutoIt scripts you can modify the security setting from outside. | |||
The UDF provides several levels to set the security from outside. The levels are processed from low to high, overwriting a previously set level. | |||
# AD.INI file in the AutoIt include directory with a GLOBAL section (this is used for ALL scripts. It's a Global setting) | |||
# AD.INI file in the AutoIt include directory with a <scriptname> section (this is used for scripts named <scriptname>.au3 or <scriptname>.exe. It's a Global setting) | |||
# AD.INI file in the working directory with a GLOBAL section name (this is used for ALL scripts in the working directory. It's a Local setting) | |||
# AD.INI file in the working directory directory with a <scriptname> section name (this is used for scripts named <scriptname>.au3 or <scriptname>.exe in the working directory. It's a Local setting) | |||
# Set Global variable $__iSecurity in your script or one of your include files | |||
Example of an INI-file: | |||
[Global] | |||
Security=1 | |||
[TEST_Open] | |||
Security=18 | |||
The Global variable $__iSecurity or the SECURITY key from the INI files can have the values as specified for the $iSecurity parameter.<br> | |||
If $iSecurity was not specified in _AD_Open then this Global values overwrite $iSecurity.<br> | |||
If $iSecurity was specified in _AD_Open then you need to add 16 to this Global values to overwrite $iSecurity.<br> | |||
This means for the INI file above: | |||
* If you didn't specify security setting for _AD_Open then security is set to 1 (encrypt userid and password) else the security setting for _AD_Open remains unchanged.</br>This is True for all scripts but _TEST_Open. | |||
* The security setting for _AD_Open gets overwritten with a value of 2 (encrypt the channel using Secure Sockets Layer (SSL)). This is because you added 16 to the security setting.</br>This is True for _TEST_Open. | |||
This rules apply to the Global variable $__iSecurity as well. | |||
=== Error handling === | === Error handling === | ||
Line 125: | Line 158: | ||
[3] LDAP Provider - name of the provider that raised the error | [3] LDAP Provider - name of the provider that raised the error | ||
[4] 52e - Win32 error code extracted from element[2] | [4] 52e - Win32 error code extracted from element[2] | ||
[5] Logon failure: unknown user name or bad password - description of the Win32 error code | [5] Logon failure: unknown user name or bad password - description of the Win32 error code | ||
Some of the Win32 error codes you can see: | Some of the Win32 error codes you can see: | ||
Line 148: | Line 181: | ||
* _AD_SetPasswordExpire (not affected) | * _AD_SetPasswordExpire (not affected) | ||
<br /> | <br /> | ||
The following functions | The following functions do not (fully) support FGPP: | ||
* _AD_GetPasswordExpired (does not support FGPP) | * _AD_GetPasswordExpired (does not support FGPP) | ||
* _AD_GetPasswordInfo (all returned values are based on the Group Policy, except value 13 (calculated password expiration date/time) which takes FGPP into account) | * _AD_GetPasswordInfo (all returned values are based on the Group Policy, except value 13 (calculated password expiration date/time) which takes FGPP into account) | ||
Line 154: | Line 187: | ||
== Debugging == | == Debugging == | ||
=== _AD_ErrorNotify === | === _AD_ErrorNotify === | ||
To get detailed error information about COM errors add function _AD_ErrorNotify. This function traps all COM errors and logs them. The following values can be used as first parameter: | To get detailed error information about COM errors add function _AD_ErrorNotify. This function traps all COM errors and logs them. The following values can be used as first parameter: | ||
Line 177: | Line 211: | ||
* [[Active_Directory_UDF_-_GetObjectsInOU|_AD_GetObjectsInOU]]: This function uses LDAP to query objects (users, computers ...) in the whole domain or a subtree. | * [[Active_Directory_UDF_-_GetObjectsInOU|_AD_GetObjectsInOU]]: This function uses LDAP to query objects (users, computers ...) in the whole domain or a subtree. | ||
* [[Active_Directory_UDF_-_GetObjectProperties|_AD_GetObjectProperties]]: This function returns all or just the specified properties of an object in a "decoded" (readable form). | * [[Active_Directory_UDF_-_GetObjectProperties|_AD_GetObjectProperties]]: This function returns all or just the specified properties of an object in a "decoded" (readable form). | ||
== Example scripts == | |||
=== Query Bitlocker Recovery Information === | |||
<syntaxhighlight lang="autoit"> | |||
; Class msFVE-RecoveryInformation: https://docs.microsoft.com/en-us/windows/desktop/ADSchema/c-msfve-recoveryinformation | |||
; Delegating access in AD to BitLocker recovery information: https://blogs.technet.microsoft.com/craigf/2011/01/26/delegating-access-in-ad-to-bitlocker-recovery-information/ | |||
#include <ad.au3> | |||
_AD_Open() | |||
$sAD_OU = _AD_SamAccountNameToFQDN(@ComputerName & "$") | |||
$aResult = _AD_GetObjectsInOU($sAD_OU, "(objectcategory=msFVE-RecoveryInformation)", 2, "distinguishedname") | |||
If @error <> 0 Then Exit MsgBox(64, "AD Test", "_AD_GetObjectsInOU @error: " & @error & ", @extended: " & @extended) | |||
_ArrayDisplay($aResult, "Result of _AD_GetObjectsInOU") | |||
$aResult = _AD_GetObjectProperties($aResult[1]) | |||
If @error <> 0 Then Exit MsgBox(64, "AD Test", "_AD_GetObjectProperties @error: " & @error & ", @extended: " & @extended) | |||
_ArrayDisplay($aResult, "Result of _AD_GetObjectProperties") | |||
_AD_Close() | |||
</syntaxhighlight> | |||
== Tips & Tricks == | == Tips & Tricks == | ||
Line 186: | Line 239: | ||
* How to run the AD UDF on Windows PE is described [https://www.autoitscript.com/forum/topic/109251-active-directory-udf-help-support/?do=findComment&comment=910175 here]. | * How to run the AD UDF on Windows PE is described [https://www.autoitscript.com/forum/topic/109251-active-directory-udf-help-support/?do=findComment&comment=910175 here]. | ||
== Good reading == | |||
* [https://blogs.technet.microsoft.com/389thoughts/2017/02/03/uniqueness-requirements-for-attributes-and-objects-in-active-directory/ Uniqueness requirements for attributes and objects in Active Directory] | |||
[[Category:UDF]] | [[Category:UDF]] |
Latest revision as of 08:42, 6 June 2020
The Active Directory UDF offers functions to control and manipulate Microsoft Active Directory. This page describes the Active Directory UDF in general.
Concept
The Active Directory UDF is based upon this concept:
- Open a connection to the AD, do all the work then close the connection
- Access one domain at a time. Cross domain scripts are not possible with this UDF
- Access the Global Catalog to search the complete forest
- Access to the Active Directory can be made secure using SSL for both the domain and the Global Catalog
More information can be found on MSDN.
Prerequisites
You have to run version 3.3.6.0 of AutoIt or newer.
Structure
Every script calls at least the following functions:
_AD_Open() ; Open a connection to the Active Directory
_AD_* ; Any function that queries or alters the Active Directory
_AD_Close() ; Close the connection
Open a connection
There are multiple ways to open a connection to the Active Directory.
Only one connection can be open at any time. This means you can't connect to two domains at the same time. You have to connect to the first domain, do all the work, close the connection and open the connection to the second domain. This is true for the Global Catalog as well.
To current domain
No additional information is needed if you want to connect to the domain the computer is already connected to. _AD_Open uses the credentials of the currently logged in user to connect to the domain. If the currently logged in user doesn't have the required privileges you can specify the credentials of a different user.
Example:
Open an AD connection to the domain the computer has logged in and use the windows logon credentials:
Local $iResult = _AD_Open()
Example:
Open an AD connection to the domain the computer has logged in and specify the credentials to use. The UserID has to be in one of the following formats (assume samAccountName = DJ and domain name = microsoft):
Windows Login Name e.g. "DJ" NetBIOS Login Name e.g. "microsoft\DJ" User Principal Name e.g. "DJ@microsoft.com"
The following examples are equivalent:
$iResult = _AD_Open("DJ", "password of DJ")
$iResult = _AD_Open("microsoft\DJ", "password of DJ")
$iResult = _AD_Open("DJ@microsoft.com", "password of DJ")
If you want to connect to a specific Domain Controller in the domain then simply specify the HostServer.
Example:
$iResult = _AD_Open("DJ", "password of DJ", "", "DC1.domain.com") ; setting alternate credentials
$iResult = _AD_Open("", "", "", "DC1.domain.com") ; using the windows login credentials
To another domain
Much more information is needed if you want to connect to a different domain than the computer is already connected to. You have to specify DNSDomain, HostServer and Configuration. If you don't specify alternate credentials then the credentials of the currently logged in windows user are used.
Examples of the needed parameters:
$sDNSDomainParam = Active Directory domain name e.g. "DC=subdomain,DC=example,DC=com"
$sHostServerParam = Name of Domain Controller e.g. "servername.subdomain.example.com" or "subdomain.example.com" (access the domain root)
$sConfigurationParam = Configuration naming context e.g. "CN=Configuration,DC=subdomain,DC=example,DC=com"
Example:
Open an AD connection to another domain and use the windows logon credentials:
Local $iResult = _AD_Open("", "", "DC=subdomain,DC=example,DC=com", "servername.subdomain.example.com", "CN=Configuration,DC=subdomain,DC=example,DC=com")
Example:
Open an AD connection to another domain and specify alternate credentials:
Local $iResult = _AD_Open("DJ", "password of DJ", "DC=subdomain,DC=example,DC=com", "servername.subdomain.example.com", "CN=Configuration,DC=subdomain,DC=example,DC=com")
More information on how to specify credentials can be found here.
From a workgroup
If your computer is not connected to any domain you have to provide the same information as if connecting to another domain. How to connect to another domain is described here.
To a Global Catalog
If you want to connect to a Global Catalog - so the search functions work on the whole forest and not just on a single domain - you have to append port number 3268 to the HostServer parameter.
Example:
Local $iResult = _AD_Open("", "", "", "DC1.company.com:3268")
All functions now access the Global Catalog. Every search function now returns results for the whole forest not just the domain.
In a forest there can be multiple Global Catalogs. To get a list of Global Catalogs connect to the domain and use _AD_ListDomainControllers.
Local $iResult = _AD_Open()
Local $aDCs = _AD_ListDomainControllers()
For $iIndex = 1 to $aDCs[0][0]
If $aDCs[$iIndex][6] = True Then ConsoleWrite("DC " & $aDCs[$iIndex][0] & " is a Global Catalog")
Next
_AD_Close()
Secure connection
To a Domain Controller
If you want to use SSL and/or password encryption when you connect to a Domain Controller you have to set parameter 6 of _AD_Open.
Valid entries are:
1 = Sets the connection property "Encrypt Password" to True to encrypt userid and password 2 = The channel is encrypted using Secure Sockets Layer (SSL). AD requires that the Certificate Server be installed to support SSL 3 = Combination of 1 and 2
Example:
Local $iResult = _AD_Open("", "", "", "", "", 2) ; Uses SSL to connect to a Domain Controller of the current domain
To a Global Catalog
If you want to use SSL for your connection to the Global Catalog use port 3269 instead of 3268.
Set security settings from outside your script
Microsoft has changed the minimum security requirements for LDAP access as described here.
If you do not want to modify all of your AutoIt scripts you can modify the security setting from outside. The UDF provides several levels to set the security from outside. The levels are processed from low to high, overwriting a previously set level.
- AD.INI file in the AutoIt include directory with a GLOBAL section (this is used for ALL scripts. It's a Global setting)
- AD.INI file in the AutoIt include directory with a <scriptname> section (this is used for scripts named <scriptname>.au3 or <scriptname>.exe. It's a Global setting)
- AD.INI file in the working directory with a GLOBAL section name (this is used for ALL scripts in the working directory. It's a Local setting)
- AD.INI file in the working directory directory with a <scriptname> section name (this is used for scripts named <scriptname>.au3 or <scriptname>.exe in the working directory. It's a Local setting)
- Set Global variable $__iSecurity in your script or one of your include files
Example of an INI-file:
[Global] Security=1 [TEST_Open] Security=18
The Global variable $__iSecurity or the SECURITY key from the INI files can have the values as specified for the $iSecurity parameter.
If $iSecurity was not specified in _AD_Open then this Global values overwrite $iSecurity.
If $iSecurity was specified in _AD_Open then you need to add 16 to this Global values to overwrite $iSecurity.
This means for the INI file above:
- If you didn't specify security setting for _AD_Open then security is set to 1 (encrypt userid and password) else the security setting for _AD_Open remains unchanged.
This is True for all scripts but _TEST_Open. - The security setting for _AD_Open gets overwritten with a value of 2 (encrypt the channel using Secure Sockets Layer (SSL)). This is because you added 16 to the security setting.
This is True for _TEST_Open.
This rules apply to the Global variable $__iSecurity as well.
Error handling
_AD_Open sets the return value to 0 and @error plus @extended to denote what kind of error occurred.
On Windows Vista and later - if you specified the UserID as NetBIOS Login Name or User Principal Name - you can get additional information about the error.
_AD_GetlastADSIError will return an array of additional information:
Local $iResult = _AD_Open()
If @error <> 0 Then
Local $aError = _AD_GetlastADSIError()
_ArrayDisplay($aError, "Error occurred when running _AD_Open")
Exit
EndIf
This could look like:
[0] 5 [1] 2148074248 - ADSI error code (decimal) [2] 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db0 - Unicode string that describes the error [3] LDAP Provider - name of the provider that raised the error [4] 52e - Win32 error code extracted from element[2] [5] Logon failure: unknown user name or bad password - description of the Win32 error code
Some of the Win32 error codes you can see:
525 = user not found 52e = invalid credentials 530 = not permitted to logon at this time 532 = password expired 533 = account disabled 701 = account expired 773 = user must reset password
Fine-Grained Password Policy
The UDF does not fully support the Fine-Grained Password Policy (FGPP). The following functions have been checked how they support the FGPP:
- _AD_IsPasswordExpired (supports FGPP)
- _AD_GetPasswordDontExpire (not affected)
- _AD_SetPassword (not affected)
- _AD_ChangePassword (not affected)
- _AD_DisablePasswordExpire (not affected)
- _AD_EnablePasswordExpire (not affected)
- _AD_EnablePasswordChange (not affected)
- _AD_DisablePasswordChange (not affected)
- _AD_SetPasswordExpire (not affected)
The following functions do not (fully) support FGPP:
- _AD_GetPasswordExpired (does not support FGPP)
- _AD_GetPasswordInfo (all returned values are based on the Group Policy, except value 13 (calculated password expiration date/time) which takes FGPP into account)
- _AD_IsObjectLocked (does not support FGPP)
Debugging
_AD_ErrorNotify
To get detailed error information about COM errors add function _AD_ErrorNotify. This function traps all COM errors and logs them. The following values can be used as first parameter:
1 = Uses ConsoleWrite to display the COM error message. Therefore doesn't work for compiled scripts 2 = Uses MsgBox to display the COM error message 3 = Uses FileWrite to write (append) the COM error message to a defined file 4 = Enable Debugging. The COM errors will be handled (the script no longer crashes) but without any output
The second parameter sets the output file. Default = @ScriptDir & "\AD_Debug.txt".
The following example parses the commandline and sets debugging to "MsgBox" when command line parameter one is set to "/debug" or "-debug".
If $CmdLine[0] And ($CmdLine[1] = "/debug" Or $CmdLine[1] = "-debug") Then _AD_ErrorNotify(2)
_AD_Open
When _AD_Open returns @error = 4 and @extended = -2147023541 (decimal) = 0x8007054b (hex) which translates to "The specified domain either does not exist or could not be contacted." then
- most of the time the reason is that you try to connect using a local account. To solve the issue logon with a domain account or provide the needed parameters to _AD_Open.
- you didn't save your compiled script to a "secure location". Secure locations are defined by group policies. I suggest to copy the exe to another directory and try again or ask your sysadmin.
- if you run a script compiled for 32 bit on a 64 Windows and SysWOW64 redirection is disabled then you will see the same error. Please check: https://www.autoitscript.com/forum/topic/106163-active-directory-udf/?do=findComment&comment=1327001
Running queries
There are many functions in the UDF that allow to query the Active Directory. The most important functions are:
- _AD_GetObjectsInOU: This function uses LDAP to query objects (users, computers ...) in the whole domain or a subtree.
- _AD_GetObjectProperties: This function returns all or just the specified properties of an object in a "decoded" (readable form).
Example scripts
Query Bitlocker Recovery Information
; Class msFVE-RecoveryInformation: https://docs.microsoft.com/en-us/windows/desktop/ADSchema/c-msfve-recoveryinformation
; Delegating access in AD to BitLocker recovery information: https://blogs.technet.microsoft.com/craigf/2011/01/26/delegating-access-in-ad-to-bitlocker-recovery-information/
#include <ad.au3>
_AD_Open()
$sAD_OU = _AD_SamAccountNameToFQDN(@ComputerName & "$")
$aResult = _AD_GetObjectsInOU($sAD_OU, "(objectcategory=msFVE-RecoveryInformation)", 2, "distinguishedname")
If @error <> 0 Then Exit MsgBox(64, "AD Test", "_AD_GetObjectsInOU @error: " & @error & ", @extended: " & @extended)
_ArrayDisplay($aResult, "Result of _AD_GetObjectsInOU")
$aResult = _AD_GetObjectProperties($aResult[1])
If @error <> 0 Then Exit MsgBox(64, "AD Test", "_AD_GetObjectProperties @error: " & @error & ", @extended: " & @extended)
_ArrayDisplay($aResult, "Result of _AD_GetObjectProperties")
_AD_Close()
Tips & Tricks
- The UDF does not fully support the "granular password policy" feature implemented with Windows Server 2008. Please check the section about Fine-Grained Password Policy above.
- Keyword "Default": You can't use the keyword "Default" to omit parameters in a function call as the UDF does not support this keyword (Edit: The UDF supports Default for versions > 1.4.7.0)
- If you run Windows Vista or higher, UAC is enabled and you use functions that change the AD then you might need to insert #RequireAdmin into your script
- The SamAccountName of a computer is the computername with a trailing "$" e.g. @ComputerName & "$"
- Special characters: If you call a function with a Fully Qualified Domain Name (FQDN) as parameter you as a user have to make sure that all special characters ("\/#,+<>;=) are escaped with a preceding backslash. You can call function _AD_FixSpecialChars(String, 1) to escape or _AD_FixSpecialChars(string, 0) to unescape the special characters
- How to run the AD UDF on Windows PE is described here.