[CmdletBinding()] param( [ValidateSet("daily", "burst")] [string]$Kind = "daily", [ValidateRange(15, 480)] [int]$BurstDurationMinutes = 120 ) $ErrorActionPreference = "Stop" $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $monitorScript = Join-Path $scriptDir "run-ocsentinel-monitor.ps1" $secretPath = "C:\ProgramData\OCSentinel\secrets\ocsentinel-upload-secret.dat" $mutexName = "Global\OfficeComSentinelScan" function Get-NinjaBurstEnabled { if (Get-Command -Name "Get-NinjaProperty" -ErrorAction SilentlyContinue) { return [bool](Get-NinjaProperty -Name "ocsentinelburst" -Type "Checkbox") } if (Get-Command -Name "Ninja-Property-Get" -ErrorAction SilentlyContinue) { $value = Ninja-Property-Get -Name "ocsentinelburst" return [string]$value -match "^(1|true|yes)$" } Write-Warning "Ninja custom-field reader is unavailable; burst scan skipped." return $false } 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 $mutex = [Threading.Mutex]::new($false, $mutexName, [ref]$createdNew) try { if (-not $mutex.WaitOne(0)) { Write-Host "OfficeCom Sentinel scan skipped: another scan is already running." exit 0 } Write-Host "OfficeCom Sentinel scheduled $Kind scan started." $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 { if ($null -ne $mutex) { try { $mutex.ReleaseMutex() } catch { } $mutex.Dispose() } }