В феврале 2017 я коротко рассказал о найденных мной утилитах для работы с AD и WMI из линукса.
Во время работы скрипта будут, скорее всего, появляться сообщения типа "error: Error in ldap_get_values for ad_get_attribute:no values found for attribute managedby in object CN=CNT306552,OU=330,OU=Comps,OU=Domain Root Entry,DC=CONTOSO,DC=MS,DC=ru", это нормально - они выводятся на stderr, а наш вывод через stdout падает сразу в файл, так что чистить выходной файл от мусора не понадобится.
На выходе получим вполне приятный CSV вроде:
Там же есть вполне реальный пример, где я ищу владельцев компьютеров. С тех пор прошло около полугода, мой боевой потрёпанный винсервер-2003 (на рабочем месте) заставили сменить на win10. Не скажу, что оно меня сильно радует, но после апгрейда до 1703 интерфейс выглядит достаточно удобным (я сказал "достаточно удобным", а не просто "удобным" - это значит, что в нем уже можно работать, матерясь не через каждую минуту, а не чаще чем раз в полчаса-час), и появилась отличная штука - "Подсистема Linux для Windows":
sergei@cnt300022:~/bin$ uname -a
Linux cnt300022 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014 x86_64 x86_64 x86_64 GNU/Linux
sergei@cnt300022:~/bin$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.3 LTS
Release: 16.04
Codename: xenial
Linux cnt300022 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014 x86_64 x86_64 x86_64 GNU/Linux
sergei@cnt300022:~/bin$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.3 LTS
Release: 16.04
Codename: xenial
Не буду подробно останавливаться на этой подсистеме, скажу лишь, что она доступна только для 64-битных версий, и что на нее без особых проблем можно ставить и обновлять софт из убунтийско-дебиановых репозиториев. При наличии работающего икс-сервера можно запускать и графические задачи (все 4 видимых приложения запущены из "встроенной" убунты для демонстрационных целей):
Возможно, кому-нибудь пригодятся и использованные приемы работы со строками, которые я применил в этом скрипте, но о которых не догадался в феврале.
# Для элегантности используем переменные и константы вместо длинных выражений.
# А дальше стандартный скучный цикл построчного чтения из созданного чуть выше списка
exit
Однако, лично мне эта возможность пока без особой надобности, а вот возможность писать и отлаживать скрипты в баше - очень полезна. Чем я и занялся, решая свои повседневные задачи. В данном случае я снова решил довести до ума скрипт, выдергивающий из AD связи между компами и пользователями.
Возможно, кому-нибудь пригодятся и использованные приемы работы со строками, которые я применил в этом скрипте, но о которых не догадался в феврале.
#!/bin/bash
# Выгребаем из AD имена компов и находим их владельцев
# Для элегантности используем переменные и константы вместо длинных выражений.
COMPS_FILE=/tmp/comps-list.txt
OUT_COMPS_FILE=/tmp/comps_out.csv
# В моем домене все компы имеют вид cnt30xxxx, где cnt30 - постоянная часть, а
# xxx ::= [0-9]{3}[0-9a-z]{1} для рабочих станций и typ[0-9]{3} для других типов железа
# Однако, разные типы компов могут жить в разных OU, поэтому вместо "list" использую "search"
# Для удобства дальнейшей обработки найденное приводим к верхнему регистру
adtool search name cnt30*|grep -iE ^cn=cnt30[0-9]{3}[0-9a-z],|tr [:lower:] [:upper:]|sort>$COMPS_FILE
# Поскольку вывожу я всё это в подобие CSV, то сначала пишем строку заголовка
echo \"Comp DN\"\;\"Comp CN
\"\;\"UAC\"\;\"Managed by\"\;\"Description\">$OUT_COMPS_FILE# А дальше стандартный скучный цикл построчного чтения из созданного чуть выше списка
while read COMP_NAME; do
# Поскольку attributeget работает по CN, а не по DN, но при этом search возвращает именно DN,
# то надо из DN как-то выделить CN.
# Так можно и работает - так же я делал и в феврале, выкусывая слева "cn=", а справа всё
# после первой запятой
# PURE_NAME=$(echo ${COMP_NAME/CN\=/}|sed "s/\,.*//")
# Но можно и так (берем 9 знаков, начиная с третьего, нумерация с нуля):
# cn=cnt300000
# 0123456789AB
PURE_NAME=${COMP_NAME:3:9}
# Не уверен, может ли attributeget получать сразу несколько атрибутов, вероятно - нет,
# так что работаем по старинке: 1 переменная хранит 1 атрибут
COMP_DESC=$(adtool attributeget $PURE_NAME description)
# Здесь тоже надо выделить чистое CN из прочитанного DN, но имя пользователя имеет неизвестную
# длину, поэтому выбираем его универсальным, хотя и более медленным способом
COMP_MGBY=$(adtool attributeget $PURE_NAME managedby|sed "s/CN=//;s/\,.*//")
# Меня не очень волнует, есть ли атрибут managedBy у заблокированных компов, но раз в списке
# будут они все, то добавим столбец, показывающий состояние учётки.
# За признак enabled/disabled отвечает бит со значением 0b10 или 0d2, сделаем с ним AND
# и точно будем знать: если 0, значит учетка активна, если 2 - заблокирована
COMP_UAC=$(( $(adtool attributeget $PURE_NAME UserAccountControl) & 2 ))
# Осталось только всё это красиво вывести
echo \"$COMP_NAME\"\;\"$PURE_NAME\"\;\"$COMP_UAC\"\;\"$COMP_MGBY\"\;\"$COMP_DESC\"
done <$COMPS_FILE >>$OUT_COMPS_FILE
exit
Во время работы скрипта будут, скорее всего, появляться сообщения типа "error: Error in ldap_get_values for ad_get_attribute:no values found for attribute managedby in object CN=CNT306552,OU=330,OU=Comps,OU=Domain Root Entry,DC=CONTOSO,DC=MS,DC=ru", это нормально - они выводятся на stderr, а наш вывод через stdout падает сразу в файл, так что чистить выходной файл от мусора не понадобится.
На выходе получим вполне приятный CSV вроде:
"Comp DN";"CompCN";"UAC";"Managed by";"Description"
...
"CN=CNT30002A,OU=330,OU=COMPS,OU=DOMAIN ROOT ENTRY,DC=CONTOSO,DC=MS,DC=RU";"CNT30002A";"2";"Сергей Павлович";"убитый комп"
"CN=CNT30002B,OU=330,OU=COMPS,OU=DOMAIN ROOT ENTRY,DC=CONTOSO,DC=MS,DC=RU";"CNT30002B";"0";"Сергей Павлович";"тестовая Win10"