Automating Nautobot - Lab 04¶
Using Nautobot Modules and Inventory Plugins in Ansible¶
- Task 1 - Update Nautobot Data Using Ansible
- Task 2 - Use Nautobot as an Inventory Source for Ansible - REST
- Task 3 - Use Nautobot as an Inventory Source for Ansible - GraphQL
Task 1 - Update Nautobot Data Using Ansible¶
Step 1-1¶
On your lab VM, open a terminal session and go to the /home/ntc/labs folder. Create a new folder called ansible.
ntc@nautobot-1:~$ cd labs/
ntc@nautobot-1:~/labs$ mkdir ansible
ntc@nautobot-1:~/labs$ cd ansible
ntc@nautobot-1:~/labs/ansible$
Step 1-2¶
In the ansible folder, create a new file named ansible.cfg. Open it in your editor of choice and add the contents below.
Note: To work with Nautobot from Ansible, you'll need the
networktocode.nautobotcollection and the file below configures Ansible to install collections under a playbook-local path.
[defaults]
# Disable automatic facts gathering.
gathering = explicit
# Do not create retry files when tasks fail. Comment this if you need
# the default behaviour.
retry_files_enabled = False
# For lab usage with password authentication only!
host_key_checking = False
# Playbook-local collections
collections_path = ./collections
Step 1-3¶
Verify that you have Ansible installed:
ntc@nautobot-1:~/labs/ansible$ ansible --version
ansible [core 2.12.4]
config file = /home/ntc/labs/ansible/ansible.cfg
configured module search path = ['/home/ntc/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/ntc/.local/lib/python3.8/site-packages/ansible
ansible collection location = /home/ntc/labs/ansible/collections
executable location = /home/ntc/.local/bin/ansible
python version = 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
jinja version = 2.10.1
libyaml = True
Step 1-4¶
The next step is to install the networktocode.nautobot collection for Ansible.
ntc@nautobot-1:~/labs/ansible$ ansible-galaxy collection install networktocode.nautobot:3.4.1
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/networktocode-nautobot-3.4.1.tar.gz to /home/ntc/.ansible/tmp/ansible-local-896949cyr_0rd/tmpj7sxztlb/networktocode-nautobot-3.4.1-_4cu4l61
Installing 'networktocode.nautobot:3.4.1' to '/home/ntc/labs/ansible/collections/ansible_collections/networktocode/nautobot'
networktocode.nautobot:3.4.1 was installed successfully
ntc@nautobot-1:~/labs/ansible$
Step 1-5¶
The Nautobot Ansible collection has documentation available and you can find the full list of plugins it provides here.
Step 1-6¶
For example, in your first playbook you will use the networktocode.nautobot.site module to manage Site objects. You can view its documentation page online, but also very handy is its documentation via the CLI command ansible-doc.
Note: Documentation is incredibly useful when you're trying to learn how to use a module - it will tell you what parameters it takes, what data it returns, and provide some examples to get you started.
ntc@nautobot-1:~/labs/ansible$ ansible-doc networktocode.nautobot.site
> NETWORKTOCODE.NAUTOBOT.SITE (/home/ntc/labs/ansible/collections/ansible_collections/networktocode/nautobot/plugins/modules/site.py)
Creates or removes sites from Nautobot
ADDED IN: version 1.0.0 of networktocode.nautobot
OPTIONS (= is mandatory):
... OUTPUT TRIMMED ...
Step 1-7¶
In the /home/ntc/ansible/ folder, create a new file named pb_manage_objects.yml. Open it in your editor of choice and add the contents below.
---
- name: MANAGE NAUTOBOT OBJECTS - CREATE
hosts: localhost
tags: create
vars:
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
tasks:
- name: CREATE NEW AMS03 SITE
networktocode.nautobot.site:
url: "{{ nautobot_host }}"
token: "{{ nautobot_token }}"
validate_certs: false
name: "AMS03"
status: "Planned"
region: "Netherlands"
tenant: "Nautobot Airports"
facility: "Amsterdam Airport Schiphol"
state: present
Note: This first task uses the
sitemodule to abstract the REST API operation of creating a new Site object in Nautobot. You get to provide some input parameters (such as name, status, region etc.) and the module does all the hard work.Note: State
presentis used for create/update operations, whileabsentis used to delete an object.
Step 1-8¶
The playbook has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Step 1-9¶
Run the playbook and check out the results in your Nautobot web interface!
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_manage_objects.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [MANAGE NAUTOBOT OBJECTS - CREATE] **************************************************
TASK [CREATE NEW AMS03 SITE] *************************************************************
changed: [localhost]
PLAY RECAP *******************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Note: You can run the playbook again if you want, the module is idempotent so if the Site is configured as per the intent, there is nothing to do.
Step 1-10¶
Now to make a modification to your site object, add a new play in the same pb_manage_objects.yml playbook:
- name: MANAGE NAUTOBOT OBJECTS - UPDATE
hosts: localhost
tags: update
vars:
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
tasks:
- name: UPDATE SITE AMS03 WITH CUSTOM FIELD
networktocode.nautobot.site:
url: "{{ nautobot_host }}"
token: "{{ nautobot_token }}"
validate_certs: false
name: "AMS03"
status: "Staging"
latitude: 52.3
longitude: 4.765
custom_fields:
site_type: "POP"
state: present
Note: This play is meant to update an existing object, adding some other attributes to it (like latitude and longitude) and setting its Site Type custom field value. Each play is set up with a tag so you can run it independently.
Note: The playbook has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Step 1-11¶
Run the playbook with the update tag only:
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_manage_objects.yml -t update
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [MANAGE NAUTOBOT OBJECTS - CREATE] **************************************************
PLAY [MANAGE NAUTOBOT OBJECTS - UPDATE] **************************************************
TASK [UPDATE SITE AMS03 WITH CUSTOM FIELD] ***********************************************
changed: [localhost]
PLAY RECAP *******************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Step 1-12¶
Check out the results in your Nautobot web interface - the coordinates have enabled a button link to show you where they are on a map!

Step 1-13¶
Finally, you want to delete the site object, so add a new play in the same pb_manage_objects.yml playbook - no more parameters this time, as you're telling the API to delete the whole object based on its name:
- name: MANAGE NAUTOBOT OBJECTS - DELETE
hosts: localhost
tags: delete
vars:
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
tasks:
- name: DELETE SITE AMS03
networktocode.nautobot.site:
url: "{{ nautobot_host }}"
token: "{{ nautobot_token }}"
validate_certs: false
name: "AMS03"
state: absent
Note: The playbook has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Step 1-14¶
Run the playbook with the delete tag only:
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_manage_objects.yml -t delete
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [MANAGE NAUTOBOT OBJECTS - CREATE] **************************************************
PLAY [MANAGE NAUTOBOT OBJECTS - UPDATE] **************************************************
PLAY [MANAGE NAUTOBOT OBJECTS - DELETE] **************************************************
TASK [DELETE SITE AMS03] *****************************************************************
changed: [localhost]
PLAY RECAP *******************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Step 1-15¶
Visit the Nautobot interface and confirm the site AMS03 is gone. Feel free to execute the three operations as many times as you like, using the tags provided to only get one of the operations at a time.
Task 2 - Use Nautobot as an Inventory Source for Ansible - REST¶
The Nautobot Ansible collection provides two dynamic inventory plugins for Ansible to leverage - when Nautobot is your inventory source of truth, you definitely want to get your list of devices and their metadata from it!
Here you will explore using the REST API based plugin.
Step 2-1¶
In the /home/ntc/ansible/ folder, create a new file named nautobot_inventory.yml. Open it in your editor of choice and add the contents below.
---
plugin: "networktocode.nautobot.inventory"
api_endpoint: "https://REPLACE_WITH_NAUTOBOT_IP/"
validate_certs: false
token: "REPLACE_WITH_NAUTOBOT_TOKEN"
device_query_filters:
- region: "france" # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
group_by:
- "device_roles"
- "platforms"
Note: It has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Step 2-2¶
Take a moment to read through the inventory plugin parameters:
- It needs details on how to access the Nautobot REST API, since that's what it uses behind the scenes to get you data.
- Then, it is good practice to reduce the scope of the data you're getting especially with large inventories!
- Here, you're telling the plugin to filter the devices by region.
- Finally, Ansible works well with groups, so you can create groups on the fly using data from Nautobot.
- Here, devices will be grouped depending on their role, but also by their platform. Devices in Ansible can belong to multiple groups at a time.
Step 2-3¶
To view the logical structure (hosts and groups) of the dynamic inventory, execute the ansible-inventory command:
ntc@nautobot-1:~/labs/ansible$ ansible-inventory -i nautobot_inventory.yml --graph
@all:
|--@device_roles_distribution:
| |--cdg01-dist-01
| |--cdg02-dist-01
|--@device_roles_edge:
| |--cdg01-edge-01
| |--cdg01-edge-02
| |--cdg02-edge-01
| |--cdg02-edge-02
|--@device_roles_leaf:
| |--cdg01-leaf-01
| |--cdg01-leaf-02
| |--cdg01-leaf-03
| |--cdg01-leaf-04
| |--cdg01-leaf-05
| |--cdg01-leaf-06
| |--cdg01-leaf-07
| |--cdg01-leaf-08
| |--cdg02-leaf-01
| |--cdg02-leaf-02
| |--cdg02-leaf-03
| |--cdg02-leaf-04
| |--cdg02-leaf-05
| |--cdg02-leaf-06
| |--cdg02-leaf-07
| |--cdg02-leaf-08
|--@platforms_arista_eos:
| |--cdg01-edge-01
| |--cdg01-edge-02
| |--cdg01-leaf-01
| |--cdg01-leaf-02
| |--cdg01-leaf-03
| |--cdg01-leaf-04
| |--cdg01-leaf-05
| |--cdg01-leaf-06
| |--cdg01-leaf-07
| |--cdg01-leaf-08
| |--cdg02-edge-01
| |--cdg02-edge-02
| |--cdg02-leaf-01
| |--cdg02-leaf-02
| |--cdg02-leaf-03
| |--cdg02-leaf-04
| |--cdg02-leaf-05
| |--cdg02-leaf-06
| |--cdg02-leaf-07
| |--cdg02-leaf-08
|--@platforms_cisco_ios:
| |--cdg01-dist-01
| |--cdg02-dist-01
|--@ungrouped:
ntc@nautobot-1:~/labs/ansible$
Note: If you're curious what it is doing and why it's taking a while, run the same command with a
-vadded to it - you will see all the REST API requests it is sending!
Step 2-4¶
Next, to view the inventory as a whole, you can run it with --list instead of --graph. That provides you with the full inventory data structure.
Execute the ansible-inventory -i nautobot_inventory.yml --list command. Output is not provided here due to its verbosity - but take a moment to scroll up in your terminal once the command finishes and look at what data is available for a particular device.
Step 2-5¶
In the /home/ntc/ansible/ folder, create a new file named pb_rest_inventory.yml. Open it in your editor of choice and add the contents below.
---
- name: EXPLORE NAUTOBOT DYNAMIC INVENTORY DATA
hosts: device_roles_edge
connection: local
vars:
device_info: >
Host: {{ inventory_hostname }}
IP: {{ ansible_host }}
Status: {{ status['label'] }}
Regions: {{ ", ".join(regions) | title }}
Model: {{ manufacturers[0] }} {{ device_types[0] }}
Rack: {{ racks[0] }}
tasks:
- debug:
var: device_info
Step 2-6¶
Run the playbook by providing it the newly created inventory:
ntc@nautobot-1:~/labs/ansible$ ansible-playbook -i nautobot_inventory.yml pb_rest_inventory.yml
PLAY [EXPLORE NAUTOBOT DYNAMIC INVENTORY DATA] *******************************************
TASK [debug] *****************************************************************************
ok: [cdg01-edge-01] => {
"device_info": "Host: cdg01-edge-01 IP: 10.8.128.1 Status: Active Regions: France, Europe Model: arista dcs-7280cr2-60 Rack: cdg01-101\n"
}
ok: [cdg01-edge-02] => {
"device_info": "Host: cdg01-edge-02 IP: 10.8.128.2 Status: Active Regions: France, Europe Model: arista dcs-7280cr2-60 Rack: cdg01-102\n"
}
ok: [cdg02-edge-01] => {
"device_info": "Host: cdg02-edge-01 IP: 10.28.128.1 Status: Active Regions: France, Europe Model: arista dcs-7280cr2-60 Rack: cdg02-101\n"
}
ok: [cdg02-edge-02] => {
"device_info": "Host: cdg02-edge-02 IP: 10.28.128.2 Status: Active Regions: France, Europe Model: arista dcs-7280cr2-60 Rack: cdg02-102\n"
}
PLAY RECAP *******************************************************************************
cdg01-edge-01 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cdg01-edge-02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cdg02-edge-01 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cdg02-edge-02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Note: The groups created via the inventory plugin allow you to only run the playbook tasks against a subset of the devices. Feel free to change that around with other groups or even
all!
Step 2-7¶
The inventory does not fetch Configuration Context data by default, since it is a potentially intensive task. Edit the nautobot_inventory.yml file and add the following line at the end:
Step 2-8¶
Add two more tasks to you pb_rest_inventory.yml playbook to print the context data and extract one particular bit of information about LLDP from it:
Step 2-9¶
Save and run the playbook again - the output will be a bit verbose, so take your time to scroll through it and identify the new information:
ntc@nautobot-1:~/labs/ansible$ ansible-playbook -i nautobot_inventory.yml pb_rest_inventory.yml
PLAY [EXPLORE NAUTOBOT DYNAMIC INVENTORY DATA] *******************************************
... OUTPUT TRIMMED ...
TASK [debug] *****************************************************************************
ok: [cdg01-edge-01] => {
"msg": "LLDP Enabled: True"
}
ok: [cdg01-edge-02] => {
"msg": "LLDP Enabled: True"
}
ok: [cdg02-edge-01] => {
"msg": "LLDP Enabled: True"
}
ok: [cdg02-edge-02] => {
"msg": "LLDP Enabled: True"
}
PLAY RECAP *******************************************************************************
cdg01-edge-01 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cdg01-edge-02 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cdg02-edge-01 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cdg02-edge-02 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Task 3 - Use Nautobot as an Inventory Source for Ansible - GraphQL¶
Depending on what sort of related data you need to get from Nautobot for your Ansible inventory, the GraphQL API might be much faster than the REST API. You will now use the GraphQL inventory plugin offered by the collection.
Step 3-1¶
In the /home/ntc/ansible/ folder, create a new file named nautobot_inventory_gql.yml. Open it in your editor of choice and add the contents below.
---
plugin: "networktocode.nautobot.gql_inventory"
api_endpoint: "https://REPLACE_WITH_NAUTOBOT_IP/"
validate_certs: false
token: "REPLACE_WITH_NAUTOBOT_TOKEN"
filters:
region: "france" # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
Note: It has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Step 3-2¶
Take a moment to read through the inventory plugin parameters:
- It needs details on how to access the Nautobot GraphQL API, since that's what it uses behind the scenes to get you data.
- Then, it is good practice to reduce the scope of the data you're getting especially with large inventories!
- Here, you're telling the plugin to filter the devices by region.
Step 3-3¶
To view the logical structure (hosts and groups) of the dynamic inventory, execute the ansible-inventory command:
ntc@nautobot-1:~/labs/ansible$ ansible-inventory -i nautobot_inventory_gql.yml --graph
@all:
|--@ungrouped:
| |--cdg01-dist-01
| |--cdg01-edge-01
| |--cdg01-edge-02
| |--cdg01-leaf-01
| |--cdg01-leaf-02
| |--cdg01-leaf-03
| |--cdg01-leaf-04
| |--cdg01-leaf-05
| |--cdg01-leaf-06
| |--cdg01-leaf-07
| |--cdg01-leaf-08
| |--cdg02-dist-01
| |--cdg02-edge-01
| |--cdg02-edge-02
| |--cdg02-leaf-01
| |--cdg02-leaf-02
| |--cdg02-leaf-03
| |--cdg02-leaf-04
| |--cdg02-leaf-05
| |--cdg02-leaf-06
| |--cdg02-leaf-07
| |--cdg02-leaf-08
Step 3-4¶
To see the full inventory, run the command with --list:
ntc@nautobot-1:~/labs/ansible$ ansible-inventory -i nautobot_inventory_gql.yml --list
{
"_meta": {
"hostvars": {
"cdg01-dist-01": {
"ansible_host": "cdg01-dist-01",
"ansible_network_os": "cisco.ios.ios"
},
"cdg01-edge-01": {
"ansible_host": "10.8.128.1",
"ansible_network_os": "arista.eos.eos"
},
... OUTPUT TRIMMED ...
Note: What you're going to immediately notice is that the data provided by the plugin is very sparse by default - there's just the hostname and the
ansible_network_os!
Step 3-5¶
Add the following parameters to the inventory file:
queryadds more requested data to the GraphQL query sent by the plugin.group_bytells the plugin to create Ansible groups based on the device role.additional_variablestells the plugin to add the listed data as inventory variables for each host.
---
plugin: "networktocode.nautobot.gql_inventory"
api_endpoint: "https://REPLACE_WITH_NAUTOBOT_IP/"
validate_certs: false
token: "REPLACE_WITH_NAUTOBOT_TOKEN"
filters:
region: "france" # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
query:
site:
slug:
device_role:
slug:
group_by:
- "device_role.slug"
additional_variables:
- "device_role"
- "site"
- "status"
Step 3-6¶
To see the full inventory again, run the command with --list:
ntc@nautobot-1:~/labs/ansible$ ansible-inventory -i nautobot_inventory_gql.yml --list
{
"_meta": {
"hostvars": {
"cdg01-dist-01": {
"ansible_host": "cdg01-dist-01",
"ansible_network_os": "cisco.ios.ios",
"device_role": {
"slug": "distribution"
},
"site": {
"slug": "cdg01"
},
"status": {
"name": "Active"
}
},
... OUTPUT TRIMMED ...
Note: You can now see the device role, the site it belongs to, and its status in the Ansible inventory variables.
Step 3-7¶
You can use this inventory just like the previous one with any playbook - if you have spare time at the end, take the pb_rest_inventory.yml playbook and adapt it to work with the new data provided by the nautobot_inventory_gql.yml inventory!