Welcome, guest | Sign In | My Account | Store | Cart

For Windows Exchange Server running on Windows Server 2000/2003, from Exchange and Active Directory, get all email addresses (and aliases) from all active users. Uses ASDI scripting, LDAP paths to get user info from Active Directory. Uses Pythonwin

Python, 126 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from win32com.client import (
    GetObject,
    Dispatch,
    )
from pythoncom import (
    com_error,
    )

# --------- #

Connection = Dispatch("ADODB.Connection")

Connection.Open("Provider=ADSDSOObject")

# --------- #

class Bunch(dict):
    def __init__(self,**kw):
        dict.__init__(self,kw)
        self.__dict__ = self
        
def LDAP_select_all_iterator(LDAP_path_str):

    R = Connection.Execute(
        "SELECT * From '%s'" % LDAP_path_str)[0]

    while not R.EOF:

        d = Bunch()
        for f in R.Fields:
            d[f.Name] = f.Value
        yield d

        R.MoveNext()

class LDAP_ObjectWrapper(object):

    def __init__(self, WINCOM_LDAP_Object):
        self._o = WINCOM_LDAP_Object

    @classmethod
    def from_LDAP_path(cls, LDAP_path_str):
        return cls(GetObject(LDAP_path_str))

    def __getattr__(self, name):
        try:
            return getattr(self._o, name)
        except (AttributeError, com_error):
            pass
        try:
            return self._o.Get(name)
        except com_error:
            raise AttributeError
        raise AssertionError

    def GetInfo(self):
        self._o.GetInfo()

# --------- #

defaultNamingContext = (
    LDAP_ObjectWrapper.from_LDAP_path(
        'LDAP://rootDSE').defaultNamingContext)

# --------- #
                     
results = {}

def _latin_lower(u):
    return u.encode('latin-1').lower()

for r in LDAP_select_all_iterator(
    'LDAP://CN=Users,%s' % (defaultNamingContext,)):
    
    UserObject = LDAP_ObjectWrapper.from_LDAP_path(r.ADsPath)
    UserObject.GetInfo()

    if getattr(UserObject, 'AccountDisabled', False):
        continue

    try:
        proxyAddresses = list(UserObject.proxyAddresses)
    except (AttributeError, TypeError):
        proxyAddresses = []

    if not proxyAddresses:
        continue

    email_addresses = []
    for s in proxyAddresses:
        if s.startswith('SMTP:'):
            email_addresses.append(
                ('MAIN', _latin_lower(s[5:])))
        elif s.startswith('smtp:'):
            email_addresses.append(
                ('alias', _latin_lower(s[5:])))

    if not email_addresses:
        continue
    
    email_addresses.sort()
        
    misc_info = []
    for n in ['displayName', 'name', 'description']:
        try:
            v = getattr(UserObject, n)
        except AttributeError:
            pass
        else:
            if v:
                misc_info.append(
                    repr(v.encode('latin-1')))
    assert misc_info
    description_str = ' '.join(misc_info)

    key = email_addresses[0][1]

    assert key not in results    

    results[key] = (description_str, email_addresses)

for (i, key) in enumerate(sorted(results.keys())):
    description_str, email_addresses = results[key]
    print i, description_str
    for t in email_addresses:
        print '    %-5s %s' % t

Was setting up 3rd party spam filtering, and wanted to get the authorative list of active email address and aliases from the Exchange server itself. Microsoft's Scripting Guide and ADSI Scriptomatic were woe-fully inadequate. Was not able to find any script in any language that would automatically discover all email addresses. This was my first attempt at ADSI scripting, and I am pretty happy with the results. My next project will be doing authoratative reporting on user security groups. I used some features of Python 2.3 and 2.4, but it would be trivial to modify for earlier Python versions. In anticipation of futher ADSI scripting, I created iterators and wrappers that cleaned up the look of the code.

4 comments

Allan Anderson 17 years, 11 months ago  # | flag

Thanks! This was very useful for me. I'm modifying the gnatsparse script that comes with Bugzilla. GNATS users are listed in a firstname.lastname format, and the bug reporter can be any email address or alias. With this, I can look up all that information and make sure to pick a real user. Very cool!

Keith Weller 17 years, 11 months ago  # | flag

Question about this script. I need to extract all smtp addresses from AD, and this looks like it will do that, but I don't know anything about Python. Is this something I have to purchase to be able to use? Is there a runtime version or something? Thanks for any help.

Ramon Linan 17 years, 6 months ago  # | flag

cool, how do i use it? Hi, this can be a useful scrip, how do i use it?

Also, i would really appreciate if you could explain a little bit how the scripts works.

Thanks

Rezuma

Bobby Forte 17 years, 3 months ago  # | flag

Small Business Users. This scripts is really great..

Question: how can I change the query from CN=Users of Active Directory to MyBusiness/Users/SBSUsers ?

With this script I can capture all users from Container Users but I cannot capture the Users from domain.local/MyBusiness/Users/SBSUsers Organizational unit..

please help you can email me at bobby@seerx.com

Thanks