Files
semaphore/playbooks/scan_inventory.yml

171 lines
6.4 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
- name: Сбор инвентаря Windows (Nmap + Smart 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"
# Список подсетей
subnets:
- "192.168.0.0/24"
- "192.168.1.0/24"
- "192.168.2.0/24"
- "192.168.3.0/24"
- "172.19.8.0/23"
- "172.19.9.0/23"
- "172.19.10.0/23"
- "172.19.24.0/23"
- "172.19.26.0/23"
- "172.19.40.0/23"
- "172.19.42.0/23"
- "172.19.56.0/23"
- "172.19.58.0/23"
# Порты: WinRM HTTP, WinRM HTTPS, SSH Std, SSH Custom
scan_ports:
- 5985
- 5986
- 22
- 22233
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)
command: >
nmap -p {{ scan_ports | join(',') }}
-Pn -n --open --min-rate 1000 -T4 -oG -
{{ subnets | join(' ') }}
register: nmap_result
changed_when: false
# 2. ПАРСИНГ РЕЗУЛЬТАТОВ
- name: Парсинг активных IP
set_fact:
# Регулярка ищет строки, где открыт ХОТЯ БЫ ОДИН из нужных портов
active_ips: "{{ nmap_result.stdout | regex_findall('Host: ([0-9.]+).*Ports:.*(?:' + scan_ports | join('|') + ')/open') | unique | list }}"
- name: Статистика сканирования
debug:
msg: "Найдено активных хостов: {{ active_ips | length }}"
# 3. DNS РЕЗОЛВ (Оптимизированный Shell)
- name: Определение Hostname через корпоративные DNS
shell: |
IP="{{ item }}"
# Функция для запроса к конкретному DNS серверу
ask_dns() {
nslookup -timeout=1 $1 $2 2>/dev/null | grep 'name =' | awk '{print $NF}' | sed 's/\.$//' | head -n 1
}
# Пробуем первый 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"
else
# Переводим в нижний регистр для удобства
echo "$NAME" | tr '[:upper:]' '[:lower:]'
fi
loop: "{{ active_ips }}"
register: dns_results
changed_when: false
# Не спамим в лог, если хостов много
no_log: true
# 4. ГРУППИРОВКА РЕЗУЛЬТАТОВ
- name: Обработка результатов DNS
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_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) ===
[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 }}
{% 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 }}
{% else %}
# DNS имя не найдено
unknown_{{ res.item | replace('.', '_') }} ansible_host={{ res.item }}
{% endif %}
{% endfor %}
# === Группы и переменные ===
[windows:children]
windows_pcs
windows_unknown
[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
dest: "{{ inventory_file }}"
- name: Финальный отчет
debug:
msg:
- "Найдено всего IP: {{ active_ips | length }}"
- "Распознано как PC: {{ pc_hosts | default([]) | length }}"
- "Не распознано: {{ unknown_list | default([]) | length }}"
- "Файл сохранен: {{ inventory_file }}"