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

Terraform для MikroTik — Infrastructure as Code

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

Terraform — это инструмент декларативного управления инфраструктурой от HashiCorp. Вы описываете желаемое состояние в .tf-файлах, а Terraform вычисляет разницу между текущим и целевым состоянием и применяет только необходимые изменения. Для MikroTik существует community-провайдер terraform-provider-routeros, поддерживающий десятки ресурсов RouterOS: IP-адреса, firewall, DNS, WireGuard, VLAN, маршруты и другие. В этом руководстве настроим Terraform-провайдер, опишем полную конфигурацию роутера в коде, разберём рабочий процесс Plan → Apply → State, импорт существующей конфигурации и лучшие практики для управления несколькими устройствами.

Описание

Зачем Terraform для MikroTik

ПреимуществоОписание
ДекларативностьОписываете "что хотите", а не "как сделать"
Version controlКонфигурация в Git — история изменений, code review, rollback
Plan перед ApplyВидите все изменения до применения (diff)
ИдемпотентностьПовторный apply ничего не сломает
МодулиПереиспользование конфигураций для разных роутеров
StateTerraform знает текущее состояние — может удалять ненужное
CI/CDАвтоматическое применение через GitLab CI / GitHub Actions

Terraform vs Ansible: когда что использовать

ПараметрTerraformAnsible
ПарадигмаДекларативная (описание состояния)Процедурная (описание шагов)
StateХранит state-файлНет state
ИдемпотентностьВстроеннаяЗависит от модуля
Удаление ресурсовАвтоматическое (убрали из кода → удалится)Нужен явный шаг удаления
Plan/Previewterraform plan--check --diff (ограничено)
MikroTik-провайдерcommunity.routeros (активно развивается)community.routeros (стабильный)
Подходит дляУправление конфигурацией (постоянное состояние)Одноразовые задачи, бэкапы, обновления
Кривая обученияСредняя (HCL-язык)Низкая (YAML)

Рекомендация: Terraform для постоянного управления конфигурацией (firewall, IP, routing, VPN). Ansible для операционных задач (бэкапы, обновления, сбор информации).

Провайдер terraform-provider-routeros

Провайдер terraform-provider-routeros (github.com/terraform-routeros/terraform-provider-routeros) — это community-проект, активно поддерживаемый сообществом. Он подключается к MikroTik через REST API (HTTPS) и управляет ресурсами RouterOS.

Поддерживаемые ресурсы (неполный список):

КатегорияРесурсы Terraform
Systemrouteros_system_identity, routeros_system_ntp_client
IProuteros_ip_address, routeros_ip_pool, routeros_ip_dns
Firewallrouteros_ip_firewall_filter, routeros_ip_firewall_nat, routeros_ip_firewall_mangle
Interfacesrouteros_interface_bridge, routeros_interface_vlan, routeros_interface_bonding
WireGuardrouteros_interface_wireguard, routeros_interface_wireguard_peer
Routingrouteros_routing_table, routeros_ip_route
DHCProuteros_ip_dhcp_server, routeros_ip_dhcp_server_network
Queuerouteros_queue_simple
Userrouteros_system_user, routeros_system_user_group

Настройка

Шаг 1: подготовка MikroTik (REST API)

Terraform-провайдер работает через REST API, поэтому на роутере нужно включить HTTPS:

[admin@MikroTik] >
# Включаем HTTPS-сервис
/ip/service/set www-ssl disabled=no

# Отключаем HTTP (небезопасный)
/ip/service/set www disabled=yes

# Создаём самоподписанный сертификат (или используйте Let's Encrypt)
/certificate/add name=terraform-cert \
  common-name=router.local \
  key-size=2048 \
  days-valid=3650 \
  key-usage=tls-server

/certificate/sign terraform-cert

# Назначаем сертификат
/ip/service/set www-ssl certificate=terraform-cert

# Создаём пользователя для Terraform
/user/group/add name=terraform-group \
  policy=api,read,write,test,winbox \
  comment="Terraform automation group"

/user/add name=terraform \
  group=terraform-group \
  password="TerraformPass!2025" \
  address=10.0.0.100/32 \
  comment="Terraform automation user"

# Ограничиваем доступ к HTTPS
/ip/service/set www-ssl address=10.0.0.100/32

# Проверяем
/ip/service/print where name=www-ssl
/user/print where name=terraform

