日本語 · English

Windows 管理者のための Kairos——PowerShell とタスク スケジューラだけで動かす

Windows のシステム管理者向けのページ。PowerShell とタスク スケジューラは使うが、開発はしない人を想定する。 言語の仕様は読まなくてよい。このページだけで「定義を書く→今日が発火日か聞く→毎日自動で聞く→Outlook に入れる」 まで通る。

例は Windows Update の日(Patch Tuesday) の定義——太平洋時間の第 2 火曜 10 時。東京の暦では 水曜の未明で、4 月と 7 月は第 3 水曜になる。

入口は 3 つ

最初の 1 本——定義を書いて list で聞く

次を C:\kairos\patch.kairos に保存する(# eval: と #=> の行はこのサイトの検証用のコメント。消してよい):

# Windows Update の日(第 2 火曜 10:00 太平洋時間)
# eval: 2026-10-01..2026-12-31 tz: Asia/Tokyo
premise PT {
  calendar-system: Gregorian
  tz: "America/Los_Angeles"
  wkst: Sun
}
@PT
patchDay = everyDay |> filter(d => weekday(d) == Tue) |> within(month) |> nth(2)
patchDay |> at(T10:00)
#=> 2026-10-14T02:00 2026-11-11T03:00 2026-12-09T03:00

PowerShell で聞く:

kairos list --from 2026-10-01 --to 2026-12-31 --tz Asia/Tokyo C:\kairos\patch.kairos
2026-10-14T02:00
2026-11-11T03:00
2026-12-09T03:00

出力の読み方:

祝日表を持つ定義では、# で始まる註釈の行が混ざることがある(この例の定義には表が無いので出ない):

2027-01-25
2027-02-25
# ⚠ 範囲外 2027-01-01..2027-03-01(holidays2026 covering 2026-01-01..2026-12-31)
# 被覆サマリ
#   holidays2026 covering 2026-01-01..2026-12-31 残走路 -59 日

PowerShell で受ける——--json

判定や加工をするなら --json で聞き、ConvertFrom-Json でオブジェクトにする:

$r = kairos list --from 2026-10-01 --to 2026-12-31 --tz Asia/Tokyo --json C:\kairos\patch.kairos | ConvertFrom-Json
$r.results[0].dates     # 2026-10-14T02:00 2026-11-11T03:00 2026-12-09T03:00
$r.results[0].points    # 同じ点の epoch ミリ秒(1791910800000 …)
$r.coverage             # 祝日表の覆域と残走路(表が無ければ空)

文字コードの落とし穴。ファイルに落とすときは > を使わない:

毎日「今日は発火日か」を聞く——タスク スケジューラ

型は 1 つ。タスク スケジューラは時計として残し、判定だけ Kairos に移す。毎朝 8:30 に「今日は点があるか」を聞き、 あれば通知スクリプトを起動する。cron の同じ型は 月末・月末最終営業日 の「動かす」節。

判定するスクリプト C:\kairos\run-if-today.ps1:

# run-if-today.ps1 —— 今日が発火日なら、続く引数のコマンドを実行する(Windows PowerShell 5.1 以上)
# 使い方: powershell -NoProfile -ExecutionPolicy Bypass -File run-if-today.ps1 <定義.kairos> <実行するコマンド> [引数...]
# リハーサル: 環境変数 KAIROS_TODAY に YYYY-MM-DD を置くと、その日を「今日」として判定する
param(
  [Parameter(Mandatory = $true)] [string] $Definition,
  [Parameter(Mandatory = $true)] [string] $Command,
  [Parameter(ValueFromRemainingArguments = $true)] [string[]] $CommandArgs = @()
)
$kairos = 'kairos'                      # SYSTEM で動かすなら C:\kairos に入れた kairos.cmd の絶対パスに(管理者の型)
$tz = 'Asia/Tokyo'                      # 「今日」をどこの暦で読むか。機械の tz 設定と揃える
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8   # kairos の出力は UTF-8

$culture = [System.Globalization.CultureInfo]::InvariantCulture
$today = if ($env:KAIROS_TODAY) { $env:KAIROS_TODAY } else { (Get-Date).ToString('yyyy-MM-dd', $culture) }
$tomorrow = [datetime]::ParseExact($today, 'yyyy-MM-dd', $culture).AddDays(1).ToString('yyyy-MM-dd', $culture)

# 今日から明日の手前までの窓を --json で聞く(--to の日は含まない)
$json = & $kairos list --from $today --to $tomorrow --tz $tz --json $Definition
if ($LASTEXITCODE -ne 0) {
  [Console]::Error.WriteLine("kairos が失敗した(終了コード $LASTEXITCODE): $json")
  exit 1
}
$report = ($json -join "`n") | ConvertFrom-Json
if (-not $report) { exit 1 }            # JSON が読めなければ何もしない
$dates = @($report.results[0].dates | Where-Object { $_ })
if ($dates.Count -eq 0) { exit 0 }      # 今日は発火日ではない。黙って終わる

& $Command @CommandArgs                 # 発火日。通知スクリプトを実行する
exit $LASTEXITCODE

先にリハーサル。日付を環境変数で与えて手で叩く。10/13(火)は何も起きず、10/14(水)だけ通知が走る:

$env:KAIROS_TODAY = '2026-10-13'
powershell -NoProfile -ExecutionPolicy Bypass -File C:\kairos\run-if-today.ps1 C:\kairos\patch.kairos C:\kairos\notify.ps1
$env:KAIROS_TODAY = '2026-10-14'
powershell -NoProfile -ExecutionPolicy Bypass -File C:\kairos\run-if-today.ps1 C:\kairos\patch.kairos C:\kairos\notify.ps1
Remove-Item Env:KAIROS_TODAY

タスクを登録する。毎日 8:30 に判定する(管理者の PowerShell かコマンド プロンプトで):

schtasks /Create /SC DAILY /ST 08:30 /TN "Kairos\PatchTuesday" /TR "powershell -NoProfile -ExecutionPolicy Bypass -File C:\kairos\run-if-today.ps1 C:\kairos\patch.kairos C:\kairos\notify.ps1"

実行ユーザーと電源(2026-10-13 実測):

すぐ試すなら schtasks /Run /TN "Kairos\PatchTuesday"。結果は schtasks /Query /TN "Kairos\PatchTuesday" /V /FO LIST の 「前回の結果」で見る(0 が成功、1 がエラー)。

管理者の型——SYSTEM で、止まらないコンピューターで

通知はログオンの有無に関わらず出てほしい。常時動いているコンピューター(サーバーなど)で、SYSTEM アカウントのタスクとして登録するのが確実(2026-10-13 実測)。

前提:

npm install --prefix C:\kairos kairos-lang
# run-if-today.ps1 の 1 行をこう書き換える
$kairos = 'C:\kairos\node_modules\.bin\kairos.cmd'

登録(管理者の PowerShell で。schtasks の代わりに ScheduledTasks モジュールを使う):

$task = @{ TaskName = 'PatchTuesday'; TaskPath = '\Kairos\' }
$argument = '-NoProfile -ExecutionPolicy Bypass -File "C:\kairos\run-if-today.ps1" -Definition "C:\kairos\patch.kairos" -Command "C:\kairos\notify.ps1"'

Get-ScheduledTask @task -ErrorAction Ignore | Unregister-ScheduledTask -Confirm:$false
Register-ScheduledTask @task `
  -Action    (New-ScheduledTaskAction -Execute '%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe' -Argument $argument) `
  -Trigger   (New-ScheduledTaskTrigger -Daily -At '08:30') `
  -Principal (New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' -LogonType ServiceAccount) `
  -Settings  (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd)
$argument = '-NoProfile -ExecutionPolicy Bypass -Command $env:KAIROS_TODAY = ''2026-10-14''; . "C:\kairos\run-if-today.ps1" -Definition "C:\kairos\patch.kairos" -Command "C:\kairos\notify.ps1"'

カレンダーに入れる——Outlook

--ics で iCalendar のファイルにして Outlook に取り込む。ファイルに落とすのは cmd /c で(上の文字コードの落とし穴):

cmd /c "kairos list --from 2026-10-01 --to 2027-01-01 --tz Asia/Tokyo --ics C:\kairos\patch.kairos > C:\kairos\patch.ics"

ファイルの中身:

Outlook への取り込み(2026-10-13 に新しい Outlook と従来の Outlook で実測):

--ics-series は Outlook では取り込めない(同日実測)。

通知を朝にしたいなら、定義の側で時刻を貼り替える。公開の点は太平洋時間の前提の中に置き、東京の前提から参照して その日の 9 時を貼る:

# Windows Update の日の通知(東京の朝 9 時)
# eval: 2026-10-01..2027-01-01 tz: Asia/Tokyo
premise PT {
  calendar-system: Gregorian
  tz: "America/Los_Angeles"
  wkst: Sun
  release = everyDay |> filter(d => weekday(d) == Tue) |> within(month) |> nth(2) |> at(T10:00)
}
premise JP {
  calendar-system: Gregorian
  tz: "Asia/Tokyo"
  wkst: Mon
}
@JP
PT.release |> snapTo(day) |> at(T09:00)
#=> 2026-10-14T09:00 2026-11-11T09:00 2026-12-09T09:00

落とし穴の一覧

落とし穴 何が起きるか 対処
--tz を書かない 機械の tz で印字される。UTC のサーバーでは火曜 17 時 必ず --tz Asia/Tokyo。定義の tz と表示の tz は別のもの
PowerShell 5.1 の > UTF-16 のファイルになり、他のツールが読めない cmd /c "kairos … > out.json"。版を問わず確実
日本語が化ける PowerShell の既定の文字コードで UTF-8 を読んでいる 先に [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
実行ポリシー .ps1 が「スクリプトの実行が無効」で止まる powershell -ExecutionPolicy Bypass -File …。そのプロセスだけに効く
タスクの実行ユーザー /RU 省略は登録した人のアカウントで、ログオン中だけ動く。SYSTEM は利用者のフォルダの kairos を使えない 常時動かすなら「管理者の型」(SYSTEM・C:\kairos に入れて絶対パス)
Outlook に --ics-series 新しい Outlook はエラー、従来の Outlook は黙って 0 件 --ics を使う
--from/--to に時刻 「日付は YYYY-MM-DD」のエラー 日付だけを書く。--to の日は含まない
祝日表の覆域切れ # ⚠ 範囲外 が出て残走路がマイナス。点は祝日を知らずに出る 表を延ばす。covering: の終端も
ノート PC のバッテリー 既定では AC 電源でないと開始しない。その日の通知が出ないまま過ぎる タスクの「条件」タブで「AC 電源で使用している場合のみ」を外す
schtasks の /SD 地域設定の日付形式に依存して失敗する 使わない。/SC DAILY /ST だけで今日から始まる
タスクの出力が残らない タスク スケジューラは標準出力を保存しない 「前回の結果」の終了コードを見る。記録が要るなら通知スクリプト側で書く

次の版

次の版で、Excel 向けの表形式(タブ区切り)の出力が入る。

ブラウザで試す

定義を Playground で開く(日本時間で表示) ——上の 3 点が出る。期間や at(T10:00) を変えて試せる。ブラウザ内で完結し、何も送信しない。

関連