結論:毎回新しい時刻フォルダへコピーし、自動削除は分離する
VBAで安全にバックアップするなら、元ファイルへ手を加えず、実行のたびに新しいフォルダを作る。保存先は backup_20260723_171900 のように秒まで含め、既存ファイルへの上書きを許可しない。
対象はWindows版デスクトップExcelである。 Windowsの絶対パスとFileSystemObjectを使うため、Mac版Excelやブラウザ版Excelへそのまま貼り付けて使うコードではない。
この記事の安全版は、次の順番で動く。
- バックアップ元と保存先が存在するか確認する
- 元と先が同じ場所や入れ子になっていないか確認する
._partial_backup_日時という作業中フォルダを新規作成する- 直下のファイルを上書き禁止でコピーし、1件ずつ存在とサイズを確認する
- 全件の検証に合格した場合だけ
backup_日時へ名前を確定する - 古い世代の整理は別マクロで候補を確認し、永久削除せず隔離する
重要:このコードが対象にするのは、指定フォルダ直下のファイルだけだ。サブフォルダの中身はコピーしない。対象範囲を明示することで、リンク先や想定外の巨大フォルダまで辿る事故を避ける。

図の「世代管理」は、この記事では即時削除ではなく、候補確認と隔離で行う。ファイルシステムのコピーや削除にはデータベースのような一括ロールバックがないため、「コピーできた」と「復元できる」を分けて確認することが大切だ。
このVBAで守れる事故と守れない事故
このマクロは、作業ファイルを誤って上書きしたときに以前のコピーへ戻るための簡易スナップショットだ。バックアップソフト、クラウドの版履歴、組織の災害対策を置き換えるものではない。
| 想定する事象 | このVBAでの備え | 追加で必要な対策 |
|---|---|---|
| 元ファイルを上書きした | 直前に成功した別フォルダのコピーへ戻せる | 実行頻度を業務の更新頻度に合わせる |
| 同じ日に複数回保存したい | 秒単位の別フォルダを作り、既存世代を上書きしない | 必要なら実行ボタンや運用時刻を決める |
| コピー途中で1件失敗した | 作業中フォルダを完成扱いにせず残す | エラー対象を直し、最初から再実行する |
| 保存先ドライブが故障した | 同じドライブに保存していた場合は守れない | 別ドライブ、サーバー、管理されたクラウドを使う |
| ランサムウェアや不正操作 | 接続中の保存先も変更される可能性がある | アクセス分離、オフライン保管、組織のバックアップを使う |
| 開いたままのファイルや未保存の変更 | コピー失敗や、最後に保存した状態だけのコピーになり得る | 保存して閉じてから実行し、復元テストを行う |
OneDriveやSharePointで管理しているOfficeファイルは、組織設定に応じて版履歴を利用できる場合がある。Windowsにはバックアップと回復の機能もある。業務上重要なデータでは、VBAだけに依存せず、管理者が定めた仕組みと組み合わせる。
危険な旧コードから変える6つの設計
バックアップ処理では、成功時の速さより失敗時の境界が重要になる。Microsoft公式によると、CopyFile は最初のエラーで停止し、それ以前のコピーを元に戻さない。DeleteFolder も内容の有無を区別せず削除し、途中変更をロールバックしない。
| 確認点 | 危険な設計 | この記事の設計 |
|---|---|---|
| 保存先 | 日付だけの同名フォルダを再利用 | 秒+連番で毎回新規作成 |
| 上書き | CopyFile ..., True |
CopyFile ..., False |
| 途中失敗 | コピー済み分も完成品に見える | ._partial_ と状態ファイルで区別 |
| 成功判定 | ループを通った件数だけ | 存在・サイズ・元の更新有無をファイル単位で確認 |
| 世代整理 | バックアップ直後に永久削除 | 候補プレビューと隔離を別処理にする |
| 日付解析 | 変換エラーを無視し、前回値が残る | 毎回0へ戻し、形式と往復結果を厳密確認 |
サイズが一致しても、ファイル内容が完全に同じと証明できるわけではない。この記事の検証は明らかな欠落や途中変更を見つけるための基本確認だ。厳密な完全性検証や大量データの差分保存が必要なら、ハッシュ検証や専用バックアップ製品を選ぶ。
本番前に確認するパス・権限・対象範囲
最初は本番フォルダを指定せず、数個のコピー可能なファイルを入れたテスト用フォルダで試す。次の6項目を説明できない状態では実行しない。

1. バックアップ元は専用の作業フォルダにする
Cドライブのルート、ユーザーフォルダ全体、デスクトップ全体のような広い場所は指定しない。コードがコピーするのは直下のファイルだけだが、対象件数と容量を説明できる専用フォルダに限定する。
2. 保存先は元と別の場所にする
元と先が同一、または一方が他方の配下にある構成はコードで中止する。同じ物理ドライブ内の別フォルダは上書き対策にはなるが、ドライブ故障への備えにはならない。重要データは別ドライブや管理された保存先を選ぶ。
3. 保存先フォルダは手作業で先に作る
安全版は、入力ミスしたパスを自動作成しない。エクスプローラーで保存先を作り、アクセスできることを確認してから定数へ貼り付ける。ネットワーク共有では、読み取りだけでなくフォルダ作成と書き込みの権限も必要だ。
4. 対象ファイルを保存して閉じる
開いているファイルは、アプリケーションや共有状態によってコピーできない場合がある。コピーできても、画面上の未保存変更は含まれない。Excelブックは保存して閉じ、他の利用者が更新していない時間帯にテストする。
5. 空き容量と対象件数を先に把握する
保存先の空き容量が不足すると、途中までコピーした作業中フォルダが残る。事前にフォルダ内のファイル一覧を取得する方法などで対象件数と容量の目安を確認する。処理後は同じ観点で結果を照合する。
6. 社内規程と機密区分を確認する
顧客情報や個人情報を、許可されていないUSBドライブや個人クラウドへコピーしてはいけない。保存先の選択は技術だけでは決められないため、組織のバックアップ方針とアクセス権限に従う。
STEP1:上書きしない検証付きバックアップを作る
次のコードを標準モジュールへまとめて貼り付ける。書き換えるのは冒頭の SOURCE_FOLDER と BACKUP_ROOT の2か所だ。どちらも実在するフォルダを指定する。
CreateVerifiedBackup は永久削除を行わない。コピー中は ._partial_backup_... を使い、全ファイルの基本検証が終わった場合だけ backup_... へ移動して完成扱いにする。
Option Explicit
Private Const SOURCE_FOLDER As String = "C:\Work\品質データ"
Private Const BACKUP_ROOT As String = "D:\Backups\品質データ"
Private Const BACKUP_PREFIX As String = "backup_"
Private Const PARTIAL_PREFIX As String = "._partial_backup_"
Private Const STATUS_FILE_NAME As String = "_BACKUP_STATUS.txt"
Public Sub CreateVerifiedBackup()
Dim fso As Object
Dim sourceFolder As Object
Dim sourceFile As Object
Dim sourcePath As String
Dim backupRootPath As String
Dim partialPath As String
Dim finalPath As String
Dim targetFilePath As String
Dim expectedCount As Long
Dim copiedCount As Long
Dim startedAt As Date
Dim sourceSizeBefore As Variant
Dim sourceModifiedBefore As Date
Dim previousStatusBar As Variant
Dim partialCreated As Boolean
Dim finalized As Boolean
Dim errNumber As Long
Dim errDescription As String
On Error GoTo ErrHandler
startedAt = Now
previousStatusBar = Application.StatusBar
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FolderExists(SOURCE_FOLDER) Then
Err.Raise vbObjectError + 2100, _
"CreateVerifiedBackup", _
"バックアップ元が見つかりません: " & SOURCE_FOLDER
End If
If Not fso.FolderExists(BACKUP_ROOT) Then
Err.Raise vbObjectError + 2101, _
"CreateVerifiedBackup", _
"保存先が見つかりません: " & BACKUP_ROOT
End If
sourcePath = NormalizeFolderPath(fso, SOURCE_FOLDER)
backupRootPath = NormalizeFolderPath(fso, BACKUP_ROOT)
If IsStorageRoot(fso, sourcePath) Then
Err.Raise vbObjectError + 2102, _
"CreateVerifiedBackup", _
"ドライブや共有のルートはバックアップ元に指定できません。"
End If
If IsStorageRoot(fso, backupRootPath) Then
Err.Raise vbObjectError + 2103, _
"CreateVerifiedBackup", _
"ドライブや共有のルートは保存先に指定できません。"
End If
If PathsOverlap(fso, sourcePath, backupRootPath) Then
Err.Raise vbObjectError + 2104, _
"CreateVerifiedBackup", _
"元と先が同一、または入れ子になっています。"
End If
If fso.FileExists( _
fso.BuildPath(sourcePath, STATUS_FILE_NAME)) Then
Err.Raise vbObjectError + 2109, _
"CreateVerifiedBackup", _
"元フォルダに予約名のファイルがあります: " & _
STATUS_FILE_NAME
End If
Set sourceFolder = fso.GetFolder(sourcePath)
expectedCount = sourceFolder.Files.Count
If expectedCount = 0 Then
MsgBox "直下にバックアップ対象ファイルがありません。" & vbCrLf & _
"サブフォルダはこのコードの対象外です。", vbInformation
GoTo CleanExit
End If
BuildUniqueBackupPaths fso, backupRootPath, startedAt, _
partialPath, finalPath
fso.CreateFolder partialPath
partialCreated = True
WriteBackupStatus fso, partialPath, "INCOMPLETE", _
sourcePath, startedAt, 0, expectedCount, _
"コピーを開始しました。"
For Each sourceFile In sourceFolder.Files
Application.StatusBar = _
"バックアップ中: " & (copiedCount + 1) & _
" / " & expectedCount & " 件"
sourceSizeBefore = sourceFile.Size
sourceModifiedBefore = sourceFile.DateLastModified
targetFilePath = fso.BuildPath(partialPath, sourceFile.Name)
fso.CopyFile sourceFile.Path, targetFilePath, False
If Not fso.FileExists(targetFilePath) Then
Err.Raise vbObjectError + 2105, _
"CreateVerifiedBackup", _
"コピー先を確認できません: " & targetFilePath
End If
If fso.GetFile(targetFilePath).Size <> sourceSizeBefore Then
Err.Raise vbObjectError + 2106, _
"CreateVerifiedBackup", _
"コピー前後のサイズが一致しません: " & _
sourceFile.Name
End If
If sourceFile.Size <> sourceSizeBefore Or _
sourceFile.DateLastModified <> sourceModifiedBefore Then
Err.Raise vbObjectError + 2107, _
"CreateVerifiedBackup", _
"コピー中に元ファイルが変更されました: " & _
sourceFile.Name
End If
copiedCount = copiedCount + 1
Next sourceFile
If copiedCount <> expectedCount Then
Err.Raise vbObjectError + 2108, _
"CreateVerifiedBackup", _
"予定件数とコピー件数が一致しません。"
End If
WriteBackupStatus fso, partialPath, "COMPLETE", _
sourcePath, startedAt, copiedCount, expectedCount, _
"存在・サイズ・コピー中の元更新を確認しました。"
fso.MoveFolder partialPath, finalPath
finalized = True
MsgBox "バックアップが完了しました。" & vbCrLf & _
"コピー件数: " & copiedCount & " 件" & vbCrLf & _
"保存先: " & finalPath & vbCrLf & vbCrLf & _
"次に、別の場所へ1ファイル復元して開けるか確認してください。", _
vbInformation
CleanExit:
Application.StatusBar = previousStatusBar
Set sourceFile = Nothing
Set sourceFolder = Nothing
Set fso = Nothing
Exit Sub
ErrHandler:
errNumber = Err.Number
errDescription = Err.Description
On Error Resume Next
If partialCreated And Not finalized Then
WriteBackupStatus fso, partialPath, "INCOMPLETE", _
sourcePath, startedAt, copiedCount, expectedCount, _
"エラー " & errNumber & ": " & errDescription
End If
Application.StatusBar = previousStatusBar
On Error GoTo 0
MsgBox "バックアップを完成扱いにしませんでした。" & vbCrLf & _
"コピー済み: " & copiedCount & " / " & _
expectedCount & " 件" & vbCrLf & _
"エラー番号: " & errNumber & vbCrLf & _
"内容: " & errDescription & vbCrLf & vbCrLf & _
"._partial_ で始まるフォルダは自動削除せず、" & _
"原因確認後に手作業で扱ってください。", vbCritical
End Sub
Private Sub BuildUniqueBackupPaths( _
ByVal fso As Object, _
ByVal backupRootPath As String, _
ByVal startedAt As Date, _
ByRef partialPath As String, _
ByRef finalPath As String)
Dim stamp As String
Dim suffix As String
Dim attempt As Long
stamp = Format$(startedAt, "yyyymmdd_hhnnss")
For attempt = 0 To 999
If attempt = 0 Then
suffix = ""
Else
suffix = "_" & Format$(attempt, "000")
End If
partialPath = fso.BuildPath( _
backupRootPath, PARTIAL_PREFIX & stamp & suffix)
finalPath = fso.BuildPath( _
backupRootPath, BACKUP_PREFIX & stamp & suffix)
If Not fso.FolderExists(partialPath) And _
Not fso.FolderExists(finalPath) Then
Exit Sub
End If
Next attempt
Err.Raise vbObjectError + 2110, _
"BuildUniqueBackupPaths", _
"一意なバックアップフォルダ名を作成できません。"
End Sub
Private Function NormalizeFolderPath( _
ByVal fso As Object, _
ByVal rawPath As String) As String
Dim normalized As String
normalized = Trim$(rawPath)
If Len(normalized) = 0 Then
Err.Raise vbObjectError + 2111, _
"NormalizeFolderPath", _
"空のフォルダパスは指定できません。"
End If
If InStr(normalized, "*") > 0 Or _
InStr(normalized, "?") > 0 Then
Err.Raise vbObjectError + 2112, _
"NormalizeFolderPath", _
"ワイルドカードを含むパスは指定できません。"
End If
If Not IsAbsoluteWindowsPath(normalized) Then
Err.Raise vbObjectError + 2113, _
"NormalizeFolderPath", _
"ドライブ名またはUNC共有名から始まる絶対パスを指定してください。"
End If
normalized = fso.GetAbsolutePathName(normalized)
Do While Len(normalized) > 3 And _
Right$(normalized, 1) = "\"
normalized = Left$(normalized, Len(normalized) - 1)
Loop
NormalizeFolderPath = normalized
End Function
Private Function IsAbsoluteWindowsPath( _
ByVal folderPath As String) As Boolean
Dim driveLetter As String
If Left$(folderPath, 2) = "\\" Then
IsAbsoluteWindowsPath = _
(Len(folderPath) > 4 And _
InStr(3, folderPath, "\") > 3)
Exit Function
End If
If Len(folderPath) < 3 Then Exit Function
driveLetter = UCase$(Left$(folderPath, 1))
IsAbsoluteWindowsPath = _
(driveLetter >= "A" And driveLetter <= "Z" And _
Mid$(folderPath, 2, 2) = ":\")
End Function
Private Function IsStorageRoot( _
ByVal fso As Object, _
ByVal folderPath As String) As Boolean
Dim normalized As String
Dim driveName As String
normalized = NormalizeFolderPath(fso, folderPath)
driveName = fso.GetDriveName(normalized)
Do While Len(normalized) > 2 And _
Right$(normalized, 1) = "\"
normalized = Left$(normalized, Len(normalized) - 1)
Loop
Do While Len(driveName) > 2 And _
Right$(driveName, 1) = "\"
driveName = Left$(driveName, Len(driveName) - 1)
Loop
IsStorageRoot = _
(StrComp(normalized, driveName, vbTextCompare) = 0)
End Function
Private Function PathsOverlap( _
ByVal fso As Object, _
ByVal firstPath As String, _
ByVal secondPath As String) As Boolean
Dim firstKey As String
Dim secondKey As String
firstKey = UCase$(AddTrailingSeparator( _
NormalizeFolderPath(fso, firstPath)))
secondKey = UCase$(AddTrailingSeparator( _
NormalizeFolderPath(fso, secondPath)))
PathsOverlap = _
(Left$(firstKey, Len(secondKey)) = secondKey) Or _
(Left$(secondKey, Len(firstKey)) = firstKey)
End Function
Private Function AddTrailingSeparator( _
ByVal folderPath As String) As String
If Right$(folderPath, 1) = "\" Then
AddTrailingSeparator = folderPath
Else
AddTrailingSeparator = folderPath & "\"
End If
End Function
Private Sub WriteBackupStatus( _
ByVal fso As Object, _
ByVal backupFolderPath As String, _
ByVal statusText As String, _
ByVal sourcePath As String, _
ByVal startedAt As Date, _
ByVal copiedCount As Long, _
ByVal expectedCount As Long, _
ByVal detail As String)
Dim stream As Object
Dim statusPath As String
statusPath = fso.BuildPath( _
backupFolderPath, STATUS_FILE_NAME)
Set stream = fso.CreateTextFile(statusPath, True, True)
stream.WriteLine "status=" & statusText
stream.WriteLine "source=" & sourcePath
stream.WriteLine "started_at=" & _
Format$(startedAt, "yyyy-mm-dd hh:nn:ss")
stream.WriteLine "updated_at=" & _
Format$(Now, "yyyy-mm-dd hh:nn:ss")
stream.WriteLine "copied_count=" & copiedCount
stream.WriteLine "expected_count=" & expectedCount
stream.WriteLine "detail=" & Replace(detail, vbCrLf, " | ")
stream.Close
Set stream = Nothing
End Sub
コードを貼る場所
対象ブックを .xlsm 形式で保存し、VBEの「挿入」→「標準モジュール」へ貼る。Option Explicit はモジュール先頭の1回だけにする。PERSONAL.XLSBへ置く場合でも処理対象は定数で指定したフォルダであり、ActiveWorkbook には依存しない。
パスへ末尾の「\」を付ける必要はない
FileSystemObject.BuildPath が区切り文字を補うため、定数は通常のフォルダパスとして書く。存在確認やパスの取得方法はVBAでファイル・フォルダの存在を確認する方法も参考になる。
同じ秒に再実行しても別フォルダになる
同じ時刻名が存在する場合は _001、_002 と連番を付ける。既存フォルダを再利用せず、CopyFile の上書き引数も False に固定しているため、同日再実行や部分失敗後の再実行で前の世代を置き換えない。
作業中フォルダはエラー調査用に残す
コピー途中で停止した場合、コードは ._partial_ フォルダを自動削除しない。失敗した時点までのファイルと _BACKUP_STATUS.txt を確認できるためだ。原因を直して新しく再実行した後、不要と判断できた作業中フォルダだけを手作業で整理する。
STEP2:テスト用ファイルで正常終了と中止条件を確認する
コードを貼った直後に本番データを指定するのではなく、まず「バックアップ元」と「保存先」の両方に専用のテストフォルダを作る。元フォルダには、開けるExcelファイル、テキストファイル、画像などを数個だけ置く。保存先は空にし、元とは別の場所にする。
定数を書き換えてマクロを実行したら、メッセージの「完了」だけで判断しない。次の表を上から順に確認すると、正常系と代表的な中止条件を小さな範囲で確かめられる。
| テスト | 操作 | 期待する結果 | 確認する意味 |
|---|---|---|---|
| 通常実行 | 数個の保存済みファイルで1回実行する | 時刻付きフォルダが1個でき、状態ファイルの先頭が「status=COMPLETE」になる | 作成・コピー・基本検証・完成名への変更が最後まで通る |
| 連続実行 | 同じテスト元で続けて2回実行する | 前のフォルダは残り、新しい時刻フォルダが増える | 既存世代を再利用せず、スナップショットが分離される |
| 空フォルダ | 元を空にして実行する | 対象がない旨を表示し、完成バックアップを作らない | 空のコピーを正常な世代として残さない |
| 同一パス | テスト時だけ元と先へ同じパスを指定する | コピー開始前に中止する | 自己コピーや保存先の取り込みを防ぐ境界が動く |
| 入れ子パス | 保存先を元フォルダの配下へ指定する | コピー開始前に中止する | バックアップを次のバックアップへ取り込む構成を拒否する |
| 存在しない保存先 | テスト用の誤ったパスへ変更する | 保存先が見つからない旨を表示し、自動作成しない | 入力ミスした場所へ新しいフォルダを作らない |
同一パスや存在しないパスのテストが終わったら、定数を正しいテスト用パスへ戻す。エラーを起こす目的で本番フォルダの権限を変更したり、実行中にドライブを抜いたりする必要はない。失敗テストも、壊れてよい一時データだけで行う。
完成フォルダと作業中フォルダを区別する
完成したバックアップは「backup_年月日_時分秒」という名前になる。コピー途中でエラーになったものは「._partial_backup_」で始まる名前のまま残る。この名前の違いは、見た目を整えるためではなく、復元候補を誤認しないための状態管理だ。
状態ファイルの先頭行も確認する。完成フォルダでは「status=COMPLETE」、作業中フォルダでは「status=INCOMPLETE」になる。ファイル数も記録されるため、元の直下件数とコピー済み件数を照合できる。ただし、この記録だけで復元可能性が保証されるわけではない。実際にファイルを開く確認まで行う。
想定どおり中止した場合は「失敗」ではない
元と先が重なる、元が空、指定先が存在しない、といった条件で止まるのは安全装置が働いた結果だ。エラー表示を消すために条件分岐を削らず、パスや対象範囲を直す。運用開始後も、中止理由が分からないまま再実行を繰り返さない。
STEP3:復元テストで「戻せるコピー」か確認する
バックアップは作成できただけでは役目を果たさない。元ファイルを失った状況を想定し、完成フォルダから別の復元テスト用フォルダへファイルをコピーして、実際に開けるかを確認する。元フォルダへ直接戻すと現行ファイルを上書きする恐れがあるため、最初の復元先は必ず別にする。
- 完成した時刻フォルダをエクスプローラーで開く。
- 状態ファイルの先頭が「COMPLETE」であることを確認する。
- 記録されたコピー件数と、状態ファイルを除いた実ファイル数を照合する。
- 復元テスト専用の空フォルダを作る。
- バックアップから代表的なファイルをそこへコピーする。
- Excelや対応アプリで開き、内容・シート・数式・更新日時など業務上重要な部分を確認する。
- 確認日、確認者、対象世代、結果を運用メモへ残す。
ファイルサイズの一致は、途中で明らかに欠けたコピーを検出する助けにはなる。しかし、内容が正しいことやアプリケーションで開けることまでは証明しない。マクロ有効ブック、外部リンクを含むブック、専用ソフトで使うファイルなどは、その形式を扱える環境で復元テストをする。
元ファイルを上書きしない復元手順
本当に復元が必要になった場合も、いきなりバックアップを元の場所へコピーしない。まず現行フォルダを触らずに保全し、選んだ世代を別フォルダへ展開する。内容と日時を確認し、関係者が復元対象を合意してから置き換える。共有フォルダなら、ほかの利用者の更新を止める時間帯も決める。
誤上書きから戻すだけなら、クラウドの版履歴の方が適切な場合もある。バックアップ元がOneDriveやSharePointで管理されているときは、管理者の設定と版履歴を先に確認する。VBAのコピーとクラウド側の履歴は、守る事故の種類が異なる。
STEP4:古い世代は自動削除せず、候補確認から始める

バックアップ作成と永久削除を同じマクロへ入れると、日付判定やパス判定の小さな不具合がデータ消失へ直結する。特に、フォルダ名から日付を読み取れなかったときに前回の変数値が残るコードは危険だ。現在のフォルダを古い世代だと誤認し、内容ごと削除する可能性がある。
そのため、この記事のコードは古いフォルダを削除しない。保存容量の整理が必要になったら、次の4段階を別作業として行う。
- 候補を抽出する:保存期間を過ぎた完成フォルダだけを一覧にする。
- 人が確認する:名前、状態ファイル、日時、業務上必要な締め世代を照合する。
- 隔離する:削除候補を同じ保存領域の「_quarantine」などへ移動し、通常の復元候補から外す。
- 待機後に手動削除する:次回の復元確認や業務サイクルを通過し、不要と確認できたものだけを削除する。
隔離はバックアップではない。同じドライブ内でフォルダを移しただけなら、ドライブ故障への耐性は増えない。ここでの目的は、誤判定した候補をすぐ永久削除せず、戻せる時間を設けることだ。
削除候補にしてよい最低条件
単に「更新日時が古い」だけでは候補にしない。更新日時はコピーや手作業で変わる場合があり、名前が似ている別フォルダも存在し得る。少なくとも次の条件をすべて確認する。
| 条件 | 確認内容 | 条件から外れた場合 |
|---|---|---|
| 直下のフォルダ | 定めたバックアップ保存先の直下にある | 触らず、用途を確認する |
| 名前が完全一致 | 決めた接頭辞と14桁の日時、必要なら3桁連番の形式である | 日付を推測せず候補外にする |
| 有効な暦日 | 月日・時分秒が実在し、文字列へ戻して同じ値になる | 候補外にし、命名元を調べる |
| 完成状態 | 状態ファイルの先頭が「COMPLETE」である | 作業中データとして別途調査する |
| 保存期間超過 | 組織で決めた起算日と期間を過ぎている | 残す |
| 保全対象ではない | 月末・年度末・監査・障害調査などで保持指定されていない | 保持指定を優先する |
| 最近の復元確認済み | 別の新しい世代で復元テストが成功している | 先に復元確認をする |
保存期間は件数ではなく業務要件から決める
「必ず7世代」「30日なら十分」と一律には決められない。更新頻度、誤りに気づくまでの時間、月次締め、監査、法令・契約、保存容量によって必要期間は変わる。毎日更新するデータと月に一度しか確定しないデータでは、残すべき間隔も異なる。
日次の短期世代に加えて、月末や年度末だけ長く残す運用も考えられる。ただし、個人判断で保持期間を短くしない。業務データでは、所有者と管理者が復元目標、保持期間、削除承認者を決め、そのルールを運用メモに残す。
永久削除をVBAで行う場合の注意
フォルダ削除のコード自体は短いが、短いから安全とは限らない。MicrosoftのDeleteFolderメソッドは、対象フォルダに内容があっても削除し、途中でエラーになるとそれまでの操作を元へ戻さない。ワイルドカードや強制指定を組み合わせる前に、対象一覧の表示、直接の親パス確認、完成印、保持除外、明示承認、実行ログを設計する必要がある。
具体的な削除処理を検討する場合は、VBAで古いファイルを削除する方法も参照できる。ただし、本記事のバックアップ保存先へそのまま適用せず、読み取り専用の候補抽出と隔離を先に作る。永久削除は、復元テスト済みの別工程として扱う。
継続運用:実行前・実行後・定期確認を分ける
一度成功したマクロでも、翌日以降に同じ条件で動くとは限らない。ファイルが開かれている、ネットワークが切れている、権限が変わった、保存容量が減った、といった外部条件は変化する。毎回の短い確認と、週次・月次の深い確認を分けておくと、異常を見逃しにくい。
| タイミング | 確認すること | 記録すること |
|---|---|---|
| 実行前 | 元と先のパス、未保存ファイル、共有利用者、保存先の接続、空き容量 | 実行予定時刻と対象業務 |
| 実行直後 | 完了メッセージ、完成フォルダ名、状態ファイル、件数、作業中フォルダの有無 | 完成世代、コピー件数、エラーの有無 |
| 定期確認 | 代表ファイルの復元、保存容量、隔離候補、保持指定、アクセス権 | 復元結果、確認者、削除・保持の判断 |
| 構成変更時 | フォルダ移動、ドライブ交換、共有名変更、担当者変更、データ形式追加 | 変更理由、テスト結果、承認者 |
実行ログはメッセージ画面だけに残さない
画面の完了メッセージは閉じると消える。安全版では各世代の状態ファイルに、元パス、開始・更新時刻、コピー件数、期待件数、詳細を残す。これに加えて、チームの運用台帳へ「いつ、誰が、どの世代を確認したか」を記録すると、障害時に選ぶべき復元候補を説明しやすい。
状態ファイルには元パスが記録されるため、そのパス自体が機密情報になる場合がある。バックアップ保存先の閲覧権限を広げすぎず、外部へ状態ファイルだけを渡すときも内容を確認する。ログを残すことと、情報を無制限に公開することは別である。
サブフォルダは意図的に対象外
掲載コードがコピーするのは、指定した元フォルダ直下のファイルだけだ。サブフォルダを黙って無視する点は、実行前に対象者へ伝える。対象件数が0のときはメッセージで中止するが、直下に数個のファイルがあれば、その下のフォルダが未コピーでも処理自体は完了する。
また、元フォルダ直下に「_BACKUP_STATUS.txt」という名前のファイルがある場合は、状態ファイルとの衝突を避けるためコピー開始前に中止する。元データ側のファイルを勝手に改名せず、管理者と用途を確認してから対象範囲または運用を見直す。
階層全体を保存したい場合は、対象範囲、循環するリンク、アクセスできないフォルダ、長いパス、非常に大きいファイル、アプリケーションが管理する作業ファイルなどを別途検討する。単純に1行をCopyFolderへ替えるだけでは、完成判定と検証範囲が合わなくなる。ファイル単位の操作はVBAでファイルをコピー・移動する方法を参照しつつ、まず小さいテスト階層で設計し直す。
自動実行の前に手動運用を安定させる
毎日決まった時刻に動かしたくても、最初からタスクスケジューラやブック起動イベントへ接続しない。無人実行では、Excelのダイアログ、マクロの信頼設定、ネットワーク認証、スリープ、開いているファイル、担当者不在時のエラー対応が加わる。手動で複数回成功し、復元確認と通知方法が決まってから自動化の範囲を広げる。
自動化後も、成功メッセージを見られない環境では別の監視が必要になる。完成世代が増えているだけでなく、状態、件数、経過時間、保存容量を確認する。処理が動かなかった日と、対象に変更がなかった日は意味が違うため、実行事実も記録する。
エラーが出たときの安全な切り分け
エラーが出たら、まずメッセージと作業中フォルダの状態ファイルを保存する。その後で元と先を確認し、原因を直して新しい世代を作る。中途半端なフォルダを完成名へ手作業で変更したり、状態行を「COMPLETE」へ書き換えたりしてはいけない。
| 表示・症状 | 主な確認点 | 安全な対応 |
|---|---|---|
| バックアップ元が見つからない | ドライブ文字、共有名、フォルダ移動、入力ミス | エクスプローラーから実在パスを確認し、絶対パスで指定し直す |
| 保存先が見つからない | 外付けドライブ、ネットワーク接続、共有権限 | 接続を回復し、保存先を手作業で開けることを確かめてから再実行する |
| 絶対パスを指定するよう求められる | 相対パス、ドライブ相対表記、不完全なUNC名 | 「C:\フォルダ」または「\\サーバー\共有\フォルダ」の形へ直す |
| ドライブや共有のルートを拒否される | 対象範囲が広すぎる | 用途が明確な子フォルダを作り、そこだけを指定する |
| 元と先が重なると表示される | 同一パス、親子関係、保存先の指定間違い | 別の独立した保存先へ変更する。安全判定を削除しない |
| 直下に対象がない | 元が空、ファイルがサブフォルダ内だけにある | 対象範囲を再確認する。空バックアップは完成扱いにしない |
| 予約名のファイルがある | 元直下の「_BACKUP_STATUS.txt」 | 元ファイルを自動変更せず、用途を確認して対象範囲を見直す |
| アクセスが拒否された | 読み取り・作成・書き込み権限、ファイルの使用中、セキュリティ製品 | 管理者へ権限を確認し、アプリを閉じる。権限昇格を安易に常用しない |
| コピー先が既に存在する | 同名ファイル、同時実行、手作業で作った世代 | 既存ファイルを消さず、同時実行を止めて別の新しい世代として再実行する |
| サイズが一致しない | コピー中断、保存容量、接続断、元ファイルの更新 | 作業中フォルダを保持し、容量と接続を確認して新しくやり直す |
| コピー中に元が変更された | 他の利用者、同期アプリ、編集中のファイル | 更新が止まる時間帯を選び、保存・クローズ後に新しい世代を作る |
| 作業中フォルダだけが残る | 状態ファイルのdetail、コピー済み件数、直前のエラー | 復元候補には使わず、原因調査後に正常な完成世代を作る |
エラー処理でも元データを変更しない
掲載コードのエラー処理は、状態を記録し、画面へ理由を表示して終了する。コピーできなかった元ファイルを削除したり、名前を変えたり、閉じたりはしない。途中まで作った保存先も自動削除しないため、原因調査後に人が整理できる。
一方、FileSystemObjectのコピーは最初のエラーで止まり、それ以前にコピーしたファイルを自動で取り消さない。だから作業中と完成をフォルダ名で分け、すべての基本確認後にだけ完成名へ変える。これは完全なトランザクションではないが、途中結果を正常世代と取り違えにくくする境界になる。
ネットワーク共有では再接続後も確認する
共有フォルダが一時的に切断されると、接続し直した後に見た目上アクセスできても、コピー途中の世代は完全とは限らない。残った作業中フォルダを使い回さず、新しい時刻で最初から実行する。共有側の監査ログや容量制限を確認できる場合は、管理者にも状況を伝える。
バックアップ設計をVBAだけに任せない
同じパソコンの別フォルダへコピーすると、誤上書きには対応できても、端末の故障、盗難、ランサムウェア、火災など同じ障害範囲にある事故には弱い。別ドライブ、管理されたネットワーク、クラウド版履歴など、異なる障害範囲の仕組みを組み合わせる。
ただし、保存先を増やすほど良いとは限らない。機密データを許可なく個人クラウドやUSBへ複製すると、情報管理上の事故になる。データ所有者、保存場所、暗号化、アクセス権、保持期間、廃棄方法を組織の方針に合わせる。
リアルタイム更新されるデータは専用手順を使う
データベース、会計システム、メールデータ、仮想マシンなど、アプリケーションが継続的に更新するデータは、単純なファイルコピーだけでは整合した状態にならない場合がある。提供元のバックアップ機能、エクスポート、スナップショット、停止手順を優先する。
Excel中心のフォルダでも、外部データ接続、リンク先、アドイン、テンプレート、証明書などが別の場所にあることがある。ブック単体を戻したときに業務が再開できるか、復元テストで依存関係も確認する。
容量不足は保持ルールで予防する
毎回すべての直下ファイルを新しいフォルダへコピーするため、変更が少なくても世代ごとに容量を使う。実行前に元フォルダの総量だけを見るのではなく、1世代の概算容量、1週間・1か月の実行回数、保持する締め世代、保存先の余裕を見積もる。
容量が逼迫してから慌てて削除すると、判断を誤りやすい。警告を出す残量、隔離を始める時期、追加容量を申請する担当者を前もって決める。差分や重複排除が必要な規模になったら、VBAの全量コピーから専用製品へ移行する合図である。
FAQ:よくある質問
Q1. バックアップ先に同名ファイルがある場合は上書きされますか?
上書きしない。実行ごとに新しい時刻フォルダを作り、CopyFileの上書き指定もFalseにしている。同じ秒の名前が既にあれば連番を付けるため、既存世代を再利用しない。
Q2. サブフォルダの中もコピーされますか?
されない。掲載コードの対象は元フォルダ直下のファイルだけである。階層全体が必要なら、対象範囲と検証方法を設計し直し、小さなテスト環境で確認する。
Q3. 作業中フォルダは削除してよいですか?
まず状態ファイルとエラー原因を確認する。正常な完成世代を新しく作り、作業中データが調査や復旧に不要だと判断できた後で、対象パスをエクスプローラー上でも確認して手動整理する。
Q4. ファイルサイズが同じなら完全なバックアップですか?
完全とは断定できない。サイズ確認は基本的な欠落検出であり、内容の一致やアプリで開けることを保証しない。代表ファイルを別フォルダへ復元し、実際に開いて確認する。
Q5. 何日分のバックアップを残せばよいですか?
一律の正解はない。更新頻度、誤りの発見までの時間、締め処理、監査・契約、保存容量から決める。業務データでは所有者と管理者が保持期間と例外世代を定める。
Q6. 毎日自動実行してもよいですか?
まず手動実行、状態確認、復元テストを複数回行う。無人化する場合は、認証、ネットワーク、スリープ、開いているファイル、失敗通知、実行ログを追加し、動かなかった日を検知できるようにする。
Q7. OneDriveの版履歴があればこのVBAは不要ですか?
用途による。版履歴は誤編集や以前の版への復元に有効な場合がある一方、対象範囲や保持期間は組織設定に依存する。VBAの別コピー、版履歴、端末・組織のバックアップが何を守るかを整理して使い分ける。
Microsoft公式資料で仕様を確認する
コピー・移動・削除の挙動は、次のMicrosoft公式資料を基準にした。環境差や仕様変更を確認するときは、解説記事だけでなく一次資料も参照する。
- FileSystemObject object:ファイルシステム操作の入口
- CopyFile method:上書き引数、最初のエラーで停止しロールバックしない挙動
- BuildPath method:親パスと名前からパスを組み立てる方法
- GetAbsolutePathName method:絶対パス表現の取得
- CreateTextFile method:状態ファイルの作成
- MoveFolder method:検証後に作業中フォルダを完成名へ移す処理
- Size property:ファイルサイズの取得
- DeleteFolder method:内容を含む削除とロールバックされない挙動
- View previous versions of Office files:Microsoft 365の版履歴
- Backup, restore, and recovery in Windows:Windowsのバックアップと回復
本番利用へ移す前の判定
次の項目をすべて満たしたときだけ、本番フォルダへ切り替える。1つでも説明できない項目があれば、テスト用パスのまま設計を見直す。コードがエラーなく終わることより、担当者が対象・保存先・復元・失敗時対応を説明できることを優先する。
- バックアップ元と保存先が実在する絶対パスで、互いに独立している。
- コピー対象が直下ファイルだけであることを、利用者が理解している。
- 対象件数と1世代のおおよその容量を把握している。
- テストデータで通常実行、連続実行、空フォルダ、誤パスの中止を確認した。
- 完成フォルダと作業中フォルダを名前と状態ファイルで区別できる。
- 別フォルダへの復元テストで、代表ファイルを実際に開けた。
- 失敗時に誰へ連絡し、作業中フォルダをどう保全するか決めている。
- 保持期間、隔離、永久削除の承認者を決めている。
- 機密区分と保存先のアクセス権が組織ルールに合っている。
- VBA以外の版履歴やバックアップと役割分担を確認した。
本番へ切り替える日は、業務への影響が小さく、担当者が結果を確認できる時間を選ぶ。最初の数回は実行後すぐに件数と状態を確認し、定期的な復元テストの日程も同時に決める。担当者が変わるときは、コードだけでなく運用記録と復元手順も引き継ぐ。
判定結果は日付付きで残し、フォルダ構成、権限、対象形式、担当者のいずれかが変わったら再判定する。過去に成功した記録は、現在の構成でも安全に動く保証にはならない。
安全版バックアップの要点
元と先を独立させ、実行ごとに新しい時刻フォルダを作り、既存ファイルを上書きしない。コピー中は作業中名のままにし、件数・サイズ・元ファイルの途中変更を確認できた場合だけ完成名へ変える。エラー時は途中結果を残し、原因を直して新しい世代を作る。
さらに重要なのは、完成メッセージではなく復元テストで確認することだ。古い世代の永久削除は作成マクロから分離し、候補確認、隔離、待機、手動削除の順にする。重要データではVBAだけに依存せず、組織の版履歴・バックアップ・アクセス管理と組み合わせる。


コメント