Bulk Operations
EnterpriseRun batch actions across many devices: bulk credential testing, bulk config pull, snapshot collection by site/type, tag management, and exports via the Controller admin API.
Overview
Bulk operations are part of the NetStacks Controller (Enterprise). They run against the Controller admin API under the base path /api/admin and require an authenticated operator token. All endpoints on this page assume a Controller reachable at http://localhost:3000; substitute your own host.
Bulk operations let you act on many devices in a single request instead of repeating the same action device by device. When you manage hundreds or thousands of network devices, the ability to test credentials fleet-wide, pull configs from every core router, snapshot a whole site, or re-tag an entire platform at once is essential for operational efficiency.
NetStacks exposes several real first-class bulk endpoints:
- Bulk credential test —
POST /api/admin/devices/bulk/test-credentialsopens an SSH connection to each target device and reports success or failure per device. - Bulk config pull —
POST /api/admin/config/devices/bulk-pullpulls structured configuration from a list of device IDs and records the run as a snapshot so it appears in the Job Monitor. - Snapshot collection — a config snapshot collection targets devices by
siteand/ordevice_typeand captures running configuration concurrently.
For actions without a dedicated batch endpoint (tag changes, credential reassignment, export, delete), iterate over the device list with the standard CRUD endpoints. The device list itself can be narrowed by source, device_type, and site before you build the target set.
Available Bulk Actions
| Action | Mechanism | Description |
|---|---|---|
| Bulk test credentials | POST /devices/bulk/test-credentials | SSH-connect to each device and report per-device success/failure |
| Bulk config pull | POST /config/devices/bulk-pull | Pull structured config from a list of device IDs; creates a snapshot |
| Snapshot collection | POST /devices/snapshots + /collect | Capture running config across devices matched by site/device_type |
| Exec command (per device) | POST /devices/{id}/exec-command | Run one command on a device; loop over a list for scripted broadcast |
| Add / remove tags | PUT /devices/{id} loop | Update the device tags array |
| Update credential | PUT /devices/{id} loop | Change default_credential_id on each device |
| Export | GET /devices + jq | List devices and format the result as CSV/JSON |
| Delete | DELETE /devices/{id} loop | Remove devices from the inventory |
Bulk delete cannot be undone, and deleting a device removes its associated backup history. Always export the device list first and verify your target set before deleting.
How It Works
Building a Target Set
The device list endpoint GET /api/admin/devices accepts three filter query parameters that narrow the returned devices:
- device_type — the platform/driver name (for example
cisco_iosorarista_eos). - site — the site label assigned to the device (for example
dc-east). - source — how the device entered inventory. Real stored values are
manual(created in the UI/API),csv(set by the CSV importer), andnetbox(NetBox sync).
GET /api/admin/devices caps limit at 100 per request. To enumerate a large fleet, page through results with offset and accumulate the IDs before submitting a bulk action.
Tags and Organization
Tags are free-form string labels stored as a JSON array on each device record. Unlike rigid folders, tags let a device belong to multiple logical groups at once. Common tagging strategies:
- By function —
core,distribution,access,edge,firewall - By environment —
production,staging,lab,dr - By site —
dc-east,dc-west,branch-nyc - By compliance —
pci,hipaa,sox
Tags help you build a device ID list with client-side filtering, but a snapshot device_filter only honors site and device_type — there is no tag key. To snapshot a tag-based group, resolve the device IDs by tag yourself and use the bulk config pull endpoint, which accepts an explicit list of device_ids.
Parallel Execution
Snapshot config collection runs concurrently. The collector uses a semaphore sized by the collection.max_concurrent setting (default 5) so a bounded number of SSH sessions run at once. Each device is collected independently with retry; one device failing does not stop the others. The default per-device collection timeout is 30 seconds with 2 retry attempts.
Metadata vs. Connection Operations
Tag updates, credential reassignment, and delete are metadata-only — they update the device row in PostgreSQL without opening any SSH connection and complete almost instantly regardless of device count. Bulk credential test, bulk config pull, and snapshot collection open real SSH connections, so their runtime scales with the target count and the concurrency limit.
Step-by-Step Guide
Test Credentials Across the Fleet
- Decide your target set: omit
device_idsto test every device, or pass an explicit list of device IDs. - Submit
POST /api/admin/devices/bulk/test-credentialswith an optionaltimeout_seconds(default 10). - Read the response summary —
total,succeeded,failed— and the per-deviceresultsarray, each entry reportingsuccessand a human-readablemessage.
Bulk Pull Configs from a Device List
- Build the list of
device_ids(for example all core routers). - Submit
POST /api/admin/config/devices/bulk-pullwith{"device_ids": [...]}. - NetStacks creates a snapshot record named
Bulk Config Pull (N devices)so the run shows up in the Job Monitor, then pulls structured config from each device. - The response returns
succeeded,failed, and anerrorsarray with the failingdevice_idand error string.
Snapshot a Whole Site or Platform
- Create a snapshot with
POST /api/admin/devices/snapshots, supplying anameand adevice_filterusingsiteand/ordevice_type. - Trigger collection with
POST /api/admin/devices/snapshots/{id}/collect. - Collection runs in the background, bounded by
collection.max_concurrent. Track totals and per-device failures through the snapshot record and its backups.
Apply Tags to a Group
- List the target devices (filter by
device_type,site, orsource) and collect their IDs. - For each device, send
PUT /api/admin/devices/{id}with the desiredtagsarray.
Use lowercase, hyphenated tags for consistency — production, core, dc-east, pci. Consistent naming keeps client-side tag filtering predictable across the team. Note that the PUT tags update replaces the array, so include existing tags when appending.
Bulk Delete Devices
- Export the device list first as a backup.
- Resolve the exact device IDs to remove and review the count.
- For each device, send
DELETE /api/admin/devices/{id}.
Deleting a device removes its config backup history along with the record. Download any backups you need to retain before deleting.
Code Examples
List Devices Filtered by Type and Site
# Get Cisco IOS devices at dc-east (limit is capped at 100 per page)
curl -s "http://localhost:3000/api/admin/devices?device_type=cisco_ios&site=dc-east&limit=100" \
-H "Authorization: Bearer ${TOKEN}" | jq '{
total: .total,
devices: [.devices[] | {name, host, device_type, site, source}]
}'
# Response shape:
# {
# "total": 14,
# "devices": [
# {"name": "core-rtr-01", "host": "10.1.0.1", "device_type": "cisco_ios", "site": "dc-east", "source": "manual"},
# ...
# ]
# }Bulk Credential Test
# Test SSH credentials on every device (omit device_ids to test all)
curl -s -X POST "http://localhost:3000/api/admin/devices/bulk/test-credentials" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{ "timeout_seconds": 10 }' | jq '{total, succeeded, failed}'
# Test a specific set of devices and list any failures
curl -s -X POST "http://localhost:3000/api/admin/devices/bulk/test-credentials" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"device_ids": [
"a1b2c3d4-0000-0000-0000-000000000001",
"a1b2c3d4-0000-0000-0000-000000000002"
],
"timeout_seconds": 15
}' | jq '.results[] | select(.success == false) | {device_name, host, message}'Bulk Config Pull from a Device List
# Resolve all Cisco IOS device IDs, then bulk-pull their configs.
# This creates a snapshot named "Bulk Config Pull (N devices)" in the Job Monitor.
DEVICE_IDS=$(curl -s "http://localhost:3000/api/admin/devices?device_type=cisco_ios&limit=100" \
-H "Authorization: Bearer ${TOKEN}" | jq -c '[.devices[].id]')
curl -s -X POST "http://localhost:3000/api/admin/config/devices/bulk-pull" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"device_ids\": ${DEVICE_IDS}}" | jq '{succeeded, failed, errors}'
# Response shape:
# { "succeeded": 12, "failed": 2,
# "errors": [ { "device_id": "...", "error": "connection timed out" } ] }Snapshot All Devices at a Site
# Create a snapshot targeting a site (device_filter only honors site/device_type)
SNAPSHOT_ID=$(curl -s -X POST "http://localhost:3000/api/admin/devices/snapshots" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "dc-east weekly backup",
"description": "Running config of every Cisco IOS device at dc-east",
"snapshot_type": "manual",
"device_filter": { "site": "dc-east", "device_type": "cisco_ios" }
}' | jq -r '.id')
# Trigger background collection (bounded by collection.max_concurrent, default 5)
curl -s -X POST "http://localhost:3000/api/admin/devices/snapshots/${SNAPSHOT_ID}/collect" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{}'Scripted Command Broadcast (per-device loop)
# There is no fleet-wide exec endpoint; loop the per-device exec-command route.
DEVICE_IDS=$(curl -s "http://localhost:3000/api/admin/devices?device_type=cisco_ios&limit=100" \
-H "Authorization: Bearer ${TOKEN}" | jq -r '.devices[].id')
for DEVICE_ID in ${DEVICE_IDS}; do
curl -s -X POST "http://localhost:3000/api/admin/devices/${DEVICE_ID}/exec-command" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{ "command": "show version", "timeout_secs": 20 }' \
| jq '{device: "'${DEVICE_ID}'", success, output}'
doneBulk Update Credential
# Reassign the default credential for every device at a site
NEW_CRED_ID="b2c3d4e5-f6a7-8901-bcde-f12345678901"
DEVICE_IDS=$(curl -s "http://localhost:3000/api/admin/devices?site=dc-east&limit=100" \
-H "Authorization: Bearer ${TOKEN}" | jq -r '.devices[].id')
for DEVICE_ID in ${DEVICE_IDS}; do
curl -s -X PUT "http://localhost:3000/api/admin/devices/${DEVICE_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"default_credential_id\": \"${NEW_CRED_ID}\"}" > /dev/null
echo "Updated: ${DEVICE_ID}"
doneExport Devices as CSV
# Page through the inventory (limit capped at 100) and format the result as CSV
TOTAL=$(curl -s "http://localhost:3000/api/admin/devices?limit=1" \
-H "Authorization: Bearer ${TOKEN}" | jq '.total')
{
echo '"name","host","port","device_type","site","source"'
OFFSET=0
while [ "${OFFSET}" -lt "${TOTAL}" ]; do
curl -s "http://localhost:3000/api/admin/devices?limit=100&offset=${OFFSET}" \
-H "Authorization: Bearer ${TOKEN}" | jq -r '
.devices[] | [.name, .host, (.port|tostring), .device_type,
(.site // ""), (.source // "")] | @csv'
OFFSET=$((OFFSET + 100))
done
} > device-export.csvQuestions & Answers
- Q: What dedicated bulk endpoints does NetStacks provide?
- A: Two device-level batch endpoints plus snapshot collection. Bulk credential test (
POST /api/admin/devices/bulk/test-credentials) SSH-connects to every target and returns per-device success/failure. Bulk config pull (POST /api/admin/config/devices/bulk-pull) pulls structured config from an explicit list ofdevice_idsand records a snapshot. Snapshot collection captures running config across devices matched by site/device_type. Tag, credential, export, and delete actions are done by looping the CRUD endpoints. - Q: Can a snapshot target devices by tag?
- A: No. A snapshot
device_filteronly honorssiteanddevice_type. To snapshot a tag-based group, resolve the device IDs by tag yourself and call the bulk config pull endpoint, which takes an explicitdevice_idslist. - Q: What values can the device source filter take?
- A: The stored values are
manual(created in the UI/API),csv(set by the CSV importer — notcsv_import), andnetbox(NetBox sync). - Q: Is there a fleet-wide "run command on everything" endpoint?
- A: Not on the Controller device API. Command execution is per device via
POST /api/admin/devices/{id}/exec-command; loop over a device ID list to broadcast. Separately, the NetStacks terminal can broadcast a command across open SSH sessions (a local session feature), which is distinct from device-inventory bulk actions. - Q: How many devices can a single list request return?
- A:
GET /api/admin/devicescapslimitat 100. Page withoffsetto enumerate larger fleets, then submit the accumulated IDs to a bulk endpoint. - Q: How is bulk config collection parallelized?
- A: Snapshot collection uses a semaphore sized by the
collection.max_concurrentsetting (default 5). Each device is collected independently with retry (default 2 attempts, 30s timeout); one failure does not abort the rest. - Q: Can I undo a bulk operation?
- A: Tag and credential changes are reversible by issuing another
PUT. Device deletion is permanent and also removes the device's backup history, so export the inventory first.
Troubleshooting
Devices report "No credential configured"
Bulk credential test and snapshot collection skip devices without a default_credential_id and mark them failed with that message. Assign a credential to the device (or, for collection, configure a global collection.default_credential_id) and retry.
Partial failures in bulk config pull or snapshot
Inspect the per-device errors array (bulk pull) or the snapshot record (collection). Common causes:
- Connection timeout — device unreachable or slow. Raise
timeout_secondsfor credential tests, or the collection timeout for snapshots. - Authentication failure — the assigned credential has the wrong username/password or key. Fix it in the vault.
- Wrong device type — the platform driver sends commands the device does not understand. Correct
device_type.
Only 100 devices come back
The list endpoint caps limit at 100. If a bulk action seems to miss devices, you almost certainly did not page through all results — loop with offset until you have collected .total IDs.
Snapshot collection ignores tags
A snapshot device_filter only reads site and device_type; any tag key is silently ignored. Resolve device IDs by tag and use the bulk config pull endpoint instead.
Bulk loop deleted more devices than expected
If you generated the ID list from a filtered query, double-check the filter was applied before iterating. Always print and review the resolved count before sending any DELETE in a loop.
Related Features
Bulk operations build on device management and snapshot features for fleet-wide workflows:
- Adding Devices — Build the inventory (and set
source) before bulk work - Config Snapshots — Snapshot collection and the Job Monitor view for bulk pulls
- Device Types — The
device_typevalues used in filters and snapshots - NetBox Sync — Devices imported with
source=netbox - Credential Vault — Credentials referenced by bulk test and credential reassignment
- Devices API — Full reference for the device CRUD endpoints used in loops