Шаг 2: установка Terraform и провайдера

[admin@MikroTik] >
# Установка Terraform (Linux/macOS)
# Скачайте с https://developer.hashicorp.com/terraform/install

# Проверка
terraform version

# Создаём рабочую директорию
mkdir mikrotik-terraform && cd mikrotik-terraform

Создаём файл versions.tf с описанием провайдера:

hcl
# versions.tf
terraform {
  required_version = ">= 1.5.0"

  required_providers {
    routeros = {
      source  = "terraform-routeros/routeros"
      version = ">= 1.30.0"
    }
  }
}

Создаём provider.tf с настройками подключения:

hcl
# provider.tf
provider "routeros" {
  hosturl  = "https://192.168.88.1"
  username = var.routeros_username
  password = var.routeros_password
  insecure = true  # Для самоподписанного сертификата (false для production)
}

Переменные в variables.tf:

hcl
# variables.tf
variable "routeros_username" {
  description = "RouterOS username"
  type        = string
  default     = "terraform"
}

variable "routeros_password" {
  description = "RouterOS password"
  type        = string
  sensitive   = true
}

variable "lan_subnet" {
  description = "LAN subnet"
  type        = string
  default     = "192.168.88.0/24"
}

variable "lan_gateway" {
  description = "LAN gateway IP"
  type        = string
  default     = "192.168.88.1"
}

Файл с секретами terraform.tfvars (не коммитить в Git!):

hcl
# terraform.tfvars
routeros_username = "terraform"
routeros_password = "TerraformPass!2025"

Инициализация:

[admin@MikroTik] >
# Скачиваем провайдер
terraform init

# Проверяем
terraform providers

Шаг 3: описание ресурсов — полная конфигурация роутера

System identity:

hcl
# system.tf
resource "routeros_system_identity" "router" {
  name = "office-router-01"
}

resource "routeros_system_ntp_client" "ntp" {
  enabled = true
}

IP-адреса:

hcl
# ip.tf
resource "routeros_ip_address" "lan" {
  address   = "${var.lan_gateway}/24"
  interface = "bridge"
  comment   = "LAN - managed by Terraform"
}

resource "routeros_ip_address" "management" {
  address   = "10.0.0.1/24"
  interface = "ether1"
  comment   = "Management - managed by Terraform"
}

DNS:

hcl
# dns.tf
resource "routeros_ip_dns" "dns" {
  servers               = "8.8.8.8,1.1.1.1"
  allow_remote_requests = true
}

DHCP-сервер:

hcl
# dhcp.tf
resource "routeros_ip_pool" "dhcp_pool" {
  name   = "dhcp-pool"
  ranges = ["192.168.88.100-192.168.88.200"]
}

resource "routeros_ip_dhcp_server" "lan_dhcp" {
  name          = "dhcp-lan"
  interface     = "bridge"
  address_pool  = routeros_ip_pool.dhcp_pool.name
  lease_time    = "1d"
  comment       = "Managed by Terraform"
}

resource "routeros_ip_dhcp_server_network" "lan_network" {
  address    = var.lan_subnet
  gateway    = var.lan_gateway
  dns_server = var.lan_gateway
  comment    = "Managed by Terraform"
}

Firewall:

hcl
# firewall.tf

# --- Input chain ---
resource "routeros_ip_firewall_filter" "input_established" {
  chain            = "input"
  action           = "accept"
  connection_state = "established,related"
  comment          = "TF: accept established,related"
}

resource "routeros_ip_firewall_filter" "input_drop_invalid" {
  chain            = "input"
  action           = "drop"
  connection_state = "invalid"
  comment          = "TF: drop invalid"
}

resource "routeros_ip_firewall_filter" "input_icmp" {
  chain    = "input"
  action   = "accept"
  protocol = "icmp"
  comment  = "TF: accept ICMP"
}

resource "routeros_ip_firewall_filter" "input_winbox" {
  chain        = "input"
  action       = "accept"
  protocol     = "tcp"
  dst_port     = "8291"
  src_address  = "192.168.88.0/24"
  comment      = "TF: Winbox from LAN"
}

resource "routeros_ip_firewall_filter" "input_ssh" {
  chain        = "input"
  action       = "accept"
  protocol     = "tcp"
  dst_port     = "22"
  src_address  = "10.0.0.0/24"
  comment      = "TF: SSH from management"
}

