Per-Customer RMM Installer Automation — Working Solution¶
Goal¶
Generate the correct ConnectWise RMM (ITSupport247) Windows agent installer for each customer automatically, e.g.:
Acme_Electric-Acme_Electric_Windows_OS_ITSPlatform_TKN77d2779b-9a06-4fe9-b3da-0eb29c0e7bb0.msi
Each installer needs: the Company/Site name, the per-site Agent Token (the TKN<GUID>),
and a download URL.
What didn't work, and why¶
- Legacy Reporting API (
api.itsupport247.net/reporting/v1/Sites) returns onlyname+siteCode. ThesiteCode(ACE, 1069, ...) is NOT the agent token, and the reporting API has no endpoint that exposes it. - New OpenAPI (
openapi.service.itsupport247.net) authenticates fine with a Client ID/Secret (snake_case JSON to/v1/token), but/v1/clientsreturns 403 regardless of scopes, and there is no usable agent-token resource on it for this tenant.
What works: the portal's own GraphQL¶
The Asio portal UI runs on a GraphQL endpoint:
POST https://control.v2.itsupport247.net/GraphQL/
It authenticates with your logged-in browser session cookie (the sso-token, etc.).
Two queries give us everything:
1. List every site (with numeric IDs):
query { Sites { siteDetailList { edges { node { clientId siteId companyName siteName } } } } }
2. Get a site's installer path + url (the token):
query AgentSetupQuery {
agentSetup(
ClientID: $ClientID SiteID: $SiteID OSType: $OSType
SetupType: $SetupType Arch: $Arch FileName: $FileName FileType: $FileType
) { path url }
}
Quirk: this gateway does literal $var text-substitution, so the operation must NOT
declare its variables — keep the bare $ClientID tokens inline and pass a variables
object. For Windows MSI use OSType:"windows", SetupType:"MSMA", Arch:"64", FileType:"MSI".
Response, e.g. for Acme (ClientID=SiteID=363177):
path: /windows/MSMA/64/ITSPlatform_TKN77d2779b-9a06-4fe9-b3da-0eb29c0e7bb0/MSI/setup
url: https://prod.setup.itsupport247.net
- Token = the GUID after
TKNinpath. - Download URL =
url + path. - Filename =
"<CompanyName>-<SiteName>"(spaces -> underscores) +_Windows_OS_ITSPlatform_TKN<token>.msi.
Key insight: tokens are static¶
A site's Agent Token is permanent unless someone regenerates it in the portal. So you harvest once into a table, then build installers forever with no authentication. The 12-hour session cookie only matters when refreshing the table.
The deliverables (scripts in engine/rmm/, docs in docs/)¶
- RMM_Site_Tokens.csv — all 51 sites: CompanyName, SiteName, ClientId, SiteId, Token, DownloadUrl. Captured live from the portal.
- Build-RMMInstaller.ps1 — builds/downloads installers from the CSV. No auth.
.\Build-RMMInstaller.ps1 -Company "Acme Electric".\Build-RMMInstaller.ps1 -All.\Build-RMMInstaller.ps1 -All -ListOnly(print names/URLs only)
- Refresh-RMMTokens.ps1 — rebuilds the CSV by calling the GraphQL endpoint. Run this when you add a customer or rotate a token. Paste a fresh portal cookie into it first.
Refreshing the table¶
- Log into
control.itsupport247.net, F12 > Network, filterGraphQL, click the request, copy the fullcookie:request-header value. - Paste it into
Refresh-RMMTokens.ps1(-CookieHeader) and run it. - It regenerates
RMM_Site_Tokens.csv. The cookie is live credentials — keep the file local and clear the value after.
Superseded files (kept for reference)¶
Probe-CWRMM-OpenAPI.ps1 and Test-AgentSetup.ps1 were exploration steps for the
OpenAPI and single-site GraphQL test. The three files above are the actual solution.