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.
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).
platform.companies.readplatform.sites.readplatform.devices.readplatform.agent-token.readplatform.asset.readplatform.agent.readplatform.automation.create (required for RunScript — see §7.2)automation.create for RunScript), so over-granting on the key side is harmless.
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.
https://setup.auplatform.connectwise.com/windows/BareboneAgent/32/ITSagent/MSI/setup (use a browser or curl -L to follow redirects).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.
| File | Where it goes | Purpose |
|---|---|---|
ConnectWiseRMM-Integration.ps1 | Integrations → New Dynamic Integration | The integration itself. |
Detect-Asio.ps1 | Software → Custom Detection Script | Returns 1.0.0 when the agent is installed, running, and registered. Returns $null otherwise. |
Get-AsioAgentDownloadLink.ps1 | Software → Dynamic Versions | Returns the fixed version + generic MSI URL. |
Install-Asio.ps1 | Software → Install Script | Fetches install token, runs the MSI with TOKEN= and SYSTEM=desktop. |
Uninstall-Asio.ps1 | Software → Uninstall Script | Uninstalls and cleans up residual services and registry keys. |
Test-Asio.ps1 | Software → Test Script | Verifies the agent is healthy after install (up to 5 minutes). |
ConnectWiseRMM-Integration.ps1 as the integration script. Give it a recognisable name (e.g. ConnectWise Platform).| Parameter | Value (AU region) |
|---|---|
| API Endpoint URL | https://openapi.service.auplatform.connectwise.com |
| Client ID | From Platform key generation |
| Client Secret | From Platform key generation |
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.
Create a new Software entry in ImmyBot called ConnectWise Platform (or similar). Then configure each section:
Detect-Asio.ps1The 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.
Get-AsioAgentDownloadLink.ps1This 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.
This is the link that lets the install script call Get-IntegrationAgentInstallToken. Without it, installs fail with "Get-IntegrationAgentInstallToken returned nothing."
Install Script: paste Install-Asio.ps1. Repair Strategy: Uninstall/Install (or Install Over — either works because detection is binary).
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.
ITSPlatform service not running or agent not registered with ConnectWise PlatformTest-Asio.ps1The 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.
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.
| ImmyBot interface | What it does | API endpoint |
|---|---|---|
ISupportsListingClients | Lists ConnectWise Platform companies as available clients | GET /api/platform/v1/company/companies |
ISupportsListingAgents | Lists endpoints for mapped companies, with online status from heartbeat | GET /api/platform/v2/device/categories/platform/endpoints, GET /api/platform/v2/device/endpoints/heartbeat |
ISupportsInventoryIdentification | Matches an ImmyBot computer to its ConnectWise Platform endpoint by reading privateendpointid from the registry | Local registry — no API call |
ISupportsTenantInstallToken | Mints a per-tenant install token for the agent MSI | GET /companies/{id}/sites, POST /api/platform/v1/device/token |
IRunScriptProvider | Runs scripts on ConnectWise Platform endpoints (ephemeral, via schedule-tasks) | POST /api/platform/v2/automation/endpoints/schedule-tasks |
ISupportsHttpRequest | Webhook receiver (stub — logs incoming events, no actions wired yet) | Inbound only |
The integration uses OAuth2 client_credentials:
/v1/token with a JSON body containing grant_type, client_id, client_secret, and a space-separated scope string.access_token from the response.Authorization: Bearer <access_token> on each API call.A fresh token is fetched per capability invocation; tokens are not cached between invocations.
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.
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.
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.).
curl -L to follow redirects).software/Get-AsioAgentDownloadLink.ps1 and replace the placeholder $URL value with your direct-download URL.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.
| HealthCheck error | Likely cause | Action |
|---|---|---|
Token endpoint returned no access_token | Client 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/token | Same 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 success | Token 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=endpoints | The 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. |
GetTenantInstallToken call. It will name the failing step:
403 "client has no access", meaning a missing scope (we now request all read scopes, so this should not recur).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.
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.
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.
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.
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.
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.
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.
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).
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.
Get-AsioAgentDownloadLink.ps1 no longer contains a specific storage URL. Users must host the barebone MSI themselves and configure the $URL variable in the script. See §2.2 and §8.IRunScriptProvider via POST /api/platform/v2/automation/endpoints/schedule-tasks. Ephemeral script execution on ConnectWise Platform endpoints.platform.automation.create, making it the only capability that carries a write scope. All other capabilities remain read-only.resourceType=endpoints. Earlier iterations omitted or used incorrect values, causing 403 errors.Significant decisions captured during development:
POST /v1/token → Bearer access_token. The earlier "secret-is-the-bearer" model in some legacy docs is not what the ConnectWise Platform spec describes.Link: rel="next". The integration follows the header until exhausted.$true to $false to match what the integration actually implements. Setting it true caused ImmyBot to attempt script execution through ConnectWise Platform and fail check-in.TOKEN=<guid> from the ConnectWise Platform scope list and silent-install doc. Plus SYSTEM=desktop for install mode.