PowerShell install script
Install-PermitUSB.ps1 is the recommended way to install, upgrade, and uninstall PermitUSB on Windows endpoints. It wraps the MSI with the bits msiexec alone can't do: prerequisite checks, friendly error messages, automatic .NET 10 Desktop Runtime install, distinct exit codes for fleet-deploy branching, and a purpose-built upgrade path that stops the running agent and tray before swapping binaries.
For raw msiexec usage (GPO software installation, SCCM package commands, embedded-in-MDT sequences), see the MSI installer page instead.
Quickstart
Get the exact command - token pre-filled - from the dashboard's Enrollment page. Roughly:
# From PowerShell as Administrator, in the folder you saved the script to:
powershell -ExecutionPolicy Bypass -File .\Install-PermitUSB.ps1 -TenantToken '<token>'We launch via powershell.exe with -ExecutionPolicy Bypassso the command works the same on any machine regardless of its execution policy - that argument applies only to that one invocation and doesn't change your system-wide policy. The script is EV code-signed, so on a RemoteSigned box it would run without Bypass too.
What the script does
- checks for admin elevation + 64-bit Windows up front
- probes for the .NET 10 Desktop Runtime via
dotnet --list-runtimesat the arch-specific path (C:\Program Files\dotnet\dotnet.exeon x64,C:\Program Files\dotnet\x64\dotnet.exeon ARM64) and, if missing, downloads + installs it silently from the official Microsoft URL - downloads
PermitUSB.msiusing your enrollment token, OR uses a local copy next to the script if you pass-LocalMsi - for upgrades (
-Upgrade), stops the agent service and taskkills the tray before runningmsiexecso the install doesn't fight locked files - runs
msiexecwith the token, server, and optional endpoint group - prints a
==>step for each phase and writes a fullmsiexeclog to%TEMP%\PermitUSB-install.logon failure
Parameters
- -TenantToken - the enrollment token from the dashboard. Required for fresh installs and for upgrades that fetch the MSI from the dashboard (the download endpoint is token-keyed). Optional when combined with
-Upgrade -LocalMsi- the existing install's enrollment persists in%ProgramData%\PermitUSB\across MajorUpgrade. - -Server - agent-channel URL. Defaults to
https://agent.permitusb.com. Pass only when testing against a non-prod deployment. On upgrades, the script only forwards this tomsiexecif you explicitly set it, so a customer on a non-prod server doesn't get silently re-pointed at prod. - -EndpointGroup- optional. Pre-assigns the endpoint to a named group. Matched case-insensitively against the tenant's groups; unrecognized names fall back to the default group.
- -InstallerBaseUrl - where to fetch the MSI from. Defaults to
https://permitusb.com. Override if you're hosting an internal mirror. - -SkipPrereqs- skip the .NET 10 prereq check + auto-install. Useful when the runtime is being deployed via a separate channel and you want this script to bail loudly if it somehow isn't there yet.
-Upgradeimplies this. - -LocalMsi - use a
PermitUSB.msisitting next to the script instead of downloading from the dashboard. See Offline / vetted installs below. - -MsiPath
<path>- explicit path to the MSI. Use this when the MSI is somewhere other than the script's folder - a network share, a fleet-deploy staging path that doesn't co-locate files, etc. UNC paths work (\\fileserver\share\PermitUSB.msi). Mutually exclusive with-LocalMsi. - -Upgrade - in-place upgrade of an existing install. See Upgrades below.
Modes
Fresh install
powershell -ExecutionPolicy Bypass -File .\Install-PermitUSB.ps1 -TenantToken '<token>'The default. Downloads the MSI by token, installs prerequisites, runs msiexec. Agent enrolls within a minute and appears in the dashboard's Endpoints page as "active".
Offline / vetted installs (-LocalMsi)
For air-gapped fleets, SCCM/Intune packaging, and admins who want to vet the binary before deploying. Put PermitUSB.msi and Install-PermitUSB.ps1 in the same folder, then:
powershell -ExecutionPolicy Bypass -File .\Install-PermitUSB.ps1 `
-TenantToken '<token>' -LocalMsiThe script resolves $PSScriptRoot to find the MSI alongside it and prints the MSI's ProductVersion for sanity check before installing. The script must be on disk for this to work - piping it through irm | iex leaves $PSScriptRoot empty and the script fails fast with a clear message.
The local MSI file is not deleted after install - you brought it, you keep it.
Upgrades (-Upgrade)
For in-place upgrades of an existing install. Skips the .NET prereq check (the runtime is already there), drops the -TenantToken requirement (when combined with -LocalMsi - see below), and explicitly stops the agent service and taskkills the tray before msiexec runs.
# Online upgrade (downloads the current MSI via token):
powershell -ExecutionPolicy Bypass -File .\Install-PermitUSB.ps1 -Upgrade -TenantToken '<token>'
# Fully offline upgrade (uses local MSI, no token needed):
powershell -ExecutionPolicy Bypass -File .\Install-PermitUSB.ps1 -Upgrade -LocalMsiWhy pre-stop? The MSI itself already declares WixCloseApplications for the tray and ServiceControl Wait="yes" for the service, which usually works. WixCloseApplications sends WM_CLOSE only - a tray process that's hung or ignoring the message keeps a handle on its own EXE and the install bails with exit code 1603 / locked-file. The wrapper using taskkill /F-equivalent (Stop-Process -Force) handles that failure mode.
What survives the upgrade. Everything in %ProgramData%\PermitUSB\ - agent.json, credentials.bin, events.db, policy.bin. The endpoint stays enrolled, keeps its identity, keeps its group assignment, keeps its event queue. On upgrades the script omits TENANT_TOKEN/SERVER/ENDPOINT_GROUP from msiexec unless you explicitly set them, so MajorUpgrade preserves the existing config rather than overwriting it with defaults.
Environment variables
For fleet-deploy wrappers (Intune install scripts, SCCM packages, Ansible tasks) that prefer environment configuration to argv:
PERMITUSB_TOKEN- fallback for-TenantTokenPERMITUSB_SERVER- fallback for-ServerPERMITUSB_ENDPOINT_GROUP- fallback for-EndpointGroup
$env:PERMITUSB_TOKEN = '<token>'
irm https://permitusb.com/install.ps1 | iexExit codes
Useful when wrapping the script in an Intune install_script.ps1, SCCM detection clause, or Ansible task. The script sets:
0- installed cleanly (or 3010 frommsiexecinternally, surfaced as 0 with a reboot-requested note).2- no-TenantToken/$env:PERMITUSB_TOKENprovided (and not-Upgrade -LocalMsi).3- not running as Administrator.4- 32-bit Windows.5- .NET runtime install failed (check Windows installer logs).6- MSI download failed (token invalid / expired / network).7--LocalMsicouldn't find a usable MSI (no script-on-disk, orPermitUSB.msimissing from the script's folder).- else -
msiexec's own exit code; check%TEMP%\PermitUSB-install.log.
Inspect before running
The script is ~250 lines of readable PowerShell. Download it from /Install-PermitUSB.ps1, open it in a text editor, read it, then run it. It's EV code-signed (verified publisher: JJMK Studios, LLC) - confirm the publisher with Get-AuthenticodeSignature before you trust it.
Reset / uninstall
For removing PermitUSB cleanly, including the data directory and bootstrap registry, see the Reset page - Reset-PermitUSB.ps1 is a separate script that handles that side of the lifecycle.