How to Use Red Hat Ansible Automation Platform to Define Intended Network State in NetBox

Exploring the Red Hat Ansible Certified Collection for NetBox Part Three

Earlier this year we announced NetBox Labs collaboration with Red Hat to support and certify the hugely popular NetBox Ansible Collection on Red Hat Ansible Automation Platform. This blog post is the third in a four-part series that will take a deep dive into the collection and show how to get the most out of this Network Automation power-pairing.

In the part one we introduced the collection and showed you how to install it on your system, and in part two we explored the first use case for the collection, that of using NetBox as a Dynamic Inventory source for Ansible. In this post we look at the second use case for the collection – namely using Ansible to define the intended network state in NetBox.

What is the “Intended State” of a Network?

The intended state is what we as network engineers and architects define based on high and low level network designs, and is typically stored in a network source of truth (NSoT), ie. NetBox. This is different to the actual state as observed by network Observability and Assurance tools.

To help make sense of this it is useful to revisit our Modern Network Automation Architecture, to see how the Ansible Certified Collection for NetBox allows NetBox and the Red Hat Ansible Automation Platform to integrate seamlessly as part of a modern network automation solution.

As you can see from the architecture diagram below, NetBox sits at the heart of the solution as the network source of truth (NSoT), and Ansible lives in the automation tooling space (bottom-right) and extracts the intended network state and inventory data from NetBox.

Ansible can then automate the deployment of the intended state in the form of device configurations out to the target network devices, which is outside the scope of this blog series. If you would like to learn how to get started with network automation using NetBox and other Ansible modules then check out this on-demand webinar, that covers things like configuration backups, gathering device facts and comparing intended vs actual states.

Defining the Intended State Using the Ansible Collection for NetBox

We can use the Red Hat Certified Ansible Collection for NetBox to define the intended state of the network in NetBox, by interacting with the NetBox database to define objects and their intended state in the following ways:

  • Make sure objects exit
  • Update objects if they do exist
  • Remove objects if they do not not exist

The collection has over 80 modules (with more being added all the time), that work with NetBox object types, from Aggregates through to Wireless Link Modules, and everything in between:

This example, taken from the collection docs, shows an Ansible playbook using the netbox_aggregate module from the collection to make sure the 192.168.0.0/16 aggregate network prefix exists in NetBox. Note how the relevant object fields are populated in the data section, and the state is defined as present:

tasks:
    - name: Create aggregate within NetBox with only required information
      netbox.netbox.netbox_aggregate:
        netbox_url: http://netbox.local
        netbox_token: thisIsMyToken
        data:
          prefix: 192.168.0.0/16
          rir: Test RIR
        state: present

The Git Repository that accompanies this blog series has an example playbook that you can use to get started defining your intended network state in NetBox. The playbook is using modules that work with IPAM (IP Address Management) objects in NetBox, but you can easily adapt this to work with other modules – just check the docs for details.

The directory structure looks like this:

├── populate_netbox_ipam.yml
└── roles
    ├── create_aggregates
    │   ├── tasks
    │   │   └── main.yml
    │   └── vars
    │       └── main.yml
    ├── create_prefix_and_vlan_roles
    │   ├── tasks
    │   │   └── main.yml
    │   └── vars
    │       └── main.yml
    ├── create_prefixes
    │   ├── tasks
    │   │   └── main.yml
    │   └── vars
    │       └── main.yml
    └── create_rirs
        ├── tasks
        │   └── main.yml
        └── vars
            └── main.yml

The example playbook populate_netbox_ipam.yml will populate NetBox with data for the following IPAM object types:

  • RIRs (Play 1)
  • Aggregates (Play 2)
  • Prefix and VLAN Roles (Play 3)
# populate_netbox_ipam.yml

---
- name: PLAY 1 - Create RIRs
  connection: local
  hosts: localhost
  gather_facts: False

  roles:
    - role: create_rirs
      tags: rirs

- name: PLAY 2 - Create Aggregates
  connection: local
  hosts: localhost
  gather_facts: False

  roles:
    - role: create_aggregates
      tags: aggregates

- name: PLAY 3 - Create Prefix and VLAN Roles
  connection: local
  hosts: localhost
  gather_facts: False

  roles:
    - role: create_prefix_and_vlan_roles
      tags: prefix_and_roles

