Ansible - Adding Arista and Multi-Vendor Playbooks

Ansible - Adding Arista and Multi-Vendor Playbooks
In: Ansible-Course
Table of Contents

This post is part of an ongoing series - see all the posts below.

Ansible Course

So far, every device in our lab has been a Cisco switch. But very few real networks are made up of a single vendor. You might have Cisco in the campus, Arista in the data centre, and a few firewalls from yet another vendor. One of the biggest benefits of Ansible is that you can manage all of them from the same project, using the same inventory, the same playbooks, and the same way of working.

In this part, we'll add Arista switches to our lab. Arista is a good first step into multi-vendor automation, because just like Cisco, Ansible connects to it over SSH using network_cli. So most of what we've learned so far applies straight away, and we can focus on the differences.

Lab Topology

For this part, I've rebuilt the lab from scratch, so all the config we added in the previous chapters, such as NTP, syslog, and interface descriptions, is gone. Every device starts with only the default config created by Netlab.

As always, your lab doesn’t need to match mine exactly. All you need is a mix of Cisco and Arista devices, and it doesn’t matter how many of each you have or how they’re connected. Even one Cisco switch and one Arista switch is enough to follow along with this chapter.

The topology is the same as before, with one change. access2 and access3 are now Arista cEOS switches, while core1, core2, and access1 are still Cisco IOL. Keeping the same topology means the only thing that's different is the vendor, which makes it easy to compare the two side by side.

If you want to follow along, you can get the cEOS image for free by creating an account on the Arista website. I've covered how to download and import it in this blog post.

https://www.packetswitch.co.uk/netlab-the-fastest-way-to-build-network-labs/

The Arista Collection

Just like Cisco has the cisco.ios collection, Arista has the arista.eos collection, which contains all the modules and plugins for managing Arista EOS devices. It comes with the ansible package, so there's nothing extra to install. You can confirm it's there by running the following command.

ansible-galaxy collection list arista.eos

If you look through the documentation for the collection, you'll notice that most of the module names are very similar to the Cisco ones. For example, ios_command becomes eos_command, ios_facts becomes eos_facts, and ios_ntp_global becomes eos_ntp_global.

Updating the Inventory

When we first created inventory groups, we added a cisco group to hold the Cisco connection settings, and I mentioned that you might add another vendor in the future. Now we have exactly that situation.

The interesting part is that access2 and access3 are still access switches, they're just made by a different vendor. So each device now needs to belong to two kinds of groups, one for its vendor and one for its role. A device can be a member of as many groups as you like, so this isn't a problem.

---
all:
  children:
    cisco:
      hosts:
        core1:
          ansible_host: 192.168.202.101
        core2:
          ansible_host: 192.168.202.102
        access1:
          ansible_host: 192.168.202.103
    arista:
      hosts:
        access2:
          ansible_host: 192.168.202.104
        access3:
          ansible_host: 192.168.202.105
    core:
      hosts:
        core1:
        core2:
    access:
      hosts:
        access1:
        access2:
        access3:

The cisco and arista groups list each device along with its IP address. The core and access groups list the same devices again, but without any variables. Since the IP addresses are already defined under the vendor groups, we only need the device names here. Let's check how the inventory looks now.

ansible-inventory --graph
@all:
  |--@ungrouped:
  |--@cisco:
  |  |--core1
  |  |--core2
  |  |--access1
  |--@arista:
  |  |--access2
  |  |--access3
  |--@core:
  |  |--core1
  |  |--core2
  |--@access:
  |  |--access1
  |  |--access2
  |  |--access3

Every device appears under its vendor group and its role group. You'll also see a group called ungrouped. This is another built-in group, which holds any devices that aren't part of any other group. Since all our devices belong to a group, it's empty.

With this setup, we can target devices by vendor, by role, or a mix of both. For example, hosts: arista runs against the two Arista switches, hosts: access runs against all three access switches regardless of the vendor.

Group Variables for Arista

Next, we need to tell Ansible how to connect to the Arista switches. Just like we did for Cisco, create a new directory called group_vars/arista/ with a file called vars.yml.

group_vars/
├── arista/
│   └── vars.yml
└── cisco/
    └── vars.yml
---
ansible_network_os: arista.eos.eos
ansible_connection: ansible.netcommon.network_cli
ansible_user: admin
ansible_password: admin
ansible_become: true
ansible_become_method: enable
ansible_become_password: admin

