ストレージ アカウントで公開した静的な Web サイトに App Service からプライベート リンク経由でアクセスする
はじめに
この記事では、下記を実現してみたいと思います。
ストレージ アカウントに格納した JSON を「静的 Web サイト ホスティング」で公開する。
ただし、パブリック インターネットからは参照できないようにする。Azure Functions で、上記で公開した JSON を参照して、内容をそのままレスポンスする HTTP トリガーを作成する。
図にすると、このような感じになります。
これを実現するとき、課題となるのは “JSON をパブリック インターネットから参照できないようにする” という制約です。
この設定をすることは簡単ですが、その状態で Azure Functions から JSON を参照するには、一工夫が必要になります。
この “一工夫” で、より安全にストレージ アカウントにアクセスすることができるようになります。
ぜひ最後までお付き合いください。
使用する機能について
ストレージ アカウントでは、下記の機能を使用します。
静的 Web サイト ホスティング
Web サーバーなどを用意しなくても、ストレージ アカウントに格納したファイルを簡単に公開することができる機能です。
プライベート エンドポイント接続
仮想ネットワーク (以下、VNet) 上のクライアントから VNet と Microsoft バックボーン ネットワークを経由することで、
パブリック インターネットを経由せずにアクセスすることができる機能です。
ストレージ アカウントのファイアウォール機能と組み合わせることで、パブリック インターネットからの接続を拒否し、
プライベート エンドポイント経由のアクセスのみできる状態にして、安全なアクセスを実現できます。
Azure Functions (App Service も含む) では、下記の機能を使用します。
VNet 統合
Azure Functions (App Service) から VNet 内のリソースにアクセスできるようになる機能です。
※ Azure Functions の場合、下記のいずれかの価格プランが必要です。
Premium プラン
App Service プラン (専用プラン)
※ App Service の場合、下記のいずれかの価格プランが必要です。
Standard プラン
Premium プラン
PremiumV2 プラン
Elastic Premium プラン
作業手順について
『JSON をストレージ アカウントに格納し、静的 Web サイト ホスティングして、プライベート エンドポイント経由で Azure Functinos からアクセスする』を実現する場合、本来なら下記のように作業を進めるのが良いと思います。
ストレージ アカウント、Azure Functions など必要なリソースをデプロイする。
ストレージ アカウントのファイアウォール機能を使用して、プライベート エンドポイント経由以外のアクセスを拒否する。
ストレージ アカウントの静的 Web サイト ホスティングを有効化する。
ストレージ アカウントに JSON を格納する。
ストレージ アカウントのプライベート エンドポイントをデプロイする。
??? の設定追加
Azure Functions の VNet 統合を有効化する。
Azure Functions で HTTP トリガーを作成する。
ですが、この記事では異なる手順で作業を進めます。
理由としては、各作業の合間で、その時点での設定状態における JSON のアクセス可否を試していきたいからです。
【この記事での作業手順】
ストレージ アカウント、Azure Functions など必要なリソースをデプロイする。
ストレージ アカウントの静的 Web サイト ホスティングを有効化する。
ストレージ アカウントに JSON を格納する。
Azure Functions の VNet 統合を有効化する。
Azure Functions で HTTP トリガーを作成する。
ストレージ アカウントのプライベート エンドポイントをデプロイする。
ストレージ アカウントのファイアウォール機能を使用して、プライベート エンドポイント経由以外のアクセスを拒否する。
??? の設定追加
ストレージ アカウント、Azure Functions など必要なリソースをデプロイする。
下記のリソースをデプロイします。
VNet
Azure Functions の VNet 統合のためのサブネット
ストレージ アカウントのプライベート エンドポイントのためのサブネット
ストレージ アカウント
Azure Functions
VNet
サブネット
※ Azure Functions の Premium プランの場合、VNet 統合のためのサブネットは 200 個以上の IP アドレスが確保できる必要があるようです。
なので、IP アドレス範囲は x.x.x.x/24 もしくは、これより大きい範囲を指定してください。
ストレージ アカウント
Azure Functions
※ Azure Functions の VNet 統合機能は、従量課金プランでは使用できません。
Premium プランや App Service プラン (専用プラン) を使用してください。
ストレージ アカウントの静的 Web サイト ホスティングを有効化する。
ストレージ アカウントの静的 Web サイト ホスティングを有効化します。
このとき、静的 Web サイトとして公開するファイルを格納するための $web というコンテナーが自動的に作成されます。
プライマリ エンドポイントの欄に表示されている URL が、ページにアクセスするときの URL になります。
今回は https://tstmmarticle002.z11.web.core.windows.net/ という URL ですが、ストレージ アカウント名の他に z11 という文字列が含まれています。
これは リージョン コード というもので、URL に含める必要はありますが、Azure の内部で使用されるだけで、他にそのコードを使用する必要はないとドキュメントに記載されています。
ストレージ アカウントに JSON を格納する。
自動的に作成された $web コンテナーに、下記の JSON をアップロードします。
sample.json
{
"message": "Hello, world!"
}
試しに、アップロードした JSON にブラウザからアクセスしてみます。
この時点では、ストレージ アカウントのファイアウォール設定はデフォルトの「すべてのネットワーク」になっています。
そのため、このようにパブリック インターネットから参照することができます。
Azure Functions の VNet 統合を有効化する。
Azure Functions の VNet 統合を有効化します。
この後、ストレージ アカウントのプライベート エンドポイントをデプロイしますが、
プライベート エンドポイントを経由した通信をするために Azure Functions のアプリケーション設定に下記 2 つの設定を追加します。
$$
\begin{array}{|l|l|} \hline
\textbf{名前} & \textbf{値} \\ \hline\hline
\text{WEBSITE\_DNS\_SERVER} & 168.63.129.16 \\ \hline \text{WEBSITE\_VNET\_ROUTE\_ALL} & 1 \\ \hline
\end{array}
$$
WEBSITE_DNS_SERVER
アプリケーションがプライベート DNS ゾーンを使用できるようにするために必要となっています。
Azure プラットフォームとデプロイしたリソース間の連携に使用されるようです。
WEBSITE_VNET_ROUTE_ALL
Azure Functions からのすべての送信トラフィックが、VNet を経由するようになります。
Azure Functions で HTTP トリガーを作成する。
公開した JSON の内容を取得し、そのままレスポンスする HTTP トリガーを作成します。 今回はお手軽に PowerShell で作成します。
関数のコード
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Retrieve the content of the published JSON.
$uri = "https://tstmmarticle002.z11.web.core.windows.net/sample.json"
$body = Invoke-RestMethod -Method Get -Uri $uri | ConvertTo-Json
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $body
})
テスト結果
このとおり、JSON の取得に成功しました。
ストレージ アカウントは、まだプライベート エンドポイントの設定をしていないので、Azure Functions からはパブリック IP でアクセスしているはずです。
それを確かめてみます。
Azure Functions のコンソール機能を使用して、下記のコマンドを実行してみます。
※ nameresolver は、DNS 機能をテストするために Azure Functions に用意されているツールです。
nameresolver tstmmarticle002.z11.web.core.windows.net
ストレージ アカウントのプライベート エンドポイントをデプロイする。
ストレージ アカウントのプライベート エンドポイントをデプロイします。
ストレージ アカウントのファイアウォール機能を使用して、
プライベート エンドポイント経由以外のアクセスを拒否する。
この時点では、公開した JSON はどこからでもアクセスできる状態になっています。
これを、プライベート エンドポイントを経由したときのみアクセスできるようにファイアウォールを設定します。
下図のように設定することで、プライベート エンドポイント経由以外のアクセスを拒否できます。
ファイアウォールが機能しているか確認するために、アップロードした JSON にブラウザからアクセスしてみます。
404 になったので、意図したとおりパブリック インターネットからのアクセスは拒否されているようです。
Azure Functions は、どうでしょうか。
VNet 統合しているので、プライベート エンドポイント経由でアクセスできるはずですが…
このようにエラーになってしまいます。
エラーメッセージに Response status code does not indicate success: 404 (The requested content does not exist.). とあることから、名前解決したときにプライベート エンドポイント (プライベート IP) が取得できていないようです。
先ほどと同じように、Azure Functions のコンソール機能を使用して、名前解決がどうなっているか確認してみます。
やはり、名前解決がうまくいっていないようです。
この問題を解決するのが、次の作業になります。
??? の設定追加
ストレージ アカウントのプライベート エンドポイントをデプロイしたとき、あるリソースが増えているはずです。
それが ”プライベート DNS ゾーン” です。
この時点で、プライベート DNS ゾーンは下図のようになっているかと思います。
リージョン コードが含まれた名前 (tstmmarticle002.z11) が登録されています。
さきほどの nameresolver コマンドでの名前解決では、リージョン コードを含まない名前で名前解決しようとしているように読み取れます。
なので、ここにリージョン コードを含まない名前を追加してみます。
※ IP アドレスは「リージョン コードあり / なし」のどちらも同じ IP アドレスを指定します。
プライベート DNS ゾーンの設定を追加した状態で、あらためて Azure Functions の HTTP トリガーをテストしてみます。
また、名前解決がどうなったかも確認してみます。
HTTP トリガーも動作するようになり、名前解決もプライベート IP が取得されるようになりました!
ストレージ アカウントのファイアウォールは変更していないので、パブリック インターネットから JSON にアクセスすることはできません。
まとめ
パブリック インターネットへの公開は防ぎつつ、Azure Functions からプライベート エンドポイント経由で JSON にアクセスすることができました。
今回実施した作業の中で、忘れがちな作業は、この 2 つでしょうか。
Azure Functions のアプリケーション構成で設定を追加する。
プライベート DNS ゾーンにレコード セットを追加する。
初めてこの構成を組んだ時、私はこの 2 点の設定が必要なことになかなか気づくことができず、非常に悩まされました…
この記事が、同じような悩みを抱えている方の一助になれば幸いです。
参考情報
ストレージ アカウント
Azure Functions
Azure Functions からプライベート エンドポイント経由でストレージ アカウントにアクセスする
この記事が気に入ったらサポートをしてみませんか?