Skip to content

Python-ldap Query MS Active Directory

I use Python to pull Active Directory information sometimes. In my case mainly to report or view information but also to create files in a LDIF or PowerShell format.  These can be manually run on the Domain Controller later. For instance find all users in a Distribution List or Group and create a rule or ldif entry that can manually be executed line by line. Off course there is also ways with PowerShell and vbscript to do this, but I prefer Python for text manipulation and it is not too cumbersome for me to batch run these files manually later.

I noticed on Ubuntu 12.10 that my query failed with the following error:

ldap.LDAP_CONTROL_PAGE_OID,True,(page_size,'')
AttributeError: 'module' object has no attribute 'LDAP_CONTROL_PAGE_OID'

I found a comment from the developers saying that with python-ldap 2.4
"there have been changes in the API for LDAPv3 extended controls. Please see Demo/page_control.py (and Demo/paged_search_ext_s.py) how to use the simple paged control with 2.4."

I found the source here: http://python-ldap.cvs.sourceforge.net/viewvc/python-ldap/python-ldap/Demo/page_control.py?view=log

I made a new script and tested as shown below:
** Note the following first:

  1. This is a "paged" query meaning you should not have issues if LDAP return a limted number of results.  More detail here: http://support.microsoft.com/kb/315071
  2. You need to install python-ldap of course.  apt-get install python-ldap should work on apt systems.
  3. Check comment about using 512 as userAccountControl.
DistributionList = "CN=IT Infrastructure,CN=Distribution Lists,CN=Users,DC=domain,DC=com"

url = "ldap://usdc101"
base = "dc=domain,dc=com"
#  search_flt = r'(objectClass=*)'
##  I used userAccountControl=512 but that would most certainly exclude some 
##  user accounts in your domain. For instance "66048 Enabled, Password Doesn't Expire"
##  values listed here: http://www.netvision.com/ad_useraccountcontrol.php
search_flt = r'(&(objectCategory=user) (userAccountControl=512) )'

page_size = 10

import ldap,pprint
from ldap.controls import SimplePagedResultsControl

searchreq_attrlist=["displayName","cn","distinguishedName","mail","memberOf"]

ldap.set_option(ldap.OPT_REFERRALS, 0)
l = ldap.initialize(url,trace_level=0)
l.protocol_version = 3
l.simple_bind_s("ADaccount@domain.com", "passsword")

req_ctrl = SimplePagedResultsControl(True,size=page_size,cookie='')

known_ldap_resp_ctrls = {
SimplePagedResultsControl.controlType:SimplePagedResultsControl,
}

# Send search request
msgid = l.search_ext(
base,
ldap.SCOPE_SUBTREE,
search_flt,
attrlist=searchreq_attrlist,
serverctrls=[req_ctrl]
)

pages = 0
i = 0
print "listing users in the list:" + DistributionList

while True:
  pages += 1
  rtype, rdata, rmsgid, serverctrls = l.result3(msgid,resp_ctrl_classes=known_ldap_resp_ctrls)

  for dn, entry in rdata:
    ##  Lets check if the user is a member of the AD List / Group
    try:
      membership = entry['memberOf']
    except:
      membership = 'none'
    if DistributionList in membership:
      i += 1
      print " \"%d\" | \"%s\" " % (i , dn)

  pctrls = [
    c
    for c in serverctrls
    if c.controlType == SimplePagedResultsControl.controlType
  ]
  if pctrls:
    if pctrls[0].cookie:
      # Copy cookie from response control to request control
      req_ctrl.cookie = pctrls[0].cookie
      msgid = l.search_ext(
        base,
        ldap.SCOPE_SUBTREE,
        search_flt,
        attrlist=searchreq_attrlist,
        serverctrls=[req_ctrl]
      )
    else:
      break
  else:
    print "Warning: Server ignores RFC 2696 control."
    break

l.unbind_s()
Published inActive DirectoryKVMPython