mikrotik-wiki.ru
Главная
Загрузка...

Ansible автоматизация MikroTik

RouterOS 7.xАвтоматизация13 мин330 мар. 2026 г.
TelegramVK

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 устройств
Обновление firmwareSSH на каждый, скачать, перезагрузитьansible-playbook upgrade.yml
Единая политика firewallКопировать правила вручнуюШаблон → автоприменение
Аудит конфигурацийSSH на каждый, /exportansible-playbook backup.yml → все конфиги в Git
Откат после ошибкиРучной откат на каждомПредыдущий playbook / backup
Документация"Где-то записано"Playbooks = документация (Infrastructure as Code)

Архитектура

code
Ansible 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Поиск и изменение записей через APIAPI
routeros_api_infoПолучение информации через APIAPI

SSH vs RouterOS API: что выбрать

ПараметрSSH (routeros_command)API (routeros_api)
Настройка MikroTikSSH включён по умолчаниюНужно включить API-сервис
ИдемпотентностьНет (выполняет команды "как есть")Да (через routeros_api_find_and_modify)
Парсинг результатовТекст — нужен ручной парсингJSON — структурированные данные
ПроизводительностьМедленнее (SSH overhead)Быстрее (binary API)
СовместимостьRouterOS 6 и 7RouterOS 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

Запуск:

bash
ansible-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] }}"
bash
ansible-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 для автоматического деплоя

Структура репозитория:

code
mikrotik-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

code
fatal: [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-ключа

code
Permission 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

code
fatal: [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

code
failure: bad command name /interface/wifi (line 1 column 18)

RouterOS 7 изменила некоторые пути:

RouterOS 6RouterOS 7
/interface wireless/interface/wifi
/ip firewall/ip/firewall
/system routerboard/system/routerboard

Используйте переменные для совместимости:

yaml
vars:
  wifi_path: "{{ '/interface/wifi' if ros_version >= 7 else '/interface wireless' }}"

6. Ошибка привилегий

code
failure: 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"
[admin@MikroTik] >
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

Не храните пароли в открытом виде:
Автоматизация / Ansible автоматизация MikroTik