Выгрузка почтовых адресов из Active Directory с помощью Python

Обновлено и опубликовано Опубликовано:

Мы рассмотрим простой скрипт для получение всех возможных почтовых адресов по LDAP из Active Directory. Скрипт можно взять за основу и использовать для других реализаций глобального каталога.

Подготовка системы

Рассмотрим процесс подготовки компьютера, на котором будет запускаться скрипт и сервера Active Directory.

Компьютер

В данном примере скрипт будет запускаться на компьютере с Linux.

1. Для начала установим сам Python и дополнительные компоненты:

а) На системах RPM (Red Hat, CentOS, Fedora):

yum install python3 python3-devel openldap-devel

б) На системах DEB (Debian, Ubuntu):

apt-get install python3 python3-pip libsasl2-dev python-dev libldap2-dev libssl-dev

2. Также установим модуль ldap для python:

pip3 install python-ldap

3. Создадим каталог, в котором будет находиться скрипт:

mkdir /scripts

Создадим сам скрипт:

vi /scripts/get_email_from_ad.py

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

* в данном примере мы создали скрипт get_email_from_ad.py с двумя строчками — шебангом и определением кодировки.

4. Даем разрешение скрипту на запуск:

chmod +x /scripts/get_email_from_ad.py

Active Directory

Для подключения к глобальному каталогу по LDAP нам нужна учетная запись для прохождения авторизации на сервере. Нужна учетная запись с минимальными правами.

Лучше всего для задач скрипта создать отдельного пользователя. В данной инструкции для связывания с Active Directory будет использоваться учетная запись export_emails. Создать ее можно с помощью инструмента Пользователи и компьютеры Active Directory.

Написание скрипта

Чтобы было проще разобраться, начнем разработку по шагам.

1. Подключение по LDAP

Для начала просто подключимся к глобальному каталогу. В наш скрипт добавим:

...

import ldap

try:
    lconn = ldap.initialize('ldap://dmosk.local:389')
    lconn.protocol_version = ldap.VERSION3
    lconn.set_option(ldap.OPT_REFERRALS, 0)
    lconn.simple_bind_s('export_emails@dmosk.local', 'export_emails_12345!')
except ldap.SERVER_DOWN:
    print("Error connection to AD")

* в данном примере мы подключаемся к серверу dmosk.local (домен должен разрешаться в любой из серверов глобального каталога) по порту 389. Для прохождения аутентификации мы используем логин export_emails и пароль export_emails_12345! (учетная запись, которая заранее нами была создана в Active Directory). Также мы указываем, что используется 3 версия протокола ldap и не должны использоваться рефералы.

Пробуем запустить скрипт:

/scripts/get_email_from_ad.py

Он должен вернуть пустую строку. Если мы получим ошибку, то необходимо проверить связь с сервером и корректность введелнных данных (логина и пароля).

2. Получение списка пользователей

Научимся извлекать пользователей по ldap. Добавим в наш скрипт:

...

base = "DC=dmosk,DC=local"
scope = ldap.SCOPE_SUBTREE
filter = "(&(objectcategory=person))"
attrs = ['displayname','title']
result_set = []

ldap_result_id = lconn.search_ext(base, scope, filter, attrs)

try:
  while 1:
    result_type, result_data = lconn.result(ldap_result_id, 0)
    if (result_data == []):
      break
    else:
      if result_type == ldap.RES_SEARCH_ENTRY:
        result_set.append(result_data)
except ldap.SIZELIMIT_EXCEEDED:
    print()

print(result_set)

* где:

  • base — начальный контейнер, с которого начинаем поиск объектов.
  • scope — область поиска. В нашем примере используется SCOPE_SUBTREE, что означает искать также в дочерних объектах.
  • filter — задаем фильтр. В текущем примере ищем объекты person (пользователи).
  • attrs — атрибуты объектов, которые нас интересуют.
  • result_set — массив данных с полученным результатом.
  • ldap_result_id — идентификатор поиска объектов в каталоге.

Запускаем наш скрипт:

/scripts/get_email_from_ad.py

Мы должны получить массив данных всех найденных пользователей и их атрибутов.

3. Получение почтовых атрибутов

На этом этапе мы отсеем все объекты, у которых нет адреса электронной почты. В итоге, наш массив данных будет содержать пользователей и атрибуты с адресами электронной почты.

Откроем наш скрипт и заменим строки:

...
filter = "(&(objectcategory=person))"
attrs = ['displayname','title']
...

... на:

