Skip to content

Automation & CI/CD

NetDefense supports two complementary automation patterns: static token authentication for CI/CD pipelines that need non-interactive access, and structured output for scripting and downstream tool integration.

The interactive browser login (ndcli auth login) does not work in headless environments like GitHub Actions or GitLab CI — there is no browser to redirect to. Personal Access Tokens (PATs) solve this: you create a token once on your workstation, store it as a CI secret, and every pipeline run uses it automatically via the NDCLI_TOKEN environment variable.

See the Personal Access Tokens guide for a full overview of token scopes, expiration, and the permission model.

  1. Create a token on your workstation (requires interactive login):

    Terminal window
    ndcli auth token create --name "github-ci" --scope rw --expiry 90d

    Copy the displayed ndpat_... value — it is shown only once.

  2. Add the token as a CI secret. In GitHub Actions: Settings > Secrets and variables > Actions > New repository secret. Name it NDCLI_TOKEN.

  3. Reference the secret in your pipeline using NDCLI_TOKEN: ${{ secrets.NDCLI_TOKEN }} (GitHub) or $NDCLI_TOKEN (GitLab).

  4. Run any ndcli command. The token is picked up automatically — no login prompt.

name: Sync Firewall Config
on:
push:
branches: [main]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Install ndcli
run: |
brew tap netdefense-io/tap
brew trust netdefense-io/tap
brew install ndcli
- name: List devices
env:
NDCLI_TOKEN: ${{ secrets.NDCLI_TOKEN }}
run: |
ndcli device list -f json
- name: Sync configuration
env:
NDCLI_TOKEN: ${{ secrets.NDCLI_TOKEN }}
run: |
ndcli sync apply --org my-org --yes

Tokens have a finite lifetime (90 days by default). To rotate without pipeline downtime:

  1. Create the replacement token locally:

    Terminal window
    ndcli auth token create --name "github-ci-v2" --scope rw --expiry 90d
  2. Update the CI secret with the new token value.

  3. Verify the pipeline passes with the new token.

  4. Revoke the old token:

    Terminal window
    ndcli auth token revoke github-ci --yes

For automated rotation in scripts (e.g. from a privileged rotation system), you can script steps 1 and 4 using JSON output:

#!/usr/bin/env bash
# Requires interactive login — run with a user session, not NDCLI_TOKEN.
set -euo pipefail
OLD_NAME="${1:?Usage: $0 <old-token-name> <new-token-name>}"
NEW_NAME="${2:?Usage: $0 <old-token-name> <new-token-name>}"
# Create the new token (capture the token value from JSON output)
NEW_TOKEN=$(ndcli auth token create --name "$NEW_NAME" --scope rw --expiry 90d -f json | jq -r '.token')
echo "New token created: $NEW_NAME"
echo "Token value: $NEW_TOKEN"
echo ""
echo "Update your CI secret to: $NEW_TOKEN"
echo "Then run: ndcli auth token revoke $OLD_NAME --yes"

Every ndcli command supports multiple output formats via the -f flag. Use -f json to get machine-readable output suitable for jq, Python scripts, or other downstream tools.

FormatFlagUse case
Table-f table (default)Human-readable aligned columns for terminal use.
Simple-f simpleCompact key-value output for quick inspection.
Detailed-f detailedExpanded view with all fields and relationships.
JSON-f jsonMachine-readable output for scripting and automation.
Terminal window
ndcli device list -f json
{
"devices": [
{
"uuid": "e2eb98b8-0ed3-11f1-8792-66a021937fa2",
"name": "fw-hq-primary",
"status": "ENABLED",
"organization": "example-org",
"organizational_units": ["production"],
"version": "2.4.1",
"heartbeat": "2026-02-21T03:17:31Z",
"synced_at": "2026-02-21T03:16:43Z"
}
],
"total": 8
}
Terminal window
# Get names of all devices not synced
ndcli sync status -f json | jq -r '.items[] | select(.in_sync == false) | .device_name'
# Count devices per OU
ndcli device list -f json | jq -r '.devices[].organizational_units[]' | sort | uniq -c
# Get overlay IPs for all VPN members
ndcli network member list corporate-vpn -f json | jq -r '.members[] | "\(.device): \(.overlay_ip)"'

Use org-scoped RO tokens for read-only CI jobs. If a pipeline only reads device lists or sync status, restrict the token: --scope ro --org <your-org>. A compromised RO token cannot modify any configuration.

Use separate tokens per CI system and environment. Create one token per pipeline (e.g. github-ci, gitlab-staging, gitlab-prod). This limits the blast radius of a leak and makes it easy to identify unauthorized use via last_used_at.

Align token expiry with your rotation policy. If you rotate every 90 days, set --expiry 90d. This ensures tokens expire even if rotation is missed.

Monitor last_used_at to find stale tokens. Run ndcli auth token list periodically. Tokens that have never been used or show a very old last_used_at are candidates for revocation.

Fail the pipeline explicitly if the token is missing. At the top of every automation script, check that NDCLI_TOKEN is set and fail loudly if it is not — otherwise the script may fall back to prompting for interactive login and hang.

Terminal window
if [ -z "${NDCLI_TOKEN:-}" ]; then
echo "Error: NDCLI_TOKEN is not set." >&2
exit 1
fi