古いWindowsサーバーや共有フォルダを整理していると、「このあたりの古いファイル、もう消してもよさそうですよね」という話が出てきます。容量も圧迫している。手作業で見るには数が多い。ならPowerShellでまとめて消そう、となる流れは自然です。
ただ、レガシー環境のファイルは、名前だけでは判断しにくいものが混ざります。old、backup、temp と付いていても、実際には月次処理で参照されていたり、前任者が障害対応時に退避したものだったりします。
この記事では、Windows環境で不要ファイル削除を自動化する前に確認したいことを整理します。完成版の削除スクリプトではなく、削除処理を書く前に決めておくべき条件と、PowerShellに入れておきたい最低限の安全確認を扱います。
レガシー環境の削除自動化は、コードより先に条件整理が必要
不要ファイル削除の話になると、最初に出てくる条件はだいたい更新日時です。「180日以上更新されていないファイルを削除する」といった決め方です。
これは分かりやすい一方で、レガシー環境では少し雑です。古いけれど参照されるファイルがあります。更新されないことに意味があるテンプレートもあります。過去の運用で、消さない前提の退避フォルダが作られていることもあります。
削除対象を決めるときは、まず次の2つを分けて考えます。
- 古いファイルを探す条件
- 消してはいけないファイルを除外する条件
この2つを混ぜると、スクリプトは短くなりますが、あとから説明しにくくなります。削除事故が起きたときに困るのは、「なぜそれを消してよいと判断したのか」を説明できないことです。
削除スクリプトを書く前に決めるべきなのは、消す処理ではなく、消してよいと判断する条件です。
Remove-Item の書き方を調べるのは、その後で十分です。
まず決めるのは「どこまで触ってよいか」
削除条件を考える前に、スクリプトが触ってよい場所を固定します。ここが曖昧なまま削除処理を書くと、後から条件を足しても不安が残ります。
たとえば、対象を共有フォルダ全体にせず、\\server\share\archive 配下だけに限定します。さらに、master、template、manual_backup のようなフォルダ名は除外します。
この時点で、削除対象はかなり狭くなります。狭すぎるくらいで始めた方が、レガシー環境では扱いやすいです。
対象フォルダは許可リストで固定する
削除スクリプトに対象パスを引数で渡せるようにすると便利です。ただ、削除処理に限っては、その便利さが危険になることがあります。
相対パスを渡してしまう。ドライブレターが別の場所を指している。タスクスケジューラで動かしたら、手元と違うユーザーで実行される。Windowsでは、こうしたズレが起きやすいです。
最低限、次の確認は入れたいところです。
- 許可するルートフォルダを決める
- 対象パスを絶対パスとして解決する
- 解決後のパスが許可ルート配下か確認する
- 許可ルート外なら処理を止める
ポイントは、入力されたパスをそのまま信用しないことです。許可ルート外なら止める。この条件を入れるだけでも、削除スクリプトの性格はかなり変わります。
除外条件は、更新日時とは別に持つ
更新日時だけで削除対象を決めると、古いけれど必要なファイルを巻き込みます。
たとえば、次のようなファイルやフォルダです。
- マスタファイル
- 帳票テンプレート
- 手動退避フォルダ
- 外部連携用のCSV
- 利用部署が直接参照している共有ファイル
これらは、頻繁に更新されないことがあります。むしろ更新されていないからこそ安定して使われている、という場合もあります。
更新日時の条件と、除外条件は別に持った方が安全です。コードの中に条件を散らすより、「この名前を含むフォルダは対象外」「この拡張子は対象外」と先に一覧化しておく方が、関係者にも確認しやすくなります。
判断に迷うものは、削除ではなく退避に回します。レガシー環境では、この一手間が効きます。
Windowsではパス、権限、実行ユーザーを先に見る
手元のPowerShellで動いたスクリプトが、サーバー上でも同じように動くとは限りません。特に削除処理では、実行場所と実行ユーザーの違いがそのまま事故につながります。
共有フォルダを Z:\Archive のようなドライブレターで扱っている環境はよくあります。人がログオンして作業する分には問題なくても、タスクスケジューラで動かすユーザーには、その Z: が存在しないかもしれません。
この手のスクリプトでは、できればUNCパスを使います。\\server\share\archive のように実体が分かる形にして、実行ユーザーから見えるか確認します。カレントディレクトリに依存する相対パスは避けた方が無難です。
もう一つ見ておきたいのが、削除できなかった場合の扱いです。使用中のファイル、読み取り専用のファイル、権限不足のフォルダは普通に出ます。
ここでエラーを握りつぶすと、見た目は終わっているのに、実際には一部だけ残っている状態になります。削除できなかったこと自体より、何が残ったのか分からない方が困ります。
ログには、少なくとも次の情報を残します。
- 実行日時
- 実行ユーザー
- 対象フォルダ
- 削除対象として抽出した条件
- 削除できなかったパス
- エラーメッセージ
ログは後回しにされがちですが、削除処理ではほぼ本体の一部です。
PowerShellでは削除前に一覧、件数、ドライランを挟む
PowerShellでファイルを消すだけなら簡単です。だからこそ、危ない形でもすぐ書けてしまいます。
たとえば、次のようなコードです。
$target = $args[0]
Remove-Item -Path $target -Recurse -Force短いですが、本番の共有フォルダでは使いたくありません。対象パスの確認がなく、削除候補の一覧も出さず、いきなり子フォルダまで消しに行きます。
-Recurse や -Force が悪いわけではありません。問題は、それらを使う前に「本当にその場所でよいか」「何件消えるのか」を確認していないことです。
最低限の形にすると、次のようになります。実際に使う場合は、パス、日数、除外条件を環境に合わせて調整してください。
$allowedRoot = "\\server\share\archive"
$targetPath = "\\server\share\archive\old-files"
$days = 180
$resolvedRoot = (Resolve-Path -LiteralPath $allowedRoot).Path
$resolvedTarget = (Resolve-Path -LiteralPath $targetPath).Path
if (-not $resolvedTarget.StartsWith($resolvedRoot, [StringComparison]::OrdinalIgnoreCase)) {
throw "対象フォルダが許可ルート外です: $resolvedTarget"
}
$limitDate = (Get-Date).AddDays(-$days)
$candidates = Get-ChildItem -LiteralPath $resolvedTarget -File -Recurse |
Where-Object {
$_.LastWriteTime -lt $limitDate -and
$_.FullName -notmatch "\\(master|template|manual_backup)\\"
}
Write-Host "削除候補: $($candidates.Count) 件"
$candidates | Select-Object FullName, LastWriteTime
$candidates | ForEach-Object {
Remove-Item -LiteralPath $_.FullName -WhatIf
}見てほしいのは、削除コマンドそのものではなく順番です。
1. 許可ルートを決める
2. 対象パスを解決する
3. 許可ルート配下か確認する
4. 削除候補を抽出する
5. 件数と一覧を表示する
6. 最初は -WhatIf で確認する
-WhatIf は、実行した場合に何が起きるかを確認するためのものです。ただし、これを付ければ安全になるわけではありません。抽出条件が間違っていれば、間違った候補が表示されるだけです。
本削除へ切り替える前に、削除候補の一覧をCSVに出す、関係者に確認する、件数が想定より多ければ止める、といった確認を挟んだ方が安全です。
いきなり削除せず、退避とログで戻れる状態を作る
レガシー環境では、完全削除よりも退避を挟んだ方が現実的なことがあります。
たとえば、最初の運用では削除せず、退避フォルダに移動します。30日や60日置いて、問い合わせがなければ削除する。容量削減を急いでいないなら、この方が関係者の合意を取りやすくなります。
削除対象の一覧を見せたときに、「それは昔の処理で使っているかもしれない」と言われることがあります。そこで初めて分かる運用もあります。レガシー環境では、それを織り込んで進めた方が現実的です。
ログも同じです。削除した後に「何を消したか分からない」となると、復旧も説明も難しくなります。削除または退避したファイル、失敗したファイル、実行ユーザー、実行日時は残しておきます。
自動化の目的は、人が何も見なくてよい状態にすることではありません。毎回の手作業を減らしつつ、問題が起きたときに追える状態を作ることです。
定期実行にする前に、小さく試してから広げる
スクリプトができたら、すぐタスクスケジューラに登録したくなります。ここは少し待った方がいいです。
最初はテストフォルダで動かします。次に、本番と同じ構成の一部フォルダで -WhatIf を使います。その後、退避だけを実行し、最後に本削除へ進めます。
あわせて、停止条件も決めます。
- 削除候補が想定件数を超えたら止める
- 許可ルート外のパスなら止める
- ログ出力に失敗したら本削除しない
- 初回は手動確認なしで定期実行しない
削除スクリプトは、動いたことより、止まるべきところで止まることの方が大事です。特にレガシー環境では、想定外のファイルが混ざっている前提で組んだ方が安全です。
まとめ
レガシー環境の不要ファイル削除を自動化するときは、PowerShellの書き方より先に、削除してよい条件を決めます。古いファイルが不要とは限りません。古いからこそ、昔の運用や例外を抱えていることがあります。
押さえておきたいのは、次の点です。
- 更新日時だけで削除対象を決めない
- 対象フォルダは許可ルート配下に限定する
- WindowsではUNCパス、実行ユーザー、権限を確認する
- 削除前に一覧、件数、ログを残す
- 最初は
-WhatIfや退避で検証する - 定期実行は小さく試してから広げる
削除スクリプトは、短く書けることより、説明できることと止められることが大切です。まずは対象条件、除外条件、退避期間、実行ユーザーを整理してから、スクリプト化へ進めるのが安全です。
