
Azure StorageにPrivate Endpointを作っても公開アクセスは止まらない?VNetの内と外から読んで確かめた
どうも!オペ部の西村祐二です!
「Blobをアプリからだけ使いたいので、Private Endpointを作れば公開アクセスも止まるはず」。Azure Storageの接続を設計するとき、この理解だけでは公開経路や運用時の接続元を見落とすことがあります。
Azure StorageのPrivate Endpointは、仮想ネットワーク(VNet)にプライベートな接続口を用意する仕組みです。実機で確かめたところ、Private Endpointを作った後も、手元のPCから公開経路でBlobを読めました。外からの読み取りが拒否されたのは、公開ネットワークアクセスを無効にした後です。
ここでいう「公開アクセス」は、公開エンドポイントへのネットワーク経路のことです。認証なしでBlobを読める匿名アクセスとは別の設定で、今回は匿名アクセスを無効にしたまま、署名付きのURLで読んでいます。
この記事では、VNet内のVMと手元のPCの両方からBlobを読み、経路・名前解決・データの認可のそれぞれで、読み取りが通るか拒否されるかを確かめました。その結果をもとに接続設計を整理します。2026年11月10日に、東日本リージョンでAzure CLI 2.91.0を使って試した結果です。
VNetの内と外から読んだ結果
| 状態 | 手元のPC(公開経路) | VNet内のVM(Private Endpoint経由) |
|---|---|---|
| Private Endpointを作った直後(公開ネットワークアクセスは有効のまま) | 200(読める) | 200(読める) |
| 公開ネットワークアクセスを無効にした後 | 403(AuthorizationFailure) |
200(読める) |
| 無効にしたまま、VMのマネージドIDで、データのロールなしに読む | - | 403(AuthorizationPermissionMismatch) |
1行目と2行目は、アカウントキーで署名した読み取り専用のSAS付きURLで読んだ結果です。3行目だけ、VMのマネージドIDで読んでいます。
図は自作した概念図で、公式の構成図ではありません。Blobへのプライベート経路と公開経路を分け、名前解決と認可が、それぞれ接続のどの段階で関わるかを示しています。信頼されたサービスなどの例外の経路は省略しています。

Private EndpointとプライベートDNSゾーンを用意した構成
- ストレージアカウント: 東日本リージョン、Standard_LRS、StorageV2。作成時の公開ネットワークアクセスは有効(すべてのネットワークを許可)。Blobの匿名アクセスは無効
- コンテナー
dataに、テキストファイルhello.txtを1つ置く - VNet(
10.0.0.0/16)にPrivate Endpoint用のサブネット10.0.1.0/24を追加し、Blob(グループIDblob)用のPrivate Endpointを作成。割り当てられたIPアドレスは10.0.1.4 - プライベートDNSゾーン
privatelink.blob.core.windows.netを作り、VNetにリンクして、Private EndpointのDNSゾーングループに登録 - VNet内のクライアントは、同じVNetにあるUbuntu 24.04のVM。コマンドは
az vm run-command invokeで実行 - 手元のPCはmacOS
az network private-endpoint create --resource-group <resource-group> --name pe-blob --location japaneast \
--subnet <サブネットのリソースID> --private-connection-resource-id <ストレージアカウントのリソースID> \
--group-id blob --connection-name pe-blob-conn
az network private-dns zone create --resource-group <resource-group> --name privatelink.blob.core.windows.net
az network private-dns link vnet create --resource-group <resource-group> --zone-name privatelink.blob.core.windows.net \
--name link-vnet --virtual-network <VNetのリソースID> --registration-enabled false
az network private-endpoint dns-zone-group create --resource-group <resource-group> --endpoint-name pe-blob \
--name default --private-dns-zone privatelink.blob.core.windows.net --zone-name blob
読み取りには、手元のPCとVMで同じSAS付きURLを使い、HTTPステータスを確かめました。
# SASの発行(アカウントキーは環境変数 AZURE_STORAGE_KEY で渡す)
SAS=$(az storage blob generate-sas --account-name <account> --container-name data --name hello.txt \
--permissions r --expiry <UTCの日時> -o tsv)
# 手元のPCとVMの両方で実行(VMでは az vm run-command invoke --command-id RunShellScript で実行)
curl -s -o /dev/null -w '%{http_code}\n' "https://<account>.blob.core.windows.net/data/hello.txt?$SAS"
Private Endpointを作っても、公開経路から読めた
Private Endpointを作った後、ストレージアカウントの設定を確かめました。
az storage account show --resource-group <resource-group> --name <account> \
--query '{publicNetworkAccess:publicNetworkAccess, defaultAction:networkRuleSet.defaultAction}' -o tsv
Enabled Allow
公開ネットワークアクセスは Enabled、ネットワークルールのデフォルトの動作(defaultAction)は Allow のままです。この状態で手元のPCからSASでBlobを読むと、200で中身を取得できました。
公開ネットワークアクセスは、Private Endpointとは別の設定です。公開エンドポイントをどのネットワークから利用できるかを決めます。主な選択肢は次の3つです(ほかに、ネットワークセキュリティ境界で保護する選択肢もあります)。
| 公開ネットワークアクセスの設定 | 公開経路の扱い |
|---|---|
| 有効にし、すべてのネットワークを許可 | ネットワークの条件では制限しない(今回の作成直後の状態) |
| 有効にし、選択したネットワークを許可 | 許可した接続元に公開経路を残す(今回は試していない) |
| 無効 | 通常の公開経路からの接続を遮断する。Private Endpoint経由の接続は影響を受けない(今回のVMは200) |
公開ネットワークアクセスを無効にすると、外からの読み取りが拒否された
次に、公開ネットワークアクセスを無効にしました。
az storage account update --resource-group <resource-group> --name <account> --public-network-access Disabled
更新の完了から1秒後の確認ではまだ200でしたが、12秒後の確認で403になりました(時刻はJST、403の行は応答のXMLからCodeとMessageを抜粋)。
01:17:07 公開ネットワークアクセスの無効化が完了
01:17:08 手元のPCからGET: 200
01:17:19 手元のPCからGET: 403 AuthorizationFailure
This request is not authorized to perform this operation.
1回だけ、約10秒おきに確かめた結果なので、反映までの時間は環境によって変わる可能性があります。無効化の直後は、しばらく公開経路から読めるものとして考えておくのが良さそうです。
同じ状態で、VNet内のVMからは200で読めました。ポータルの「ネットワーク」画面では、公開ネットワークアクセスは「Disabled」、Private Endpointの接続は「Approved」と表示されています(赤枠は追記)。


