ImmyBot Integration · Platinum Technology

ConnectWise Platform — Deployment & Operations Guide

How to install, configure, and troubleshoot the ImmyBot integration for ConnectWise Platform. Covers the integration script, the software package, the underlying API contract, and the trade-offs we made.

1. Overview

This integration connects ImmyBot to ConnectWise Platform (formerly known as Continuum/Command/ASIO) so that:

It is built as a self-contained ImmyBot Dynamic Integration script — one PowerShell file, no external module — paired with a software package consisting of detection, dynamic-versions, install, uninstall, and test scripts.

Regional note. This deployment targets the Australia region (openapi.service.auplatform.connectwise.com). The integration works regionally — only the API Endpoint URL field changes for other regions (EU/US).

2. Prerequisites

2.1 ConnectWise Platform side

2.2 Installer MSI hosting

You must host the barebone MSI yourself. ConnectWise Platform's generic download URL uses a redirect chain that ImmyBot's downloader cannot follow. You need to download the MSI once, upload it to your own storage, and paste the direct-download URL into the Get-AsioAgentDownloadLink.ps1 script.

  1. Download the current generic MSI from https://setup.auplatform.connectwise.com/windows/BareboneAgent/32/ITSagent/MSI/setup (use a browser or curl -L to follow redirects).
  2. Upload it to your own storage — Azure Blob Storage, AWS S3, a public file share, or any location that serves a direct-download URL.
  3. Open software/Get-AsioAgentDownloadLink.ps1 and replace the placeholder $URL value with your direct-download URL.

Recommend refreshing the hosted MSI on a quarterly cadence, or whenever ConnectWise Platform announces an agent update. The agent self-updates after install, so a slightly stale bootstrapper is not harmful — but very old versions may fail to bootstrap.

2.3 ImmyBot side

2.4 Files

FileWhere it goesPurpose
ConnectWiseRMM-Integration.ps1Integrations → New Dynamic IntegrationThe integration itself.
Detect-Asio.ps1Software → Custom Detection ScriptReturns 1.0.0 when the agent is installed, running, and registered. Returns $null otherwise.
Get-AsioAgentDownloadLink.ps1Software → Dynamic VersionsReturns the fixed version + generic MSI URL.
Install-Asio.ps1Software → Install ScriptFetches install token, runs the MSI with TOKEN= and SYSTEM=desktop.
Uninstall-Asio.ps1Software → Uninstall ScriptUninstalls and cleans up residual services and registry keys.
Test-Asio.ps1Software → Test ScriptVerifies the agent is healthy after install (up to 5 minutes).

3. Installing the integration

  1. In ConnectWise Platform, generate a Client ID and Client Secret with the scopes listed in §2. Copy the secret immediately — it is only shown once.
  2. In ImmyBot, go to Integrations → New Dynamic Integration.
  3. Paste the contents of ConnectWiseRMM-Integration.ps1 as the integration script. Give it a recognisable name (e.g. ConnectWise Platform).
  4. Configure the three integration parameters:
    ParameterValue (AU region)
    API Endpoint URLhttps://openapi.service.auplatform.connectwise.com
    Client IDFrom Platform key generation
    Client SecretFrom Platform key generation
  5. Save and initialise. The integration's HealthCheck performs an OAuth token exchange and one companies read — if it goes Healthy, the credentials and scopes are confirmed good.

If HealthCheck fails with a token error, see Troubleshooting. The most common cause is a missing scope or a secret that wasn't copied completely from the ConnectWise Platform UI.

4. Installing the software package

Create a new Software entry in ImmyBot called ConnectWise Platform (or similar). Then configure each section:

4.1 Detection

The detection script returns a fixed 1.0.0 when the ConnectWise Platform agent is installed, the ITSPlatform service is running, and the registry contains a populated privateendpointid (proving the agent is registered with ConnectWise Platform). Anything else returns $null — meaning ImmyBot will treat the machine as "not installed" and remediate.

4.2 Dynamic Versions

This script returns a fixed version of 1.0.0 paired with a barebone MSI URL that you must configure (see §2.2). Both detection and desired version agree on 1.0.0, so once installed, the machine reports compliant and stays compliant — the agent updates itself in the background.

4.3 Agent Integration

This is the link that lets the install script call Get-IntegrationAgentInstallToken. Without it, installs fail with "Get-IntegrationAgentInstallToken returned nothing."

4.4 Install Script

Install Script: paste Install-Asio.ps1. Repair Strategy: Uninstall/Install (or Install Over — either works because detection is binary).

