I’ve got a playbook that is triggered by a Netbox Interface change and it’s almost working as expected. When the playbook runs and connected to the Cisco device if it needs to push the block of changes it will push it multiple times which in some cases causes errors. And it will push the commands for every command in the list. For example if there are 3 commands in the task is will send all 3 commands 3 times to the switch. I’m at a loss as to why
it causes issues with something like “auto qos voip cisco-phone” because it can only be run once so it errors out.
Running Ansibe 2.20.2 on Ubuntu 24.04
---
- name: Configure switch interface based on NetBox
hosts: "{{ switch_name }}"
gather_facts: no
run_once: true
vars:
netbox_url: "https://localhost"
netbox_token: Removed
# default values (can override via --extra-vars)
tasks:
- name: Get switch info from NetBox
set_fact:
switch_info: >-
{{ query('netbox.netbox.nb_lookup', 'devices',
api_endpoint=netbox_url,
api_filter='name=' ~ switch_name,
token=netbox_token)[0].value }}
- name: Get Virtual Chassis Information from stacked switch
set_fact:
vc_info: >-
{{ query('netbox.netbox.nb_lookup', 'virtual-chassis',
api_endpoint=netbox_url,
api_filter='master_id=' ~ switch_info['virtual_chassis']['master']['id'],
token=netbox_token)[0].value }}
when: switch_info.virtual_chassis is defined and switch_info.virtual_chassis is not none
- name: Loop thru Virtual Chassis members to find Interface Data from stacked switch
set_fact:
found_interfaces: >-
{{ lookup(
'netbox.netbox.nb_lookup',
'interfaces',
api_endpoint=netbox_url,
token=netbox_token,
api_filter='device_id=' ~ item.id ~ ' name=' ~ interface_name
) | default([]) }}
loop: "{{ vc_info.members }}"
loop_control:
label: "{{ item.name }}"
break_when: found_interfaces | length > 0
when: vc_info is defined
- name: Get Interface Data from non-stacked switch
set_fact:
found_interfaces: >-
{{ lookup(
'netbox.netbox.nb_lookup',
'interfaces',
api_endpoint=netbox_url,
token=netbox_token,
api_filter='device=' ~ switch_info['name'] ~ ' name=' ~ interface_name
) | default([]) }}
when: vc_info is not defined
- name: Fail if interface not found in Netbox
fail:
msg: "Interface {{ interface_name }} was not found on device {{ switch_info['name'] }} in Netbox."
when: found_interfaces | length == 0
- name: Extract tag slugs
set_fact:
interface_tag_slugs: "{{ found_interfaces.value.tags | map(attribute='slug') | list }}"
- name: Build interface_info object
set_fact:
interface_info:
description: "{{ found_interfaces['value']['description'] | default('') }}"
vlan: "{{ found_interfaces['value']['untagged_vlan']['vid'] | default(None) }}"
vlan_mode: "{{ found_interfaces['value']['mode']['value'] }}"
is_unmanaged: "{{ 'ansible-unmanaged' in interface_tag_slugs }}"
voice: "{{ found_interfaces['value']['custom_fields']['Voice_VLAN'] }}"
enabled: "{{ found_interfaces['value']['enabled'] }}"
# - name: Show interface summary
# debug:
# var: interface_info
- name: Stop playbook if unmanaged
meta: end_play
when: interface_info.is_unmanaged | bool
- name: Get interface config
cisco.ios.ios_command:
commands:
- show running-config interface {{ interface_name }}
register: interface_config
- name: Configure - Interface Description
cisco.ios.ios_config:
lines:
- description {{ interface_info.description }}
parents: "interface {{ interface_name }}"
- name: Configure - Interface VLAN
cisco.ios.ios_config:
lines:
- switchport access vlan {{ interface_info.vlan }}
- switchport mode access
- spanning-tree portfast
parents: "interface {{ interface_name }}"
when: interface_info.vlan_mode == "access"
- name: Configure - Enable CDP and LLDP
cisco.ios.ios_config:
lines:
- cdp enable
- lldp transmit
parents: "interface {{ interface_name }}"
when:
- interface_info.voice == true
- "'cdp' in interface_config.stdout[0]"
- name: Configure - Disable CDP and LLDP
cisco.ios.ios_config:
lines:
- no cdp enable
- no lldp transmit
parents: "interface {{ interface_name }}"
when:
- interface_info.voice == false
- name: Configure - Enable VOIP Phone
cisco.ios.ios_config:
commands:
- switchport voice vlan 100
- auto qos voip cisco-phone
parents: "interface {{ interface_name }}"
match: none
ignore_errors: yes
when:
- interface_info.voice == true
- "'switchport voice vlan 100' not in interface_config.stdout[0]"
- name: Configure - Disable VOIP Phone
cisco.ios.ios_config:
commands:
- no switchport voice vlan 100
- no auto qos voip cisco-phone
parents: "interface {{ interface_name }}"
match: none
ignore_errors: yes
when:
- interface_info.voice == false
- "'switchport voice vlan ' in interface_config.stdout[0]"