しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

独自のバックエンドとリンクした Azure Static Web Apps のプレビュー環境を自動で作成する

Azure Static Web Apps で何も考えずに GitHub で Pull Request を作成すると、標準で提供されているプレビュー環境が自動的に作成されるので便利です。Managed Functions も SWA とセットでデプロイされるので、フロントエンドだけではなくバックエンドも含めてプレビュー出来るのが特徴です。

簡単なアプリケーションであれば Managed Functions で十分なケースも多いはずですが、Web API の数が多い場合には Azure Functions ではなく App Service や API Management を API リンクで利用するケースがあります。SWA の Standard Plan が必要になりますが、App Service / API Management / Container Apps などをバックエンドに利用できるので SWA の利用範囲が広がります。

SWA へのバックエンドのリンク機能については以下のエントリでまとめていますので、使ったことがない方はこちらも参照してください。

しかし、バックエンドに独自の App Service などで実装した API をリンクすると、SWA のプレビュー環境がフロントエンド側でしか動作しなくなるため、実質使い物にならなくなります。ドキュメントにもバックエンドのリンクを利用すると Pull Request でのプレビュー環境が使えなくなると明記されています。

Backend integration is not supported on Static Web Apps pull request environments.

API support in Azure Static Web Apps with Azure App Service | Microsoft Learn

こればかりはバックエンドがどのようにホストされているかに依存するので、SWA 側ではどうしようもない部分です。SWA で独自のバックエンドを利用する場合の制約となるのですが、非常に便利な機能なのでやはり活用したいです。

ドキュメントには Pull Request のプレビュー環境はバックエンドのリンクを利用すると使えないと書いてありますが、実は名前付きプレビュー環境ではバックエンドのリンクが利用できるので、それを利用して構築を行なっていきます。

App Service や Azure Functions には Deployment Slot が用意されているので、SWA 側のプレビュー環境と App Service の Deployment Slot を一対一に紐づけると綺麗に解決できます。以前に App Service で Pull Request プレビュー環境を作成する方法を書いているので、こちらも参考にしてください。

組み込みでのサポートは用意されていないので、基本的に GitHub Actions と Azure CLI を利用して実現していきますが、以下の 3 ステップで済むため意外に簡単です。

  1. API デプロイ用の Web Apps の Deployment Slot を作成
  2. Static Web Apps のプレビュー環境を作成
  3. Static Web Apps の API リンクで Web Apps を関連付け

SWA 側で App Service をリンクする際には Deployment Slot 名を指定できるので、そういう意味では想定された使い方かもしれません。この方針で Azure CLI を使ってプレビュー環境を構築していきます。

Web Apps でプレビュー環境を作成

まずは Web Apps に対して新しい Deployment Slot を作成して、そこにプレビュー表示したいブランチのバックエンドをデプロイします。デプロイの処理自体は変わらないので省略します。

Azure CLI を使って Deployment Slot を作成するには az webapp deployment slot create を使います。

Deployment Slot を作成するときのオプションとして、コピー元の Slot の指定があるので常に Production Slot の設定をコピーして作るようにします。引数としては --configuration-source が該当しますが、Production Slot を指定する際には Web App 名を指定します。

az webapp deployment slot create -n ${{ env.BACKEND_APP_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }} -s ${{ env.PREVIEW_ENV_NAME }} --configuration-source ${{ env.BACKEND_APP_NAME }}

App Service 名などは他の部分でも必要となるので環境変数して追加しておくと楽です。このコマンドが成功すると新しい名前で Deployment Slot が作成されます。

ちなみにこのコマンドは既に同名の Deployment Slot が存在している場合でも実行でき、更にその時点でのコピー元の最新の設定を適用してくれるので、多少実行に時間はかかりますがアプリケーションデプロイの度に毎回呼び出したほうが安全です。

Static Web Apps でプレビュー環境を作成

次は Static Web Apps 側のプレビュー環境を作成します。SWA のプレビュー環境は Azure CLI でも作成可能ですが、アプリケーションデプロイの際になければ自動で作ってくれるのでそれを利用します。

ただし公式の SWA デプロイ Action を使うと Pull Request 上の場合に特殊処理が走ってしまうので、デプロイには SWA CLI を使うようにして対応します。SWA CLI を使ったデプロイ方法は以下のエントリで紹介しているので、こちらも参照してください。

