【PowerShell】定期的にSSH経由でログを取得する方法|タスクスケジューラ連携

運用中のLinuxサーバーからアプリやOSのログを「決まった間隔で安全に収集」したい場面では、PowerShell+OpenSSHにタスクスケジューラを組み合わせるのが手堅い解です。
ここでは鍵認証を前提に、取得スクリプトの雛形、ログの保存・ローテーション、失敗時の再試行、そしてタスクスケジューラ(GUI/CLI/XML)での自動実行までを通しで解説します。

前提と準備(鍵認証・ssh-agent)

Windows 10以降でOpenSSHクライアントを利用し、対象Linux側には公開鍵を登録します。パスフレーズ付き鍵はWindowsのssh-agentに読み込ませ、バッチ実行時の停止を防ぎます。

# 一度だけ実施(管理者PowerShell)
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
ssh-add $HOME\.ssh\id_ed25519
ssh-add -l   # 読み込み確認

収集スクリプトの雛形(取得・保存・ローテーション)

指定サーバーへSSHでコマンドを実行して必要なログを束ね、SCPで持ち帰るか、その場で標準出力をファイル化します。障害解析のために終了コードとエラーログを残します。

# Get-RemoteLogs.ps1
param(
  [string]$Host = "app.internal",
  [string]$User = "ubuntu",
  [string]$OutDir = "$PSScriptRoot\logs",
  [int]$Retry = 2
)

$ErrorActionPreference = "Stop"
$ts   = Get-Date -Format "yyyyMMdd-HHmmss"
$base = Join-Path $OutDir $Host
$new  = Join-Path $base "logs-$ts.txt"
$err  = Join-Path $base "logs-$ts.err.txt"

New-Item -ItemType Directory -Force -Path $base | Out-Null

# 収集したいコマンド群(必要に応じて編集)
$remote = @'
/usr/bin/env bash -lc '
  set -euo pipefail
  echo "== host: $(hostname) user: $(whoami) time: $(date -Is)"
  echo "-- journal (nginx) last 200"
  journalctl -u nginx -n 200 --no-pager || true
  echo "-- dmesg tail"
  dmesg | tail -n 100 || true
  echo "-- app log sample"
  test -f /var/log/app/app.log && tail -n 200 /var/log/app/app.log || true
'
'@

# 再試行付きで実行
function Invoke-Remote {
  param([int]$max=[int]$Retry)
  for($i=1;$i -le $max+1;$i++){
    $p = Start-Process -FilePath "ssh" -ArgumentList @(
      "-o","BatchMode=yes",
      "-o","StrictHostKeyChecking=accept-new",
      "$User@$Host",$remote
    ) -NoNewWindow -RedirectStandardOutput $new -RedirectStandardError $err -PassThru -Wait
    if($p.ExitCode -eq 0){ return 0 }
    Start-Sleep -Seconds ([math]::Pow(2,$i))  # 1,2,4...秒
  }
  return 1
}

$rc = Invoke-Remote
if($rc -ne 0){
  Write-Error "Remote collection failed: $Host (see $err)"
  exit 1
}

# 古いファイルのローテーション(30日保持)
Get-ChildItem $base -File | Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-30) } | Remove-Item -Force

# 圧縮して保存(任意)
Compress-Archive -Path $new -DestinationPath ($new + ".zip") -CompressionLevel Optimal -Force
Remove-Item $new -Force  # 生ログを残したい場合は削除しない

Write-Host "Saved: $($new).zip"

動作確認(手動実行)

まずは手動で実行し、期待するログが保存されるか、エラー時に*.err.txtが作成されるかを確認します。

pwsh -File .\Get-RemoteLogs.ps1 -Host app.internal -User ubuntu -OutDir C:\Ops\Logs

タスクスケジューラでの自動実行(GUI)

タスクスケジューラを起動し、「基本タスクの作成」からトリガー(例:1時間おき)を設定。操作は「プログラムの開始」で以下を指定します。

プログラム/スクリプト: pwsh
引数の追加: -NoProfile -ExecutionPolicy Bypass -File "C:\Ops\Get-RemoteLogs.ps1" -Host app.internal -User ubuntu -OutDir "C:\Ops\Logs"
開始(作業)ディレクトリ: C:\Ops

「最上位の特権で実行する」を有効化し、ログオン状態に関わらず実行する場合は、実行アカウントに対して「バッチジョブとしてログオン」を許可します(グループポリシー/ローカルセキュリティポリシー)。

タスクスケジューラのCLI(schtasks)

コマンドで登録する場合は以下を利用します(1時間ごと/無期限)。引数やパスは環境に合わせて調整してください。

schtasks /Create /TN "LogPull-Hourly" `
  /TR "pwsh -NoProfile -ExecutionPolicy Bypass -File C:\Ops\Get-RemoteLogs.ps1 -Host app.internal -User ubuntu -OutDir C:\Ops\Logs" `
  /SC HOURLY /MO 1 /F /RU "DOMAIN\User" /RP *

XMLで高度なスケジュール(5分間隔・失敗時再試行)

細かい制御が必要ならXMLでインポートします。5分に1回、失敗時は1分おきに3回再試行する例です。

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <TimeTrigger>
      <Repetition><Interval>PT5M</Interval><StopAtDurationEnd>false</StopAtDurationEnd></Repetition>
      <StartBoundary>2025-08-21T00:00:00</StartBoundary>
      <Enabled>true</Enabled>
    </TimeTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author"><RunLevel>HighestAvailable</RunLevel></Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <RestartOnFailure><Interval>PT1M</Interval><Count>3</Count></RestartOnFailure>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>pwsh</Command>
      <Arguments>-NoProfile -ExecutionPolicy Bypass -File C:\Ops\Get-RemoteLogs.ps1 -Host app.internal -User ubuntu -OutDir C:\Ops\Logs</Arguments>
      <WorkingDirectory>C:\Ops</WorkingDirectory>
    </Exec>
  </Actions>
</Task>

保存戦略と容量管理(圧縮・期限・日次ディレクトリ)

前掲スクリプトではZip圧縮と30日での削除を実装しています。大量ログは日付ディレクトリに分けると探索性が上がります。

$day = Get-Date -Format "yyyyMMdd"
$base = Join-Path $OutDir "$Host\$day"
New-Item -ItemType Directory -Force -Path $base | Out-Null

セキュリティと実務上の注意

秘密鍵は端末ごとに発行し、ssh-agentに読み込ませてファイルパスを露出しない運用が安全です。サーバー側はPasswordAuthentication no、必要最小の権限でログ取得のみ許可します。機微ログを扱う場合は保存先ドライブの暗号化(BitLocker)を推奨します。

トラブルシュート

タスクが失敗する場合は履歴を有効化して原因を特定します。SSHの詳細はssh -vで確認、Windows側はイベントビューアー(Microsoft-Windows-TaskScheduler/Operational)を参照します。文字コードや改行で~/.ssh/configが読まれない場合はUTF-8(BOMなし)+LFに変換します。

# タスク履歴の確認(GUI: タスクスケジューラの「履歴」タブ)
Get-ScheduledTask -TaskName "LogPull-Hourly" | Get-ScheduledTaskInfo

まとめ

PowerShellの短いスクリプトに、鍵認証・リトライ・圧縮・ローテーションを組み込み、タスクスケジューラで定期起動すれば、SSH経由のログ収集を安全かつ継続的に運用できます。まずは手動で成功を確認し、スケジュール化→監視・通知の追加へと段階的に拡張していきましょう。