Most of this will look familiar. ansible_network_os is set to arista.eos.eos, which tells Ansible these are Arista EOS devices, and we're still using network_cli to connect over SSH.

The last three variables are new. When you log in to a network device, you're usually placed in one of two modes. In user EXEC mode, the prompt ends with >, and you can only run a limited set of commands. To configure anything, you first need to type enable to move into privileged EXEC mode, where the prompt ends with #. These three variables tell Ansible to do the same thing after it logs in.

  • ansible_become - Tells Ansible that it needs to elevate its privileges after logging in.
  • ansible_become_method - How to elevate the privileges. For network devices, this is enable.
  • ansible_become_password - The enable password, if one is configured.

Arista's own documentation recommends using become with enable when connecting with network_cli, which is why we've added it here. My lab doesn't have an enable password configured on the Arista switches, so ansible_become_password isn't strictly needed. I've included it anyway, in case your lab does have one set.

You might be wondering why we didn't need these for the Cisco switches. In my lab, the admin user on the Cisco switches logs straight into privileged EXEC mode, so there's no need to run enable. As we mentioned earlier in the book, if your Cisco devices log you into user EXEC mode instead, you'd add the same three variables to group_vars/cisco/vars.yml. Let's check the variables for access2.

ansible-inventory --host access2
{
  "ansible_become": true,
  "ansible_become_method": "enable",
  "ansible_become_password": "admin",
  "ansible_connection": "ansible.netcommon.network_cli",
  "ansible_host": "192.168.202.104",
  "ansible_network_os": "arista.eos.eos",
  "ansible_password": "admin",
  "ansible_user": "admin"
}

access2 gets its IP address from the inventory and everything else from group_vars/arista/vars.yml. None of the Cisco settings apply to it, since it's not part of the cisco group.

Running Commands on Arista

Let's make sure Ansible can connect to the Arista switches by running show version as an ad-hoc command. This time, we use the eos_command module instead of ios_command.

ansible arista -m arista.eos.eos_command -a "commands='show version'"
access2 | SUCCESS => {
  "changed": false,
  "stdout": [
    "..."
  ],
  "stdout_lines": [
    [
      "Arista cEOSLab",
      "Hardware version: ",
      "Serial number: A4A5B2490CA0B061B6F71595E36C769B",
      "Hardware MAC address: 001c.73eb.1913",
      "System MAC address: 001c.73eb.1913",
      "",
      "Software image version: 4.34.2F-43232954.4342F.1 (engineering build)",
      "Architecture: i686",
      ...
    ]
  ]
}
access3 | SUCCESS => {
  ...
}

Both Arista switches respond, and the output looks exactly like what we've seen with Cisco, with stdout and stdout_lines. Only the module name and the device output are different.

Since we changed the inventory, it's also worth making sure the Cisco switches still work.

ansible cisco -m cisco.ios.ios_command -a "commands='show version | incl RELEASE'"
access1 | SUCCESS => {
  "changed": false,
  "stdout": [
    "..."
  ],
  "stdout_lines": [
    [
      "Cisco IOS Software [Dublin], Linux Software (X86_64BI_LINUX_L2-ADVENTERPRISEK9-M), Version 17.12.1, RELEASE SOFTWARE (fc5)"
    ]
  ]
}
core2 | SUCCESS => {
  ...
}
core1 | SUCCESS => {
  ...
}

Gathering Facts on Arista

Gathering facts works the same way too, using the eos_facts module.

ansible access2 -m arista.eos.eos_facts
access2 | SUCCESS => {
  "ansible_facts": {
    "ansible_net_api": "cliconf",
    "ansible_net_fqdn": "access2",
    "ansible_net_gather_network_resources": [],
    "ansible_net_gather_subset": [
      "default"
    ],
    "ansible_net_hostname": "access2",
    "ansible_net_model": "cEOSLab",
    "ansible_net_python_version": "3.12.10",
    "ansible_net_serialnum": "A4A5B2490CA0B061B6F71595E36C769B",
    "ansible_net_system": "eos",
    "ansible_net_version": "4.34.2F-43232954.4342F.1 (engineering build)",
    "ansible_network_resources": {}
  },
  "changed": false
}

