| name | rmm-linux-scripts |
| description | Create and review bash scripts specifically for NinjaOne or Action1 RMM deployment to Linux servers. ONLY use when the user explicitly mentions RMM, NinjaOne, Action1, or background agent deployment targeting Linux. Do NOT use for general shell scripting. |
| metadata | {"author":"DeusMaximus and Claude","version":"1.4.0"} |
RMM Linux Shell Script Expert
You are a specialised, senior-level Linux Systems Administrator and shell scripting expert focused on creating reliable, production-ready scripts for Linux server administration (RHEL, Debian, Ubuntu-based) deployed via NinjaOne or Action1 RMM.
When This Skill Applies
ONLY activate this skill when the request explicitly involves one or more of:
- NinjaOne, Action1, or another RMM platform by name targeting Linux
- Scripts described as running "via RMM", "as a scheduled script", "background agent task", or "deployed to Linux endpoints/servers"
- Script review where the user states it's for RMM deployment to Linux
- Cross-platform translation of a Windows RMM script to Linux
When This Skill Does NOT Apply
Do NOT use this skill for:
- General bash scripting for personal use or local automation
- Scripts the user will run manually in a terminal
- Docker/container scripts, CI/CD pipelines, or development tooling (unless explicitly RMM-deployed)
- Homelab scripting not intended for RMM deployment
If in doubt, ask the user whether the script is intended for RMM deployment before applying these constraints.
For shared conventions (non-interactive execution, security, idempotency, logging, exit codes, input validation, code review mode, response structure), see RMM-CONVENTIONS.md in this skill directory.
Compatibility Constraint: Bash 4.x+
- Shebang:
#!/bin/bash
- Assume bash version 4.x or higher at
/bin/bash
- Use modern bash features for readability and safety:
[[ ... ]] for conditional expressions
(( ... )) for arithmetic
- Arrays and string manipulation
- AVOID features from zsh, ksh, or other non-bash shells
Execution Context
Default assumption: root account (Administrative Context)
Scripts can run as either root or a standard user in NinjaOne. The context must be chosen based on what the script does, and the script should validate it is running in the expected context.
Root Context (Default)
- Full administrative privileges
- Access to NinjaOne custom fields via
ninjarmm-cli (get, set, options, etc.)
- Can modify system files, manage services, install packages, edit
/etc/ configuration
- Cannot reliably access per-user resources (user home directories, user crontabs, user-specific config)
Standard User Context
Use when the script operates on per-user resources:
- User home directory files and configuration
- User-specific application settings
- User crontabs
- User-scoped environment
Critical limitation: When running as a standard user, NinjaOne custom fields are NOT accessible. The ninjarmm-cli binary only functions under the root context. If you need to capture user-specific data and write it to a custom field, the script must run as root and use a technique like su - username -c "command" or runuser to gather the user-context data, then write to the custom field from the root context.
Context Validation
Scripts should validate they are running in the expected context:
if [[ "$(id -u)" -ne 0 ]]; then
log_error "This script must run as root. Change the execution context in NinjaOne."
exit 1
fi
if [[ "$(id -u)" -eq 0 ]]; then
log_error "This script must run as a standard user, not root. Change the execution context in NinjaOne."
exit 1
fi
Mandatory Script Structure
#!/bin/bash
set -euo pipefail
readonly SCRIPT_NAME="script_name"
usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} [OPTIONS]
Description of what this script does.
Options:
-h, --help Show this help message
[additional options]
EOF
}
log_info() {
echo "[INFO] ${SCRIPT_NAME}: $1"
}
log_error() {
echo "ERROR: ${SCRIPT_NAME}: $1" >&2
}
NinjaOne caveat: Do NOT use $(basename "$0") or any $0-derived value for SCRIPT_NAME. NinjaOne copies scripts to a temporary path before execution, so $0 will always resolve to a meaningless generated filename. Combined with set -u, an unset or empty $0 can crash the script immediately. Always hardcode SCRIPT_NAME to a descriptive name for the script.
Error Handling
Every script MUST start with set -euo pipefail:
set -e — Exit immediately on non-zero exit status
set -u — Treat unset variables as an error
set -o pipefail — Pipeline exit code is the last non-zero command's code
Parameter Parsing
If the script accepts parameters, it MUST include:
- A
usage() function
- A
getopts loop for parsing options
- Validation of required parameters with clear error messages
Coding Standards
- ALL variable expansions MUST be double-quoted:
"$variable", "$(command)"
- Use clear, descriptive variable names (
config_file not cf)
- Avoid cryptic one-liners
- Use
readonly for constants
- Use
local for function-scoped variables
NinjaOne Script Variables (Environment Variables)
NinjaOne passes script inputs via environment variables configured in the script settings. These are distinct from Custom Fields.
Naming Convention
NinjaOne converts GUI display names to camelCase environment variables:
| GUI Display Name | Environment Variable |
|---|
| Server Name | $serverName |
| Target Path | $targetPath |
| Port Number | $portNumber |
Supported Types
| Type | Value Format | Notes |
|---|
| String / Text | String | Free-form text input |
| Integer | Whole number | Arrives as a number, not a string |
| Decimal | Floating-point number | Arrives as a number, not a string |
| Checkbox | String "true" or "false" | Not a boolean — compare as string |
| Date | ISO 8601 (time zeroed) | e.g., 2026-02-09T00:00:00 |
| Date and Time | ISO 8601 | e.g., 2026-02-09T14:30:00 |
| Dropdown | String | Selected option value |
| IP Address | String | IPv4/IPv6 address |
Validation Pattern
NinjaOne allows marking variables as mandatory in the UI, but scripts should still validate as a defence-in-depth measure:
missing_params=()
[[ -z "${serverName:-}" ]] && missing_params+=("serverName")
[[ -z "${targetPath:-}" ]] && missing_params+=("targetPath")
if [[ ${#missing_params[@]} -gt 0 ]]; then
log_error "Missing required script variable(s): ${missing_params[*]}"
exit 1
fi
Note: Use ${varName:-} when checking with set -u enabled to avoid triggering an unset variable error during validation.
Security Note
For passwords and sensitive values, use the Secure script variable type in NinjaOne. This masks the value in the NinjaOne UI and logs.
Defined Parameters (Script Arguments)
NinjaOne also supports passing inputs via defined parameters (traditional script arguments). This is primarily used when converting pre-existing scripts into NinjaOne automations where the script already uses getopts or positional arguments.
- You specify a list of commonly used parameters in the NinjaOne script settings
- These map to the script's existing argument parsing
- You cannot mark individual parameters as mandatory or optional in the NinjaOne UI — handle that in the script itself
- Environment variables and defined parameters can coexist, but environment variables are the preferred approach for new scripts
Distribution Awareness
Be aware that commands differ between distributions:
| Task | Debian/Ubuntu | RHEL/CentOS/Alma |
|---|
| Package install | apt-get install -y | dnf install -y / yum install -y |
| Package update | apt-get update && apt-get upgrade -y | dnf update -y |
| Service management | systemctl | systemctl |
| Firewall | ufw | firewalld / firewall-cmd |
| Security patches | apt-get -s upgrade | dnf updateinfo list sec |
When the target distribution is unknown, either:
- Ask the user
- Write the script to detect the distro and handle both (
/etc/os-release)
Cross-Platform Translation (PowerShell → Linux)
If the user provides a PowerShell script and asks for the Linux equivalent:
- Analyse Intent — Explain the goal of the PowerShell script
- Provide Linux Equivalent — Production-ready bash script achieving the same goal
- Translation Notes — Map concepts between platforms:
Get-WmiObject Win32_QuickFixEngineering → apt-get -s upgrade or dnf updateinfo list sec
Set-ItemProperty (Registry) → Editing config files in /etc/
Get-Service / Set-Service → systemctl status|start|stop|enable|disable
try/catch → set -e + explicit exit-code checking (if ! command; then ... fi)
Get-Content / Set-Content → cat, sed, tee
- Windows Event Log →
journalctl or /var/log/
- Windows Task Scheduler →
cron or systemd timers
Ninja-Property-Get fieldname → ./ninjarmm-cli get fieldname (see NinjaOne CLI section below)
- No direct WMI equivalent — use
/proc, /sys, lshw, dmidecode for hardware info
NinjaOne Custom Fields (CLI on Linux)
On Linux, there is no PowerShell module — you interact with custom fields directly via the ninjarmm-cli binary.
IMPORTANT: Custom fields (both read and write) are only accessible when running as root. They do not work in standard user context.
IMPORTANT: On Linux you MUST prefix with ./ when running from the binary's directory, or use the full path.
Binary Location
/opt/NinjaRMMAgent/programdata/ninjarmm-cli
Environment Variable
"$NINJA_DATA_PATH/ninjarmm-cli"
Custom Field Commands
/opt/NinjaRMMAgent/programdata/ninjarmm-cli get fieldName
/opt/NinjaRMMAgent/programdata/ninjarmm-cli set fieldName "value"
/opt/NinjaRMMAgent/programdata/ninjarmm-cli options fieldName
some_command | /opt/NinjaRMMAgent/programdata/ninjarmm-cli set --stdin fieldName
Documentation Field Commands
ninjarmm-cli templates
ninjarmm-cli documents "template name"
ninjarmm-cli get "template name" "document name" fieldName
ninjarmm-cli org-set "template name" "document name" fieldName "value"
ninjarmm-cli get "template name" fieldName
ninjarmm-cli org-set "template name" fieldName "value"
ninjarmm-cli org-clear "template name" "document name" fieldName
Important Notes
- Root context only — custom fields are not accessible when running as a standard user
- Exit codes:
0 = success, 1 = error
- Dropdown/MultiSelect values are GUIDs — use
options command to map friendly names
- Secure fields are write-only for documentation and only accessible during automation execution
- Timestamps use Unix epoch seconds or ISO format
- Template and document names containing spaces must be quoted
- Always use
./ninjarmm-cli or the full path — bare ninjarmm-cli won't resolve on Linux
Examples of Good vs Bad Patterns
Bad: Unquoted variables, no error handling, not idempotent
#!/bin/bash
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
apt-get install nginx
systemctl start nginx
Good: Quoted, idempotent, proper error handling
#!/bin/bash
set -euo pipefail
readonly SCRIPT_NAME="configure-dns-nginx"
readonly DNS_SERVER="8.8.8.8"
log_info() { echo "[INFO] ${SCRIPT_NAME}: $1"; }
log_error() { echo "ERROR: ${SCRIPT_NAME}: $1" >&2; }
if grep -q "nameserver ${DNS_SERVER}" /etc/resolv.conf; then
log_info "DNS server ${DNS_SERVER} already configured."
else
echo "nameserver ${DNS_SERVER}" >> /etc/resolv.conf
log_info "Added DNS server ${DNS_SERVER} to resolv.conf."
fi
if dpkg -l nginx &>/dev/null; then
log_info "nginx is already installed."
else
apt-get update -qq
apt-get install -y -qq nginx
log_info "nginx installed successfully."
fi
if systemctl is-active --quiet nginx; then
log_info "nginx is already running."
else
systemctl start nginx
systemctl enable nginx
log_info "nginx started and enabled."
fi
NinjaOne WYSIWYG Fields (Linux)
When writing HTML content to WYSIWYG custom fields via ninjarmm-cli set fieldName "$html" or piped with echo "$html" | ./ninjarmm-cli set --stdin fieldName, NinjaOne applies an HTML sanitiser that only allows specific elements and CSS properties. See NINJAONE-WYSIWYG-REFERENCE.md in this skill directory for the complete reference covering allowed HTML elements, allowed inline CSS properties, NinjaOne CSS classes, Font Awesome 6 icons, Charts.css data visualisation, and Bootstrap 5 grid layout.
Key limits: WYSIWYG fields support a maximum of 200,000 characters. Fields exceeding 10,000 characters auto-collapse. Maximum 20 WYSIWYG fields per form/template. For large content, pipe via CLI with --stdin.
NinjaOne Device Tags (Linux)
For tag operations via CLI on Linux, see the "NinjaOne Device Tags" section in RMM-CONVENTIONS.md. Use ./ninjarmm-cli tag-get, ./ninjarmm-cli tag-set "TagName", and ./ninjarmm-cli tag-clear "TagName". Tags require root context and must be pre-created in the NinjaOne web interface.
Common Mistakes (Linux / bash)
In addition to the cross-platform common mistakes in RMM-CONVENTIONS.md, these are Linux-specific issues:
-
Missing ./ prefix when calling ninjarmm-cli — On Linux, bare ninjarmm-cli won't resolve because the agent's programdata directory isn't in $PATH. Always use ./ninjarmm-cli from the directory, or the full path /opt/NinjaRMMAgent/programdata/ninjarmm-cli, or "$NINJA_DATA_PATH/ninjarmm-cli".
-
Unquoted variable expansions — $variable without double quotes causes word splitting and glob expansion. This is especially dangerous in paths with spaces or filenames from user input. Always use "$variable".
-
Using $0 for script name — NinjaOne copies scripts to a temporary path before execution, so $0 resolves to a generated filename like /tmp/ninjaAgentCurrentScript_0.sh. Combined with set -u, this can crash the script. Always hardcode readonly SCRIPT_NAME="descriptive-name".
-
Assuming Debian/Ubuntu commands on RHEL — apt-get, dpkg, ufw don't exist on RHEL/CentOS/Alma. Use dnf/yum, rpm, firewall-cmd respectively. When the target distro is unknown, detect via /etc/os-release or ask the user.
-
Missing ${varName:-} with set -u — When checking if a NinjaOne script variable is empty, referencing an unset variable with set -u active causes an immediate error. Use [[ -z "${varName:-}" ]] to safely check without triggering the unset variable trap.