{"id":72,"date":"2012-11-06T23:06:39","date_gmt":"2012-11-07T07:06:39","guid":{"rendered":"http:\/\/blog.ls-al.com\/?p=72"},"modified":"2012-11-06T23:06:39","modified_gmt":"2012-11-07T07:06:39","slug":"python-ldap-query-active-directory","status":"publish","type":"post","link":"https:\/\/blog.ls-al.com\/python-ldap-query-active-directory\/","title":{"rendered":"Python-ldap Query MS Active Directory"},"content":{"rendered":"

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.\u00a0 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.<\/p>\n

I noticed on Ubuntu 12.10 that my query failed with the following error:<\/strong><\/p>\n

ldap.LDAP_CONTROL_PAGE_OID,True,(page_size,'')\r\nAttributeError: 'module' object has no attribute 'LDAP_CONTROL_PAGE_OID<\/span>'<\/pre>\n

I found a comment from the developers saying that with python-ldap 2.4
\n\"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.\"<\/em><\/p>\n

I found the source here: http:\/\/python-ldap.cvs.sourceforge.net\/viewvc\/python-ldap\/python-ldap\/Demo\/page_control.py?view=log<\/a><\/p>\n

I made a new script and tested as shown below:<\/strong>
\n** Note the following first:<\/p>\n

    \n
  1. This is a \"paged\" query meaning you should not have issues if LDAP return a limted number of results.\u00a0 More detail here: http:\/\/support.microsoft.com\/kb\/315071<\/li>\n
  2. You need to install python-ldap of course.\u00a0 apt-get install python-ldap should work on apt systems.<\/li>\n
  3. Check comment about using 512 as userAccountControl.<\/li>\n<\/ol>\n
    DistributionList = \"CN=IT Infrastructure,CN=Distribution Lists,CN=Users,DC=domain,DC=com\"\r\n\r\nurl = \"ldap:\/\/usdc101\"\r\nbase = \"dc=domain,dc=com\"\r\n#  search_flt = r'(objectClass=*)'\r\n##  I used userAccountControl=512 but that would most certainly exclude some \r\n##  user accounts in your domain. For instance \"66048 Enabled, Password Doesn't Expire\"\r\n##  values listed here: http:\/\/www.netvision.com\/ad_useraccountcontrol.php\r\nsearch_flt = r'(&(objectCategory=user) (userAccountControl=512) )'\r\n\r\npage_size = 10\r\n\r\nimport ldap,pprint\r\nfrom ldap.controls import SimplePagedResultsControl\r\n\r\nsearchreq_attrlist=[\"displayName\",\"cn\",\"distinguishedName\",\"mail\",\"memberOf\"]\r\n\r\nldap.set_option(ldap.OPT_REFERRALS, 0)\r\nl = ldap.initialize(url,trace_level=0)\r\nl.protocol_version = 3\r\nl.simple_bind_s(\"ADaccount@domain.com\", \"passsword\")\r\n\r\nreq_ctrl = SimplePagedResultsControl(True,size=page_size,cookie='')\r\n\r\nknown_ldap_resp_ctrls = {\r\nSimplePagedResultsControl.controlType:SimplePagedResultsControl,\r\n}\r\n\r\n# Send search request\r\nmsgid = l.search_ext(\r\nbase,\r\nldap.SCOPE_SUBTREE,\r\nsearch_flt,\r\nattrlist=searchreq_attrlist,\r\nserverctrls=[req_ctrl]\r\n)\r\n\r\npages = 0\r\ni = 0\r\nprint \"listing users in the list:\" + DistributionList\r\n\r\nwhile True:\r\n  pages += 1\r\n  rtype, rdata, rmsgid, serverctrls = l.result3(msgid,resp_ctrl_classes=known_ldap_resp_ctrls)\r\n\r\n  for dn, entry in rdata:\r\n    ##  Lets check if the user is a member of the AD List \/ Group\r\n    try:\r\n      membership = entry['memberOf']\r\n    except:\r\n      membership = 'none'\r\n    if DistributionList in membership:\r\n      i += 1\r\n      print \" \\\"%d\\\" | \\\"%s\\\" \" % (i , dn)\r\n\r\n  pctrls = [\r\n    c\r\n    for c in serverctrls\r\n    if c.controlType == SimplePagedResultsControl.controlType\r\n  ]\r\n  if pctrls:\r\n    if pctrls[0].cookie:\r\n      # Copy cookie from response control to request control\r\n      req_ctrl.cookie = pctrls[0].cookie\r\n      msgid = l.search_ext(\r\n        base,\r\n        ldap.SCOPE_SUBTREE,\r\n        search_flt,\r\n        attrlist=searchreq_attrlist,\r\n        serverctrls=[req_ctrl]\r\n      )\r\n    else:\r\n      break\r\n  else:\r\n    print \"Warning: Server ignores RFC 2696 control.\"\r\n    break\r\n\r\nl.unbind_s()<\/pre>\n","protected":false},"excerpt":{"rendered":"

    I use Python to pull Active Directory information sometimes. In my case mainly to report or view information but also<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,8,13],"tags":[],"class_list":["post-72","post","type-post","status-publish","format-standard","hentry","category-active-directory","category-kvm","category-python"],"_links":{"self":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/posts\/72","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/comments?post=72"}],"version-history":[{"count":0,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/posts\/72\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/media?parent=72"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/categories?post=72"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/tags?post=72"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}