Ansible автоматизация MikroTik
Ansible автоматизация MikroTik — playbooks и примеры
Управление одним MikroTik-роутером через Winbox или CLI вполне комфортно. Но когда устройств становится 10, 50 или 200, ручная настройка каждого превращается в кошмар: одна ошибка множится на все устройства, конфигурации расходятся, а на обновление firmware уходят часы. Ansible решает эту проблему: описываете желаемое состояние в YAML-файлах, запускаете playbook — и все устройства приводятся к единой конфигурации за минуты. В этом руководстве разберём установку Ansible с коллекцией community.routeros, настройку подключения к MikroTik через SSH и RouterOS API, а также пять практических playbooks: получение информации, массовое обновление RouterOS, настройку firewall, создание WireGuard VPN-пиров и резервное копирование всех устройств.
Описание
Зачем Ansible для MikroTik
| Проблема | Без Ansible | С Ansible |
|---|---|---|
| Настройка 20 роутеров | 20 × ручная настройка | Один playbook → 20 устройств |
| Обновление firmware | SSH на каждый, скачать, перезагрузить | ansible-playbook upgrade.yml |
| Единая политика firewall | Копировать правила вручную | Шаблон → автоприменение |
| Аудит конфигураций | SSH на каждый, /export | ansible-playbook backup.yml → все конфиги в Git |
| Откат после ошибки | Ручной откат на каждом | Предыдущий playbook / backup |
| Документация | "Где-то записано" | Playbooks = документация (Infrastructure as Code) |
Архитектура
codeAnsible Control Node (Linux/macOS) │ ├── inventory.yml (список MikroTik-устройств) ├── playbooks/ (YAML-файлы с задачами) └── roles/ (переиспользуемые конфигурации) │ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ MikroTik 1 │ │ MikroTik 2 │ │ MikroTik N │ │ SSH / API │ │ SSH / API │ │ SSH / API │ └────────────┘ └────────────┘ └────────────┘
Ansible работает по push-модели: управляющий узел (control node) подключается к устройствам и выполняет команды. Агент на устройствах не нужен.
Модули community.routeros
Коллекция community.routeros предоставляет следующие модули:
| Модуль | Описание | Протокол |
|---|---|---|
routeros_command | Выполнение CLI-команд | SSH |
routeros_facts | Сбор фактов (interfaces, IP, routes) | SSH |
routeros_api | Управление через RouterOS API (8728) | API |
routeros_api_find_and_modify | Поиск и изменение записей через API | API |
routeros_api_info | Получение информации через API | API |
SSH vs RouterOS API: что выбрать
| Параметр | SSH (routeros_command) | API (routeros_api) |
|---|---|---|
| Настройка MikroTik | SSH включён по умолчанию | Нужно включить API-сервис |
| Идемпотентность | Нет (выполняет команды "как есть") | Да (через routeros_api_find_and_modify) |
| Парсинг результатов | Текст — нужен ручной парсинг | JSON — структурированные данные |
| Производительность | Медленнее (SSH overhead) | Быстрее (binary API) |
| Совместимость | RouterOS 6 и 7 | RouterOS 6.43+ и 7 |
Рекомендация: используйте SSH для простых задач (команды, бэкапы) и API для идемпотентного управления конфигурацией.
Настройка
Шаг 1: установка Ansible и коллекции
На управляющем узле (Linux/macOS):
[admin@MikroTik] ># Установка Ansible (Python 3.9+) pip3 install ansible # Установка коллекции community.routeros ansible-galaxy collection install community.routeros # Проверка установки ansible --version ansible-galaxy collection list | grep routeros
Для подключения через RouterOS API (не SSH) нужна дополнительная библиотека:
[admin@MikroTik] ># Библиотека для RouterOS API pip3 install librouteros # Для шифрованного API (8729) pip3 install pyOpenSSL
Шаг 2: подготовка MikroTik-устройств
На каждом MikroTik-роутере:
[admin@MikroTik] ># Создаём пользователя для Ansible /user/add name=ansible group=full \ password="AnsiblePass!2025" \ address=10.0.0.100/32 \ comment="Ansible automation user" # Для SSH-подключения — убедимся что SSH включён /ip/service/set ssh disabled=no # Для API-подключения — включаем API /ip/service/set api disabled=no /ip/service/set api address=10.0.0.100/32 # SSH-ключ (опционально, но рекомендуется) # Загружаем публичный ключ ansible-пользователя на роутер # через Winbox: Files → Upload → id_rsa.pub # затем: /user/ssh-keys/import user=ansible public-key-file=id_rsa.pub
Шаг 3: Inventory — список устройств
Создаём файл inventory.yml:
yaml# inventory.yml all: vars: # Общие переменные для всех устройств ansible_user: ansible ansible_password: "AnsiblePass!2025" ansible_connection: ansible.netcommon.network_cli ansible_network_os: community.routeros.routeros # Если используете SSH-ключ: # ansible_ssh_private_key_file: ~/.ssh/mikrotik_key children: # Офисные роутеры office_routers: hosts: office-main: ansible_host: 10.0.0.1 office-branch1: ansible_host: 10.0.1.1 office-branch2: ansible_host: 10.0.2.1 # Домашние роутеры home_routers: hosts: home-router: ansible_host: 192.168.88.1 # Все роутеры (для API-подключения) api_routers: hosts: office-main: ansible_host: 10.0.0.1 office-branch1: ansible_host: 10.0.1.1 vars: # Переменные для API-модулей api_user: ansible api_pass: "AnsiblePass!2025" api_port: 8728
Проверяем inventory:
[admin@MikroTik] ># Список всех хостов ansible-inventory -i inventory.yml --list # Пинг всех устройств (через SSH) ansible -i inventory.yml all -m community.routeros.command -a "commands='/system/identity/print'"
Playbook 1: получение информации об устройствах
yaml# playbooks/gather-facts.yml --- - name: Gather MikroTik device information hosts: office_routers gather_facts: false tasks: - name: Get system identity community.routeros.command: commands: - /system/identity/print register: identity_result - name: Get system resource info community.routeros.command: commands: - /system/resource/print register: resource_result - name: Get interface list community.routeros.command: commands: - /interface/print register: interface_result - name: Get RouterOS version community.routeros.command: commands: - /system/package/update/print register: version_result - name: Display results ansible.builtin.debug: msg: | === {{ inventory_hostname }} === Identity: {{ identity_result.stdout_lines[0] }} Resource: {{ resource_result.stdout_lines[0] }} Update: {{ version_result.stdout_lines[0] }} # Сбор через routeros_facts (структурированные данные) - name: Gather RouterOS facts community.routeros.facts: gather_subset: - interfaces - default register: facts - name: Show facts ansible.builtin.debug: var: facts.ansible_facts
Запуск:
bashansible-playbook -i inventory.yml playbooks/gather-facts.yml
Playbook 2: массовое обновление RouterOS
yaml# playbooks/upgrade-routeros.yml --- - name: Upgrade RouterOS on all devices hosts: office_routers gather_facts: false serial: 1 # Обновляем по одному (не все сразу!) vars: target_version: "7.20" upgrade_channel: "stable" tasks: - name: Check current version community.routeros.command: commands: - /system/resource/print register: current_version - name: Display current version ansible.builtin.debug: msg: "{{ inventory_hostname }}: {{ current_version.stdout_lines[0] }}" - name: Set update channel community.routeros.command: commands: - "/system/package/update/set channel={{ upgrade_channel }}" - name: Check for updates community.routeros.command: commands: - /system/package/update/check-for-updates register: update_check - name: Display available update ansible.builtin.debug: msg: "Available: {{ update_check.stdout_lines[0] }}" - name: Create backup before upgrade community.routeros.command: commands: - "/system/backup/save name=pre-upgrade-{{ ansible_date_time.date | default('backup') }}" ignore_errors: true - name: Download and install update community.routeros.command: commands: - /system/package/update/install async: 300 poll: 0 register: upgrade_task - name: Wait for router to reboot (3 minutes) ansible.builtin.wait_for: host: "{{ ansible_host }}" port: 22 delay: 60 timeout: 300 state: started delegate_to: localhost - name: Verify new version community.routeros.command: commands: - /system/resource/print register: new_version - name: Display new version ansible.builtin.debug: msg: "{{ inventory_hostname }} upgraded: {{ new_version.stdout_lines[0] }}"
bashansible-playbook -i inventory.yml playbooks/upgrade-routeros.yml
Playbook 3: настройка Firewall на всех роутерах
yaml# playbooks/configure-firewall.yml --- - name: Configure standard firewall on all routers hosts: office_routers gather_facts: false vars: allowed_winbox_nets: - "10.0.0.0/8" - "192.168.88.0/24" tasks: - name: Reset firewall filter rules community.routeros.command: commands: - /ip/firewall/filter/remove [find where comment~"ansible-managed"] ignore_errors: true - name: Add firewall rules — input chain community.routeros.command: commands: # Established/related - >- /ip/firewall/filter/add chain=input action=accept connection-state=established,related comment="ansible-managed: accept established,related" # Drop invalid - >- /ip/firewall/filter/add chain=input action=drop connection-state=invalid comment="ansible-managed: drop invalid" # Accept ICMP - >- /ip/firewall/filter/add chain=input action=accept protocol=icmp comment="ansible-managed: accept ICMP" # Accept Winbox from trusted networks - >- /ip/firewall/filter/add chain=input action=accept protocol=tcp dst-port=8291 src-address-list=winbox-allowed comment="ansible-managed: Winbox from trusted" # Accept SSH from trusted - >- /ip/firewall/filter/add chain=input action=accept protocol=tcp dst-port=22 src-address-list=winbox-allowed comment="ansible-managed: SSH from trusted" # Accept DNS from LAN - >- /ip/firewall/filter/add chain=input action=accept protocol=udp dst-port=53 in-interface-list=LAN comment="ansible-managed: DNS from LAN" # Drop all other input - >- /ip/firewall/filter/add chain=input action=drop in-interface-list=WAN comment="ansible-managed: drop all other input from WAN" - name: Add firewall rules — forward chain community.routeros.command: commands: - >- /ip/firewall/filter/add chain=forward action=accept connection-state=established,related comment="ansible-managed: forward established,related" - >- /ip/firewall/filter/add chain=forward action=drop connection-state=invalid comment="ansible-managed: forward drop invalid" - >- /ip/firewall/filter/add chain=forward action=accept in-interface-list=LAN out-interface-list=WAN comment="ansible-managed: LAN to WAN" - >- /ip/firewall/filter/add chain=forward action=drop in-interface-list=WAN connection-nat-state=!dstnat comment="ansible-managed: drop from WAN not DSTNATed" - name: Configure allowed networks address-list community.routeros.command: commands: - /ip/firewall/address-list/remove [find where list=winbox-allowed comment~"ansible"] ignore_errors: true - name: Add trusted networks to address-list community.routeros.command: commands: - >- /ip/firewall/address-list/add list=winbox-allowed address={{ item }} comment="ansible-managed" loop: "{{ allowed_winbox_nets }}" - name: Verify firewall rules community.routeros.command: commands: - /ip/firewall/filter/print where comment~"ansible-managed" register: fw_result - name: Display firewall rules count ansible.builtin.debug: msg: "{{ inventory_hostname }}: {{ fw_result.stdout_lines | length }} firewall rules applied"
Playbook 4: создание WireGuard VPN-пиров
yaml# playbooks/wireguard-peers.yml --- - name: Configure WireGuard peers on hub router hosts: office-main gather_facts: false vars: wg_interface: "wireguard1" wg_listen_port: 13231 wg_address: "10.10.10.1/24" peers: - name: "branch1" public_key: "aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3=" allowed_address: "10.10.10.2/32" endpoint: "branch1.example.com:13231" - name: "branch2" public_key: "zY9xW8vU7tS6rQ5pO4nM3lK2jI1hG0fE9dC8bA7=" allowed_address: "10.10.10.3/32" endpoint: "branch2.example.com:13231" - name: "remote-worker" public_key: "qW1eR2tY3uI4oP5aS6dF7gH8jK9lZ0xC1vB2nM3=" allowed_address: "10.10.10.10/32" # Без endpoint — клиент за NAT tasks: - name: Ensure WireGuard interface exists community.routeros.command: commands: - >- :if ([:len [/interface/wireguard/find where name={{ wg_interface }}]] = 0) do={ /interface/wireguard/add name={{ wg_interface }} listen-port={{ wg_listen_port }} comment="ansible-managed WireGuard" } - name: Assign IP address to WireGuard interface community.routeros.command: commands: - >- :if ([:len [/ip/address/find where interface={{ wg_interface }}]] = 0) do={ /ip/address/add address={{ wg_address }} interface={{ wg_interface }} comment="ansible-managed" } - name: Remove old ansible-managed peers community.routeros.command: commands: - /interface/wireguard/peers/remove [find where comment~"ansible-managed"] ignore_errors: true - name: Add WireGuard peers community.routeros.command: commands: - >- /interface/wireguard/peers/add interface={{ wg_interface }} public-key="{{ item.public_key }}" allowed-address={{ item.allowed_address }} {% if item.endpoint is defined %} endpoint-address={{ item.endpoint.split(':')[0] }} endpoint-port={{ item.endpoint.split(':')[1] }} {% endif %} persistent-keepalive=25s comment="ansible-managed: {{ item.name }}" loop: "{{ peers }}" - name: Add firewall rule for WireGuard community.routeros.command: commands: - >- :if ([:len [/ip/firewall/filter/find where comment="ansible-managed: WireGuard"]] = 0) do={ /ip/firewall/filter/add chain=input protocol=udp dst-port={{ wg_listen_port }} action=accept comment="ansible-managed: WireGuard" place-before=0 } - name: Verify peers community.routeros.command: commands: - /interface/wireguard/peers/print register: peers_result - name: Show peers ansible.builtin.debug: msg: "{{ peers_result.stdout_lines }}"
Playbook 5: резервное копирование всех устройств
yaml# playbooks/backup-all.yml --- - name: Backup all MikroTik devices hosts: all gather_facts: false vars: backup_dir: "./backups/{{ ansible_date_time.date | default('latest') }}" backup_name: "{{ inventory_hostname }}" tasks: - name: Create local backup directory ansible.builtin.file: path: "{{ backup_dir }}" state: directory mode: "0755" delegate_to: localhost run_once: true - name: Get system identity community.routeros.command: commands: - /system/identity/print register: identity - name: Create binary backup on router community.routeros.command: commands: - "/system/backup/save name={{ backup_name }} dont-encrypt=yes" - name: Create text export on router community.routeros.command: commands: - "/export file={{ backup_name }}-export" - name: Wait for files to be created ansible.builtin.pause: seconds: 5 - name: Download binary backup ansible.builtin.fetch: src: "/{{ backup_name }}.backup" dest: "{{ backup_dir }}/{{ backup_name }}.backup" flat: true vars: ansible_connection: ansible.netcommon.network_cli - name: Download text export ansible.builtin.fetch: src: "/{{ backup_name }}-export.rsc" dest: "{{ backup_dir }}/{{ backup_name }}-export.rsc" flat: true vars: ansible_connection: ansible.netcommon.network_cli - name: Clean up files on router community.routeros.command: commands: - "/file/remove {{ backup_name }}.backup" - "/file/remove {{ backup_name }}-export.rsc" ignore_errors: true - name: Display backup status ansible.builtin.debug: msg: "{{ inventory_hostname }}: backup saved to {{ backup_dir }}/"
Запуск с сохранением в Git:
[admin@MikroTik] ># Создаём бэкапы ansible-playbook -i inventory.yml playbooks/backup-all.yml # Коммитим в Git (для версионирования) cd backups git add . git commit -m "Backup $(date +%Y-%m-%d)" git push
Использование routeros_api для идемпотентного управления
Модуль routeros_api работает через RouterOS API (порт 8728) и поддерживает идемпотентность:
yaml# playbooks/api-configure.yml --- - name: Configure via RouterOS API (idempotent) hosts: api_routers gather_facts: false # Не используем SSH — подключаемся через API connection: local tasks: - name: Ensure DNS servers are set community.routeros.api_modify: hostname: "{{ ansible_host }}" username: "{{ api_user }}" password: "{{ api_pass }}" path: "ip dns" data: - servers: "8.8.8.8,1.1.1.1" allow-remote-requests: "yes" - name: Get interface information via API community.routeros.api_info: hostname: "{{ ansible_host }}" username: "{{ api_user }}" password: "{{ api_pass }}" path: "interface" register: interfaces - name: Show interfaces ansible.builtin.debug: var: interfaces.result - name: Ensure DHCP pool exists community.routeros.api_find_and_modify: hostname: "{{ ansible_host }}" username: "{{ api_user }}" password: "{{ api_pass }}" path: "ip pool" find: name: "dhcp-pool" values: name: "dhcp-pool" ranges: "192.168.88.100-192.168.88.200" allow_create: true
CI/CD: Git + Ansible для автоматического деплоя
Структура репозитория:
codemikrotik-ansible/ ├── inventory/ │ ├── production.yml │ └── staging.yml ├── playbooks/ │ ├── firewall.yml │ ├── wireguard.yml │ └── backup.yml ├── roles/ │ ├── base-config/ │ │ └── tasks/main.yml │ └── firewall/ │ └── tasks/main.yml ├── group_vars/ │ ├── all.yml │ └── office_routers.yml ├── ansible.cfg └── .gitlab-ci.yml
Пример .gitlab-ci.yml:
yaml# .gitlab-ci.yml stages: - validate - deploy validate: stage: validate script: - ansible-playbook --syntax-check -i inventory/production.yml playbooks/firewall.yml - ansible-lint playbooks/ deploy: stage: deploy script: - ansible-playbook -i inventory/production.yml playbooks/firewall.yml when: manual # Деплой только по кнопке only: - main
Проверка
Проверка подключения к устройствам
[admin@MikroTik] ># Пинг через Ansible (SSH) ansible -i inventory.yml all -m community.routeros.command -a "commands='/system/identity/print'" # Подробный вывод для отладки ansible -i inventory.yml office-main -m community.routeros.command \ -a "commands='/system/resource/print'" -vvv
На роутере — проверяем что подключения проходят:
[admin@MikroTik] ># Активные SSH-сессии /ip/ssh/print # Проверяем лог подключений /log/print where topics~"system" message~"ansible" # Проверяем API-подключения (если используете API-модули) /user/active/print
Проверка результатов playbook
[admin@MikroTik] ># Сухой запуск (check mode) — показывает что будет изменено, но не применяет ansible-playbook -i inventory.yml playbooks/firewall.yml --check # Diff mode — показывает различия ansible-playbook -i inventory.yml playbooks/firewall.yml --diff # Ограничить выполнение одним хостом ansible-playbook -i inventory.yml playbooks/firewall.yml --limit office-main # Verbose-вывод ansible-playbook -i inventory.yml playbooks/firewall.yml -v
Валидация конфигурации после применения
[admin@MikroTik] ># На роутере — проверяем что правила применились /ip/firewall/filter/print where comment~"ansible-managed" # Проверяем WireGuard-пиры /interface/wireguard/peers/print where comment~"ansible-managed" # Проверяем бэкапы /file/print where name~"backup"
Типичные ошибки
1. Ошибка подключения по SSH
codefatal: [router]: UNREACHABLE! => { "msg": "Failed to connect to the host via ssh: ssh: connect to host 10.0.0.1 port 22: Connection timed out" }
Решения:
[admin@MikroTik] ># На роутере — проверяем SSH-сервис /ip/service/print where name=ssh # Должен быть disabled=no # Проверяем что Ansible-сервер в разрешённых адресах /ip/service/print detail where name=ssh # address= должен включать IP Ansible-сервера # Проверяем firewall — не блокирует ли SSH /ip/firewall/filter/print where chain=input protocol=tcp dst-port=22 # Проверяем что пользователь существует /user/print where name=ansible
2. Ошибка формата SSH-ключа
codePermission denied (publickey,keyboard-interactive,password)
MikroTik RouterOS 7 поддерживает RSA и Ed25519 ключи:
[admin@MikroTik] ># Генерируем ключ RSA 4096 ssh-keygen -t rsa -b 4096 -f ~/.ssh/mikrotik_key -C "ansible@control-node" # Или Ed25519 (рекомендуется) ssh-keygen -t ed25519 -f ~/.ssh/mikrotik_key -C "ansible@control-node" # Копируем публичный ключ на роутер (через SCP) scp ~/.ssh/mikrotik_key.pub ansible@10.0.0.1:
[admin@MikroTik] ># На роутере — импортируем ключ /user/ssh-keys/import user=ansible public-key-file=mikrotik_key.pub # Проверяем /user/ssh-keys/print
3. Ошибка таймаута при обновлении firmware
codefatal: [router]: FAILED! => {"msg": "command timeout triggered, timeout value is 30 seconds"}
Увеличиваем таймауты в ansible.cfg:
ini# ansible.cfg [defaults] timeout = 120 [persistent_connection] command_timeout = 120 connect_timeout = 60
Или в playbook:
yaml- name: Upgrade RouterOS community.routeros.command: commands: - /system/package/update/install vars: ansible_command_timeout: 300
4. Отсутствие идемпотентности с routeros_command
Модуль routeros_command просто выполняет команды — он не проверяет текущее состояние. Повторный запуск создаст дубликаты:
yaml# НЕПРАВИЛЬНО — создаст дубликат при повторном запуске - name: Add IP address community.routeros.command: commands: - /ip/address/add address=10.0.0.1/24 interface=ether2 # ПРАВИЛЬНО — проверяем перед созданием - name: Add IP address (idempotent) community.routeros.command: commands: - >- :if ([:len [/ip/address/find where address="10.0.0.1/24" interface=ether2]] = 0) do={ /ip/address/add address=10.0.0.1/24 interface=ether2 comment="ansible" }
Или используйте модуль routeros_api_find_and_modify для идемпотентных операций.
5. Ошибка "bad command name" — различия RouterOS 6 и 7
codefailure: bad command name /interface/wifi (line 1 column 18)
RouterOS 7 изменила некоторые пути:
| RouterOS 6 | RouterOS 7 |
|---|---|
/interface wireless | /interface/wifi |
/ip firewall | /ip/firewall |
/system routerboard | /system/routerboard |
Используйте переменные для совместимости:
yamlvars: wifi_path: "{{ '/interface/wifi' if ros_version >= 7 else '/interface wireless' }}"
6. Ошибка привилегий
codefailure: not enough permissions
[admin@MikroTik] ># Проверяем группу пользователя /user/print where name=ansible # Для полного управления нужна группа full /user/set [find name=ansible] group=full # Или создайте группу с нужными политиками /user/group/add name=ansible-group \ policy=local,ssh,read,write,test,api,winbox,reboot,sensitive /user/set [find name=ansible] group=ansible-group
7. Параллельное выполнение — перегрузка роутера
По умолчанию Ansible выполняет задачи параллельно на всех хостах. Для слабых устройств это может быть проблемой:
yaml# Ограничиваем параллелизм - name: Configure routers hosts: all serial: 5 # Максимум 5 устройств одновременно # или # serial: "30%" # 30% от общего числа # Для обновления firmware — строго по одному # serial: 1
ini# ansible.cfg — глобальный лимит [defaults] forks = 10 # Максимум 10 параллельных подключений
8. Безопасное хранение паролей — Ansible Vault
Не храните пароли в открытом виде:
[admin@MikroTik] ># Шифруем файл с переменными ansible-vault encrypt group_vars/all.yml # Создаём зашифрованный файл ansible-vault create group_vars/secrets.yml # Запуск playbook с расшифровкой ansible-playbook -i inventory.yml playbooks/firewall.yml --ask-vault-pass # Или через файл с паролем ansible-playbook -i inventory.yml playbooks/firewall.yml --vault-password-file .vault_pass
yaml# group_vars/secrets.yml (зашифрованный) ansible_password: "SecretPass!2025" api_pass: "ApiSecretPass!2025"
Ansible Control Node (Linux/macOS)
│
├── inventory.yml (список MikroTik-устройств)
├── playbooks/ (YAML-файлы с задачами)
└── roles/ (переиспользуемые конфигурации)
│
▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ MikroTik 1 │ │ MikroTik 2 │ │ MikroTik N │
│ SSH / API │ │ SSH / API │ │ SSH / API │
└────────────┘ └────────────┘ └────────────┘
# Установка Ansible (Python 3.9+)
pip3 install ansible
# Установка коллекции community.routeros
ansible-galaxy collection install community.routeros
# Проверка установки
ansible --version
ansible-galaxy collection list | grep routeros
# Библиотека для RouterOS API
pip3 install librouteros
# Для шифрованного API (8729)
pip3 install pyOpenSSL
# Создаём пользователя для Ansible
/user/add name=ansible group=full \
password="AnsiblePass!2025" \
address=10.0.0.100/32 \
comment="Ansible automation user"
# Для SSH-подключения — убедимся что SSH включён
/ip/service/set ssh disabled=no
# Для API-подключения — включаем API
/ip/service/set api disabled=no
/ip/service/set api address=10.0.0.100/32
# SSH-ключ (опционально, но рекомендуется)
# Загружаем публичный ключ ansible-пользователя на роутер
# через Winbox: Files → Upload → id_rsa.pub
# затем:
/user/ssh-keys/import user=ansible public-key-file=id_rsa.pub
Проверяем inventory:
#### Playbook 1: получение информации об устройствах
Запуск:
#### Playbook 2: массовое обновление RouterOS
#### Playbook 3: настройка Firewall на всех роутерах
#### Playbook 4: создание WireGuard VPN-пиров
#### Playbook 5: резервное копирование всех устройств
Запуск с сохранением в Git:
#### Использование routeros_api для идемпотентного управления
Модуль `routeros_api` работает через RouterOS API (порт 8728) и поддерживает идемпотентность:
#### CI/CD: Git + Ansible для автоматического деплоя
Структура репозитория:
Пример `.gitlab-ci.yml`:
### Проверка
#### Проверка подключения к устройствам
На роутере — проверяем что подключения проходят:
#### Проверка результатов playbook
#### Валидация конфигурации после применения
### Типичные ошибки
#### 1. Ошибка подключения по SSH
Решения:
#### 2. Ошибка формата SSH-ключа
MikroTik RouterOS 7 поддерживает RSA и Ed25519 ключи:
#### 3. Ошибка таймаута при обновлении firmware
Увеличиваем таймауты в `ansible.cfg`:
Или в playbook:
#### 4. Отсутствие идемпотентности с routeros_command
Модуль `routeros_command` просто выполняет команды — он не проверяет текущее состояние. Повторный запуск создаст дубликаты:
Или используйте модуль `routeros_api_find_and_modify` для идемпотентных операций.
#### 5. Ошибка "bad command name" — различия RouterOS 6 и 7
RouterOS 7 изменила некоторые пути:
| RouterOS 6 | RouterOS 7 |
|-----------|-----------|
| `/interface wireless` | `/interface/wifi` |
| `/ip firewall` | `/ip/firewall` |
| `/system routerboard` | `/system/routerboard` |
Используйте переменные для совместимости:
#### 6. Ошибка привилегий
#### 7. Параллельное выполнение — перегрузка роутера
По умолчанию Ansible выполняет задачи параллельно на всех хостах. Для слабых устройств это может быть проблемой:
#### 8. Безопасное хранение паролей — Ansible Vault
Не храните пароли в открытом виде: