MakerFLOSS_Mikrotik/roles/makerfloss.mikrotik_switch
sjat e6c1651da1 feat(users): model operators as accounts; add claus
Replace the single `switch_admin_user` day-2 task with a `switch_operators`
list in host_vars: one RouterOS user per person, each with the public keys
that may log in as it. Logins become attributable and revocable per person
instead of accreting as extra keys on `sjat`.

Adds claus@stjerno.dk as user `claus` (group full), and codifies the two
keys already on the device (sjat's mamba seat, the claude@ubongo automation
seat that was hand-imported earlier today).

Two things the mechanism has to get right:
- Idempotency: query /user/ssh-keys first and upload only missing keys.
  An unconditional net_put reports changed on every run.
- No passwordless accounts: WinBox/console accept an empty password and
  WinBox is deliberately left on for recovery, so each operator gets a
  vaulted password. users.yml asserts one exists before touching anything.

Verified: yamllint, ansible-lint (production), syntax-check, and the play
run twice against crs310-maker — second run changed=0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EyYJWABgSEHzbjsZGrkxVE
2026-09-01 20:29:18 +02:00
..
defaults feat(users): model operators as accounts; add claus 2026-09-01 20:29:18 +02:00
meta feat: role skeleton, host_vars, day-2 play (stubbed domains) 2026-06-07 08:34:13 +02:00
tasks feat(users): model operators as accounts; add claus 2026-09-01 20:29:18 +02:00
README.md docs: mark domain tasks implemented; note deferred vlans device run 2026-06-08 19:45:36 +02:00

makerfloss.mikrotik_switch

Configure a MikroTik RouterOS switch (CRS310) over SSH with community.routeros. The role provides the mechanism; real values live in host_vars. Each domain is gated by an enable-flag (defined in group_vars/mikrotik.yml) so you can apply a subset with --tags.

Domains (enable-flags)

Flag Task file Tag Does
switch_identity_enabled identity.yml identity identity, mgmt IP, DNS/NTP, SSH on, disable unused services
switch_users_enabled users.yml users named admin user, import SSH key, disable default admin
switch_vlans_enabled vlans.yml vlans VLAN-aware bridge, access/trunk ports, mgmt VLAN iface
switch_backup_enabled backup.yml backup /export + binary backup, fetched into the repo
switch_firmware_enabled firmware.yml firmware RouterOS + RouterBOOT upgrade to switch_firmware_target (opt-in)

All per-domain task files are implemented. identity, users, backup and firmware are idempotency-verified against the device; vlans is implemented and Jinja-validated but its device run is deferred until the real topology is in host_vars and an on-site recovery channel is available (it enables vlan-filtering last, which can strand management if the mgmt path is wrong).

Variables (defaults/main.yml)

Variable Default Purpose
switch_identity_name {{ inventory_hostname }} system identity
switch_mgmt_vlan_id 99 management VLAN id
switch_mgmt_address placeholder mgmt IP addr/cidr (override in host_vars)
switch_mgmt_gateway placeholder default gateway
switch_dns_servers placeholder DNS server(s)
switch_ntp_servers placeholder NTP server(s)
switch_disabled_services telnet,ftp,www,www-ssl,api,api-ssl services to disable (winbox kept for recovery)
switch_ssh_port 22 SSH service port
switch_admin_user sjat named admin user
switch_admin_group full RouterOS group for the admin user
switch_admin_ssh_pubkey_file ~/.ssh/id_ed25519.pub operator public key to import
switch_disable_default_admin true disable the built-in admin after key login works
switch_bridge_name bridge bridge to manage
switch_vlans example list of {id, name}
switch_bridge_ports example list of port definitions (see below)
switch_firmware_target "" RouterOS version to pin/upgrade to

Data shapes

switch_vlans:
  - {id: 99, name: "mgmt"}
  - {id: 10, name: "members"}

switch_bridge_ports:
  # access port: untagged member of one VLAN (pvid)
  - {interface: "ether1", pvid: 10, mode: access}
  # trunk port: carries tagged VLANs; pvid sets the untagged/native VLAN
  - {interface: "sfp-sfpplus1", pvid: 1, mode: trunk, tagged_vlans: [99, 10]}

Idempotency

RouterOS has no rich declarative module set over network_cli, so tasks use community.routeros.command with :if ([:len [... find ...]] = 0) do={ ... } guards. Always run twice and confirm the second run is a no-op.