Purple+ Desktop

Deployment guide

How to roll out the Purple+ desktop client: a per-user installer that keeps itself up to date, a machine-wide MSI for SCCM, Intune, or Ivanti fleets, and a public release feed for automation. The same signed client in every path.

Where everything lives#

Every Purple+ desktop release is published to the locations below: the download page for people, and the release feed plus installer files for tooling.

Download page For people
Latest-version feed For automation
https://purpleplusinstaller.blob.core.windows.net/releases/latest.yml
Installer files For automation
https://purpleplusinstaller.blob.core.windows.net/releases/<filename>

Note

Always provision the latest version. We usually support one version back, but not always: a release can raise the minimum required client version with immediate effect. The goal is always to pull the newest released build, never to pin to a specific older one.

Choose a deployment model#

Every release ships the same signed client in two packages, built for two management styles.

Criterion Per-user installer Default Machine-wide MSI
Best for Most organizations; people install it themselves or provisioning runs in the user's context Fleets managed with SCCM, Intune, or Ivanti; shared and multi-user machines
Runs as The signed-in user; no admin rights SYSTEM or an admin account
Installs to %LOCALAPPDATA%\Programs\PurplePlus C:\Program Files\<product name>, once for every account
Updates Keeps itself current from the release feed You deploy each new MSI; falling behind can lock people out
File PurplePlus-<version>-x64-Setup.exe PurplePlus-<version>-x64-Setup.msi

Default to the per-user installer: machines stay current with zero effort from IT. Choose the MSI when updates must go through your own change control, or when several people share one machine.

Choosing the MSI means owning the updates

A machine-wide install never updates itself, and we usually support only one version back, sometimes none. When a release requires a minimum client version and your deployed version is below it, people get a blocking "update required" screen and cannot use Purple+ at all until you roll out the newer MSI. Only choose this model with a deployment process that can ship every Purple+ release promptly; see Updates are yours to deploy.

Per-user installer (.exe)#

The default installer: PurplePlus-<version>-x64-Setup.exe. It installs into the signed-in user's profile with no admin rights and keeps itself up to date from the release feed.

Manual install#

Send people to download.letsgopurple.plus: the page detects the latest version, starts the download of the signed installer, and shows the version, release date, and file size with a manual download button in case the browser blocks the automatic one. There is nothing to configure.

Silent install and uninstall#

For scripted and unattended provisioning, the installer takes /S:

PurplePlus-<version>-x64-Setup.exe /S

The uninstaller sits in the install folder and takes the same flag:

"%LOCALAPPDATA%\Programs\PurplePlus\Uninstall Purple+.exe" /S

Scripted provisioning#

Poll the feed for the newest released build and download it when it is newer than what you last provisioned, then run the silent install above. Both scripts do the same thing; pick your shell. The manifest they read is documented in The release feed.

PowerShell
$feed = "https://purpleplusinstaller.blob.core.windows.net/releases"
# The blob is served as application/octet-stream, so Invoke-WebRequest would
# return raw bytes; DownloadString gives a string the regex can read.
$yaml = (New-Object System.Net.WebClient).DownloadString("$feed/latest.yml")

# latest.yml is tiny and predictable, so a simple regex is enough.
$version  = ([regex]'(?m)^version:\s*(\S+)').Match($yaml).Groups[1].Value
$filename = ([regex]'(?m)^path:\s*(\S+)').Match($yaml).Groups[1].Value
$fileUrl  = "$feed/$filename"

# A stagingPercentage below 100 marks a pre-release. When that happens, fall back
# to the last released build: the newest history.json entry whose version
# differs from the pre-release.
$staging = ([regex]'(?m)^stagingPercentage:\s*(\d+)').Match($yaml)
if ($staging.Success -and [int]$staging.Groups[1].Value -lt 100) {
    $history  = Invoke-RestMethod "$feed/history.json"
    $previous = $history | Where-Object { $_.version -ne $version } | Select-Object -First 1
    $version  = $previous.version
    $filename = $previous.filename
    $fileUrl  = $previous.url
}

# Compare against whatever version you last provisioned, then download if newer.
$current = "3.42.0"
if ([version]$version -gt [version]$current) {
    $out = Join-Path $env:TEMP $filename
    Invoke-WebRequest -Uri $fileUrl -OutFile $out
    Write-Host "Downloaded Purple+ $version to $out"
    # Provision $out into your workspace here.
}
Bash / curl
FEED="https://purpleplusinstaller.blob.core.windows.net/releases"

# Pull the version, filename, and release state from the canonical manifest.
YAML=$(curl -fsSL "$FEED/latest.yml")
VERSION=$(echo "$YAML" | grep -E '^version:' | awk '{print $2}')
FILE=$(echo "$YAML" | grep -E '^path:' | awk '{print $2}')
URL="$FEED/$FILE"
STAGING=$(echo "$YAML" | grep -E '^stagingPercentage:' | awk '{print $2}')

