Configures Raritan PDU RADIUS server entries and supporting local user accounts from a shared JSON config file.
- What it does
- What it does not do
- How it works
- Requirements
- Windows setup
- The IP list file
- The RADIUS config file
- Secrets
- Usage
- Command-line options
- Common results
- Troubleshooting
- Exit codes
- Implementation notes
- Safety notes
- Source references
This script stages RADIUS-related configuration on Raritan PDUs.
It can do two related jobs:
- Configure the PDU's RADIUS server entries.
- Create local proxy/supporting user accounts with SNMPv3 settings.
The script reads a list of PDU management IPs from pdus.txt, reads RADIUS/user settings from a JSON config file, logs in to each PDU, and applies the requested configuration.
The default config file is:
radius_config.json
The script is intended to be used before a later authentication-policy change. It prepares the PDUs by staging the RADIUS servers and local accounts, but it does not turn RADIUS login on by itself.
- Does not change the active authentication method
- Does not change authentication order or login policy
- Does not switch the PDU from local login to RADIUS login
- Does not disable local login
- Does not change the admin password
- Does not update firmware
- Does not turn outlets on or off
- Does not reboot the PDU
- Does not change network settings
- Does not configure DNS, NTP, syslog, outlet settings, sensors, or power
- Does not delete users
- Does not modify existing users listed in the config
- Does not store secrets in the JSON config file
- Does not verify that RADIUS logins work after a future policy switch
This script makes real changes in live mode, but those changes are limited to RADIUS server entries and local user creation.
For each PDU IP address:
-
Read the IP list. The script reads
pdus.txtby default, or another file if you pass--ips. -
Read and validate the JSON config. The script validates the requested
radius_serversand/orusersbefore contacting any PDU. -
Reject bad local input early. Examples include missing config sections, duplicate RADIUS servers, duplicate usernames, invalid IP syntax in
pdus.txt, or both skip flags being used. -
Prompt for the PDU admin password. Dry-run still needs this because it logs in and reads current PDU state.
-
In live mode, collect write-side secrets if needed. If RADIUS servers are enabled, the script needs the RADIUS shared secret. If users are enabled, it needs the new local-user password.
-
Log in to each PDU. The default PDU username is
admin. -
Configure RADIUS servers if enabled. In dry-run mode, the script reports that it would set the RADIUS server list. In live mode, it writes the server list and verifies the visible fields afterward.
-
Create local users if enabled. The script checks existing user accounts first. If a configured user already exists, it reports
already_exists. If not, dry-run reportswould_create, and live mode creates the user. -
Verify writes where possible. After writing RADIUS server entries, the script reads back the visible fields. After creating a user, it confirms the username appears in the account list.
-
Report per-PDU results. A failure on one PDU does not hide results from the rest of the batch.
- Python 3.8+
- The
raritanPython package:pip install raritan - HTTPS access, usually TCP 443, from your machine to each PDU management interface
- A PDU username and password with permission to configure RADIUS and create users
- A
pdus.txtfile listing the PDU management IPs - A JSON config file such as
radius_config.json - The RADIUS shared secret for live RADIUS configuration
- The desired password for new local users, if creating users
-
Install Python. Download from python.org/downloads and run the installer, enabling Add python.exe to PATH if offered. Alternatively, the Python install manager via
winget install 9NQ7512CXL7Tor the Microsoft Store also works. -
Open PowerShell. A normal user window is fine; you do not usually need Administrator.
-
Confirm Python works:
py --versionIf that does not work, try:
python --version -
Create a working folder and put the script there:
mkdir C:\pdu-radius cd C:\pdu-radius
-
Create
pdus.txt. See The IP list file. -
Create
radius_config.json. See The RADIUS config file. -
Create and activate a virtual environment:
py -m venv .venv .\.venv\Scripts\Activate.ps1If PowerShell blocks activation with an execution-policy error:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned .\.venv\Scripts\Activate.ps1
Your prompt should now start with
(.venv). -
Install the Raritan package:
python -m pip install --upgrade pip python -m pip install raritan python -c "import raritan.rpc; print('raritan import OK')"
Default filename: pdus.txt, one PDU management IP per line, in the same folder you run the script from unless you pass --ips.
notepad .\pdus.txtExample:
# Example group of PDUs
10.10.5.130
10.10.5.131
10.10.5.132
# Spare PDU
10.10.5.133
Inline comments are also supported:
10.10.5.130 # pdu-row3-a12.example.com
10.10.5.131 # pdu-row3-a13.example.com
Blank lines are ignored. Lines starting with # are ignored. Duplicate IPs are skipped automatically.
Invalid IP addresses fail clearly and include the line number.
Default filename:
radius_config.json
Create it with:
notepad .\radius_config.jsonExample:
{
"radius_servers": [
{
"server": "10.10.5.10",
"auth_port": 1812,
"acct_port": 1813,
"auth_type": "PAP",
"timeout": 2,
"retries": 3
},
{
"server": "10.10.5.11",
"auth_port": 1812,
"acct_port": 1813,
"auth_type": "PAP",
"timeout": 2,
"retries": 3
}
],
"users": [
{
"username": "SVC-RADIUS-ADMIN",
"full_name": "RADIUS service account (admin)",
"role": "Admin"
},
{
"username": "SVC-RADIUS-OPERATOR",
"full_name": "RADIUS service account (operator)",
"role": "Operator"
}
]
}Do not put passwords or shared secrets in this file.
The config can contain:
radius_serversonlyusersonly- both
radius_serversandusers
If both sections exist, the script manages both unless you use --skip-radius or --skip-users.
Supported RADIUS auth types:
| Value |
|---|
PAP |
CHAP |
MSCHAPV2 |
The script rejects duplicate RADIUS server entries and duplicate usernames before contacting any PDU.
The script can use three secrets:
| Secret | Environment variable | CLI option |
|---|---|---|
| PDU admin password | PDU_ADMIN_PASSWORD |
--password |
| RADIUS shared secret | RADIUS_SHARED_SECRET |
--radius-secret |
| New local-user password | PDU_RADIUS_USER_PASSWORD |
--user-password |
Passing secrets on the command line is supported, but not recommended, because command-line arguments may be visible in shell history and process listings.
Prefer interactive prompts or environment variables.
Dry-run still needs the PDU admin password because it logs in and reads current state.
Dry-run does not ask for the RADIUS shared secret.
Dry-run does not ask for the new local-user password.
That is intentional. Dry-run does not write RADIUS settings and does not create users.
In live mode, if the RADIUS shared secret is typed interactively, the script asks for it twice:
RADIUS shared secret
Confirm RADIUS shared secret
If the new local-user password is typed interactively, the script asks for it twice:
New user account password
Confirm new user account password
Values supplied by environment variable or CLI option are trusted as-is.
$env:PDU_ADMIN_PASSWORD = "<pdu-admin-password>"
$env:RADIUS_SHARED_SECRET = "<radius-shared-secret>"
$env:PDU_RADIUS_USER_PASSWORD = "<new-local-user-password>"These environment variables only apply to the current PowerShell session. Close the window when done, and avoid this while screen-sharing or recording.
Run dry-run first:
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --dry-run -vGood dry-run output:
2026-07-05 10:12:04 INFO Processing 1 PDU(s) [DRY RUN, concurrency=1, verify_cert=False]...
2026-07-05 10:12:05 INFO 10.10.5.130: RADIUS - would_set; servers=2
2026-07-05 10:12:05 INFO 10.10.5.130: USER SVC-RADIUS-ADMIN - would_create
2026-07-05 10:12:05 INFO 10.10.5.130: USER SVC-RADIUS-OPERATOR - would_create
2026-07-05 10:12:05 INFO Done. OK=1 Failed=0
The important dry-run results are:
RADIUS - would_set
USER <username> - would_create
USER <username> - already_exists
If visible RADIUS fields already match, dry-run may include this note:
note=visible fields already match; secret cannot be verified and will still be reapplied
That is expected. The RADIUS shared secret cannot be verified by reading it back, so a future live run will still reapply the RADIUS server settings.
After the dry-run output looks correct:
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json -v --log-file .\pdu-radius.logGood live output:
2026-07-05 10:20:04 INFO Processing 1 PDU(s) [LIVE, concurrency=1, verify_cert=False]...
2026-07-05 10:20:05 INFO 10.10.5.130: RADIUS - set; servers=2
2026-07-05 10:20:06 INFO 10.10.5.130: USER SVC-RADIUS-ADMIN - created
2026-07-05 10:20:07 INFO 10.10.5.130: USER SVC-RADIUS-OPERATOR - created
2026-07-05 10:20:07 INFO Done. OK=1 Failed=0
The important live results are:
RADIUS - set
USER <username> - created
USER <username> - already_exists
After the live run, run dry-run again:
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --dry-run -vExpected user result after a successful live run:
USER <username> - already_exists
Expected RADIUS result:
RADIUS - would_set
This may seem odd at first, but it is expected. The script cannot verify the RADIUS shared secret by reading it back from the API, so it treats a future live run as something that would reapply the RADIUS server settings.
Only manage local users and leave RADIUS server entries alone:
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --skip-radius --dry-run -vOnly manage RADIUS server entries and leave users alone:
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --skip-users --dry-run -vThis is invalid:
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --skip-radius --skip-usersThe script rejects it because there is nothing left to do.
- Put one test PDU in
pdus.txt. - Confirm
radius_config.jsonhas the intended RADIUS servers and users. - Dry-run the one PDU.
- If dry-run looks good, run live against the one PDU.
- Dry-run again and confirm local users report
already_exists. - Check the PDU directly if needed.
- Add the remaining PDU IPs to
pdus.txt. - Dry-run again.
- Run live against the remaining PDUs.
- Save the log file for your records.
# Dry-run with the default pdus.txt and radius_config.json
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --dry-run -v
# Live run with a log file
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json -v --log-file .\pdu-radius.log
# Different IP list file
python .\bootstrap_pdu_radius.py --ips .\my-pdus.txt --config .\radius_config.json --dry-run -v
# Different config file
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.example.json --dry-run -v
# Different PDU username
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --username admin --dry-run -v
# Longer timeout
python .\bootstrap_pdu_radius.py --ips .\pdus.txt --config .\radius_config.json --dry-run --timeout 30Keep --concurrency 1, the default, for first use.
| Flag | Default | Description |
|---|---|---|
--ips PATH |
pdus.txt |
IP list file |
--config PATH |
radius_config.json |
JSON config file describing radius_servers and/or users |
--username USERNAME |
admin |
PDU login username |
--password PASSWORD |
(env/prompt) | PDU admin password. Supported but not recommended on the CLI |
--radius-secret SECRET |
(env/prompt) | RADIUS shared secret. Supported but not recommended on the CLI |
--user-password PASSWORD |
(env/prompt) | Password for every new local user in the config. Supported but not recommended on the CLI |
--skip-radius |
off | Do not touch RADIUS server configuration |
--skip-users |
off | Do not create any local user accounts |
--timeout SECONDS |
10 |
Connection timeout for ordinary API calls, per PDU |
--concurrency NUMBER |
1 |
Number of PDUs processed in parallel. Invalid values like 0 or negative numbers are blocked |
--dry-run |
off | Show what would happen, but do not write RADIUS settings or create users |
-v, --verbose |
off | Debug logging |
--log-file PATH |
none | Also write logs to this file |
--no-insecure |
— | Require valid, trusted HTTPS certificates |
--insecure |
on by default | Disable HTTPS certificate verification. This is already the default, since many PDUs use self-signed certs |
| Result | Meaning |
|---|---|
RADIUS - would_set |
Dry-run mode. The script would write the configured RADIUS server entries during a live run |
RADIUS - set |
Live mode. The script wrote the configured RADIUS server entries and verified visible fields afterward |
RADIUS - setRadiusServers_failed_ret_<N> |
The PDU rejected the RADIUS server settings |
RADIUS - verify_failed |
RADIUS settings were written, but the visible fields read afterward did not match the desired values |
USER <username> - already_exists |
The account already exists. The script did not modify it |
USER <username> - would_create |
Dry-run mode. The account does not exist and would be created during live mode |
USER <username> - created |
Live mode. The account was created and verified afterward |
USER <username> - role_not_found:<role> |
The requested role name was not found on the PDU |
USER <username> - user_already_exists |
The create call reported that the user already exists |
USER <username> - password_too_short |
The new local-user password is shorter than the PDU allows |
USER <username> - password_too_long |
The new local-user password is longer than the PDU allows |
USER <username> - password_empty |
The new local-user password was rejected as empty |
USER <username> - password_has_control_chars |
The new local-user password contains disallowed control characters |
USER <username> - password_needs_lowercase |
The new local-user password needs a lowercase letter |
USER <username> - password_needs_uppercase |
The new local-user password needs an uppercase letter |
USER <username> - password_needs_numeric |
The new local-user password needs a numeric character |
USER <username> - password_needs_special |
The new local-user password needs a special character |
USER <username> - password_too_short_for_snmp |
The password is too short for the PDU's SNMP requirements |
USER <username> - username_invalid |
The PDU rejected the username |
USER <username> - verify_failed_not_found_after_create |
The create call returned success, but the username was not found afterward |
ERROR - HTTP Error 401 / 403 |
The username or password is wrong, or the account does not have permission |
ERROR (connection timeout / refused / DNS failure) |
The script could not reach the PDU |
| Problem | Fix |
|---|---|
No IPs found in pdus.txt |
Make sure pdus.txt exists in the folder you are running from, with one IP per line |
invalid IP address |
Check pdus.txt for a typo. The script reports the line number |
no radius_servers defined |
Add a radius_servers section to radius_config.json, or use --skip-radius if you only want to manage users |
no users defined |
Add a users section to radius_config.json, or use --skip-users if you only want to manage RADIUS servers |
Both --skip-radius and --skip-users were given |
Remove one of the skip options. Using both leaves nothing to do |
unknown auth_type |
Use one of PAP, CHAP, or MSCHAPV2 |
duplicate radius server |
Remove the duplicate server from radius_config.json |
duplicate username |
Remove the duplicate username from radius_config.json |
RADIUS shared secret and confirmation do not match |
Re-run the live command and type the same shared secret both times |
New user account password and confirmation do not match |
Re-run the live command and type the same new local-user password both times |
python is not recognized / py is not recognized |
Python is not installed correctly or is not on PATH — reinstall and enable the PATH option |
No module named raritan |
Activate your virtual environment and run python -m pip install raritan |
| Connection timed out / refused / no route to host | Check the IP, that the PDU is online, that your network can reach it, and firewall rules for TCP 443 |
HTTP Error 401 or 403 |
The username or password is wrong, or the account does not have permission |
RADIUS - setRadiusServers_failed_ret_<N> |
The PDU rejected the RADIUS server settings. Check the config values and confirm this PDU supports the settings being sent |
RADIUS - verify_failed |
The script wrote RADIUS server settings, but the visible fields read afterward did not match. Check the PDU directly and review the log |
USER <username> - role_not_found:<role> |
The role name in radius_config.json does not exist on the PDU. Fix the role name or create the role separately |
USER <username> - password_needs_uppercase / _numeric / _special / too_short |
The new local-user password does not meet the PDU password policy. Choose a stronger password and rerun |
USER <username> - verify_failed_not_found_after_create |
The create call returned success, but the account did not appear afterward. Check the PDU directly and review the log |
| Some PDUs changed, others failed | Review the failed IPs printed at the end, fix the issue, and rerun dry-run first |
| Code | Meaning |
|---|---|
0 |
All PDUs processed successfully |
1 |
Local setup/input problem, such as invalid IP syntax, no IPs, invalid config, both skip flags being used, invalid concurrency, or interactive secret confirmation mismatch |
2 |
One or more PDUs failed |
130 |
Interrupted with Ctrl+C |
The script uses the Raritan Python package and the Xerus JSON-RPC API.
For login verification, it uses a small authenticated user API call:
usermgmt.User(...).getInfo()It intentionally does not use:
pdumodel.Pdu(...).getMetaData()because that call can fail on some tested PDU/API/library combinations while decoding unrelated fields.
The script uses these Raritan resource IDs:
/auth/radius RADIUS manager
/auth/role Role manager
/auth/user User manager
/auth/user/<username> User login check
The RADIUS shared secret cannot be reliably read back from the API.
Because of that, live mode always calls setRadiusServers() when RADIUS servers are enabled for the run. It does this even if the visible fields already match.
This avoids a false success case where the server IPs, ports, auth type, timeout, and retries match, but the shared secret is wrong or stale.
The script creates configured local users if they do not already exist.
If a user already exists, the script reports already_exists and does not modify that user.
If a user does not exist, dry-run reports would_create.
In live mode, the script creates the user and then verifies that the account name appears afterward.
If a configured user specifies a role, the script resolves the role name through the PDU role list.
Role lookup happens before dry-run reports would_create, so typoed role names should be caught in dry-run.
When creating users, the script enables SNMPv3 settings.
Current defaults:
| Setting | Value |
|---|---|
| Security level | AUTH_PRIV |
| Authentication protocol | SHA1 |
| Privacy protocol | AES128 |
| Authentication passphrase | Same as user password |
| Privacy passphrase | Same as authentication passphrase |
These defaults are kept in the script rather than exposed as command-line flags.
- Run
--dry-runfirst, every time, especially against an unfamiliar batch of PDUs. - Keep
--concurrency 1for first use. - Use one test PDU first.
- Keep
pdus.txtlimited to the intended PDUs. - Keep
radius_config.jsonlimited to the intended RADIUS servers and users. - Remember that this script makes real changes in live mode.
- Remember that this script does not enable RADIUS login by itself.
- Do not put passwords or shared secrets in
radius_config.json. - Do not paste real passwords or shared secrets into tickets, chat, email, or screenshots.
- Prefer interactive prompts or environment variables over CLI secret arguments.
- Close PowerShell after using secret environment variables.
- Save a log file for audit records.
- Review log files before sharing them. They should not contain passwords or shared secrets, but they may contain device IPs, usernames, RADIUS server IPs, and other infrastructure-identifying details.