WindowsのタスクスケジューラとPowershellで定期バックアップ

前に書いた記事に関連して、WoLで深夜に起動させたPCで定期的にバックアップを走らせる設定。

バックアップの処理を書いたPowershell(後述)を、Windowsのタスクスケジューラに登録。 タスクの開始時間はPCが完全に起動したタイミングってことで、NASからWoLパケットを送出する時刻の3分後にした。

タスクスケジューラにPowershellを登録するには、タスク作成ウインドウの「操作」タブで「新規」→「操作」タブを開き以下のように入力。

項目 内容
操作 プログラムの開始
プログラム/スクリプト %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe
引数の追加 -ExecutionPolicy Bypass "C:\path\to\script.ps1"

引数の追加の部分は、実行ファイルのパスの前に「-ExecutionPolicy Bypass 」を入れないと走ってくれない。

Powershellスクリプトの中身は以下。BunBackupでデータのバックアップを取っている。

PCの使用頻度が高くないので、バックアップは2週間に1回で十分なのだが、NASのタスクスケジューラでは「2週間に1回起動」が設定できなかったので、Powershellで前回の実行日から10日以内の場合は即座にシャットダウンする設定を入れることで対応。
ついでにLINE Notifyで完了のお知らせを投げるようにした。

# 準備
# ---------------------------------------------------------------------
# 作業場所を指定 = Set-Location $PSScriptRoot 現在のコマンドを呼び出したスクリプトへの完全なパス
Set-Location $PSScriptRoot
# 実行日記録用の日付フォーマット
$format = "yyyyMMdd"
# 日付取得
$today = Get-Date -Format $format
# ログの保存先
$log_path = "${PSScriptRoot}\backup_lastrun.log"
# 実行フラグ
$is_run = $false
$ErrorActionPreference = "silentlycontinue"

# 前回実行日からの日数をチェック
# ---------------------------------------------------------------------
try{
    # ログファイルに書き込んだ前回実行日を読み出し(Get-Content)、日付型に変換([DateTime]::ParseExact)
    $last_run_time = [DateTime]::ParseExact((Get-Content $log_path), $format, $null)
    # 現在と、最終実行日の差を計算(TimeSpanが返される)
    $span =  (Get-Date) - $last_run_time
    # 前回実行と現在の日付差がn以上ならプログラムを実行する(-ge: ~より大きい)
    # WoLは毎週走るが、バックアップは2週間に一度にしたいので、10日以上経過してることをチェック
    if ($span.Days -ge 10){
        $is_run = $true
    }
}catch{
    # 前回実行日を取得できなかった場合(ログファイルが無い or 正しい日時でない)も、プログラムを実行する
    $is_run = $true
}
$ErrorActionPreference = "continue"


# バックアップ実行
# ---------------------------------------------------------------------
if($is_run){
    # バックアップ開始時刻
    $bk_start = (Get-Date)
    # ログファイル保存場所
    $logfile = "${PSScriptRoot}\backup_log.csv"
    # ログファイル記録用フォーマット
    $logDate = "yyyy/MM/dd HH:mm:ss"

    # 開始のお知らせ(実行中は、デスクトップに「バックアップ中」というファイルを置いておく)
	New-Item "$Env:USERPROFILE\Desktop\バックアップ中" -type file

    # BunBackupでバックアップ実行
    # ログファイルに開始時刻を記録
    Write-Output([datetime]::Now.ToString($logDate) + " BunBackup start") | Out-File $logfile -Append
    # Bunbackupに渡す引数を設定
    $bunarg = "-NORESULT -BACKUP:C:\MyApps\BunBackup\backup-weekly.lbk"
    # Bunbackup実行
    # PowerShell は、実行可能プログラムの実行時にコマンドが終了するのを待たないが、
    # 以下のように書くことで、コマンド(プロセス)の終了を待ってから次へ行ける
    # https://harikofu.blog.fc2.com/blog-entry-28.html
    Start-Process -FilePath "C:\MyApps\BunBackup\BunBackup.exe" -ArgumentList $bunarg -Wait
    # ログファイルに終了時刻を記録
    Write-Output([datetime]::Now.ToString($logDate) + " BunBackup end") | Out-File $logfile -Append

    # バックアップ所要時間計算
    $bk_time = New-TimeSpan $bk_start (Get-Date)
    $h = "{0:00}" -f $bk_time.Hours
    $m = "{0:00}" -f $bk_time.Minutes
    $s = "{0:00}" -f $bk_time.Seconds

    # LINE Notify 送信
    $line_uri    = "https://notify-api.line.me/api/notify"
    $line_token  = "Bearer [ここにLINE Notifyのトークンを入れる]"
    $header = @{Authorization=$line_token}
    $body   = @{message="`r`nBackup Complet`r`nTime $($h):$($m):$($s)"}
    $res    = Invoke-RestMethod -Uri $line_uri -Method Post -Headers $header -Body $body

    # 実行日を記録
    Write-Output $today | Out-File $log_path

    # 終了のお知らせ(デスクトップのお知らせファイルを削除)
    Remove-item "$Env:USERPROFILE\Desktop\バックアップ中"
}

# シャットダウン
Write-Output "60秒後にシャットダウン。中止するには shutdown /a"
shutdown /s /t 60
pause

参考にしたページ

コメント