resource "routeros_ip_firewall_filter" "input_dns" {
  chain             = "input"
  action            = "accept"
  protocol          = "udp"
  dst_port          = "53"
  in_interface_list = "LAN"
  comment           = "TF: DNS from LAN"
}

resource "routeros_ip_firewall_filter" "input_drop_wan" {
  chain             = "input"
  action            = "drop"
  in_interface_list = "WAN"
  comment           = "TF: drop all from WAN"
}

# --- NAT ---
resource "routeros_ip_firewall_nat" "masquerade" {
  chain              = "srcnat"
  action             = "masquerade"
  out_interface_list = "WAN"
  comment            = "TF: masquerade to WAN"
}

WireGuard VPN:

hcl
# wireguard.tf
resource "routeros_interface_wireguard" "wg0" {
  name        = "wireguard1"
  listen_port = 13231
  comment     = "Managed by Terraform"
}

resource "routeros_ip_address" "wg_address" {
  address   = "10.10.10.1/24"
  interface = routeros_interface_wireguard.wg0.name
  comment   = "WireGuard - managed by Terraform"
}

resource "routeros_interface_wireguard_peer" "peer_branch1" {
  interface            = routeros_interface_wireguard.wg0.name
  public_key           = "aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3="
  allowed_address      = "10.10.10.2/32"
  endpoint_address     = "branch1.example.com"
  endpoint_port        = 13231
  persistent_keepalive = "25s"
  comment              = "TF: Branch 1"
}

resource "routeros_interface_wireguard_peer" "peer_remote" {
  interface            = routeros_interface_wireguard.wg0.name
  public_key           = "qW1eR2tY3uI4oP5aS6dF7gH8jK9lZ0xC1vB2nM3="
  allowed_address      = "10.10.10.10/32"
  persistent_keepalive = "25s"
  comment              = "TF: Remote worker"
}

# Firewall для WireGuard
resource "routeros_ip_firewall_filter" "wg_input" {
  chain    = "input"
  action   = "accept"
  protocol = "udp"
  dst_port = tostring(routeros_interface_wireguard.wg0.listen_port)
  comment  = "TF: allow WireGuard"
}

Шаг 4: рабочий процесс Plan → Apply → State

[admin@MikroTik] >
# 1. Plan — смотрим что будет изменено
terraform plan

# Вывод:
# routeros_system_identity.router: will be created
# routeros_ip_address.lan: will be created
# routeros_ip_firewall_filter.input_established: will be created
# ...
# Plan: 15 to add, 0 to change, 0 to destroy.

# 2. Apply — применяем изменения
terraform apply
# Terraform запросит подтверждение: "Do you want to perform these actions?"
# Введите "yes"

# Для автоматического применения (CI/CD)
terraform apply -auto-approve

# 3. State — просмотр текущего состояния
terraform state list
terraform state show routeros_ip_address.lan

# 4. Повторный plan — ничего не изменится (идемпотентность)
terraform plan
# No changes. Your infrastructure matches the configuration.

Шаг 5: импорт существующей конфигурации

Если роутер уже настроен, можно импортировать существующие ресурсы в Terraform state:

[admin@MikroTik] >
# Импорт IP-адреса по его .id
terraform import routeros_ip_address.lan "*1"

# Импорт identity
terraform import routeros_system_identity.router "."

# Импорт firewall-правила
terraform import routeros_ip_firewall_filter.input_established "*1"

# После импорта — сгенерируйте конфигурацию
terraform plan
# Terraform покажет различия между state и .tf-файлами
# Скорректируйте .tf-файлы чтобы plan показал "No changes"

Начиная с Terraform 1.5 можно использовать import блоки:

hcl
# imports.tf
import {
  to = routeros_ip_address.lan
  id = "*1"
}

import {
  to = routeros_system_identity.router
  id = "."
}
[admin@MikroTik] >
# Генерация конфигурации из импортированных ресурсов
terraform plan -generate-config-out=generated.tf

Модули: переиспользование конфигураций

Создаём модуль для стандартного firewall:

hcl
# modules/firewall/main.tf
variable "lan_networks" {
  type    = list(string)
  default = ["192.168.88.0/24"]
}

variable "management_networks" {
  type    = list(string)
  default = ["10.0.0.0/24"]
}

resource "routeros_ip_firewall_filter" "input_established" {
  chain            = "input"
  action           = "accept"
  connection_state = "established,related"
  comment          = "TF-module: established,related"
}

