Skip to content

Variables

Variables allow you to parameterize snippet content so the same snippet can produce different configurations for different devices or OUs. Inside a snippet’s JSON content, you can use ${variable_name} placeholders that are resolved at sync time.

Variables are defined at multiple levels, and more specific scopes override broader ones:

PriorityScopeMeaning
1 (lowest)OrganizationThe default value for all devices in the organization.
2OUOverrides the organization value for all devices in a specific OU.
3TemplateOverrides the value for all snippets within a specific template.
4 (highest)DeviceOverrides all other values for a specific device.

For example, if you define ${listen_port} at the organization level as 51820, you could override it to 52830 for a specific OU’s devices, or to 53840 for one particular device — all while using the same snippet definition.

Consider a snippet that allows DNS queries. Instead of hardcoding the DNS server addresses, you can use a variable:

{
"enabled": true,
"action": "pass",
"interface": "lan",
"direction": "in",
"ipprotocol": "inet",
"protocol": "TCP/UDP",
"source_net": "any",
"destination_net": "${dns_server}",
"destination_port": "53",
"description": "Allow DNS to ${dns_server}"
}

Then define the variable at different scopes:

Terminal window
ndcli variable org create dns_server 8.8.8.8

This sets the default DNS server for all devices in the organization. Now override it for the production OU to use an internal resolver:

Terminal window
ndcli variable ou create production dns_server 10.0.1.53

And for a specific branch device that needs a local DNS:

Terminal window
ndcli variable device create fw-branch-austin dns_server 10.50.1.53

At sync time, the variable is substituted before the configuration reaches the device:

  • fw-hq-primary (production OU) resolves ${dns_server} to 10.0.1.53
  • fw-branch-austin (branch-offices OU, device override) resolves to 10.50.1.53
  • fw-guest-lobby (guest-networks OU, no override) resolves to 8.8.8.8 (org default)

A value reaches the device exactly as you entered it. NetDefense escapes it to fit the snippet’s JSON, so quotes, backslashes, and line breaks are safe: a password containing " or \ works, and a multi-line value such as a certificate or an SSH key arrives with its line breaks. A value always stays a single string — it can’t add keys or list entries to the snippet.

Escape sequences are not interpreted: a value typed as \n or \" reaches the device as those two characters, not as a line break or a quote. If a value relied on that, enter the real character instead — for a line break, store the value from a file with --value-stdin.

Secret variables are the right place for passwords, API keys, and pre-shared keys that snippets need — reference them as ${name}, the same as any other variable; NetDefense does not scan snippet content for secrets, so anything sensitive has to go through a variable instead of sitting in a snippet’s JSON body. See Content Format for the snippet-side half of this.

Some variables carry sensitive content — API tokens, shared secrets, anything you want substituted into snippets but never read back. Add --secret when creating the variable at the organization scope, and keep the value off the command line with --value-stdin rather than passing it as an argument — a literal argument lands in shell history and is visible to other users on the host via ps (--value-stdin and the no-echo prompt need ndcli 1.35.0 or later):

Terminal window
ndcli variable org create syslog_token --secret --value-stdin < ./syslog-token.txt

Exactly one trailing newline is trimmed. Leave off both the value argument and --value-stdin to be prompted interactively instead — the input isn’t echoed to the terminal.

At sync time a secret behaves exactly like a regular variable: its value is substituted into every snippet that references it, and the rendered configuration reaches the device unchanged. The difference is on the read path — the web UI, ndcli variable org describe, and every API response return a redacted placeholder instead of the cleartext. Once written, the value cannot be retrieved.

To rotate a secret, set a new value at the same name the same way:

Terminal window
ndcli variable org set syslog_token --value-stdin < ./new-syslog-token.txt

The --secret flag itself is only available at the organization scope, but an override at a narrower scope (OU, template, or device) of an already-secret organization variable inherits the secret handling automatically, and --value-stdin works at every scope — see the variable command reference.

Every variable value records whether a Superuser saved it, separately at each scope. That decides whether a snippet that grants administrator rights can use it:

  • A USER or GROUP snippet with elevated content reaches a device only if a Superuser saved the snippet and the value of every variable it uses. The value that counts is the one that wins for that device — an OU, template, or device override, not just the organization default.
  • A ZABBIX_USERPARAMETER or ZABBIX_SETTINGS snippet needs a Superuser-saved value for each variable that can decide its command fields.
  • A value can’t add elevated content its snippet doesn’t already carry, whoever saved it: a USER snippet with "scope": "${account_scope}" builds while account_scope holds user, and fails with VARIABLE_ELEVATES_SNIPPET once it’s set to system.
  • Once a Superuser saves a secret variable, every snippet that uses it, whatever its type, needs Superuser clearance too — otherwise the device’s build fails with SECRET_VARIABLE_CLEARANCE. A secret last saved by a Read-Write member, or before NetDefense recorded whether a Superuser saved it, doesn’t trigger this.

To record a Superuser’s save on an existing value, a Superuser signed in interactively sets it again. The same value is fine:

Terminal window
ndcli variable org set backup_role --value operators
ndcli variable device set fw-branch-01 backup_role --value operators
ndcli variable org set svc_backup_password --value-stdin < ./svc-backup-password.txt

A secret’s value can’t be read back, so it has to be entered again. Changing only the description doesn’t count, and neither does a save through a personal access token, which never carries Superuser clearance. In the web app, open the variable or the override with Edit and save it — retyping the value of a secret.

If a device belongs to multiple OUs that define the same variable with different values, NetDefense detects the conflict and prevents the sync.

Terminal window
# List all variables across scopes
ndcli variable overview
# Create variables at each scope
ndcli variable org create <name> <value>
ndcli variable ou create <ou-name> <name> <value>
ndcli variable template create <template-name> <name> <value>
ndcli variable device create <device-name> <name> <value>