完成品
動作イメージ
※質問に対する回答時間がやや長めです。
全体像
本環境で作成したリソースの構成は以下の通りです。
※ インターネット公開されているため、セキュリティ面の注意が必要です。
次回以降は以下の図のような、プライベートまたはアクセス可能なユーザを限定した環境を構築していこうかと思います。
Azureの専門ではないため、図は正確でない部分もあるかと思います。
あくまでイメージとして捉えていただければと思います。
Prerequisites
nvm install 16.19.0
nvm use 16.19.0
- Python3+
python -V
Python 3.10.9 # 本環境はv3.10.9
- Azureアカウント
- GitHubアカウント
- Visual Studio Code
- Azure Functions Core Tools
1. 雛形プロジェクトを作成
①フロントエンド (Azure Static Web Apps)
本環境ではVue.jsを使用してフロントエンドを構築します。
vue-cliをインストールします。
本環境では、vue/cli v5.0.8
を使用します。
# インストール
npm install -g @vue/cli
vue --version
雛形プロジェクトを作成します。
本環境ではvue2
を使用します。
vue create dev-app # dev-appは任意のプロジェクト名
# vue3 or vue2を対話型で聞かれる→vue2を選択
作成したプロジェクトの起動確認を行います。
cd dev-app && npm run serve
### 出力 ###
DONE Compiled successfully in 5932ms
App running at:
- Local: http://localhost:8080/
- Network: http://192.168.0.7:8080/
Note that the development build is not optimized.
To create a production build, run npm run build.
出力されたURLを開き、以下のような画面が開かれることを確認します。
URLはどちらでも構いません。
起動確認後buildしておきます。
npm run build
②バックエンド (Azure Functions)
環境構築
本環境では、Flaskを使用してバックエンドを構築します。
VSCodeの左側拡張機能
からAzure Toolsを検索し、ダウンロードします。
拡張機能を使い、VSCode上からAzureにログインします。
ファンクションキーF1
を押下し、Azure: Sign Inをクリックします。
Webページが開かれたら、お使いのAzureアカウントを選択してログインします。
Static Web Appsと連携するAzure Functionsを作ります。
再度ファンクションキーF1
を押下し、Azure Static Web Apps: Create HTTP Functionをクリックします。
言語選択画面にてPython
を選択し、バージョン選択画面にてv.3.10.9
を選択します。
※公式ドキュメントに記載がある通り、Python3.10.xはプレビュー版でのみ提供されています。
任意の関数名を入力し、Enterを押下します。
ここまで以下のようなフォルダ構成になるかと思います。
-
apiフォルダ
(Azure Functionsに該当します) -
dev-appフォルダ
(Azure Static Web Appsに該当します)
Flaskの環境を構築します。
ローカル開発用にpip install flask
を実行することに加え、
Azure Functionsにもflaskがインストールされるようにapi/requirements.txt
にflaskを追記します。
pip install flask
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues
azure-functions
flask # 追記
雛形となるAzure Functionsを作成
Flaskのソースコードを作成します。
api/flask_appフォルダ
を作成し、その配下にapp.py
を作成します。
from flask import Flask
app = Flask(__name__)
@app.route('/api/test', methods=['GET'])
def api_test():
return 'This is Test'
if __name__ == '__main__':
app.run()
Azure Fuctionsの設定変更
Azure FunctionsへのHTTPリクエストがFlask側に流れるように設定を変更します。
① Azure FunctionsへのリクエストをWSGI handler(flask)にリダイレクトする
-
api/HttpTrigger1/__init__.py
を以下のように編集します。
import azure.functions as func
from flask_app.main import app
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
"""
Each request is redirected to the WSGI handler.
"""
return func.WsgiMiddleware(app).handle(req, context)
②ルーティング規則を変更する
具体的には以下2つの変更を行います。
-
/api
のリクエストがAzure Functionsに流れるようにする。 -
/api/*
のリクエストがWSGI handler=Flaskに流れるようにする
編集するファイルは以下の2つです。
api/host.json
api/HttpTrigger1/function.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[3.*, 4.0.0)"
},
### 追記 ###
"extensions": {
"http": {
"routePrefix": "api"
}
}
############
}
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
],
"route": "{*route}" ← 追記
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
ローカル環境でAzure Functionsをテストします。
Azure Functions Core Toolsをインストールし、func start
を実行します。
cd api
func start
### 出力 ###
Functions:
HttpTrigger1: [GET,POST] http://localhost:7071/api/{*route}
For detailed output, run func with --verbose flag.
[2023-05-13T15:05:43.763Z] Worker process started and initialized.
[2023-05-13T15:05:47.523Z] Host lock lease acquired by instance ID '000000000000000000000000083FF14C'.
別ターミナルを開き、curlでGETリクエストを送ります。
curl http://localhost:7071/api/test
### 出力 ###
StatusCode : 200
StatusDescription : OK
Content : This is Test
RawContent : HTTP/1.1 200 OK
Content-Length: 12
リクエストが成功すればAzure Functionsの雛形は完成です。
2. CI/CD環境の構築
①GitHubへアップロード
ここまでの手順で作成したフォルダをGitHubへアップロードします。
私の環境では、api
フォルダとdev-app
がアップロード対象となります。
②Azure Static Web Appsの作成
AzureポータルからStatic Web Appsを作成します。
主な設定項目は以下の通りです。
- デプロイの詳細
- ソース=GitHub
- アカウント=GitHubアカウント名
- 組織:GitHubアカウント名
- リポジトリ:本環境のリポジトリ
- 分岐:main
- ビルドのプリセット:Vue.js
- アプリの場所:dev-app (vueのプロジェクト名)
- APIの場所:api (Azure Functionsフォルダ)
- 出力先:dist
確認及び作成→作成とクリックし、リソースを作成します。
デプロイ画面に遷移します。成功したらリソースに移動をクリックします。
CI/CDプロセスのステータスを確認します。
Static Web Appsのリソース画面に移動した後、画面右側のGitHubアクションの実行をクリックします。
GitHubの画面に遷移します。
デプロイエラーが発生していなければデプロイが完了しています。
Static Web Appsの画面に戻り、画面右側に表示されているURLをクリックします。
1-①で作成した画面と同じものが表示されていればCI/CD環境の構築は完了です。
3. ユーザ認証を実装する (未完了)
認証方法は多数ありますが、今回は簡易的な認証を実装します。
- Static Web Appsからアプリケーションへのアクセスを許可するユーザを招待
- 招待リンクをユーザに送信し、ユーザ側で招待を
※他認証方法は本記事では取り扱いません。参考情報をいくつか紹介します。
①ユーザ招待リンクの生成
画面左側タブ:ロール管理をクリックします。
認証プロバイダーを選択し、必要な情報を入力します。
本環境では、GitHubアカウントを認証に使用します。
生成された招待リンクをコピーし、ユーザに送信します。
②ユーザ側で招待リンクを開き、アカウント連携を行う
①で生成したリンクを開き、招待を承認します。
Static Web Appsとのアカウント連携を承認します。
③Static Web Appsに認証機能を追加する
②で招待したユーザのロールを使用し、認証機能を実装します。
先ほど招待したユーザのロールを確認します。
Static Web Apps → ロール管理を開くと、以下のように先ほど招待したユーザのロールを確認できます。
①で設定したロール名invited_usersにて、招待済みユーザを表すロールが付与されています。
a. フロントエンドに認証ページ機能を追加
ログイン機能は以下のように実装します。
# GitHubでログインする場合
<a href="/.auth/login/github">Login</a>
# GitHubでログインし、リダイレクト先を指定する場合
<a href="/.auth/login/github?post_login_redirect_uri=<Static Web AppsのURL>/<任意のパス>">Login</a>
本環境では、Vue.jsでフロントエンドを実装しているため、
ページ遷移の機能を追加し、以下ファイルを作成・編集します。
-
dev-app/src/components/UserLogin.vue
(作成) -
dev-app/src/components/ChatOpenAI.vue
(作成) -
dev-app/src/App.vue
(編集) -
dev-app/src/main.js
(編集)
ページ遷移の機能を追加します。
cd dev-app
# Vue2を使用している場合、vue-router3系をinstall
npm install vue-router@3.6.1
dev-app/src/components/UserLogin.vue
を作成し、以下のようなコードを配置します。
ログイン後、/homeにリダイレクトする設定になっています。
<template>
<div class="login-container">
<h1>Login</h1>
<a href="/.auth/login/github" class="btn btn-primary" >Login with GitHub</a>
</div>
</template>
<script>
export default {
name: "UserLogin",
data: ()=>({
user_data: "",
userRoles: []
}),
methods:{
LoginClicked: async function(){
try{
// ログイン情報の取得
const ret = await fetch('/.auth/me');
this.user_data = await ret.json();
this.userRoles = this.user_data.clientPrincipal.userRoles
if (this.userRoles.includes('invited_users')){
console.log("Success.")
}
else{
console.log("Not Authorized.")
}
}
catch{
this.user_data = ""
this.userRoles = []
console.log("Error.")
}
},
CheckLogIn: async function(){
try{
// ログイン情報の取得
const ret = await fetch('/.auth/me');
this.user_data = await ret.json();
this.userRoles = this.user_data.clientPrincipal.userRoles
// invited_usersロールを持っているか確認
if(this.userRoles.includes('invited_users')){
this.$router.push({path: "home"})
console.log("Login.")
}else{
console.log("Not LogIn.")
}
}
catch{
console.log("Not LogIn.")
}
}
},
mounted(){
this.CheckLogIn()
}
}
</script>
<style scoped>
.login-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.btn {
margin-top: 20px;
padding: 10px 20px;
text-decoration: none;
color: #fff;
background-color: #007bff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn:hover {
background-color: #0056b3;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
color: #b6b2b2;
}
</style>
dev-app/src/components/ChatOpenAI.vue
を作成し、以下のようなソースコードを配置します。
<template>
<div>
<h1> Login Successed. </h1>
</div>
</template>
dev-app/src/App.vue
を以下のように編集します。
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
body {
background-color: #383737; /* ダークモードの背景色 */
}
</style>
dev-app/src/main.js
を以下のように編集します。
ルーティング設定を行います。
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import UserLogin from './components/UserLogin.vue';
import ChatOpenAI from './components/ChatOpenAI.vue';
Vue.config.productionTip = false
// ルーティング設定
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes:[
{
path: '/',
component: UserLogin
},
{
path: '/home',
component: ChatOpenAI
}
]
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')
b. Static Web Appsの設定ファイルを作成
以下のファイルを作成し、アクセス前に認証を実施するリクエストパスを定義します。
また、
-
dev-app/staticwebapp.config.json
- リクエストパス「/」は全てのユーザを許可
- リクエストパス「/home」はinvited_usersロールを持つユーザのみアクセス許可
- リクエストパス「/api*」はinvited_usersロールを持つユーザのみアクセス許可
{
"$schema": "https://json.schemastore.org/staticwebapp.config.json",
"routes": [
{
"route": "/", "allowedRoles" : ["anonymous"]
},
{
"route": "/home", "allowedRoles" : ["invited_users"]
},
{
"route": "/api*", "allowedRoles" : ["invited_users"]
}
]
}
c. Githubに変更をアップロードし、挙動を確認
GitHubにアップロードし、Static Web Appsの画面からGitHubアクションの実行をクリックします。
CI/CDプロセスが問題なく完了していることを確認し、Static Web AppsのURLをクリックしてアプリケーションにアクセスします。
※既に一度ローカル環境で認証をしている場合、ログイン情報が保持されているため認証画面に遷移しないことがあります。その際は、プライベートモードなどでタブを開いてアクセスします。
Login with GitHubをクリックすると以下の画面に遷移します。
必要な情報を入力し、Sign inをクリックします。
サインイン後、以下のような画面に遷移すれば完了です。
4. Static Web AppsとAzure Functionsを連携
Azure FunctionsでAPIエンドポイントを作成し、Static Web Apps側からAPIを実行します。
実際の作業は以下のような流れです。
-
Vue.js (Static Web Apps)
- フォーム(入力欄&ボタン)を作成
- APIを叩くための関数を作成
- ボタンが押されたら関数を実行
-
Flask (Azure Functions)
- 特定のURIでPOSTリクエストを受け付ける
- Vue.jsで入力されたデータを取得
- 取得したデータを入力として実行可能な関数を作成
- 関数の実行結果をVue.jsに送信
①Static Web Apps側の開発
はじめに、HTTPリクエストを送信するためのモジュールをインストールしておきます。
cd dev-app
npm install axios
フォーム・APIを実行する関数を作成します。
以下のファイルを作成(または編集)します。
dev-app/src/components/ChatOpenAI.vue
<template>
<div>
<div class="chat-container">
<div
v-for="message in prev_messages"
:key="message.id"
:class="['chat-message', message.isResponse ? 'response' : 'user']"
>
<span class="chat-message-text">{{ message.text }}</span>
</div>
</div>
<div class="input-container">
<input
v-model="question"
type="text"
@keydown.enter="ExecOpenAIApi(question)"
placeholder="質問を入力してください"
class="chat-input"
/>
<button
@click="ExecOpenAIApi(question)"
class="chat-button"
:class="{ 'button-hover': isButtonHovered }"
@mouseover="isButtonHovered = true"
@mouseout="isButtonHovered = false"
>
送信</button>
</div>
</div>
</template>
<script>
const axios = require('axios').create();
export default{
name: "ExecOpenAI",
data:() => ({
post_data:{
question: ""
},
response: "",
// prev_messages:[],
prev_messages:[],
message:{
id: 0,
text: "",
isResponse: false
},
isButtonHovered: false
}),
methods: {
ExecOpenAIApi: async function (question){
// Azure OpenAIの呼び出し
this.post_data.question = question;
await axios.post("/api/question", this.post_data)
.then((res) =>{
// ユーザの入力データを会話履歴に追加
const UserMessage = {
id: Date.now(),
text: question,
isResponse: false
}
this.prev_messages.push(UserMessage);
return res.data["message"]
})
.then((answer) => {
// 回答を会話履歴に追加
const ResponseMessage = {
id: Date.now(),
text: answer,
isResponse: true
}
this.prev_messages.push(ResponseMessage);
})
.catch((error) => {
console.log(error)
})
}
}
}
</script>
<style scoped>
.chat-container {
max-height: 600px;
overflow-y: scroll;
border: 1px solid #6d6a6a;
background-color: #555252; /* ダークモードの背景色 */
padding: 10px;
}
.chat-message {
padding: 10px;
margin-bottom: 10px;
display: flex;
}
.response {
justify-content: flex-start;
}
.user {
justify-content: flex-end;
}
.chat-message-text {
word-wrap: break-word;
max-width: 80%;
padding: 8px;
border-radius: 10px;
background-color: #353333;
color: #dad5d5;
text-align: left;
font-family: "Yu Gothic", "ヒラギノ角ゴシック", "Hiragino Sans", "メイリオ", Meiryo, sans-serif;
font-size: 18px
}
.response .chat-message-text {
background-color: #2e2d2d;
}
.user .chat-message-text {
background-color: #2e2d2d;
}
.input-container {
display: flex;
margin-top: auto;
position: sticky;
bottom: 0;
left: 30px;
right: 0;
padding: 20px;
height: 60px;
width: 70%;
background-color: #1a1919; /* ダークモードの背景色 */
}
.chat-input {
flex: 1;
padding: 5px;
border: 1px solid #3b3b3b; /* 入力欄の枠線の色 */
color: #0e0d0d; /* 入力欄の文字色 */
}
.chat-button {
margin-left: 5px;
padding: 8px 15px;
font-size: 16px;
background-color: #4477c9; /* ボタンの背景色 */
color: #ffffff;
border: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.chat-button:hover {
background-color: #9fb2da; /* マウスが重なった時の背景色 */
}
</style>
②Azure Functions側の開発
作成・編集するファイルは以下の2つです。
api/flask_app/app.py
api/flask_app/modules/openai_client.py
Pythonパッケージのopenai
が必要となるため、先にインストールしておきます。
追加でインストールしたパッケージがAzure Functions側でもインストールされるようにrequirements.txtへの追記が必要です。
pip install openai
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues
azure-functions
flask
openai
a. フロントエンドからのリクエストを処理
ユーザ(フロントエンド)からのPOSTリクエストを受け取り、入力を抽出します。
Azure OpenAIを実行するモジュールを呼び出します。
最後に、ユーザ(フロントエンド)へAzure OpenAIの回答を送信します。
from flask import Flask, request, jsonify
# モジュールのインポート
from .modules.openai_client import OpenAIClient
app = Flask(__name__)
@app.route('/api/test', methods=['GET'])
def api_test():
return 'This is Test'
@app.route('/api/question', methods=['POST'])
def handle_message():
question = request.json['question']
# Azure OpenAIのAPIを実行
response = OpenAIClient().chat_openai(question)
response = {'message': response}
return jsonify(response)
if __name__ == '__main__':
app.run()
b. Azure OpenAIをAPI経由で実行する
import openai
import os
class OpenAIClient:
def __init__(self):
# Azure OpenAIの基本設定
openai.api_type = "azure"
openai.api_base = "https://openai-for-app.openai.azure.com/"
openai.api_version = os.getenv('API_VERSION')
# Azure OpenAIのAPIキーを設定
openai.api_key = os.getenv('OPENAI_API_KEY')
self.model_name = os.getenv('MODEL_NAME')
if (openai.api_key is None) or (openai.api_base is None):
raise ValueError("エンドポイントまたはOpenAI APIキーが設定されていません。")
def chat_openai(self, question):
try:
system_message = """
You are an AWS specialist.
Avoid using technical jargon and explain in easy-to-understand language.
You need to help users understand with analogies that even elementary school students can understand."
"""
response = openai.ChatCompletion.create(
engine=self.model_name,
messages = [
{"role": "system", "content": system_message},
{"role": "user" , "content": question}
],
temperature=0.7,
max_tokens=800,
top_p=0.5,
frequency_penalty=0,
presence_penalty=0,
stop=None
)
return response.choices[0].message.content
except Exception as e:
return "Error"
if __name__ == "__main__":
test = OpenAIClient()
res = test.chat_openai("これはテストです。")
print(res)
③APIの挙動をテストする
a. Azure OpenAIを作成する
Azure OpenAIにモデルをデプロイし、API経由で実行可能な状態にしておきます。
前回リソース作成部分をまとめています。詳細はそちらをご覧ください。
b. Static Web Appsの環境変数にAzure OpenAIの情報を登録
Static Web Appsの画面に移動し、Azure OpenAIのエンドポイントとAPIキーを環境変数として登録します。
環境変数を使う主なメリットを一部紹介します。
- クレデンシャル情報のハードコーディングを避けられます。
- モデルを変更したい場合など、ソースコードを編集せずに済みます。
環境変数として設定するものは以下の3つです。
環境変数として設定するものは以下の4つです。
※以降、モデルのエンドポイントも環境変数と扱えるように読み替えていただければと思います。
- モデルのエンドポイント (エンドポイントの環境変数化を失念しており、追記致しました。)
- モデルのデプロイ名
- モデルのバージョン
- APIキー
プレイグラウンドから、コードの表示をクリックすると全て取得できます。
設定していきます。
Static Web Appsの画面左側タブの構成をクリックします。
追加をクリックします。
(ステージング環境を構築している場合は、環境を選択することで設定を分離できます。)
必要な情報を入力し、OKをクリックします。
- 名前:環境変数の変数名。バックエンドで変数を読み込む際にこの名前を指定する
- 値: 環境変数の値。
モデルのデプロイ名・モデルのバージョン・APIキーをそれぞれ設定し、画面上部の保存をクリックします。
以上で環境変数の設定は完了です。
API経由で実行するモデルの参照先を変更する場合は、同様の手順でエンドポイントなどを変更します。
c. ローカル環境からAPI経由でAzure OpenAIを呼び出す
本環境で使用しているStatic Web Apps×Azure Functionsをローカル環境で動かしてデバッグするためには、azure/static-web-apps-cliが必要です。(参考)
npm install
でインストールし、swaコマンド
を実行することで、
Static Web Apps×Azure Functionsの環境がローカルで実行されます。
# インストール
cd dev-app
npm install -g @azure/static-web-apps-cli
# 依存関係を基に必要なモジュールをインストールし、buildする
npm install && npm run build
# ローカル環境で実行する
swa start dist --api-location ../api
### 出力 ###
Serving static content:
[swa] Azure-OpenAI-test\dev-app\dist
[swa]
[swa] Serving API:
[swa] Azure-OpenAI-test\api
[swa]
[swa] Azure Static Web Apps emulator started at http://localhost:4280. Press CTRL+C to exit.
[swa]
[swa]
[api] [2023-05-13T16:23:50.785Z] Host lock lease acquired by instance ID '000000000000000000000000083FF14C'.
プライベートブラウザでhttp://localhost:4280
を開きます。
Login with GitHubをクリックします。
上記の画面にて、UsernameとUser's rolesを編集します。
実際にログインを行っているわけではなく、Static Web Appsの認証フローをシミュレーションしたものとなっています。
そのため、3-①ユーザ招待リンクの生成で設定した値を入力し、本番環境をシミュレーションします。
- Username: <ユーザ名>
- User's roles: invited_users
上記2項目を入力します。
ログインをクリックすると、/homeにリダイレクトされます。
ユーザ名・ロール名が正しくない場合は、ログイン画面に戻ります。
このように、認証フローをシミュレーション可能です。
質問を入力し、送信ボタンまたはEnterを押下します。
以下のように、Azure OpenAIからの回答が得られます。
これでローカル環境でのデバッグは完了です。
5. GitHubに変更をアップロード
ここまでの変更をGitHubリポジトリに反映し、CI/CDプロセスを開始します。
問題なくCI/CDプロセスが完了したらStatic Web AppsのURLをクリックし、一通り動作検証を行います。
まとめ
様々なAzureサービスと連携させてみると活用方法の幅が広がりそうです。
普段はAWSに触れているので、これを機にAzureサービスについても色々と勉強しようかと思います。
また、プライベート環境の構築が完了していないため、引き続き本環境を触っていきます。
以前、ファイルをアップロードしてEmbeddingを作成し、
アップロードしたファイルの内容を基に回答を生成するという機能を作成したため、
そちらを本環境に組み込みたいと思います。
余談ですが、今回フロントエンドのデザイン部分は全てChatGPTに任せました。
例えば、本環境ではユーザの質問・Azure OpenAIの回答がチャット形式で表示されます。
これはChatGPTに以下のようにお願いしました。
チャット形式で会話履歴を表示したい。LINEのようなイメージです。
右側にユーザの質問を表示し、左側には質問への回答を表示してください。
これ以外にもプロンプトエンジニアリングで多少カスタマイズはしていますが、
話し言葉のようにお願いするだけで意図を汲み取りコードを提案してくれるのは非常に便利でした。
【追記予定】プライベートな環境を構築する (未完了)
※今後追記予定です。
アプリケーションへのパブリックアクセスを許可する場合、この手順は不要です。
また、Static Web AppsをStandardプランにする必要があります。
Static Web Appsにプライベートエンドポイントを構築することで、
アプリケーションにアクセスできる経路をプライベートな仮想ネットワーク(VNet)経由のみに限定します。
特定のVnetに所属するAzure仮想マシンや、閉域網からのアクセスに対してアプリケーションを公開し、
パブリックIPからのアクセスを拒否することが可能となります。
①仮想ネットワークの作成
以下リソースをAzureポータルから作成します。
- 仮想ネットワーク ×1
- サブネット ×2
- クライアント用に用意する仮想マシンを配置
- Static Web Apps用プライベートリンクのNICを配置
仮想ネットワークの作成画面を開き、必要な情報を入力します。
IPアドレスの設計を行うと同時に、サブネットを追加します。
他の設定はデフォルトのままとし、リソースを作成します。
②プライベートリンクの作成
Static Web Appsの画面に移り、画面左側のタブ:プライベートエンドポイントをクリックします。