From 3443977c6d9ed5d860175f2b7616d4cf088ce260 Mon Sep 17 00:00:00 2001 From: ogrechko Date: Wed, 10 Dec 2025 10:01:13 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20playbooks/inventory.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playbooks/inventory.yml | 172 ++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 102 deletions(-) diff --git a/playbooks/inventory.yml b/playbooks/inventory.yml index 3c216a1..bfee436 100644 --- a/playbooks/inventory.yml +++ b/playbooks/inventory.yml @@ -1,107 +1,86 @@ --- -- name: Сбор инвентаря и создание его в Semaphore UI +- name: Сбор инвентаря и создание (Метод через файл) hosts: localhost connection: local gather_facts: no vars: - # --- НАСТРОЙКИ SEMAPHORE --- - semaphore_url: "http://192.168.0.198:9999" # Укажите ваш URL (или http://localhost:3000) + # --- НАСТРОЙКИ --- + semaphore_url: "http://192.168.0.198:9999" semaphore_project_id: 1 - # ID ключа, который будет использоваться для подключения к новым хостам semaphore_key_id: 7 - # Токен API (лучше хранить в Secret, но для теста можно тут) semaphore_api_token: "9ojexqiwt1xkemig7j1bd1pe-frh7hkre4reryk2occ=" inventory_name: "Auto Scanned Network" - - # --- НАСТРОЙКИ СЕТИ --- + + # --- СЕТИ --- subnets: - - "192.168.0.0/24" - - "192.168.1.0/24" + - "192.168.0.0/23" + - "192.168.1.0/23" - "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" - - "172.19.90.0/23" - # - "192.168.1.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" + # добавьте остальные ваши подсети сюда... scan_ports: [5985, 22, 445] tasks: - # --------------------------------------------------------- - # ШАГ 1: Сканирование (Nmap) - # --------------------------------------------------------- - - name: Сканирование сети (поиск живых IP) + # 1. Сканирование (Ваш код) + - name: Сканирование сети command: > nmap -p {{ scan_ports | join(',') }} -Pn -n --open --min-rate 1000 -T4 -oG - {{ subnets | join(' ') }} register: nmap_result changed_when: false - ignore_errors: yes # Чтобы не падало, если ничего не нашлось + ignore_errors: yes - - name: Извлечение IP адресов + - name: Извлечение IP set_fact: active_ips: "{{ nmap_result.stdout | regex_findall('Host: ([0-9.]+).*Ports:.*(?:' + scan_ports | join('|') + ')/open') | unique | list }}" - - name: Проверка, найдены ли IP + - name: Проверка IP fail: - msg: "Не найдено ни одного активного IP. Проверьте настройки сети или Nmap." + msg: "IP не найдены!" when: active_ips | length == 0 - # --------------------------------------------------------- - # ШАГ 2: Определение имен - # --------------------------------------------------------- - - name: Определение имен хостов (SMB Discovery + DNS) + # 2. Имена (Ваш код) + - name: Определение имен shell: | IP="{{ item }}" SMB_NAME=$(nmap -p 445 --script smb-os-discovery $IP -Pn -n | grep "Computer name:" | awk -F': ' '{print $2}') - if [ ! -z "$SMB_NAME" ]; then - echo "$SMB_NAME" | tr '[:upper:]' '[:lower:]' - else + if [ ! -z "$SMB_NAME" ]; then echo "$SMB_NAME" | tr '[:upper:]' '[:lower:]'; else 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 + if [ ! -z "$DNS_NAME" ]; then echo "$DNS_NAME" | tr '[:upper:]' '[:lower:]'; else echo "UNKNOWN"; fi fi loop: "{{ active_ips }}" register: host_names changed_when: false no_log: true - # --------------------------------------------------------- - # ШАГ 3: Сортировка и генерация текста - # --------------------------------------------------------- - - name: Сортировка хостов + # 3. Сортировка (Ваш код) + - name: Сортировка set_fact: 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 }} + {{ 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 }} + {{ host_names.results | rejectattr('stdout', 'search', 'pc') | map(attribute='item') | list | zip(host_names.results | rejectattr('stdout', 'search', 'pc') | map(attribute='stdout') | list) | list }} - - name: Генерация текста инвентаря + # 4. Текст инвентаря + - name: Генерация текста set_fact: inventory_content: | - # === Найденные PC === [windows_pcs] {% for ip, name in pc_list %} {{ name }} ansible_host={{ ip }} {% endfor %} - # === Остальные устройства === [windows_other] {% for ip, name in other_list %} {% if name == "UNKNOWN" %} @@ -121,54 +100,43 @@ ansible_shell_type=powershell ansible_user=o.grechko - # --------------------------------------------------------- - # ШАГ 4: Отладка (проверка данных перед отправкой) - # --------------------------------------------------------- - - name: Debug Payload (Проверка данных) + # ========================================================== + # ИЗМЕНЕННАЯ ЧАСТЬ: СОЗДАНИЕ ЧЕРЕЗ ФАЙЛ + # ========================================================== + + - name: Сохранение JSON-пейлоада в файл (для надежности) + copy: + content: | + { + "name": "{{ inventory_name }} {{ 1000 | random }}", + "project_id": {{ semaphore_project_id | int }}, + "type": "static", + "ssh_key_id": {{ semaphore_key_id | int }}, + "become_key_id": null, + "repository_id": null, + "inventory": {{ inventory_content | to_json }} + } + dest: /tmp/semaphore_payload.json + + - name: Отправка через CURL (чтение из файла) + command: > + curl -v -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" + -H "Authorization: Bearer {{ semaphore_api_token }}" + -H "Content-Type: application/json" + -H "Accept: application/json" + -d @/tmp/semaphore_payload.json + register: curl_result + ignore_errors: yes + + - name: Показать полный ответ CURL debug: - msg: - - "URL: {{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" - - "SSH Key ID: {{ semaphore_key_id }}" - - "Количество символов в инвентаре: {{ inventory_content | length }}" + var: curl_result.stderr_lines - # --------------------------------------------------------- - # ШАГ 5: Отправка в API (ИСПРАВЛЕНО ssh_key_id) - # --------------------------------------------------------- - - name: Создание инвентаря в Semaphore через API - uri: - url: "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory" - method: POST - headers: - Authorization: "Bearer {{ semaphore_api_token }}" - Content-Type: "application/json" - Accept: "application/json" - body_format: json - body: - # Добавляем рандом, чтобы имя было уникальным при каждом запуске - name: "{{ inventory_name }} {{ 10000 | random }}" - project_id: "{{ semaphore_project_id | int }}" - type: "static" - ssh_key_id: "{{ semaphore_key_id | int }}" - become_key_id: null - # !!! ВОТ ЭТО ПОЛЕ ОБЯЗАТЕЛЬНО !!! - repository_id: null - inventory: "{{ inventory_content }}" - status_code: [201, 200] - register: api_response - ignore_errors: yes # Чтобы увидеть вывод ошибки, если она случится - - # Эта задача покажет, ПОЧЕМУ именно сервер ругается (текст ошибки) - - name: ОТЛАДКА - Ответ сервера (если ошибка) + - name: Показать ответ сервера (body) debug: - msg: "Server response: {{ api_response.json | default(api_response.content) }}" - when: api_response.failed is defined and api_response.failed + var: curl_result.stdout_lines - - name: Остановка сценария при ошибке - fail: - msg: "Не удалось создать инвентарь. См. причину выше." - when: api_response.failed is defined and api_response.failed - - - name: Успех - debug: - msg: "Инвентарь создан! ID: {{ api_response.json.id }}" - when: api_response.status == 201 or api_response.status == 200 + - name: Удалить временный файл + file: + path: /tmp/semaphore_payload.json + state: absent \ No newline at end of file