msp runbooks•Published

Building Proactive PowerShell Daily Health Checks for Multi-Tenant MSPs

How to automate daily tenant hygiene checks across Exchange Online, Entra ID, and on-premises domain controllers using certificate-based authentication and secure app registrations.

#PowerShell#MSP#Automation#M365#Monitoring

Running manual checks across dozens of Microsoft 365 tenants is untenable. Technicians miss expiring certificates, orphaned privileged accounts, and failed directory synchronizations until an outage occurs.

This runbook provides our battle-tested, headless PowerShell script architecture using Certificate-Based Authentication (CBA) to query multiple tenants securely without storing client secrets in plain text.


1. Authentication Architecture: Why Client Secrets Fail in MSPs

Storing client secrets in RMM script parameters is an audit liability:

  1. Secrets expire quietly (typically after 1 to 2 years) and break unattended jobs without warning.
  2. Secrets can be extracted from RMM agent logs or local script caches.

Instead, we generate a dedicated self-signed certificate, store the private key in the Windows Certificate Store or Azure Key Vault, and upload the public .cer to the Multi-Tenant App Registration:

[ Scheduled Task / RMM Agent ] 
              │
              ▼
    [ Load Thumbprint from Cert:\LocalMachine\My ]
              │
              ▼
[ Connect-MgGraph -CertificateThumbprint $Thumbprint -TenantId $TenantId ]
              │
              ▼
[ Execute Automated Health Check & Post Summary to Webhook ]

2. Core Tenant Health Inspection Script

Here is the modular PowerShell script checking for three critical failure modes:

  • Entra Connect Sync Latency: Alerts if directory synchronization hasn’t succeeded in the last 3 hours.
  • Privileged Role Creep: Enumerates active Global Administrators.
  • Expiring App Secrets & Certificates: Flags credentials expiring within 14 days.
[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]$TenantId,

    [Parameter(Mandatory = $true)]
    [string]$ClientId,

    [Parameter(Mandatory = $true)]
    [string]$CertThumbprint
)

# Connect headlessly using Certificate-Based Auth
try {
    Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -CertificateThumbprint $CertThumbprint -NoWelcome
    Write-Host "[+] Successfully authenticated to tenant: $TenantId" -ForegroundColor Green
} catch {
    Write-Error "[-] Failed to authenticate: $_"
    exit 1
}

$HealthReport = [PSCustomObject]@{
    TenantId             = $TenantId
    CheckTimestamp       = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
    GlobalAdminCount     = 0
    ExpiringCredentials  = @()
    SyncStatus           = "OK"
}

# 1. Enumerate Global Administrators
$GlobalAdminRole = Get-MgDirectoryRole | Where-Object { $_.DisplayName -eq "Global Administrator" }
if ($GlobalAdminRole) {
    $Admins = Get-MgDirectoryRoleMember -DirectoryRoleId $GlobalAdminRole.Id
    $HealthReport.GlobalAdminCount = $Admins.Count
}

# 2. Check for App Secrets/Certs Expiring in next 14 days
$Threshold = (Get-Date).AddDays(14)
$Applications = Get-MgApplication -All
foreach ($App in $Applications) {
    foreach ($Key in $App.PasswordCredentials) {
        if ($Key.EndDateTime -and $Key.EndDateTime -lt $Threshold) {
            $HealthReport.ExpiringCredentials += "$($App.DisplayName) (Secret expires: $($Key.EndDateTime))"
        }
    }
}

# Output sanitized JSON report for PSA/RMM consumption
$HealthReport | ConvertTo-Json -Depth 3

  • Frequency: Run once daily at 05:00 local time before business operations begin.
  • Notification Routing: Send warning payloads directly to your PSA (ConnectWise Manage, Autotask, HaloPSA) or secure Teams/Slack webhook channels.
  • Remediation SLA: Flag any tenant having more than 5 Global Administrators for immediate review.
Weekly Technical BriefingIncludes Podcast Feed

Get The Next Production Runbook in Your Inbox

Join 500+ MSP & SecOps engineers. Receive battle-tested configurations, 5-minute audio briefings, and critical security advisories every Tuesday.

✓ Zero spam & zero marketing fluff•✓ 5-min audio overview included•✓ Unsubscribe anytime in 1-click