4.5 Uninstall Script

Uninstall Script: paste Uninstall-Asio.ps1.
Set the Detection String parameter to: SaazOnDemand|ITSPlatform

The script uses ImmyBot's Detect-Software with this regex to find the installed product entry and parse its UninstallString. After uninstall, a cleanup block removes the ITSPlatform and ITSPlatformManager services and the registry keys, which the MSI sometimes leaves behind.

4.6 Test Script

The test loops for up to 5 minutes after install, retrying every 15 seconds and attempting to start the service if it's not running yet. It only passes when both the service is Running and privateendpointid is populated.

5. Mapping tenants to ConnectWise Platform companies

After the integration goes Healthy, ImmyBot pulls the ConnectWise Platform company list. Go to Integration → Clients tab and map each ImmyBot tenant to its corresponding ConnectWise Platform company.

Why this matters. The install-token capability uses the mapped company ID to generate a tenant-scoped install token. If a tenant isn't mapped, the install script throws "GetTenantInstallToken received no mapped company id."

Tenants only need to be mapped once. Re-mapping is harmless. Listing additional companies that you don't intend to deploy to is fine — only mapped tenants are touched.

6. Capabilities provided

ImmyBot interfaceWhat it doesAPI endpoint
ISupportsListingClientsLists ConnectWise Platform companies as available clientsGET /api/platform/v1/company/companies
ISupportsListingAgentsLists endpoints for mapped companies, with online status from heartbeatGET /api/platform/v2/device/categories/platform/endpoints, GET /api/platform/v2/device/endpoints/heartbeat
ISupportsInventoryIdentificationMatches an ImmyBot computer to its ConnectWise Platform endpoint by reading privateendpointid from the registryLocal registry — no API call
ISupportsTenantInstallTokenMints a per-tenant install token for the agent MSIGET /companies/{id}/sites, POST /api/platform/v1/device/token
IRunScriptProviderRuns scripts on ConnectWise Platform endpoints (ephemeral, via schedule-tasks)POST /api/platform/v2/automation/endpoints/schedule-tasks
ISupportsHttpRequestWebhook receiver (stub — logs incoming events, no actions wired yet)Inbound only

7. API scopes and auth model

7.1 Auth flow

The integration uses OAuth2 client_credentials:

  1. POST /v1/token with a JSON body containing grant_type, client_id, client_secret, and a space-separated scope string.
  2. Read access_token from the response.
  3. Send Authorization: Bearer <access_token> on each API call.

A fresh token is fetched per capability invocation; tokens are not cached between invocations.

7.2 Scope set requested

All token requests in the integration request the same read-scope string:

platform.companies.read platform.sites.read platform.devices.read platform.agent-token.read platform.asset.read platform.agent.read

This is deliberate over-granting per call. Earlier iterations requested narrower scopes per capability, and we hit a class of bug where a token minted for one scope was used against an endpoint gated by another (e.g. agent-token scope used for a sites lookup → 403 "client has no access"). Requesting the full read surface on every token removes that whole failure class. The trade-off is that we're not running least-privilege at the call level — but the key itself already has these scopes granted, so there's no additional risk.

Write-scope exception: RunScript

IRunScriptProvider is the sole capability that requires a write scope. Its token request adds platform.automation.create to the scope string — the schedule-tasks endpoint is a POST that creates ephemeral automation tasks. Every other capability in the integration remains read-only. The exception is acceptable because RunScript is an inherently mutating operation (it runs a script on a remote endpoint), so a write scope is the correct representation of what it does.

8. Installer hosting

The dynamic-versions script returns a download URL that you must configure to point to your own hosted copy of the ConnectWise Platform barebone MSI.

ConnectWise Platform's original generic download URL —

https://setup.auplatform.connectwise.com/windows/BareboneAgent/32/ITSagent/MSI/setup

— serves the file via a redirect chain that ImmyBot's downloader doesn't follow cleanly, causing install actions to fail to retrieve the MSI. This is why you need to host the file yourself on storage that provides a direct-download URL (Azure Blob Storage, AWS S3, a web server, etc.).

8.1 Setup

  1. Download the current generic MSI from the URL above (use a browser or curl -L to follow redirects).
  2. Upload it to your own storage.
  3. Open software/Get-AsioAgentDownloadLink.ps1 and replace the placeholder $URL value with your direct-download URL.

8.2 Keeping the MSI fresh