Many of the facts have the same names as the Cisco ones, such as ansible_net_hostname, ansible_net_version, and ansible_net_serialnum. There are also a few differences. Arista gives us the model, cEOSLab, and the FQDN, but not the image name. This is a good reminder to always check the documentation for each vendor's facts module, rather than assuming the facts will be identical.

Configuring NTP and Syslog on Arista

Now let's configure NTP and syslog on the Arista switches, just like we did for Cisco in the previous chapter. Arista has its own resource modules for both, eos_ntp_global and eos_logging_global. Create a new playbook called arista_ntp_syslog.yml.

---
- name: Configure NTP and syslog on Arista
  hosts: arista
  gather_facts: false

  tasks:
    - name: Configure NTP servers
      arista.eos.eos_ntp_global:
        config:
          servers:
            - server: 10.10.10.13
            - server: 10.10.10.14
      notify: Save config

    - name: Configure syslog servers
      arista.eos.eos_logging_global:
        config:
          hosts:
            - name: 10.10.10.50
            - name: 10.10.10.51
      notify: Save config

  handlers:
    - name: Save config
      arista.eos.eos_config:
        save_when: always

If you compare this with the Cisco playbook from the previous chapter, it's almost identical. The module names start with arista.eos.eos_ instead of cisco.ios.ios_, and the handler uses eos_config to save the config.

But if you look closely, there's one important difference. For the syslog servers, the Cisco module uses host as the key, while the Arista module uses name. The NTP servers, on the other hand, use server for both vendors. I only found this by checking the documentation for each module with ansible-doc. Resource modules from different vendors often look very similar, but the data they expect isn't always identical, so it's always worth checking.

Let's run the playbook.

ansible-playbook arista_ntp_syslog.yml
PLAY [Configure NTP and syslog on Arista] *************

TASK [Configure NTP servers] **************************
changed: [access3]
changed: [access2]

TASK [Configure syslog servers] ***********************
changed: [access2]
changed: [access3]

RUNNING HANDLER [Save config] *************************
changed: [access3]
changed: [access2]

PLAY RECAP ********************************************
access2  : ok=3  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
access3  : ok=3  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

Both tasks made changes on both switches, so the handler saved the config. Let's check the config on access2.

access2#show running-config | section ntp
ntp server 10.10.10.13
ntp server 10.10.10.14

access2#show running-config | section logging
logging monitor debugging
logging host 10.10.10.50
logging host 10.10.10.51

The NTP and syslog servers have been added. If we run the playbook again, everything shows ok, and the handler doesn't run, just like it did with Cisco.

PLAY [Configure NTP and syslog on Arista] *************

TASK [Configure NTP servers] **************************
ok: [access3]
ok: [access2]

TASK [Configure syslog servers] ***********************
ok: [access3]
ok: [access2]

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

Testing ansible_become

To see become in action, I created a new user on access2 with privilege level 0, which means it logs in to user EXEC mode, and I also set an enable password.

access2(config)#username admin1 privilege 0 secret admin1
access2(config)#enable password admin1

Next, I updated group_vars/arista/vars.yml to use the new user, and removed the three become variables.

---
ansible_network_os: arista.eos.eos
ansible_connection: ansible.netcommon.network_cli
ansible_user: admin1
ansible_password: admin1

Let's run the playbook against access2.

ansible-playbook arista_ntp_syslog.yml --limit access2
PLAY [Configure NTP and syslog on Arista] *************

TASK [Configure NTP servers] **************************
[ERROR]: Task failed: Action failed: b'show running-config
| section ntp\r\n% Invalid input (privileged mode
required)\r\naccess2>'

fatal: [access2]: FAILED! => {"changed": false, "msg": "..."}

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

The playbook fails straight away. Before making any changes, the module runs show running-config | section ntp to check the current config. But the prompt in the error is access2>, which means we're in user EXEC mode, and Arista refuses to run the command because it needs privileged mode.

Now, let's add the three become variables back, with the enable password we just configured.

---
ansible_network_os: arista.eos.eos
ansible_connection: ansible.netcommon.network_cli
ansible_user: admin1
ansible_password: admin1
ansible_become: true
ansible_become_method: enable
ansible_become_password: admin1
ansible-playbook arista_ntp_syslog.yml --limit access2
PLAY [Configure NTP and syslog on Arista] *************

TASK [Configure NTP servers] **************************
ok: [access2]

TASK [Configure syslog servers] ***********************
ok: [access2]

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