# A stagingPercentage below 100 marks a pre-release. Fall back to the last
# released build: the newest history.json entry whose version differs from the
# pre-release. (history.json is real JSON, so this step uses jq.)
if [ -n "$STAGING" ] && [ "$STAGING" -lt 100 ]; then
    PREV=$(curl -fsSL "$FEED/history.json" | jq -c --arg v "$VERSION" 'map(select(.version != $v)) | .[0]')
    VERSION=$(echo "$PREV" | jq -r '.version')
    FILE=$(echo "$PREV" | jq -r '.filename')
    URL=$(echo "$PREV" | jq -r '.url')
fi

echo "Provisioning Purple+ $VERSION"
curl -fSL -o "$FILE" "$URL"

Provisioning context and detection#

Warning

It installs for the account that runs it. The per-user installer needs no admin rights and no elevation, so it lands in the profile of whoever runs it. If your provisioning tool runs the installer as SYSTEM or a service account, run it in the signed-in user's context instead, or it installs into the wrong profile and the user never sees the app. If the tool has a machine-versus-user install setting, choose user. For a true machine-level install, use the MSI instead.

Detecting whether it is installed. A per-user install lands at %LOCALAPPDATA%\Programs\PurplePlus with Purple+.exe inside. If your tool needs a detection rule, check that this path is present rather than matching an exact version: Purple+ keeps itself up to date, so a version-pinned rule goes stale after the app self-updates and can trigger a needless reinstall. Confirm the exact names against a reference install.

Machine-wide MSI#

For SCCM, Intune, or Ivanti deployments we publish a machine-wide MSI next to the .exe: the same signed Purple+ client, installed once for every account on the machine into C:\Program Files\<product name>. Unlike the per-user installer, run this one with administrative rights, typically as SYSTEM from your deployment tool. The filename swaps -Setup.exe for -Setup.msi, in the same releases folder.

Install and upgrade#

msiexec /i PurplePlus-<version>-x64-Setup.msi /qn

Add /l*v install.log to capture a verbose log. Only a per-machine install is supported: do not pass ALLUSERS or MSIINSTALLPERUSER overrides.

Upgrading is the same command with the newer MSI: it replaces the old version in place, and reinstalling the same version is safe too. Uninstall goes by product code, the way any MSI is removed:

msiexec /x {ProductCode} /qn

{ProductCode} changes with every release. Your deployment tool typically reads it straight from the MSI when you package the install; if you need it for a script, the registry DisplayVersion value under that product code's uninstall key also confirms which version is installed.

Scripted download#

Resolve the newest released build from the feed and download its MSI. The script is self-contained; the manifest it reads is documented in The release feed.

PowerShell
$feed = "https://purpleplusinstaller.blob.core.windows.net/releases"
$yaml = (New-Object System.Net.WebClient).DownloadString("$feed/latest.yml")

# Resolve the newest released build; fall back past a pre-release
# (stagingPercentage below 100) to the last released history.json entry.
$version  = ([regex]'(?m)^version:\s*(\S+)').Match($yaml).Groups[1].Value
$filename = ([regex]'(?m)^path:\s*(\S+)').Match($yaml).Groups[1].Value
$staging  = ([regex]'(?m)^stagingPercentage:\s*(\d+)').Match($yaml)
if ($staging.Success -and [int]$staging.Groups[1].Value -lt 100) {
    $previous = (Invoke-RestMethod "$feed/history.json") | Where-Object { $_.version -ne $version } | Select-Object -First 1
    $version  = $previous.version
    $filename = $previous.filename
}

# The MSI ships next to the exe under the same name: swap the extension.
$msiName = $filename -replace '\.exe$', '.msi'
$out = Join-Path $env:TEMP $msiName
Invoke-WebRequest -Uri "$feed/$msiName" -OutFile $out
Write-Host "Downloaded Purple+ $version MSI to $out"

# Import $out into SCCM, Intune, or Ivanti, or install directly:
msiexec /i "$out" /qn /l*v install.log

Updates are yours to deploy#

A machine-wide install never updates itself. Every Purple+ copy on that machine, including any per-user install already there, switches to a notify-only banner that asks people to contact their IT administrator. From that point on, deploying each new release is your job.

Falling behind locks people out

We usually support the current release plus one version back, but not always. When a release raises the minimum required client version and the installed version is below it, everyone on that machine gets a blocking "update required" screen instead of the app: Purple+ is unusable until you deploy the newer MSI. Treat every Purple+ release as a rollout to schedule promptly, not a backlog item.

