diff --git a/playbooks/scan_inventory.yml b/playbooks/scan_inventory.yml index e60c503..1dd3e44 100644 --- a/playbooks/scan_inventory.yml +++ b/playbooks/scan_inventory.yml @@ -1,14 +1,9 @@ --- -- name: Сбор инвентаря Windows (Nmap + Smart DNS) +- name: Сбор инвентаря Windows (Nmap SMB + DNS) hosts: localhost connection: local gather_facts: yes vars: - domain: "zag.lan" - # Ваши DNS (важен порядок) - dns_servers: - - "192.168.1.250" - - "192.168.1.254" inventory_file: "/tmp/windows_inventory.ini" # Список подсетей @@ -26,27 +21,15 @@ - "172.19.42.0/23" - "172.19.56.0/23" - "172.19.58.0/23" - - # Порты: WinRM HTTP, WinRM HTTPS, SSH Std, SSH Custom + + # Порты для проверки доступности (WinRM, SSH, SMB) scan_ports: - 5985 - - 5986 - 22 - - 22233 + - 445 tasks: - - name: Проверка nmap - command: which nmap - register: nmap_check - failed_when: nmap_check.rc != 0 - changed_when: false - - # 1. СКАНИРОВАНИЕ - # -Pn : Не пинговать (считать хост живым). РЕШАЕТ ПРОБЛЕМУ "НЕ ВИДИТ ХОСТЫ" - # -n : Не делать Reverse DNS самим nmap-ом (сделаем сами точнее) - # --min-rate : Ускоряет процесс - # --open : Показать только открытые - - name: Сканирование сети (Nmap) + - name: Сканирование сети (поиск живых IP) command: > nmap -p {{ scan_ports | join(',') }} -Pn -n --open --min-rate 1000 -T4 -oG - @@ -54,118 +37,93 @@ register: nmap_result changed_when: false - # 2. ПАРСИНГ РЕЗУЛЬТАТОВ - - name: Парсинг активных IP + - name: Извлечение IP адресов set_fact: - # Регулярка ищет строки, где открыт ХОТЯ БЫ ОДИН из нужных портов active_ips: "{{ nmap_result.stdout | regex_findall('Host: ([0-9.]+).*Ports:.*(?:' + scan_ports | join('|') + ')/open') | unique | list }}" - - name: Статистика сканирования + - name: Статистика debug: - msg: "Найдено активных хостов: {{ active_ips | length }}" + msg: "Найдено активных IP: {{ active_ips | length }}" - # 3. DNS РЕЗОЛВ (Оптимизированный Shell) - - name: Определение Hostname через корпоративные DNS + # ГЛАВНОЕ ИЗМЕНЕНИЕ: Получаем имя через SMB (NetBIOS) или DNS + - name: Определение имен хостов (SMB Discovery) shell: | IP="{{ item }}" - # Функция для запроса к конкретному DNS серверу - ask_dns() { - nslookup -timeout=1 $1 $2 2>/dev/null | grep 'name =' | awk '{print $NF}' | sed 's/\.$//' | head -n 1 - } + # 1. Пробуем узнать имя через SMB (самый надежный способ для Windows) + SMB_NAME=$(nmap -p 445 --script smb-os-discovery $IP -Pn -n | grep "Computer name:" | awk -F': ' '{print $2}') - # Пробуем первый DNS - NAME=$(ask_dns $IP {{ dns_servers[0] }}) - - # Если пусто, пробуем второй - if [ -z "$NAME" ]; then - NAME=$(ask_dns $IP {{ dns_servers[1] }}) - fi - - # Вывод результата - if [ -z "$NAME" ]; then - echo "NO_DNS" + if [ ! -z "$SMB_NAME" ]; then + echo "$SMB_NAME" | tr '[:upper:]' '[:lower:]' else - # Переводим в нижний регистр для удобства - echo "$NAME" | tr '[:upper:]' '[:lower:]' + # 2. Если SMB закрыт/не ответил, пробуем DNS (как раньше) + DNS_NAME=$(nslookup -timeout=1 $IP 192.168.1.250 2>/dev/null | grep 'name =' | awk '{print $NF}' | sed 's/\.$//' | head -n 1) + if [ ! -z "$DNS_NAME" ]; then + echo "$DNS_NAME" | tr '[:upper:]' '[:lower:]' + else + echo "UNKNOWN" + fi fi loop: "{{ active_ips }}" - register: dns_results + register: host_names changed_when: false - # Не спамим в лог, если хостов много - no_log: true + no_log: true # Чтобы не засорять лог - # 4. ГРУППИРОВКА РЕЗУЛЬТАТОВ - - name: Обработка результатов DNS + # Фильтруем и готовим списки + - name: Сортировка хостов set_fact: - # Хосты с правильным именем pc...zag.lan - pc_hosts: >- - {{ pc_hosts | default([]) + - [{'hostname': item.stdout, 'ip': item.item}] - }} - # Хосты без имени или с левым именем - other_hosts: >- - {{ other_hosts | default([]) + - [{'ip': item.item, 'resolved_name': item.stdout}] - }} - loop: "{{ dns_results.results }}" - when: - - item.stdout is defined - - item.stdout != "NO_DNS" - - item.stdout | trim is match('^pc\\d+.*' + domain + '$') + # Ищем 'pc' в любом месте имени (было ^pc - только в начале) + pc_list: >- + {{ host_names.results + | selectattr('stdout', 'search', 'pc') + | map(attribute='item') | list + | zip(host_names.results | selectattr('stdout', 'search', 'pc') | map(attribute='stdout') | list) + | list }} + + # Остальные (сервера, принтеры, неизвестные) + other_list: >- + {{ host_names.results + | rejectattr('stdout', 'search', 'pc') + | map(attribute='item') | list + | zip(host_names.results | rejectattr('stdout', 'search', 'pc') | map(attribute='stdout') | list) + | list }} - # Собираем тех, кто не попал в pc_hosts (включая NO_DNS и странные имена) - - name: Сбор остальных (Unknown) - set_fact: - unknown_list: >- - {{ dns_results.results | rejectattr('stdout', 'match', '^pc\\d+.*' + domain + '$') | map(attribute='item') | list }} - - # 5. ГЕНЕРАЦИЯ ФАЙЛА - name: Запись инвентаря copy: content: | - # Автоматический инвентарь от {{ ansible_date_time.iso8601 }} - - # === Корпоративные PC (pcXX.zag.lan) === + # === Найденные PC (по имени содержит 'pc') === [windows_pcs] - {% for host in pc_hosts | default([]) | sort(attribute='hostname') %} - {{ host.hostname | regex_replace('\\.' + domain + '$', '') }} ansible_host={{ host.hostname }} ansible_host_ip={{ host.ip }} + {% for ip, name in pc_list %} + {{ name }} ansible_host={{ ip }} {% endfor %} - # === Остальные найденные (Unknown / IP only) === - [windows_unknown] - {% for res in dns_results.results if res.item in unknown_list %} - {% if res.stdout != "NO_DNS" %} - # Найден по DNS как: {{ res.stdout }} - unknown_{{ res.item | replace('.', '_') }} ansible_host={{ res.item }} + # === Остальные устройства (Servers, Unknown) === + [windows_other] + {% for ip, name in other_list %} + {% if name == "UNKNOWN" %} + unknown_{{ ip | replace('.', '_') }} ansible_host={{ ip }} {% else %} - # DNS имя не найдено - unknown_{{ res.item | replace('.', '_') }} ansible_host={{ res.item }} + {{ name }} ansible_host={{ ip }} {% endif %} {% endfor %} - # === Группы и переменные === [windows:children] windows_pcs - windows_unknown + windows_other [windows:vars] - # Настройки подключения ansible_connection=winrm ansible_winrm_transport=ntlm ansible_winrm_server_cert_validation=ignore ansible_port=5985 - - # Если вы хотите использовать SSH для unknown хостов, можно переопределить ниже: - # [windows_unknown:vars] - # ansible_port=22233 - # ansible_connection=ssh - # ansible_shell_type=powershell + ansible_user=Administrator + # Пароль лучше задать в Secret Store, не здесь dest: "{{ inventory_file }}" - - name: Финальный отчет + - name: ПОКАЗАТЬ РЕЗУЛЬТАТ (Скопируйте это в новый инвентарь) + command: cat {{ inventory_file }} + register: cat_inventory + changed_when: false + + - name: Вывод в лог debug: - msg: - - "Найдено всего IP: {{ active_ips | length }}" - - "Распознано как PC: {{ pc_hosts | default([]) | length }}" - - "Не распознано: {{ unknown_list | default([]) | length }}" - - "Файл сохранен: {{ inventory_file }}" \ No newline at end of file + var: cat_inventory.stdout_lines \ No newline at end of file