This is a maintenance dependency. If ConnectWise Platform releases a new barebone installer and you don't refresh your hosted copy, deployments will install an older bootstrapper. The agent self-updates after install, so a slightly stale MSI is not harmful — but very old versions may fail to bootstrap. Recommend refreshing quarterly or whenever ConnectWise Platform announces an agent update.

9. Troubleshooting

9.1 Integration won't go Healthy

HealthCheck errorLikely causeAction
Token endpoint returned no access_tokenClient ID or secret wrong, secret revoked, or wrong region URL.Regenerate the Platform key (the secret only displays once — it may have been truncated on first copy). Re-enter, save.
HTTP 401 from /v1/tokenSame as above, or the key is disabled in ConnectWise Platform.Check the key's status in Platform Integrations.
HTTP 403 on companies read after token successToken issued but missing the platform.companies.read scope.Confirm the scope is granted on the Platform key; re-tick if needed.
HTTP 403 on schedule-tasks (RunScript)Token missing platform.automation.create scope. This is the only write scope the integration requires — easy to miss when granting read-only scopes.Add platform.automation.create to the Platform key scopes and regenerate the token.
HTTP 403 on heartbeat with resourceType=endpointsThe heartbeat endpoint requires resourceType=endpoints (not devices or omitted). A mismatch returns 403 "client has no access" even when scopes are correct.Ensure the heartbeat request uses resourceType=endpoints. This is already correct in the current integration script; if you see this error after modifying the script, check the query-string parameter.

9.2 Install fails: "Get-IntegrationAgentInstallToken returned nothing"

9.3 Install fails: "This package is configured for workstations only"

The install script gates on Win32_OperatingSystem.ProductType -ne 1 (workstation). Server install would require validating the correct SYSTEM= property value for ConnectWise Platform server mode, which has not been done yet. Servers should be excluded from the deployment's target filter, or a separate server package should be created once the server install property is known.

9.4 Device shows the script's source code instead of a GUID under inventory

The GetInventoryScript block must use Invoke-ImmyCommand { ... }, not return { ... }. The latter returns the scriptblock AST itself to ImmyBot, which then renders it as inventory data instead of executing it.

Cleanup: if a device already shows the AST blob and the inventory doesn't refresh, temporarily change the inventory script to return $null, force inventory re-identification, then switch back to the real script.

9.5 Detection shows installed but check-in reports failed

This was symptomatic of SupportsRunningScripts:$true being set on emitted agents when the integration didn't implement the script-execution capability. The current integration emits $false; if the symptom recurs, verify the flag in New-IntegrationAgent.

9.6 ITSPlatform service or registry remains after uninstall

The uninstall script includes a cleanup block (sc.exe delete on the services, Remove-Item -Recurse on the registry keys). If detection still reports installed after an uninstall, run a fresh uninstall — the cleanup block is idempotent.

10. Design decisions & trade-offs

10.1 Why a fixed 1.0.0 version instead of dynamic versions?

The ConnectWise Platform agent self-updates after install. The version installed today is not the version installed tomorrow. Returning the real agent version would cause ImmyBot to perceive constant drift, and in the worst case attempt downgrades when the running agent is newer than the MSI bootstrapper. Collapsing to a binary "installed-and-healthy / not-installed" check matches the agent's actual lifecycle.

10.2 Why is the token requested at install time, not version-determination time?

Earlier iterations called Get-IntegrationAgentInstallToken during the dynamic-versions script, so every machine doing a version check exercised the token endpoint. A token failure (transient API issue, scope problem) would then block detection across the whole fleet. Moving the call to install-time means the token endpoint is only exercised on machines being actively installed — version checks now never touch ConnectWise Platform.

10.3 Why hedge the OAuth scope string?

See §7.2. Net: one broad scope string per token, applied across every call, removes the class of "token scoped for X used against endpoint gated by Y" bugs that bit us three different ways during development.

10.4 Why does the user need to self-host the MSI?

See §8. ConnectWise Platform's generic URL serves the file via a redirect chain ImmyBot can't follow. Hosting the MSI on your own storage (Azure Blob, S3, etc.) provides a direct-download URL. Trade-off: you own the freshness of the file (see the refresh guidance in §8.2).

10.5 Why workstation-only install?

The server install needs SYSTEM= set to a value other than desktop (likely server, but unconfirmed). Rather than guess and risk installing workstation mode on production servers, the install gates on ProductType == 1. To add server support: validate the correct property value with ConnectWise Platform, then either branch the install script on ProductType, or create a separate server software package.

11. Change history

v1.2

v1.1

v1.0 (initial release)

Significant decisions captured during development: