「更新日時が60日より古いファイルだけを安全に自動削除したい」。Azure Data Factory(ADF)/ Synapse Pipelines の Delete アクティビティで迷いやすいのが Start Time / End Time の解釈と、意図せず当日のファイルまで消してしまう設定ミスです。本記事は“実務者の視点”で式・設定・検証・誤削除の防止策・ログ出力の止め方までを一気通貫で解説します。
60日より古いファイルだけを削除する最短解
まず結論から。60日より前だけを対象にする End Time の式は次のとおりです。Start Time は空欄にします。
@formatDateTime(addDays(utcNow(), -60), 'yyyy-MM-ddTHH:mm:ssZ')
addDays(utcNow(), -60)で現在から 60 日前の UTC 時刻を算出formatDateTime(..., 'yyyy-MM-ddTHH:mm:ssZ')で ISO 8601(Zulu/UTC)に整形
| パラメーター | 設定値 | 意味 |
|---|---|---|
| Start Time | (空欄) | 下限なし(=上限の End Time より前のすべて) |
| End Time | @formatDateTime(addDays(utcNow(), -60), 'yyyy-MM-ddTHH:mm:ssZ') | 「60 日より前」に最終更新されたファイルまでを対象 |
なぜそれで正しいのか:更新日時フィルターの仕様を理解する
Delete アクティビティの更新日時フィルターは、概念的に次の条件で動きます。
Start Time ≤ ファイルの最終更新日時 < End Time
つまり、Start Time は下限(以上)、End Time は上限(未満)です。上限のみを与えたい(=〇〇日より前は全部)なら Start Time を空欄にし、End Time だけ設定します。
| よくある意図 | 正しい設定 | 結果 |
|---|---|---|
| 60 日より前を全部消す | Start 空欄 / End = utcNow()-60d | 〜60 日前より前のみが対象 |
| 60〜90 日前だけを消す | Start = utcNow()-90d / End = utcNow()-60d | 90 日前以上かつ 60 日前未満が対象 |
| 直近 2 日(当日含む)を消す(※推奨しない) | Start = utcNow()-2d / End 空欄 | 2 日前以上〜上限なし ⇒ 当日も対象 |
ポイント:
- 時刻は UTC 前提で判定されます。日本時間で考えるとズレますが、式側で
Zを付けた ISO 形式にしておけば安全に比較されます。 - End Time は未満判定なので、「End = 2025-11-01T00:00:00Z」の場合、2025-11-01 00:00:00 ちょうどの更新は含まれません。
実装手順(ADF / Synapse Pipelines 共通)
前提
- 対象ストレージ:Azure Blob Storage / ADLS Gen2(ほか S3 や SFTP なども概ね同様。ただし更新日時フィルターの対応有無はコネクタに依存します)
- データセット:対象フォルダーを指すファイルシステム系データセット(DelimitedText、Binary など)
手順
- パイプラインパラメーターに
retentionDays(文字列/数値)を追加(デフォルト 60)。 - Set Variable で
nowUtc(String)に@utcNow()を一度だけ代入(再現性のため)。 - 続けて Set Variable で
cutoffUtc(String)に次の式を設定。@formatDateTime( addDays(variables('nowUtc'), -int(parameters('retentionDays'))), 'yyyy-MM-ddTHH:mm:ssZ' ) - Delete アクティビティを配置し、次のように設定。
- Dataset:削除対象のルートフォルダーを指すデータセット
- Recursively:必要に応じて有効(サブフォルダーも削除対象に含める)
- Wildcard file path(任意):
*.csvなど - Last modified フィルター:
- Start Time:空欄
- End Time:
@variables('cutoffUtc')
- Logging settings:CSV ログが不要なら Enable logging をオフ
「-2 日」で当日のファイルまで消えた原因
Start Time に @addDays(utcNow(), -2) を入れて End を空欄にすると、条件は Start Time ≤ 更新日時 <(上限なし) になり、直近 2 日+当日がすべて対象になります。「古いものだけ」を狙うときは Start を空欄、End に「〇日前」を入れるのが鉄則です。
誤削除を防ぐベストプラクティス
- ドライラン(事前確認):本番実行前に List Files / Get Metadata(childItems)で対象件数とファイル名を出力し、Pipeline の Debug 実行で確認。
- サンドボックス:削除テストはコピーした検証用コンテナー・フォルダーで行う。
- ソフト削除 / バージョン管理:Blob Soft Delete と Versioning を有効化しておけば、誤削除からの復旧が可能。
- アクセス制御:本番コンテナーの削除権限(Storage Blob Data Contributor など)はパイプライン実行 ID のみに限定。
- 二重安全装置:本番前に Approval(承認フロー)や「閾値超過時は停止」ブランチを用意。
フォルダーや CSV レポートが勝手に作られる?の真相
Delete アクティビティ自体はファイルを作成しません。次のケースで CSV が出力されます。
| 現象 | 原因(代表例) | 対処 |
|---|---|---|
| 削除結果の CSV が生成される | Delete の Logging settings > Enable logging がオン | 不要ならチェックを外す。必要なら保存先とファイル名パターンを明示的に指定 |
| フォルダーが自動で増える | パイプライン後段のアクティビティ(Copy、Notebook、Stored Procedure など)が成果物を出力 | 後続の依存アクティビティを見直し、不要な生成を無効化 |
| 監査用に CSV が残る | 共通部品(カスタム・ラッパー)やトリガー後処理でログ集約している | 共通ロジックを設定ファイル化し、環境別に出力有無を切替 |
対象フォルダー・ファイルの絞り込みテクニック
| 項目 | 用途 | 設定例 |
|---|---|---|
| File path in dataset | 特定のルートを対象に | /raw/logs/ |
| Recursively | サブフォルダーも含める | チェック |
| Wildcard file path | 拡張子/接頭辞で絞る | *.csv、backup_* |
| 最大同時接続 | 大量ファイルの並列削除 | IR の性能に合わせて調整 |
可変日数にする(再利用性を高める)
パラメーター化して 30 日、90 日などのポリシーに横展開できます。
@formatDateTime(
addDays(utcNow(), -int(parameters('retentionDays'))),
'yyyy-MM-ddTHH:mm:ssZ'
)
運用時はトリガーごとに retentionDays を上書き設定するだけで別の期限に対応できます。
時刻のズレをなくす:式の評価タイミングに注意
utcNow()は評価されるたびに値が変わる可能性があります。パイプラインの先頭で変数nowUtcに一度だけ保存し、以降の計算はすべてこの値を基準に行うとブレません。- 日付境界の 00:00 付近の実行は、想定より 1 日分多い/少ない対象を生むことがあります。スケジュールは余裕をもった時刻(例:03:15 UTC)に設定すると安定します。
大規模運用のパフォーマンス最適化
- フォルダー分割(年/月パーティション):
/yyyy/MM/dd/のように日付でディレクトリパーティションを切ると、対象列挙が高速化します。 - ワイルドカードで対象縮小:ログの種類別に接頭辞を付け、Delete で種類ごとに削除。
- 並列度の調整:Integration Runtime のサイズやストレージのスループットに合わせ、同時接続数(並列度)を段階的に上げる。
- 大量削除の分割:初回導入時に膨大な旧データを消す場合は、月単位・年単位で「範囲(Start / End)」を区切って分割実行する。
監視と監査の実務ポイント
- 実行結果のサマリー:成功・失敗件数、スキップ件数をパイプラインの Set Variable で集計し、必要に応じてメールや Teams 通知。
- 業務時間外の実行:アプリケーションの夜間メンテナンス時間に合わせてトリガーを設定。
- ログ設計:CSV ログを出す場合は保存先を
/logs/retention/<yyyyMMdd>/などに固定し、混在を防ぐ。
コネクタごとの注意点(概要)
| コネクタ | 更新日時フィルター | 補足 |
|---|---|---|
| ADLS Gen2 / Blob | 概ね利用可能 | ソフト削除・バージョン管理の活用で安全性向上 |
| Amazon S3 | 概ね利用可能 | リージョン間レプリカで時刻差分に注意 |
| SFTP / FTP | サーバー実装依存 | 更新日時が取れない場合は拡張子・パスで代替 |
更新日時フィルターが利用できない環境では、次善策として「ファイル名に日付を含める(例:log_yyyyMMdd.csv)」「サブフォルダーで日付パーティションを切る」などの規約化が有効です。
安全にロールバックするための備え
- Blob の Soft Delete + バージョン:削除済みのオブジェクトを一定期間復元可能。運用ルールとして有効化を検討。
- ライフサイクル管理と役割分担:アーカイブ保管(コピー)→削除の 2 段階構成にし、削除は翌日実行に遅延させる。
- 削除対象のスナップショット:初期導入時などは、削除前に対象一覧(パスのみ)の CSV を必ず保存。
よくある質問(FAQ)
Q. 「60 日前」とは「60 × 24 時間」で計算されますか?
A. はい。addDays(utcNow(), -60) は現在の UTC から 60 × 24 時間を引いた時刻です。日付の切り替え境界ではなく、実行時刻ベースで判断されます。
Q. ローカル時刻(JST)で指定できますか?
A. Delete の判定は UTC ベースです。式で Z 付き ISO 形式に統一するのが安全です。JST を使いたい場合は、addHours で 9 時間を補正して ISO 化するなど、煩雑なので推奨しません。
Q. 当日のファイルを絶対に削除したくありません。どうすれば?
A. End Time を @formatDateTime(addDays(utcNow(), -1), ...) にすれば、「前日まで」しか対象になりません。さらに保険として Start を空欄のままにし、ドライランで件数を確認してください。
Q. ログ CSV の列項目は変更できますか?
A. 変更はできません。不要なら Enable logging をオフにするか、後続で自作の集計ロジックに置き換えます。
Q. 削除の並列度を上げたら失敗が増えました。
A. ストレージ側のスロットリングや IR の帯域が原因のことがあります。並列度を段階的に下げ、失敗時の再試行ポリシー(Retry)を設定してください。
設定例(コピペ用スニペット)
End Time を 60 日前に固定
@formatDateTime(addDays(utcNow(), -60), 'yyyy-MM-ddTHH:mm:ssZ')
日数をパラメーター化(retentionDays)
@formatDateTime(
addDays(utcNow(), -int(parameters('retentionDays'))),
'yyyy-MM-ddTHH:mm:ssZ'
)
70〜60 日前の範囲に限定(区間削除)
| 項目 | 式 |
|---|---|
| Start Time | @formatDateTime(addDays(utcNow(), -70), 'yyyy-MM-ddTHH:mm:ssZ') |
| End Time | @formatDateTime(addDays(utcNow(), -60), 'yyyy-MM-ddTHH:mm:ssZ') |
導入チェックリスト(使う前に 1 分で確認)
- Start Time は本当に空欄か?(古いだけを消す場合)
- End Time の式は
Z付き ISO 形式になっているか? - ワイルドカードや再帰の設定は想定どおりか?
- ドライランで対象一覧と件数を確認したか?
- Blob の Soft Delete / Versioning は有効か?
- ログ CSV は必要か?不要なら Enable logging をオフにしたか?
まとめ
「〇日より古いだけを消す」要件は、Delete アクティビティの End Time に“基準日”を渡し、Start Time は空欄にするのが最短・安全解です。式は
@formatDateTime(addDays(utcNow(), -60), 'yyyy-MM-ddTHH:mm:ssZ')
を基点に、パラメーター化・ドライラン・ログ方針・リカバリ策を組み合わせれば、Synapse/ADF どちらでも堅牢に運用できます。誤設定で当日のファイルを巻き込む事故は「Start/End の意味の取り違え」が大半です。本記事の手順とチェックリストをテンプレート化し、環境ごとのルールに落とし込めば、保守容易で安全なライフサイクル運用が実現できます。

コメント