--- - name: "Полное сканирование (включая Unknown)" hosts: localhost connection: local gather_facts: no vars: # --- SEMAPHORE API --- semaphore_url: "http://192.168.0.198:9999" semaphore_project_id: 1 semaphore_api_token: "9ojexqiwt1xkemig7j1bd1pe-frh7hkre4reryk2occ=" # --- ID КЛЮЧЕЙ (ПРОВЕРЬТЕ ИХ В SEMAPHORE!) --- key_windows: 7 key_linux: 7 key_mikrotik: 7 key_printers: 7 key_other: 7 # --- СЕТИ --- subnets: - "192.168.0.0/24" - "192.168.0.0/23" - "192.168.2.0/24" - "192.168.3.0/24" - "172.19.8.0/24" - "172.19.9.0/24" - "172.19.10.0/24" - "172.19.24.0/24" - "172.19.26.0/24" - "172.19.40.0/24" - "172.19.42.0/24" - "172.19.56.0/24" - "172.19.58.0/24" - "172.19.90.0/24" tasks: # ---------------------------------------------------------------- # ШАГ 0: Проверка наличия утилиты nmap (защита от Errno 2) # ---------------------------------------------------------------- - name: Проверка установки nmap command: which nmap register: nmap_check ignore_errors: yes changed_when: false - name: Остановка, если nmap не установлен fail: msg: > Утилита nmap не найдена! Если вы используете Docker-версию Semaphore (Alpine), зайдите в контейнер и выполните: apk add nmap. Если это Ubuntu/Debian: apt-get install nmap. when: nmap_check.rc != 0 # ---------------------------------------------------------------- # ШАГ 1: Поиск живых хостов # ---------------------------------------------------------------- - name: Ping Sweep command: "nmap -sn -n --min-rate 1000 -T4 -oG - {{ subnets | join(' ') }}" register: ping_scan changed_when: false - name: Формирование списка активных IP set_fact: active_ips: "{{ ping_scan.stdout | regex_findall('Host: ([0-9.]+)') | unique | list }}" - name: Проверка наличия хостов fail: msg: "Сеть пуста или ни один хост не ответил на пинг." when: active_ips | length == 0 # ---------------------------------------------------------------- # ШАГ 2: Сканирование портов # ---------------------------------------------------------------- - name: TCP Port Scan & Name Discovery shell: | nmap -sT -p 22,445,5985,5986,8291,9100 \ --script smb-os-discovery \ -Pn -n -T4 {{ item }} loop: "{{ active_ips }}" register: scan_results changed_when: false # ---------------------------------------------------------------- # ШАГ 3: Классификация (ИСПРАВЛЕНО) # ---------------------------------------------------------------- - name: Обработка результатов set_fact: classified_hosts: [] - name: Классификация хостов set_fact: classified_hosts: "{{ classified_hosts + [host_info] }}" loop: "{{ scan_results.results }}" vars: out: "{{ item.stdout }}" ip: "{{ item.item }}" smb_found: "{{ out | regex_search('Computer name: ([\\w-]+)', '\\1') | default([]) }}" smb_name: "{{ smb_found[0] if smb_found else '' }}" type: >- {%- if out | regex_search('5985/tcp\\s+open') or out | regex_search('5986/tcp\\s+open') -%} windows {%- elif out | regex_search('8291/tcp\\s+open') -%} mikrotik {%- elif out | regex_search('9100/tcp\\s+open') -%} printer {%- elif out | regex_search('22/tcp\\s+open') -%} linux {%- else -%} other {%- endif %} final_name: >- {%- if type == 'windows' and smb_name != '' -%} {{ smb_name | lower }} {%- else -%} {{ type }}_{{ ip | replace('.', '_') }} {%- endif %} host_info: ip: "{{ ip }}" type: "{{ type }}" name: "{{ final_name }}" when: item.stdout is defined and item.stdout != "" # ---------------------------------------------------------------- # ШАГ 4: Списки (ИСПРАВЛЕНО) # ---------------------------------------------------------------- - name: Формирование списков set_fact: list_win: "{{ classified_hosts | selectattr('type', 'equalto', 'windows') | list }}" list_lin: "{{ classified_hosts | selectattr('type', 'equalto', 'linux') | list }}" list_tik: "{{ classified_hosts | selectattr('type', 'equalto', 'mikrotik') | list }}" list_prn: "{{ classified_hosts | selectattr('type', 'equalto', 'printer') | list }}" list_oth: "{{ classified_hosts | selectattr('type', 'equalto', 'other') | list }}" - name: СТАТИСТИКА debug: msg: - "Windows (WinRM): {{ list_win | length }}" - "MikroTik: {{ list_tik | length }}" - "Linux: {{ list_lin | length }}" - "Printers: {{ list_prn | length }}" - "Unknown: {{ list_oth | length }}" # ---------------------------------------------------------------- # ШАГ 5: Отправка Windows # ---------------------------------------------------------------- - block: - set_fact: content_win: | [windows] {% for h in list_win %} {{ h.name }} ansible_host={{ h.ip }} {% endfor %} [windows:vars] ansible_connection=winrm ansible_port=5985 ansible_winrm_server_cert_validation=ignore ansible_winrm_transport=ntlm - copy: content: | { "name": "Auto Windows {{ 1000 | random }}", "project_id": {{ semaphore_project_id }}, "type": "static", "ssh_key_id": {{ key_windows }}, "become_key_id": null, "repository_id": null, "inventory": {{ content_win | to_json }} } dest: /tmp/p_win.json - command: > curl -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" -H "Authorization: Bearer {{ semaphore_api_token }}" -H "Content-Type: application/json" -d @/tmp/p_win.json ignore_errors: yes when: list_win | length > 0 # ---------------------------------------------------------------- # ШАГ 6: Отправка MikroTik # ---------------------------------------------------------------- - block: - set_fact: content_tik: | [routers] {% for h in list_tik %} {{ h.name }} ansible_host={{ h.ip }} {% endfor %} [routers:vars] ansible_connection=network_cli ansible_network_os=routeros - copy: content: | { "name": "Auto MikroTik {{ 1000 | random }}", "project_id": {{ semaphore_project_id }}, "type": "static", "ssh_key_id": {{ key_mikrotik }}, "become_key_id": null, "repository_id": null, "inventory": {{ content_tik | to_json }} } dest: /tmp/p_tik.json - command: > curl -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" -H "Authorization: Bearer {{ semaphore_api_token }}" -H "Content-Type: application/json" -d @/tmp/p_tik.json ignore_errors: yes when: list_tik | length > 0 # ---------------------------------------------------------------- # ШАГ 7: Отправка Linux # ---------------------------------------------------------------- - block: - set_fact: content_lin: | [linux] {% for h in list_lin %} {{ h.name }} ansible_host={{ h.ip }} {% endfor %} [linux:vars] ansible_connection=ssh ansible_user=root - copy: content: | { "name": "Auto Linux {{ 1000 | random }}", "project_id": {{ semaphore_project_id }}, "type": "static", "ssh_key_id": {{ key_linux }}, "become_key_id": null, "repository_id": null, "inventory": {{ content_lin | to_json }} } dest: /tmp/p_lin.json - command: > curl -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" -H "Authorization: Bearer {{ semaphore_api_token }}" -H "Content-Type: application/json" -d @/tmp/p_lin.json ignore_errors: yes when: list_lin | length > 0 # ---------------------------------------------------------------- # ШАГ 8: Отправка Printers # ---------------------------------------------------------------- - block: - set_fact: content_prn: | [printers] {% for h in list_prn %} {{ h.name }} ansible_host={{ h.ip }} {% endfor %} [printers:vars] ansible_connection=local - copy: content: | { "name": "Auto Printers {{ 1000 | random }}", "project_id": {{ semaphore_project_id }}, "type": "static", "ssh_key_id": {{ key_printers }}, "become_key_id": null, "repository_id": null, "inventory": {{ content_prn | to_json }} } dest: /tmp/p_prn.json - command: > curl -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" -H "Authorization: Bearer {{ semaphore_api_token }}" -H "Content-Type: application/json" -d @/tmp/p_prn.json ignore_errors: yes when: list_prn | length > 0 # ---------------------------------------------------------------- # ШАГ 9: Отправка UNKNOWN (Other) # ---------------------------------------------------------------- - block: - set_fact: content_oth: | [unknown_devices] {% for h in list_oth %} {{ h.name }} ansible_host={{ h.ip }} {% endfor %} [unknown_devices:vars] ansible_connection=local - copy: content: | { "name": "Auto Unknown {{ 1000 | random }}", "project_id": {{ semaphore_project_id }}, "type": "static", "ssh_key_id": {{ key_other }}, "become_key_id": null, "repository_id": null, "inventory": {{ content_oth | to_json }} } dest: /tmp/p_oth.json - command: > curl -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" -H "Authorization: Bearer {{ semaphore_api_token }}" -H "Content-Type: application/json" -d @/tmp/p_oth.json ignore_errors: yes when: list_oth | length > 0 - name: Очистка shell: rm -f /tmp/p_*.json