Active Directory is Microsoft's answer to LDAP, the industry-standard directory service holding information about users, computers and other resources in a tree structure, arranged by departments or geographical location, and optimized for searching.

There are several ways of attaching to Active Directory. This module uses the Dispatchable LDAP:// objects and wraps them lightly in helpful Python classes which do a bit of the otherwise tedious plumbing. The module is quite naive, and has only really been developed to aid searching, but since you can always access the original COM object, there's nothing to stop you using it for any AD operations.

The principal object is the AD_object class instantiated either with a AD path (something like ) or with an existing AD object, eg from GetObject ("LDAP://RootDSE"). From this object, you can enumerate its children (it's its own iterator so just loop over it), find its parent or search on its members and subtrees.
Note that, previously, you had to specify path= if you were passing a string path representation. Now you can simply pass the the string or the object. ie either of these things is possible:

import active_directory

me = active_directory.AD_object ("LDAP://ou=it,dc=gb,dc=vo,dc=local")

me = active_directory.AD_object (GetObject ("LDAP://ou=it,dc=gb,dc=vo,dc=local"))

# Historical method
me = active_directory.AD_object (path="LDAP://ou=ot,dc=gb,dc=vo,dc=local")

For convenience, and given that you're nearly always going to be operating on your default AD (ie the one you're logged into), there are some useful module-level functions:

With the exception of .root, each of these methods can also be applied to any AD object returned. (In fact, the module-level functions simply hand off to the root object). Some effort has been made to wrap the most common objects and their properties so that, for example, the .member property of an AD group returns a list of wrapped objects representing its members. If any of them are themselves groups, they will have a similarly wrapped method.

The .root object is the result of a call to the module-level AD function against the default domain. To connect to a different domain, you could instantiate a root against a different domain controller:

import active_directory
my_root = active_directory.root ()
print my_root
for master in my_root.masteredBy:
  print master.Parent.dNSHostName

other_root = active_directory.AD ("other_dc")
print other_root
for master in other_root.masteredBy:
  print master.Parent.dNSHostName

or instantiate a different domain root:
import active_directory
domain = active_directory.AD_object ("LDAP://gb.vo.local")
repr (domain)
# '<_AD_domain_dns: LDAP://gb.vo.local/DC=gb,DC=vo,DC=local>'

Every AD object offers its properties via attribute access. To get a rough-and-ready look at an object, call its .dump method. In addition there is a small number of extra convenience functions and more will be added. The most useful one, perhaps, is the .walk method on groups, which mimics the behaviour of os.path but for AD groups, iterating recursively over the users and subgroups in a group:

import active_directory
all_users = set ()
for group, groups, users in active_directory.find_group ("Domain Admins").walk ():
  all_users.update (users)

for u in all_users:
  print u.displayName

Wrapped properties are given slightly more pythonic access. (At present this is readonly access; hopefully write access will be added before long). This means that properties returning other AD objects will have the objects wrapped, so you can chain calls on them. Properties returning internal Windows objects such as SIDs or Security Descriptors will have those wrapped. Raw data will be presented as hex strings, and guids in the conventional format. The AD datetime type, mysteriously representing the number of 100-nanosecond intervals since 1st Jan 1601 (!) is represented as a Python datetime object. And so on. Since I can't find anyway to introspect the AD schemas dynmically, these converters are hardcoded at present.


import active_directory

for person in ("objectCategory='Person'"):
  print person.displayName

# or

for person in (objectCategory='Person'):
  print person.displayName

Note that the .search method has two styles of parameters, which can be mixed. If you need simple a=b and c=d operations, just use keyword parameters as in the second example above. If you need something more sophisticated with non-equality operators or embedded ors, then use positional parameters. All the parameters will be AND-ed together in the final result.

More examples


NB The module requires Python 2.4 at least (sets, datetime & genexps)

23th Mar 2007: (0.6.7, zipped - tiny) - readme - changelog

17th May 2005: (v0.4, zipped - tiny)

Installing It

Unzip to somewhere sensible (eg %TEMP%) and then just

python install