132 lines
5.0 KiB
PowerShell
132 lines
5.0 KiB
PowerShell
<#
|
|||
|
|
.SYNOPSIS
|
||
|
|
Register (or remove) the two Windows scheduled tasks that make AVWatch
|
||
|
|
run by itself.
|
||
|
|
|
||
|
|
.DESCRIPTION
|
||
|
|
Creates:
|
||
|
|
"AVWatch Scan" mid-week checks. Cheap: it only asks Claude about
|
||
|
|
versions it has never seen before, and reuses cached
|
||
|
|
web lookups. Writes reports\latest.html. No email.
|
||
|
|
"AVWatch Digest" the Monday briefing. Full pass, every outstanding
|
||
|
|
update gets a verdict, then the email goes out.
|
||
|
|
|
||
|
|
Tasks run as the current user so they inherit your environment variables
|
||
|
|
(API key, SMTP password). They are set to run whether or not you are
|
||
|
|
logged in, and to start late if the machine was off at the scheduled time.
|
||
|
|
|
||
|
|
Run this from a normal PowerShell window -- no administrator rights are
|
||
|
|
needed for a per-user task.
|
||
|
|
|
||
|
|
.EXAMPLE
|
||
|
|
.\install_tasks.ps1
|
||
|
|
.\install_tasks.ps1 -ScanDays Tuesday,Thursday,Saturday -ScanTime 06:00 -DigestTime 07:15
|
||
|
|
.\install_tasks.ps1 -Remove
|
||
|
|
#>
|
||
|
|
[CmdletBinding()]
|
||
|
|
param(
|
||
|
|
[string[]]$ScanDays = @('Tuesday', 'Thursday', 'Saturday'),
|
||
|
|
[string]$ScanTime = '06:00',
|
||
|
|
[string]$DigestDay = 'Monday',
|
||
|
|
[string]$DigestTime = '07:00',
|
||
|
|
[string]$ProjectRoot,
|
||
|
|
[switch]$Remove
|
||
|
|
)
|
||
|
|
|
||
|
|
$ErrorActionPreference = 'Stop'
|
||
|
|
|
||
|
|
if (-not $ProjectRoot) {
|
||
|
|
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
||
|
|
}
|
||
|
|
$runner = Join-Path $ProjectRoot 'scripts\run_avwatch.ps1'
|
||
|
|
|
||
|
|
$scanTaskName = 'AVWatch Scan'
|
||
|
|
$digestTaskName = 'AVWatch Digest'
|
||
|
|
|
||
|
|
if ($Remove) {
|
||
|
|
foreach ($name in $scanTaskName, $digestTaskName) {
|
||
|
|
if (Get-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue) {
|
||
|
|
Unregister-ScheduledTask -TaskName $name -Confirm:$false
|
||
|
|
Write-Output "removed scheduled task: $name"
|
||
|
|
} else {
|
||
|
|
Write-Output "not present: $name"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not (Test-Path $runner)) {
|
||
|
|
throw "runner script not found: $runner"
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- sanity check before we schedule anything ---------------------------
|
||
|
|
Write-Output "Checking the setup first..."
|
||
|
|
Push-Location $ProjectRoot
|
||
|
|
try {
|
||
|
|
$venvPython = Join-Path $ProjectRoot '.venv\Scripts\python.exe'
|
||
|
|
if (Test-Path $venvPython) { $python = $venvPython }
|
||
|
|
else { $python = (Get-Command python).Source }
|
||
|
|
& $python -m avwatch doctor
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
Write-Warning ("'avwatch doctor' reported problems (exit $LASTEXITCODE). " +
|
||
|
|
"Scheduling anyway -- fix them and the tasks will start working.")
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
Pop-Location
|
||
|
|
}
|
||
|
|
|
||
|
|
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" `
|
||
|
|
-LogonType S4U -RunLevel Limited
|
||
|
|
|
||
|
|
$settings = New-ScheduledTaskSettingsSet `
|
||
|
|
-StartWhenAvailable `
|
||
|
|
-DontStopOnIdleEnd `
|
||
|
|
-AllowStartIfOnBatteries `
|
||
|
|
-DontStopIfGoingOnBatteries `
|
||
|
|
-MultipleInstances IgnoreNew `
|
||
|
|
-ExecutionTimeLimit (New-TimeSpan -Hours 3)
|
||
|
|
|
||
|
|
function New-AvwatchAction([string]$Command) {
|
||
|
|
# -NoProfile keeps a slow or broken PowerShell profile out of the picture.
|
||
|
|
$argument = ('-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "{0}" ' +
|
||
|
|
'-Command {1} -ProjectRoot "{2}"') -f $runner, $Command, $ProjectRoot
|
||
|
|
New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $argument `
|
||
|
|
-WorkingDirectory $ProjectRoot
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- mid-week scans ------------------------------------------------------
|
||
|
|
$scanTrigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek $ScanDays -At $ScanTime
|
||
|
|
Register-ScheduledTask -TaskName $scanTaskName `
|
||
|
|
-Action (New-AvwatchAction 'scan') `
|
||
|
|
-Trigger $scanTrigger `
|
||
|
|
-Principal $principal `
|
||
|
|
-Settings $settings `
|
||
|
|
-Description ("AVWatch: check the AV fleet for new firmware and software " +
|
||
|
|
"releases and refresh reports\latest.html. No email.") `
|
||
|
|
-Force | Out-Null
|
||
|
|
Write-Output "registered: $scanTaskName ($($ScanDays -join ', ') at $ScanTime)"
|
||
|
|
|
||
|
|
# --- Monday digest -------------------------------------------------------
|
||
|
|
$digestTrigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek $DigestDay -At $DigestTime
|
||
|
|
Register-ScheduledTask -TaskName $digestTaskName `
|
||
|
|
-Action (New-AvwatchAction 'digest') `
|
||
|
|
-Trigger $digestTrigger `
|
||
|
|
-Principal $principal `
|
||
|
|
-Settings $settings `
|
||
|
|
-Description ("AVWatch: full update-and-risk pass, then email the weekly " +
|
||
|
|
"briefing to the recipients in config.toml.") `
|
||
|
|
-Force | Out-Null
|
||
|
|
Write-Output "registered: $digestTaskName ($DigestDay at $DigestTime)"
|
||
|
|
|
||
|
|
Write-Output ""
|
||
|
|
Write-Output "Done. Useful commands:"
|
||
|
|
Write-Output " Start-ScheduledTask -TaskName '$digestTaskName' # run the digest now"
|
||
|
|
Write-Output " Get-ScheduledTaskInfo -TaskName '$digestTaskName' # last run time and result"
|
||
|
|
Write-Output " Get-ChildItem '$ProjectRoot\logs' | Sort LastWriteTime -Desc | Select -First 5"
|
||
|
|
Write-Output ""
|
||
|
|
Write-Output "Secrets must be set as USER environment variables so the tasks"
|
||
|
|
Write-Output "inherit them. For example:"
|
||
|
|
Write-Output ' setx ANTHROPIC_API_KEY "sk-ant-..."'
|
||
|
|
Write-Output ' setx AVWATCH_SMTP_PASSWORD "your-app-password"'
|
||
|
|
Write-Output "Open a new shell after setx before testing."
|