resource "routeros_ip_firewall_filter" "input_drop_invalid" {
  chain            = "input"
  action           = "drop"
  connection_state = "invalid"
  comment          = "TF-module: drop invalid"
}

resource "routeros_ip_firewall_filter" "input_icmp" {
  chain    = "input"
  action   = "accept"
  protocol = "icmp"
  comment  = "TF-module: accept ICMP"
}

resource "routeros_ip_firewall_filter" "input_winbox" {
  for_each     = toset(var.management_networks)
  chain        = "input"
  action       = "accept"
  protocol     = "tcp"
  dst_port     = "8291"
  src_address  = each.value
  comment      = "TF-module: Winbox from ${each.value}"
}

resource "routeros_ip_firewall_nat" "masquerade" {
  chain              = "srcnat"
  action             = "masquerade"
  out_interface_list = "WAN"
  comment            = "TF-module: masquerade"
}

Использование модуля:

hcl
# main.tf
module "firewall" {
  source = "./modules/firewall"

  lan_networks        = ["192.168.88.0/24", "10.20.0.0/24"]
  management_networks = ["10.0.0.0/24"]
}

Управление несколькими роутерами (Workspaces / Multiple Providers)

Вариант 1: Terraform workspaces:

[admin@MikroTik] >
# Workspace для каждого роутера
terraform workspace new office-main
terraform workspace new office-branch1
terraform workspace new office-branch2

# Переключение
terraform workspace select office-main
terraform apply -var-file="vars/office-main.tfvars"

Вариант 2: multiple providers с alias:

hcl
# providers.tf
provider "routeros" {
  alias    = "office_main"
  hosturl  = "https://10.0.0.1"
  username = var.routeros_username
  password = var.routeros_password
  insecure = true
}

provider "routeros" {
  alias    = "branch1"
  hosturl  = "https://10.0.1.1"
  username = var.routeros_username
  password = var.routeros_password
  insecure = true
}

# Ресурсы для office-main
resource "routeros_system_identity" "office_main" {
  provider = routeros.office_main
  name     = "office-main"
}

# Ресурсы для branch1
resource "routeros_system_identity" "branch1" {
  provider = routeros.branch1
  name     = "office-branch1"
}

Best practices: структура проекта

code
mikrotik-terraform/
├── environments/
│   ├── production/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── terraform.tfvars     # .gitignore!
│   │   └── backend.tf
│   └── staging/
│       ├── main.tf
│       └── ...
├── modules/
│   ├── firewall/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── wireguard/
│   │   └── ...
│   └── dhcp/
│       └── ...
├── .gitignore
└── README.md

.gitignore для Terraform:

[admin@MikroTik] >
# .gitignore
*.tfstate
*.tfstate.backup
.terraform/
terraform.tfvars
*.auto.tfvars
.terraform.lock.hcl

Проверка

Валидация конфигурации

[admin@MikroTik] >
# Проверка синтаксиса
terraform validate

# Форматирование кода
terraform fmt -recursive

# Plan — предпросмотр изменений
terraform plan

# Plan с сохранением в файл (для CI/CD)
terraform plan -out=tfplan
terraform apply tfplan

На роутере после apply:

[admin@MikroTik] >
# Проверяем identity
/system/identity/print

# Проверяем IP-адреса
/ip/address/print where comment~"Terraform"

# Проверяем firewall
/ip/firewall/filter/print where comment~"TF"

# Проверяем NAT
/ip/firewall/nat/print where comment~"TF"

# Проверяем WireGuard
/interface/wireguard/print
/interface/wireguard/peers/print

# Проверяем DHCP
/ip/dhcp-server/print
/ip/pool/print

Просмотр state

[admin@MikroTik] >
# Список всех управляемых ресурсов
terraform state list

# Детали конкретного ресурса
terraform state show routeros_ip_firewall_filter.input_established

# Полный state в JSON
terraform show -json | python3 -m json.tool

Проверка drift (расхождение state и реальности)

[admin@MikroTik] >
# Если кто-то изменил конфигурацию вручную через Winbox/CLI,
# terraform plan покажет drift:
terraform plan

# Если plan показывает изменения, которых вы не делали —
# значит кто-то изменил конфигурацию вручную.
# terraform apply вернёт конфигурацию к описанной в .tf-файлах.