The playbook is modularized using roles, for example the file roles/create_aggregates/tasks/main.yml loops over the list of aggregates defined in the file roles/create_aggregates/vars/main.yml, and ensures they are present in NetBox:

# roles/create_aggregates/tasks/main.yml

---
- name: Create Aggregates within NetBox
  netbox.netbox.netbox_aggregate:
    netbox_url: "{{ lookup('ansible.builtin.env', 'NETBOX_API') }}"
    netbox_token: "{{ lookup('ansible.builtin.env', 'NETBOX_TOKEN') }}"
    data: "{{ aggregate }}"
    state: present
  loop: "{{ ipam_aggregates }}"
  loop_control:
    loop_var: aggregate
    label: "{{ aggregate['prefix']}}"
# roles/create_aggregates/vars/main.yml
---
ipam_aggregates:

  - prefix: 10.0.0.0/8
    rir: RFC 1918

  - prefix: 172.16.0.0/12
    rir: RFC 1918

  - prefix: 192.168.0.0/16
    rir: RFC 1918

Similarly, the file roles/create_prefix_and_vlan_roles/tasks/main.yml loops over the list of Prefix and VLAN Roles defined in the file roles/create_prefix_and_vlan_roles/vars/main.yml, and ensures they are present in NetBox:

# roles/create_prefix_and_vlan_roles/vars/main.yml
---
ipam_roles:

  - name: Branch_Data
  - name: Branch_Voice  
  - name: Branch_WiFi
  - name: Guest_WiFi
  - name: Network_Management
  - name: Point_to_Point

Running the playbook results in the following output, and we can see that our RIRs, Aggregates, Prefix and VLAN roles have been created in NetBox:

ansible-playbook populate_netbox_ipam.yml
PLAY [PLAY 1 - Create RIRs] ***********************************************************************************************************************************

TASK [create_rirs : Create RIRs] ******************************************************************************************************************************
ok: [localhost] => (item=RFC 1918)

PLAY [PLAY 2 - Create Aggregates] *****************************************************************************************************************************

TASK [create_aggregates : Create Aggregates within NetBox] ****************************************************************************************************
ok: [localhost] => (item=10.0.0.0/8)
ok: [localhost] => (item=172.16.0.0/12)
ok: [localhost] => (item=192.168.0.0/16)

PLAY [PLAY 3 - Create Prefix and VLAN Roles] ******************************************************************************************************************

TASK [create_prefix_and_vlan_roles : Create Prefix and VLAN Roles within NetBox] ******************************************************************************
ok: [localhost] => (item=Branch_Data)
ok: [localhost] => (item=Branch_Voice)
ok: [localhost] => (item=Branch_WiFi)
ok: [localhost] => (item=Guest_WiFi)
ok: [localhost] => (item=Network_Management)
ok: [localhost] => (item=Point_to_Point)

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

Note – as Ansible playbooks are Idempotent, meaning that hosts are only updated if there is a change, then you can run this playbook repeatedly and it will not make any more changes to the NetBox database.

If we take a look in NetBox now we can see that all the objects we created are present in the database:

So, that’s how easy it is to use the Ansible collection for NetBox to define and maintain the intended state of your network in NetBox. This example has only covered a limited set of IPAM data object types, but as mentioned earlier, the collection has over 80 modules (and counting), that work with NetBox object types from Aggregates through to Wireless Link Modules, and everything in between!

The Git Repository that accompanies this blog series has the example playbook that you can use as a starting point to develop your own playbooks that define and maintain the intended state of your network in NetBox.

Learn more in an upcoming Ansible Collection for NetBox webinar

That’s it for part three of this series. In part four, we will explore the third use case of the Ansible Collection for NetBox – querying NetBox and returning data to drive network automation, such as lists of devices, device configurations, prefixes and IP addresses etc.. Don’t forget to check out the NetBox Learning Git Repo for all the code used in this blog series, as well as other great NetBox Learning resources.

If you want to learn more and see all three use cases in action then register for the upcoming Webinar: Exploring the Red Hat Ansible Certified Collection for NetBox on August 13, 2024 11:00 ET | 15:00 UTC | 17:00 CET.

Share the Post:

Related Posts