This time, the playbook works. After logging in as admin1, Ansible ran enable, provided the enable password, and moved into privileged EXEC mode, just like you would manually. Both tasks show ok, since the NTP and syslog servers were already configured from the previous section.

Before moving on, I changed group_vars/arista/vars.yml back to the admin user.

Multi-Vendor Playbooks

So far, we've configured Cisco and Arista using separate playbooks. Now, let's look at how to manage both vendors from a single playbook.

Before we start, I rebuilt the lab once again, so all the NTP and syslog config we added earlier in this chapter is gone. Every switch is back to the default config created by Netlab, so we can see exactly what the playbook changes.

Organising the Variables

Let's start with the variables, since this is where most of the multi-vendor work happens.

Both ios_ntp_global and eos_ntp_global expect the NTP servers in exactly the same format, a list of dictionaries with a server key. Since the data is the same for both vendors, there's no need to define it twice. We can put it in group_vars/all.yml, so every device in the inventory gets it.

---
ntp_servers:
  - server: 10.10.10.13
  - server: 10.10.10.14

Syslog is slightly different. As we saw earlier, the Cisco module uses host as the key, while the Arista module uses name. So we can't put a single version in all.yml that works for both. Instead, we define a variable called syslog_servers in each vendor's group_vars file, using the same name, but with the data shaped for that vendor.

group_vars/cisco/vars.yml

---
ansible_network_os: cisco.ios.ios
ansible_connection: ansible.netcommon.network_cli
ansible_user: admin
ansible_password: admin

syslog_servers:
  - host: 10.10.10.50
  - host: 10.10.10.51

group_vars/arista/vars.yml

---
ansible_network_os: arista.eos.eos
ansible_connection: ansible.netcommon.network_cli
ansible_user: admin
ansible_password: admin
ansible_become: true
ansible_become_method: enable
ansible_become_password: admin

syslog_servers:
  - name: 10.10.10.50
  - name: 10.10.10.51

If you've been following along from the previous chapter, make sure you remove the old ntp_servers and ping_targets variables from group_vars/cisco/vars.yml. The cisco group has a higher priority than all, so the old ntp_servers would override the new one in all.yml.

Here's what the group_vars directory looks like now.

group_vars/
├── all.yml
├── arista/
│   └── vars.yml
└── cisco/
    └── vars.yml

Let's check the variables for a Cisco switch and an Arista switch.

ansible-inventory --host core1
{
  "ansible_connection": "ansible.netcommon.network_cli",
  "ansible_host": "192.168.202.101",
  "ansible_network_os": "cisco.ios.ios",
  "ansible_password": "admin",
  "ansible_user": "admin",
  "ntp_servers": [
    {
      "server": "10.10.10.13"
    },
    {
      "server": "10.10.10.14"
    }
  ],
  "syslog_servers": [
    {
      "host": "10.10.10.50"
    },
    {
      "host": "10.10.10.51"
    }
  ]
}
ansible-inventory --host access2
{
  "ansible_become": true,
  "ansible_become_method": "enable",
  "ansible_become_password": "admin",
  "ansible_connection": "ansible.netcommon.network_cli",
  "ansible_host": "192.168.202.104",
  "ansible_network_os": "arista.eos.eos",
  "ansible_password": "admin",
  "ansible_user": "admin",
  "ntp_servers": [
    {
      "server": "10.10.10.13"
    },
    {
      "server": "10.10.10.14"
    }
  ],
  "syslog_servers": [
    {
      "name": "10.10.10.50"
    },
    {
      "name": "10.10.10.51"
    }
  ]
}

Both switches get the same ntp_servers from all.yml. They also both get a variable called syslog_servers, but core1 gets the Cisco version with host, while access2 gets the Arista version with name. This means our playbook can simply use {{ syslog_servers }}, and each switch will get the data in the format its module expects.

One Play per Vendor

The simplest way to manage multiple vendors in a single playbook is to create one play for each vendor. If you remember from the YAML chapter, a playbook is a list of plays, and so far, all our playbooks have only had one play. There's nothing stopping us from adding more.

Create a new playbook called multi_vendor.yml.

---
- name: Configure NTP and syslog on Cisco
  hosts: cisco
  gather_facts: false

  tasks:
    - name: Configure NTP servers
      cisco.ios.ios_ntp_global:
        config:
          servers: "{{ ntp_servers }}"

    - name: Configure syslog servers
      cisco.ios.ios_logging_global:
        config:
          hosts: "{{ syslog_servers }}"

