SCENARIO
Organizations deploying SentinelOne endpoint protection require continuous monitoring of agent health to ensure comprehensive threat coverage across their environment. Unlike traditional antivirus solutions, SentinelOne agents must maintain active console connectivity, real-time protection engines, and current threat intelligence to function effectively.
Use this script when you need to:
- Automate SentinelOne agent health monitoring across managed endpoints
- Detect agents with stopped services, disabled protection, or console connectivity issues
- Report agent status metrics to NinjaRMM for centralized dashboard visibility
- Identify endpoints requiring manual intervention (service restarts, re-registration, updates)
- Validate SentinelOne deployment health after mass installations or policy changes
Business Impact:
- Security gaps: Disconnected or disabled agents leave endpoints vulnerable to threats
- Compliance risks: Inactive protection may violate security compliance requirements (SOC 2, HIPAA, PCI-DSS)
- Operational visibility: Automated reporting eliminates manual console checks across hundreds of endpoints
This script provides proactive alerting before security incidents occur, rather than reactive troubleshooting after breaches.
REQUIREMENTS & ASSUMPTIONS
Prerequisites:
- SentinelOne Agent: Installed with
sentinelctl.exepresent inC:\Program Files\SentinelOne\Sentinel Agent *\ - Administrator Privileges: Script requires
#Requires -RunAsAdministratorfor service queries - PowerShell 5.1 or Later: Uses modern cmdlets and error handling
- NinjaRMM Agent (optional but recommended): For automated custom field updates
NinjaRMM Custom Fields Configuration: The script updates these custom fields in NinjaRMM. Create them as text fields before deployment:
s1_agent_status- Overall health status (HEALTHY, DISCONNECTED, PROTECTION_DISABLED, etc.)s1_agent_version- Installed agent version (e.g., "23.4.2.15")s1_last_update- Last agent update timestamps1_protection_status- Threat engine status (ACTIVE, DISABLED, UNKNOWN)s1_console_connectivity- Management console connection (CONNECTED, DISCONNECTED)
System Assumptions:
- SentinelOne service name is
SentinelAgent(standard installation) - Log directory
C:\BIN\is writable (created automatically if missing) - Network connectivity allows console communication (firewall rules permit SentinelOne management traffic)
- Agent is registered with a SentinelOne management console
Tool Verification:
- Validate SentinelOne installation:
Test-Path "${env:ProgramFiles}\SentinelOne\Sentinel Agent *\SentinelCtl.exe" - Verify service status:
Get-Service -Name SentinelAgent - Confirm NinjaRMM integration:
Get-Command Ninja-Property-Set -ErrorAction SilentlyContinue
PROCESS
Agent Health Check Workflow
The script performs a comprehensive eight-step health validation process:
flowchart TD
Start([Script Execution]) --> CheckInstall[Step 1: Test-SentinelOneInstalled]
CheckInstall --> InstallFound{sentinelctl.exe<br/>Found?}
InstallFound -->|No| NotInstalled[Set Status: NOT_INSTALLED<br/>Exit Code 1]
InstallFound -->|Yes| CheckService[Step 2: Get-SentinelService]
CheckService --> ServiceRunning{Service<br/>Running?}
ServiceRunning -->|No| ServiceStopped[Set Status: SERVICE_STOPPED<br/>Exit Code 1]
ServiceRunning -->|Yes| GetInfo[Step 3: Get-SentinelAgentInfo]
GetInfo --> InfoSuccess{Query<br/>Successful?}
InfoSuccess -->|No| QueryFailed[Set Status: QUERY_FAILED<br/>Exit Code 1]
InfoSuccess -->|Yes| CheckProtection[Step 4: Get-SentinelThreatProtection]
CheckProtection --> CheckConnectivity[Step 5: Test-SentinelConsoleConnectivity]
CheckConnectivity --> CheckUpdates[Step 6: Get-SentinelPendingActions]
CheckUpdates --> Evaluate[Step 7: Determine Overall Status]
Evaluate --> EvalConsole{Console<br/>Connected?}
EvalConsole -->|No| StatusDisc[Status: DISCONNECTED<br/>Exit Code 2]
EvalConsole -->|Yes| EvalProtection{Protection<br/>Active?}
EvalProtection -->|No| StatusProt[Status: PROTECTION_DISABLED<br/>Exit Code 2]
EvalProtection -->|Yes| EvalUpdate{Pending<br/>Updates?}
EvalUpdate -->|Yes| StatusUpdate[Status: UPDATE_PENDING<br/>Exit Code 2]
EvalUpdate -->|No| StatusHealthy[Status: HEALTHY<br/>Exit Code 0]
StatusDisc --> Report[Step 8: Report to NinjaRMM]
StatusProt --> Report
StatusUpdate --> Report
StatusHealthy --> Report
Report --> UpdateFields[Update Custom Fields:<br/>- s1_agent_status<br/>- s1_agent_version<br/>- s1_last_update<br/>- s1_protection_status<br/>- s1_console_connectivity]
UpdateFields --> Complete([Script Exit])
NotInstalled --> Complete
ServiceStopped --> Complete
QueryFailed --> Complete
style Start fill:#90EE90
style StatusHealthy fill:#90EE90
style Complete fill:#90EE90
style NotInstalled fill:#FF6B6B
style ServiceStopped fill:#FF6B6B
style QueryFailed fill:#FF6B6B
style StatusDisc fill:#FFD700
style StatusProt fill:#FFD700
style StatusUpdate fill:#FFA500Step 1: Validate Script Syntax and Parameters
Download or copy the script to your management workstation and validate it compiles correctly:
pwsh -NoLogo -File .\Check-SentinelOne-Health.ps1 -WhatIfStep 2: Test on a Single Endpoint
Before mass deployment, run the script on a test system with known-good SentinelOne agent:
.\Check-SentinelOne-Health.ps1Step 3: Review Log Output
Check the generated log file for detailed execution results:
# Open today's log file
notepad "C:\BIN\LOGS-$(Get-Date -Format 'yyyy-MM-dd')-SentinelOne-Health.log"Expected log entries for a healthy agent:
[2025-11-26 14:30:01] [INFO] ========== SentinelOne Health Check Started ==========
[2025-11-26 14:30:02] [SUCCESS] SentinelOne sentinelctl.exe found: C:\Program Files\SentinelOne\Sentinel Agent 23.4.2.15\SentinelCtl.exe
[2025-11-26 14:30:03] [SUCCESS] Service 'SentinelAgent' status: Running
[2025-11-26 14:30:05] [INFO] Agent Version: 23.4.2.15
[2025-11-26 14:30:05] [INFO] Console Connectivity: Yes
[2025-11-26 14:30:06] [SUCCESS] Threat Protection Engine: Active
[2025-11-26 14:30:07] [SUCCESS] No pending updates detected
[2025-11-26 14:30:08] [SUCCESS] Overall Agent Status: HEALTHY
Step 4: Verify NinjaRMM Custom Field Updates
If NinjaRMM integration is configured, confirm the custom fields populated correctly:
- Log into NinjaRMM dashboard
- Navigate to the test endpoint device details
- Check the custom fields section for
s1_*fields - Verify values match the log output
Step 5: Interpret Health Status Codes
SentinelOne Agent Architecture
Understanding the agent's component dependencies helps diagnose health check failures:
graph TB
subgraph Endpoint["Windows Endpoint"]
subgraph Agent["SentinelOne Agent Installation"]
SentinelCtl[sentinelctl.exe<br/>CLI Management Tool]
Service[SentinelAgent Service<br/>Windows Service]
Engine[Threat Protection Engine<br/>Behavioral AI]
Config[Agent Configuration<br/>Console URL, Policies]
end
subgraph OS["Operating System"]
WinService[Windows Service Manager]
Network[Network Stack]
FileSystem[File System Monitoring]
end
end
subgraph External["External Dependencies"]
Console[SentinelOne Management Console<br/>Cloud/On-Premises]
ThreatIntel[Threat Intelligence Feed<br/>Real-Time Updates]
end
Service --> WinService
Service --> Engine
Service --> FileSystem
Engine --> FileSystem
Engine --> ThreatIntel
Service --> Network
Network --> Console
Config --> Console
SentinelCtl --> Service
Script[Health Check Script] -.Query.-> SentinelCtl
Script -.Check Status.-> Service
Script -.Validate.-> Engine
Script -.Test Connectivity.-> Console
style Service fill:#FF9999,stroke:#333,stroke-width:3px
style Engine fill:#99CCFF,stroke:#333,stroke-width:2px
style Console fill:#90EE90,stroke:#333,stroke-width:2px
style Script fill:#FFD700,stroke:#333,stroke-width:3pxComponent Health Checks:
| Component | Health Check Method | Failure Impact |
|---|---|---|
| sentinelctl.exe | File existence check | Script cannot query agent status (NOT_INSTALLED) |
| SentinelAgent Service | Get-Service status check | Agent inactive, no threat protection (SERVICE_STOPPED) |
| Threat Engine | sentinelctl engine status | Service running but not blocking threats (PROTECTION_DISABLED) |
| Console Connectivity | sentinelctl status parsing | No policy updates, unmanaged agent (DISCONNECTED) |
| Pending Updates | sentinelctl check-update | Agent functional but outdated (UPDATE_PENDING) |
The script reports these overall agent status codes:
| Status Code | Meaning | Recommended Action |
|---|---|---|
HEALTHY | All checks passed | No action required |
DISCONNECTED | Console connectivity lost | Check network/firewall, verify console URL |
PROTECTION_DISABLED | Threat engine inactive | Review policies, re-enable protection in console |
UPDATE_PENDING | Agent update available | Schedule maintenance window for update |
SERVICE_STOPPED | SentinelAgent service not running | Restart service: Restart-Service SentinelAgent |
SERVICE_NOT_FOUND | Service missing | Reinstall SentinelOne agent |
NOT_INSTALLED | Agent not detected | Deploy SentinelOne agent |
QUERY_FAILED | sentinelctl.exe error | Check agent installation integrity |
HEALTH_CHECK_FAILED | Script exception | Review log file for error details |
Step 6: Deploy via NinjaRMM Automation
For mass deployment to managed endpoints:
- Upload script to NinjaRMM Scripts repository
- Create scheduled automation task (recommended: daily execution)
- Set execution context to System/Administrator
- Configure alerting for non-HEALTHY status codes
- Monitor dashboard for custom field population
Step 7: Troubleshoot Unhealthy Agents
When agents report non-HEALTHY status, follow these remediation steps:
For DISCONNECTED status:
# Verify network connectivity to management console
Test-NetConnection -ComputerName <your-sentinelone-console>.sentinelone.net -Port 443
# Check proxy settings if applicable
& "C:\Program Files\SentinelOne\Sentinel Agent *\SentinelCtl.exe" config show
# Force agent re-registration
& "C:\Program Files\SentinelOne\Sentinel Agent *\SentinelCtl.exe" management reconnectFor PROTECTION_DISABLED status:
# Check protection engine state
& "C:\Program Files\SentinelOne\Sentinel Agent *\SentinelCtl.exe" engine status
# Enable protection (if disabled locally)
& "C:\Program Files\SentinelOne\Sentinel Agent *\SentinelCtl.exe" engine start
# Verify policy in SentinelOne console (may be disabled by policy)For SERVICE_STOPPED status:
# Check service startup type
Get-Service SentinelAgent | Select-Object Name, Status, StartType
# Set to automatic and start
Set-Service SentinelAgent -StartupType Automatic
Start-Service SentinelAgent
# Verify service started successfully
Get-Service SentinelAgentStep 8: Schedule Automated Execution
For standalone systems not using NinjaRMM, create a scheduled task:
# Create daily scheduled task (runs at 2 AM)
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-ExecutionPolicy Bypass -NoProfile -File "C:\Scripts\Check-SentinelOne-Health.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At 2AM
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName "SentinelOne Health Monitor" -Action $action -Trigger $trigger -Principal $principal -Settings $settingsStep 9: Monitor and Alert
Establish monitoring thresholds for proactive alerting:
- Critical Alert: Status is
NOT_INSTALLED,SERVICE_NOT_FOUND, orDISCONNECTEDfor >24 hours - Warning Alert: Status is
PROTECTION_DISABLED,UPDATE_PENDING, orSERVICE_STOPPED - Info Alert: First-time agent detection (status changes from
NOT_INSTALLEDtoHEALTHY)
Configure NinjaRMM conditions or parse log files for automated ticketing integration.
SCRIPT DETAIL
Core Functions and Logic Flow
1. Installation Detection (Test-SentinelOneInstalled)
The script uses wildcard path expansion to locate sentinelctl.exe regardless of agent version:
$sentinelCtlPath = "${env:ProgramFiles}\SentinelOne\Sentinel Agent *\SentinelCtl.exe"
$sentinelCtl = Get-Item $sentinelCtlPath -ErrorAction SilentlyContinue | Select-Object -First 1This handles version-specific installation directories like Sentinel Agent 23.4.2.15 without hardcoding version numbers. If multiple versions exist (rare), it selects the first match.
2. Service Status Validation (Get-SentinelService)
Checks the Windows service using standard Get-Service cmdlet:
$service = Get-Service -Name $serviceName -ErrorAction Stop
if ($status -ne 'Running') {
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue "SERVICE_STOPPED"
return $false
}The service name SentinelAgent is standard across all SentinelOne installations. Non-running states (Stopped, Paused, StartPending) all trigger the SERVICE_STOPPED status.
3. Agent Information Query (Get-SentinelAgentInfo)
Executes sentinelctl.exe status and parses text output using regex:
$agentInfo = & $SentinelCtlPath status 2>&1
foreach ($line in $agentInfo) {
if ($line -match "Agent version:\s*(.+)") {
$agentData.Version = $matches[1].Trim()
} elseif ($line -match "Last updated:\s*(.+)") {
$agentData.LastUpdate = $matches[1].Trim()
}
# ... additional parsing
}The 2>&1 redirection captures both stdout and stderr. Exit code validation ($LASTEXITCODE -ne 0) detects command failures even when partial output is returned.
4. Threat Protection Engine Check (Get-SentinelThreatProtection)
Queries the behavioral AI engine separately from service status:
$protectionInfo = & $SentinelCtlPath engine status 2>&1
if ($line -match "Engine state:\s*(.+)") {
$engineStatus = $matches[1].Trim()
}This detects scenarios where the service is running but protection is disabled by policy or manual intervention. Regex matching uses (?i)active|enabled|on to handle various console output formats.
5. Console Connectivity Validation (Test-SentinelConsoleConnectivity)
Parses the "Connected to management" field from agent status:
$isConnected = $ConnectionStatus -match "(?i)yes|true|connected|online"Connectivity issues indicate:
- Network/firewall blocking SentinelOne management ports (443, 8443)
- Agent token expired (re-registration required)
- Management console URL misconfigured
- Proxy settings preventing HTTPS connections
6. Pending Update Detection (Get-SentinelPendingActions)
Executes sentinelctl.exe check-update to identify available agent updates:
$updateInfo = & $SentinelCtlPath check-update 2>&1
foreach ($line in $updateInfo) {
if ($line -match "(?i)update available|new version") {
$hasPendingUpdate = $true
}
}This check does NOT automatically install updates. It only reports availability for scheduled maintenance windows.
7. Overall Status Determination
The script prioritizes health issues hierarchically:
$overallStatus = "HEALTHY"
if ($consoleStatus -eq "DISCONNECTED") {
$overallStatus = "DISCONNECTED" # Critical - no management
} elseif ($protectionStatus -notmatch "(?i)active|enabled|on") {
$overallStatus = "PROTECTION_DISABLED" # High - no threat blocking
} elseif ($hasPendingUpdates) {
$overallStatus = "UPDATE_PENDING" # Low - functional but outdated
}Disconnection overrides all other issues because an unmanaged agent cannot receive policy updates or threat intelligence.
8. NinjaRMM Integration (Set-NinjaProperty)
The script gracefully handles missing NinjaRMM integration:
if (Get-Command Ninja-Property-Set -ErrorAction SilentlyContinue) {
Ninja-Property-Set $PropertyName $PropertyValue
} else {
Write-Log "NinjaRMM integration not available" -Level 'WARNING'
}When NinjaRMM cmdlet is unavailable (standalone execution), the script continues and logs locally without failing. This allows the same script to function in both NinjaRMM and non-NinjaRMM environments.
9. Exit Code Strategy
The script uses three exit codes for automation integration:
- Exit 0:
HEALTHYstatus - all checks passed - Exit 1: Critical failures (agent not installed, service missing, query errors)
- Exit 2: Warning states (disconnected, protection disabled, updates pending)
NinjaRMM and scheduled task monitoring can trigger different alert severities based on exit codes.
Logging and Troubleshooting
All operations log to C:\BIN\LOGS-\<date\>-SentinelOne-Health.log with timestamps and severity levels:
[2025-11-26 14:30:08] [ERROR] Console connectivity: DISCONNECTED
Severity levels:
- INFO: Normal operational messages
- SUCCESS: Validation checks passed
- WARNING: Non-critical issues (NinjaRMM unavailable, pending updates)
- ERROR: Critical failures requiring intervention
Logs are date-stamped and append throughout the day, allowing historical analysis of agent state changes.
Security Considerations
Credential Handling:
- Script runs under SYSTEM account when deployed via NinjaRMM or scheduled tasks
- No hardcoded credentials or API keys
- Relies on Windows service authentication (SentinelOne agent's embedded token)
Execution Policy:
- Requires administrator privileges via
#Requires -RunAsAdministrator - Should be deployed via trusted automation platforms (NinjaRMM, GPO, SCCM)
- Validate script signature if code signing is enforced:
Get-AuthenticodeSignature .\Check-SentinelOne-Health.ps1
Tool Validation:
- Script validates
sentinelctl.exeexistence before execution to prevent arbitrary command injection - Exit code validation prevents partial failures from appearing successful
- Error handling (
$ErrorActionPreference = 'Stop') ensures exceptions are logged
Integration Points
NinjaRMM Custom Fields:
The script populates five custom fields that can be used for:
- Dashboard widgets showing SentinelOne fleet health percentages
- Automated ticketing when status != HEALTHY
- Patch compliance reporting (agent version inventory)
- Filtered device views (e.g., all DISCONNECTED agents)
SentinelOne Console:
The script does NOT modify SentinelOne console settings. It is read-only monitoring. Administrative actions (policy changes, agent updates, quarantine) must be performed in the SentinelOne management console.
Scheduled Task / Automation Platform:
Recommended execution frequency:
- Daily for standard monitoring
- Hourly for high-security environments requiring real-time visibility
- On-demand for troubleshooting specific endpoints
Avoid excessive execution (e.g., every 5 minutes) as sentinelctl.exe queries can consume CPU resources.
Known Limitations
1. Multi-Version Installations:
If multiple SentinelOne agent versions are installed (non-standard scenario), the script selects the first detected sentinelctl.exe. This may not represent the active agent.
2. Offline/Disconnected Agents: If the endpoint itself is offline, the script cannot execute. NinjaRMM will report "script failed to run" rather than "DISCONNECTED" status.
3. Parse Dependency:
The script relies on sentinelctl.exe text output formatting. SentinelOne updates that change output format may break regex parsing. Regular testing after agent updates is recommended.
4. No Automated Remediation: The script reports health issues but does not automatically restart services or re-register agents. This is intentional to prevent unintended service disruptions. Remediation should be reviewed and approved before execution.
SCRIPT CONTENTS
<#
.SYNOPSIS
Checks SentinelOne agent health status and reports metrics to NinjaRMM.
.DESCRIPTION
SCENARIO:
Organizations using SentinelOne need automated monitoring of agent health to ensure
continuous endpoint protection. This script validates service status, console connectivity,
agent version, threat protection state, and pending actions.
REQUIREMENTS:
- SentinelOne agent installed (sentinelctl.exe present in Program Files)
- Local administrator privileges
- NinjaRMM agent with custom fields configured:
* s1_agent_status (text)
* s1_agent_version (text)
* s1_last_update (text)
* s1_protection_status (text)
* s1_console_connectivity (text)
- C:\BIN\ directory for logging (created automatically if missing)
PROCESS:
1. Validate sentinelctl.exe presence and service status
2. Query agent version and last update timestamp
3. Check management console connectivity
4. Verify threat protection engine status
5. Detect pending updates or actions
6. Report all metrics to NinjaRMM custom fields
7. Log detailed results with timestamps
.NOTES
Author: CosmicBytez IT Operations
Version: 1.0
Requires: PowerShell 5.1+, Administrator privileges
#>
#Requires -RunAsAdministrator
$ErrorActionPreference = 'Stop'
# Configuration
$logDirectory = "C:\BIN"
$logFile = Join-Path $logDirectory "LOGS-$(Get-Date -Format 'yyyy-MM-dd')-SentinelOne-Health.log"
$sentinelCtlPath = "${env:ProgramFiles}\SentinelOne\Sentinel Agent *\SentinelCtl.exe"
$serviceName = "SentinelAgent"
# Initialize logging
function Write-Log {
param(
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[ValidateSet('INFO', 'WARNING', 'ERROR', 'SUCCESS')]
[string]$Level = 'INFO'
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$logEntry = "[$timestamp] [$Level] $Message"
# Create log directory if it doesn't exist
if (-not (Test-Path $logDirectory)) {
New-Item -ItemType Directory -Path $logDirectory -Force | Out-Null
}
Add-Content -Path $logFile -Value $logEntry
# Console output with color
$color = switch ($Level) {
'ERROR' { 'Red' }
'WARNING' { 'Yellow' }
'SUCCESS' { 'Green' }
default { 'White' }
}
Write-Host $logEntry -ForegroundColor $color
}
function Set-NinjaProperty {
param(
[Parameter(Mandatory = $true)]
[string]$PropertyName,
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[string]$PropertyValue
)
try {
if (Get-Command Ninja-Property-Set -ErrorAction SilentlyContinue) {
Ninja-Property-Set $PropertyName $PropertyValue
Write-Log "NinjaRMM: Set $PropertyName = $PropertyValue" -Level 'INFO'
} else {
Write-Log "NinjaRMM integration not available (Ninja-Property-Set not found)" -Level 'WARNING'
}
} catch {
Write-Log "Failed to set NinjaRMM property ${PropertyName}: $($_.Exception.Message)" -Level 'WARNING'
}
}
function Test-SentinelOneInstalled {
Write-Log "Checking SentinelOne installation..." -Level 'INFO'
$sentinelCtl = Get-Item $sentinelCtlPath -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $sentinelCtl) {
Write-Log "SentinelOne agent not found at expected path" -Level 'ERROR'
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue "NOT_INSTALLED"
return $null
}
Write-Log "SentinelOne sentinelctl.exe found: $($sentinelCtl.FullName)" -Level 'SUCCESS'
return $sentinelCtl.FullName
}
function Get-SentinelService {
Write-Log "Checking SentinelOne service status..." -Level 'INFO'
try {
$service = Get-Service -Name $serviceName -ErrorAction Stop
$status = $service.Status
Write-Log "Service '$serviceName' status: $status" -Level $(if ($status -eq 'Running') { 'SUCCESS' } else { 'WARNING' })
if ($status -ne 'Running') {
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue "SERVICE_STOPPED"
return $false
}
return $true
} catch {
Write-Log "Service '$serviceName' not found: $($_.Exception.Message)" -Level 'ERROR'
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue "SERVICE_NOT_FOUND"
return $false
}
}
function Get-SentinelAgentInfo {
param(
[Parameter(Mandatory = $true)]
[string]$SentinelCtlPath
)
Write-Log "Querying SentinelOne agent information..." -Level 'INFO'
try {
$agentInfo = & $SentinelCtlPath status 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Log "sentinelctl status command failed with exit code $LASTEXITCODE" -Level 'ERROR'
return $null
}
# Parse output
$agentData = @{
Version = ""
LastUpdate = ""
ConnectionStatus = ""
ProtectionStatus = ""
AgentId = ""
}
foreach ($line in $agentInfo) {
if ($line -match "Agent version:\s*(.+)") {
$agentData.Version = $matches[1].Trim()
} elseif ($line -match "Last updated:\s*(.+)") {
$agentData.LastUpdate = $matches[1].Trim()
} elseif ($line -match "Connected to management:\s*(.+)") {
$agentData.ConnectionStatus = $matches[1].Trim()
} elseif ($line -match "Protection status:\s*(.+)") {
$agentData.ProtectionStatus = $matches[1].Trim()
} elseif ($line -match "Agent ID:\s*(.+)") {
$agentData.AgentId = $matches[1].Trim()
}
}
Write-Log "Agent Version: $($agentData.Version)" -Level 'INFO'
Write-Log "Last Update: $($agentData.LastUpdate)" -Level 'INFO'
Write-Log "Console Connectivity: $($agentData.ConnectionStatus)" -Level 'INFO'
Write-Log "Protection Status: $($agentData.ProtectionStatus)" -Level 'INFO'
return $agentData
} catch {
Write-Log "Error querying agent info: $($_.Exception.Message)" -Level 'ERROR'
return $null
}
}
function Get-SentinelThreatProtection {
param(
[Parameter(Mandatory = $true)]
[string]$SentinelCtlPath
)
Write-Log "Checking threat protection engine status..." -Level 'INFO'
try {
$protectionInfo = & $SentinelCtlPath engine status 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Log "sentinelctl engine status command failed with exit code $LASTEXITCODE" -Level 'WARNING'
return "UNKNOWN"
}
$engineStatus = "UNKNOWN"
foreach ($line in $protectionInfo) {
if ($line -match "Engine state:\s*(.+)") {
$engineStatus = $matches[1].Trim()
break
} elseif ($line -match "Protection:\s*(.+)") {
$engineStatus = $matches[1].Trim()
break
}
}
Write-Log "Threat Protection Engine: $engineStatus" -Level $(if ($engineStatus -match "(?i)active|enabled|on") { 'SUCCESS' } else { 'WARNING' })
return $engineStatus
} catch {
Write-Log "Error checking threat protection: $($_.Exception.Message)" -Level 'WARNING'
return "ERROR"
}
}
function Get-SentinelPendingActions {
param(
[Parameter(Mandatory = $true)]
[string]$SentinelCtlPath
)
Write-Log "Checking for pending actions or updates..." -Level 'INFO'
try {
# Check for pending updates
$updateInfo = & $SentinelCtlPath check-update 2>&1
$hasPendingUpdate = $false
foreach ($line in $updateInfo) {
if ($line -match "(?i)update available|new version") {
$hasPendingUpdate = $true
Write-Log "Pending update detected: $line" -Level 'WARNING'
}
}
if (-not $hasPendingUpdate) {
Write-Log "No pending updates detected" -Level 'SUCCESS'
}
return $hasPendingUpdate
} catch {
Write-Log "Error checking pending actions: $($_.Exception.Message)" -Level 'WARNING'
return $false
}
}
function Test-SentinelConsoleConnectivity {
param(
[Parameter(Mandatory = $true)]
[string]$ConnectionStatus
)
Write-Log "Validating management console connectivity..." -Level 'INFO'
$isConnected = $ConnectionStatus -match "(?i)yes|true|connected|online"
if ($isConnected) {
Write-Log "Console connectivity: CONNECTED" -Level 'SUCCESS'
return "CONNECTED"
} else {
Write-Log "Console connectivity: DISCONNECTED" -Level 'ERROR'
return "DISCONNECTED"
}
}
# Main execution
Write-Log "========== SentinelOne Health Check Started ==========" -Level 'INFO'
try {
# Step 1: Check if SentinelOne is installed
$sentinelCtlExe = Test-SentinelOneInstalled
if (-not $sentinelCtlExe) {
Write-Log "SentinelOne agent not installed. Exiting." -Level 'ERROR'
Set-NinjaProperty -PropertyName "s1_agent_version" -PropertyValue "N/A"
Set-NinjaProperty -PropertyName "s1_last_update" -PropertyValue "N/A"
Set-NinjaProperty -PropertyName "s1_protection_status" -PropertyValue "N/A"
Set-NinjaProperty -PropertyName "s1_console_connectivity" -PropertyValue "N/A"
exit 1
}
# Step 2: Check service status
$serviceRunning = Get-SentinelService
if (-not $serviceRunning) {
Write-Log "SentinelOne service not running. Health check incomplete." -Level 'ERROR'
Set-NinjaProperty -PropertyName "s1_agent_version" -PropertyValue "SERVICE_DOWN"
Set-NinjaProperty -PropertyName "s1_last_update" -PropertyValue "N/A"
Set-NinjaProperty -PropertyName "s1_protection_status" -PropertyValue "SERVICE_DOWN"
Set-NinjaProperty -PropertyName "s1_console_connectivity" -PropertyValue "N/A"
exit 1
}
# Step 3: Get agent information
$agentInfo = Get-SentinelAgentInfo -SentinelCtlPath $sentinelCtlExe
if (-not $agentInfo) {
Write-Log "Failed to retrieve agent information" -Level 'ERROR'
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue "QUERY_FAILED"
exit 1
}
# Step 4: Check threat protection
$protectionStatus = Get-SentinelThreatProtection -SentinelCtlPath $sentinelCtlExe
# Step 5: Check console connectivity
$consoleStatus = Test-SentinelConsoleConnectivity -ConnectionStatus $agentInfo.ConnectionStatus
# Step 6: Check for pending actions
$hasPendingUpdates = Get-SentinelPendingActions -SentinelCtlPath $sentinelCtlExe
# Step 7: Determine overall agent status
$overallStatus = "HEALTHY"
if ($consoleStatus -eq "DISCONNECTED") {
$overallStatus = "DISCONNECTED"
} elseif ($protectionStatus -notmatch "(?i)active|enabled|on") {
$overallStatus = "PROTECTION_DISABLED"
} elseif ($hasPendingUpdates) {
$overallStatus = "UPDATE_PENDING"
}
Write-Log "Overall Agent Status: $overallStatus" -Level $(if ($overallStatus -eq "HEALTHY") { 'SUCCESS' } else { 'WARNING' })
# Step 8: Report to NinjaRMM
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue $overallStatus
Set-NinjaProperty -PropertyName "s1_agent_version" -PropertyValue $agentInfo.Version
Set-NinjaProperty -PropertyName "s1_last_update" -PropertyValue $agentInfo.LastUpdate
Set-NinjaProperty -PropertyName "s1_protection_status" -PropertyValue $protectionStatus
Set-NinjaProperty -PropertyName "s1_console_connectivity" -PropertyValue $consoleStatus
Write-Log "========== SentinelOne Health Check Completed ==========" -Level 'SUCCESS'
# Exit with appropriate code
if ($overallStatus -eq "HEALTHY") {
exit 0
} else {
exit 2 # Warning status
}
} catch {
Write-Log "Critical error during health check: $($_.Exception.Message)" -Level 'ERROR'
Write-Log "Stack trace: $($_.ScriptStackTrace)" -Level 'ERROR'
Set-NinjaProperty -PropertyName "s1_agent_status" -PropertyValue "HEALTH_CHECK_FAILED"
exit 1
}