
I’ve just added a new script to my Office365 repository on GitHub: cw-skykick-exposure-check.ps1. It does a read-only check of Microsoft 365 and Entra tenants for signs of exposure to the ConnectWise Cloud Backup (formerly SkyKick) vendor-credential incident, and produces a report you can act on.
If you’re an MSP with ConnectWise or SkyKick apps consented in customer tenants, you want answers quickly and across every tenant, not one portal click at a time. That’s what this is for.
What happened
According to Huntress’s advisory, a Microsoft 365 credential belonging to ConnectWise Cloud Backup was compromised and used on 15 October 2026 (UTC) to obtain Microsoft Graph tokens in customer tenants. Huntress also reports that in at least one tenant a ConnectWise Cloud Management app granted itself Global Administrator and added new secrets to other applications to keep a foothold. ConnectWise has acknowledged the incident. Huntress’s guidance is to disable (not delete) the affected apps, review the credentials on all your applications, and check service principal sign-ins and audit logs.
That review is exactly what the script automates.
What the script checks
For each tenant it runs four checks, then gives a verdict:
- ConnectWise/SkyKick service principals: matched by seven known app IDs, display name or the ConnectWise owner tenant, with enabled state, directory roles, OAuth2 grants and app role assignments. A match holding a highly privileged role is flagged.
- Service principal sign-ins since 1 October 2026 for those apps and the published indicator IP addresses, flagging indicator IPs and Python or curl user agents. This needs Entra ID P1 or P2.
- Directory audit events since 13 October 2026 (role additions, application updates and new service principal credentials), plus a scan of every app registration and service principal for secrets or certificates whose validity starts in that window.
- Conditional Access exclusions that cover the ConnectWise apps.
- Verdict: Suspicious, Present-no-activity, Not present, Unknown or Error, with recommended actions.
It is strictly read-only. Every Graph call goes through a wrapper that only issues GET requests, so it never creates, changes, disables or deletes anything. If a check can’t run because of licensing or permissions, it’s reported as Unavailable rather than crashing, and one tenant failing never stops the rest.
How to run it
You’ll need PowerShell 7.4 or later and the Microsoft.Graph.Authentication module, plus read permissions: Application.Read.All, Directory.Read.All, AuditLog.Read.All, RoleManagement.Read.Directory and Policy.Read.All. For a single tenant with an interactive sign-in:
./cw-skykick-exposure-check.ps1 -TenantId contoso.onmicrosoft.com -UseDeviceCode
For many tenants unattended, supply a CSV of TenantId, ClientId and a certificate thumbprint or PFX path, with the PFX password injected through an environment variable. It’s the same certificate-based, app-only approach I described in my recent post on headless connections.
./cw-skykick-exposure-check.ps1 -TenantCsv ./tenants.csv -OutputPath ./reports
Each run writes three reports: a CSV with one summary row per tenant, a detailed JSON file, and a readable HTML report with times in Sydney time.
Read the results carefully
This is an investigation aid, not a forensic verdict. Not present means the checks found none of the configured matches; it doesn’t prove a tenant was never touched. Graph doesn’t expose when a credential was actually created, only when it becomes valid, so confirm every flagged secret or certificate was added by your team. And review the indicators and date windows against the latest advisories before you rely on them.
Get the script
- Script: cw-skykick-exposure-check.ps1
- Documentation: ConnectWise SkyKick Exposure Check wiki
Run it against a test tenant first, then across your customers. If you find a bug, a missed indicator or a way to make it better, let me know. Feedback from people running it in the real world is what makes these scripts better for everyone.