diff --git a/docs/ocsentinel-deployment.md b/docs/ocsentinel-deployment.md index fcc03b2..92c6ca2 100644 --- a/docs/ocsentinel-deployment.md +++ b/docs/ocsentinel-deployment.md @@ -39,12 +39,21 @@ The installer creates two Windows Scheduled Tasks running as `SYSTEM`: `04:00` and `06:59`, derived from its Windows `MachineGuid`. This distributes a fleet rollout instead of sending all reports at the same time. - `OCSentinel Burst Check`: runs every five minutes. It performs no scan unless - the NinjaOne device custom field `ocsentinelburst` is enabled. + the NinjaOne device custom field `ocsentinelburst` is enabled. Once enabled, + it scans for two hours and then disables itself automatically. Create `ocsentinelburst` as a device-level `Checkbox` custom field and allow -automation read access. Set it to `true` for a device to begin the five-minute -burst scans; clear it to stop them. The normal daily scan continues regardless -of the checkbox. +automation read and write access. Set it to `true` for a device to begin the +five-minute burst scans; clear it to stop them early. The normal daily scan +continues regardless of the checkbox. + +Create these accompanying device custom fields and allow automation write +access: + +| Field name | Type | Purpose | +| --- | --- | --- | +| `ocsentinelburstuntilutc` | Date/Time | UTC time at which the active burst ends | +| `ocsentinelburststatus` | Text | `idle`, `active until ...`, or `completed` | ## Upload Reliability And Client Health diff --git a/installer/runtime-run-ocsentinel-scheduled.ps1 b/installer/runtime-run-ocsentinel-scheduled.ps1 index f48aecb..1449087 100644 --- a/installer/runtime-run-ocsentinel-scheduled.ps1 +++ b/installer/runtime-run-ocsentinel-scheduled.ps1 @@ -1,7 +1,9 @@ [CmdletBinding()] param( [ValidateSet("daily", "burst")] - [string]$Kind = "daily" + [string]$Kind = "daily", + [ValidateRange(15, 480)] + [int]$BurstDurationMinutes = 120 ) $ErrorActionPreference = "Stop" @@ -25,9 +27,69 @@ function Get-NinjaBurstEnabled { return $false } -if ($Kind -eq "burst" -and -not (Get-NinjaBurstEnabled)) { - Write-Host "OfficeCom Sentinel burst check: disabled." - exit 0 +function Get-NinjaValue { + param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Type) + + try { + if (Get-Command -Name "Get-NinjaProperty" -ErrorAction SilentlyContinue) { + return Get-NinjaProperty -Name $Name -Type $Type + } + if (Get-Command -Name "Ninja-Property-Get" -ErrorAction SilentlyContinue) { + return Ninja-Property-Get -Name $Name + } + } + catch { + Write-Warning "Could not read Ninja field '$Name': $($_.Exception.Message)" + } + return $null +} + +function Set-NinjaValue { + param([Parameter(Mandatory)][string]$Name, [AllowEmptyString()][string]$Value, [Parameter(Mandatory)][string]$Type) + + try { + if (Get-Command -Name "Set-NinjaProperty" -ErrorAction SilentlyContinue) { + Set-NinjaProperty -Name $Name -Value $Value -Type $Type -Force | Out-Null + return $true + } + if (Get-Command -Name "Ninja-Property-Set" -ErrorAction SilentlyContinue) { + Ninja-Property-Set -Name $Name -Value $Value | Out-Null + return $true + } + } + catch { + Write-Warning "Could not update Ninja field '$Name': $($_.Exception.Message)" + } + return $false +} + +if ($Kind -eq "burst") { + if (-not (Get-NinjaBurstEnabled)) { + Set-NinjaValue -Name "ocsentinelburststatus" -Value "idle" -Type "Text" | Out-Null + Write-Host "OfficeCom Sentinel burst check: disabled." + exit 0 + } + + $now = [DateTimeOffset]::UtcNow + $untilValue = Get-NinjaValue -Name "ocsentinelburstuntilutc" -Type "DateTime" + $until = $null + if (-not [string]::IsNullOrWhiteSpace([string]$untilValue)) { + try { $until = [DateTimeOffset]$untilValue } catch { Write-Warning "Burst end time is invalid and will be restarted." } + } + + if ($null -eq $until) { + $until = $now.AddMinutes($BurstDurationMinutes) + Set-NinjaValue -Name "ocsentinelburstuntilutc" -Value $until.ToString("o") -Type "DateTime" | Out-Null + Write-Host "OfficeCom Sentinel burst window started until $($until.ToString('u'))." + } + elseif ($until -le $now) { + Set-NinjaValue -Name "ocsentinelburst" -Value "false" -Type "Checkbox" | Out-Null + Set-NinjaValue -Name "ocsentinelburststatus" -Value "completed" -Type "Text" | Out-Null + Write-Host "OfficeCom Sentinel burst window completed and was disabled." + exit 0 + } + + Set-NinjaValue -Name "ocsentinelburststatus" -Value "active until $($until.ToUniversalTime().ToString('o'))" -Type "Text" | Out-Null } $createdNew = $false @@ -39,7 +101,11 @@ try { } Write-Host "OfficeCom Sentinel scheduled $Kind scan started." - & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $monitorScript -Mode status -UploadMode required -SecretPath $secretPath -SuppressTriggerExit + $monitorArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $monitorScript, "-Mode", "status", "-UploadMode", "required", "-SecretPath", $secretPath, "-SuppressTriggerExit") + if ($Kind -eq "burst") { + $monitorArgs += @("-LookbackDays", "1", "-TopCount", "25") + } + & powershell.exe @monitorArgs exit $LASTEXITCODE } finally { diff --git a/scripts/run-ocsentinel-scheduled.ps1 b/scripts/run-ocsentinel-scheduled.ps1 index f48aecb..1449087 100644 --- a/scripts/run-ocsentinel-scheduled.ps1 +++ b/scripts/run-ocsentinel-scheduled.ps1 @@ -1,7 +1,9 @@ [CmdletBinding()] param( [ValidateSet("daily", "burst")] - [string]$Kind = "daily" + [string]$Kind = "daily", + [ValidateRange(15, 480)] + [int]$BurstDurationMinutes = 120 ) $ErrorActionPreference = "Stop" @@ -25,9 +27,69 @@ function Get-NinjaBurstEnabled { return $false } -if ($Kind -eq "burst" -and -not (Get-NinjaBurstEnabled)) { - Write-Host "OfficeCom Sentinel burst check: disabled." - exit 0 +function Get-NinjaValue { + param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Type) + + try { + if (Get-Command -Name "Get-NinjaProperty" -ErrorAction SilentlyContinue) { + return Get-NinjaProperty -Name $Name -Type $Type + } + if (Get-Command -Name "Ninja-Property-Get" -ErrorAction SilentlyContinue) { + return Ninja-Property-Get -Name $Name + } + } + catch { + Write-Warning "Could not read Ninja field '$Name': $($_.Exception.Message)" + } + return $null +} + +function Set-NinjaValue { + param([Parameter(Mandatory)][string]$Name, [AllowEmptyString()][string]$Value, [Parameter(Mandatory)][string]$Type) + + try { + if (Get-Command -Name "Set-NinjaProperty" -ErrorAction SilentlyContinue) { + Set-NinjaProperty -Name $Name -Value $Value -Type $Type -Force | Out-Null + return $true + } + if (Get-Command -Name "Ninja-Property-Set" -ErrorAction SilentlyContinue) { + Ninja-Property-Set -Name $Name -Value $Value | Out-Null + return $true + } + } + catch { + Write-Warning "Could not update Ninja field '$Name': $($_.Exception.Message)" + } + return $false +} + +if ($Kind -eq "burst") { + if (-not (Get-NinjaBurstEnabled)) { + Set-NinjaValue -Name "ocsentinelburststatus" -Value "idle" -Type "Text" | Out-Null + Write-Host "OfficeCom Sentinel burst check: disabled." + exit 0 + } + + $now = [DateTimeOffset]::UtcNow + $untilValue = Get-NinjaValue -Name "ocsentinelburstuntilutc" -Type "DateTime" + $until = $null + if (-not [string]::IsNullOrWhiteSpace([string]$untilValue)) { + try { $until = [DateTimeOffset]$untilValue } catch { Write-Warning "Burst end time is invalid and will be restarted." } + } + + if ($null -eq $until) { + $until = $now.AddMinutes($BurstDurationMinutes) + Set-NinjaValue -Name "ocsentinelburstuntilutc" -Value $until.ToString("o") -Type "DateTime" | Out-Null + Write-Host "OfficeCom Sentinel burst window started until $($until.ToString('u'))." + } + elseif ($until -le $now) { + Set-NinjaValue -Name "ocsentinelburst" -Value "false" -Type "Checkbox" | Out-Null + Set-NinjaValue -Name "ocsentinelburststatus" -Value "completed" -Type "Text" | Out-Null + Write-Host "OfficeCom Sentinel burst window completed and was disabled." + exit 0 + } + + Set-NinjaValue -Name "ocsentinelburststatus" -Value "active until $($until.ToUniversalTime().ToString('o'))" -Type "Text" | Out-Null } $createdNew = $false @@ -39,7 +101,11 @@ try { } Write-Host "OfficeCom Sentinel scheduled $Kind scan started." - & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $monitorScript -Mode status -UploadMode required -SecretPath $secretPath -SuppressTriggerExit + $monitorArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $monitorScript, "-Mode", "status", "-UploadMode", "required", "-SecretPath", $secretPath, "-SuppressTriggerExit") + if ($Kind -eq "burst") { + $monitorArgs += @("-LookbackDays", "1", "-TopCount", "25") + } + & powershell.exe @monitorArgs exit $LASTEXITCODE } finally { diff --git a/src/OCSentinelCli/OCSentinelCli.csproj b/src/OCSentinelCli/OCSentinelCli.csproj index 0ce2e82..5cbf053 100644 --- a/src/OCSentinelCli/OCSentinelCli.csproj +++ b/src/OCSentinelCli/OCSentinelCli.csproj @@ -9,10 +9,10 @@ OCSentinelCli OfficeCom Sentinel OfficeCom - 1.3.3 - 1.3.3.0 - 1.3.3.0 - 1.3.3 + 1.3.4 + 1.3.4.0 + 1.3.4.0 + 1.3.4