Pardon
Contents

Deploying with an RMM or Intune

Pardon has no agent and no server. Whatever already runs scripts on your machines (an RMM, Intune, a GPO scheduled task) copies the exe down, runs it with a campaign, and records the exit code. This page covers the parts that are different when you do that across a fleet.

Start from a template. templates/ (reference coming soon) has an install script, a wrapper that runs a campaign and passes the result back, and ready-made versions for NinjaOne, Datto RMM, Atera, ConnectWise Automate, Intune and a plain scheduled task. The rest of this page explains what those scripts are doing.

Run as SYSTEM

RMMs and Intune normally run scripts as SYSTEM, which has no desktop of its own. Pardon handles that for you:

  1. The SYSTEM process finds every signed-in user's session (console and Remote Desktop).
  2. It starts a copy of itself inside each session, as that user, so the card appears on their screen.
  3. Each copy reports its answer back to the SYSTEM process.
  4. The SYSTEM process combines them into one result and exits with one code.

You don't pass anything special for this; Pardon notices it is SYSTEM. If nobody is signed in, the result is no_session, exit 6 (reboot campaigns follow no_user_action instead; see Reboot campaigns).

One result for many users

On a shared machine, the combined result has a top-level outcome plus one entry per person:

{
  "schema": "result/v1",
  "campaign_id": "aug-security-reboot",
  "type": "reboot",
  "host": { "name": "TS-APP-07", "domain": "LAKESIDE", "os": "Windows Server 2022", "os_build": "20348.2582", "tool_version": "0.1.0" },
  "context": "system",
  "outcome": "deferred",
  "exit_code": 4,
  "results": [
    { "user": "LAKESIDE\\alice", "outcome": "deferred", "elapsed_ms": 17240, "detail": { "defer": { "chosen_label": "1 hour", "chosen_minutes": 60, "defers_remaining": 2 } } },
    { "user": "LAKESIDE\\bob", "outcome": "dismissed", "elapsed_ms": 8110, "detail": {} }
  ]
}

The top-level outcome is the most significant answer anyone gave, in this order: reboot_initiated, deferred, acknowledged (or a survey or ping submitted), dismissed, timeout. So "did anyone see and accept this?" is answered by the exit code alone, and the results list has the detail per person. The results reference (reference coming soon) describes the shape in full.

Machine facts with every result

Add "dex_snapshot": true to any campaign and each result carries a dex block of facts about the machine:

"dex": { "uptime_seconds": 412300, "pending_reboot": false, "free_disk_gb": 112.4, "ram_gb": 16, "os_build": "22631.4169", "last_boot": "2026-10-23T19:30:31Z" }

It turns every campaign into a light health check: which machines haven't restarted in weeks, which are nearly out of disk, which are still on an old build. A combined SYSTEM result has one snapshot for the machine at the top level as well as one per person, and the snapshot is attached even when nothing was shown (suppressed or no_session), so a machine that was skipped still reports its uptime.

These are facts about the machine, not the person. anonymous removes the username from a result but leaves the host name and these facts, so an anonymous answer with a snapshot can still be traced to its machine. Leave dex_snapshot off for surveys where that matters.

The SYSTEM process waits up to 30 minutes for people to answer. Reboot prompts close sooner if prompt_timeout_seconds is shorter.

A script for your RMM

The templates (reference coming soon) do this for you. If you'd rather write your own: RMM script boxes are hard on quotes and line breaks, so the reliable pattern is to keep the campaign as a file next to the exe, or to pass it base64-encoded:

$exe  = 'C:\Program Files\Pardon\pardon.exe'
$json = @'
{
  "schema": "campaign/v1",
  "campaign_id": "wifi-upgrade-2026-08",
  "type": "toast",
  "title": "Wi-Fi upgrade Thursday evening",
  "body": "Office Wi-Fi restarts at 8:00 PM for about 10 minutes.",
  "buttons": [ { "id": "ack", "label": "Got it", "style": "primary" } ],
  "branding": { "org_name": "Lakeside IT", "accent": "#1e3a5f" }
}
'@
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($json))

& $exe show --payload "base64:$b64" --result-file 'C:\ProgramData\Pardon\results\wifi-upgrade.json'
exit $LASTEXITCODE

Pass the exit code through (exit $LASTEXITCODE) so the RMM records Pardon's answer rather than the wrapper's. If your RMM shows script output, the JSON line on stdout is there too; if not, --result-file leaves a copy on the machine for a later script to collect.

No RMM at all? A scheduled task and a campaign on a web server do the same job; see Hosted campaigns.

Intune

Any Intune feature that runs a PowerShell script as SYSTEM works, for example a Remediation, or a Win32 app whose install command calls Pardon. Ship pardon.exe inside the package or copy it from a share in the script, then call it as above. Intune keeps the exit code of the script.

For reboot campaigns on a schedule, a Remediation that runs every hour or so is a natural fit: Pardon's own memory of deferrals means running it often is harmless (deferred machines just exit 7 until their time is up).

Before you go wide

  • Run pardon.exe selftest on a few representative machines. webview2.present: false means the first real run will install WebView2, which needs outbound HTTPS to Microsoft.
  • Try each campaign with --dry-run to catch JSON mistakes (exit 11) without anyone seeing anything.
  • Send it to yourself, then to IT, then to everyone.
  • Give each campaign a fresh campaign_id, and set expires_at on announcements so a machine that was off for a week doesn't show last week's news.