なお、公開ネットワークアクセスを無効にした後も、手元のPCから az storage account show で設定を取得できました。Storageのファイアウォールの制限事項では、ネットワークの制限はデータ操作だけが対象で、コントロールプレーン(Azure Resource Manager経由の管理操作)は対象外と説明されています。CLIやポータルで設定を見られることと、Blobの中身を読めることは別の話です。
名前はそのまま、DNSで経路が分かれた
Private Endpoint経由でも、アプリは通常の <account>.blob.core.windows.net という名前を使います。手元のPCとVNet内のVMで、同じ名前を引いた結果は次のとおりです。
# 手元のPC(dig)
<account>.blob.core.windows.net. CNAME <account>.privatelink.blob.core.windows.net.
<account>.privatelink.blob.core.windows.net. CNAME blob.tyo20prdstr12a.store.core.windows.net.
blob.tyo20prdstr12a.store.core.windows.net. A <パブリックIP>
# VNet内のVM(nslookup)
<account>.blob.core.windows.net canonical name = <account>.privatelink.blob.core.windows.net.
Name: <account>.privatelink.blob.core.windows.net
Address: 10.0.1.4
どちらも途中で privatelink を含む名前を経由しますが、VNet内ではプライベートDNSゾーンのレコードによって 10.0.1.4 に、手元のPCではパブリックIPに解決されました。privatelink という文字の有無だけでは、どちらの経路を使っているかを判断できません。
- アプリの接続先URLに
privatelinkという文字がないことは、公開経路を使っている証拠にはなりません。今回のVMも、通常の名前のままPrivate Endpoint経由で読めました - DNSの応答に
privatelinkを含む名前が見えても、プライベート経路を使っているとは限りません。手元のPCでもprivatelinkのCNAMEを経由して、パブリックIPに解決されました
Private EndpointのDNS構成では、プライベートDNSゾーンとVNetのリンクなどを使い、クライアントが通常の名前をPrivate EndpointのIPアドレスに解決できるようにする、と説明されています。同じページには、公開側の privatelink のCNAMEはインターネットのどこからでも解決できるように作られており、DNSの名前解決とアクセス制御は独立しているとも書かれています。独自DNSやオンプレミスのDNSを使う場合も、そのDNSで通常のBlobの名前がPrivate EndpointのIPアドレスに解決されるようにする必要があります。
Private Endpoint経由で届いても、Blobを読む権限は別に必要だった
最後に、VMにシステム割り当てのマネージドIDを付けました。Blobのデータを読むロールは割り当てないまま、公開ネットワークアクセスも無効にしたまま、Microsoft Entra IDの認証でBlobの読み取りを試しました。
# VM内で実行(IMDSからストレージ用のトークンを取得して読む)
TOKEN=$(curl -s -H Metadata:true 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://storage.azure.com/' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
curl -s -w '\n%{http_code}\n' -H "Authorization: Bearer $TOKEN" -H 'x-ms-version: 2023-11-03' \
'https://<account>.blob.core.windows.net/data/hello.txt'
トークンは取得できましたが、読み取りは403で拒否されました(応答のXMLからCodeとMessageを抜粋)。
AuthorizationPermissionMismatch
This request is not authorized to perform this operation using this permission.
メッセージは「この権限では操作を許可されていない」という内容で、公開経路で拒否されたときの AuthorizationFailure とは別のエラーコードでした。リクエストはPrivate Endpoint経由で届き、IDの権限の確認で拒否されたと考えられます。Private Endpointは経路を用意するだけで、データへのアクセス権は与えません。
前半のSASで読めたのは、アカウントキーで署名したSAS自体が読み取りの権限を持っていたためです。Entra IDで読む場合は、IDにBlobのデータを読むロールが必要とされています。ただし、ロールを付けたマネージドIDで読めることは今回試していません(検証に使ったサービスプリンシパルにロールを割り当てる権限がなかったため)。
接続元ごとに、経路・名前解決・認可を確かめる
Blobの接続設計では、接続元ごとに次の3つを分けて確かめます。
- 経路: その実行環境からPrivate Endpointに届くか。公開経路を使うなら、公開ネットワークアクセスの設定で許可されているか
- 名前解決: その実行環境のDNSが、通常のBlobの名前をPrivate EndpointのIPアドレスに解決するか
- 認可: そのID(またはSAS)に、対象のデータを読む・書く権限があるか
「VNet内のアプリだけがBlobを読む」という要件でも、ファイルを配置するCIや、障害時にBlobを調べる管理端末があると、必要な接続元は増えます。以下は、今回の結果と公式の説明をもとにした設計上の整理です。CIや管理端末の構成は試していません。
| 利用者・実行環境 | 公開ネットワークアクセスを無効にする場合に必要なもの |
|---|---|
| VNet内のアプリ | Private Endpointへの経路、プライベートDNSゾーンでの名前解決、アプリのIDのデータ権限 |
| Blobへ成果物を配置するCI | CIの実行環境からPrivate Endpointへの経路と名前解決、書き込み権限。用意できないなら、公開経路を一部残す判断が必要 |
| 管理者の手元のPC | データを読むなら、VPNなどでVNetへ届く経路と名前解決、データ権限。設定の参照は影響を受けない |
自分がこの構成を設計するなら、公開ネットワークアクセスを無効にする前に、Blobを読み書きする実行環境を列挙します。そのうえで、各環境にPrivate Endpointへの経路を用意するのか、一部の公開経路を残す必要があるのかを判断します。接続元をPrivate Endpointだけに絞るなら、以前に設定した信頼されたサービスやリソースインスタンスの例外が残っていないかも確認します。
まとめ
Private Endpointの作成と公開ネットワークアクセスの無効化は別の操作で、外からの読み取りを止めたのは後者でした。VNet内では通常の名前のままPrivate EndpointのIPアドレスに解決され、無効化の後もPrivate Endpoint経由で読めました。一方、経路が通っていても、データの権限がないIDは拒否されました。
注意点は3つです。無効化の1秒後には、まだ公開経路から読めました(12秒後には拒否)。以前に設定した例外が残る場合があります。管理操作は、無効化の影響を受けません。Blobの接続設計では、アプリだけでなくCIや管理端末も含めて、接続元ごとに経路・名前解決・認可を確かめる必要がありそうです。
参考リンク
- Use private endpoints for Azure Storage
- Set the default public network access rule
- Firewall rules and network access for Azure Blob, Data Lake, Queue, and Table Storage
- Guidelines and limitations for the Azure Storage firewall
- Azure Private Endpoint private DNS zone values
- Authorize access to blobs using Microsoft Entra ID
- az network private-endpoint



