【PowerShell】RobocopyでローカルフォルダのデータをID/パスワード付きのNASにバックアップする

スポンサーリンク
powershell
スポンサーリンク

ローカルフォルダ(Dドライブ)のデータを自宅NASにバックアップしたい

Cドライブはシステムイメージバックアップを取っているけど、そういえばDドライブに溜め込んだデータはバックアップ設定していないなーと思ったので自宅のNASにデータバックアップをすることにした。

Dドライブ丸ごとコピーしてNASにペーストしても良かったのだが、それなりにちょこちょこデータが更新されるので毎回コピー・ペーストするのもカッコ悪いなと思い、PowerShellとRobocopyを利用してバックアップスクリプトを作ることにした。

うちのNASはID/PASSをアクセス時に要求するので、NASの資格情報をWindowsの資格情報マネージャーに保存しておくことにした。
また、robocopy結果をログに残す仕様にすることにした。
さらに、一々管理者権限で実行を指定するのは面倒なので、スクリプト内で管理者として実行しているか確認し、管理者として実行されていない場合は管理者として実行するように促す形にした。

搭載機能

  • 指定したコピー元($srcFolder)から指定したコピー先($dstFolder)へrobocopyを実施する。
    今回は、コピー元を「Dドライブ」、コピー先を「\\192.168.1.253\backup\Dドライブ」とする。(適宜修正してください)
  • NASのユーザー名とパスワードはスクリプトに書かず、Windowsの資格情報マネージャーに保存しておく。最初に1回だけ、PowerShellかコマンドプロンプトで次を実行する(NASのアドレスとユーザー名は適宜修正してください)。/passの後ろを空けておくとパスワードを聞かれるので、パスワードが画面や履歴に残らない。
    スクリプトは、NASのログフォルダに届かない時(資格情報が保存されていない時など)はコピーせずに止まる。
cmdkey /add:192.168.1.253 /user:backupper /pass
  • robocopyのオプションは以下とする。
    /MIR /DCOPY:DAT /COPY:DAT /IT /B /XD $excluded /log+:"$logFile" /np /R:3 /W:3
    /MIRはコピー元とコピー先を同じ状態になるようにするオプションである。コピー元にないファイルはコピー先から消えるので、Dドライブが見えない時や空の時は、NASのバックアップまで消えてしまう。そのため、スクリプトはその場合はコピーせずに止まるようにしている。
     
    /DCOPY:DATはディレクトリにコピーする情報を指定するオプションである。
    D(データ)、A(属性)、T(タイムスタンプ)をコピーする。
     
    /COPY:DATはファイルにコピーする情報を指定するオプションである。
    D(データ)、A(属性)、T(タイムスタンプ)をコピーする。
    なお、/COPYALLは恐らく以下のようなエラーとなる。
    注意: セキュリティがコピーされない可能性があります - コピー元で固定 ACL がサポートされていない可能性があります。
    エラー 1314 (0x00000522) NTFS セキュリティをコピー先ディレクトリにコピーしています \\192.168.1.253\backup\Dドライブ\ クライアントは要求された特権を保有していません。

    これは、/COPYALLだとセキュリティ情報等までもコピー対象にしてしまうので、ローカルと権限の管理管轄が異なるNASではコピーしようがないためと思われる。同一ドメインで管理されていれば問題なく通るかもしれない(未検証)。

    /ITはファイル属性だけが異なるファイル(Tweaked)もコピー対象に含めるオプションである。

    /Bはバックアップモードでの実行を指定するオプションである。これは対象ファイルへのアクセス権がなかったとしてもファイルコピーできるようになるオプションである。(なので本スクリプトの実行は管理者権限を持つユーザーが対象となる)

    /XD $excludedは、指定したフォルダをコピーしないオプションである。ドライブのルートにある「$RECYCLE.BIN」(ごみ箱)と「System Volume Information」を除外している。

    /log+:"$logFile"は$logFileにログを出力させるオプションである。

    /npはコピーの完了率を表示させないオプションである。これはログ出力時にログが完了率だらけで読めなくなるのを防ぐためである。

    /R:3は失敗したコピーに対する再試行回数を3に指定するものである。
    /Rは既定値が1,000,000であり、コピー時間が無駄に増大するだけなので最低限の回数に変更する。

    /W:3は再試行と再試行の間の待機時間を3秒間に指定するものである。
    /Wは既定値が30秒であり、/Rと合わせてコピー時間が無駄に増大するだけなので最低限の秒数に変更する。
  • 管理者権限で実行をされているかを確認し、されていない場合は管理者権限で再実行する。
  • robocopyの実行結果をログ出力し、ログ保存用のフォルダに保存する。
    $logFolderでログフォルダパスを指定し、$logFileでログのファイル名を指定する。
    本スクリプトではログフォルダパスを「\\192.168.1.253\backup\バックアップ用ツール\Dドライブロボコピー\log」とする。(適宜修正してください)
    なお、ログフォルダは予め作成しておく必要あり。
    ファイル名はyyyy-mm-dd_datacopy.logとなるようにする。
  • 指定された日数よりも古いログファイルがあった場合、自動でログを削除するようにする。
    本スクリプトでは93日(約3ヶ月間)より古いログは削除する仕様とする。
  • robocopyの後に、コピー先フォルダの隠し属性とシステム属性を外す。ドライブのルート(D:\)をコピー元にすると、コピー先のフォルダが隠しフォルダになるためだ。詳しくはrobocopyでコピー先フォルダが非表示になる原因と直し方に書いた。
  • robocopyの終了コードが8以上の時は、エラーとして表示する。robocopyの終了コードは、8以上がコピーに失敗したファイルがあることを表す。終了コードの詳しい意味はrobocopyの終了コード一覧と意味に書いた。

