# mikrotikbackup_clean.yml --- - name: Backup and/or upgrade MikroTik hosts: mikrotiks gather_facts: false vars: # This directory is located on the Semaphore runner backup_dir: /opt/mikrotik_backups # Maximum time for a router to return after an upgrade upgrade_wait_timeout: 900 tasks: # ------------------------------------------------------------------------- # Common information # ------------------------------------------------------------------------- - name: Get router identity community.routeros.command: commands: - /system identity get name register: identity_raw changed_when: false tags: - always - name: Build safe router name ansible.builtin.set_fact: router_name: >- {{ ( identity_raw.stdout[0] | default(inventory_hostname, true) | trim ) | regex_replace('[^A-Za-z0-9._-]+', '_') | regex_replace('^_+|_+$', '') }} tags: - always - name: Validate router name ansible.builtin.assert: that: - router_name | length > 0 fail_msg: >- Could not determine RouterOS identity for {{ inventory_hostname }}. quiet: true tags: - always - name: Generate backup timestamp ansible.builtin.command: argv: - date - --utc - "+%Y-%m-%d_%H-%M-%S" register: date_out changed_when: false delegate_to: localhost tags: - always - name: Set timestamp fact ansible.builtin.set_fact: backup_timestamp: "{{ date_out.stdout | trim }}" tags: - always # ------------------------------------------------------------------------- # Backup # Run with: --tags backup # ------------------------------------------------------------------------- - name: Ensure local backup directory exists ansible.builtin.file: path: "{{ backup_dir }}" state: directory mode: "0700" delegate_to: localhost run_once: true tags: - backup - never - name: Export RouterOS configuration community.routeros.command: commands: - /export terse show-sensitive register: export_cfg changed_when: false no_log: true tags: - backup - never - name: Validate exported configuration ansible.builtin.assert: that: - export_cfg.stdout is defined - export_cfg.stdout | length > 0 - export_cfg.stdout[0] | default('') | trim | length > 0 fail_msg: >- RouterOS returned an empty configuration export for {{ inventory_hostname }}. quiet: true no_log: true tags: - backup - never - name: Set backup filename ansible.builtin.set_fact: backup_file: >- {{ backup_dir ~ '/' ~ router_name ~ '-' ~ backup_timestamp ~ '.rsc' }} tags: - backup - never - name: Save RouterOS configuration locally ansible.builtin.copy: content: "{{ export_cfg.stdout[0] | trim }}\n" dest: "{{ backup_file }}" mode: "0600" delegate_to: localhost no_log: true diff: false tags: - backup - never - name: Show backup result ansible.builtin.debug: msg: >- MikroTik backup completed: host={{ inventory_hostname }} identity={{ router_name }} file={{ backup_file }} tags: - backup - never # ------------------------------------------------------------------------- # Upgrade # Run with: --tags upgrade # Run with backup before upgrade: --tags backup,upgrade # ------------------------------------------------------------------------- - name: Check current and latest RouterOS versions community.routeros.command: commands: - /system package update check-for-updates register: update_check changed_when: false tags: - upgrade - never - name: Normalize update output ansible.builtin.set_fact: update_text: >- {{ update_check.stdout[0] | default('') | replace('\r', '') }} tags: - upgrade - never - name: Extract version matches ansible.builtin.set_fact: installed_version_matches: >- {{ update_text | regex_findall( '(?im)^\\s*(?:installed|current)-version:\\s*(\\S+)\\s*$' ) }} latest_version_matches: >- {{ update_text | regex_findall( '(?im)^\\s*(?:latest|newest)-version:\\s*(\\S+)\\s*$' ) }} tags: - upgrade - never - name: Set installed and latest versions ansible.builtin.set_fact: installed_version: >- {{ installed_version_matches[0] if installed_version_matches | length > 0 else 'unknown' }} latest_version: >- {{ latest_version_matches[0] if latest_version_matches | length > 0 else 'unknown' }} tags: - upgrade - never - name: Show raw update output ansible.builtin.debug: msg: "{{ update_text }}" tags: - upgrade - never - name: Validate parsed RouterOS versions ansible.builtin.assert: that: - installed_version != 'unknown' - latest_version != 'unknown' fail_msg: >- Could not parse RouterOS versions on {{ inventory_hostname }}. Raw output: {{ update_text }} quiet: true tags: - upgrade - never - name: Show RouterOS versions ansible.builtin.debug: msg: - "Router: {{ router_name }}" - "Installed: {{ installed_version }}" - "Latest: {{ latest_version }}" tags: - upgrade - never - name: Show already up-to-date result ansible.builtin.debug: msg: >- Router {{ router_name }} is already running RouterOS {{ installed_version }}. when: installed_version == latest_version tags: - upgrade - never - name: Trigger RouterOS package installation community.routeros.command: commands: - /system package update install register: upgrade_result changed_when: true failed_when: false when: installed_version != latest_version tags: - upgrade - never - name: Wait for router SSH service to stop ansible.builtin.wait_for: host: "{{ ansible_host | default(inventory_hostname) }}" port: "{{ ansible_port | default(22) | int }}" state: stopped connect_timeout: 5 timeout: 180 delegate_to: localhost failed_when: false when: installed_version != latest_version tags: - upgrade - never - name: Wait for router SSH service to start ansible.builtin.wait_for: host: "{{ ansible_host | default(inventory_hostname) }}" port: "{{ ansible_port | default(22) | int }}" state: started delay: 10 connect_timeout: 5 timeout: "{{ upgrade_wait_timeout }}" delegate_to: localhost when: installed_version != latest_version tags: - upgrade - never - name: Reset RouterOS connection ansible.builtin.meta: reset_connection when: installed_version != latest_version tags: - upgrade - never - name: Read RouterOS version after upgrade community.routeros.command: commands: - /system resource get version register: post_upgrade_version retries: 20 delay: 15 until: post_upgrade_version is succeeded changed_when: false when: installed_version != latest_version tags: - upgrade - never - name: Show RouterOS version after upgrade ansible.builtin.debug: msg: >- Router {{ router_name }} is online. RouterOS version: {{ post_upgrade_version.stdout[0] | default('unknown') | trim }} when: installed_version != latest_version tags: - upgrade - never