Add NinjaOne bootstrap deployment script
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 24s
OfficeCom Sentinel Client / build-client-windows (push) Has been cancelled

This commit is contained in:
OfficeCom Codex
2026-07-25 01:34:52 +02:00
parent ea90ddd1f6
commit f9f4d862bf
2 changed files with 105 additions and 1 deletions

View File

@@ -32,7 +32,16 @@ powershell -ExecutionPolicy Bypass -File .\build\build-release-manifest.ps1 `
## NinjaOne Tasks
Initial install/update:
Create a PowerShell script in NinjaOne named `OCSentinel - Installieren oder aktualisieren`.
Run it as `SYSTEM` in 64-bit PowerShell and copy the content of
`scripts/bootstrap-ocsentinel-ninja.ps1` into the NinjaOne script editor.
It is idempotent: new devices install the current package, while installed devices
only update when a newer manifest version is published.
Use it for the one-time rollout and, later, as the monthly update task. For an
initial validation scan, add `-RunInitialStatusScan` to the script parameters.
Installed-client update only:
```powershell
& "C:\Program Files\OCSentinel\scripts\update-ocsentinel.ps1" `

View File

@@ -0,0 +1,95 @@
[CmdletBinding()]
param(
[string]$ManifestUrl = "https://gitea.officecom.cloud/officecom/oc-sentinel/raw/main/release/stable/version.json",
[switch]$RunInitialStatusScan
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$installRoot = Join-Path $env:ProgramFiles "OCSentinel"
$updaterPath = Join-Path $installRoot "scripts\update-ocsentinel.ps1"
$monitorPath = Join-Path $installRoot "scripts\run-ocsentinel-monitor.ps1"
$appPath = Join-Path $installRoot "app\OCSentinelCli.exe"
function Assert-ArtifactSignature {
param([Parameter(Mandatory)][string]$ExecutablePath)
$signature = Get-AuthenticodeSignature -FilePath $ExecutablePath
if ($signature.Status -notin @("Valid", "NotSigned")) {
throw "Executable signature validation failed with status: $($signature.Status)"
}
if ($signature.Status -eq "NotSigned") {
Write-Warning "The package hash was verified, but OCSentinelCli.exe is not code-signed yet."
}
}
if (Test-Path -LiteralPath $updaterPath) {
Write-Host "Existing OCSentinel installation found. Checking for updates."
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $updaterPath -ManifestUrl $ManifestUrl
if ($LASTEXITCODE -ne 0) {
throw "OCSentinel updater exited with code $LASTEXITCODE"
}
}
else {
Write-Host "Reading OCSentinel release manifest: $ManifestUrl"
$manifest = Invoke-RestMethod -Method Get -Uri $ManifestUrl -TimeoutSec 60
if ([string]::IsNullOrWhiteSpace($manifest.version) -or [string]::IsNullOrWhiteSpace($manifest.artifactUrl) -or [string]::IsNullOrWhiteSpace($manifest.sha256)) {
throw "Release manifest is missing version, artifactUrl, or sha256."
}
$downloadRoot = Join-Path $env:ProgramData ("OCSentinel\\bootstrap\\" + [Guid]::NewGuid().ToString("N"))
$zipPath = Join-Path $downloadRoot "OCSentinelClient.zip"
$extractRoot = Join-Path $downloadRoot "payload"
try {
New-Item -ItemType Directory -Force -Path $extractRoot | Out-Null
Write-Host "Downloading OCSentinel $($manifest.version)"
Invoke-WebRequest -Uri ([string]$manifest.artifactUrl) -OutFile $zipPath -TimeoutSec 300
$actualHash = (Get-FileHash -LiteralPath $zipPath -Algorithm SHA256).Hash.ToLowerInvariant()
$expectedHash = ([string]$manifest.sha256).ToLowerInvariant()
if ($actualHash -ne $expectedHash) {
throw "SHA-256 mismatch for the downloaded OCSentinel package."
}
Write-Host "Package hash verified. Extracting release payload."
Expand-Archive -LiteralPath $zipPath -DestinationPath $extractRoot -Force
$payloadApp = Get-ChildItem -Path $extractRoot -Recurse -Filter "OCSentinelCli.exe" | Select-Object -First 1
$installer = Get-ChildItem -Path $extractRoot -Recurse -Filter "install-ocsentinel.ps1" | Select-Object -First 1
if ($null -eq $payloadApp -or $null -eq $installer) {
throw "The downloaded package is incomplete."
}
Assert-ArtifactSignature -ExecutablePath $payloadApp.FullName
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $installer.FullName
if ($LASTEXITCODE -ne 0) {
throw "OCSentinel installer exited with code $LASTEXITCODE"
}
}
finally {
if (Test-Path -LiteralPath $downloadRoot) {
Remove-Item -LiteralPath $downloadRoot -Recurse -Force
}
}
}
if (-not (Test-Path -LiteralPath $appPath)) {
throw "OCSentinel installation completed, but the client executable was not found."
}
if ($RunInitialStatusScan) {
if (-not (Test-Path -LiteralPath $monitorPath)) {
throw "OCSentinel was installed, but the monitor script is missing."
}
Write-Host "Running initial OCSentinel status scan."
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $monitorPath -Mode status -OutputPath "..\\reports\\ocsentinel-summary.json"
if ($LASTEXITCODE -ne 0) {
throw "Initial OCSentinel status scan exited with code $LASTEXITCODE"
}
}
Write-Host "OCSentinel bootstrap completed successfully."