Azure Storageアカウントで「Public network access: Disabled(パブリック ネットワーク アクセス無効)」にし、Private Endpoint(PE)経由に切り替えた途端に403(認証/認可エラー)になるケースは珍しくありません。DNS・PE状態・サブネット設定・ルーティング・権限のどこかが少しでも外れると発生するため、原因を最短で切り分ける手順を整理します。
症状の整理:なぜ「403」になりやすいのか
Azure Storageの403は、単純に「権限がない」だけでなく、ネットワーク制限で拒否されたときも同じように403で返ることがあります。Public network access を Disabled にした場合、許可される入口が極端に狭くなり、少しでも経路がズレると(本人はPEで行っているつもりでも)結果として403になりがちです。
まずは「403=RBAC不足」と決め打ちせず、どの種類の403なのかを切り分けるのが近道です。
| 観点 | よくある状況 | 現象 | 次に確認すること |
|---|---|---|---|
| ネットワーク由来の403 | DNSがパブリックIPに向く/PE未承認/経路がPEを通っていない | 「権限不足」に見える403だが、実際は入口が不一致 | DNS、PE接続状態、NSG/UDR、アクセス元がVNet内か |
| 認証(トークン)由来の403/401 | 意図せずSAS/キーで実行、トークンの対象が違う | AuthenticationFailed 等で失敗 | 認証方式(Azure AD/OAuth2)とツールの設定 |
| 認可(RBAC/ACL)由来の403 | データプレーン権限が不足、ADLS Gen2のACL不足 | AuthorizationPermissionMismatch 等で失敗 | RBACの付与スコープ、ロール種別、ACL |
前提:Public network access を Disabled にしたときの挙動
ここが一番の落とし穴です。Storageアカウントのネットワーク設定で Public network access を Disabled にすると、基本的にパブリックエンドポイント(例:account.blob.core.windows.net)からの到達は全面的にブロックされます。
つまり「PEがあるから大丈夫」ではなく、実際の通信が必ずPrivate EndpointのプライベートIPに向かい、PEが正常(承認済み・到達可能)で、必要なサブリソース(blob/dfs/file/queue/table等)まで揃っている必要があります。どこか1つでも欠けると、結果として403になります。
最短で切り分けるチェック(上から潰す)
Private Endpoint接続が「Approved(承認済み)」か
まずはストレージアカウント側で、該当PEの接続状態が Approved になっているか確認します。作成直後や手動承認が必要な構成だと、Pending のままになっていることがあります。
- ストレージアカウント → ネットワーク → Private endpoint connections
- 該当の接続が Approved になっているか
- 対象のサブリソース(blob / dfs / file / queue / table)が要件に合っているか
ポイント:たとえばBlob操作だけのつもりでも、利用しているSDK/ツールが dfs エンドポイント(ADLS Gen2系)を叩いていると、blobのPEだけでは足りず403に見える失敗になります。
DNSが「必ず」Private EndpointのプライベートIPを引いているか
Public network access が Disabled の状態で最も多いのがこれです。クライアントが名前解決した結果、パブリックエンドポイントのIPを引いてしまうと、到達はできても(あるいは到達したように見えても)ストレージ側で拒否され、403になります。
確認はシンプルで、アクセス元(実際に実行しているVM/コンテナ/Functionsの実行環境など)で名前解決結果がプライベートIPになっているかを見ます。
nslookup <storage-account-name>.blob.core.windows.net
nslookup <storage-account-name>.dfs.core.windows.net
返ってくるIPが 10.x / 172.16-31.x / 192.168.x のようなプライベートレンジで、しかもPEに割り当てられたIPになっていることが条件です。
Private DNS Zone を使う場合は、少なくとも次の組み合わせを押さえると事故が減ります。
| 用途 | アクセスするFQDN | 必要になりやすいPrivate DNS Zone | PEで選ぶサブリソース例 |
|---|---|---|---|
| Blob(一般的なBlob操作) | <account>.blob.core.windows.net | privatelink.blob.core.windows.net | blob |
| ADLS Gen2(階層型名前空間/HNS、abfs/dfs系) | <account>.dfs.core.windows.net | privatelink.dfs.core.windows.net | dfs |
| Azure Files | <account>.file.core.windows.net | privatelink.file.core.windows.net | file |
| Queue | <account>.queue.core.windows.net | privatelink.queue.core.windows.net | queue |
| Table | <account>.table.core.windows.net | privatelink.table.core.windows.net | table |
さらに、Private DNS Zone を用意しただけで終わりではありません。
- Private DNS Zone が対象VNetにリンクされているか
- クライアントが参照するDNS(Azure提供DNS、独自DNS、オンプレDNS)がそのゾーンを引ける経路になっているか
- 複数VNet/複数DNS(Hub-Spoke、NVA、カスタムDNS)を使う場合、条件付きフォワーダーや名前解決の優先順位で外れていないか
現場のあるある:「DNS解決できている」は、しばしば「名前が引ける」だけの意味になりがちです。重要なのは“どのIPを引いているか”です。Public access無効時はここで勝負が決まります。
PE用サブネットの必須設定(network policies)
Private Endpoint を置くサブネットには、PE要件としてサブネット側のネットワークポリシー(Private Endpoint network policies)を無効化する必要があります。これが未設定だと、PE自体は作れても通信が不安定になったり、想定外の拒否につながることがあります。
ポータルでも設定できますが、IaC/CLIで統一するなら次のように確認・設定します(例)。
# サブネットでPrivate Endpoint network policiesを無効化(例)
az network vnet subnet update \
--resource-group <rg> \
--vnet-name <vnet> \
--name <subnet-for-pe> \
--disable-private-endpoint-network-policies true
組織によっては「サブネットはNSG必須」などのルールがありますが、PE用サブネットは一般サブネットと同じ感覚で固めすぎると詰まります。PEのサブネットは“PE専用”にし、要件に沿ってシンプルに保つのが安全です。
NSG・UDR・NVA・プロキシが443を邪魔していないか
Private Endpoint は「VNet内のプライベートIP(NIC)宛ての通信」なので、通信経路上の制御(NSG、ユーザー定義ルート、NVA、透過プロキシ、SSLインスペクション)が強い環境だと、想定外のブロックや迂回が発生します。
- アクセス元サブネットのNSGで、PEのIP宛てのTCP/443が許可されているか
- UDRで0.0.0.0/0をNVAへ向けている場合、PE宛てが強制的にNVAへ吸い込まれていないか
- NVAでプライベート宛てトラフィックが落ちていないか(ログ/フローで確認)
「ネットワークを緩めると成功する」場合、DNSがずれているか、NSG/UDR/NVAでPE宛てが正しく通っていない可能性が高いです。
そもそも実行場所は“本当に”VNet内か
意外と見落とされるのがこれです。たとえば次のようなケースでは、実行主体がVNet外にいて、Private DNS Zone を見ていない(または見れていない)ことがあります。
- CI/CD(GitHub-hosted runner / SaaS型のビルド環境)から実行している
- Functions/Container Apps などで VNet 統合が未完了、または別の環境スロットで未設定
- 踏み台(Bastion等)で接続しているつもりが、実際は別ネットワークから叩いている
この場合は、同じコマンドでも実行場所を変えるだけで結果が変わります。切り分けとして、VNet内の検証用VMから同じ操作を行い、DNS・到達性・権限を確認すると速いです。
RBAC(データプレーン権限)でよくある落とし穴
ストレージは「管理プレーン(ARM)」と「データプレーン(Blob/Filesなどの中身)」が別物です。サービス プリンシパル(FIC含む)にRBACを付けたつもりでも、ARM操作のロールしか付いていない、またはスコープがズレていると、データアクセスは403になります。
用途ごとの代表的なロールを整理すると次の通りです(最小権限の設計は別途必要ですが、切り分けではまず要件を満たすロールを付与して動作確認するのが確実です)。
| やりたいこと | 代表的なロール | 備考 |
|---|---|---|
| Blobの読み取り | Storage Blob Data Reader | 一覧取得・ダウンロード中心 |
| Blobの読み書き | Storage Blob Data Contributor | アップロード/削除/上書きを含む |
| ACL変更や所有者/権限管理(ADLS Gen2) | Storage Blob Data Owner | HNS/ACL操作が絡む場合に必要になりやすい |
スコープの注意:「リソースグループに付けたからOK」と思いがちですが、運用の癖や構成によっては、対象が想定とズレて403になることがあります。切り分け段階では、まずストレージアカウントスコープでロールを付け、動作を安定させてから、必要に応じてコンテナ/ファイルシステム単位へ絞るのが安全です。
反映遅延:RBACは付与直後に反映しないことがあります。テストのたびにロールを付け替えると混乱しやすいので、ロール変更後はログの時刻を揃えて検証し、失敗が古いトークンによるものではないかも疑ってください。
サービスプリンシパル(FIC)の「どのID」に付与するか
Federated Identity Credential(FIC)を使う場合、アクセス時の主体は「アプリ登録(Application)」ではなくサービスプリンシパル(Enterprise application)側のオブジェクトとして評価されます。ロールを付ける相手(オブジェクトID)がズレると、付与したのに403になります。
- ロール付与対象が、実際にトークンを出している主体(SP)と一致しているか
- 同名アプリが複数ある、テナントを跨いでいる、作り直しで古いSPに付与している…などを疑う
認証方式の確認:本当にAzure AD(OAuth2)で叩いているか
「RBAC付与済みなのに403」の裏で、実はツールがSAS/アカウントキーでアクセスしている、または逆にAzure ADでアクセスしているつもりが別方式になっている、ということがあります。
特にCLI系はオプション次第で認証方式が変わります。代表例として、Azure CLIのストレージ操作は –auth-mode login を付けないと別方式になりやすいものがあります(環境変数・既定値・接続文字列などの影響を受けます)。
# 例:Azure ADでの認証を明示(環境やコマンドにより異なる)
az storage blob list \
--account-name <account> \
--container-name <container> \
--auth-mode login
SDKの場合は、DefaultAzureCredential を使っているか、Managed Identityを使うならその割り当てが正しいか、Workload Identity/FICなら環境変数やフェデレーション設定が正しいかを確認します。ここでの狙いは「正しい主体で、正しいリソース(Storage向け)のトークンを取得し、そのトークンで呼んでいる」ことです。
ADLS Gen2(階層型名前空間)なら「RBAC+ACL」の二段階で403が起きる
ストレージアカウントで 階層型名前空間(HNS)を有効にしている(= ADLS Gen2として使っている)場合、403の原因はRBACだけではありません。POSIXライクなACL(ディレクトリ/ファイルの権限)でも弾かれます。
このときの典型パターンは次の通りです。
- RBACは付いているのに、特定のコンテナ(ファイルシステム)配下だけ403になる
- 一覧は取れるが、ディレクトリ作成や書き込みだけ403になる
- 別のユーザー/別のSPは成功する(= ACLが主体ごとに違う)
ADLS Gen2でハマりやすい要点を表にまとめます。
| 操作 | RBACの例 | ACLの例 | よくある落とし穴 |
|---|---|---|---|
| ファイルの読み取り | Blob Data Reader 以上 | 対象パスに r(read) | 親ディレクトリに x(execute)がなく辿れない |
| ファイルの書き込み/作成 | Blob Data Contributor 以上 | 対象ディレクトリに w(write)+x | デフォルトACLが未設定で新規作成が失敗 |
| ACLの変更 | Blob Data Owner が必要になることが多い | ACLを変更できる権限 | 権限設計がRBACだけで完結すると誤認 |
「blobのPEはあるのにdfsで失敗」パターン
HNS有効のストレージを、Spark/Hadoop系(abfs://)、Data Lake SDK、あるいは一部のツールで扱うと、裏で dfs.core.windows.net を叩きます。ここで、PEやDNSが blob しか用意されていないと、Public network access Disabled の条件下では破綻し、403に見える失敗になります。
対処は明快で、要件に応じて以下を揃えます。
- dfs サブリソースのPrivate Endpointを作る
- privatelink.dfs.core.windows.net のPrivate DNS Zoneを用意し、VNetへリンク
- アクセス元で <account>.dfs.core.windows.net がPEのプライベートIPを引くことを確認
リージョン違い(PEとストレージが別リージョン)をどう捉えるべきか
Private Endpoint は「VNet内に作るNIC」であり、接続先リソース(ストレージ)が別リージョンでも成立するケースはあります。ただし、実務上は切り分け難易度が上がりやすいため、403の調査段階ではまず同一リージョンに寄せて正常化させるのが安全です。
同一リージョン/別リージョンの比較観点を整理します。
| 観点 | 同一リージョン | 別リージョン |
|---|---|---|
| トラブルシュート | 構成が単純で原因追跡がしやすい | DNS/ルーティング/組織ポリシーの影響が増え、ハマりどころが増える |
| レイテンシ | 最小になりやすい | 遅延が増える可能性(業務要件で影響が出る場合あり) |
| 運用設計 | 監視・障害時対応が分かりやすい | ネットワーク/組織境界を跨ぐと運用責任が分散しやすい |
結論としては、次の順番が堅実です。
- まず同一リージョンで PE+DNS+権限 を揃えて100%成功する状態を作る
- その後、要件上どうしても別リージョンが必要なら、レイテンシ・コスト・運用を評価して再設計する
診断の順番テンプレート(403を最短で潰す)
実際の現場では、検証が散らかるほど調査時間が伸びます。次の順番で潰すと、403の大半は短時間で切れます。
- 実行環境を固定する
- まずVNet内の検証VMなど「ネットワークが見える環境」でテストする
- CI/CDやPaaSは、切り分け後に戻す
- DNS(blob/dfs等)を確認する
- nslookup の結果がPEのプライベートIPであること
- HNSなら dfs も同様に確認する
- PE接続の状態(Approved)とサブリソースを確認する
- 必要なサブリソース(blob/dfs/file…)が揃っているか
- NSG/UDR/NVAでPE宛て443が落ちていないか
- 特にUDRで強制トンネルしている環境は最優先で疑う
- 認証方式がAzure ADになっているか
- ツールの既定認証がSAS/キーに寄っていないか
- FICの場合は対象SPが正しいか
- RBAC(データプレーン)をストレージアカウントスコープで確認する
- 必要なら一時的にBlob Data Contributor/Ownerで検証する
- 付与後は反映遅延を考慮して再ログイン/再取得する
- ADLS Gen2ならACLを確認する
- 書き込み系の403はACL不足の可能性が高い
ログで裏取りする:403の“種類”を確定させる
推測で直すと遠回りになります。403の根拠を取るなら、次の観測が有効です。
- クライアント側のエラー詳細(例:SDK例外のエラーコード、レスポンスヘッダーの x-ms-error-code、AzCopyログ)
- ストレージの診断ログ(Azure Monitor / Diagnostic settings)
- 読み取り/書き込み/削除系のログを有効化し、対象時間帯の StatusCode、認証種別、要求元IP などを見る
- 要求元IPがプライベートIPになっていればPE経由の可能性が高く、そうでなければDNS/経路を疑う
- ネットワーク側ログ(NSGフローログ、NVAログ、Firewallログなど)
- PEのIP宛て443が許可/拒否どちらになっているかを確認
ログで「要求元IPが想定外」「認証種別が想定外」が見えた瞬間に、原因がほぼ確定します。
原因別の対処まとめ(チェックリスト)
最後に、典型パターンを“症状→原因→対処”でまとめます。運用メモとしてそのまま使える形にしています。
| 症状 | 原因候補 | 対処 |
|---|---|---|
| Public access有効だと成功、Disabledにすると403 | DNSがパブリックIPを解決している | Private DNS Zone(privatelink.*)の作成、VNetリンク、クライアントのDNS参照を修正。nslookupでPEのプライベートIPを引くまで固定 |
| PEを作ったのに改善しない | PE接続が未承認 / サブリソース不足(dfs等) | Private endpoint connectionをApprovedに。必要なサブリソース(blob/dfs/file…)のPEを追加 |
| 同じVNet内でも環境によって成功/失敗が分かれる | NSG/UDR/NVA/プロキシでPE宛てが阻害 | PEのIP宛てTCP/443を許可。強制トンネル時は例外ルートやNVA設定を見直し |
| 一覧は取れるが書き込みだけ403(HNS有効) | ACL不足(RBACだけでは不足) | 対象ディレクトリ/ファイルのACL(w/x等)とデフォルトACLを確認。必要ならBlob Data Ownerも検討 |
| RBACを付けたのに403が変わらない | 付与対象(SPのオブジェクト)やスコープがズレている/反映遅延 | 付与先が実際の主体か確認。ストレージアカウントスコープで付与し、トークン再取得後に再検証 |
| リージョンが跨っていて不安 | 構成が複雑化し、DNSや経路のズレを招きやすい | 切り分け目的で同一リージョンにPEを作り正常化→要件があれば別リージョン構成へ段階的に戻す |
まとめ:Public network access Disabled は「経路が1本」になる
Public network access を Disabled にすること自体は、データ保護として非常に強力です。その一方で、許可される経路が“Private Endpoint経由だけ”に収束するため、DNS・PE状態・サブネット要件・NSG/UDR・RBAC/ACLのどれかが少しでも外れると403として顕在化します。
対処のコツは、DNS(プライベートIP解決)→PE状態(承認/サブリソース)→ネットワーク到達性→認証方式→RBAC→(ADLSなら)ACL、の順に上から固めることです。ここまで揃えると、ネットワーク制限を緩めなくても、PE経由で安定してアクセスできる状態を作れます。

コメント