NetBox plugin for full integration with Windows DHCP Server via PowerShell Universal
Project description
netbox-windows-dhcp
A NetBox v4.5.0+ plugin for full integration with Windows DHCP Server via PowerShell Universal (PSU v5).
Features
- Define Windows DHCP Servers (hostname, port, HTTP/HTTPS, App Token auth, optional SSL verification)
- Define DHCP Failover relationships (Load Balance or Hot Standby)
- Correlate DHCP Scopes with NetBox Prefixes (many scopes → one prefix)
- Lease lifetime stored in seconds, displayed in the most readable unit (e.g.
3 Days,73 Hours,30 Minutes) - Global library of reusable DHCP Option Values with Option Code Definitions (pre-populated with all standard Windows DHCP built-in codes)
- One-time Import from Server — pulls failovers, scopes, scope-level option values, and exclusion ranges from a live DHCP server into NetBox
- Active sync mode: pull leases and reservations → create/update NetBox IP Addresses with status, DNS name, and client MAC
- Stale record cleanup: expired leases and removed reservations are deleted or downgraded automatically
- Push reservations, scope config, and exclusion ranges from NetBox → DHCP server (optional, settings-controlled)
- DHCP Scopes panel injected into the NetBox Prefix detail view
- All sync and import operations run as background jobs — no HTTP timeouts on large servers
- All background job changes appear in the NetBox changelog (attributed to the user who queued the job)
- Scheduled background sync (hourly by default, configurable) + manual Sync Now per server or for all servers
- Full REST API for all plugin objects
- Plugin-wide settings managed via the NetBox UI (no
PLUGINS_CONFIGentry required)
Requirements
NetBox host
- NetBox >= 4.5.0
- Python >= 3.10
requests>= 2.28- NetBox RQ workers running (required for background sync and import jobs)
Each Windows DHCP server
- Windows Server 2016 or later with the DHCP Server role installed
- PowerShell Universal v5.x (tested on 5.6.11+) — see PSU Setup
DhcpServerPowerShell module (included with the DHCP Server role)
Installation
1. Install the package
pip install netbox-windows-dhcp
Or from source (editable install recommended for development):
pip install -e /path/to/netbox-windows-dhcp
2. Add to NetBox configuration (configuration.py)
PLUGINS = [
'netbox_windows_dhcp',
]
No PLUGINS_CONFIG entry is required. All settings are managed through the plugin's Admin → Settings page in the NetBox UI.
3. Run migrations
python manage.py migrate
Running migrations also triggers the post_migrate signal which auto-registers the dhcp_client_id custom field on IP Address objects.
4. Restart NetBox
Restart gunicorn/uwsgi and the RQ workers.
Getting Started
Add a DHCP Server
- Go to Windows DHCP → Infrastructure → Servers → Add
- Enter the hostname/IP, port, and PSU App Token
- Toggle Use HTTPS as appropriate (default: on)
- Disable Verify SSL Certificate if the server uses a self-signed certificate
Import from a Server
Once a server is configured, click Import from Server on the server detail page to run a one-time background import of:
- Failover relationships (matched to existing DHCP Server objects by hostname)
- Scopes (NetBox Prefixes are created automatically if they don't exist)
- Scope-level option values (unknown option codes are created automatically)
- Option 3 (Router) and Option 51 (Lease Time) are skipped — they are stored directly on the Scope object
You will be redirected to the job status page where you can monitor progress and view the full results log. Existing records are skipped; the import never overwrites data already in NetBox.
Configure Sync Settings
Go to Windows DHCP → Admin → Settings to configure:
| Setting | Description |
|---|---|
| Sync IP Addresses from Leases & Reservations | When checked, pull leases and reservations and create/update/delete NetBox IP Address records. When unchecked, sync scope config only. |
| Push Reservations to DHCP Server | Push NetBox reserved IPs to the DHCP server as reservations |
| Push Scope Info to DHCP Server | Push scope config changes from NetBox to the DHCP server |
| Sync Interval (minutes) | How often the background sync runs (5–1440) |
| Sync Job Queue | Worker queue priority (High / Default / Low) used for all sync and import background jobs. Default: Default. |
| Sync-Protected Tag | A NetBox tag. Any IP Address carrying this tag is fully shielded from sync — its status, DNS name, and record itself are never modified or removed. Leave blank to disable. |
| Update Client ID for Protected IPs | When enabled, the sync updates the dhcp_client_id custom field on protected IPs to match the DHCP server's active lease. All other sync writes remain blocked. Useful after a server replacement when the client MAC changes. |
Editing Rules
Not all objects in the plugin support full CRUD. The table below summarises what operations are allowed and under what conditions.
Failover Relationships
| Operation | Allowed |
|---|---|
| View | ✅ Always |
| Delete | ✅ Always |
| Create | ❌ Never — import from the DHCP server |
| Edit | ✅ Via direct URL only (not exposed in the UI) |
Failover relationships are created automatically during an Import from Server run. The Add button and Edit button are intentionally hidden from the list and detail views — relationships should be managed on the DHCP server and imported into NetBox. The Sync Enabled toggle on the list view is the primary way to manage a failover relationship after import.
DHCP Scopes
| Operation | Push Scope Info off | Push Scope Info on |
|---|---|---|
| View | ✅ | ✅ |
| Create | ❌ | ✅ |
| Edit | ❌ | ✅ |
| Delete | ❌ | ✅ |
When Push Scope Info to DHCP Server is disabled, the DHCP server is the source of truth for scope configuration. Scopes reach NetBox via Import from Server and are kept up to date by periodic sync. Direct edits are blocked to prevent NetBox from drifting out of sync with the server.
When Push Scope Info to DHCP Server is enabled, NetBox becomes the source of truth. Scope changes saved in NetBox are pushed to the DHCP server on the next sync. Full CRUD is permitted.
Note: Exclusion Ranges follow scope info — create, edit, and delete are permitted whenever Push Scope Info is enabled, and blocked when it is disabled.
IP Address Status Validation
Any IP Address with status dhcp must satisfy both of the following conditions when saved through the UI or REST API:
- The IP address must fall within the prefix of at least one configured DHCP Scope.
- The IP address must not fall within any exclusion range of that scope.
This validation runs during form submission and API writes. It does not run during background sync — the sync sets statuses based on authoritative data from the DHCP server and bypasses this check by design.
The reserved status is not validated against DHCP scope membership — it is a general-purpose NetBox status used for both DHCP reservations and non-DHCP purposes.
There are no UI-level restrictions on editing IP addresses with dhcp or reserved status beyond the above. Changes made manually to DHCP-managed IPs will be overwritten on the next sync if Sync IP Addresses from Leases & Reservations is enabled.
Scope Source
Every DHCP Scope must be associated with exactly one of:
- Server — for standalone scopes that are not part of a failover relationship. Set automatically during Import from Server when the scope has no failover name.
- Failover Relationship — for scopes managed under a Windows DHCP failover pair.
This association determines which server is responsible for syncing the scope. Standalone scopes are only synced against their assigned server; failover scopes are synced via the primary server of their failover relationship.
Sync Behavior
Scope eligibility
Not every scope is synced on every server. Before processing a scope, the sync checks eligibility:
| Scope type | Synced when |
|---|---|
| Standalone (has Server) | Server's Sync Standalone Scopes is enabled, and the scope's assigned server matches the server being synced |
| Failover-linked | The failover relationship's Sync Enabled is on, and the server being synced is the primary of that failover |
| Neither assigned | Never synced |
Server pre-flight: If a server has Sync Standalone Scopes disabled and is not the primary server for any active failover relationship, the sync skips it entirely — no network connection is made.
Per-failover sync control
Each failover relationship has a Sync Enabled toggle (visible on the failover list and toggleable per-row). When disabled, all scopes using that failover are excluded from sync. This allows pausing sync for a specific failover pair without affecting others.
IP Address sync
When Sync IP Addresses from Leases & Reservations is unchecked (default), the sync pulls and stores scope config only — no IP Address objects are created or modified.
When checked, the sync pulls leases and reservations from each DHCP server and creates/updates/deletes NetBox IP Address records.
IP Address lifecycle (active sync)
Creating and updating:
| Source | IPAddress.status |
dns_name |
dhcp_client_id |
|---|---|---|---|
| Active DHCP lease | dhcp |
Hostname from DHCP (lowercased) | Client MAC address |
| DHCP reservation | reserved |
Name from reservation (lowercased) | Client MAC address |
Reservations take precedence — a reserved IP is not overwritten by a lease record for the same address.
Special case — reserved IP with no dhcp_client_id: If a reserved IP address has no dhcp_client_id set (e.g. it was manually created before the client MAC was known), and a lease is discovered on the DHCP server for that address, the sync will populate dhcp_client_id from the lease but will not change the status or dns_name. This allows the IP to be pre-registered before provisioning.
New IP Addresses are created using the prefix length of the associated scope's NetBox Prefix (not /32).
The dhcp_client_id custom field (auto-registered on IP Address) stores the client MAC address in Windows DHCP hyphen-separated format (00-11-22-33-44-55).
All IP Address creates and updates are recorded in the NetBox changelog, attributed to the user who queued the sync job. DHCP lease metadata (lease hostname, active status, expiration) is stored in a separate non-changelog side-table (DHCPLeaseInfo) and displayed on the IP Address detail page — these updates do not generate changelog entries.
Cleanup (stale record removal):
After syncing leases and reservations for a scope, the plugin removes records that no longer exist on the server:
| NetBox status | dhcp_client_id |
DHCP-managed? | Server state | Action |
|---|---|---|---|---|
dhcp |
— | — | Lease still active | No change |
dhcp |
— | — | Lease expired or gone | Deleted |
reserved |
— | — | Any | Never auto-deleted (pre-staged or manual) |
reserved |
set | No (manually created) | Any | Never auto-deleted |
reserved |
set | Yes (sync-created) | Reservation still exists | No change |
reserved |
set | Yes (sync-created) | No reservation, but lease exists | Downgraded to dhcp |
reserved |
set | Yes (sync-created) | Neither reservation nor lease | Deleted |
"DHCP-managed" means the record was created or previously updated by the sync (indicated by a DHCPLeaseInfo side-record). Manually-created reserved IPs are never auto-deleted even if they have a dhcp_client_id.
Note: When Push Reservations to DHCP Server is enabled, NetBox is the source of truth for reservations.
reservedIP Addresses with adhcp_client_idare never deleted or downgraded based on server state — they will be pushed to the server on the next sync instead.
Scope cleanup:
When a scope exists in NetBox but is no longer reported by the DHCP server, and the server was successfully reached, and Push Scope Info to DHCP Server is disabled, the scope is deleted from NetBox. Only scopes whose Scope Source links them to the server being synced (either directly as a standalone scope, or via a failover relationship where this server is primary) are considered.
When Push Scope Info to DHCP Server is enabled, a scope missing from the server will instead be created on the server.
Safety: If the API call to fetch leases/reservations fails for a scope, cleanup is skipped for that scope. If list_scopes() fails entirely, the server is unreachable and no cleanup runs at all.
Pre-Staging IPs
To register a device in NetBox before its client MAC is known, create the IP Address with:
- Status
reserved dns_nameset to the planned canonical hostname- Leave
dhcp_client_idblank
The sync will never delete or overwrite the status or dns_name of a reserved IP with no dhcp_client_id. If a lease appears on the DHCP server for that address, the sync populates dhcp_client_id from the lease (so the reservation can be pushed once the MAC is known) but leaves everything else unchanged.
Typical lifecycle:
- Create the IP in NetBox with status
reserved,dns_nameset to the planned hostname, and nodhcp_client_id. - Device is provisioned; it receives a DHCP lease. The sync populates
dhcp_client_idfrom the lease. - Once
dhcp_client_idis set, the reservation is pushed to the DHCP server on the next sync (if Push Reservations is enabled). Thedns_nameis used as the reservation name on the server.
Sync-Protected IPs
Any IP Address tagged with the configured Sync-Protected Tag is fully shielded from sync writes. The sync will never change its status, DNS name, or dhcp_client_id, and will never delete the record during cleanup.
DHCP lease metadata (DHCPLeaseInfo) is always updated for protected IPs regardless of protection status — the lease hostname, active state, and expiration are still recorded and visible on the IP Address detail page.
Update Client ID for Protected IPs
Enable this checkbox to allow one narrow exception: the sync may update the dhcp_client_id custom field on a protected IP when the server's active lease carries a different client MAC. All other writes (status, DNS name, record deletion) remain blocked. This is useful after a DHCP server replacement where the client MAC for the same device changes.
Typical use: Tag an IP as protected when it has been manually configured and should not be overwritten by automated sync, even if the DHCP server has a conflicting record.
Sync Now
The Sync Now button on a server detail page enqueues a background sync job and redirects to the job status page. The full sync log is visible there. A Sync All Servers action is also available from the server list page.
Lease Lifetime Display
Lease lifetimes are stored in seconds and displayed in the most readable exact unit:
86400→ 1 Day259200→ 3 Days262800→ 73 Hours (not an exact number of days)3600→ 1 Hour60→ 1 Minute45→ 45 Seconds
When configuring a scope, enter the value and select the unit (Seconds / Minutes / Hours / Days). The form defaults to 1 Day for new scopes. Values learned from the DHCP server are automatically decomposed to the largest clean unit.
Navigation
The plugin adds a Windows DHCP menu to the NetBox left sidebar:
- Infrastructure → Servers, Failover
- Scopes → Scopes
- Options → Option Values, Option Code Definitions
- Admin → Settings
PSU Setup
See psu/README.md for full deployment instructions.
The plugin expects PowerShell Universal v5.x (tested on 5.6.11+) on each DHCP server, exposing endpoints under /api/dhcp/. The PSU script is at psu/dhcp_api_endpoints.ps1.
Endpoint reference
| Method | Path | Description |
|---|---|---|
| GET | /api/dhcp/scopes |
List all scopes (includes router and failover name) |
| GET | /api/dhcp/scopes/:scope_id |
Get a single scope |
| POST | /api/dhcp/scopes |
Create a scope |
| PUT | /api/dhcp/scopes/:scope_id |
Update a scope |
| GET | /api/dhcp/leases[?scope_id=] |
List active leases |
| GET | /api/dhcp/reservations[?scope_id=] |
List reservations |
| POST | /api/dhcp/reservations |
Create a reservation |
| PUT | /api/dhcp/reservations/:client_id |
Update a reservation |
| DELETE | /api/dhcp/reservations/:client_id |
Delete a reservation |
| GET | /api/dhcp/failover |
List failover relationships |
| POST | /api/dhcp/failover |
Create a failover relationship |
| GET | /api/dhcp/options/server |
Server-level option values |
| GET | /api/dhcp/options/scope/:scope_id |
Scope-level option values |
| GET | /api/dhcp/exclusions?scope_id= |
List exclusion ranges for a scope |
| POST | /api/dhcp/exclusions |
Create an exclusion range |
| DELETE | /api/dhcp/exclusions |
Delete an exclusion range (body: scope_id, start_ip, end_ip) |
Authentication: PSU v5 App Tokens are sent as Authorization: Bearer <token>. Generate a token in the PSU admin console under Security → App Tokens and paste it into the App Token field on the DHCP Server object in NetBox.
REST API
The plugin exposes a REST API under /api/plugins/netbox-windows-dhcp/:
| Endpoint | Model |
|---|---|
/servers/ |
DHCP Servers |
/failover/ |
Failover relationships |
/option-codes/ |
Option Code Definitions |
/option-values/ |
Option Values |
/scopes/ |
DHCP Scopes (includes nested exclusion_ranges) |
/exclusion-ranges/ |
DHCP Exclusion Ranges |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netbox_windows_dhcp-1.0.1.tar.gz.
File metadata
- Download URL: netbox_windows_dhcp-1.0.1.tar.gz
- Upload date:
- Size: 64.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a5a24e9f23b41416c235a14976a30613032a2e44673f7ee6bd17eff750dd9fb
|
|
| MD5 |
978c9b5471f6beba2689984a074d1854
|
|
| BLAKE2b-256 |
2725b7263d5e205d24c40ba55fb6e2d7223e7b158e68df54b14932172b156559
|
File details
Details for the file netbox_windows_dhcp-1.0.1-py3-none-any.whl.
File metadata
- Download URL: netbox_windows_dhcp-1.0.1-py3-none-any.whl
- Upload date:
- Size: 71.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72f97de3138ebd4c7844a0265c6215d8f165d76fea2144aaac3982c78dd73d9a
|
|
| MD5 |
7fff3a676f9b6a5321e100fbf0361712
|
|
| BLAKE2b-256 |
3b741a3df057676f04e27285a7fbadb77fb4e72b676b4c79c5553f003468a429
|