SWA CLI を使ったデプロイの際にプレビュー環境名を指定すれば、名前付きプレビュー環境が作成されます。App Service のように Deployment Slot とデプロイを個別に行う必要がないためシンプルです。

swa deploy ./dist -n ${{ env.FRONTEND_APP_NAME}} --env ${{ env.PREVIEW_ENV_NAME}} --no-use-keychain

これで SWA 側に指定した名前でプレビュー環境が作成されます。この方法で作成したプレビュー環境には独自のバックエンドをリンク出来るので、後は Azure CLI を使ってリンクを行うだけとなります。

バックエンドのリンク

最後は作成した Web App と Static Web App をリンクするのですが、これも Azure CLI を使って行えるので GitHub Actions に載せることが容易です。ただし渡すパラメータに癖があるので注意が必要です。

Azure CLI で SWA へのバックエンドのリンクを行う機会は正直ほぼ無いと思いますし、そもそも SWA を Azure CLI で管理する機会もほぼ無いとは思うので az staticwebapp というコマンド自体があまり知られていない気がしますが、意外に多くのコマンドが用意されています。

バックエンドのリンク自体はコマンド 1 つで終わるのですが、コマンドのパラメータとしてリンク先のリソース ID とリージョン名が必要となるのが最大のネックです。リソース ID はともかく、リージョン名が必要になるのは謎です。

バックエンドのリソース ID とリージョン名を取得するために、今回は az webapp show コマンドを使うことにして、以下のような処理でバックエンドのリンクを実現しました。

read -d "" backendId location <<< $(az webapp show -n ${{ env.BACKEND_APP_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }} -s ${{ env.PREVIEW_ENV_NAME }} --query "[id, location]" -o tsv) || true
az staticwebapp backends link -n ${{ env.FRONTEND_APP_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }} --environment-name ${{ env.PREVIEW_ENV_NAME }} --backend-resource-id "$backendId" --backend-region "$location"

read -d を使った呪文のようなコマンドは、az webapp show が返した複数の値を変数に格納するために使っています。以下のドキュメントで紹介されていた処理を流用しているだけですが、今回のように複数の値が必要な場合に時間のかかる ARM REST API の呼び出しを最小限にできます。

これでプレビュー環境同士をリンク出来ました。Azure Portal で確認するとプレビュー環境にバックエンドがリンクされているのが確認出来ます。

必要なくなったプレビュー環境を削除

プレビュー環境の作成はここまでの処理で完成したので、最後に Pull Request が閉じられた際にプレビュー環境も削除する処理を追加する必要があります。削除は非常にシンプルで Web App と Static Web App のそれぞれで削除コマンドを実行するだけです。

厳密にはどちらのコマンドが失敗した場合のリカバリについても考慮する必要がありますが、削除が失敗することは可能性としては低いため以下のように単純なコマンドで実現します。

az webapp deployment slot delete -n ${{ env.BACKEND_APP_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }} -s ${{ env.PREVIEW_ENV_NAME }}
az staticwebapp environment delete -n ${{ env.FRONTEND_APP_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }} --environment-name ${{ env.PREVIEW_ENV_NAME }} --yes

このコマンドを Pull Request が閉じられた時に実行するようにすると、プレビュー環境と Pull Requst のライフサイクルを一致させることができます。

実際の動作例

ここまでのプレビュー環境の自動作成とデプロイを組み合わせたサンプルリポジトリを作成して、実際に動作確認を行いましたが Pull Request を作成すると、自動でバックエンド付きのプレビュー環境が作成され、素早く動作確認が行えるようになりました。

作成したサンプルでは GitHub の Environment も利用しているので、以下のように Pull Request からプレビュー用の URL に素早く飛べるようになっています。

実際にバックエンドに新しい API を追加して、プレビュー環境上で動作確認を行いましたが問題なく動作しました。実際のプロダクト開発にこの仕組みを実装して、しばらく運用していますが非常にスムーズにレビューが行えるのでかなり便利に使っています。

プレビュー環境で問題となりがちな認証については、今回は Entra ID のワイルドカードを利用してプレビュー環境に対しても本番と同等の Entra ID 認証を追加することで綺麗に解決することが出来ています。

Entra ID 認証がワイルドカードで利用出来ることが確認できたのが、今回の独自のバックエンドをリンクした SWA でプレビュー環境を構築するモチベーションになっています。

実際のプロダクト開発に組み込んだ話を含めて、どこかで喋る機会があれば共有したいなと思っています。