PowerShell

いまの PowerShell スクリプトに、なかったスケジューリングを。

Dagu は PowerShell、pwsh、cmd.exe を通常のワークフローステップとして実行します。既存のスクリプトは形を変えないまま、実行順序、リトライ、ステップ単位のログ、そしてブラウザで開ける実行履歴を手に入れます。

同じワークフロー内で PowerShell / pwsh / cmd.exe
シェルはワークフロー単位でもステップ単位でも指定
あるスクリプトの出力を次のスクリプトへ
Windows サービスとして常駐
01

シェルは一度決める。必要ならステップごとに変える

ワークフローがシェルを宣言すると、全ステップがそれを引き継ぎます。指定しない場合は PowerShell、pwsh、cmd.exe の順に選ばれるため、あるマシンで書いたワークフローが別のマシンでも予測どおりに動きます。

  • シェルは文字列でも配列でも書けます。-NoProfile のような引数のクォート問題は配列形式で避けられます
  • ステップは出力を名前付き変数に取り込み、後続のステップや実行条件から読めます
  • preconditions がその値でステップを制御するので、サービスが実際に停止しているときだけ再起動します
サービスを確認し、止まっているときだけ再起動する
# service-health.yaml
schedule: "*/15 * * * *"
max_active_runs: 1
shell: powershell -NoProfile

steps:
  - id: check_service
    run: |
      (Get-Service -Name 'MyAppSvc').Status
    output: SVC_STATUS

  - id: restart_if_stopped
    run: Restart-Service -Name 'MyAppSvc'
    depends: check_service
    preconditions:
      - condition: "${SVC_STATUS}"
        expected: "Stopped"
    retry_policy:
      limit: 2
      interval_sec: 30

mail_on:
  failure: true
02

PowerShell と cmd.exe を 1 つのワークフローに

スケジューラを変えたからといって、古いバッチファイルを書き直すことは普通ありません。ステップ単位でワークフローのシェルを上書きできるので、ほぼ PowerShell のワークフローの中から 10 年動いている .bat を呼べます。

  • ステップのシェル上書きは with.shell に書きます。裸の shell フィールドは run と併用できず、バリデータが弾きます
  • シェル解釈を一切挟まず引数を厳密に渡したい場合は、command と args を持つ exec ステップを使います
  • どのステップがどのシェルを使っても、スケジュール、履歴、通知経路は 1 つのままです

Windows のパスにはバックスラッシュが含まれるため、YAML ではブロックスカラーかシングルクォートを使ってください。ダブルクォート文字列の中では、バックスラッシュに続く文字はエスケープとして解釈され、パス区切りにはなりません。

PowerShell と cmd と直接 exec を 1 ファイルで
# maintenance.yaml
schedule: "0 3 * * *"
shell: ["powershell", "-NoProfile"]

steps:
  - id: report_disk
    run: Get-PSDrive -PSProvider FileSystem | Out-String
    output: DISK_REPORT

  - id: legacy_job
    run: |
      @echo off
      call C:\ops\legacy\run.bat
    with:
      shell: cmd
    depends: report_disk

  - id: direct_exec
    action: exec
    with:
      command: C:\Windows\System32\cmd.exe
      args:
        - /c
        - echo
        - done
    depends: legacy_job

mail_on:
  failure: true
03

複数行のスクリプトも読みやすいまま

ステップは 1 行のコマンドではなくスクリプトブロックを丸ごと持てます。パイプラインやフィルタを、PowerShell を書く人が普通に書く形のまま置けます。

  • ブロックスカラーは改行を保持するので、複数行に分けたパイプラインがそのまま残ります
  • retry_policy はステップ全体に効くため、不安定なネットワークパスや使用中のファイルを触るスクリプトに向きます
  • pwsh と Windows PowerShell は名前で選ぶので、両方入っているホストでも意図したほうで実行できます
ログのアーカイブ処理を定期実行する
# archive-logs.yaml
schedule: "30 1 * * *"
shell: pwsh -NoProfile

steps:
  - id: archive
    run: |
      $cutoff = (Get-Date).AddDays(-30)
      Get-ChildItem -Path 'D:\logs' -Filter *.log |
        Where-Object { $_.LastWriteTime -lt $cutoff } |
        Compress-Archive -DestinationPath 'D:\archive\logs.zip' -Update
    retry_policy:
      limit: 2
      interval_sec: 60

  - id: verify
    run: Test-Path 'D:\archive\logs.zip'
    depends: archive

mail_on:
  failure: true
04

どこから動くか

スケジューラ自体が動いていなければスケジュールは意味を持ちません。Windows インストーラは Dagu をサービスとして登録できるので、ログオンセッションではなくマシンの起動に合わせてワークフローが動き出します。

  • PowerShell のインストーラが、バージョン固定された WinSW ラッパー経由で Windows サービスを登録します。Get-Service で確認できます
  • Windows 版バイナリは amd64、386、arm64 で提供されています
  • Web 画面が実行履歴とステップごとの出力をブラウザから見せるので、ジョブの確認にリモートデスクトップは要りません

Windows Server 2019 以降が安全な対象です。AF_UNIX に対応していない古いビルドでもワークフローは実行されますが、ライブ状態と停止制御用のソケットが使えず、Dagu は警告を記録します。

FAQ

導入前によくある質問

スクリプトを YAML に書き直す必要がありますか?

ありません。YAML が書くのは、いつ実行するか、何に依存するか、失敗したらどうするかです。スクリプト本体は .ps1 ファイルのままで、タスクスケジューラが呼んでいたのと同じ形で呼ばれます。

あるスクリプトの値を次のスクリプトへ渡すには?

出力側のステップに出力名を付け、後続のステップから参照します。同じ参照は precondition でも使えるので、前段の確認結果が特定の値でない限りステップを飛ばす、といった書き方ができます。

1 つのワークフローで Windows PowerShell と pwsh を併用できますか?

できます。片方をワークフローの既定にし、必要なステップだけもう片方で上書きします。どちらも実行ファイル名で選ばれるので、両方入っているホストでも意図したほうで動きます。

ステップに shell を書いたらバリデータに弾かれました

裸の shell フィールドは同じステップの run と併用できません。上書きは with.shell に書いてください。dagu validate を実行すれば、スケジュールが動く前に検出できます。

Linux でも使えますか?

使えます。pwsh は Linux と macOS でも動き、同じワークフロー定義がそのまま通ります。Windows 固有になるのは、cmd.exe や Windows 専用コマンドレットを呼ぶステップだけです。

次のステップ

まず 1 本から始める。

Dagu を入れて、いま cron で動いているスクリプトを 1 本だけ YAML に移し、実際の実行履歴で判断してください。