SCENARIO
This runbook provides a standardized process for onboarding new MSP clients to SentinelOne Singularity Complete. Following this methodology ensures consistent deployments, minimizes disruption to client operations, and establishes proper security baselines from day one.
Use this runbook when:
- Onboarding a new managed services client
- Migrating a client from another EDR/AV solution to SentinelOne
- Upgrading a client from Control to Complete SKU
- Re-deploying after a significant environment change
- Standardizing an inconsistent existing deployment
Target Timeline: 2-4 weeks for full deployment (varies by client size)
Reference Documentation:
- SentinelOne Seamless Onboarding Guide
- SentinelOne GO Guided Onboarding
- SentinelOne MSSP Partner Resources
REQUIREMENTS & ASSUMPTIONS
Prerequisites
MSP Console Access:
- Global Admin or Account Admin role in multi-tenant console
- Ability to create new Sites and Groups
- API key with appropriate permissions (for automation)
Client Information Required:
| Information | Purpose | Example |
|---|---|---|
| Company name | Site naming | "Contoso Ltd" |
| Primary contact | Notifications & escalations | IT Manager |
| Endpoint count | Licensing & planning | 150 endpoints |
| Endpoint types | Policy planning | 120 workstations, 25 servers, 5 laptops |
| Current AV/EDR | Migration planning | Symantec Endpoint Protection |
| Domain structure | Deployment method | Single AD domain |
| RMM platform | Deployment automation | NinjaRMM |
| Critical applications | Exclusion planning | QuickBooks, AutoCAD, custom LOB apps |
| Compliance requirements | Policy configuration | HIPAA, PCI-DSS |
| Business hours | Deployment scheduling | M-F 8am-5pm CST |
Technical Requirements:
- Network access: HTTPS/443 outbound to
*.sentinelone.net - Supported OS: Windows 10/11, Server 2016+, macOS 11+, Linux (RHEL, Ubuntu, etc.)
- Minimum specs: 2GB RAM, 2GB disk space per endpoint
- Local admin rights for installation (or RMM/GPO deployment)
Assumed Environment
- Client has existing RMM agent (NinjaRMM, Datto, ConnectWise) for mass deployment
- Client has Active Directory or Azure AD for group policy/Intune deployment
- MSP has established change management process with client
- SentinelOne Singularity Complete licensing is provisioned
PROCESS
Phase 1: Discovery & Planning (Days 1-3)
1.1 Conduct Client Discovery Call
Agenda Items:
- Review endpoint inventory and types
- Identify critical systems (servers, exec workstations, POS systems)
- Document current security software installed
- Identify line-of-business applications requiring exclusions
- Discuss compliance requirements (HIPAA, PCI, SOC2, etc.)
- Establish communication channels and escalation contacts
- Define maintenance windows for deployment
- Set expectations for pilot and full deployment timeline
Discovery Questions:
1. How many total endpoints need protection?
- Windows workstations: ___
- Windows servers: ___
- macOS devices: ___
- Linux servers: ___
2. What security software is currently installed?
- Product name: ___
- Managed or standalone: ___
- Who manages removal: ___
3. What critical applications run in your environment?
- ERP/Accounting: ___
- Industry-specific: ___
- Custom/LOB apps: ___
4. What compliance frameworks apply?
- [ ] HIPAA
- [ ] PCI-DSS
- [ ] SOC2
- [ ] NIST
- [ ] Other: ___
5. Who are your pilot users? (2-5 users recommended)
- Name/Department: ___
- Endpoint type: ___
- Technical proficiency: ___1.2 Assess Current Security Posture
Run pre-deployment assessment on representative endpoints:
<#
.SYNOPSIS
Pre-deployment assessment for SentinelOne onboarding
.DESCRIPTION
Collects system information, identifies conflicting software, and validates requirements
#>
function Get-PreDeploymentAssessment {
param(
[string]$OutputPath = "C:\Temp\S1-PreDeployment-Assessment.json"
)
$assessment = @{
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
ComputerName = $env:COMPUTERNAME
SystemInfo = @{}
SecuritySoftware = @()
Requirements = @{}
Recommendations = @()
}
# System Information
$os = Get-CimInstance Win32_OperatingSystem
$assessment.SystemInfo = @{
OSName = $os.Caption
OSVersion = $os.Version
OSArchitecture = $os.OSArchitecture
TotalMemoryGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
FreeMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
FreeDiskSpaceGB = [math]::Round((Get-PSDrive C).Free / 1GB, 2)
LastBootTime = $os.LastBootUpTime
Domain = (Get-CimInstance Win32_ComputerSystem).Domain
}
# Detect Security Software
try {
$avProducts = Get-CimInstance -Namespace "root\SecurityCenter2" -ClassName AntiVirusProduct -ErrorAction SilentlyContinue
foreach ($av in $avProducts) {
$assessment.SecuritySoftware += @{
Name = $av.displayName
State = $av.productState
Path = $av.pathToSignedProductExe
}
}
} catch {
$assessment.SecuritySoftware += @{ Name = "Unable to query SecurityCenter2" }
}
# Check for known conflicting products
$conflictingProducts = @(
"McAfee*", "Symantec*", "Norton*", "Trend Micro*", "CrowdStrike*",
"Carbon Black*", "Cylance*", "Sophos*", "ESET*", "Kaspersky*",
"Webroot*", "Malwarebytes*", "Bitdefender*"
)
$installedSoftware = Get-CimInstance Win32_Product | Select-Object Name, Version
foreach ($pattern in $conflictingProducts) {
$matches = $installedSoftware | Where-Object { $_.Name -like $pattern }
foreach ($match in $matches) {
$assessment.Recommendations += "REMOVE: $($match.Name) v$($match.Version) - conflicts with SentinelOne"
}
}
# Validate Requirements
$assessment.Requirements = @{
DiskSpaceOK = $assessment.SystemInfo.FreeDiskSpaceGB -ge 2
MemoryOK = $assessment.SystemInfo.TotalMemoryGB -ge 2
OSVersionOK = [version]$os.Version -ge [version]"10.0"
Architecture64Bit = $os.OSArchitecture -eq "64-bit"
}
# Test connectivity to SentinelOne
$connectivityTest = Test-NetConnection -ComputerName "management-prod.sentinelone.net" -Port 443 -WarningAction SilentlyContinue
$assessment.Requirements.ConnectivityOK = $connectivityTest.TcpTestSucceeded
# Add recommendations
if (-not $assessment.Requirements.DiskSpaceOK) {
$assessment.Recommendations += "WARNING: Less than 2GB free disk space"
}
if (-not $assessment.Requirements.MemoryOK) {
$assessment.Recommendations += "WARNING: Less than 2GB RAM installed"
}
if (-not $assessment.Requirements.ConnectivityOK) {
$assessment.Recommendations += "CRITICAL: Cannot reach SentinelOne management servers - check firewall/proxy"
}
# Check proxy configuration
$proxySettings = netsh winhttp show proxy
if ($proxySettings -match "Direct access") {
$assessment.SystemInfo.ProxyConfigured = $false
} else {
$assessment.SystemInfo.ProxyConfigured = $true
$assessment.SystemInfo.ProxySettings = $proxySettings
}
# Export results
$assessment | ConvertTo-Json -Depth 5 | Out-File $OutputPath -Encoding UTF8
# Summary output
Write-Host "`n=== Pre-Deployment Assessment Summary ===" -ForegroundColor Cyan
Write-Host "Computer: $($assessment.ComputerName)"
Write-Host "OS: $($assessment.SystemInfo.OSName)"
Write-Host "Memory: $($assessment.SystemInfo.TotalMemoryGB) GB"
Write-Host "Free Disk: $($assessment.SystemInfo.FreeDiskSpaceGB) GB"
Write-Host "Connectivity: $(if($assessment.Requirements.ConnectivityOK){'PASS'}else{'FAIL'})"
if ($assessment.SecuritySoftware.Count -gt 0) {
Write-Host "`nDetected Security Software:" -ForegroundColor Yellow
$assessment.SecuritySoftware | ForEach-Object { Write-Host " - $($_.Name)" }
}
if ($assessment.Recommendations.Count -gt 0) {
Write-Host "`nRecommendations:" -ForegroundColor Yellow
$assessment.Recommendations | ForEach-Object { Write-Host " - $_" }
}
Write-Host "`nFull report saved to: $OutputPath" -ForegroundColor Green
return $assessment
}
# Run assessment
Get-PreDeploymentAssessment1.3 Design Console Hierarchy
Recommended MSP Hierarchy Structure:
Account (MSP Level)
└── Site: [Client Name]
├── Group: Workstations
│ ├── Sub-group: Standard Users
│ ├── Sub-group: Power Users
│ └── Sub-group: Executives (VIP)
├── Group: Servers
│ ├── Sub-group: Domain Controllers
│ ├── Sub-group: File Servers
│ ├── Sub-group: Application Servers
│ └── Sub-group: Database Servers
├── Group: Remote/Laptops
└── Group: Pilot (temporary)
Naming Convention:
- Site:
[ClientCode]-[ClientName](e.g.,CNT-Contoso) - Groups: Descriptive, matching client terminology
- Tags:
client:[code],type:[workstation|server],location:[site]
1.4 Plan Policy Configuration
Policy Inheritance Strategy:
Account Policy (MSP Baseline)
↓ inherits
Site Policy (Client-specific overrides)
↓ inherits
Group Policy (Endpoint type specific)
Recommended Policy Settings by Endpoint Type:
| Setting | Workstations | Servers | Executives |
|---|---|---|---|
| Engine Mode | Protect | Protect | Protect |
| Mitigation Mode | Protect | Protect | Protect |
| Anti-Tamper | Enabled | Enabled | Enabled |
| Snapshots | Enabled | Enabled | Enabled |
| Deep Visibility | Enabled | Enabled | Enabled |
| Network Quarantine | Enabled | Disabled* | Enabled |
| Remote Shell | Disabled | Enabled | Disabled |
| Scan on Write | Enabled | Enabled | Enabled |
| Cloud Validation | Enabled | Enabled | Enabled |
*Network quarantine on servers may cause service disruption - evaluate per client
Phase 2: Console Configuration (Days 2-4)
2.1 Create Site and Groups
Via SentinelOne Console:
- Navigate to Settings → Sites
- Click Add Site
- Configure:
- Name:
[ClientCode]-[ClientName] - Description: Client full name and contract reference
- Account: Select MSP account
- SKU: Singularity Complete
- Name:
- Click Save
- Copy the Site Token and store securely in password manager
Create Groups:
- Navigate to Sentinels → Groups
- Click Add Group
- For each group:
- Name: Descriptive name (e.g., "Workstations - Standard")
- Site: Select client site
- Description: Purpose and assignment criteria
- Repeat for all planned groups
Via API (PowerShell):
# SentinelOne API - Create Site and Groups
$apiToken = "YOUR_API_TOKEN"
$consoleUrl = "https://yourtenant.sentinelone.net"
$headers = @{
"Authorization" = "ApiToken $apiToken"
"Content-Type" = "application/json"
}
# Create Site
$siteBody = @{
data = @{
name = "CNT-Contoso"
description = "Contoso Ltd - Contract #12345"
accountId = "YOUR_ACCOUNT_ID"
sku = "complete"
siteType = "Paid"
}
} | ConvertTo-Json
$site = Invoke-RestMethod -Uri "$consoleUrl/web/api/v2.1/sites" -Method POST -Headers $headers -Body $siteBody
$siteId = $site.data.id
Write-Host "Created Site: $($site.data.name) (ID: $siteId)"
# Create Groups
$groups = @(
@{ name = "Workstations"; description = "Standard workstations" },
@{ name = "Servers"; description = "Production servers" },
@{ name = "Remote-Laptops"; description = "Remote and mobile devices" },
@{ name = "Pilot"; description = "Pilot deployment group" }
)
foreach ($group in $groups) {
$groupBody = @{
data = @{
name = $group.name
siteId = $siteId
description = $group.description
}
} | ConvertTo-Json
$newGroup = Invoke-RestMethod -Uri "$consoleUrl/web/api/v2.1/groups" -Method POST -Headers $headers -Body $groupBody
Write-Host "Created Group: $($newGroup.data.name) (ID: $($newGroup.data.id))"
}2.2 Configure Security Policies
Create Site Policy:
- Navigate to Settings → Policies
- Click New Policy
- Scope: Select client site
- Configure Agent Settings:
# Recommended Singularity Complete Policy Configuration
Agent Mode:
Engine: Protect
Mitigation: Protect
Detection Engines:
Static AI: Enabled
Behavioral AI: Enabled
Documents & Scripts: Enabled
Lateral Movement: Enabled
Anti-Exploitation: Enabled
Automated Actions:
Threats:
- Kill: Enabled
- Quarantine: Enabled
- Remediate: Enabled
Suspicious: Alert Only (initially)
Deep Visibility (Complete Feature):
Collection: Full
Data Retention: 14 days
Process Tracking: Enabled
Network Tracking: Enabled
File Tracking: Enabled
STAR Custom Rules:
Default Rules: Enabled
Custom Rules: Per client requirements
Remote Shell (Complete Feature):
Enable: Per group policy
Require Approval: Enabled
Session Timeout: 30 minutes
Network Controls:
Firewall Control: Enabled
Device Control: Enabled
Network Quarantine: Configurable per group
Anti-Tamper:
Protection: Enabled
Password: Set unique per client (store in password manager)
Agent UI:
Tray Icon: Show
User Notifications: Enabled
Allow Local Upgrade: Disabled2.3 Configure Baseline Exclusions
Pre-configure common exclusions using the Exclusions Catalog:
- Navigate to Settings → Exclusions
- Click Exclusions Catalog
- Enable recommended exclusions for:
- Microsoft Windows (built-in)
- Microsoft Office
- Microsoft SQL Server (if applicable)
- Microsoft Exchange (if applicable)
- Backup software (Veeam, Datto, etc.)
- RMM agent (NinjaRMM, Datto RMM, etc.)
Client-Specific Exclusions Template:
# Document all exclusions with justification
Exclusion Request Form:
Client: [Client Name]
Requested By: [Name]
Date: [Date]
Exclusions:
- Path: C:\Program Files\[Application]\
Type: Path
Scope: Site
Justification: [Business application - vendor recommendation]
Vendor Doc: [Link to vendor KB article]
- Hash: [SHA256]
Type: Hash
Scope: Site
Justification: [Custom internal application]
Reviewed By: [Analyst name]
Review Date: [Date]Common MSP Client Exclusions:
| Application | Exclusion Type | Path/Process |
|---|---|---|
| QuickBooks | Path | C:\Program Files\Intuit\* |
| Sage | Path | C:\Program Files\Sage\* |
| AutoCAD | Path | C:\Program Files\Autodesk\* |
| SQL Server | Path | C:\Program Files\Microsoft SQL Server\* |
| Backup Exec | Process | beremote.exe, beserver.exe |
| Veeam | Path | C:\Program Files\Veeam\* |
| NinjaRMM | Process | NinjaRMMAgent.exe |
2.4 Configure Notifications and Integrations
Email Notifications:
- Navigate to Settings → Notifications
- Configure alerts for:
- New threats detected (High/Critical)
- Agent offline > 24 hours
- Agent health issues
- Policy changes
- Unresolved threats > 1 hour
Recipients:
- MSP SOC team distribution list
- Client IT contact (optional - discuss with client)
SIEM/Syslog Integration (if applicable):
- Navigate to Settings → Integrations
- Configure Syslog forwarding:
- Server: Client SIEM IP
- Port: 514 (UDP) or 6514 (TCP/TLS)
- Format: CEF or LEEF
NinjaRMM Integration:
Configure custom fields in NinjaRMM to receive SentinelOne status:
s1_agent_statuss1_agent_versions1_protection_statuss1_console_connectivitys1_last_threat_date
Phase 3: Pilot Deployment (Days 5-10)
3.1 Select Pilot Endpoints
Pilot Selection Criteria:
- 3-5 endpoints representing different roles
- Include at least one from each endpoint type (workstation, laptop, server)
- Users who can provide constructive feedback
- Non-critical systems (avoid exec assistants, finance during month-end)
Pilot Group Composition Example:
| User | Role | Endpoint Type | Reason |
|---|---|---|---|
| IT Admin | Technical | Workstation | Can identify technical issues |
| Sales Rep | Standard | Laptop | Mobile worker, various networks |
| Operations | Power User | Workstation | Heavy application usage |
| Test Server | N/A | Server | Validate server policy |
3.2 Remove Existing Security Software
Before SentinelOne installation, remove conflicting software:
<#
.SYNOPSIS
Pre-deployment cleanup - removes conflicting security software
.DESCRIPTION
Identifies and removes common AV/EDR products before SentinelOne deployment
#>
function Remove-ConflictingSecurity {
param(
[switch]$WhatIf
)
$removalTools = @{
"McAfee" = @{
Detection = "McAfee*"
RemovalTool = "https://download.mcafee.com/molbin/iss-loc/SupportTools/MCPR/MCPR.exe"
Instructions = "Run MCPR.exe with /silent switch"
}
"Symantec" = @{
Detection = "Symantec*", "Norton*"
RemovalTool = "CleanWipe utility from Symantec support"
Instructions = "Download CleanWipe from Broadcom support portal"
}
"Trend Micro" = @{
Detection = "Trend Micro*"
RemovalTool = "Built-in uninstaller with password override"
Instructions = "Use WFBS uninstall password or contact Trend support"
}
"CrowdStrike" = @{
Detection = "CrowdStrike*"
RemovalTool = "CsUninstallTool.exe"
Instructions = "Requires maintenance token from CrowdStrike console"
}
"Sophos" = @{
Detection = "Sophos*"
RemovalTool = "Built-in uninstaller"
Instructions = "May require tamper protection password"
}
"Webroot" = @{
Detection = "Webroot*"
RemovalTool = "wsasme.exe /uninstall"
Instructions = "Requires removal password from Webroot console"
}
}
Write-Host "Scanning for conflicting security software..." -ForegroundColor Cyan
# Check installed products
$installedProducts = Get-CimInstance Win32_Product |
Where-Object { $_.Name -match "McAfee|Symantec|Norton|Trend|CrowdStrike|Sophos|Webroot|ESET|Kaspersky|Bitdefender|Malwarebytes" }
# Check running services
$securityServices = Get-Service |
Where-Object { $_.DisplayName -match "McAfee|Symantec|Norton|Trend|CrowdStrike|Sophos|Webroot|ESET|Kaspersky|Bitdefender" }
if ($installedProducts) {
Write-Host "`nInstalled Security Products:" -ForegroundColor Yellow
$installedProducts | ForEach-Object {
Write-Host " - $($_.Name) v$($_.Version)"
}
}
if ($securityServices) {
Write-Host "`nRunning Security Services:" -ForegroundColor Yellow
$securityServices | ForEach-Object {
Write-Host " - $($_.DisplayName) [$($_.Status)]"
}
}
if (-not $installedProducts -and -not $securityServices) {
Write-Host "`n[PASS] No conflicting security software detected" -ForegroundColor Green
return
}
if ($WhatIf) {
Write-Host "`n[WHATIF] Would remove the above products" -ForegroundColor Cyan
return
}
# Attempt removal
foreach ($product in $installedProducts) {
Write-Host "`nAttempting to remove: $($product.Name)" -ForegroundColor Yellow
try {
# Try standard MSI uninstall
$result = $product.Uninstall()
if ($result.ReturnValue -eq 0) {
Write-Host "[SUCCESS] Removed $($product.Name)" -ForegroundColor Green
} else {
Write-Host "[WARNING] Standard uninstall returned code $($result.ReturnValue)" -ForegroundColor Yellow
Write-Host " Manual removal may be required - check vendor documentation"
}
} catch {
Write-Host "[ERROR] Failed to remove: $($_.Exception.Message)" -ForegroundColor Red
Write-Host " Use vendor-specific removal tool"
}
}
Write-Host "`n[INFO] Reboot recommended after security software removal" -ForegroundColor Cyan
}
# Run with -WhatIf first to preview
Remove-ConflictingSecurity -WhatIf
# Then run without -WhatIf to execute removal
# Remove-ConflictingSecurity3.3 Deploy to Pilot Group
Deployment Method Selection:
| Method | Best For | Automation Level |
|---|---|---|
| Manual MSI | 1-5 endpoints, testing | None |
| RMM Push | Managed endpoints | High |
| GPO | Domain-joined, consistent | High |
| Intune | Azure AD joined, modern | High |
| SCCM/MECM | Enterprise, existing infrastructure | High |
RMM Deployment (NinjaRMM Example):
-
Upload SentinelOne MSI to NinjaRMM:
- Navigate to Administration → Library → Software Repository
- Upload
SentinelInstaller_windows_64bit_vX.X.X.msi
-
Create deployment script:
# NinjaRMM SentinelOne Deployment Script
# Deploy to: Pilot group devices
$installerPath = "C:\ProgramData\NinjaRMMAgent\SentinelInstaller.msi"
$siteToken = "YOUR_SITE_TOKEN_HERE"
$logPath = "C:\BIN\LOGS-$(Get-Date -Format 'yyyyMMdd')-SentinelOne-Install.log"
# Ensure log directory exists
New-Item -Path "C:\BIN" -ItemType Directory -Force -ErrorAction SilentlyContinue
# Check if already installed
$existingService = Get-Service -Name "SentinelAgent" -ErrorAction SilentlyContinue
if ($existingService -and $existingService.Status -eq "Running") {
Write-Host "SentinelOne already installed and running"
Ninja-Property-Set s1_agent_status "INSTALLED"
exit 0
}
# Download installer from NinjaRMM repository (or use pre-staged path)
# This assumes installer is staged via NinjaRMM file distribution
# Install SentinelOne
$arguments = "/i `"$installerPath`" /qn SITE_TOKEN=`"$siteToken`" /l*v `"$logPath`""
$process = Start-Process msiexec.exe -ArgumentList $arguments -Wait -PassThru -NoNewWindow
# Wait for service to start
$maxWait = 120
$waited = 0
while ($waited -lt $maxWait) {
$service = Get-Service -Name "SentinelAgent" -ErrorAction SilentlyContinue
if ($service -and $service.Status -eq "Running") {
Write-Host "SentinelOne installed successfully"
Ninja-Property-Set s1_agent_status "HEALTHY"
Ninja-Property-Set s1_install_date (Get-Date -Format "yyyy-MM-dd")
exit 0
}
Start-Sleep -Seconds 5
$waited += 5
}
# Installation failed
Write-Error "SentinelOne service failed to start"
Ninja-Property-Set s1_agent_status "INSTALL_FAILED"
exit 1- Deploy to pilot devices:
- Target pilot group/devices
- Schedule during maintenance window
- Monitor deployment status
3.4 Validate Pilot Deployment
Pilot Validation Checklist:
## Pilot Deployment Validation
Client: _______________
Date: _______________
Technician: _______________
### Console Verification
- [ ] All pilot agents appear in console
- [ ] Agents assigned to correct site
- [ ] Agents in correct groups
- [ ] Policy applied correctly (verify settings)
- [ ] Agent version matches expected
- [ ] All agents show "Connected" status
### Endpoint Verification (per device)
Device: _______________
- [ ] SentinelAgent service running
- [ ] System tray icon visible
- [ ] No user-reported issues
- [ ] Application compatibility verified
- [ ] Performance acceptable (no slowdown complaints)
- [ ] Network connectivity maintained
### Threat Detection Test
- [ ] EICAR test file detected and blocked
- [ ] Alert visible in console
- [ ] Threat auto-remediated (if configured)
- [ ] Notification received (if configured)
### Complete Features Validation (Singularity Complete)
- [ ] Deep Visibility data populating
- [ ] Process timeline visible
- [ ] Network connections logged
- [ ] Remote Shell accessible (if enabled)
- [ ] STAR rules active
### Issues Identified
1. _______________
2. _______________
3. _______________
### Exclusions Required
1. _______________
2. _______________
3. _______________
### Sign-off
Pilot Validated: [ ] Yes [ ] No - Issues to resolve
Ready for Full Deployment: [ ] Yes [ ] NoEICAR Test Procedure:
# Create EICAR test file to validate detection
# IMPORTANT: This is a harmless test file recognized by all AV products
$eicarString = 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'
$testPath = "$env:USERPROFILE\Desktop\eicar-test.txt"
Write-Host "Creating EICAR test file..."
Write-Host "Expected behavior: SentinelOne should detect and block this file"
Write-Host ""
try {
Set-Content -Path $testPath -Value $eicarString -ErrorAction Stop
Write-Host "[WARNING] File was created - SentinelOne may not be protecting" -ForegroundColor Yellow
} catch {
Write-Host "[SUCCESS] File creation blocked - SentinelOne is protecting" -ForegroundColor Green
}
# Check if file exists (should be quarantined immediately)
Start-Sleep -Seconds 2
if (Test-Path $testPath) {
Write-Host "[WARNING] Test file still exists - check SentinelOne status" -ForegroundColor Yellow
Remove-Item $testPath -Force -ErrorAction SilentlyContinue
} else {
Write-Host "[SUCCESS] Test file was quarantined" -ForegroundColor Green
}3.5 Pilot Feedback Collection
Collect feedback after 3-5 business days:
## Pilot User Feedback Form
User: _______________
Device: _______________
Pilot Duration: ___ days
### Performance
1. Have you noticed any slowdown since installation?
[ ] No change [ ] Slight slowdown [ ] Significant slowdown
2. Application startup times:
[ ] Same [ ] Slightly slower [ ] Much slower
3. Boot/login time:
[ ] Same [ ] Slightly slower [ ] Much slower
### Compatibility
4. Any applications not working correctly?
[ ] No [ ] Yes - List: _______________
5. Any error messages observed?
[ ] No [ ] Yes - Details: _______________
### User Experience
6. Frequency of notifications:
[ ] None [ ] Occasional [ ] Too many
7. Any disruption to your work?
[ ] No [ ] Minor [ ] Significant
### Additional Comments
_______________________________________________Phase 4: Full Deployment (Days 10-21)
4.1 Wave-Based Deployment Schedule
Recommended Deployment Waves:
| Wave | Endpoints | Timeline | Description |
|---|---|---|---|
| 1 | Pilot | Days 5-10 | Validation complete |
| 2 | Standard Workstations (25%) | Days 11-12 | Low-risk endpoints |
| 3 | Standard Workstations (50%) | Days 13-14 | Continue workstations |
| 4 | Standard Workstations (remaining) | Days 15-16 | Complete workstations |
| 5 | Laptops/Remote | Days 17-18 | Mobile devices |
| 6 | Non-Critical Servers | Days 19-20 | File servers, print servers |
| 7 | Critical Servers | Day 21+ | Domain controllers, DB servers |
Between Each Wave:
- Validate deployment success
- Review alerts for false positives
- Add required exclusions
- Collect user feedback
- Address any issues before proceeding
4.2 Mass Deployment Script
<#
.SYNOPSIS
Mass SentinelOne deployment orchestration script
.DESCRIPTION
Deploys SentinelOne to multiple endpoints with progress tracking and error handling
.PARAMETER ComputerList
Array of computer names or path to text file
.PARAMETER SiteToken
SentinelOne site token
.PARAMETER WaveSize
Number of concurrent deployments
#>
param(
[Parameter(Mandatory=$true)]
[string[]]$ComputerList,
[Parameter(Mandatory=$true)]
[string]$SiteToken,
[Parameter(Mandatory=$true)]
[string]$InstallerShare,
[int]$WaveSize = 10,
[string]$LogPath = "C:\BIN\LOGS-S1-MassDeployment-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv"
)
# If ComputerList is a file path, read contents
if ($ComputerList.Count -eq 1 -and (Test-Path $ComputerList[0])) {
$ComputerList = Get-Content $ComputerList[0]
}
Write-Host "=== SentinelOne Mass Deployment ===" -ForegroundColor Cyan
Write-Host "Target: $($ComputerList.Count) computers"
Write-Host "Wave Size: $WaveSize"
Write-Host "Installer: $InstallerShare"
Write-Host ""
$results = @()
$completed = 0
# Process in waves
for ($i = 0; $i -lt $ComputerList.Count; $i += $WaveSize) {
$wave = $ComputerList[$i..([Math]::Min($i + $WaveSize - 1, $ComputerList.Count - 1))]
$waveNumber = [Math]::Floor($i / $WaveSize) + 1
Write-Host "`nWave $waveNumber - Deploying to $($wave.Count) computers..." -ForegroundColor Yellow
$waveResults = $wave | ForEach-Object -ThrottleLimit $WaveSize -Parallel {
$computer = $_
$siteToken = $using:SiteToken
$installerShare = $using:InstallerShare
$result = [PSCustomObject]@{
ComputerName = $computer
StartTime = Get-Date
EndTime = $null
Status = "Pending"
Message = ""
ServiceRunning = $false
}
try {
# Test connectivity
if (-not (Test-Connection -ComputerName $computer -Count 1 -Quiet)) {
$result.Status = "Offline"
$result.Message = "Computer not reachable"
return $result
}
# Check if already installed
$service = Invoke-Command -ComputerName $computer -ScriptBlock {
Get-Service -Name "SentinelAgent" -ErrorAction SilentlyContinue
} -ErrorAction SilentlyContinue
if ($service -and $service.Status -eq "Running") {
$result.Status = "AlreadyInstalled"
$result.Message = "Agent already running"
$result.ServiceRunning = $true
return $result
}
# Copy installer
$remotePath = "\\$computer\C$\Temp\SentinelInstaller.msi"
Copy-Item -Path $installerShare -Destination $remotePath -Force
# Install remotely
$installResult = Invoke-Command -ComputerName $computer -ScriptBlock {
param($token)
$process = Start-Process msiexec.exe -ArgumentList "/i C:\Temp\SentinelInstaller.msi /qn SITE_TOKEN=`"$token`" /l*v C:\Temp\S1-install.log" -Wait -PassThru -NoNewWindow
# Wait for service
$attempts = 0
while ($attempts -lt 24) {
$svc = Get-Service -Name "SentinelAgent" -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -eq "Running") {
return @{ Success = $true; ExitCode = $process.ExitCode }
}
Start-Sleep -Seconds 5
$attempts++
}
return @{ Success = $false; ExitCode = $process.ExitCode }
} -ArgumentList $siteToken
if ($installResult.Success) {
$result.Status = "Success"
$result.Message = "Installed successfully"
$result.ServiceRunning = $true
} else {
$result.Status = "Failed"
$result.Message = "Service not running after install (Exit: $($installResult.ExitCode))"
}
# Cleanup
Remove-Item -Path $remotePath -Force -ErrorAction SilentlyContinue
} catch {
$result.Status = "Error"
$result.Message = $_.Exception.Message
}
$result.EndTime = Get-Date
return $result
}
$results += $waveResults
$completed += $wave.Count
# Display wave results
$waveResults | ForEach-Object {
$color = switch ($_.Status) {
"Success" { "Green" }
"AlreadyInstalled" { "Cyan" }
"Failed" { "Red" }
"Error" { "Red" }
default { "Yellow" }
}
Write-Host " $($_.ComputerName): $($_.Status) - $($_.Message)" -ForegroundColor $color
}
Write-Host "Progress: $completed / $($ComputerList.Count) ($([Math]::Round($completed/$ComputerList.Count*100))%)"
# Brief pause between waves
if ($i + $WaveSize -lt $ComputerList.Count) {
Start-Sleep -Seconds 10
}
}
# Export results
$results | Export-Csv -Path $LogPath -NoTypeInformation
# Summary
Write-Host "`n=== Deployment Summary ===" -ForegroundColor Cyan
Write-Host "Total: $($results.Count)"
Write-Host "Success: $(($results | Where-Object { $_.Status -eq 'Success' }).Count)" -ForegroundColor Green
Write-Host "Already Installed: $(($results | Where-Object { $_.Status -eq 'AlreadyInstalled' }).Count)" -ForegroundColor Cyan
Write-Host "Failed: $(($results | Where-Object { $_.Status -eq 'Failed' }).Count)" -ForegroundColor Red
Write-Host "Errors: $(($results | Where-Object { $_.Status -eq 'Error' }).Count)" -ForegroundColor Red
Write-Host "Offline: $(($results | Where-Object { $_.Status -eq 'Offline' }).Count)" -ForegroundColor Yellow
Write-Host "`nResults exported to: $LogPath"4.3 Server Deployment Considerations
Pre-Deployment Server Checklist:
## Server Deployment Checklist
Server: _______________
Role: _______________
Criticality: [ ] Low [ ] Medium [ ] High [ ] Critical
### Pre-Deployment
- [ ] Maintenance window scheduled
- [ ] Change management approval obtained
- [ ] Backup verified (within last 24 hours)
- [ ] Rollback plan documented
- [ ] Stakeholders notified
### Server-Specific Exclusions
Application exclusions added:
- [ ] SQL Server (if applicable)
- [ ] Exchange Server (if applicable)
- [ ] IIS/Web applications (if applicable)
- [ ] Backup software (if applicable)
- [ ] Line-of-business applications
### Policy Configuration
- [ ] Network Quarantine: DISABLED (production servers)
- [ ] Remote Shell: ENABLED (for investigation)
- [ ] Scan exclusions for high-I/O paths
### Deployment
- [ ] Agent installed successfully
- [ ] Service running
- [ ] Console connectivity verified
- [ ] Assigned to correct group
- [ ] Policy applied correctly
### Post-Deployment Validation
- [ ] All services started normally
- [ ] Application functionality verified
- [ ] No excessive alerts generated
- [ ] Performance within acceptable range
- [ ] Monitoring systems show healthy
### Sign-off
Deployed By: _______________
Date/Time: _______________
Post-Deployment Check: [ ] Pass [ ] FailPhase 5: Post-Deployment (Ongoing)
5.1 Deployment Completion Verification
Final Validation Script:
<#
.SYNOPSIS
Post-deployment validation for SentinelOne rollout
.DESCRIPTION
Verifies all endpoints are protected and reports deployment status
#>
function Get-S1DeploymentStatus {
param(
[Parameter(Mandatory=$true)]
[string]$ApiToken,
[Parameter(Mandatory=$true)]
[string]$ConsoleUrl,
[Parameter(Mandatory=$true)]
[string]$SiteId
)
$headers = @{
"Authorization" = "ApiToken $ApiToken"
"Content-Type" = "application/json"
}
# Get all agents for the site
$agents = @()
$cursor = $null
do {
$url = "$ConsoleUrl/web/api/v2.1/agents?siteIds=$SiteId&limit=1000"
if ($cursor) { $url += "&cursor=$cursor" }
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method GET
$agents += $response.data
$cursor = $response.pagination.nextCursor
} while ($cursor)
Write-Host "=== SentinelOne Deployment Status ===" -ForegroundColor Cyan
Write-Host "Site ID: $SiteId"
Write-Host "Total Agents: $($agents.Count)"
Write-Host ""
# Status breakdown
$statusGroups = $agents | Group-Object -Property isActive
$connected = ($statusGroups | Where-Object { $_.Name -eq "True" }).Count
$disconnected = ($statusGroups | Where-Object { $_.Name -eq "False" }).Count
Write-Host "Connection Status:" -ForegroundColor Yellow
Write-Host " Connected: $connected" -ForegroundColor Green
Write-Host " Disconnected: $disconnected" -ForegroundColor $(if($disconnected -gt 0){"Red"}else{"Green"})
# Protection status
$protectionGroups = $agents | Group-Object -Property infected
$clean = ($protectionGroups | Where-Object { $_.Name -eq "False" }).Count
$infected = ($protectionGroups | Where-Object { $_.Name -eq "True" }).Count
Write-Host "`nProtection Status:" -ForegroundColor Yellow
Write-Host " Clean: $clean" -ForegroundColor Green
Write-Host " Active Threats: $infected" -ForegroundColor $(if($infected -gt 0){"Red"}else{"Green"})
# Agent versions
Write-Host "`nAgent Versions:" -ForegroundColor Yellow
$agents | Group-Object -Property agentVersion | Sort-Object Count -Descending | ForEach-Object {
Write-Host " $($_.Name): $($_.Count)"
}
# OS Distribution
Write-Host "`nOS Distribution:" -ForegroundColor Yellow
$agents | Group-Object -Property osName | Sort-Object Count -Descending | ForEach-Object {
Write-Host " $($_.Name): $($_.Count)"
}
# List disconnected agents
if ($disconnected -gt 0) {
Write-Host "`nDisconnected Agents (requires attention):" -ForegroundColor Red
$agents | Where-Object { -not $_.isActive } | ForEach-Object {
Write-Host " - $($_.computerName) (Last seen: $($_.lastActiveDate))"
}
}
# Return summary object
return [PSCustomObject]@{
TotalAgents = $agents.Count
Connected = $connected
Disconnected = $disconnected
Clean = $clean
ActiveThreats = $infected
DeploymentComplete = ($disconnected -eq 0 -and $infected -eq 0)
}
}
# Usage:
# $status = Get-S1DeploymentStatus -ApiToken "YOUR_TOKEN" -ConsoleUrl "https://tenant.sentinelone.net" -SiteId "SITE_ID"5.2 Configure Ongoing Monitoring
NinjaRMM Health Check Integration:
Add the existing Check-SentinelOne-Health.ps1 script as a scheduled task in NinjaRMM:
- Frequency: Every 4 hours
- Alert on:
s1_agent_statusnot equal to "HEALTHY"
Create SentinelOne Monitoring Dashboard:
Configure NinjaRMM dashboard widgets for:
- Agent status distribution
- Threat detection trends
- Disconnected agents
- Version compliance
5.3 Establish Ongoing Procedures
Weekly Tasks:
- Review threat detections and ensure resolution
- Check for disconnected agents
- Review exclusion requests
- Monitor Deep Visibility alerts
Monthly Tasks:
- Review and tune policies
- Audit exclusion list (remove unnecessary)
- Check for agent updates
- Generate client security report
- Review STAR rule effectiveness
Quarterly Tasks:
- Comprehensive policy review
- Agent version standardization
- Exclusion audit and cleanup
- Client security posture presentation
- Training refresh for SOC team
VERIFICATION
Deployment Success Criteria:
| Metric | Target | Measurement |
|---|---|---|
| Agent Coverage | 100% | All endpoints have agent installed |
| Agent Connectivity | >98% | Agents connected to console |
| Policy Compliance | 100% | Correct policy applied to all agents |
| Version Currency | >95% | Running current GA version |
| Active Threats | 0 | No unresolved threats |
| False Positive Rate | `<5% | Minimal tuning required |
Client Sign-Off Checklist:
## SentinelOne Deployment Sign-Off
Client: _______________
Deployment Date Range: _______________ to _______________
MSP Engineer: _______________
### Deployment Metrics
- Total endpoints deployed: ___
- Agent coverage: ___%
- Policy compliance: ___%
- Outstanding issues: ___
### Deliverables Completed
- [ ] All endpoints protected
- [ ] Policies configured per requirements
- [ ] Exclusions documented and applied
- [ ] Notifications configured
- [ ] Integration with RMM completed
- [ ] Client documentation provided
- [ ] Client contacts added to notifications (if requested)
### Training Provided
- [ ] Console navigation overview
- [ ] Threat response procedures
- [ ] Escalation process
### Outstanding Items
1. _______________
2. _______________
### Client Acceptance
I confirm that the SentinelOne deployment has been completed satisfactorily.
Client Signature: _______________
Date: _______________
MSP Signature: _______________
Date: _______________TROUBLESHOOTING
Common Deployment Issues
Issue: Agent not appearing in console after installation
# Verify agent status locally
$sentinelCtl = Get-Item "C:\Program Files\SentinelOne\Sentinel Agent*\SentinelCtl.exe" | Select-Object -First 1
& $sentinelCtl.FullName status
# Check connectivity
$consoleHost = (Get-ItemProperty "HKLM:\SOFTWARE\SentinelOne\Sentinel Agent").ManagementServerUrl
Test-NetConnection -ComputerName ([Uri]$consoleHost).Host -Port 443
# Check for proxy issues
netsh winhttp show proxy
# Force reconnection
Restart-Service -Name "SentinelAgent" -ForceIssue: High CPU/Memory usage after installation
# Check if initial scan is running
& "C:\Program Files\SentinelOne\Sentinel Agent*\SentinelCtl.exe" is_scan_in_progress
# Review agent activity
& "C:\Program Files\SentinelOne\Sentinel Agent*\SentinelCtl.exe" create_agent_analyzer_report -o "C:\Temp\S1-analyzer.txt" -m 60
# Common cause: Initial scan and cloud verification
# Wait 30-60 minutes for initial activities to completeIssue: Application blocked incorrectly (false positive)
- Identify the blocked file/process in console
- Navigate to Activity → Threats
- Find the detection and click for details
- If false positive:
- Click Actions → Mark as Threat → False Positive
- Create exclusion based on path, hash, or certificate
- Document exclusion with justification
Issue: Agent fails to install (Error 1603)
# Check installation log
$logPath = Get-ChildItem "C:\Temp" -Filter "*SentinelOne*Install*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Select-String -Path $logPath.FullName -Pattern "error|failed|return value 3" -Context 2,5
# Common causes:
# - Conflicting security software not fully removed
# - Insufficient disk space
# - Corrupted installer
# - Group Policy blocking installationCOMMANDS/SCRIPTS
Quick Reference Commands
# Check agent status
cd "C:\Program Files\SentinelOne\Sentinel Agent*"
.\SentinelCtl.exe status
# Check if scan is running
.\SentinelCtl.exe is_scan_in_progress
# Generate diagnostic report
.\SentinelCtl.exe create_agent_analyzer_report -o "C:\Temp\S1-diag.txt" -m 60
# Get agent UUID
.\SentinelCtl.exe uuid
# Check protection status
.\SentinelCtl.exe protection_status
# View agent log
Get-Content "C:\ProgramData\SentinelOne\Logs\Agent.log" -Tail 50
# Restart agent service
Restart-Service -Name "SentinelAgent" -Force
# Check agent version
(Get-Item "C:\Program Files\SentinelOne\Sentinel Agent*\SentinelAgent.exe").VersionInfo.FileVersionAPI Quick Reference
# Set up API headers
$apiToken = "YOUR_API_TOKEN"
$consoleUrl = "https://yourtenant.sentinelone.net"
$headers = @{
"Authorization" = "ApiToken $apiToken"
"Content-Type" = "application/json"
}
# List all sites
Invoke-RestMethod -Uri "$consoleUrl/web/api/v2.1/sites" -Headers $headers
# List agents for a site
Invoke-RestMethod -Uri "$consoleUrl/web/api/v2.1/agents?siteIds=SITE_ID" -Headers $headers
# Get threat summary
Invoke-RestMethod -Uri "$consoleUrl/web/api/v2.1/threats?siteIds=SITE_ID" -Headers $headers
# Get Deep Visibility events (Complete feature)
Invoke-RestMethod -Uri "$consoleUrl/web/api/v2.1/dv/events?siteIds=SITE_ID&query=YOUR_QUERY" -Headers $headersRELATED DOCUMENTATION
- HOWTO- SentinelOne Deploy Agent Manual Installation
- HOWTO- SentinelOne Deploy Agent via Group Policy
- HOWTO- SentinelOne Create and Manage Exclusion Policies
- HOWTO- SentinelOne Threat Investigation Workflow
- HOWTO- SentinelOne PowerShell API Automation
REVISION HISTORY
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-08 | CosmicBytez | Initial creation - Singularity Complete focus |