Обновить playbooks/scan_inventory.yml

This commit is contained in:
2025-12-05 12:28:31 +00:00
parent b84bada015
commit fe6aba0e5f

View File

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