Microsoft Graph API で SharePoint/OneDrive のファイルを「完全削除」したいのに、permanentDelete が 400(API Not Found)や 404(Not Found)になる──。原因は多くの場合、エンドポイントの指定ミスです。この記事では正しい URL 形式、ID の取り方、必要な権限、エラー時の切り分けまでまとめます。
結論:permanentDelete は「drive-id と item-id」を含めて呼び出す
ドライブ内のアイテムを permanentDelete(完全削除)する公式の呼び出し方は、drive-id と item-id の両方を URL に含めた次の形式です。
POST /drives/{drive-id}/items/{item-id}/permanentDelete
一方で、次のように item-id を省いた URL は API のルーティングに一致せず、400(Bad Request / API not found)や 404(Not Found)になりやすいので注意してください。
POST /drives/{drive-id}/items/permanentDelete
permanentDelete と通常削除(Delete)の違い
「削除」と一口に言っても、Graph API には大きく 2 種類あります。通常削除(Delete)は、SharePoint/OneDrive のリサイクル ビンに移動する挙動です。対して permanentDelete は、リサイクル ビンを経由せずに完全削除する挙動で、復元できません。
| 目的 | HTTP メソッド / エンドポイント | リサイクル ビン | 復元 | 成功時ステータス |
|---|---|---|---|---|
| 通常削除(ゴミ箱へ) | DELETE /drives/{drive-id}/items/{item-id} | 移動する | 可能(リサイクル ビンに残っている間) | 204 |
| 完全削除(ゴミ箱を迂回) | POST /drives/{drive-id}/items/{item-id}/permanentDelete | 移動しない | 不可 | 204 |
「まずは削除して、一定期間は戻せるようにしておきたい」場合は Delete(通常削除)を選び、どうしても完全削除が必要なときだけ permanentDelete を使う、という使い分けが現実的です。
なぜ 400 / 404 が出るのか:今回の根本原因
Graph API は、URL パスで「どのリソースに対して」「どの操作をするか」を厳密に判断します。permanentDelete は “drive の中の特定アイテム(driveItem)” に対する操作なので、URL に {item-id} が入っていないと対象を特定できません。
結果として、次のようなエラーになりがちです(400 はリクエストが不正、404 はリソースが存在しないことを意味します)。
| ステータス | よくある意味 | このケースで起きる理由 |
|---|---|---|
| 400 Bad Request | リクエストが不正・解釈できない | パス構造が仕様どおりでなく、API として成立しない(item-id がない等) |
| 404 Not Found | 対象リソースが存在しない | item-id が未指定/誤り、または既に削除済みで取得できない |
手順:drive-id と item-id を取得して permanentDelete を実行する
「正しい URL に直したのにまだ動かない」という場合も、ほとんどは ID 取得か権限に原因があります。ここでは SharePoint ドキュメント ライブラリを想定して、最短の流れを整理します。
drive-id を取得する
SharePoint のドキュメント ライブラリは Graph API 上では “drive” として扱われます。サイト配下のドライブ一覧を取得し、削除対象があるライブラリの drive-id を確認します。
GET /sites/{site-id}/drives
レスポンスの value 配列に、各ライブラリの id(drive-id)や name が含まれます。複数ライブラリがある場合は、名前が似ていることも多いので、まずはブラウザーのライブラリ名と突き合わせるのがおすすめです。
item-id を取得する
item-id(driveItem の ID)の取り方は大きく 2 通りあります。Graph では driveItem を「ID で指定する」方法と「パスで指定する」方法が用意されているため、状況に合わせて使い分けます。
| 取り方 | 例 | 向いているケース | 注意点 |
|---|---|---|---|
| フォルダーの children から探す | GET /drives/{drive-id}/root/children | 対象がライブラリ直下、または親フォルダーが分かっている | 階層が深いと何度か children を辿る必要がある |
| パスで直接参照してメタデータを取る | GET /drives/{drive-id}/root:/path/to/file | ファイルのパスが分かっている | 日本語/スペースを含むパスは URL エンコードに注意 |
どちらの方法でも、レスポンスに含まれる id が item-id です。削除処理では、パス指定よりも item-id を使うほうが安定します(リネームや URL エンコードの影響を受けにくい)。
permanentDelete を呼び出す
drive-id と item-id が揃ったら、いよいよ完全削除です。permanentDelete は POST で呼び出し、リクエスト ボディは不要です。成功すると 204 No Content が返り、レスポンス本文は空です。
POST https://graph.microsoft.com/v1.0/drives/{drive-id}/items/{item-id}/permanentDelete
動作確認のコツ(204 で終わるため)
permanentDelete は “成功しても返却ボディがない” ため、ログや監視がないと「本当に消えたのか」不安になりがちです。次のような確認フローにすると、運用が安定します。
- 削除前に
GET /drives/{drive-id}/items/{item-id}でnameやwebUrlをログに残す - permanentDelete の戻り値が 204 であることを必ず記録する
- 削除後に同じ GET を実行して 404 になることを確認する(削除直後は反映に時間がかかるケースもあるので、数秒後に再試行する設計にする)
必要なアクセス許可(権限):委任とアプリ権限の違い
permanentDelete は「完全削除」という性質上、適切な権限がないと失敗します。公式ドキュメントでは、委任権限(ユーザーがサインインして実行)とアプリケーション権限(バックエンド処理など)で必要スコープが整理されています。
| 権限の種類 | 最小権限(例) | より強い権限(例) | 使いどころ |
|---|---|---|---|
| 委任(組織アカウント) | Files.ReadWrite | Files.ReadWrite.All, Sites.ReadWrite.All | 利用者が自分の権限範囲で削除する |
| 委任(個人 Microsoft アカウント) | Files.ReadWrite | Files.ReadWrite.All | OneDrive Personal のツール/アプリ |
| アプリケーション | Files.ReadWrite.All | Sites.ReadWrite.All | サーバー側で自動削除する(管理者承認が必要なことが多い) |
権限設定で迷ったら、「まずは最小権限で動作する構成」から始めて、どうしても足りない場合にだけ上位権限へ引き上げるのが安全です。特に *.All 系の権限は影響範囲が大きくなりやすいので、テナント全体に付与する前に運用設計(監査ログ、誤削除時の復旧手段、承認フロー)までセットで検討してください。
また、SharePoint Embedded を利用している場合は FileStorageContainer.Selected など追加の前提が必要になるケースがあります。該当する場合は、コンテナー関連の権限要件も合わせて確認してください。
400 / 404 が続くときのチェックリスト
正しい URL でもエラーが出る場合に、上から順に潰していけるチェック項目です。
| 症状 | チェックするポイント | 具体的な対処 |
|---|---|---|
| 400(API not found / Bad Request) | URL 形式、HTTP メソッド、ボディ有無 | POST /drives/{drive-id}/items/{item-id}/permanentDelete になっているか。POST 以外で呼んでいないか。ボディを送っていないか。 |
| 404(Not Found) | drive-id / item-id の一致 | 別ライブラリの drive-id を使っていないか。item-id を取り直して同じものか確認。削除前に GET が通るか確認。 |
| 403(Forbidden) | 権限不足、管理者承認 | アクセストークンに必要スコープが入っているか、アプリ権限なら管理者コンセント済みか確認。 |
| 環境によって動かない | クラウド種別 | permanentDelete は利用できるクラウドに制限があります。グローバル環境は利用できますが、US Government L4/L5(DOD)や中国(21Vianet)では利用不可とされています。 |
なお、404 は「対象がない」以外にも、削除対象がすでに別の処理で消えていたり、削除前提の取得手順(item-id の取り方)がズレているときに起きやすいです。まずは削除前の GET を通して “本当にその ID のアイテムが存在するか” を確認してから permanentDelete を実行してください。
実装例:curl と PowerShell で最小構成テスト
アプリ側の実装に入る前に、まずは “API 自体が通るか” を最小構成で検証すると切り分けが速くなります。トークンが用意できている前提で、次の例をベースに試してください。
curl
curl -X POST "https://graph.microsoft.com/v1.0/drives/{drive-id}/items/{item-id}/permanentDelete" \
-H "Authorization: Bearer {access-token}" \
-H "Accept: application/json" \
-i
レスポンスが 204 No Content であれば成功です。ボディが空でも正常なので、HTTP ステータスと request-id(返ってくる場合)をログに残しましょう。
PowerShell(Invoke-WebRequest / Invoke-RestMethod)
$driveId = "{drive-id}"
$itemId = "{item-id}"
$token = "{access-token}"
$uri = "[https://graph.microsoft.com/v1.0/drives/$driveId/items/$itemId/permanentDelete](https://graph.microsoft.com/v1.0/drives/$driveId/items/$itemId/permanentDelete)"
$headers = @{
Authorization = "Bearer $token"
Accept = "application/json"
}
# 成功時は 204 でボディが返らないため、例外処理を含めて呼び出すと扱いやすいです
try {
Invoke-WebRequest -Method Post -Uri $uri -Headers $headers -UseBasicParsing | Out-Null
"permanentDelete succeeded (expected 204)."
}
catch {
$*.Exception.Message
if ($*.ErrorDetails -and $*.ErrorDetails.Message) {
$*.ErrorDetails.Message
}
}
安全に運用するためのポイント
- いきなり permanentDelete しない:業務要件が許すなら、まずは通常削除(リサイクル ビンへ)にして「一定期間で自動完全削除」など段階的にするほうが、誤削除に強いです。
- 削除対象のログを残す:削除前に
name、webUrl、parentReferenceなどを記録しておくと、監査や問い合わせ対応が楽になります。 - 権限は最小から:委任権限で足りるならそれが第一候補です。アプリ権限で
*.Allを付与する場合は、利用範囲・監査・責任分界を明確にしてから運用しましょう。 - 失敗時の再試行は“安全に”:404 は「既に消えている」可能性もあります。再試行する前に、削除前の GET が成功していたか、削除依頼が重複していないかを確認する設計が必要です。
まとめ
SharePoint の Graph API で permanentDelete が 400 / 404 になる場合、まず疑うべきは URL の形式です。POST /drives/{drive-id}/items/{item-id}/permanentDelete の形に直し、drive-id と item-id を正しく取得できているか、そして必要な権限がトークンに入っているかを順に確認すれば、多くのケースは解決できます。

コメント