TTR Networks

Intermediate · Operating VyOS: monitoring, backups and automation · section 6 of 8

Automation with Ansible

✓ Lab-tested 18 October 2026 on VyOS 2026.10.16-0024-rolling, Debian 12 (rsyslog, nfcapd)

Changing one router by hand is fine. Changing fifty the same way, and proving they're all the same, needs automation. Ansible connects to each router over SSH and uses modules that understand VyOS: the vyos.vyos collection.

  • The inventory lists the routers and how to reach them (network_cli over SSH, as the vyos user).
  • vyos_facts gathers what the router is: hostname, version and more. Useful for audits and for templates.
  • vyos_config applies configuration lines and only reports changed if something actually changed.

The banner playbook shows the workflow:

  1. --check --diff (a dry run) shows exactly what would change, without touching the router.
  2. --diff applies it and shows the diff.
  3. Running it again reports changed=0: the router already matches, so nothing is done. That's idempotence, and it's what makes it safe to run a playbook against every router on a schedule.

Two notes from the lab:

  • The output contains stray [?2004l strings, including inside the gathered hostname. That's the router shell's bracketed-paste mode leaking through the vyos.vyos 4.1.0 collection packaged with Debian 12, which is older than this VyOS build. Use a current collection from Ansible Galaxy, and test it against your VyOS version.
  • The lab logs in with the default vyos password and turns off host key checking. In production use SSH keys, keep secrets in Ansible Vault, and keep host key checking on.
inventory.ini
[routers]
r1 ansible_host=198.51.100.1

[routers:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=vyos.vyos.vyos
ansible_user=vyos
ansible_password=vyos
  • Line 2: One router.
  • Lines 5–6: SSH, with VyOS-aware modules.
  • Lines 7–8: Lab credentials only: use keys and Vault in production.
banner.yml
- name: Standard login banner on every router
  hosts: routers
  gather_facts: false
  tasks:
    - name: Set the banner
      vyos.vyos.vyos_config:
        lines:
          - set system login banner pre-login 'Authorised access only. Managed by Ansible.'
  • Lines 5–7: vyos_config: apply lines, report only real changes.
Facts, a dry run, a run, and a re-run (real output)
root@ops:/ansible# ansible-playbook facts.yml

PLAY [What is this router?] ****************************************************

TASK [Gather facts] ************************************************************
[WARNING]: ansible-pylibssh not installed, falling back to paramiko
ok: [r1]

TASK [Show the interesting ones] ***********************************************
ok: [r1] => {
    "msg": "\u001b[?2004l\n\nR1 runs VyOS 2026.10.16-0024-rolling (serial Hardware)"
}

PLAY RECAP *********************************************************************
r1                         : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

root@ops:/ansible# ansible-playbook banner.yml --check --diff

PLAY [Standard login banner on every router] ***********************************

TASK [Set the banner] **********************************************************
[WARNING]: ansible-pylibssh not installed, falling back to paramiko
[WARNING]: To ensure idempotency and correct diff the input configuration lines
should be similar to how they appear if present in the running configuration on
device
[?2004l

[system login]
+ banner {
+     pre-login "Authorised access only. Managed by Ansible."
+ }


[?2004h[edit]
changed: [r1]

PLAY RECAP *********************************************************************
r1                         : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

root@ops:/ansible# ansible-playbook banner.yml --diff

PLAY [Standard login banner on every router] ***********************************

TASK [Set the banner] **********************************************************
[WARNING]: ansible-pylibssh not installed, falling back to paramiko
[WARNING]: To ensure idempotency and correct diff the input configuration lines
should be similar to how they appear if present in the running configuration on
device
[?2004l

[system login]
+ banner {
+     pre-login "Authorised access only. Managed by Ansible."
+ }


[?2004h[edit]
changed: [r1]

PLAY RECAP *********************************************************************
r1                         : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

root@ops:/ansible# ansible-playbook banner.yml

PLAY [Standard login banner on every router] ***********************************

TASK [Set the banner] **********************************************************
[WARNING]: ansible-pylibssh not installed, falling back to paramiko
ok: [r1]

PLAY RECAP *********************************************************************
r1                         : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • Line 11: Facts gathered (with a stray escape code from the old collection).
  • Line 17: Dry run: what would change…
  • Lines 29–31: …shown as a diff…
  • Line 38: …but nothing changed yet.
  • Lines 40–61: The real run.
  • Line 63: Run again…
  • Line 72: …changed=0: idempotent.

Check your understanding

Why does running the same playbook again report changed=0?

  • vyos_config compares the desired lines with the running config and does nothing if they match
  • The second run was a dry run
  • Ansible caches the result
  • The router rejected the change