filter = "(&(mail=*))"
attrs = ['mail','proxyAddresses']

* в данном примере мы выгрузим все объекты, у которых есть адрес почты (не пустой атрибут mail). Нас будут интересовать 2 атрибута — собственно, mail и proxyAddresses, в котором могут быть перечислены все возможные адреса объекта.
* данный фильтр позволит получить список не только пользователей, но и групп. Возможно, вам это не нужно. Тогда можно изменить наш фильтр, например на "(&(objectcategory=person)(mail=*))" — тогда мы получим только пользователей, у которых есть электронные адреса.

Запускаем наш скрипт:

/scripts/get_email_from_ad.py

Мы должны получить список объектов с адресами электронной почты.

4. Получение списка адресов электронной почты

На конец, вытащим почтовые адреса и составим общий список.

В нашем скрипте удаляем строку:

print(result_set)

Добавляем строки:

all_emails = []
for user in result_set:
    proxyAddresses = user[0][1].get('proxyAddresses')
    mail = user[0][1].get('mail')
    if (proxyAddresses):
        for email_b in proxyAddresses:
            email = email_b.decode("utf-8")
            all_emails.append(email.split(':')[1])
    else:
        all_emails.append(mail[0].decode("utf-8"))

unique_all_emails = list(set(all_emails))
print(*unique_all_emails, sep = '\n')

* и так, данными действиями мы перебираем полученный массив пользователей и извлекаем все email-адреса. Если у пользователя не пустой атрибут proxyAddresses, значит берем адреса из него, если у пользователя есть только mail, то берем его. В итоге, все данные помещаем в массив all_emails, который в конце преобразовываем для получения уникальных значений (в каталоге могут хранится одинаковые email для разных объектов).

Запускаем наш скрипт:

/scripts/get_email_from_ad.py

Мы должны получить список уникальных email-адресов для всех объектов, которые были найдены в глобальном каталоге.

Пример готового скрипта

Написанный нами скрипт можно представить так:

  1. #!/usr/bin/env python3
  2. # -*- encoding: utf-8 -*-
  3.  
  4. # Импортируем python-модули
  5. import ldap
  6.  
  7. # Задаем необходимые переменные
  8. domain = 'dmosk.local'
  9. base = 'DC=dmosk,DC=local'
  10. bind_dn = f'export_emails@{domain}'
  11. bind_dn_password = 'export_emails_12345!'
  12. scope = ldap.SCOPE_SUBTREE
  13. filter = "(&(mail=*))"
  14. attrs = ['mail','proxyAddresses']
  15. result_set = []
  16. all_emails = []
  17.  
  18. # Подключаемся к глобальному каталогу по LDAP
  19. try:
  20.     lconn = ldap.initialize(f'ldap://{domain}:389')
  21.     lconn.protocol_version = ldap.VERSION3
  22.     lconn.set_option(ldap.OPT_REFERRALS, 0)
  23.     lconn.simple_bind_s(bind_dn, bind_dn_password)
  24. except ldap.SERVER_DOWN:
  25.     print("Error connection to AD")
  26.  
  27. # Получаем результаты поиска объектов в AD
  28. ldap_result_id = lconn.search_ext(base, scope, filter, attrs)
  29.  
  30. Все результаты поиска объектов заносим в переменную result_set
  31. try:
  32.   while 1:
  33.     result_type, result_data = lconn.result(ldap_result_id, 0)
  34.     if (result_data == []):
  35.       break
  36.     else:
  37.       if result_type == ldap.RES_SEARCH_ENTRY:
  38.         result_set.append(result_data)
  39. except ldap.SIZELIMIT_EXCEEDED:
  40.     print()
  41.  
  42. # Получаем список email-адресов и заносим его в переменную all_emails
  43. for user in result_set:
  44.     proxyAddresses = user[0][1].get('proxyAddresses')
  45.     mail = user[0][1].get('mail')
  46.     if (proxyAddresses):
  47.         for email_b in proxyAddresses:
  48.             email = email_b.decode("utf-8")
  49.             all_emails.append(email.split(':')[1])
  50.     else:
  51.         all_emails.append(mail[0].decode("utf-8"))
  52.  
  53. # Получаем уникальные значения электронных адресов и заносим их в переменную unique_all_emails
  54. unique_all_emails = list(set(all_emails))
  55.  
  56. # Выводим результат на экран
  57. print(*unique_all_emails, sep = '\n')
# Почта # Active Directory
Дмитрий Моск — частный мастер
Был ли вам полезен этот скрипт?

Да            Нет