この記事は以下のシリーズの12番目の記事です。
この記事でやること
PowerShellをタスクスケジューラで実行すると、処理は画面の見えないところで動きます。
そのため、手動実行のように「画面を見て成功・失敗を判断する」ことができません。
そこで重要になるのがログ設計です。
この記事では、タスクスケジューラでPowerShellを実行するときに、最低限どのようなログを残すべきかを整理します。
扱う内容は次の通りです。
| 項目 | 内容 |
|---|---|
| 開始ログ | タスクが起動したことを記録する |
| 終了ログ | 最後まで処理されたことを記録する |
| 成功ログ | 正常終了したことを記録する |
| エラーログ | 失敗理由を記録する |
| 実行ユーザーログ | どのユーザーで動いたか記録する |
| 処理件数ログ | 何件処理したか記録する |
| 終了コード | 外部から成功・失敗を判断しやすくする |
なぜタスクスケジューラではログが重要なのか
手動実行なら、PowerShellの画面にエラーが表示されます。
しかし、タスクスケジューラで実行すると、画面を見られないことが多いです。
特に夜間実行や無人実行では、失敗していても気づかないことがあります。
たとえば、次のような問題が起きます。
| 問題 | ログがない場合 |
|---|---|
| タスクが起動していない | そもそも実行されたか分からない |
| ファイルが見つからない | どのパスで失敗したか分からない |
| 権限がない | どのユーザーで動いたか分からない |
| CSV処理が途中で止まった | 何行目まで処理したか分からない |
| 共有フォルダにアクセスできない | ネットワークか権限か判断しにくい |
タスクスケジューラで動かすPowerShellでは、ログがないと運用できません。
検証用フォルダを作る
まず、検証用フォルダを作成します。
New-Item -ItemType Directory -Path "C:\work\ps-task-log-test" -Force
Set-Location "C:\work\ps-task-log-test"
ログ用フォルダも作ります。
New-Item -ItemType Directory -Path ".\logs" -Force
確認します。
Get-ChildItem
最低限残したいログ項目
タスクスケジューラ実行では、最低限次のログを残すと切り分けがしやすくなります。
| ログ項目 | 例 | 目的 |
|---|---|---|
| 処理開始 | タスク開始 | タスクが起動したか確認する |
| 処理終了 | タスク終了 | 最後まで到達したか確認する |
| 実行ユーザー | DOMAIN\user01 | 権限問題を確認する |
| スクリプト場所 | C:\work\script | パス問題を確認する |
| カレントディレクトリ | C:\Windows\System32 など | 相対パス問題を確認する |
| 対象ファイル数 | 10件 | 処理対象を確認する |
| 成功件数 | 9件 | 処理結果を確認する |
| 失敗件数 | 1件 | 異常有無を確認する |
| エラー内容 | Access denied | 原因調査に使う |
特に重要なのは、開始ログと終了ログの両方を残すことです。
開始ログだけあって終了ログがない場合、途中で止まった可能性があります。
ログ出力用の関数を作る
まず、ログ出力用の関数を作ります。
$logDir = Join-Path $PSScriptRoot "logs"
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
$now = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $logPath -Value "[$now][$Level] $Message" -Encoding UTF8
}
使い方は次の通りです。
Write-Log "タスク開始"
Write-Log "処理成功"
Write-Log "エラーが発生しました" "ERROR"
ログには、時刻とレベルを付けておくと見やすくなります。
[2026-07-05 22:00:00][INFO] タスク開始
[2026-07-05 22:00:01][INFO] 処理成功
[2026-07-05 22:00:02][ERROR] エラーが発生しました
実行ユーザーと実行場所を記録する
タスクスケジューラでは、手動実行時と違うユーザー・違う場所で実行されることがあります。
そのため、ログに実行ユーザーと実行場所を残します。
Write-Log "実行ユーザー: $env:USERDOMAIN\$env:USERNAME"
Write-Log "PSScriptRoot: $PSScriptRoot"
Write-Log "CurrentDirectory: $(Get-Location)"
特に CurrentDirectory は重要です。
タスクスケジューラでは、開始場所の設定が空欄だった場合などに、想定外の場所を基準に実行されることがあります。
そのため、スクリプト内のパス指定は、できるだけ $PSScriptRoot を基準にします。
$targetDir = Join-Path $PSScriptRoot "target"
try/catch/finallyで終了ログを必ず残す
タスクスケジューラ実行では、エラーが発生したときもログに残す必要があります。
基本形は、try/catch/finally です。
Write-Log "タスク開始"
try {
Write-Log "処理本体を開始"
# ここに実際の処理を書く
Write-Log "処理本体が正常終了"
}
catch {
Write-Log "エラー発生: $($_.Exception.Message)" "ERROR"
}
finally {
Write-Log "タスク終了"
}
finally に書いた処理は、成功しても失敗しても実行されます。
そのため、終了ログは finally に書いておくとよいです。
エラーをcatchに入れるために -ErrorAction Stop を使う
PowerShellでは、エラーが発生しても必ず catch に入るとは限りません。
たとえば、Copy-Item などで確実に catch に入れたい場合は、-ErrorAction Stop を付けます。
try {
Copy-Item -Path ".\not_found.txt" -Destination ".\backup\not_found.txt" -ErrorAction Stop
Write-Log "コピー成功"
}
catch {
Write-Log "コピー失敗: $($_.Exception.Message)" "ERROR"
}
-ErrorAction Stop を付けることで、エラーを例外として扱いやすくなります。
タスクスケジューラで安定運用したい処理では、重要なコマンドに -ErrorAction Stop を付けることを検討します。
処理件数をログに残す
一括処理では、何件処理したかをログに残すと便利です。
まず、検証用ファイルを作ります。
$targetDir = Join-Path $PSScriptRoot "target"
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
Set-Content -Path (Join-Path $targetDir "a.txt") -Value "a"
Set-Content -Path (Join-Path $targetDir "b.txt") -Value "b"
Set-Content -Path (Join-Path $targetDir "c.log") -Value "c"
.txt ファイルだけを対象にして、件数をログに残します。
$files = Get-ChildItem -Path $targetDir -File -Filter "*.txt" -ErrorAction Stop
Write-Log "対象ファイル数: $($files.Count)"
foreach ($file in $files) {
Write-Log "処理対象: $($file.FullName)"
}
処理対象と件数をログに出しておくと、「そもそも対象がなかった」のか、「処理途中で失敗した」のかが分かりやすくなります。
成功件数・失敗件数を記録する
一括処理では、成功件数と失敗件数を分けて記録すると便利です。
$successCount = 0
$errorCount = 0
$files = Get-ChildItem -Path $targetDir -File -Filter "*.txt" -ErrorAction Stop
foreach ($file in $files) {
try {
Write-Log "処理開始: $($file.FullName)"
# ここにファイルごとの処理を書く
# 例として、ファイル名だけログに出す
Write-Log "処理成功: $($file.Name)"
$successCount++
}
catch {
Write-Log "処理失敗: $($file.FullName) / $($_.Exception.Message)" "ERROR"
$errorCount++
}
}
Write-Log "成功件数: $successCount"
Write-Log "失敗件数: $errorCount"
このようにしておくと、処理全体の結果を後から確認しやすくなります。
終了コードを設定する
タスクスケジューラでは、スクリプトの終了コードを見ることで成功・失敗を判断しやすくなります。
PowerShellでは、最後に exit を使って終了コードを返せます。
一般的には、次のように考えます。
| 終了コード | 意味 |
|---|---|
| 0 | 正常終了 |
| 1 | 異常終了 |
たとえば、失敗件数が1件以上ある場合は exit 1 にします。
if ($errorCount -gt 0) {
Write-Log "異常終了: 失敗件数 $errorCount" "ERROR"
exit 1
}
else {
Write-Log "正常終了"
exit 0
}
タスクスケジューラの履歴や実行結果と組み合わせると、異常終了を検知しやすくなります。
実務で使いやすいログ設計テンプレート
ここまでの内容をまとめたテンプレートです。
$logDir = Join-Path $PSScriptRoot "logs"
$targetDir = Join-Path $PSScriptRoot "target"
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
$now = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $logPath -Value "[$now][$Level] $Message" -Encoding UTF8
}
$successCount = 0
$errorCount = 0
Write-Log "タスク開始"
Write-Log "実行ユーザー: $env:USERDOMAIN\$env:USERNAME"
Write-Log "PSScriptRoot: $PSScriptRoot"
Write-Log "CurrentDirectory: $(Get-Location)"
try {
$files = Get-ChildItem -Path $targetDir -File -Filter "*.txt" -ErrorAction Stop
Write-Log "対象ファイル数: $($files.Count)"
foreach ($file in $files) {
try {
Write-Log "処理開始: $($file.FullName)"
# ここに実際の処理を書く
# 例: ファイル名をログに出す
Write-Log "処理成功: $($file.Name)"
$successCount++
}
catch {
Write-Log "処理失敗: $($file.FullName) / $($_.Exception.Message)" "ERROR"
$errorCount++
}
}
Write-Log "成功件数: $successCount"
Write-Log "失敗件数: $errorCount"
}
catch {
Write-Log "全体処理エラー: $($_.Exception.Message)" "ERROR"
$errorCount++
}
finally {
Write-Log "タスク終了"
}
if ($errorCount -gt 0) {
Write-Log "異常終了: 失敗件数 $errorCount" "ERROR"
exit 1
}
else {
Write-Log "正常終了"
exit 0
}
このテンプレートでは、次の情報を残します。
| ログ項目 | 内容 |
|---|---|
| タスク開始 | タスクが起動したか確認 |
| 実行ユーザー | 権限問題の切り分け |
| PSScriptRoot | スクリプト基準パスの確認 |
| CurrentDirectory | カレントディレクトリの確認 |
| 対象ファイル数 | 処理対象の確認 |
| 処理開始 | ファイルごとの開始確認 |
| 処理成功 | ファイルごとの成功確認 |
| 処理失敗 | ファイルごとの失敗確認 |
| 成功件数 | 全体結果の確認 |
| 失敗件数 | 異常有無の確認 |
| タスク終了 | 最後まで到達したか確認 |
| 終了コード | 外部から成功・失敗を判断 |
ログファイルを日付で分ける
上記テンプレートでは、ログファイル名を日付単位にしています。
$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"
これにより、次のようなログになります。
logs
├─ task_20260705.log
├─ task_20260706.log
└─ task_20260707.log
日次実行のタスクでは、この形式が管理しやすいです。
1回の実行ごとにログを分けたい場合は、秒まで含めます。
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logPath = Join-Path $logDir "task_$timestamp.log"
ログが増えすぎる場合
タスクを毎日実行すると、ログファイルが増えていきます。
古いログを削除したい場合は、たとえば30日より古いログを削除します。
Get-ChildItem -Path $logDir -File -Filter "*.log" |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -WhatIf
最初は必ず -WhatIf を付けて、削除対象を確認します。
問題なければ、-WhatIf を外します。
Get-ChildItem -Path $logDir -File -Filter "*.log" |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item
ログ設計では、「残す」だけでなく「いつまで残すか」も考える必要があります。
よくあるつまずき
ログが出ない
まず、ログ出力先のパスを確認します。
Write-Log "PSScriptRoot: $PSScriptRoot"
Write-Log "CurrentDirectory: $(Get-Location)"
また、ログフォルダを作っているか確認します。
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
エラーがcatchに入らない
重要なコマンドには -ErrorAction Stop を付けます。
Get-ChildItem -Path $targetDir -File -ErrorAction Stop
タスクは成功扱いなのに処理が失敗している
スクリプト内でエラーをログに出していても、最後に exit 0 になっていると正常終了扱いになります。
失敗件数がある場合は、exit 1 にします。
if ($errorCount -gt 0) {
exit 1
}
else {
exit 0
}
ログが文字化けする
日本語ログを扱う場合は、-Encoding を指定します。
Add-Content -Path $logPath -Value "処理開始" -Encoding UTF8
環境によっては、PowerShell 7で utf8BOM を使うこともあります。
Add-Content -Path $logPath -Value "処理開始" -Encoding utf8BOM
ログが長くなりすぎる
日付別ログにするか、古いログを削除する処理を入れます。
$today = Get-Date -Format "yyyyMMdd"
$logPath = Join-Path $logDir "task_$today.log"
まとめ
タスクスケジューラでPowerShellを実行する場合、ログ設計は必須です。
最低限残したいログは次の通りです。
| ログ | 目的 |
|---|---|
| タスク開始 | 起動確認 |
| タスク終了 | 最後まで到達したか確認 |
| 実行ユーザー | 権限問題の切り分け |
| PSScriptRoot | スクリプト基準パスの確認 |
| CurrentDirectory | 相対パス問題の確認 |
| 対象件数 | 処理対象の確認 |
| 成功件数 | 正常処理数の確認 |
| 失敗件数 | 異常有無の確認 |
| エラー内容 | 原因調査 |
| 終了コード | 外部から成功・失敗を判断 |
特に重要なのは、次の3つです。
Write-Log "タスク開始"
Write-Log "エラー発生: $($_.Exception.Message)" "ERROR"
Write-Log "タスク終了"
そして、失敗時には終了コードを返します。
exit 1
成功時は次のようにします。
exit 0
タスクスケジューラで動かすPowerShellは、画面で見えない場所で動きます。
だからこそ、ログに「いつ、誰が、どこで、何を、何件処理し、成功したのか失敗したのか」を残すことが重要です。