Automating Network Data Tasks with NetBox Custom Scripts
Learn how to create a NetBox custom script that assigns a site to all devices missing one, with step‑by‑step placement, execution, and verification guidance.
13 Jan 2026, 22:00 UTC

Why use NetBox custom scripts
NetBox custom scripts let you run Python code inside the NetBox application to create, update, or validate objects without writing external API clients. The script runs with the same permissions as the NetBox web process, has direct access to the internal ORM, and appears as a form in the UI where you can supply parameters.
How a custom script works
When NetBox starts, it scans a directory defined by SCRIPTS_ROOT in configuration.py. Any file that defines a class inheriting from extras.scripts.Script is discovered, its Meta attributes are used to build a form, and the run method is executed when you click “Run”. Logging inside the method streams back to the UI in real time.
Worked example: bulk‑assign a site to all devices missing a site
Suppose you imported devices from a CSV and many lack a site assignment. The following script adds a site chosen by the user to every device that currently has site = None.
from extras.scripts import Script, ObjectVar
from dcim.models import Device, Site
class AssignMissingSite(Script):
class Meta:
name = "Assign site to devices without a site"
description = "Pick a site and apply it to all devices that currently have no site."
site = ObjectVar(
description="Site to assign",
queryset=Site.objects.all(),
required=True
)
def run(self, data, commit):
target_site = data['site']
qs = Device.objects.filter(site__isnull=True)
count = qs.count()
self.log_info(f"Found {count} devices without a site.")
if count == 0:
return "No action needed."
if not commit:
self.log_warning("Dry run – no changes will be saved.")
return f"Would assign site {target_site} to {count} devices."
updated = qs.update(site=target_site)
self.log_success(f"Assigned site {target_site} to {updated} devices.")
return f"Done – {updated} devices updated."
Place this file, e.g. assign_missing_site.py, in the scripts directory (commonly /opt/netbox/netbox/scripts/). Ensure the file is readable by the user running the NetBox service (usually netbox or www-data). After saving, restart the NetBox web service (or touch configuration.py) so the discovery process picks up the new script.
Running the script
- Log in to NetBox as a user with the
extras.change_script permission (typically an admin or a custom role granted script access).
- Navigate to Customization → Scripts. You should see "Assign site to devices without a site" in the list.
- Click the script name, select a site from the dropdown, and optionally toggle the “Commit” checkbox to perform a dry run.
- Press Run. The UI will show log lines as the script executes, ending with a summary message.
- Verify the change by searching for devices with no site (
site__isnull=True) – the count should be zero after a committed run.
extras.change_script permission (typically an admin or a custom role granted script access).site__isnull=True) – the count should be zero after a committed run.Limits and common mistakes
- Permission scope – Scripts run with the NetBox process’ privileges. A logic error that deletes objects will affect the database directly; there is no extra sandbox. Always test with
commit=Falsefirst. - Performance – The
runmethod executes in a worker thread. Long loops over thousands of objects can block the worker or hit the gunicorn timeout. Use queryset methods (update,bulk_create) and consider chunking (iterator(chunk_size=1000)) for large data sets. - Validation bypass – Direct ORM calls skip some validation that the REST API enforces (e.g., custom field constraints, webhook triggers). If you rely on those, call the appropriate model’s
full_cleanmethod or invoke the API viarequestsinside the script. - Discovery issues – If the script does not appear, check:
- The file is inside
SCRIPTS_ROOT(defaultNETBOX_ROOT/scripts). - The file ends with
.pyand is readable. - No syntax errors – start NetBox in debug mode (
NETBOX_DEBUG=True) to see import tracebacks in the logs.
- The file is inside
- Logging visibility – The built‑in logger only shows messages sent via
self.log_info,log_success,log_warning, orlog_error. Plainprintstatements go to the server console, not the UI.
Practical verification checklist
- Confirm the Scripts menu appears under Customization.
- Run the example script with
commit=Falseand ensure the dry‑run message appears in the UI log. - Run with
commit=Trueand check that the device count with missing site drops to zero (use the NetBox filtersite__isnull=True). - Review
/var/log/netbox/web.logfor any tracebacks if the script fails silently.
By following the pattern above you can automate repetitive data‑maintenance tasks while staying inside NetBox’s permission and audit model. Keep scripts small, test with dry runs, and prefer ORM bulk operations to avoid performance pitfalls.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.