NetStacksNetStacks

Bulk Operations

Enterprise

Run 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

Controller feature

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-credentials opens an SSH connection to each target device and reports success or failure per device.
  • Bulk config pull — POST /api/admin/config/devices/bulk-pull pulls 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 site and/or device_type and 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

ActionMechanismDescription
Bulk test credentialsPOST /devices/bulk/test-credentialsSSH-connect to each device and report per-device success/failure
Bulk config pullPOST /config/devices/bulk-pullPull structured config from a list of device IDs; creates a snapshot
Snapshot collectionPOST /devices/snapshots + /collectCapture running config across devices matched by site/device_type
Exec command (per device)POST /devices/{id}/exec-commandRun one command on a device; loop over a list for scripted broadcast
Add / remove tagsPUT /devices/{id} loopUpdate the device tags array
Update credentialPUT /devices/{id} loopChange default_credential_id on each device
ExportGET /devices + jqList devices and format the result as CSV/JSON
DeleteDELETE /devices/{id} loopRemove devices from the inventory
Destructive operations

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_ios or arista_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), and netbox (NetBox sync).
List pages are capped at 100

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 filter selection, not snapshots

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

  1. Decide your target set: omit device_ids to test every device, or pass an explicit list of device IDs.
  2. Submit POST /api/admin/devices/bulk/test-credentials with an optional timeout_seconds (default 10).
  3. Read the response summary — total, succeeded, failed — and the per-device results array, each entry reporting success and a human-readable message.

Bulk Pull Configs from a Device List

  1. Build the list of device_ids (for example all core routers).
  2. Submit POST /api/admin/config/devices/bulk-pull with {"device_ids": [...]}.
  3. 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.
  4. The response returns succeeded, failed, and an errors array with the failing device_id and error string.

Snapshot a Whole Site or Platform

  1. Create a snapshot with POST /api/admin/devices/snapshots, supplying a name and a device_filter using site and/or device_type.
  2. Trigger collection with POST /api/admin/devices/snapshots/{id}/collect.
  3. 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

  1. List the target devices (filter by device_type, site, or source) and collect their IDs.
  2. For each device, send PUT /api/admin/devices/{id} with the desired tags array.
Tag naming conventions

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

  1. Export the device list first as a backup.
  2. Resolve the exact device IDs to remove and review the count.
  3. For each device, send DELETE /api/admin/devices/{id}.
Deletes remove backup history

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

filter-devices.shbash
# 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

bulk-test-credentials.shbash
# 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

bulk-pull-configs.shbash
# 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

snapshot-site.shbash
# 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)

broadcast-command.shbash
# 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}'
done

Bulk Update Credential

bulk-update-credential.shbash
# 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}"
done

Export Devices as CSV

export-devices.shbash
# 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.csv

Questions & 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 of device_ids and 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_filter only honors site and device_type. To snapshot a tag-based group, resolve the device IDs by tag yourself and call the bulk config pull endpoint, which takes an explicit device_ids list.
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 — not csv_import), and netbox (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/devices caps limit at 100. Page with offset to 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_concurrent setting (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_seconds for 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.

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_type values 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