Powershellでタスクスケジューラに日次バックアップタスクを作成する

Windowsのタスクスケジューラは、その名の通り開始条件を設定して特定の操作を定期的に実行してくれる機能ですね。
Linuxでいうところのcronです。Windowsのシステムそのものや、その他のソフトでも更新の確認などにタスクを仕込んでいます。

GUIが「コンピューターの管理」画面(MMC)にあるほか、コマンドラインツールも提供されています。
「コンピュータの管理」はスタートボタンのコンテキストメニューから開けます。

歴史があるのはコマンドラインですが、Windows 8やWindows Server 2012以降ではPowerShellコマンドレットからタスクスケジューラを操作することができます。
ゆるく使う例として、今回はrobocopyコマンドを使ったバックアップをタスクスケジューラで設定してみたいと思います。

検証した環境はWindows 10 Proです。

もくじ:

スクリプト例

先にスクリプト例を記載しておきます。以下の設定値は例なので、当然ですが環境に合わせて正しく設定する必要があります。

タスクを登録するPowerShellスクリプト:

タスクに指定するバッチファイル(例えばC:\bat_dir\batch_file.sh):

詳細を以降に書いておきます。

タスクスケジューラの操作

上の例を見てもわかりますが、PowerShellのタスクスケジューラ関連のコマンドレットはだいたい名詞(noun)の部分にScheduledTaskを含んでいますので使い方を調べる際に参考にして下さい。

今回はログオンしていなくてもバックアップを実行したいので、サービスアカウントを使ってタスクを実行させます。また、その際には管理者権限が要求されるので、PowerShellコンソールを「管理者として実行する」を指定して起動するなどの注意が必要です。

タスクの登録

タスクを登録するコマンドレットはRegister-ScheduledTaskで、アクション、トリガなど、そこに渡す設定値を事前に作っておく、という流れになります。

既に同じパスにタスクが定義されていた場合には弾かれますが、-Forceオプションを付けることで上書きできます。

よく使う設定を列挙しておくと以下のようになります。必須パラメータ以外を特に指定しなければデフォルト値が適用されます。

項目作成コマンドレット説明
ActionNew-ScheduledTaskActionタスク起動時に実行する処理
TriggerNew-ScheduledTaskTriggerタスクの起動条件(トリガ)
SettingsSetNew-ScheduledTaskSettingsSetタスクの設定
PrincipalNew-ScheduledTaskPrincipalタスクの設定(セキュリティオプション)

Get-ScheduledTaskコマンドレットを使って既存のタスクの設定を調べると参考になるでしょう。

設定は見たまんまなのですが、上の例では毎日PM11:00に(Trigger)バッチを起動する(Action)ものになっています。他のタスクと区別するために、管理コンソールで事前にmytasksディレクトリを作っておき、そこに格納することにしています。

PCが起動していてユーザがログインしていない状態でもタスクを実行させるため、Principalを設定しています。実行ユーザ-UserIdにシステムアカウント"NT AUTHORITY\SYSTEM"-LogonTypeServiceAccountを指定しているところがポイントです。

その他の操作

登録したタスクを一時的に無効化するなどその他の操作については、MMCから操作するか、もしくは下記のコマンドレットを使います。

robocopyを使ったバッチ

ディレクトリ階層を保持したままコピーを作成するツールとして、Windowsではxcopyコマンドがありますが、robocopyのほうがより高機能です。
Linuxでいうところのrsyncみたいなやつ。ローカルのファイルシステムだけではなくWindows共有のパス(\\で始まるUNC)も指定できるので、簡易なバックアップ機能を定義することができます。

なぜバッチファイルにするかというと、アクションの設定が楽(バッチファイルのパスを指定するだけ)だから。
他のアプリケーションだと、実行ファイルのパス(しばしば環境依存)と引数をそれぞれ指定する必要があり、自由度が高い代わりに間違いも起きやすいためです。

robocopy

基本の形式はこんな感じ。コピー元、コピー先のパスに続けて、オプションを指定していきます。

コピー元・先のパスはディレクトリを指定します。コマンドを実行するとコピー元ディレクトリ配下の内容を、コピー先ディレクトリ配下にコピーするので、指定したコピー元ディレクトリの名前は失われます(が属性はコピーしているようです)。

よく使うオプションは以下の通りです。

オプション説明
/s空でないサブディレクトリを対象に含める/s
/e空のディレクトリ含むサブディレクトリを対象に含める/e
/mir同期(コピー元から削除されたファイルをコピー先からも削除)する/mir
*.*対象のファイル名を指定する(複数指定可)*.jpg *.png
/max:”n”“n”バイトより大きいファイルを除外する/max:100000
/log:”file path”指定したパス”file path”のファイルにログを出力する/log:”C:\temp dir\mylog.log”
/npログに進行状況(%)を表示しない/np
/r:”n”失敗時のリトライ回数を”n”回に設定する/r:2
/teeログをファイルと標準出力の両方に出力する/tee

上記の例では、ログファイルを作成し、コピー先にない(または更新日付が異なる)100MB以下のファイルをコピー元からコピー先へ一方向にサブディレクトリ含めてコピーします。
同期、つまりコピー元の(可能な限り)完全な複製を作成する場合には/mirオプションを使います。そうでない場合は、コピー元で移動・削除されたファイルもコピー先に残り続けるので、コピー先のディスク消費量がどんどん増えていきます。

なお、今回はタスクの実行ユーザをシステムアカウントsystemにしているので、他のユーザが定義した環境変数は使えません。
Windowsのシステムツールならともかく、例えば、pythonコマンドなど特定のユーザのみにインストールしている(つまりユーザ定義の環境変数に依存している)場合は、実行ファイル(.exeや.cmdバッチファイル)のパスを絶対パスで指定するなど、何らかの方法で渡す必要があります。

リモートサーバの利用

NASなどリモートにあるサーバを利用する場合にはアカウントに注意が必要です。
実行するユーザと同じアカウント(ユーザ名とパスワード)がリモートサーバに設定されている場合や、事前にアカウントをWindowsに記憶させておいた場合は問題ないのですが、たいていのサーバは別途アカウントを用意している場合がほとんどでしょう。

robocopy自体にはアカウントを指定するパラメータはありませんので、そのままではコマンドを実行している(バッチを起動している)ユーザの権限に依存します。
接続用に別のアカウントが必要になる場合は、net useコマンドを使ってマッピングしてしまう方法があるようです。

シンタックスはこんな感じ。
接続したいUNCパスは”\\{リモートサーバ}\{共有名}\{共有名からのパス}”の形式になっていると思いますので、おなじリモートサーバを指定して、ホスト名(またはIPアドレス)、ユーザ名、パスワードを指定します。

先に記載したスクリプト例でいうと、コピー先がリモートサーバ(\\smbserver)上のパスなので、そのマッピング(IPCリソース共有)を設定しています。
ユーザ名は”ドメイン\ユーザ名”の形式で指定します。ドメイン、例えばActive Directoryに参加していない(WORKGROUPなど)サーバに接続する際は”コンピュータ名\ユーザ名”になります。

おわり。