- name: Configure NTP and syslog on Arista
  hosts: arista
  gather_facts: false

  tasks:
    - name: Configure NTP servers
      arista.eos.eos_ntp_global:
        config:
          servers: "{{ ntp_servers }}"

    - name: Configure syslog servers
      arista.eos.eos_logging_global:
        config:
          hosts: "{{ syslog_servers }}"

The first play targets the cisco group and uses the Cisco modules, while the second play targets the arista group and uses the Arista modules. Notice that each play starts with a dash, at the same level, since they're both items in the playbook's list of plays.

Both plays use exactly the same variables, {{ ntp_servers }} and {{ syslog_servers }}. Thanks to the way we organised our group_vars, each switch gets the data in the right format for its vendor, so the playbook doesn't need to worry about it. Let's run the playbook.

ansible-playbook multi_vendor.yml
PLAY [Configure NTP and syslog on Cisco] **************

TASK [Configure NTP servers] **************************
changed: [core2]
changed: [access1]
changed: [core1]

TASK [Configure syslog servers] ***********************
changed: [core2]
changed: [access1]
changed: [core1]

PLAY [Configure NTP and syslog on Arista] *************

TASK [Configure NTP servers] **************************
changed: [access3]
changed: [access2]

TASK [Configure syslog servers] ***********************
changed: [access2]
changed: [access3]

PLAY RECAP ********************************************
access1  : ok=2  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
access2  : ok=2  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
access3  : ok=2  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
core1    : ok=2  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
core2    : ok=2  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

You can see the two plays running one after the other. First, the Cisco play configures NTP and syslog on the three Cisco switches, and then the Arista play does the same on the two Arista switches. Since we rebuilt the lab, everything shows changed. At the end, the PLAY RECAP combines the results from both plays into a single summary.

If we run the playbook again, all five switches show ok, since NTP and syslog are already configured.

ansible-playbook multi_vendor.yml
PLAY [Configure NTP and syslog on Cisco] **************

TASK [Configure NTP servers] **************************
ok: [core2]
ok: [core1]
ok: [access1]

TASK [Configure syslog servers] ***********************
ok: [core2]
ok: [access1]
ok: [core1]

PLAY [Configure NTP and syslog on Arista] *************

TASK [Configure NTP servers] **************************
ok: [access3]
ok: [access2]

TASK [Configure syslog servers] ***********************
ok: [access3]
ok: [access2]

PLAY RECAP ********************************************
access1  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
access2  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
access3  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
core1    : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
core2    : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

With a single command, we configured NTP and syslog across two vendors. If you add another Cisco or Arista switch to the inventory, it's picked up automatically by the right play, without any changes to the playbook.

This works, but it does mean the same syslog IP addresses are stored in two places. If a syslog server ever changes, you’d need to remember to update both files. There’s a better way to handle this, where we store the IP addresses only once and convert them into the right format for each vendor. It uses filters, which we’ll cover in the next part of this series.

A Module That Works on Every Vendor

So far, each vendor has needed its own modules, such as ios_command for Cisco and eos_command for Arista. But for simple tasks, like running a show command, there's a module that works across vendors, called cli_command.

cli_command comes from the ansible.netcommon collection, which we've been using all along for the network_cli connection. It isn't tied to any vendor. Instead, it uses ansible_network_os to work out how to talk to each device. Let's run show clock on every switch in the lab using an ad-hoc command.

ansible all -m ansible.netcommon.cli_command -a "command='show clock'"
access3 | SUCCESS => {
  "changed": false,
  "stdout": "Sun Oct 18 12:04:15 2026\nTimezone: UTC\nClock source: local",
  "stdout_lines": [
    "Sun Oct 18 12:04:15 2026",
    "Timezone: UTC",
    "Clock source: local"
  ]
}
access2 | SUCCESS => {
  ...
}
access1 | SUCCESS => {
  "changed": false,
  "stdout": "*12:04:15.524 UTC Sun Oct 18 2026",
  "stdout_lines": [
    "*12:04:15.524 UTC Sun Oct 18 2026"
  ]
}
core2 | SUCCESS => {
  ...
}
core1 | SUCCESS => {
  ...
}

