Русифицировать и доработать Ansible развёртывание
This commit is contained in:
+36
-35
@@ -1,37 +1,33 @@
|
|||||||
# Ansible ensemble for AWatch-rus
|
# Ansible ensemble for AWatch-rus
|
||||||
|
|
||||||
Эта директория содержит Ansible-ensemble для двух сценариев:
|
Эта директория содержит Ansible-ensemble для полного развёртывания AWatch-rus:
|
||||||
|
|
||||||
- деплой на уже существующий Debian host/CT;
|
- деплой на уже существующий Debian host/CT;
|
||||||
- полный цикл с нуля в Proxmox: создание CT + bootstrap + установка ActivityWatch + RU patch.
|
- полный цикл с нуля в Proxmox: создание CT + bootstrap + установка ActivityWatch + RU patch;
|
||||||
- централизованный деплой Windows phase-2 collectors по WinRM.
|
- централизованное развёртывание Windows phase-2 collector'ов по WinRM;
|
||||||
- deployment внешнего pfSense poller'а на Debian/Ubuntu utility VM.
|
- развёртывание внешнего pfSense poller'а на Debian/Ubuntu utility VM.
|
||||||
|
|
||||||
## Файлы
|
## Файлы
|
||||||
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_server.yml` — основной playbook.
|
- `ansible/deploy_aw_server.yml` — основной playbook для уже существующего Debian/CT host.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/provision_proxmox_ct_and_deploy_aw.yml` — full-stack playbook для Proxmox.
|
- `ansible/provision_proxmox_ct_and_deploy_aw.yml` — полный playbook для Proxmox.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml` — массовый full-stack playbook (несколько CT).
|
- `ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml` — массовый полный playbook (несколько CT).
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_windows_phase2.yml` — WinRM playbook для развёртывания phase-2 Windows collector'ов.
|
- `ansible/deploy_aw_windows_phase2.yml` — WinRM playbook для развёртывания Windows/RDP collector'ов.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_pfsense_poller.yml` — deployment pfSense poller'а.
|
- `ansible/deploy_aw_pfsense_poller.yml` — развёртывание pfSense poller'а.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/install_full_stack.yml` — полный установочный playbook (оркестратор всех этапов).
|
- `ansible/install_full_stack.yml` — полный установочный playbook (оркестратор всех этапов).
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/inventory.example.ini` — шаблон inventory.
|
- `ansible/inventory.example.ini` — шаблон inventory.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml` — шаблон переменных.
|
- `ansible/group_vars/*.example.yml` — шаблоны переменных.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox.example.yml` — шаблон переменных CT в Proxmox.
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox-matrix.example.yml` — шаблон матрицы CT.
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/windows.example.yml` — шаблон переменных Windows phase-2.
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/pfsense-poller.example.yml` — шаблон переменных pfSense poller'а.
|
|
||||||
|
|
||||||
## Быстрый запуск
|
## Быстрый запуск
|
||||||
|
|
||||||
1. Скопируйте шаблоны:
|
1. Скопируйте шаблоны:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/all.yml`
|
- `cp ansible/group_vars/all.example.yml ansible/group_vars/all.yml`
|
||||||
2. Заполните значения в `inventory.ini` и `group_vars/all.yml`.
|
2. Заполните значения в `inventory.ini` и `group_vars/all.yml`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini deploy_aw_server.yml
|
ansible-playbook -i inventory.ini deploy_aw_server.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -40,7 +36,7 @@ ansible-playbook -i inventory.ini deploy_aw_server.yml
|
|||||||
Если нужно прогнать полный цикл одной командой:
|
Если нужно прогнать полный цикл одной командой:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini install_full_stack.yml
|
ansible-playbook -i inventory.ini install_full_stack.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -56,50 +52,50 @@ ansible-playbook -i inventory.ini install_full_stack.yml
|
|||||||
## Полный запуск с нуля в Proxmox
|
## Полный запуск с нуля в Proxmox
|
||||||
|
|
||||||
1. Подготовьте inventory и vars:
|
1. Подготовьте inventory и vars:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/all.yml`
|
- `cp ansible/group_vars/all.example.yml ansible/group_vars/all.yml`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox.yml`
|
- `cp ansible/group_vars/proxmox.example.yml ansible/group_vars/proxmox.yml`
|
||||||
2. Заполните `group_vars/proxmox.yml` и `group_vars/all.yml`.
|
2. Заполните `group_vars/proxmox.yml` и `group_vars/all.yml`.
|
||||||
3. Запустите playbook:
|
3. Запустите playbook:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini provision_proxmox_ct_and_deploy_aw.yml
|
ansible-playbook -i inventory.ini provision_proxmox_ct_and_deploy_aw.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Массовый запуск (матрица CT)
|
## Массовый запуск (матрица CT)
|
||||||
|
|
||||||
1. Подготовьте матрицу:
|
1. Подготовьте матрицу:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox-matrix.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox-matrix.yml`
|
- `cp ansible/group_vars/proxmox-matrix.example.yml ansible/group_vars/proxmox-matrix.yml`
|
||||||
2. Заполните `proxmox-matrix.yml`.
|
2. Заполните `proxmox-matrix.yml`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini provision_proxmox_ct_matrix_and_deploy_aw.yml
|
ansible-playbook -i inventory.ini provision_proxmox_ct_matrix_and_deploy_aw.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Windows phase-2 rollout (WinRM)
|
## Windows phase-2 rollout (WinRM)
|
||||||
|
|
||||||
1. Подготовьте inventory и vars:
|
1. Подготовьте inventory и vars:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/windows.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/windows.yml`
|
- `cp ansible/group_vars/windows.example.yml ansible/group_vars/windows.yml`
|
||||||
2. Заполните `inventory.ini` (секция `[aw_windows]`) и `group_vars/windows.yml`.
|
2. Заполните `inventory.ini` (секция `[aw_windows]`) и `group_vars/windows.yml`.
|
||||||
- Для русской локализации Windows часто нужен `ansible_user=Администратор` (а не `Administrator`).
|
- Для русской локализации Windows часто нужен `ansible_user=Администратор` (а не `Administrator`).
|
||||||
- Если WinRM закрыт, playbook не сможет стартовать и нужно сначала открыть `5985/5986` и `wsman`.
|
- Если WinRM закрыт, playbook не сможет стартовать и нужно сначала открыть `5985/5986` и `wsman`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini deploy_aw_windows_phase2.yml
|
ansible-playbook -i inventory.ini deploy_aw_windows_phase2.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
Playbook:
|
Playbook:
|
||||||
|
|
||||||
- выгружает `windows/*` toolkit на целевой хост в `C:\Deploy\AWatch-rus\windows`;
|
- выгружает полный `windows/*` toolkit на целевой хост в `C:\Deploy\AWatch-rus\windows`, включая DLP и `worktime-session-collector.ps1`;
|
||||||
- выполняет `deploy-ensemble.ps1` (deploy + hardening/recovery) с phase-2 policy/rules;
|
- выполняет `deploy-ensemble.ps1` (deploy + hardening/recovery) с phase-2 policy/rules;
|
||||||
- после deploy принудительно запускает `ActivityWatch Recovery` и все `ActivityWatch Launch *` задачи;
|
- после deploy принудительно запускает `ActivityWatch Recovery` и все `ActivityWatch Launch *` задачи;
|
||||||
- выполняет API smoke-check bucket `aw-watcher-afk_SHARKON2025` и ожидает свежие `not-afk` события;
|
- выполняет API smoke-check bucket `aw-watcher-afk_<COMPUTERNAME>` и ожидает свежие `not-afk` события;
|
||||||
- запускает `validate-deployment.ps1`;
|
- запускает `validate-deployment.ps1`;
|
||||||
- забирает JSON-отчёт в локальную директорию (`/tmp/aw-rus-validation` по умолчанию).
|
- забирает JSON-отчёт в локальную директорию (`/tmp/aw-rus-validation` по умолчанию).
|
||||||
|
|
||||||
@@ -110,17 +106,20 @@ Playbook:
|
|||||||
- `aw_windows_incident_capture_enabled: false` — отключить блок incidentCapture;
|
- `aw_windows_incident_capture_enabled: false` — отключить блок incidentCapture;
|
||||||
- `aw_windows_incident_screenshot_enabled: false` — не делать скриншот при DLP-инциденте;
|
- `aw_windows_incident_screenshot_enabled: false` — не делать скриншот при DLP-инциденте;
|
||||||
- `aw_windows_incident_artifacts_root: 'C:\...\incident-artifacts'` — переопределить путь артефактов;
|
- `aw_windows_incident_artifacts_root: 'C:\...\incident-artifacts'` — переопределить путь артефактов;
|
||||||
|
- `aw_windows_package_version`, `aw_windows_package_url`, `aw_windows_package_zip_path` — версия и источник Windows-пакета ActivityWatch;
|
||||||
|
- `aw_windows_api_smoke_check_bucket: ""` — автоматически использовать `aw-watcher-afk_<COMPUTERNAME>`;
|
||||||
|
- `aw_windows_fail_on_validation_error: true` — завершать playbook ошибкой, если `validate-deployment.ps1` возвращает `overallOk=false`;
|
||||||
- `aw_windows_skip_hardening: true` — пропустить `hardening-recovery.ps1` внутри ensemble-скрипта.
|
- `aw_windows_skip_hardening: true` — пропустить `hardening-recovery.ps1` внутри ensemble-скрипта.
|
||||||
|
|
||||||
## pfSense poller rollout
|
## Развёртывание pfSense poller
|
||||||
|
|
||||||
1. Подготовьте vars:
|
1. Подготовьте vars:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/pfsense-poller.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/pfsense-poller.yml`
|
- `cp ansible/group_vars/pfsense-poller.example.yml ansible/group_vars/pfsense-poller.yml`
|
||||||
2. Добавьте inventory group `[aw_pfsense_pollers]`.
|
2. Добавьте inventory group `[aw_pfsense_pollers]`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini deploy_aw_pfsense_poller.yml
|
ansible-playbook -i inventory.ini deploy_aw_pfsense_poller.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -139,4 +138,6 @@ Playbook:
|
|||||||
- Для Web UI используется checksum-based cache-bust для `ru-patch-v5.js` и `sw-cleanup.js`, чтобы браузер не держал старую DLP/русскую статику после деплоя.
|
- Для Web UI используется checksum-based cache-bust для `ru-patch-v5.js` и `sw-cleanup.js`, чтобы браузер не держал старую DLP/русскую статику после деплоя.
|
||||||
- На `#/home` Web UI делит хосты на `Windows RDP` и `Virtual servers + Proxmox`.
|
- На `#/home` Web UI делит хосты на `Windows RDP` и `Virtual servers + Proxmox`.
|
||||||
- Выполнена валидация API `http://127.0.0.1:5600/api/0/info`.
|
- Выполнена валидация API `http://127.0.0.1:5600/api/0/info`.
|
||||||
- Для full-stack сценария CT создаётся автоматически через `pct create`.
|
- Для полного сценария CT создаётся автоматически через `pct create`.
|
||||||
|
- На Windows/RDP host развёрнуты AFK/window watchers, browser domain collector, DLP endpoint collector и worktime session collector.
|
||||||
|
- Проверочный JSON-отчёт Windows playbook должен иметь `overallOk=true`.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy pfSense ActivityWatch poller
|
- name: Развернуть pfSense ActivityWatch poller
|
||||||
hosts: aw_pfsense_pollers
|
hosts: aw_pfsense_pollers
|
||||||
become: true
|
become: true
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
@@ -10,14 +10,14 @@
|
|||||||
aw_pfsense_service_name: "aw-pfsense-poller.service"
|
aw_pfsense_service_name: "aw-pfsense-poller.service"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Install required packages
|
- name: Установить обязательные пакеты
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- python3
|
- python3
|
||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
|
|
||||||
- name: Ensure directories exist
|
- name: Создать каталоги
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
@@ -26,29 +26,29 @@
|
|||||||
- "{{ aw_pfsense_install_root }}"
|
- "{{ aw_pfsense_install_root }}"
|
||||||
- "{{ aw_pfsense_config_dir }}"
|
- "{{ aw_pfsense_config_dir }}"
|
||||||
|
|
||||||
- name: Install pfSense poller script
|
- name: Установить скрипт pfSense poller
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py"
|
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py"
|
||||||
dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py"
|
dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
- name: Install systemd service
|
- name: Установить systemd service
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service"
|
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service"
|
||||||
dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}"
|
dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify:
|
notify:
|
||||||
- Reload systemd
|
- Перезагрузить systemd
|
||||||
|
|
||||||
- name: Write pfSense poller config
|
- name: Записать конфигурацию pfSense poller
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ aw_pfsense_config_dir }}/poller.json"
|
dest: "{{ aw_pfsense_config_dir }}/poller.json"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
content: "{{ aw_pfsense_poller_config | to_nice_json }}"
|
content: "{{ aw_pfsense_poller_config | to_nice_json }}"
|
||||||
notify:
|
notify:
|
||||||
- Restart pfSense poller
|
- Перезапустить pfSense poller
|
||||||
|
|
||||||
- name: Enable and start pfSense poller
|
- name: Включить и запустить pfSense poller
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: "{{ aw_pfsense_service_name }}"
|
name: "{{ aw_pfsense_service_name }}"
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -56,11 +56,11 @@
|
|||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: Reload systemd
|
- name: Перезагрузить systemd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Restart pfSense poller
|
- name: Перезапустить pfSense poller
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: "{{ aw_pfsense_service_name }}"
|
name: "{{ aw_pfsense_service_name }}"
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
+159
-49
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy AWatch-rus server
|
- name: Развернуть сервер AWatch-rus
|
||||||
hosts: aw_server
|
hosts: aw_server
|
||||||
become: true
|
become: true
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
@@ -9,22 +9,29 @@
|
|||||||
aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}"
|
aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}"
|
||||||
aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip"
|
aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip"
|
||||||
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||||||
|
aw_release_install_dir: "{{ aw_release_root }}/aw-server-rust-{{ aw_server_version }}"
|
||||||
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||||
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||||
aw_host_groups_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-host-groups.json') | hash('sha1') | truncate(12, true, '') }}"
|
|
||||||
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
||||||
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Install base packages
|
- name: Установить базовые пакеты
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- curl
|
- curl
|
||||||
|
- rsync
|
||||||
- unzip
|
- unzip
|
||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
|
|
||||||
- name: Ensure service account exists
|
- name: Создать системную группу сервиса
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ aw_server_group }}"
|
||||||
|
system: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Создать системную учётную запись сервиса
|
||||||
ansible.builtin.user:
|
ansible.builtin.user:
|
||||||
name: "{{ aw_server_user }}"
|
name: "{{ aw_server_user }}"
|
||||||
group: "{{ aw_server_group }}"
|
group: "{{ aw_server_group }}"
|
||||||
@@ -33,7 +40,25 @@
|
|||||||
system: true
|
system: true
|
||||||
create_home: false
|
create_home: false
|
||||||
|
|
||||||
- name: Ensure required directories
|
- name: Создать обязательные каталоги
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- "{{ aw_release_root }}"
|
||||||
|
- "{{ aw_release_dir }}"
|
||||||
|
- "{{ aw_release_install_dir }}"
|
||||||
|
- /opt/activitywatch
|
||||||
|
- /opt/activitywatch/bin
|
||||||
|
- "{{ aw_server_webui_dir }}"
|
||||||
|
- "{{ aw_server_webui_dir }}/js"
|
||||||
|
- "{{ aw_server_data_dir }}"
|
||||||
|
- "{{ aw_server_log_dir }}"
|
||||||
|
- /etc/activitywatch
|
||||||
|
- "{{ aw_bootstrap_dir }}"
|
||||||
|
|
||||||
|
- name: Настроить каталоги ActivityWatch с владельцем сервиса
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
@@ -41,103 +66,188 @@
|
|||||||
group: "{{ aw_server_group }}"
|
group: "{{ aw_server_group }}"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
loop:
|
loop:
|
||||||
|
- /opt/activitywatch
|
||||||
|
- /opt/activitywatch/bin
|
||||||
- "{{ aw_release_root }}"
|
- "{{ aw_release_root }}"
|
||||||
- "{{ aw_release_dir }}"
|
- "{{ aw_release_dir }}"
|
||||||
|
- "{{ aw_release_install_dir }}"
|
||||||
- "{{ aw_server_webui_dir }}"
|
- "{{ aw_server_webui_dir }}"
|
||||||
|
- "{{ aw_server_webui_dir }}/js"
|
||||||
- "{{ aw_server_data_dir }}"
|
- "{{ aw_server_data_dir }}"
|
||||||
- "{{ aw_server_log_dir }}"
|
- "{{ aw_server_log_dir }}"
|
||||||
- /etc/activitywatch
|
|
||||||
- "{{ aw_bootstrap_dir }}"
|
|
||||||
|
|
||||||
- name: Download ActivityWatch release archive
|
- name: Скачать архив релиза ActivityWatch
|
||||||
ansible.builtin.get_url:
|
ansible.builtin.get_url:
|
||||||
url: "{{ aw_server_download_url }}"
|
url: "{{ aw_server_download_url }}"
|
||||||
dest: "{{ aw_archive_path }}"
|
dest: "{{ aw_archive_path }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Unpack ActivityWatch release
|
- name: Распаковать релиз ActivityWatch
|
||||||
ansible.builtin.unarchive:
|
ansible.builtin.unarchive:
|
||||||
src: "{{ aw_archive_path }}"
|
src: "{{ aw_archive_path }}"
|
||||||
dest: "{{ aw_release_dir }}"
|
dest: "{{ aw_release_dir }}"
|
||||||
remote_src: true
|
remote_src: true
|
||||||
extra_opts: ["-o"]
|
extra_opts: ["-o"]
|
||||||
|
|
||||||
- name: Discover extracted AW directory
|
- name: Найти распакованный каталог ActivityWatch
|
||||||
ansible.builtin.find:
|
ansible.builtin.find:
|
||||||
paths: "{{ aw_release_dir }}"
|
paths: "{{ aw_release_dir }}"
|
||||||
file_type: directory
|
file_type: directory
|
||||||
patterns: "activitywatch*"
|
patterns: "activitywatch*"
|
||||||
register: aw_release_find
|
register: aw_release_find
|
||||||
|
|
||||||
- name: Set release extracted path
|
- name: Найти бинарный файл AW server
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.find:
|
||||||
aw_release_extracted: "{{ (aw_release_find.files | sort(attribute='path') | map(attribute='path') | list | first) }}"
|
paths: "{{ aw_release_dir }}"
|
||||||
|
file_type: file
|
||||||
|
patterns:
|
||||||
|
- aw-server-rust
|
||||||
|
- aw-server
|
||||||
|
register: aw_server_binary_find
|
||||||
|
|
||||||
- name: Verify extracted directory exists
|
- name: Найти каталог WebUI
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: "{{ aw_release_dir }}"
|
||||||
|
file_type: directory
|
||||||
|
patterns:
|
||||||
|
- aw-webui
|
||||||
|
- webui
|
||||||
|
register: aw_webui_dir_find
|
||||||
|
|
||||||
|
- name: Сохранить пути распакованного релиза
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
aw_release_extracted: "{{ (aw_release_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||||||
|
aw_server_binary_path: "{{ (aw_server_binary_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||||||
|
aw_webui_source_path: "{{ (aw_webui_dir_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||||||
|
|
||||||
|
- name: Проверить, что компоненты релиза найдены
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- aw_release_extracted is defined
|
- aw_release_extracted is defined
|
||||||
- aw_release_extracted | length > 0
|
- aw_release_extracted | length > 0
|
||||||
fail_msg: "Cannot locate extracted ActivityWatch release directory."
|
- aw_server_binary_path is defined
|
||||||
|
- aw_server_binary_path | length > 0
|
||||||
|
- aw_webui_source_path is defined
|
||||||
|
- aw_webui_source_path | length > 0
|
||||||
|
fail_msg: "Не удалось найти бинарный файл или WebUI в распакованном релизе ActivityWatch."
|
||||||
|
|
||||||
- name: Sync release content to /opt/activitywatch
|
- name: Создать каталог установленного релиза
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ aw_release_install_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Установить бинарный файл AW server
|
||||||
|
ansible.builtin.copy:
|
||||||
|
remote_src: true
|
||||||
|
src: "{{ aw_server_binary_path }}"
|
||||||
|
dest: "{{ aw_release_install_dir }}/aw-server-rust"
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Создать ссылку на активный бинарный файл AW server
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "{{ aw_release_install_dir }}/aw-server-rust"
|
||||||
|
dest: /opt/activitywatch/bin/aw-server-rust
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Синхронизировать WebUI в RU каталог
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "rsync -a --delete {{ aw_release_extracted }}/ /opt/activitywatch/"
|
cmd: "rsync -a {{ aw_webui_source_path }}/ {{ aw_server_webui_dir }}/"
|
||||||
|
|
||||||
- name: Copy bootstrap files from repository
|
- name: Настроить владельца файлов /opt/activitywatch
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /opt/activitywatch
|
||||||
|
state: directory
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
- name: Установить systemd service из шаблона репозитория
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/systemd/system/activitywatch-server.service
|
||||||
|
mode: "0644"
|
||||||
|
content: >-
|
||||||
|
{{
|
||||||
|
lookup('file', aw_repo_root + '/aw-server/activitywatch-server.service')
|
||||||
|
| replace('__AW_SERVER_USER__', aw_server_user)
|
||||||
|
| replace('__AW_SERVER_GROUP__', aw_server_group)
|
||||||
|
| replace('__AW_SERVER_DATA_DIR__', aw_server_data_dir)
|
||||||
|
}}
|
||||||
|
notify:
|
||||||
|
- Перезагрузить systemd
|
||||||
|
- Перезапустить activitywatch
|
||||||
|
|
||||||
|
- name: Скопировать RU patch файлы WebUI из репозитория
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ item.src }}"
|
src: "{{ item.src }}"
|
||||||
dest: "{{ item.dest }}"
|
dest: "{{ item.dest }}"
|
||||||
mode: "{{ item.mode }}"
|
mode: "{{ item.mode }}"
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
loop:
|
loop:
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/activitywatch-server.service", dest: "/etc/systemd/system/activitywatch-server.service", mode: "0644" }
|
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" }
|
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" }
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" }
|
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" }
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
||||||
notify:
|
|
||||||
- Reload systemd
|
|
||||||
- Restart activitywatch
|
|
||||||
|
|
||||||
- name: Copy WebUI index template from installed distribution
|
- name: Проверить наличие index.html после копирования
|
||||||
ansible.builtin.copy:
|
ansible.builtin.stat:
|
||||||
remote_src: true
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
src: "/opt/activitywatch/aw-webui/index.html"
|
register: aw_webui_ru_index
|
||||||
dest: "{{ aw_server_webui_dir }}/index.html"
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Insert RU patch scripts into index.html
|
- name: Проверить, что index.html доступен для RU patch
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- aw_webui_ru_index.stat.exists
|
||||||
|
fail_msg: "Не найден index.html WebUI для применения RU patch."
|
||||||
|
|
||||||
|
- name: Удалить старые теги RU patch из index.html
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
|
regexp: '<script[^>]+(?:ru-patch-v5\.js|sw-cleanup\.js|aw-ru-patch\.js|aw-sw-cleanup\.js)[^>]*></script>'
|
||||||
|
replace: ''
|
||||||
|
|
||||||
|
- name: Добавить cleanup script RU patch в index.html
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: "{{ aw_server_webui_dir }}/index.html"
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
regexp: '</head>'
|
regexp: '</head>'
|
||||||
replace: '<script src="/js/sw-cleanup.js?v={{ aw_sw_cleanup_cache_bust }}"></script></head>'
|
replace: '<script src="/js/sw-cleanup.js?v={{ aw_sw_cleanup_cache_bust }}"></script></head>'
|
||||||
|
|
||||||
- name: Insert RU patch loader before body end
|
- name: Добавить загрузчик RU patch перед закрытием body
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: "{{ aw_server_webui_dir }}/index.html"
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
regexp: '</body>'
|
regexp: '</body>'
|
||||||
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v={{ aw_ru_patch_cache_bust }}"></script></body>'
|
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v={{ aw_ru_patch_cache_bust }}"></script></body>'
|
||||||
|
|
||||||
- name: Write /etc/activitywatch/aw-server.env
|
- name: Записать /etc/activitywatch/aw-server.env
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /etc/activitywatch/aw-server.env
|
dest: /etc/activitywatch/aw-server.env
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
content: |
|
content: |
|
||||||
AW_SERVER_HOST={{ aw_server_bind_host }}
|
AW_SERVER_BIND_HOST={{ aw_server_bind_host }}
|
||||||
AW_SERVER_PORT={{ aw_server_port }}
|
AW_SERVER_PORT={{ aw_server_port }}
|
||||||
AW_DATA_DIR={{ aw_server_data_dir }}
|
AW_SERVER_DATA_DIR={{ aw_server_data_dir }}
|
||||||
AW_LOG_DIR={{ aw_server_log_dir }}
|
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
||||||
AW_WEBUI_DIR={{ aw_server_webui_dir }}
|
AW_SERVER_WEBUI_DIR={{ aw_server_webui_dir }}
|
||||||
AW_SERVER_USER={{ aw_server_user }}
|
AW_SERVER_USER={{ aw_server_user }}
|
||||||
AW_SERVER_GROUP={{ aw_server_group }}
|
AW_SERVER_GROUP={{ aw_server_group }}
|
||||||
|
|
||||||
- name: Enable and start service
|
- name: Включить и запустить сервис
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: activitywatch-server.service
|
name: activitywatch-server.service
|
||||||
enabled: true
|
enabled: true
|
||||||
state: restarted
|
state: restarted
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Wait for API
|
- name: Дождаться ответа API
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info"
|
||||||
method: GET
|
method: GET
|
||||||
@@ -147,25 +257,25 @@
|
|||||||
delay: 3
|
delay: 3
|
||||||
until: aw_api.status == 200
|
until: aw_api.status == 200
|
||||||
|
|
||||||
- name: Apply baseline worktime settings (classes)
|
- name: Применить базовые worktime settings (classes)
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
||||||
method: POST
|
method: POST
|
||||||
body: "{{ aw_worktime_classes }}"
|
body: "{{ aw_worktime_classes }}"
|
||||||
body_format: json
|
body_format: json
|
||||||
status_code: 201
|
status_code: [200, 201]
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Apply baseline views (include DLP and worktime)
|
- name: Применить базовые views для DLP и worktime
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
||||||
method: POST
|
method: POST
|
||||||
body: "{{ aw_default_views }}"
|
body: "{{ aw_default_views }}"
|
||||||
body_format: json
|
body_format: json
|
||||||
status_code: 201
|
status_code: [200, 201]
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Derive worktime durationDefault from aw_worktime_from/to
|
- name: Вычислить worktime durationDefault из aw_worktime_from/to
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}"
|
aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}"
|
||||||
aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}"
|
aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}"
|
||||||
@@ -182,7 +292,7 @@
|
|||||||
}}
|
}}
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Normalize derived durationDefault for overnight shifts
|
- name: Нормализовать durationDefault для ночных смен
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
aw_worktime_duration_default_effective: >-
|
aw_worktime_duration_default_effective: >-
|
||||||
{{
|
{{
|
||||||
@@ -192,15 +302,15 @@
|
|||||||
}}
|
}}
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Validate derived durationDefault is sane
|
- name: Проверить корректность durationDefault
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- aw_worktime_duration_default_effective | int > 0
|
- aw_worktime_duration_default_effective | int > 0
|
||||||
- aw_worktime_duration_default_effective | int <= 86400
|
- aw_worktime_duration_default_effective | int <= 86400
|
||||||
fail_msg: "Invalid worktime window: {{ aw_worktime_from }}..{{ aw_worktime_to }}"
|
fail_msg: "Некорректный интервал рабочего времени: {{ aw_worktime_from }}..{{ aw_worktime_to }}"
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Apply baseline worktime period (startOfDay)
|
- name: Применить базовый период worktime (startOfDay)
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay"
|
||||||
method: POST
|
method: POST
|
||||||
@@ -209,7 +319,7 @@
|
|||||||
status_code: 200
|
status_code: 200
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Apply baseline worktime period (durationDefault seconds)
|
- name: Применить базовый период worktime (durationDefault seconds)
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault"
|
||||||
method: POST
|
method: POST
|
||||||
@@ -219,11 +329,11 @@
|
|||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: Reload systemd
|
- name: Перезагрузить systemd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Restart activitywatch
|
- name: Перезапустить activitywatch
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: activitywatch-server.service
|
name: activitywatch-server.service
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy AWatch-rus Windows phase2 collectors
|
- name: Развернуть Windows/RDP phase-2 collector'ы AWatch-rus
|
||||||
hosts: aw_windows
|
hosts: aw_windows
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
aw_windows_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||||
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
||||||
|
aw_windows_server_scheme: "http"
|
||||||
aw_windows_server_host: "10.10.10.13"
|
aw_windows_server_host: "10.10.10.13"
|
||||||
aw_windows_server_port: 5600
|
aw_windows_server_port: 5600
|
||||||
|
aw_windows_package_version: "v0.13.2"
|
||||||
|
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||||
|
aw_windows_package_zip_path: ""
|
||||||
aw_windows_domain: "SHARKON2025"
|
aw_windows_domain: "SHARKON2025"
|
||||||
aw_windows_users:
|
aw_windows_users:
|
||||||
- user1
|
- user1
|
||||||
@@ -35,22 +39,24 @@
|
|||||||
aw_windows_recovery_task_name: "ActivityWatch Recovery"
|
aw_windows_recovery_task_name: "ActivityWatch Recovery"
|
||||||
aw_windows_force_task_restart: true
|
aw_windows_force_task_restart: true
|
||||||
aw_windows_api_smoke_check_enabled: true
|
aw_windows_api_smoke_check_enabled: true
|
||||||
aw_windows_api_smoke_check_bucket: "aw-watcher-afk_SHARKON2025"
|
aw_windows_api_smoke_check_bucket: ""
|
||||||
aw_windows_api_smoke_check_limit: 10
|
aw_windows_api_smoke_check_limit: 10
|
||||||
|
aw_windows_fail_on_validation_error: true
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Validate required variables
|
- name: Проверить обязательные переменные
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- aw_windows_server_host is defined
|
- aw_windows_server_host is defined
|
||||||
- aw_windows_server_port is defined
|
- aw_windows_server_port is defined
|
||||||
|
- aw_windows_server_scheme is defined
|
||||||
- aw_windows_domain is defined
|
- aw_windows_domain is defined
|
||||||
- aw_windows_users_effective | length > 0
|
- aw_windows_users_effective | length > 0
|
||||||
- aw_windows_install_root is defined
|
- aw_windows_install_root is defined
|
||||||
- aw_windows_state_root is defined
|
- aw_windows_state_root is defined
|
||||||
fail_msg: "Missing required Windows deployment variables."
|
fail_msg: "Не заданы обязательные переменные Windows-развёртывания."
|
||||||
|
|
||||||
- name: Ensure deploy directories exist
|
- name: Создать каталоги развёртывания
|
||||||
ansible.windows.win_file:
|
ansible.windows.win_file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
@@ -58,7 +64,7 @@
|
|||||||
- "{{ aw_windows_deploy_root }}"
|
- "{{ aw_windows_deploy_root }}"
|
||||||
- "{{ aw_windows_deploy_root }}\\windows"
|
- "{{ aw_windows_deploy_root }}\\windows"
|
||||||
|
|
||||||
- name: Upload Windows deployment toolkit
|
- name: Загрузить Windows toolkit развёртывания
|
||||||
ansible.windows.win_copy:
|
ansible.windows.win_copy:
|
||||||
src: "{{ aw_windows_repo_root }}/windows/{{ item }}"
|
src: "{{ aw_windows_repo_root }}/windows/{{ item }}"
|
||||||
dest: "{{ aw_windows_deploy_root }}\\windows\\{{ item }}"
|
dest: "{{ aw_windows_deploy_root }}\\windows\\{{ item }}"
|
||||||
@@ -67,6 +73,7 @@
|
|||||||
- ActivityWatch.Windows.Common.psm1
|
- ActivityWatch.Windows.Common.psm1
|
||||||
- browser-domains-native-collector.ps1
|
- browser-domains-native-collector.ps1
|
||||||
- dlp-endpoint-signals-collector.ps1
|
- dlp-endpoint-signals-collector.ps1
|
||||||
|
- worktime-session-collector.ps1
|
||||||
- deploy-domain-users.ps1
|
- deploy-domain-users.ps1
|
||||||
- deploy-ensemble.ps1
|
- deploy-ensemble.ps1
|
||||||
- hardening-recovery.ps1
|
- hardening-recovery.ps1
|
||||||
@@ -74,24 +81,23 @@
|
|||||||
- web-category-rules.example.json
|
- web-category-rules.example.json
|
||||||
- dlp-policy.example.json
|
- dlp-policy.example.json
|
||||||
|
|
||||||
- name: Upload user list for domain deploy
|
- name: Загрузить список пользователей для доменного развёртывания
|
||||||
ansible.windows.win_copy:
|
ansible.windows.win_copy:
|
||||||
dest: "{{ aw_windows_deploy_root }}\\windows\\users.txt"
|
dest: "{{ aw_windows_deploy_root }}\\windows\\users.txt"
|
||||||
content: |
|
content: |
|
||||||
{% for user in aw_windows_users -%}
|
{% for user in aw_windows_users_effective -%}
|
||||||
{{ user }}
|
|
||||||
{% endfor -%}
|
|
||||||
{% for user in aw_windows_extra_users -%}
|
|
||||||
{{ user }}
|
{{ user }}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
|
|
||||||
- name: Run phase2 ensemble deployment
|
- name: Запустить phase-2 ensemble развёртывание
|
||||||
ansible.windows.win_powershell:
|
ansible.windows.win_powershell:
|
||||||
script: |
|
script: |
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$params = @{
|
$params = @{
|
||||||
|
ServerScheme = "{{ aw_windows_server_scheme }}"
|
||||||
ServerHost = "{{ aw_windows_server_host }}"
|
ServerHost = "{{ aw_windows_server_host }}"
|
||||||
ServerPort = {{ aw_windows_server_port }}
|
ServerPort = {{ aw_windows_server_port }}
|
||||||
|
Version = "{{ aw_windows_package_version }}"
|
||||||
Domain = "{{ aw_windows_domain }}"
|
Domain = "{{ aw_windows_domain }}"
|
||||||
UserListPath = "{{ aw_windows_deploy_root }}\windows\users.txt"
|
UserListPath = "{{ aw_windows_deploy_root }}\windows\users.txt"
|
||||||
InstallRoot = "{{ aw_windows_install_root }}"
|
InstallRoot = "{{ aw_windows_install_root }}"
|
||||||
@@ -106,12 +112,18 @@
|
|||||||
CustomRulesPath = "{{ aw_windows_rules_path }}"
|
CustomRulesPath = "{{ aw_windows_rules_path }}"
|
||||||
CustomPolicyPath = "{{ aw_windows_policy_path }}"
|
CustomPolicyPath = "{{ aw_windows_policy_path }}"
|
||||||
}
|
}
|
||||||
|
{% if (aw_windows_package_url | default('') | string | length) > 0 %}
|
||||||
|
$params.PackageUrl = "{{ aw_windows_package_url }}"
|
||||||
|
{% endif %}
|
||||||
|
{% if (aw_windows_package_zip_path | default('') | string | length) > 0 %}
|
||||||
|
$params.PackageZipPath = "{{ aw_windows_package_zip_path }}"
|
||||||
|
{% endif %}
|
||||||
{% if aw_windows_skip_hardening | bool %}
|
{% if aw_windows_skip_hardening | bool %}
|
||||||
$params.SkipHardening = $true
|
$params.SkipHardening = $true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
& "{{ aw_windows_deploy_root }}\windows\deploy-ensemble.ps1" @params
|
& "{{ aw_windows_deploy_root }}\windows\deploy-ensemble.ps1" @params
|
||||||
|
|
||||||
- name: Force start ActivityWatch recovery and launch tasks
|
- name: Принудительно запустить ActivityWatch recovery и launch tasks
|
||||||
when: aw_windows_force_task_restart | bool
|
when: aw_windows_force_task_restart | bool
|
||||||
ansible.windows.win_powershell:
|
ansible.windows.win_powershell:
|
||||||
script: |
|
script: |
|
||||||
@@ -121,11 +133,33 @@
|
|||||||
Where-Object TaskName -like "{{ aw_windows_launch_task_pattern }}" |
|
Where-Object TaskName -like "{{ aw_windows_launch_task_pattern }}" |
|
||||||
ForEach-Object { Start-ScheduledTask -TaskName $_.TaskName }
|
ForEach-Object { Start-ScheduledTask -TaskName $_.TaskName }
|
||||||
|
|
||||||
- name: Wait for fresh AFK events to appear on AW server
|
- name: Получить Windows hostname для AW smoke-check bucket
|
||||||
when: aw_windows_api_smoke_check_enabled | bool
|
when:
|
||||||
|
- aw_windows_api_smoke_check_enabled | bool
|
||||||
|
- aw_windows_afk_enabled | bool
|
||||||
|
ansible.windows.win_command: powershell.exe -NoProfile -Command "$env:COMPUTERNAME"
|
||||||
|
register: aw_windows_hostname_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Вычислить AW AFK smoke-check bucket
|
||||||
|
when:
|
||||||
|
- aw_windows_api_smoke_check_enabled | bool
|
||||||
|
- aw_windows_afk_enabled | bool
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
aw_windows_api_smoke_check_bucket_effective: >-
|
||||||
|
{{
|
||||||
|
aw_windows_api_smoke_check_bucket
|
||||||
|
if (aw_windows_api_smoke_check_bucket | default('') | string | length) > 0
|
||||||
|
else 'aw-watcher-afk_' ~ (aw_windows_hostname_result.stdout | trim)
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Дождаться свежих AFK событий на AW server
|
||||||
|
when:
|
||||||
|
- aw_windows_api_smoke_check_enabled | bool
|
||||||
|
- aw_windows_afk_enabled | bool
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://{{ aw_windows_server_host }}:{{ aw_windows_server_port }}/api/0/buckets/{{ aw_windows_api_smoke_check_bucket }}/events?limit={{ aw_windows_api_smoke_check_limit }}"
|
url: "{{ aw_windows_server_scheme }}://{{ aw_windows_server_host }}:{{ aw_windows_server_port }}/api/0/buckets/{{ aw_windows_api_smoke_check_bucket_effective }}/events?limit={{ aw_windows_api_smoke_check_limit }}"
|
||||||
method: GET
|
method: GET
|
||||||
return_content: true
|
return_content: true
|
||||||
register: aw_windows_api_smoke
|
register: aw_windows_api_smoke
|
||||||
@@ -141,29 +175,32 @@
|
|||||||
retries: 10
|
retries: 10
|
||||||
delay: 6
|
delay: 6
|
||||||
|
|
||||||
- name: Run validation and store report on target
|
- name: Выполнить валидацию и сохранить отчёт на целевом Windows host
|
||||||
ansible.windows.win_powershell:
|
ansible.windows.win_powershell:
|
||||||
script: |
|
script: |
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$report = & "{{ aw_windows_deploy_root }}\windows\validate-deployment.ps1" `
|
$report = & "{{ aw_windows_deploy_root }}\windows\validate-deployment.ps1" `
|
||||||
-ConfigPath "{{ aw_windows_state_root }}\deployment-config.json"
|
-ConfigPath "{{ aw_windows_state_root }}\deployment-config.json"
|
||||||
$report | ConvertTo-Json -Depth 12 | Out-File -FilePath "{{ aw_windows_validation_remote_path }}" -Encoding utf8
|
$report | ConvertTo-Json -Depth 12 | Out-File -FilePath "{{ aw_windows_validation_remote_path }}" -Encoding utf8
|
||||||
|
if ({{ '$true' if (aw_windows_fail_on_validation_error | bool) else '$false' }} -and -not [bool]$report.overallOk) {
|
||||||
|
throw "Проверка развёртывания ActivityWatch завершилась ошибкой. Отчёт: {{ aw_windows_validation_remote_path }}"
|
||||||
|
}
|
||||||
|
|
||||||
- name: Ensure local validation directory exists
|
- name: Создать локальный каталог для validation reports
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ aw_windows_validation_local_dir }}"
|
path: "{{ aw_windows_validation_local_dir }}"
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
- name: Fetch validation report
|
- name: Забрать validation report
|
||||||
ansible.builtin.fetch:
|
ansible.builtin.fetch:
|
||||||
src: "{{ aw_windows_validation_remote_path }}"
|
src: "{{ aw_windows_validation_remote_path }}"
|
||||||
dest: "{{ aw_windows_validation_local_dir }}/"
|
dest: "{{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_phase2_ansible.json"
|
||||||
flat: false
|
flat: true
|
||||||
|
|
||||||
- name: Show report location
|
- name: Показать путь к отчёту
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg:
|
msg:
|
||||||
- "Windows phase2 deploy completed on {{ inventory_hostname }}."
|
- "Windows phase2 развёртывание завершено на {{ inventory_hostname }}."
|
||||||
- "Validation report: {{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}/C$/Windows/Temp/aw_validate_phase2_ansible.json"
|
- "Отчёт проверки: {{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_phase2_ansible.json"
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ aw_server_log_dir: "/var/log/activitywatch"
|
|||||||
aw_server_user: "activitywatch"
|
aw_server_user: "activitywatch"
|
||||||
aw_server_group: "activitywatch"
|
aw_server_group: "activitywatch"
|
||||||
|
|
||||||
aw_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_repo_root: "{{ playbook_dir | dirname }}"
|
||||||
|
|
||||||
# Optional: apply a baseline worktime-focused categorization and views via AW settings API.
|
# Опционально: применить базовые категории и views для рабочего времени через AW settings API.
|
||||||
# WARNING: this overwrites existing server-side settings/classes/views.
|
# Внимание: это перезаписывает существующие server-side settings/classes/views.
|
||||||
aw_apply_worktime_settings: false
|
aw_apply_worktime_settings: false
|
||||||
|
|
||||||
# Optional defaults for the worktime period in Web UI.
|
# Опциональные значения периода рабочего времени в Web UI.
|
||||||
# startOfDay controls day-boundary and default report window start.
|
# startOfDay задаёт границу дня и стартовое время окна отчёта.
|
||||||
# durationDefault controls default time range (seconds) shown in UI.
|
# durationDefault задаёт диапазон по умолчанию в секундах.
|
||||||
#
|
#
|
||||||
# Recommended: set worktime window explicitly and let the playbook derive duration.
|
# Рекомендуется явно задать рабочий интервал и дать playbook вычислить duration.
|
||||||
aw_worktime_from: "08:00"
|
aw_worktime_from: "08:00"
|
||||||
aw_worktime_to: "17:00"
|
aw_worktime_to: "17:00"
|
||||||
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
aw_windows_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||||
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
||||||
|
aw_windows_server_scheme: "http"
|
||||||
aw_windows_server_host: "10.10.10.13"
|
aw_windows_server_host: "10.10.10.13"
|
||||||
aw_windows_server_port: 5600
|
aw_windows_server_port: 5600
|
||||||
|
aw_windows_package_version: "v0.13.2"
|
||||||
|
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||||
|
aw_windows_package_zip_path: ""
|
||||||
aw_windows_domain: "SHARKON2025"
|
aw_windows_domain: "SHARKON2025"
|
||||||
aw_windows_users:
|
aw_windows_users:
|
||||||
- user1
|
- user1
|
||||||
@@ -31,3 +35,10 @@ aw_windows_policy_path: "{{ aw_windows_deploy_root }}\\windows\\dlp-policy.examp
|
|||||||
|
|
||||||
aw_windows_validation_remote_path: "C:\\Windows\\Temp\\aw_validate_phase2_ansible.json"
|
aw_windows_validation_remote_path: "C:\\Windows\\Temp\\aw_validate_phase2_ansible.json"
|
||||||
aw_windows_validation_local_dir: "/tmp/aw-rus-validation"
|
aw_windows_validation_local_dir: "/tmp/aw-rus-validation"
|
||||||
|
aw_windows_fail_on_validation_error: true
|
||||||
|
|
||||||
|
# По умолчанию AFK bucket вычисляется как aw-watcher-afk_<COMPUTERNAME>.
|
||||||
|
# Задайте явное значение только если watcher пишет в нестандартный bucket.
|
||||||
|
aw_windows_api_smoke_check_enabled: true
|
||||||
|
aw_windows_api_smoke_check_bucket: ""
|
||||||
|
aw_windows_api_smoke_check_limit: 10
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
# Full-stack installer for AWatch-rus.
|
# Полный установщик AWatch-rus.
|
||||||
# Runs end-to-end rollout in one command:
|
# Выполняет развёртывание одной командой:
|
||||||
# 1) Proxmox CT provision + AW bootstrap (if [proxmox] exists in inventory)
|
# 1) создание Proxmox CT + bootstrap AW (если в inventory есть [proxmox])
|
||||||
# 2) AW server deploy on [aw_server] hosts
|
# 2) развёртывание AW server на хостах [aw_server]
|
||||||
# 3) Windows phase2 rollout on [aw_windows] hosts
|
# 3) развёртывание Windows/RDP collector'ов на [aw_windows]
|
||||||
# 4) pfSense poller deploy on [aw_pfsense_pollers] hosts
|
# 4) развёртывание pfSense poller'а на [aw_pfsense_pollers]
|
||||||
#
|
#
|
||||||
# Notes:
|
# Примечания:
|
||||||
# - Keep only relevant inventory groups filled for your environment.
|
# - Заполняйте только нужные группы inventory для своего окружения.
|
||||||
# - Plays with unmatched host groups are skipped automatically by Ansible.
|
# - Play без совпадающих host groups Ansible пропускает автоматически.
|
||||||
|
|
||||||
- import_playbook: provision_proxmox_ct_and_deploy_aw.yml
|
- import_playbook: provision_proxmox_ct_and_deploy_aw.yml
|
||||||
- import_playbook: deploy_aw_server.yml
|
- import_playbook: deploy_aw_server.yml
|
||||||
|
|||||||
@@ -5,5 +5,8 @@ pve-main ansible_host=192.168.10.2 ansible_user=root ansible_port=22
|
|||||||
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||||
|
|
||||||
[aw_windows]
|
[aw_windows]
|
||||||
# NOTE: in RU-localized installs this account is often "Администратор" instead of "Administrator".
|
# Примечание: в русифицированных Windows часто нужен "Администратор", а не "Administrator".
|
||||||
win-node1 ansible_host=192.168.100.21 ansible_user=Администратор ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
win-node1 ansible_host=192.168.100.21 ansible_user=Администратор ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
||||||
|
|
||||||
|
[aw_pfsense_pollers]
|
||||||
|
# pfsense-poller1 ansible_host=192.168.100.30 ansible_user=root ansible_port=22
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Provision single Proxmox CT and deploy AWatch-rus
|
- name: Создать один Proxmox CT и развернуть AWatch-rus
|
||||||
hosts: proxmox
|
hosts: proxmox
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
- settings/views-default.json
|
- settings/views-default.json
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Execute single-CT provisioning workflow
|
- name: Выполнить workflow создания одного CT
|
||||||
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
||||||
vars:
|
vars:
|
||||||
ct_id: "{{ proxmox_ct_id }}"
|
ct_id: "{{ proxmox_ct_id }}"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Provision Proxmox CT matrix and deploy AWatch-rus with RU patch
|
- name: Создать матрицу Proxmox CT и развернуть AWatch-rus с RU patch
|
||||||
hosts: proxmox
|
hosts: proxmox
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
- settings/views-default.json
|
- settings/views-default.json
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Validate CT matrix is provided
|
- name: Проверить, что матрица CT задана
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- proxmox_ct_matrix is defined
|
- proxmox_ct_matrix is defined
|
||||||
- proxmox_ct_matrix | length > 0
|
- proxmox_ct_matrix | length > 0
|
||||||
fail_msg: "Define proxmox_ct_matrix in group_vars/proxmox-matrix.yml"
|
fail_msg: "Задайте proxmox_ct_matrix в group_vars/proxmox-matrix.yml"
|
||||||
|
|
||||||
- name: Execute provisioning workflow for each CT
|
- name: Выполнить workflow создания для каждого CT
|
||||||
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
||||||
vars:
|
vars:
|
||||||
ct_id: "{{ item.id }}"
|
ct_id: "{{ item.id }}"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Validate required per-CT variables
|
- name: Проверить обязательные переменные CT
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- ct_id is defined
|
- ct_id is defined
|
||||||
@@ -27,14 +27,14 @@
|
|||||||
- aw_server_log_dir is defined
|
- aw_server_log_dir is defined
|
||||||
- aw_server_user is defined
|
- aw_server_user is defined
|
||||||
- aw_server_group is defined
|
- aw_server_group is defined
|
||||||
fail_msg: "Missing required variables for CT provisioning/deploy."
|
fail_msg: "Не заданы обязательные переменные для создания CT и развёртывания."
|
||||||
|
|
||||||
- name: Build CT network string
|
- name: Сформировать сетевую строку CT
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
ct_net0: >-
|
ct_net0: >-
|
||||||
name=eth0,bridge={{ ct_bridge }},ip={{ ct_ip }},gw={{ ct_gw }}{% if (ct_vlan | default('') | string | length) > 0 %},tag={{ ct_vlan }}{% endif %}
|
name=eth0,bridge={{ ct_bridge }},ip={{ ct_ip }},gw={{ ct_gw }}{% if (ct_vlan | default('') | string | length) > 0 %},tag={{ ct_vlan }}{% endif %}
|
||||||
|
|
||||||
- name: Check whether CT already exists
|
- name: Проверить, существует ли CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Create CT when absent
|
- name: Создать CT, если он отсутствует
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -78,8 +78,9 @@
|
|||||||
- --ostype
|
- --ostype
|
||||||
- debian
|
- debian
|
||||||
when: ct_status_check.rc != 0
|
when: ct_status_check.rc != 0
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Check current CT runtime state
|
- name: Проверить текущее состояние CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
register: ct_runtime_status
|
register: ct_runtime_status
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Start CT when stopped
|
- name: Запустить CT, если он остановлен
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -96,20 +97,23 @@
|
|||||||
- "{{ ct_id }}"
|
- "{{ ct_id }}"
|
||||||
when: "'stopped' in ct_runtime_status.stdout"
|
when: "'stopped' in ct_runtime_status.stdout"
|
||||||
|
|
||||||
- name: Ensure bootstrap directory on Proxmox host
|
- name: Создать bootstrap каталог на Proxmox host
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ proxmox_bootstrap_dir }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
|
loop:
|
||||||
|
- "{{ proxmox_bootstrap_dir }}"
|
||||||
|
- "{{ proxmox_bootstrap_dir }}/settings"
|
||||||
|
|
||||||
- name: Copy AW bootstrap files to Proxmox host temp
|
- name: Скопировать AW bootstrap файлы во временный каталог Proxmox host
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ aw_repo_root }}/aw-server/{{ item }}"
|
src: "{{ aw_repo_root }}/aw-server/{{ item }}"
|
||||||
dest: "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
dest: "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
loop: "{{ aw_bootstrap_files }}"
|
loop: "{{ aw_bootstrap_files }}"
|
||||||
|
|
||||||
- name: Bootstrap CT OS dependencies
|
- name: Установить базовые зависимости ОС внутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -122,12 +126,16 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server
|
apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server python3
|
||||||
mkdir -p /root/bootstrap /etc/activitywatch
|
mkdir -p /root/bootstrap/settings /etc/activitywatch
|
||||||
systemctl enable ssh || true
|
systemctl enable ssh || true
|
||||||
systemctl restart ssh || true
|
systemctl restart ssh || true
|
||||||
|
register: ct_bootstrap_result
|
||||||
|
retries: 10
|
||||||
|
delay: 6
|
||||||
|
until: ct_bootstrap_result.rc == 0
|
||||||
|
|
||||||
- name: Push bootstrap files into CT
|
- name: Передать bootstrap файлы внутрь CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -137,7 +145,7 @@
|
|||||||
- "/root/bootstrap/{{ item }}"
|
- "/root/bootstrap/{{ item }}"
|
||||||
loop: "{{ aw_bootstrap_files }}"
|
loop: "{{ aw_bootstrap_files }}"
|
||||||
|
|
||||||
- name: Write AW server env file on Proxmox host temp
|
- name: Записать AW server env во временный каталог Proxmox host
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
dest: "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
@@ -151,8 +159,9 @@
|
|||||||
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
||||||
AW_SERVER_USER={{ aw_server_user }}
|
AW_SERVER_USER={{ aw_server_user }}
|
||||||
AW_SERVER_GROUP={{ aw_server_group }}
|
AW_SERVER_GROUP={{ aw_server_group }}
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Push AW server env into CT
|
- name: Передать AW server env внутрь CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -160,8 +169,9 @@
|
|||||||
- "{{ ct_id }}"
|
- "{{ ct_id }}"
|
||||||
- "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
- "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
||||||
- /etc/activitywatch/aw-server.env
|
- /etc/activitywatch/aw-server.env
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Set mode for env inside CT
|
- name: Настроить права env файла внутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -172,7 +182,7 @@
|
|||||||
- "0600"
|
- "0600"
|
||||||
- /etc/activitywatch/aw-server.env
|
- /etc/activitywatch/aw-server.env
|
||||||
|
|
||||||
- name: Install server and apply RU patch inside CT
|
- name: Установить сервер и применить RU patch внутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -188,7 +198,7 @@
|
|||||||
bash /root/bootstrap/apply_webui_ru_patch.sh
|
bash /root/bootstrap/apply_webui_ru_patch.sh
|
||||||
systemctl restart activitywatch-server.service
|
systemctl restart activitywatch-server.service
|
||||||
|
|
||||||
- name: Validate AW API from inside CT
|
- name: Проверить AW API изнутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -199,7 +209,7 @@
|
|||||||
- -lc
|
- -lc
|
||||||
- "curl -fsS http://127.0.0.1:{{ aw_server_port }}/api/0/info >/dev/null"
|
- "curl -fsS http://127.0.0.1:{{ aw_server_port }}/api/0/info >/dev/null"
|
||||||
|
|
||||||
- name: Validate RU patch hooks in index
|
- name: Проверить hooks RU patch в index.html
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -210,8 +220,8 @@
|
|||||||
- -lc
|
- -lc
|
||||||
- "grep -q 'ru-patch-v5.js' {{ aw_server_webui_dir }}/index.html && grep -q 'sw-cleanup.js' {{ aw_server_webui_dir }}/index.html"
|
- "grep -q 'ru-patch-v5.js' {{ aw_server_webui_dir }}/index.html && grep -q 'sw-cleanup.js' {{ aw_server_webui_dir }}/index.html"
|
||||||
|
|
||||||
- name: Show final endpoint
|
- name: Показать итоговый endpoint
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg:
|
msg:
|
||||||
- "CT {{ ct_id }} is provisioned and configured."
|
- "CT {{ ct_id }} создан и настроен."
|
||||||
- "ActivityWatch endpoint: http://{{ ct_ip | regex_replace('/[0-9]+$', '') }}:{{ aw_server_port }}"
|
- "Endpoint ActivityWatch: http://{{ ct_ip | regex_replace('/[0-9]+$', '') }}:{{ aw_server_port }}"
|
||||||
|
|||||||
@@ -1,82 +1,101 @@
|
|||||||
# Ansible ensemble for AWatch-rus
|
# Ansible ensemble for AWatch-rus
|
||||||
|
|
||||||
Эта директория содержит Ansible-ensemble для двух сценариев:
|
Эта директория содержит Ansible-ensemble для полного развёртывания AWatch-rus:
|
||||||
|
|
||||||
- деплой на уже существующий Debian host/CT;
|
- деплой на уже существующий Debian host/CT;
|
||||||
- полный цикл с нуля в Proxmox: создание CT + bootstrap + установка ActivityWatch + RU patch.
|
- полный цикл с нуля в Proxmox: создание CT + bootstrap + установка ActivityWatch + RU patch;
|
||||||
- централизованный деплой Windows phase-2 collectors по WinRM.
|
- централизованное развёртывание Windows phase-2 collector'ов по WinRM;
|
||||||
- deployment внешнего pfSense poller'а на Debian/Ubuntu utility VM.
|
- развёртывание внешнего pfSense poller'а на Debian/Ubuntu utility VM.
|
||||||
|
|
||||||
## Файлы
|
## Файлы
|
||||||
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_server.yml` — основной playbook.
|
- `ansible/deploy_aw_server.yml` — основной playbook для уже существующего Debian/CT host.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/provision_proxmox_ct_and_deploy_aw.yml` — full-stack playbook для Proxmox.
|
- `ansible/provision_proxmox_ct_and_deploy_aw.yml` — полный playbook для Proxmox.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml` — массовый full-stack playbook (несколько CT).
|
- `ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml` — массовый полный playbook (несколько CT).
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_windows_phase2.yml` — WinRM playbook для развёртывания phase-2 Windows collector'ов.
|
- `ansible/deploy_aw_windows_phase2.yml` — WinRM playbook для развёртывания Windows/RDP collector'ов.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_pfsense_poller.yml` — deployment pfSense poller'а.
|
- `ansible/deploy_aw_pfsense_poller.yml` — развёртывание pfSense poller'а.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/inventory.example.ini` — шаблон inventory.
|
- `ansible/install_full_stack.yml` — полный установочный playbook (оркестратор всех этапов).
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml` — шаблон переменных.
|
- `ansible/inventory.example.ini` — шаблон inventory.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox.example.yml` — шаблон переменных CT в Proxmox.
|
- `ansible/group_vars/*.example.yml` — шаблоны переменных.
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox-matrix.example.yml` — шаблон матрицы CT.
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/windows.example.yml` — шаблон переменных Windows phase-2.
|
|
||||||
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/pfsense-poller.example.yml` — шаблон переменных pfSense poller'а.
|
|
||||||
|
|
||||||
## Быстрый запуск
|
## Быстрый запуск
|
||||||
|
|
||||||
1. Скопируйте шаблоны:
|
1. Скопируйте шаблоны:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/all.yml`
|
- `cp ansible/group_vars/all.example.yml ansible/group_vars/all.yml`
|
||||||
2. Заполните значения в `inventory.ini` и `group_vars/all.yml`.
|
2. Заполните значения в `inventory.ini` и `group_vars/all.yml`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini deploy_aw_server.yml
|
ansible-playbook -i inventory.ini deploy_aw_server.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Полный установочный playbook (всё за один запуск)
|
||||||
|
|
||||||
|
Если нужно прогнать полный цикл одной командой:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ansible
|
||||||
|
ansible-playbook -i inventory.ini install_full_stack.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Что делает:
|
||||||
|
|
||||||
|
- `provision_proxmox_ct_and_deploy_aw.yml` (если есть хосты в группе `[proxmox]`);
|
||||||
|
- `deploy_aw_server.yml` (группа `[aw_server]`);
|
||||||
|
- `deploy_aw_windows_phase2.yml` (группа `[aw_windows]`);
|
||||||
|
- `deploy_aw_pfsense_poller.yml` (группа `[aw_pfsense_pollers]`).
|
||||||
|
|
||||||
|
Пустые группы в `inventory.ini` безопасны: соответствующий play будет пропущен.
|
||||||
|
|
||||||
## Полный запуск с нуля в Proxmox
|
## Полный запуск с нуля в Proxmox
|
||||||
|
|
||||||
1. Подготовьте inventory и vars:
|
1. Подготовьте inventory и vars:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/all.yml`
|
- `cp ansible/group_vars/all.example.yml ansible/group_vars/all.yml`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox.yml`
|
- `cp ansible/group_vars/proxmox.example.yml ansible/group_vars/proxmox.yml`
|
||||||
2. Заполните `group_vars/proxmox.yml` и `group_vars/all.yml`.
|
2. Заполните `group_vars/proxmox.yml` и `group_vars/all.yml`.
|
||||||
3. Запустите playbook:
|
3. Запустите playbook:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini provision_proxmox_ct_and_deploy_aw.yml
|
ansible-playbook -i inventory.ini provision_proxmox_ct_and_deploy_aw.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Массовый запуск (матрица CT)
|
## Массовый запуск (матрица CT)
|
||||||
|
|
||||||
1. Подготовьте матрицу:
|
1. Подготовьте матрицу:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox-matrix.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/proxmox-matrix.yml`
|
- `cp ansible/group_vars/proxmox-matrix.example.yml ansible/group_vars/proxmox-matrix.yml`
|
||||||
2. Заполните `proxmox-matrix.yml`.
|
2. Заполните `proxmox-matrix.yml`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini provision_proxmox_ct_matrix_and_deploy_aw.yml
|
ansible-playbook -i inventory.ini provision_proxmox_ct_matrix_and_deploy_aw.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Windows phase-2 rollout (WinRM)
|
## Windows phase-2 rollout (WinRM)
|
||||||
|
|
||||||
1. Подготовьте inventory и vars:
|
1. Подготовьте inventory и vars:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/windows.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/windows.yml`
|
- `cp ansible/group_vars/windows.example.yml ansible/group_vars/windows.yml`
|
||||||
2. Заполните `inventory.ini` (секция `[aw_windows]`) и `group_vars/windows.yml`.
|
2. Заполните `inventory.ini` (секция `[aw_windows]`) и `group_vars/windows.yml`.
|
||||||
|
- Для русской локализации Windows часто нужен `ansible_user=Администратор` (а не `Administrator`).
|
||||||
|
- Если WinRM закрыт, playbook не сможет стартовать и нужно сначала открыть `5985/5986` и `wsman`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini deploy_aw_windows_phase2.yml
|
ansible-playbook -i inventory.ini deploy_aw_windows_phase2.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
Playbook:
|
Playbook:
|
||||||
|
|
||||||
- выгружает `windows/*` toolkit на целевой хост в `C:\Deploy\AWatch-rus\windows`;
|
- выгружает полный `windows/*` toolkit на целевой хост в `C:\Deploy\AWatch-rus\windows`, включая DLP и `worktime-session-collector.ps1`;
|
||||||
- выполняет `deploy-ensemble.ps1` (deploy + hardening/recovery) с phase-2 policy/rules;
|
- выполняет `deploy-ensemble.ps1` (deploy + hardening/recovery) с phase-2 policy/rules;
|
||||||
|
- после deploy принудительно запускает `ActivityWatch Recovery` и все `ActivityWatch Launch *` задачи;
|
||||||
|
- выполняет API smoke-check bucket `aw-watcher-afk_<COMPUTERNAME>` и ожидает свежие `not-afk` события;
|
||||||
- запускает `validate-deployment.ps1`;
|
- запускает `validate-deployment.ps1`;
|
||||||
- забирает JSON-отчёт в локальную директорию (`/tmp/aw-rus-validation` по умолчанию).
|
- забирает JSON-отчёт в локальную директорию (`/tmp/aw-rus-validation` по умолчанию).
|
||||||
|
|
||||||
@@ -87,17 +106,20 @@ Playbook:
|
|||||||
- `aw_windows_incident_capture_enabled: false` — отключить блок incidentCapture;
|
- `aw_windows_incident_capture_enabled: false` — отключить блок incidentCapture;
|
||||||
- `aw_windows_incident_screenshot_enabled: false` — не делать скриншот при DLP-инциденте;
|
- `aw_windows_incident_screenshot_enabled: false` — не делать скриншот при DLP-инциденте;
|
||||||
- `aw_windows_incident_artifacts_root: 'C:\...\incident-artifacts'` — переопределить путь артефактов;
|
- `aw_windows_incident_artifacts_root: 'C:\...\incident-artifacts'` — переопределить путь артефактов;
|
||||||
|
- `aw_windows_package_version`, `aw_windows_package_url`, `aw_windows_package_zip_path` — версия и источник Windows-пакета ActivityWatch;
|
||||||
|
- `aw_windows_api_smoke_check_bucket: ""` — автоматически использовать `aw-watcher-afk_<COMPUTERNAME>`;
|
||||||
|
- `aw_windows_fail_on_validation_error: true` — завершать playbook ошибкой, если `validate-deployment.ps1` возвращает `overallOk=false`;
|
||||||
- `aw_windows_skip_hardening: true` — пропустить `hardening-recovery.ps1` внутри ensemble-скрипта.
|
- `aw_windows_skip_hardening: true` — пропустить `hardening-recovery.ps1` внутри ensemble-скрипта.
|
||||||
|
|
||||||
## pfSense poller rollout
|
## Развёртывание pfSense poller
|
||||||
|
|
||||||
1. Подготовьте vars:
|
1. Подготовьте vars:
|
||||||
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/pfsense-poller.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/pfsense-poller.yml`
|
- `cp ansible/group_vars/pfsense-poller.example.yml ansible/group_vars/pfsense-poller.yml`
|
||||||
2. Добавьте inventory group `[aw_pfsense_pollers]`.
|
2. Добавьте inventory group `[aw_pfsense_pollers]`.
|
||||||
3. Запустите:
|
3. Запустите:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/igor/tmp/AWatch-rus/ansible
|
cd ansible
|
||||||
ansible-playbook -i inventory.ini deploy_aw_pfsense_poller.yml
|
ansible-playbook -i inventory.ini deploy_aw_pfsense_poller.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -116,4 +138,6 @@ Playbook:
|
|||||||
- Для Web UI используется checksum-based cache-bust для `ru-patch-v5.js` и `sw-cleanup.js`, чтобы браузер не держал старую DLP/русскую статику после деплоя.
|
- Для Web UI используется checksum-based cache-bust для `ru-patch-v5.js` и `sw-cleanup.js`, чтобы браузер не держал старую DLP/русскую статику после деплоя.
|
||||||
- На `#/home` Web UI делит хосты на `Windows RDP` и `Virtual servers + Proxmox`.
|
- На `#/home` Web UI делит хосты на `Windows RDP` и `Virtual servers + Proxmox`.
|
||||||
- Выполнена валидация API `http://127.0.0.1:5600/api/0/info`.
|
- Выполнена валидация API `http://127.0.0.1:5600/api/0/info`.
|
||||||
- Для full-stack сценария CT создаётся автоматически через `pct create`.
|
- Для полного сценария CT создаётся автоматически через `pct create`.
|
||||||
|
- На Windows/RDP host развёрнуты AFK/window watchers, browser domain collector, DLP endpoint collector и worktime session collector.
|
||||||
|
- Проверочный JSON-отчёт Windows playbook должен иметь `overallOk=true`.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy pfSense ActivityWatch poller
|
- name: Развернуть pfSense ActivityWatch poller
|
||||||
hosts: aw_pfsense_pollers
|
hosts: aw_pfsense_pollers
|
||||||
become: true
|
become: true
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
@@ -10,14 +10,14 @@
|
|||||||
aw_pfsense_service_name: "aw-pfsense-poller.service"
|
aw_pfsense_service_name: "aw-pfsense-poller.service"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Install required packages
|
- name: Установить обязательные пакеты
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- python3
|
- python3
|
||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
|
|
||||||
- name: Ensure directories exist
|
- name: Создать каталоги
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
@@ -26,29 +26,29 @@
|
|||||||
- "{{ aw_pfsense_install_root }}"
|
- "{{ aw_pfsense_install_root }}"
|
||||||
- "{{ aw_pfsense_config_dir }}"
|
- "{{ aw_pfsense_config_dir }}"
|
||||||
|
|
||||||
- name: Install pfSense poller script
|
- name: Установить скрипт pfSense poller
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py"
|
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py"
|
||||||
dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py"
|
dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
- name: Install systemd service
|
- name: Установить systemd service
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service"
|
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service"
|
||||||
dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}"
|
dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify:
|
notify:
|
||||||
- Reload systemd
|
- Перезагрузить systemd
|
||||||
|
|
||||||
- name: Write pfSense poller config
|
- name: Записать конфигурацию pfSense poller
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ aw_pfsense_config_dir }}/poller.json"
|
dest: "{{ aw_pfsense_config_dir }}/poller.json"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
content: "{{ aw_pfsense_poller_config | to_nice_json }}"
|
content: "{{ aw_pfsense_poller_config | to_nice_json }}"
|
||||||
notify:
|
notify:
|
||||||
- Restart pfSense poller
|
- Перезапустить pfSense poller
|
||||||
|
|
||||||
- name: Enable and start pfSense poller
|
- name: Включить и запустить pfSense poller
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: "{{ aw_pfsense_service_name }}"
|
name: "{{ aw_pfsense_service_name }}"
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -56,11 +56,11 @@
|
|||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: Reload systemd
|
- name: Перезагрузить systemd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Restart pfSense poller
|
- name: Перезапустить pfSense poller
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: "{{ aw_pfsense_service_name }}"
|
name: "{{ aw_pfsense_service_name }}"
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy AWatch-rus server
|
- name: Развернуть сервер AWatch-rus
|
||||||
hosts: aw_server
|
hosts: aw_server
|
||||||
become: true
|
become: true
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
@@ -9,22 +9,29 @@
|
|||||||
aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}"
|
aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}"
|
||||||
aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip"
|
aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip"
|
||||||
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||||||
|
aw_release_install_dir: "{{ aw_release_root }}/aw-server-rust-{{ aw_server_version }}"
|
||||||
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||||
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||||
aw_host_groups_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-host-groups.json') | hash('sha1') | truncate(12, true, '') }}"
|
|
||||||
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
||||||
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Install base packages
|
- name: Установить базовые пакеты
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- curl
|
- curl
|
||||||
|
- rsync
|
||||||
- unzip
|
- unzip
|
||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
|
|
||||||
- name: Ensure service account exists
|
- name: Создать системную группу сервиса
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ aw_server_group }}"
|
||||||
|
system: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Создать системную учётную запись сервиса
|
||||||
ansible.builtin.user:
|
ansible.builtin.user:
|
||||||
name: "{{ aw_server_user }}"
|
name: "{{ aw_server_user }}"
|
||||||
group: "{{ aw_server_group }}"
|
group: "{{ aw_server_group }}"
|
||||||
@@ -33,7 +40,25 @@
|
|||||||
system: true
|
system: true
|
||||||
create_home: false
|
create_home: false
|
||||||
|
|
||||||
- name: Ensure required directories
|
- name: Создать обязательные каталоги
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- "{{ aw_release_root }}"
|
||||||
|
- "{{ aw_release_dir }}"
|
||||||
|
- "{{ aw_release_install_dir }}"
|
||||||
|
- /opt/activitywatch
|
||||||
|
- /opt/activitywatch/bin
|
||||||
|
- "{{ aw_server_webui_dir }}"
|
||||||
|
- "{{ aw_server_webui_dir }}/js"
|
||||||
|
- "{{ aw_server_data_dir }}"
|
||||||
|
- "{{ aw_server_log_dir }}"
|
||||||
|
- /etc/activitywatch
|
||||||
|
- "{{ aw_bootstrap_dir }}"
|
||||||
|
|
||||||
|
- name: Настроить каталоги ActivityWatch с владельцем сервиса
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
@@ -41,103 +66,188 @@
|
|||||||
group: "{{ aw_server_group }}"
|
group: "{{ aw_server_group }}"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
loop:
|
loop:
|
||||||
|
- /opt/activitywatch
|
||||||
|
- /opt/activitywatch/bin
|
||||||
- "{{ aw_release_root }}"
|
- "{{ aw_release_root }}"
|
||||||
- "{{ aw_release_dir }}"
|
- "{{ aw_release_dir }}"
|
||||||
|
- "{{ aw_release_install_dir }}"
|
||||||
- "{{ aw_server_webui_dir }}"
|
- "{{ aw_server_webui_dir }}"
|
||||||
|
- "{{ aw_server_webui_dir }}/js"
|
||||||
- "{{ aw_server_data_dir }}"
|
- "{{ aw_server_data_dir }}"
|
||||||
- "{{ aw_server_log_dir }}"
|
- "{{ aw_server_log_dir }}"
|
||||||
- /etc/activitywatch
|
|
||||||
- "{{ aw_bootstrap_dir }}"
|
|
||||||
|
|
||||||
- name: Download ActivityWatch release archive
|
- name: Скачать архив релиза ActivityWatch
|
||||||
ansible.builtin.get_url:
|
ansible.builtin.get_url:
|
||||||
url: "{{ aw_server_download_url }}"
|
url: "{{ aw_server_download_url }}"
|
||||||
dest: "{{ aw_archive_path }}"
|
dest: "{{ aw_archive_path }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Unpack ActivityWatch release
|
- name: Распаковать релиз ActivityWatch
|
||||||
ansible.builtin.unarchive:
|
ansible.builtin.unarchive:
|
||||||
src: "{{ aw_archive_path }}"
|
src: "{{ aw_archive_path }}"
|
||||||
dest: "{{ aw_release_dir }}"
|
dest: "{{ aw_release_dir }}"
|
||||||
remote_src: true
|
remote_src: true
|
||||||
extra_opts: ["-o"]
|
extra_opts: ["-o"]
|
||||||
|
|
||||||
- name: Discover extracted AW directory
|
- name: Найти распакованный каталог ActivityWatch
|
||||||
ansible.builtin.find:
|
ansible.builtin.find:
|
||||||
paths: "{{ aw_release_dir }}"
|
paths: "{{ aw_release_dir }}"
|
||||||
file_type: directory
|
file_type: directory
|
||||||
patterns: "activitywatch*"
|
patterns: "activitywatch*"
|
||||||
register: aw_release_find
|
register: aw_release_find
|
||||||
|
|
||||||
- name: Set release extracted path
|
- name: Найти бинарный файл AW server
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.find:
|
||||||
aw_release_extracted: "{{ (aw_release_find.files | sort(attribute='path') | map(attribute='path') | list | first) }}"
|
paths: "{{ aw_release_dir }}"
|
||||||
|
file_type: file
|
||||||
|
patterns:
|
||||||
|
- aw-server-rust
|
||||||
|
- aw-server
|
||||||
|
register: aw_server_binary_find
|
||||||
|
|
||||||
- name: Verify extracted directory exists
|
- name: Найти каталог WebUI
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: "{{ aw_release_dir }}"
|
||||||
|
file_type: directory
|
||||||
|
patterns:
|
||||||
|
- aw-webui
|
||||||
|
- webui
|
||||||
|
register: aw_webui_dir_find
|
||||||
|
|
||||||
|
- name: Сохранить пути распакованного релиза
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
aw_release_extracted: "{{ (aw_release_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||||||
|
aw_server_binary_path: "{{ (aw_server_binary_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||||||
|
aw_webui_source_path: "{{ (aw_webui_dir_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||||||
|
|
||||||
|
- name: Проверить, что компоненты релиза найдены
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- aw_release_extracted is defined
|
- aw_release_extracted is defined
|
||||||
- aw_release_extracted | length > 0
|
- aw_release_extracted | length > 0
|
||||||
fail_msg: "Cannot locate extracted ActivityWatch release directory."
|
- aw_server_binary_path is defined
|
||||||
|
- aw_server_binary_path | length > 0
|
||||||
|
- aw_webui_source_path is defined
|
||||||
|
- aw_webui_source_path | length > 0
|
||||||
|
fail_msg: "Не удалось найти бинарный файл или WebUI в распакованном релизе ActivityWatch."
|
||||||
|
|
||||||
- name: Sync release content to /opt/activitywatch
|
- name: Создать каталог установленного релиза
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ aw_release_install_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Установить бинарный файл AW server
|
||||||
|
ansible.builtin.copy:
|
||||||
|
remote_src: true
|
||||||
|
src: "{{ aw_server_binary_path }}"
|
||||||
|
dest: "{{ aw_release_install_dir }}/aw-server-rust"
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Создать ссылку на активный бинарный файл AW server
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "{{ aw_release_install_dir }}/aw-server-rust"
|
||||||
|
dest: /opt/activitywatch/bin/aw-server-rust
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Синхронизировать WebUI в RU каталог
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "rsync -a --delete {{ aw_release_extracted }}/ /opt/activitywatch/"
|
cmd: "rsync -a {{ aw_webui_source_path }}/ {{ aw_server_webui_dir }}/"
|
||||||
|
|
||||||
- name: Copy bootstrap files from repository
|
- name: Настроить владельца файлов /opt/activitywatch
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /opt/activitywatch
|
||||||
|
state: directory
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
- name: Установить systemd service из шаблона репозитория
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/systemd/system/activitywatch-server.service
|
||||||
|
mode: "0644"
|
||||||
|
content: >-
|
||||||
|
{{
|
||||||
|
lookup('file', aw_repo_root + '/aw-server/activitywatch-server.service')
|
||||||
|
| replace('__AW_SERVER_USER__', aw_server_user)
|
||||||
|
| replace('__AW_SERVER_GROUP__', aw_server_group)
|
||||||
|
| replace('__AW_SERVER_DATA_DIR__', aw_server_data_dir)
|
||||||
|
}}
|
||||||
|
notify:
|
||||||
|
- Перезагрузить systemd
|
||||||
|
- Перезапустить activitywatch
|
||||||
|
|
||||||
|
- name: Скопировать RU patch файлы WebUI из репозитория
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ item.src }}"
|
src: "{{ item.src }}"
|
||||||
dest: "{{ item.dest }}"
|
dest: "{{ item.dest }}"
|
||||||
mode: "{{ item.mode }}"
|
mode: "{{ item.mode }}"
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
loop:
|
loop:
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/activitywatch-server.service", dest: "/etc/systemd/system/activitywatch-server.service", mode: "0644" }
|
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" }
|
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" }
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" }
|
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" }
|
||||||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
||||||
notify:
|
|
||||||
- Reload systemd
|
|
||||||
- Restart activitywatch
|
|
||||||
|
|
||||||
- name: Copy WebUI index template from installed distribution
|
- name: Проверить наличие index.html после копирования
|
||||||
ansible.builtin.copy:
|
ansible.builtin.stat:
|
||||||
remote_src: true
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
src: "/opt/activitywatch/aw-webui/index.html"
|
register: aw_webui_ru_index
|
||||||
dest: "{{ aw_server_webui_dir }}/index.html"
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Insert RU patch scripts into index.html
|
- name: Проверить, что index.html доступен для RU patch
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- aw_webui_ru_index.stat.exists
|
||||||
|
fail_msg: "Не найден index.html WebUI для применения RU patch."
|
||||||
|
|
||||||
|
- name: Удалить старые теги RU patch из index.html
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
|
regexp: '<script[^>]+(?:ru-patch-v5\.js|sw-cleanup\.js|aw-ru-patch\.js|aw-sw-cleanup\.js)[^>]*></script>'
|
||||||
|
replace: ''
|
||||||
|
|
||||||
|
- name: Добавить cleanup script RU patch в index.html
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: "{{ aw_server_webui_dir }}/index.html"
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
regexp: '</head>'
|
regexp: '</head>'
|
||||||
replace: '<script src="/js/sw-cleanup.js?v={{ aw_sw_cleanup_cache_bust }}"></script></head>'
|
replace: '<script src="/js/sw-cleanup.js?v={{ aw_sw_cleanup_cache_bust }}"></script></head>'
|
||||||
|
|
||||||
- name: Insert RU patch loader before body end
|
- name: Добавить загрузчик RU patch перед закрытием body
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: "{{ aw_server_webui_dir }}/index.html"
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
regexp: '</body>'
|
regexp: '</body>'
|
||||||
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v={{ aw_ru_patch_cache_bust }}"></script></body>'
|
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v={{ aw_ru_patch_cache_bust }}"></script></body>'
|
||||||
|
|
||||||
- name: Write /etc/activitywatch/aw-server.env
|
- name: Записать /etc/activitywatch/aw-server.env
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /etc/activitywatch/aw-server.env
|
dest: /etc/activitywatch/aw-server.env
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
content: |
|
content: |
|
||||||
AW_SERVER_HOST={{ aw_server_bind_host }}
|
AW_SERVER_BIND_HOST={{ aw_server_bind_host }}
|
||||||
AW_SERVER_PORT={{ aw_server_port }}
|
AW_SERVER_PORT={{ aw_server_port }}
|
||||||
AW_DATA_DIR={{ aw_server_data_dir }}
|
AW_SERVER_DATA_DIR={{ aw_server_data_dir }}
|
||||||
AW_LOG_DIR={{ aw_server_log_dir }}
|
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
||||||
AW_WEBUI_DIR={{ aw_server_webui_dir }}
|
AW_SERVER_WEBUI_DIR={{ aw_server_webui_dir }}
|
||||||
AW_SERVER_USER={{ aw_server_user }}
|
AW_SERVER_USER={{ aw_server_user }}
|
||||||
AW_SERVER_GROUP={{ aw_server_group }}
|
AW_SERVER_GROUP={{ aw_server_group }}
|
||||||
|
|
||||||
- name: Enable and start service
|
- name: Включить и запустить сервис
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: activitywatch-server.service
|
name: activitywatch-server.service
|
||||||
enabled: true
|
enabled: true
|
||||||
state: restarted
|
state: restarted
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Wait for API
|
- name: Дождаться ответа API
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info"
|
||||||
method: GET
|
method: GET
|
||||||
@@ -147,25 +257,25 @@
|
|||||||
delay: 3
|
delay: 3
|
||||||
until: aw_api.status == 200
|
until: aw_api.status == 200
|
||||||
|
|
||||||
- name: Apply baseline worktime settings (classes)
|
- name: Применить базовые worktime settings (classes)
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
||||||
method: POST
|
method: POST
|
||||||
body: "{{ aw_worktime_classes }}"
|
body: "{{ aw_worktime_classes }}"
|
||||||
body_format: json
|
body_format: json
|
||||||
status_code: 201
|
status_code: [200, 201]
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Apply baseline views (include DLP and worktime)
|
- name: Применить базовые views для DLP и worktime
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
||||||
method: POST
|
method: POST
|
||||||
body: "{{ aw_default_views }}"
|
body: "{{ aw_default_views }}"
|
||||||
body_format: json
|
body_format: json
|
||||||
status_code: 201
|
status_code: [200, 201]
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Derive worktime durationDefault from aw_worktime_from/to
|
- name: Вычислить worktime durationDefault из aw_worktime_from/to
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}"
|
aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}"
|
||||||
aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}"
|
aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}"
|
||||||
@@ -182,7 +292,7 @@
|
|||||||
}}
|
}}
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Normalize derived durationDefault for overnight shifts
|
- name: Нормализовать durationDefault для ночных смен
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
aw_worktime_duration_default_effective: >-
|
aw_worktime_duration_default_effective: >-
|
||||||
{{
|
{{
|
||||||
@@ -192,15 +302,15 @@
|
|||||||
}}
|
}}
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Validate derived durationDefault is sane
|
- name: Проверить корректность durationDefault
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- aw_worktime_duration_default_effective | int > 0
|
- aw_worktime_duration_default_effective | int > 0
|
||||||
- aw_worktime_duration_default_effective | int <= 86400
|
- aw_worktime_duration_default_effective | int <= 86400
|
||||||
fail_msg: "Invalid worktime window: {{ aw_worktime_from }}..{{ aw_worktime_to }}"
|
fail_msg: "Некорректный интервал рабочего времени: {{ aw_worktime_from }}..{{ aw_worktime_to }}"
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Apply baseline worktime period (startOfDay)
|
- name: Применить базовый период worktime (startOfDay)
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay"
|
||||||
method: POST
|
method: POST
|
||||||
@@ -209,7 +319,7 @@
|
|||||||
status_code: 200
|
status_code: 200
|
||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
- name: Apply baseline worktime period (durationDefault seconds)
|
- name: Применить базовый период worktime (durationDefault seconds)
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault"
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault"
|
||||||
method: POST
|
method: POST
|
||||||
@@ -219,11 +329,11 @@
|
|||||||
when: aw_apply_worktime_settings | default(false) | bool
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: Reload systemd
|
- name: Перезагрузить systemd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Restart activitywatch
|
- name: Перезапустить activitywatch
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: activitywatch-server.service
|
name: activitywatch-server.service
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy AWatch-rus Windows phase2 collectors
|
- name: Развернуть Windows/RDP phase-2 collector'ы AWatch-rus
|
||||||
hosts: aw_windows
|
hosts: aw_windows
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
aw_windows_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||||
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
||||||
|
aw_windows_server_scheme: "http"
|
||||||
aw_windows_server_host: "10.10.10.13"
|
aw_windows_server_host: "10.10.10.13"
|
||||||
aw_windows_server_port: 5600
|
aw_windows_server_port: 5600
|
||||||
|
aw_windows_package_version: "v0.13.2"
|
||||||
|
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||||
|
aw_windows_package_zip_path: ""
|
||||||
aw_windows_domain: "SHARKON2025"
|
aw_windows_domain: "SHARKON2025"
|
||||||
aw_windows_users:
|
aw_windows_users:
|
||||||
- user1
|
- user1
|
||||||
@@ -31,20 +35,28 @@
|
|||||||
aw_windows_policy_path: "{{ aw_windows_deploy_root }}\\windows\\dlp-policy.example.json"
|
aw_windows_policy_path: "{{ aw_windows_deploy_root }}\\windows\\dlp-policy.example.json"
|
||||||
aw_windows_validation_remote_path: "C:\\Windows\\Temp\\aw_validate_phase2_ansible.json"
|
aw_windows_validation_remote_path: "C:\\Windows\\Temp\\aw_validate_phase2_ansible.json"
|
||||||
aw_windows_validation_local_dir: "/tmp/aw-rus-validation"
|
aw_windows_validation_local_dir: "/tmp/aw-rus-validation"
|
||||||
|
aw_windows_launch_task_pattern: "ActivityWatch Launch *"
|
||||||
|
aw_windows_recovery_task_name: "ActivityWatch Recovery"
|
||||||
|
aw_windows_force_task_restart: true
|
||||||
|
aw_windows_api_smoke_check_enabled: true
|
||||||
|
aw_windows_api_smoke_check_bucket: ""
|
||||||
|
aw_windows_api_smoke_check_limit: 10
|
||||||
|
aw_windows_fail_on_validation_error: true
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Validate required variables
|
- name: Проверить обязательные переменные
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- aw_windows_server_host is defined
|
- aw_windows_server_host is defined
|
||||||
- aw_windows_server_port is defined
|
- aw_windows_server_port is defined
|
||||||
|
- aw_windows_server_scheme is defined
|
||||||
- aw_windows_domain is defined
|
- aw_windows_domain is defined
|
||||||
- aw_windows_users_effective | length > 0
|
- aw_windows_users_effective | length > 0
|
||||||
- aw_windows_install_root is defined
|
- aw_windows_install_root is defined
|
||||||
- aw_windows_state_root is defined
|
- aw_windows_state_root is defined
|
||||||
fail_msg: "Missing required Windows deployment variables."
|
fail_msg: "Не заданы обязательные переменные Windows-развёртывания."
|
||||||
|
|
||||||
- name: Ensure deploy directories exist
|
- name: Создать каталоги развёртывания
|
||||||
ansible.windows.win_file:
|
ansible.windows.win_file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
@@ -52,7 +64,7 @@
|
|||||||
- "{{ aw_windows_deploy_root }}"
|
- "{{ aw_windows_deploy_root }}"
|
||||||
- "{{ aw_windows_deploy_root }}\\windows"
|
- "{{ aw_windows_deploy_root }}\\windows"
|
||||||
|
|
||||||
- name: Upload Windows deployment toolkit
|
- name: Загрузить Windows toolkit развёртывания
|
||||||
ansible.windows.win_copy:
|
ansible.windows.win_copy:
|
||||||
src: "{{ aw_windows_repo_root }}/windows/{{ item }}"
|
src: "{{ aw_windows_repo_root }}/windows/{{ item }}"
|
||||||
dest: "{{ aw_windows_deploy_root }}\\windows\\{{ item }}"
|
dest: "{{ aw_windows_deploy_root }}\\windows\\{{ item }}"
|
||||||
@@ -61,6 +73,7 @@
|
|||||||
- ActivityWatch.Windows.Common.psm1
|
- ActivityWatch.Windows.Common.psm1
|
||||||
- browser-domains-native-collector.ps1
|
- browser-domains-native-collector.ps1
|
||||||
- dlp-endpoint-signals-collector.ps1
|
- dlp-endpoint-signals-collector.ps1
|
||||||
|
- worktime-session-collector.ps1
|
||||||
- deploy-domain-users.ps1
|
- deploy-domain-users.ps1
|
||||||
- deploy-ensemble.ps1
|
- deploy-ensemble.ps1
|
||||||
- hardening-recovery.ps1
|
- hardening-recovery.ps1
|
||||||
@@ -68,24 +81,23 @@
|
|||||||
- web-category-rules.example.json
|
- web-category-rules.example.json
|
||||||
- dlp-policy.example.json
|
- dlp-policy.example.json
|
||||||
|
|
||||||
- name: Upload user list for domain deploy
|
- name: Загрузить список пользователей для доменного развёртывания
|
||||||
ansible.windows.win_copy:
|
ansible.windows.win_copy:
|
||||||
dest: "{{ aw_windows_deploy_root }}\\windows\\users.txt"
|
dest: "{{ aw_windows_deploy_root }}\\windows\\users.txt"
|
||||||
content: |
|
content: |
|
||||||
{% for user in aw_windows_users -%}
|
{% for user in aw_windows_users_effective -%}
|
||||||
{{ user }}
|
|
||||||
{% endfor -%}
|
|
||||||
{% for user in aw_windows_extra_users -%}
|
|
||||||
{{ user }}
|
{{ user }}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
|
|
||||||
- name: Run phase2 ensemble deployment
|
- name: Запустить phase-2 ensemble развёртывание
|
||||||
ansible.windows.win_powershell:
|
ansible.windows.win_powershell:
|
||||||
script: |
|
script: |
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$params = @{
|
$params = @{
|
||||||
|
ServerScheme = "{{ aw_windows_server_scheme }}"
|
||||||
ServerHost = "{{ aw_windows_server_host }}"
|
ServerHost = "{{ aw_windows_server_host }}"
|
||||||
ServerPort = {{ aw_windows_server_port }}
|
ServerPort = {{ aw_windows_server_port }}
|
||||||
|
Version = "{{ aw_windows_package_version }}"
|
||||||
Domain = "{{ aw_windows_domain }}"
|
Domain = "{{ aw_windows_domain }}"
|
||||||
UserListPath = "{{ aw_windows_deploy_root }}\windows\users.txt"
|
UserListPath = "{{ aw_windows_deploy_root }}\windows\users.txt"
|
||||||
InstallRoot = "{{ aw_windows_install_root }}"
|
InstallRoot = "{{ aw_windows_install_root }}"
|
||||||
@@ -100,34 +112,95 @@
|
|||||||
CustomRulesPath = "{{ aw_windows_rules_path }}"
|
CustomRulesPath = "{{ aw_windows_rules_path }}"
|
||||||
CustomPolicyPath = "{{ aw_windows_policy_path }}"
|
CustomPolicyPath = "{{ aw_windows_policy_path }}"
|
||||||
}
|
}
|
||||||
|
{% if (aw_windows_package_url | default('') | string | length) > 0 %}
|
||||||
|
$params.PackageUrl = "{{ aw_windows_package_url }}"
|
||||||
|
{% endif %}
|
||||||
|
{% if (aw_windows_package_zip_path | default('') | string | length) > 0 %}
|
||||||
|
$params.PackageZipPath = "{{ aw_windows_package_zip_path }}"
|
||||||
|
{% endif %}
|
||||||
{% if aw_windows_skip_hardening | bool %}
|
{% if aw_windows_skip_hardening | bool %}
|
||||||
$params.SkipHardening = $true
|
$params.SkipHardening = $true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
& "{{ aw_windows_deploy_root }}\windows\deploy-ensemble.ps1" @params
|
& "{{ aw_windows_deploy_root }}\windows\deploy-ensemble.ps1" @params
|
||||||
|
|
||||||
- name: Run validation and store report on target
|
- name: Принудительно запустить ActivityWatch recovery и launch tasks
|
||||||
|
when: aw_windows_force_task_restart | bool
|
||||||
|
ansible.windows.win_powershell:
|
||||||
|
script: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Start-ScheduledTask -TaskName "{{ aw_windows_recovery_task_name }}"
|
||||||
|
Get-ScheduledTask |
|
||||||
|
Where-Object TaskName -like "{{ aw_windows_launch_task_pattern }}" |
|
||||||
|
ForEach-Object { Start-ScheduledTask -TaskName $_.TaskName }
|
||||||
|
|
||||||
|
- name: Получить Windows hostname для AW smoke-check bucket
|
||||||
|
when:
|
||||||
|
- aw_windows_api_smoke_check_enabled | bool
|
||||||
|
- aw_windows_afk_enabled | bool
|
||||||
|
ansible.windows.win_command: powershell.exe -NoProfile -Command "$env:COMPUTERNAME"
|
||||||
|
register: aw_windows_hostname_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Вычислить AW AFK smoke-check bucket
|
||||||
|
when:
|
||||||
|
- aw_windows_api_smoke_check_enabled | bool
|
||||||
|
- aw_windows_afk_enabled | bool
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
aw_windows_api_smoke_check_bucket_effective: >-
|
||||||
|
{{
|
||||||
|
aw_windows_api_smoke_check_bucket
|
||||||
|
if (aw_windows_api_smoke_check_bucket | default('') | string | length) > 0
|
||||||
|
else 'aw-watcher-afk_' ~ (aw_windows_hostname_result.stdout | trim)
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Дождаться свежих AFK событий на AW server
|
||||||
|
when:
|
||||||
|
- aw_windows_api_smoke_check_enabled | bool
|
||||||
|
- aw_windows_afk_enabled | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ aw_windows_server_scheme }}://{{ aw_windows_server_host }}:{{ aw_windows_server_port }}/api/0/buckets/{{ aw_windows_api_smoke_check_bucket_effective }}/events?limit={{ aw_windows_api_smoke_check_limit }}"
|
||||||
|
method: GET
|
||||||
|
return_content: true
|
||||||
|
register: aw_windows_api_smoke
|
||||||
|
until: >
|
||||||
|
aw_windows_api_smoke.status == 200 and
|
||||||
|
(aw_windows_api_smoke.json | length) > 0 and
|
||||||
|
(
|
||||||
|
aw_windows_api_smoke.json
|
||||||
|
| selectattr('data.status', 'equalto', 'not-afk')
|
||||||
|
| list
|
||||||
|
| length
|
||||||
|
) > 0
|
||||||
|
retries: 10
|
||||||
|
delay: 6
|
||||||
|
|
||||||
|
- name: Выполнить валидацию и сохранить отчёт на целевом Windows host
|
||||||
ansible.windows.win_powershell:
|
ansible.windows.win_powershell:
|
||||||
script: |
|
script: |
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$report = & "{{ aw_windows_deploy_root }}\windows\validate-deployment.ps1" `
|
$report = & "{{ aw_windows_deploy_root }}\windows\validate-deployment.ps1" `
|
||||||
-ConfigPath "{{ aw_windows_state_root }}\deployment-config.json"
|
-ConfigPath "{{ aw_windows_state_root }}\deployment-config.json"
|
||||||
$report | ConvertTo-Json -Depth 12 | Out-File -FilePath "{{ aw_windows_validation_remote_path }}" -Encoding utf8
|
$report | ConvertTo-Json -Depth 12 | Out-File -FilePath "{{ aw_windows_validation_remote_path }}" -Encoding utf8
|
||||||
|
if ({{ '$true' if (aw_windows_fail_on_validation_error | bool) else '$false' }} -and -not [bool]$report.overallOk) {
|
||||||
|
throw "Проверка развёртывания ActivityWatch завершилась ошибкой. Отчёт: {{ aw_windows_validation_remote_path }}"
|
||||||
|
}
|
||||||
|
|
||||||
- name: Ensure local validation directory exists
|
- name: Создать локальный каталог для validation reports
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ aw_windows_validation_local_dir }}"
|
path: "{{ aw_windows_validation_local_dir }}"
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
- name: Fetch validation report
|
- name: Забрать validation report
|
||||||
ansible.builtin.fetch:
|
ansible.builtin.fetch:
|
||||||
src: "{{ aw_windows_validation_remote_path }}"
|
src: "{{ aw_windows_validation_remote_path }}"
|
||||||
dest: "{{ aw_windows_validation_local_dir }}/"
|
dest: "{{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_phase2_ansible.json"
|
||||||
flat: false
|
flat: true
|
||||||
|
|
||||||
- name: Show report location
|
- name: Показать путь к отчёту
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg:
|
msg:
|
||||||
- "Windows phase2 deploy completed on {{ inventory_hostname }}."
|
- "Windows phase2 развёртывание завершено на {{ inventory_hostname }}."
|
||||||
- "Validation report: {{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}/C$/Windows/Temp/aw_validate_phase2_ansible.json"
|
- "Отчёт проверки: {{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_phase2_ansible.json"
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ aw_server_log_dir: "/var/log/activitywatch"
|
|||||||
aw_server_user: "activitywatch"
|
aw_server_user: "activitywatch"
|
||||||
aw_server_group: "activitywatch"
|
aw_server_group: "activitywatch"
|
||||||
|
|
||||||
aw_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_repo_root: "{{ playbook_dir | dirname }}"
|
||||||
|
|
||||||
# Optional: apply a baseline worktime-focused categorization and views via AW settings API.
|
# Опционально: применить базовые категории и views для рабочего времени через AW settings API.
|
||||||
# WARNING: this overwrites existing server-side settings/classes/views.
|
# Внимание: это перезаписывает существующие server-side settings/classes/views.
|
||||||
aw_apply_worktime_settings: false
|
aw_apply_worktime_settings: false
|
||||||
|
|
||||||
# Optional defaults for the worktime period in Web UI.
|
# Опциональные значения периода рабочего времени в Web UI.
|
||||||
# startOfDay controls day-boundary and default report window start.
|
# startOfDay задаёт границу дня и стартовое время окна отчёта.
|
||||||
# durationDefault controls default time range (seconds) shown in UI.
|
# durationDefault задаёт диапазон по умолчанию в секундах.
|
||||||
#
|
#
|
||||||
# Recommended: set worktime window explicitly and let the playbook derive duration.
|
# Рекомендуется явно задать рабочий интервал и дать playbook вычислить duration.
|
||||||
aw_worktime_from: "08:00"
|
aw_worktime_from: "08:00"
|
||||||
aw_worktime_to: "17:00"
|
aw_worktime_to: "17:00"
|
||||||
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
aw_windows_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||||
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
aw_windows_deploy_root: "C:\\Deploy\\AWatch-rus"
|
||||||
|
aw_windows_server_scheme: "http"
|
||||||
aw_windows_server_host: "10.10.10.13"
|
aw_windows_server_host: "10.10.10.13"
|
||||||
aw_windows_server_port: 5600
|
aw_windows_server_port: 5600
|
||||||
|
aw_windows_package_version: "v0.13.2"
|
||||||
|
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||||
|
aw_windows_package_zip_path: ""
|
||||||
aw_windows_domain: "SHARKON2025"
|
aw_windows_domain: "SHARKON2025"
|
||||||
aw_windows_users:
|
aw_windows_users:
|
||||||
- user1
|
- user1
|
||||||
@@ -31,3 +35,10 @@ aw_windows_policy_path: "{{ aw_windows_deploy_root }}\\windows\\dlp-policy.examp
|
|||||||
|
|
||||||
aw_windows_validation_remote_path: "C:\\Windows\\Temp\\aw_validate_phase2_ansible.json"
|
aw_windows_validation_remote_path: "C:\\Windows\\Temp\\aw_validate_phase2_ansible.json"
|
||||||
aw_windows_validation_local_dir: "/tmp/aw-rus-validation"
|
aw_windows_validation_local_dir: "/tmp/aw-rus-validation"
|
||||||
|
aw_windows_fail_on_validation_error: true
|
||||||
|
|
||||||
|
# По умолчанию AFK bucket вычисляется как aw-watcher-afk_<COMPUTERNAME>.
|
||||||
|
# Задайте явное значение только если watcher пишет в нестандартный bucket.
|
||||||
|
aw_windows_api_smoke_check_enabled: true
|
||||||
|
aw_windows_api_smoke_check_bucket: ""
|
||||||
|
aw_windows_api_smoke_check_limit: 10
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# Полный установщик AWatch-rus.
|
||||||
|
# Выполняет развёртывание одной командой:
|
||||||
|
# 1) создание Proxmox CT + bootstrap AW (если в inventory есть [proxmox])
|
||||||
|
# 2) развёртывание AW server на хостах [aw_server]
|
||||||
|
# 3) развёртывание Windows/RDP collector'ов на [aw_windows]
|
||||||
|
# 4) развёртывание pfSense poller'а на [aw_pfsense_pollers]
|
||||||
|
#
|
||||||
|
# Примечания:
|
||||||
|
# - Заполняйте только нужные группы inventory для своего окружения.
|
||||||
|
# - Play без совпадающих host groups Ansible пропускает автоматически.
|
||||||
|
|
||||||
|
- import_playbook: provision_proxmox_ct_and_deploy_aw.yml
|
||||||
|
- import_playbook: deploy_aw_server.yml
|
||||||
|
- import_playbook: deploy_aw_windows_phase2.yml
|
||||||
|
- import_playbook: deploy_aw_pfsense_poller.yml
|
||||||
@@ -5,4 +5,8 @@ pve-main ansible_host=192.168.10.2 ansible_user=root ansible_port=22
|
|||||||
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||||
|
|
||||||
[aw_windows]
|
[aw_windows]
|
||||||
win-node1 ansible_host=192.168.100.21 ansible_user=Administrator ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
# Примечание: в русифицированных Windows часто нужен "Администратор", а не "Administrator".
|
||||||
|
win-node1 ansible_host=192.168.100.21 ansible_user=Администратор ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
||||||
|
|
||||||
|
[aw_pfsense_pollers]
|
||||||
|
# pfsense-poller1 ansible_host=192.168.100.30 ansible_user=root ansible_port=22
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Provision single Proxmox CT and deploy AWatch-rus
|
- name: Создать один Proxmox CT и развернуть AWatch-rus
|
||||||
hosts: proxmox
|
hosts: proxmox
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
- settings/views-default.json
|
- settings/views-default.json
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Execute single-CT provisioning workflow
|
- name: Выполнить workflow создания одного CT
|
||||||
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
||||||
vars:
|
vars:
|
||||||
ct_id: "{{ proxmox_ct_id }}"
|
ct_id: "{{ proxmox_ct_id }}"
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Provision Proxmox CT matrix and deploy AWatch-rus with RU patch
|
- name: Создать матрицу Proxmox CT и развернуть AWatch-rus с RU patch
|
||||||
hosts: proxmox
|
hosts: proxmox
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
- settings/views-default.json
|
- settings/views-default.json
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Validate CT matrix is provided
|
- name: Проверить, что матрица CT задана
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- proxmox_ct_matrix is defined
|
- proxmox_ct_matrix is defined
|
||||||
- proxmox_ct_matrix | length > 0
|
- proxmox_ct_matrix | length > 0
|
||||||
fail_msg: "Define proxmox_ct_matrix in group_vars/proxmox-matrix.yml"
|
fail_msg: "Задайте proxmox_ct_matrix в group_vars/proxmox-matrix.yml"
|
||||||
|
|
||||||
- name: Execute provisioning workflow for each CT
|
- name: Выполнить workflow создания для каждого CT
|
||||||
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
||||||
vars:
|
vars:
|
||||||
ct_id: "{{ item.id }}"
|
ct_id: "{{ item.id }}"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Validate required per-CT variables
|
- name: Проверить обязательные переменные CT
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- ct_id is defined
|
- ct_id is defined
|
||||||
@@ -27,14 +27,14 @@
|
|||||||
- aw_server_log_dir is defined
|
- aw_server_log_dir is defined
|
||||||
- aw_server_user is defined
|
- aw_server_user is defined
|
||||||
- aw_server_group is defined
|
- aw_server_group is defined
|
||||||
fail_msg: "Missing required variables for CT provisioning/deploy."
|
fail_msg: "Не заданы обязательные переменные для создания CT и развёртывания."
|
||||||
|
|
||||||
- name: Build CT network string
|
- name: Сформировать сетевую строку CT
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
ct_net0: >-
|
ct_net0: >-
|
||||||
name=eth0,bridge={{ ct_bridge }},ip={{ ct_ip }},gw={{ ct_gw }}{% if (ct_vlan | default('') | string | length) > 0 %},tag={{ ct_vlan }}{% endif %}
|
name=eth0,bridge={{ ct_bridge }},ip={{ ct_ip }},gw={{ ct_gw }}{% if (ct_vlan | default('') | string | length) > 0 %},tag={{ ct_vlan }}{% endif %}
|
||||||
|
|
||||||
- name: Check whether CT already exists
|
- name: Проверить, существует ли CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Create CT when absent
|
- name: Создать CT, если он отсутствует
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -78,8 +78,9 @@
|
|||||||
- --ostype
|
- --ostype
|
||||||
- debian
|
- debian
|
||||||
when: ct_status_check.rc != 0
|
when: ct_status_check.rc != 0
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Check current CT runtime state
|
- name: Проверить текущее состояние CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
register: ct_runtime_status
|
register: ct_runtime_status
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Start CT when stopped
|
- name: Запустить CT, если он остановлен
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -96,20 +97,23 @@
|
|||||||
- "{{ ct_id }}"
|
- "{{ ct_id }}"
|
||||||
when: "'stopped' in ct_runtime_status.stdout"
|
when: "'stopped' in ct_runtime_status.stdout"
|
||||||
|
|
||||||
- name: Ensure bootstrap directory on Proxmox host
|
- name: Создать bootstrap каталог на Proxmox host
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ proxmox_bootstrap_dir }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
|
loop:
|
||||||
|
- "{{ proxmox_bootstrap_dir }}"
|
||||||
|
- "{{ proxmox_bootstrap_dir }}/settings"
|
||||||
|
|
||||||
- name: Copy AW bootstrap files to Proxmox host temp
|
- name: Скопировать AW bootstrap файлы во временный каталог Proxmox host
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: "{{ aw_repo_root }}/aw-server/{{ item }}"
|
src: "{{ aw_repo_root }}/aw-server/{{ item }}"
|
||||||
dest: "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
dest: "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
loop: "{{ aw_bootstrap_files }}"
|
loop: "{{ aw_bootstrap_files }}"
|
||||||
|
|
||||||
- name: Bootstrap CT OS dependencies
|
- name: Установить базовые зависимости ОС внутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -122,12 +126,16 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server
|
apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server python3
|
||||||
mkdir -p /root/bootstrap /etc/activitywatch
|
mkdir -p /root/bootstrap/settings /etc/activitywatch
|
||||||
systemctl enable ssh || true
|
systemctl enable ssh || true
|
||||||
systemctl restart ssh || true
|
systemctl restart ssh || true
|
||||||
|
register: ct_bootstrap_result
|
||||||
|
retries: 10
|
||||||
|
delay: 6
|
||||||
|
until: ct_bootstrap_result.rc == 0
|
||||||
|
|
||||||
- name: Push bootstrap files into CT
|
- name: Передать bootstrap файлы внутрь CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -137,7 +145,7 @@
|
|||||||
- "/root/bootstrap/{{ item }}"
|
- "/root/bootstrap/{{ item }}"
|
||||||
loop: "{{ aw_bootstrap_files }}"
|
loop: "{{ aw_bootstrap_files }}"
|
||||||
|
|
||||||
- name: Write AW server env file on Proxmox host temp
|
- name: Записать AW server env во временный каталог Proxmox host
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
dest: "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
@@ -151,8 +159,9 @@
|
|||||||
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
||||||
AW_SERVER_USER={{ aw_server_user }}
|
AW_SERVER_USER={{ aw_server_user }}
|
||||||
AW_SERVER_GROUP={{ aw_server_group }}
|
AW_SERVER_GROUP={{ aw_server_group }}
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Push AW server env into CT
|
- name: Передать AW server env внутрь CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -160,8 +169,9 @@
|
|||||||
- "{{ ct_id }}"
|
- "{{ ct_id }}"
|
||||||
- "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
- "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
||||||
- /etc/activitywatch/aw-server.env
|
- /etc/activitywatch/aw-server.env
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Set mode for env inside CT
|
- name: Настроить права env файла внутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -172,7 +182,7 @@
|
|||||||
- "0600"
|
- "0600"
|
||||||
- /etc/activitywatch/aw-server.env
|
- /etc/activitywatch/aw-server.env
|
||||||
|
|
||||||
- name: Install server and apply RU patch inside CT
|
- name: Установить сервер и применить RU patch внутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -188,7 +198,7 @@
|
|||||||
bash /root/bootstrap/apply_webui_ru_patch.sh
|
bash /root/bootstrap/apply_webui_ru_patch.sh
|
||||||
systemctl restart activitywatch-server.service
|
systemctl restart activitywatch-server.service
|
||||||
|
|
||||||
- name: Validate AW API from inside CT
|
- name: Проверить AW API изнутри CT
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -199,7 +209,7 @@
|
|||||||
- -lc
|
- -lc
|
||||||
- "curl -fsS http://127.0.0.1:{{ aw_server_port }}/api/0/info >/dev/null"
|
- "curl -fsS http://127.0.0.1:{{ aw_server_port }}/api/0/info >/dev/null"
|
||||||
|
|
||||||
- name: Validate RU patch hooks in index
|
- name: Проверить hooks RU patch в index.html
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- pct
|
- pct
|
||||||
@@ -210,8 +220,8 @@
|
|||||||
- -lc
|
- -lc
|
||||||
- "grep -q 'ru-patch-v5.js' {{ aw_server_webui_dir }}/index.html && grep -q 'sw-cleanup.js' {{ aw_server_webui_dir }}/index.html"
|
- "grep -q 'ru-patch-v5.js' {{ aw_server_webui_dir }}/index.html && grep -q 'sw-cleanup.js' {{ aw_server_webui_dir }}/index.html"
|
||||||
|
|
||||||
- name: Show final endpoint
|
- name: Показать итоговый endpoint
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg:
|
msg:
|
||||||
- "CT {{ ct_id }} is provisioned and configured."
|
- "CT {{ ct_id }} создан и настроен."
|
||||||
- "ActivityWatch endpoint: http://{{ ct_ip | regex_replace('/[0-9]+$', '') }}:{{ aw_server_port }}"
|
- "Endpoint ActivityWatch: http://{{ ct_ip | regex_replace('/[0-9]+$', '') }}:{{ aw_server_port }}"
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ $policyPath = if ($config.paths.PSObject.Properties.Name -contains 'policyPath')
|
|||||||
$launchScript = [string]$config.paths.launchScript
|
$launchScript = [string]$config.paths.launchScript
|
||||||
$recoveryScript = [string]$config.paths.recoveryScript
|
$recoveryScript = [string]$config.paths.recoveryScript
|
||||||
|
|
||||||
|
$afkExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'afkEnabled') { [bool]$config.collectors.afkEnabled } else { $true }
|
||||||
|
$windowExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]$config.collectors.windowEnabled } else { $true }
|
||||||
$requiredFiles = @(
|
$requiredFiles = @(
|
||||||
(Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe'),
|
|
||||||
(Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe'),
|
|
||||||
$collectorScript,
|
$collectorScript,
|
||||||
$endpointCollectorScript,
|
$endpointCollectorScript,
|
||||||
$sessionCollectorScript,
|
$sessionCollectorScript,
|
||||||
@@ -32,13 +32,24 @@ $requiredFiles = @(
|
|||||||
$recoveryScript,
|
$recoveryScript,
|
||||||
$ConfigPath
|
$ConfigPath
|
||||||
)
|
)
|
||||||
|
if ($afkExpected) {
|
||||||
|
$requiredFiles += (Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe')
|
||||||
|
}
|
||||||
|
if ($windowExpected) {
|
||||||
|
$requiredFiles += (Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe')
|
||||||
|
}
|
||||||
|
|
||||||
$missingFiles = @(
|
$missingFiles = @(
|
||||||
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
|
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
|
||||||
)
|
)
|
||||||
|
|
||||||
$processNames = @('aw-watcher-afk', 'aw-watcher-window')
|
$processNames = @()
|
||||||
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
if ($afkExpected) { $processNames += 'aw-watcher-afk' }
|
||||||
|
if ($windowExpected) { $processNames += 'aw-watcher-window' }
|
||||||
|
$runningProcesses = @()
|
||||||
|
if ($processNames.Count -gt 0) {
|
||||||
|
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
||||||
|
}
|
||||||
$sessionCollectorProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
$sessionCollectorProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
||||||
Where-Object {
|
Where-Object {
|
||||||
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
|
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
|
||||||
@@ -88,10 +99,14 @@ $result = [ordered]@{
|
|||||||
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
|
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
|
||||||
}
|
}
|
||||||
processes = [ordered]@{
|
processes = [ordered]@{
|
||||||
|
expected = $processNames
|
||||||
list = @($runningProcesses)
|
list = @($runningProcesses)
|
||||||
sessionCollectors = @($sessionCollectorProcesses)
|
sessionCollectors = @($sessionCollectorProcesses)
|
||||||
ok = [bool](
|
ok = [bool](
|
||||||
(($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2) -and
|
(
|
||||||
|
($processNames.Count -eq 0) -or
|
||||||
|
(($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge $processNames.Count)
|
||||||
|
) -and
|
||||||
($sessionCollectorProcesses.Count -ge 1)
|
($sessionCollectorProcesses.Count -ge 1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ $policyPath = if ($config.paths.PSObject.Properties.Name -contains 'policyPath')
|
|||||||
$launchScript = [string]$config.paths.launchScript
|
$launchScript = [string]$config.paths.launchScript
|
||||||
$recoveryScript = [string]$config.paths.recoveryScript
|
$recoveryScript = [string]$config.paths.recoveryScript
|
||||||
|
|
||||||
|
$afkExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'afkEnabled') { [bool]$config.collectors.afkEnabled } else { $true }
|
||||||
|
$windowExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]$config.collectors.windowEnabled } else { $true }
|
||||||
$requiredFiles = @(
|
$requiredFiles = @(
|
||||||
(Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe'),
|
|
||||||
(Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe'),
|
|
||||||
$collectorScript,
|
$collectorScript,
|
||||||
$endpointCollectorScript,
|
$endpointCollectorScript,
|
||||||
$sessionCollectorScript,
|
$sessionCollectorScript,
|
||||||
@@ -32,13 +32,24 @@ $requiredFiles = @(
|
|||||||
$recoveryScript,
|
$recoveryScript,
|
||||||
$ConfigPath
|
$ConfigPath
|
||||||
)
|
)
|
||||||
|
if ($afkExpected) {
|
||||||
|
$requiredFiles += (Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe')
|
||||||
|
}
|
||||||
|
if ($windowExpected) {
|
||||||
|
$requiredFiles += (Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe')
|
||||||
|
}
|
||||||
|
|
||||||
$missingFiles = @(
|
$missingFiles = @(
|
||||||
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
|
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
|
||||||
)
|
)
|
||||||
|
|
||||||
$processNames = @('aw-watcher-afk', 'aw-watcher-window')
|
$processNames = @()
|
||||||
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
if ($afkExpected) { $processNames += 'aw-watcher-afk' }
|
||||||
|
if ($windowExpected) { $processNames += 'aw-watcher-window' }
|
||||||
|
$runningProcesses = @()
|
||||||
|
if ($processNames.Count -gt 0) {
|
||||||
|
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
||||||
|
}
|
||||||
$sessionCollectorProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
$sessionCollectorProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
||||||
Where-Object {
|
Where-Object {
|
||||||
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
|
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
|
||||||
@@ -88,10 +99,14 @@ $result = [ordered]@{
|
|||||||
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
|
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
|
||||||
}
|
}
|
||||||
processes = [ordered]@{
|
processes = [ordered]@{
|
||||||
|
expected = $processNames
|
||||||
list = @($runningProcesses)
|
list = @($runningProcesses)
|
||||||
sessionCollectors = @($sessionCollectorProcesses)
|
sessionCollectors = @($sessionCollectorProcesses)
|
||||||
ok = [bool](
|
ok = [bool](
|
||||||
(($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2) -and
|
(
|
||||||
|
($processNames.Count -eq 0) -or
|
||||||
|
(($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge $processNames.Count)
|
||||||
|
) -and
|
||||||
($sessionCollectorProcesses.Count -ge 1)
|
($sessionCollectorProcesses.Count -ge 1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user