# Если нужно принять ручные изменения — обновите .tf-файлы
# и выполните refresh:
terraform apply -refresh-only

Типичные ошибки

1. Ошибка подключения к REST API

code
Error: failed to connect to RouterOS: Get "https://192.168.88.1/rest/system/resource": dial tcp 192.168.88.1:443: connect: connection refused
[admin@MikroTik] >
# Проверяем что www-ssl включён
/ip/service/print where name=www-ssl

# Проверяем сертификат
/ip/service/print detail where name=www-ssl
# certificate= должен быть заполнен

# Проверяем разрешённые адреса
/ip/service/set www-ssl address=10.0.0.100/32
# IP Terraform-хоста должен быть в списке

2. Ошибка "401 Unauthorized"

code
Error: failed to authenticate: 401 Unauthorized
[admin@MikroTik] >
# Проверяем пользователя
/user/print where name=terraform

# Проверяем что группа имеет policy=api
/user/group/print where name=terraform-group

# Проверяем ограничение по IP
/user/print detail where name=terraform

3. Порядок firewall-правил

Terraform не гарантирует порядок создания ресурсов. Для firewall порядок правил критичен:

hcl
# Используйте depends_on для управления порядком
resource "routeros_ip_firewall_filter" "input_established" {
  chain            = "input"
  action           = "accept"
  connection_state = "established,related"
  comment          = "TF: 01 - established"
}

resource "routeros_ip_firewall_filter" "input_drop_all" {
  chain   = "input"
  action  = "drop"
  comment = "TF: 99 - drop all"

  depends_on = [
    routeros_ip_firewall_filter.input_established,
    routeros_ip_firewall_filter.input_icmp,
    routeros_ip_firewall_filter.input_winbox,
  ]
}

# Альтернатива: используйте place_before в некоторых ресурсах
# (если провайдер поддерживает)

4. Не все ресурсы RouterOS поддерживаются

Провайдер не покрывает 100% RouterOS. Если нужный ресурс отсутствует:

hcl
# Используйте routeros_system_script для выполнения произвольных команд
resource "routeros_system_script" "custom_config" {
  name   = "terraform-custom"
  source = <<-EOT
    /interface/bridge/port/add interface=ether2 bridge=bridge
    /interface/bridge/port/add interface=ether3 bridge=bridge
  EOT
}

# ВНИМАНИЕ: такие ресурсы не идемпотентны
# Terraform не может отслеживать их состояние

Проверьте документацию провайдера на наличие нужного ресурса:

[admin@MikroTik] >
# Список всех поддерживаемых ресурсов
terraform providers schema -json | python3 -m json.tool | grep "routeros_"

5. Конфликт при одновременном управлении Terraform и Winbox

Если администратор изменит конфигурацию через Winbox, следующий terraform apply перезатрёт эти изменения. Решения:

  1. Правило: всё что в Terraform — меняется только через Terraform
  2. Маркировка: все Terraform-ресурсы имеют comment с "TF:" — не трогайте их в Winbox
  3. Locking: используйте remote state с locking (S3 + DynamoDB)
  4. CI/CD: применяйте Terraform только через CI/CD pipeline

6. Потеря state-файла

State-файл (terraform.tfstate) содержит маппинг между .tf-ресурсами и реальными объектами на роутере. Потеря state означает, что Terraform "забудет" обо всех управляемых ресурсах.

Используйте remote state:

hcl
# backend.tf — хранение state в S3
terraform {
  backend "s3" {
    bucket         = "mikrotik-terraform-state"
    key            = "office-main/terraform.tfstate"
    region         = "eu-central-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Или для небольших проектов — коммитьте state в приватный Git-репозиторий (не идеально, но лучше чем потерять).

7. Ошибка при destroy — роутер становится недоступным

[admin@MikroTik] >
# ОПАСНО: terraform destroy удалит ВСЕ управляемые ресурсы
# Включая IP-адреса и firewall — роутер может стать недоступным!

# Защита: используйте lifecycle prevent_destroy
hcl
resource "routeros_ip_address" "management" {
  address   = "10.0.0.1/24"
  interface = "ether1"
  comment   = "Management - DO NOT DELETE"

  lifecycle {
    prevent_destroy = true
  }
}

resource "routeros_ip_service" "ssh" {
  numbers  = "ssh"
  disabled = false

  lifecycle {
    prevent_destroy = true
  }
}

8. Ограничения провайдера

ОграничениеОписаниеWorkaround
Порядок firewall-правилНет встроенной поддержки порядкаdepends_on, нумерация в comment
Не все ресурсы~70% RouterOS покрытоrouteros_system_script для остального
Только REST APIНужен HTTPS на роутереВключите www-ssl
RouterOS 7 onlyREST API недоступен в RouterOS 6Обновитесь до RouterOS 7
Один роутер на провайдерДля N роутеров нужно N провайдеровИспользуйте alias или workspaces
[admin@MikroTik] >
# Включаем HTTPS-сервис
/ip/service/set www-ssl disabled=no

# Отключаем HTTP (небезопасный)
/ip/service/set www disabled=yes

# Создаём самоподписанный сертификат (или используйте Let's Encrypt)
/certificate/add name=terraform-cert \
  common-name=router.local \
  key-size=2048 \
  days-valid=3650 \
  key-usage=tls-server

/certificate/sign terraform-cert

# Назначаем сертификат
/ip/service/set www-ssl certificate=terraform-cert

# Создаём пользователя для Terraform
/user/group/add name=terraform-group \
  policy=api,read,write,test,winbox \
  comment="Terraform automation group"

/user/add name=terraform \
  group=terraform-group \
  password="TerraformPass!2025" \
  address=10.0.0.100/32 \
  comment="Terraform automation user"

# Ограничиваем доступ к HTTPS
/ip/service/set www-ssl address=10.0.0.100/32

# Проверяем
/ip/service/print where name=www-ssl
/user/print where name=terraform
# Установка Terraform (Linux/macOS)
# Скачайте с https://developer.hashicorp.com/terraform/install

# Проверка
terraform version

# Создаём рабочую директорию
mkdir mikrotik-terraform && cd mikrotik-terraform
Создаём `provider.tf` с настройками подключения:
Переменные в `variables.tf`:
Файл с секретами `terraform.tfvars` (не коммитить в Git!):
Инициализация:
#### Шаг 3: описание ресурсов — полная конфигурация роутера

**System identity:**
**IP-адреса:**
**DNS:**
**DHCP-сервер:**
**Firewall:**
**WireGuard VPN:**
#### Шаг 4: рабочий процесс Plan → Apply → State
#### Шаг 5: импорт существующей конфигурации

Если роутер уже настроен, можно импортировать существующие ресурсы в Terraform state:
Начиная с Terraform 1.5 можно использовать `import` блоки:

#### Модули: переиспользование конфигураций

Создаём модуль для стандартного firewall:
Использование модуля:
#### Управление несколькими роутерами (Workspaces / Multiple Providers)

Вариант 1: Terraform workspaces:
Вариант 2: multiple providers с alias:
#### Best practices: структура проекта
`.gitignore` для Terraform:
### Проверка

#### Валидация конфигурации
На роутере после apply:
#### Просмотр state
#### Проверка drift (расхождение state и реальности)
### Типичные ошибки

#### 1. Ошибка подключения к REST API

#### 2. Ошибка "401 Unauthorized"

#### 3. Порядок firewall-правил

Terraform не гарантирует порядок создания ресурсов. Для firewall порядок правил критичен:
#### 4. Не все ресурсы RouterOS поддерживаются

Провайдер не покрывает 100% RouterOS. Если нужный ресурс отсутствует:
Проверьте документацию провайдера на наличие нужного ресурса:
#### 5. Конфликт при одновременном управлении Terraform и Winbox

Если администратор изменит конфигурацию через Winbox, следующий `terraform apply` перезатрёт эти изменения. Решения:

1. **Правило**: всё что в Terraform — меняется только через Terraform
2. **Маркировка**: все Terraform-ресурсы имеют comment с "TF:" — не трогайте их в Winbox
3. **Locking**: используйте remote state с locking (S3 + DynamoDB)
4. **CI/CD**: применяйте Terraform только через CI/CD pipeline

#### 6. Потеря state-файла

State-файл (`terraform.tfstate`) содержит маппинг между .tf-ресурсами и реальными объектами на роутере. Потеря state означает, что Terraform "забудет" обо всех управляемых ресурсах.

Используйте remote state:
Или для небольших проектов — коммитьте state в приватный Git-репозиторий (не идеально, но лучше чем потерять).

#### 7. Ошибка при destroy — роутер становится недоступным
Автоматизация / Terraform для MikroTik — Infrastructure as Code