The same module ran on both vendors, and each switch returned show clock in its own format. The Arista switches show the date, the timezone, and the clock source on separate lines, while the Cisco switches show it all on a single line.

You might also notice that the output looks slightly different from ios_command. cli_command only takes a single command, using command rather than commands. So stdout is a single string rather than a list, and stdout_lines is a single list of lines, rather than a list of lists.

cli_command is handy for quick checks across a mixed network, but keep in mind that it only sends the command and returns the raw output. The vendor modules, especially resource modules, do a lot more for you, such as checking the current config and only making changes when needed. So for anything beyond simple show commands, I'd stick with the vendor-specific modules.

Targeting Devices in More Than One Group

Since every device now belongs to both a vendor group and a role group, we can combine them using host patterns. Earlier in the book, we used : to combine groups and ! to exclude devices. There's also &, which means "and". It only matches devices that are in both groups.

For example, let's see which devices multi_vendor.yml would run against if we limited it to devices that are both access switches and Arista switches.

ansible-playbook multi_vendor.yml --list-hosts --limit "access:&arista"
playbook: multi_vendor.yml

  play #1 (cisco): Configure NTP and syslog on Cisco  TAGS: []
    pattern: ['cisco']
    hosts (0):

  play #2 (arista): Configure NTP and syslog on Arista  TAGS: []
    pattern: ['arista']
    hosts (2):
      access2
      access3

Only access2 and access3 match, since they're the only devices in both groups. access1 is an access switch, but it's not an Arista switch, so it's left out.

This output also shows how --limit works with multiple plays. The limit is applied to each play separately. The Cisco play ends up with no devices at all, so it would simply be skipped, while the Arista play runs against the two switches that match.

One Play with Conditions

Another way to handle multiple vendors is to use a single play that runs against every device, and use when to decide which task runs on which device. We covered conditionals in the previous part, and this is a really common use for them.

Create a new playbook called multi_vendor_when.yml. To keep things short, we'll only configure NTP this time.

---
- name: Configure NTP on all switches
  hosts: all
  gather_facts: false

  tasks:
    - name: Configure NTP servers on Cisco
      cisco.ios.ios_ntp_global:
        config:
          servers: "{{ ntp_servers }}"
      when: ansible_network_os == "cisco.ios.ios"

    - name: Configure NTP servers on Arista
      arista.eos.eos_ntp_global:
        config:
          servers: "{{ ntp_servers }}"
      when: ansible_network_os == "arista.eos.eos"

This time, the play runs against all, so every switch goes through both tasks. Each task has a when condition that checks ansible_network_os, which we defined for each vendor in group_vars. So the Cisco task only runs on devices where it's set to cisco.ios.ios, and the Arista task only runs where it's set to arista.eos.eos. Let's run the playbook.

ansible-playbook multi_vendor_when.yml
PLAY [Configure NTP on all switches] ******************

TASK [Configure NTP servers on Cisco] *****************
skipping: [access2]
skipping: [access3]
ok: [core2]
ok: [access1]
ok: [core1]

TASK [Configure NTP servers on Arista] ****************
skipping: [core1]
skipping: [core2]
skipping: [access1]
ok: [access2]
ok: [access3]

PLAY RECAP ********************************************
access1  : ok=1  changed=0  unreachable=0  failed=0  skipped=1  rescued=0  ignored=0
access2  : ok=1  changed=0  unreachable=0  failed=0  skipped=1  rescued=0  ignored=0
access3  : ok=1  changed=0  unreachable=0  failed=0  skipped=1  rescued=0  ignored=0
core1    : ok=1  changed=0  unreachable=0  failed=0  skipped=1  rescued=0  ignored=0
core2    : ok=1  changed=0  unreachable=0  failed=0  skipped=1  rescued=0  ignored=0

The Cisco task ran on the three Cisco switches and was skipped on the two Arista switches. The Arista task did the opposite. Since NTP was already configured by multi_vendor.yml, every task shows ok. In the PLAY RECAP, every switch has one task ok and one task skipped, since only one of the two tasks applies to each switch.

This post is part of an ongoing series - see all the posts below.

Ansible Course
Written by
Suresh Vinasiththamby
Tech enthusiast sharing Networking, Cloud & Automation insights. Join me in a welcoming space to learn & grow with simplicity and practicality.
Comments
More from Packetswitch
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Packetswitch.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.