コード

書き換えるのは$srcFolder・$dstFolder・$logFolderの3行だけだ(自分の環境に合わせてください)。

# ============================================================
#  Dドライブを NAS へミラーする (2026-11-08 改訂版)
#  旧版からの変更点:
#   1. パスワードをスクリプトに書かない。NAS の資格情報は
#      資格情報マネージャー(cmdkey)に保存したものを使う。
#   2. D: が見えない・空の時は止める(/MIR で NAS 側まで消えるのを防ぐ)。
#   3. $RECYCLE.BIN と System Volume Information をコピーしない。
#   4. (NAS 側のスナップショット/ごみ箱は NAS の設定で行う)
#   5. robocopy の結果を見て、エラーの時はそう表示する。
#  /B を使うので管理者として実行する(自動で管理者として開き直す)。
# ============================================================

# 管理者として実行されていなければ、管理者として開き直す
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Host "管理者として再実行しています..."
    Start-Sleep -Seconds 2
    Start-Process powershell.exe -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File "$PSCommandPath""
    exit
}

$srcFolder = "D:\"
$dstFolder = "\\192.168.1.253\backup\Dドライブ"
$logFolder = "\\192.168.1.253\backup\バックアップ用ツール\Dドライブロボコピー\log"
$logFile   = Join-Path $logFolder "$((Get-Date).ToString('yyyy-MM-dd'))_datacopy.log"

function Stop-WithMessage($msg) {
    Write-Host $msg -ForegroundColor Red
    Read-Host "Enter キーで閉じます"
    exit 1
}

# --- 安全確認 1: NAS に届くか(資格情報マネージャーの設定が無いとここで止まる)
if (-not (Test-Path -LiteralPath $logFolder)) {
    Stop-WithMessage "NAS に接続できません: $logFolder`n資格情報マネージャーに NAS の資格情報が保存されているか確認してください。"
}

# --- 安全確認 2: D: が見えない・ほぼ空なら止める
$excluded = @('$RECYCLE.BIN', 'System Volume Information')
if (-not (Test-Path -LiteralPath $srcFolder)) {
    Stop-WithMessage "D: が見つかりません。NAS のバックアップを守るため、コピーせずに終了します。"
}
$items = @(Get-ChildItem -LiteralPath $srcFolder -Force -ErrorAction SilentlyContinue | Where-Object { $excluded -notcontains $_.Name })
if ($items.Count -eq 0) {
    Stop-WithMessage "D: が空です。NAS のバックアップを守るため、コピーせずに終了します。"
}

# --- コピー(ルートの隠し・システム属性がコピー先に付かないよう、最後に attrib で外す)
robocopy $srcFolder $dstFolder /MIR /DCOPY:DAT /COPY:DAT /IT /B /XD $excluded /log+:"$logFile" /np /R:3 /W:3
$rc = $LASTEXITCODE
attrib -s -h "$dstFolder"

# --- 古いログを削除(93日より前)
Get-ChildItem -LiteralPath $logFolder -Filter "*.log" |
    Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-93) } |
    ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force }

# --- 結果表示(robocopy は 8 以上がエラー)
if ($rc -ge 8) {
    Stop-WithMessage "コピーでエラーがありました(終了コード $rc)。ログを確認してください: $logFile"
}
Write-Host "完了しました(終了コード $rc)。ログ: $logFile" -ForegroundColor Green
Read-Host "Enter キーで閉じます"

タスクスケジューラで毎日自動で実行する

毎回手で実行するのは忘れるので、タスクスケジューラに登録した。PowerShellを「管理者として実行」で開き、次を貼り付ける(スクリプトのパスは適宜修正してください)。毎日12:30に実行し、PCの電源が切れていてその時刻に動けなかった日は、次に起動した時に実行される。

$a = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "D:\backup-scripts\Dドライブロボコピー.ps1"'
$t = New-ScheduledTaskTrigger -Daily -At 12:30
$p = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive -RunLevel Highest
$s = New-ScheduledTaskSettingsSet -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 12)
Register-ScheduledTask -TaskName 'DドライブをNASへコピー' -Action $a -Trigger $t -Principal $p -Settings $s -Force
  • -RunLevel Highestは「最上位の特権で実行する」の設定で、管理者として実行されるので、確認画面を出さずに/Bが使える。
  • -LogonType Interactiveは、ログオンしている時だけ実行する設定だ。資格情報マネージャーに保存したNASの資格情報は、ログオンしているユーザーのものを使うため。
  • -StartWhenAvailableは「スケジュールされた時刻にタスクを開始できなかった場合、すぐにタスクを実行する」の設定だ。
  • -NonInteractiveを付けているのは、スクリプトの最後の「Enter キーで閉じます」で止まったままにならないようにするためだ。

実行イメージ

※画面は、資格情報マネージャーを使う前のスクリプトで実行した時のもの。

注意点と補足

  • PowerShellが実行可能な環境である必要があります。
  • コピー元をドライブのルート(D:など)にすると、コピー先のフォルダに隠し属性とシステム属性が付いて、エクスプローラーに表示されなくなることがあります。原因と直し方はrobocopyでコピー先フォルダが非表示になる原因と直し方に書きました。

改版履歴

2024/02/08 初版公開
2026/11/08 パスワードを資格情報マネージャーに保存する形に変更。安全確認・不要フォルダの除外・エラー判定・タスクスケジューラでの自動実行を追加

コメント

タイトルとURLをコピーしました