3

How can I view LDAP Query executed by Get-ADUser behind the scenes, ie. this command

Get-ADUser -Filter * -Properties * | Where { $_.Enabled -eq $True } | Where { $_.PasswordNeverExpires -eq $False } | Where { $_.PasswordExpired -eq $False }

I would like to know exact LDAP query sent to Active Directory servers.

Edit: I am trying to convert some Powershell scripts to Python, therefore I need raw LDAP query I can feed to python-ldap.

Edit2: Active Directory Administrative Center has nice feature for learning LDAP queries. In Global search you can build query by using keywords and ticking checkboxes, and than click on Convert to LDAP. Enjoy nice and complex LDAP query.

ADAC GUI

  • You can remove the first pipe and everything following. The Where clauses perform their work on the PowerShell object returned from the Get-ADUser, not by querying AD again. – jscott Nov 24 '14 at 18:03
  • @jscott I was hoping that Get-ADUser command behaves like ORM where you can always get raw SQL query sent to DB. – svakak Nov 24 '14 at 18:16
  • 1
    I think you should reword your question to indicate you want an LDAP Filter for enabled users, with "expired" passwords and the userAccountControl flag for UF_DONT_EXPIRE_PASSWD. – jscott Nov 24 '14 at 19:04
  • @jscott I'm not looking for solution for this one specific query, instead looking for tool where I could write Get-ADUser command (it's easy), and get LDAP query (more complicated to write). – svakak Nov 24 '14 at 20:23
6

The ActiveDirectory module has clever logic that calculate "popular" properties like whether a user account is Enabled or have PasswordNeverExpires set and presents them like regular attributes.

Internally, they're derived from actual account attributes like userAccountControl and pwdLastSet.

Account Settings

userAccountControl is a bitfield, and contains a long list of account security related settings, like "User cannot change password" and account "Disabled".

Microsoft's LDAP implementation let's you filter such an attribute with bitwise operators, identified by an Object Identifier (OID):

  • LDAP_MATCHING_RULE_BIT_AND: 1.2.840.113556.1.4.803
  • LDAP_MATCHING_RULE_BIT_OR : 1.2.840.113556.1.4.804

To find Disabled accounts, we can use the following filter syntax:

(&(userAccountControl:1.2.840.113556.1.4.803:=2))

As always, you can negate an ldap expression with !, in this example retrieving all Enabled accounts:

(!(userAccountControl:1.2.840.113556.1.4.803:=2))

Likewise, a setting called DONT_EXPIRE_PASSWORD has value 65536 (0x10000), and we can find these accounts with:

(&(userAccountControl:1.2.840.113556.1.4.803:=65536))

Password Expiration

Calculating password expiration is a bit more complicated. To aid developers and integrators, Microsoft has implemented a dynamic attribute named msDS-User-Account-Control-Computed.

msDS-User-Account-Control-Computed transparently returns the same value as userAccountControl, but with the addition of the following bits, computed on the fly at lookup time:

UF_LOCKOUT                     0x0010
UF_PASSWORD_EXPIRED          0x800000
UF_PARTIAL_SECRETS_ACCOUNT  0x4000000
UF_USE_AES_KEYS             0x8000000

I haven't tested this, but if memory serves me right, this should help you reliably identify accounts with expired passwords with this filter:

(&(msDS-User-Account-Control-Computed:1.2.840.113556.1.4.803:=8388608))

Unfortunately you cannot use contructed attributes in LDAP query filters, so what you'll have to do is filter on the first two statements:

(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(!userAccountControl:1.2.840.113556.1.4.803:=65536))

Be sure to ask the directory server for the msDS-User-Account-Control-Computed value, and then perform your bitwise AND mask on the results, client-side.

  • Unfortunately msDS-User-Account-Control-Computed is computed attribute and cannot be used in LDAP search. – svakak Nov 25 '14 at 8:23
  • My mistake, you'll have to retrieve all objects matching the first two expressions and inspect the value of msDS-User-Account-Control-Computed client-side side then – Mathias R. Jessen Nov 25 '14 at 9:38
7

If you really want to know what exact LDAP queries are being performed by the Powershell Cmdlets, then you can decompile them by using DotPeek, using the method I outline here:

https://www.myotherpcisacloud.com/post/2013/07/08/Taking-a-Peek-Inside-Powershell-Cmdlets.aspx

Use $(Get-Command Get-ADUser).DLL to see what DLL the Cmdlet is being imported from. Then use Trace-Command to see the name of the method within the DLL that is called by Get-ADUser.

Trace-Command -Name CommandDiscovery -Expression { Get-ADUser bob } -PSHost

Now use JetBrains DotPeek to decompile that DLL and look at the code for yourself.


Alternatively, instead of going through all that mess, why don't you just do this:

Get-ADUser -LDAPFilter "(objectCategory=person)"

And now you know (and have control over) exactly what LDAP query it's using.

  • 3
    I would choose -LDAPFilter over -Filter for almost any Get-Ad* operation. Thanks for the DotPeek link. – jscott Nov 24 '14 at 18:35
  • 3
    -Filter has it's merits when it comes to precompiled matching rules, like Enabled -eq $True vs. (&(!userAccountControl:1.2.840.113556.1.4.803:=2)) – Mathias R. Jessen Nov 24 '14 at 19:30
4

I think you'd have the best luck spending some time learning about the AD schema and building queries yourself, rather than trying to reverse-engineer Microsoft's own tools. AD is enough of a plain-vanilla LDAP directory that reference articles that speak about LDAP generally will be applicable. There are some oddball things like bit fields in attributes inherited from Windows NT 4.0 that are fairly important for sysadmin-type queries, for example.

  • I had no luck with Wireshark, because Powershell uses Active Directory Web Services over TCP/9389 port, and not standard LDAP blogs.msdn.com/b/adpowershell/archive/2009/04/06/…. – svakak Nov 25 '14 at 8:40
  • @svakak - Quite right. Teaches me for talking about cmdlets that I never actually use. I've removed the incorrect material. – Evan Anderson Nov 25 '14 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.