Because an upgrade replaces files in place, it closes any running Purple+ session, ending active calls, so schedule it in a maintenance window, particularly on machines shared across shifts or sign-ins.

Detection#

Two ways to confirm the MSI is installed, in the order most deployment tools reach for them:

1

Windows Installer / product code

Most tools, SCCM included, can natively detect "this exact MSI is installed" once you import it, since they read the product code straight from the file. Because the product code rotates every release, this rule is scoped to the version you packaged: give each new release its own deployment type revision rather than reusing one across versions.

2

File and registry presence

Check for the install folder and its exe, then read DisplayVersion from that product code's uninstall registry key if you need the exact version string.

Warning

Never compare versions using the MSI's own ProductVersion property. Windows Installer forces it into a plain major.minor.build number, so every prerelease build of a version collapses to the same value (7.0.0-alpha.1, 7.0.0-alpha.2, and the eventual 7.0.0 release all read back as 7.0.0.0). Use the registry DisplayVersion value instead, which keeps the full version string, and always deploy monotonically.

Per-user leftover cleanup#

If a per-user install already exists on the machine, the machine-wide app removes it automatically the first time that Windows account launches Purple+ after the MSI is deployed (until then, that copy keeps working, just also frozen into the same notify-only mode). For an account that never opens the app again, clean up its leftover copy directly, run as that user:

"%LOCALAPPDATA%\Programs\PurplePlus\Uninstall Purple+.exe" /S

The release feed#

The reference for the scripts above. latest.yml is the canonical "what is the newest build" manifest: small, stable, always present, and the same file the Purple+ app uses to update itself, so per-user installs stay current on their own. You only need the feed to provision new installs, not to push updates.

latest.yml The newest-build manifest#

yaml
version: 6.0.0
files:
  - url: PurplePlus-6.0.0-x64-Setup.exe
    sha512: <base64 sha512>
    size: 96118784
path: PurplePlus-6.0.0-x64-Setup.exe
sha512: <base64 sha512>
releaseDate: '2026-06-09T10:00:00.000Z'
stagingPercentage: 100 # 0 while the build is a pre-release; absent also means released

Read version to compare against what you have provisioned, then download the installer from the same releases folder using the path filename. The sha512 values let you verify the download if you want. Each release publishes the MSI alongside the exe under the same name, so swapping -Setup.exe for -Setup.msi on that filename gives the MSI URL.

Warning

Provision only released builds. A build is released when its stagingPercentage is 100 or absent. While it is below 100 (it sits at 0 before release) the build is a pre-release: skip it and provision the last released build from history.json instead. The provisioning scripts above do this automatically.

history.json Previous releases, newest first#

Sits in the same folder, each entry with an absolute url. Read it only for the pre-release case above, to grab the last released build.

json
[
  {
    "version": "6.0.0",
    "filename": "PurplePlus-6.0.0-x64-Setup.exe",
    "url": "https://purpleplusinstaller.blob.core.windows.net/releases/PurplePlus-6.0.0-x64-Setup.exe",
    "date": "2026-06-09T10:00:00.000Z",
    "size": 96118784
  },
  { /* the previous release, and so on (newest first) */ }
]

Frequently asked#

No. The download page and the releases feed (latest.yml and the installer files) are public and anonymous over HTTPS.

  • download.letsgopurple.plus
  • purpleplusinstaller.blob.core.windows.net

Yes. Both the .exe and the MSI are Authenticode code signed. You can verify the signature, and the sha512 values in latest.yml let you verify the exe download itself.

Poll latest.yml on a schedule (hourly or daily is plenty; releases are not more frequent than that) and provision the version it reports. Always provision the latest build. We usually support the current release and the one immediately before it, but a release can require immediate adoption, so never track or pin older versions. One exception: a manifest with a stagingPercentage below 100 is a pre-release; hold off until it is released (see the next question).

Whether the build has been released. A new build first lands on the feed with a stagingPercentage below 100 (it sits at 0 while we finish verifying it); that is a pre-release and should not be provisioned. When we release it, the value becomes 100 or the line is removed.

So provision the latest.yml build only when the field is 100 or absent; while it is below 100, fall back to the last released build from history.json. The download page does this automatically, and the provisioning scripts above show it.

For per-user installs, no: the app updates itself from this same feed, so you only need latest.yml to provision new installs.

For machine-wide (MSI) installs, yes: they never update themselves. You deploy each new version through your own tooling, and the app shows people a notification when an update is waiting on your rollout. Fall too far behind and a minimum-version requirement blocks the client entirely until you update it (see Updates are yours to deploy).

Need a hand with your rollout?

If your deployment relies on a pattern not covered here, or you want us to sanity-check your provisioning against the release feed, open a ticket through the Purple+ support portal and we will think along.