Azure Static Web AppsにてPrivate Endpointsを試す

はじめに

全然気がついていなかったのですが、Azure Static Web AppsにてPrivate EndpointsのサポートがGAしていました。

azure.microsoft.com

ものは試しということで早速やってみました。

設定

設定と言ってもそんなに難しいことはなく、以下のドキュメントに沿って作業をするだけです。

learn.microsoft.com

Azure Static Web AppsのプランをStanadrdホスティングプランに設定するところが注意点でしょうか。

これだけでインターネットからアクセスすると以下の画面のように403のエラー画面が表示されますが、

仮想ネットワーク上にデプロイしたVMからはアクセスできるようになります。

ちなみに、IPアドレスでアクセスしてみるとAzure Web Appsの404のエラー画面となりました。

Private Endpoints設定後に更新作業をしてみる

Private Endpointsを設定したあと、Webページを更新しGitHub Actionsでデプロイできるかどうか試してみました。

あっけなくGitHub Actionsが成功し...

更新後の内容も仮想ネットワーク上にデプロイしたVMから確認できました。

Azure Database for PostgreSQL Flexible ServerにてAzure AD認証を行う

はじめに

先日、Azure Database for MySQL Flexible ServerにてAzure AD認証を行うことを実施しました。

miyohide.hatenablog.com

そんな中、PostgreSQL Flexible ServerでもAzure AD認証がGAしたというアナウンスがあったので試してみました。

techcommunity.microsoft.com

設定

ドキュメントが以下にありますのでそれに沿って作業をします。

learn.microsoft.com

PowerShellの環境がないのでAzure CloudShellを使い、以下のコマンドを打ちます。

Connect-AzureAD

その後、読み取りアクセスを許可します。

New-AzureADServicePrincipal -AppId 5657e26c-cc92-45d9-bc47-9da6cfdb4ed9

その後はAzure Portalで設定します。今回はAzure AD上でグループを作成し、使用しているアカウントを所属させ、Azure Database for PostgreSQL Flexible Serverの管理者として設定しました。

この段階で作成したAzure Database for PostgreSQL Flexible Serverのユーザーには以下のユーザーが作成されています。myhogehogeadminはAzure Database for PostgreSQL Flexible Serverの作成時に指定した管理者、postgresadminはAzure AD上のグループです。

postgres=> \du
                                                                         List of roles
     Role name      |                         Attributes                         |                                  Member of                                  
--------------------+------------------------------------------------------------+-----------------------------------------------------------------------------
 azure_pg_admin     | Cannot login                                               | {pg_monitor}
 azuresu            | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 myhogehogeadmin | Create role, Create DB                                     | {pg_read_all_settings,pg_read_all_stats,pg_stat_scan_tables,azure_pg_admin}
 postgresadmin      | Create role, Create DB                                     | {azure_pg_admin}
 replication        | Replication                                                | {}

postgres=> 

これでpostgresadminでアクセスができます。Azure Cloud Shell上でexport PGPASSWORD=$(az account get-access-token --resource-type oss-rdbms --query "[accessToken]" -o tsv)を実行してアクセストークンを環境変数PGPASSWORDに設定したのち、psql "host=<作成したAzure Database for PostgreSQL Flexible Serverのホスト名> port=5432 dbname=postgres user=postgresadmin sslmode=require"で接続できました。

接続しているユーザーをAzure Database for PostgreSQL Flexible Serverにて確認すると、postgresadminが存在していることがわかります。

postgres=> select usename, query from pg_stat_activity where datname = 'postgres';
    usename    |                                                             query                                                             
---------------+-------------------------------------------------------------------------------------------------------------------------------
 azuresu       | update cron.job_run_details set status = 'failed', return_message = 'server restarted' where status in ('starting','running')
 azuresu       | SELECT pg_catalog.pg_is_in_recovery() as is_in_Recovery
 postgresadmin | select usename, query from pg_stat_activity where datname = 'postgres';
(3 rows)

postgres=> 

マネージドIDを使用して接続する

次に仮想マシンからアクセスをしてみます。ドキュメントは以下にあるのですが、2023年1月29日時点、色々と分かりにくいので苦労しました。

learn.microsoft.com

仮想マシン作成後、マネージドIDを有効化し、システム割り当てマネージドIDのアプリケーションIDを以下のコマンドで取得します。

az ad sp list --display-name <仮想マシン名> --query [*].appId --out tsv

その後、マネージドIDのPostgreSQLユーザーを作成します。Azure Database for PostgreSQL Flexible Serverに入り、以下のSQLを実行します。

postgres=> select * from pgaadauth_create_principal_with_oid('任意の名前', '上記で取得したアプリケーションID', 'service', false, false);
 pgaadauth_create_principal_with_oid 
-------------------------------------
 Created role for "任意の名前"
(1 row)

postgres=> 

発行するSQLは以下のドキュメントは以下を参考に。

learn.microsoft.com

無事作成できると、Azure Database for PostgreSQL Flexible Server上にユーザーが作られます。

postgres=> \du
                                                                         List of roles
     Role name      |                         Attributes                         |                                  Member of                                  
--------------------+------------------------------------------------------------+-----------------------------------------------------------------------------
 azure_pg_admin     | Cannot login                                               | {pg_monitor}
 azuresu            | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 任意の名前          |                                                            | {}
 myhogehogeadmin | Create role, Create DB                                     | {pg_read_all_settings,pg_read_all_stats,pg_stat_scan_tables,azure_pg_admin}
 postgresadmin      | Create role, Create DB                                     | {azure_pg_admin}
 replication        | Replication                                                | {}

postgres=> 

あとは作成した仮想マシンからアクセスしてみます。Azure Instance Metadata Serviceからアクセストークンを取得し、環境変数PGPASSWORDに格納します。

export PGPASSWORD=`curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fossrdbms-aad.database.windows.net' -H Metadata:true | jq -r .access_token`

その後はpsqlコマンドで接続します。

$ psql -h <作成したAzure Database for PostgreSQL Flexible Serverのホスト名> --user <任意の名前> postgres
psql (15.1 (Ubuntu 15.1-1.pgdg20.04+1), server 14.6)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

postgres=>

無事接続できました。

Azure Database for MySQL Flexible serverにてActive Directory認証を試す

はじめに

Azure Database for MySQL Flexible serverにてActive Directory認証がサポートされたので試してみます。なんとなくActive Directory認証については恐怖感があったのですが、実際にやってみると、一部を除きあっさりとできてしまったので実施した内容を記しておきます。

概念としては以下のものが公式ドキュメントとしてあります。

learn.microsoft.com

実際のやり方は以下のものが公式ドキュメントとして公開されています。

learn.microsoft.com

必要なもの

今回は、Azure ADグループとVirtual MachineのSystem Managed Identityによる認証を行います。

実際に設定する際には以下のものが必要でした。

  • Azure Database for MySQL Flexible server
  • マネージドID
  • Azure Active Directoryのグループ
  • Virtual Machine
  • Cloud Shell

具体的な設定

Active Directory管理者の設定と認証

具体的な設定については以下の公式ドキュメントに記載がありますので、それに従います。

learn.microsoft.com

Azure AD管理者の構成のところでマネージドIDに以下のアクセス許可が必要とあります。

  • User.Read.All
  • GroupMember.Read.All
  • Application.Read.ALL

このアクセス許可を付与する方法がわからなかったのですが、以下のブログにPowerShellを使ったやり方が掲載されていましたので、それをそのまま拝借します(というか、ほとんどこのブログに掲載されていることを真似れば以降の作業はうまくいきます)。

zenn.dev

PowerShellの環境をどうやって用意しようかなと思っていたのですが、Cloud Shellから設定できることを知り、そこから上記のやり方を実行することにしました。

learn.microsoft.com

うまくいくと、エンタープライズアプリケーションにてアクセス許可が設定できていることが確認できます。

あとは、Azure ADグループでログインすることを実行します。AzureにログインしているユーザーをAzure ADグループmysql-admin-hogehogeに所属しておき、以下のコマンドを実行します。

mysql -h "Azure Database for MySQL Flexible serverのホスト名" -u mysql-admin-hogehoge(Azure ADグループ) --enable-cleartext-plugin --password=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken)

成功すると、以下のように表示されます。

$ mysql -h "Azure Database for MySQL Flexible serverのホスト名" -u mysql-admin-hogehoge(Azure ADグループ) --enable-cleartext-plugin --password=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken)
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 22
Server version: 8.0.28 Source distribution

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

Virtual MachineのSystem Managed Identityによる認証

まずはVirtual MachineにてSystem Managed Identityを有効にします。やり方の公式ドキュメントは以下の通り。

learn.microsoft.com

これをAzure Database for MySQL Flexible serverにてユーザーとして設定する必要がありますが、公式ドキュメントには2023年1月22日時点で詳細な説明はなく、Azure Database for MySQL Single serverのドキュメントに記載がありました。

learn.microsoft.com

要はCREATE AADUSER '任意のユーザ名' IDENTIFIED BY 'CLIENT_ID';を実行すればよさそうです。システム要件に合わせて権限を付与してあげると良いでしょう。

dev.mysql.com

これでAzure Database for MySQL Flexible serverでユーザーとしての設定は完了です。

接続にはアクセストークンが必要となります。アクセストークンの取得にはAzure Instance Metadata Serviceから取得します。

learn.microsoft.com

以下のコマンドで取得することができました。

$ accessToken=$(curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fossrdbms-aad.database.windows.net' -H Metadata:true | jq -r .access_token)

このアクセストークンを使って以下のコマンドを実行すると、無事接続することができました。

$ mysql -h "Azure Database for MySQL Flexible serverのホスト名" -u 上記で設定した任意のユーザ名 --enable-cleartext-plugin --password=$accessToken
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 8.0.28 Source distribution

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Apple Silicon(M2 MacBook Air)上でAzure Functionsのローカル開発を試す

昨年8月にM2 MacBook Air上でAzure Functionsのローカル開発を試してみてうまく動かないという状態をメモしました。

miyohide.hatenablog.com

時間も経っていることなので再度試してみました。

環境

環境は以下の通りです。まずはAzure Functions Core Tools。

Azure Functions Core Tools
Core Tools Version:       4.0.4915 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.14.0.19631

Javaのバージョンは以下の通り。

openjdk version "17.0.5" 2022-10-18 LTS
OpenJDK Runtime Environment Microsoft-6841604 (build 17.0.5+8-LTS)
OpenJDK 64-Bit Server VM Microsoft-6841604 (build 17.0.5+8-LTS, mixed mode)

サンプルアプリ

サンプルアプリはAzure Functionsのチュートリアルから。

learn.microsoft.com

環境設定

実際に動作させるためには、環境変数JAVA_HOMEを設定する必要があります。macOSにおいては以下の方法でJAVA_HOMEを設定することができます。

export JAVA_HOME=`/usr/libexec/java_home -v "17"`

HttpTrigger以外ではAzure Storageの設定が必要です。今回はAzuriteエミュレーターを使用します。

learn.microsoft.com

接続先をlocal.settings.jsonに以下の記述をします。AzureWebJobsStorageとしてUseDevelopmentStorage=trueを指定するのがポイントです。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "java"
  }
}

あとはgradle azureFunctionsRunを実行すれば起動します。

% gradle azureFunctionsRun

> Configure project :
default messager has already been registered

> Task :azureFunctionsPackage

Step 1 of 8: Searching for Azure Functions entry points
2 Azure Functions entry point(s) found.

Step 2 of 8: Generating Azure Functions configurations
Generation done.

Step 3 of 8: Validating generated configurations
Validation done.

Step 4 of 8: Saving host.json
Successfully saved to /Users/xxxxxx/work/azure_func_java/build/azure-functions/azure_func_java/host.json

Step 5 of 8: Saving local.settings.json
Successfully saved to /Users/xxxxxxe/work/azure_func_java/build/azure-functions/azure_func_java/local.settings.json

Step 6 of 8: Saving configurations to function.json
Starting processing function: TimerTriggerJava
Successfully saved to /Users/xxxxxx/work/azure_func_java/build/azure-functions/azure_func_java/TimerTriggerJava/function.json
Starting processing function: HttpTriggerJava1
Successfully saved to /Users/xxxxxx/work/azure_func_java/build/azure-functions/azure_func_java/HttpTriggerJava1/function.json

Step 7 of 8: Copying JARs to staging directory: /Users/xxxxxx/work/azure_func_java/build/azure-functions/azure_func_java
Copied successfully.

Step 8 of 8: Installing function extensions if needed
Extension bundle specified, skip install extension
Successfully built Azure Functions.

> Task :azureFunctionsRun
Azure Function App's staging directory found at: /Users/xxxxxx/work/azure_func_java/build/azure-functions/azure_func_java

Azure Functions Core Tools
Core Tools Version:       4.0.4915 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.14.0.19631

[2023-01-15T07:28:44.938Z] OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
[2023-01-15T07:28:44.960Z] Listening for transport dt_socket at address: 5005

Functions:

        HttpTriggerJava1: [GET,POST] http://localhost:7071/api/HttpTriggerJava1

        TimerTriggerJava: timerTrigger

For detailed output, run func with --verbose flag.
[2023-01-15T07:28:45.239Z] Worker process started and initialized.
[2023-01-15T07:28:49.937Z] Host lock lease acquired by instance ID '0000000000000000000000001869F4F6'.

Http triggerを実装し、アクセスすると、以下のようにログが出力され無事動くことが確認できました。

[2023-01-15T07:16:24.368Z] Executing 'Functions.HttpTriggerJava1' (Reason='This function was programmatically called via the host APIs.', Id=5c835b97-c978-4bd7-a051-35a2104d739f)
[2023-01-15T07:16:24.415Z] Java HTTP trigger processed a request.
[2023-01-15T07:16:24.415Z] Function "HttpTriggerJava1" (Id: 5c835b97-c978-4bd7-a051-35a2104d739f) invoked by Java Worker
[2023-01-15T07:16:24.442Z] Executed 'Functions.HttpTriggerJava1' (Succeeded, Id=5c835b97-c978-4bd7-a051-35a2104d739f, Duration=84ms)

Timer triggerを実装すると、以下のようにログが出力され無事動くことが確認できました。

[2023-01-15T07:30:00.042Z] Executing 'Functions.TimerTriggerJava' (Reason='Timer fired at 2023-01-15T16:30:00.0150420+09:00', Id=b0054268-981c-4559-a77a-76d9e25a15d5)
[2023-01-15T07:30:00.091Z] Function "TimerTriggerJava" (Id: b0054268-981c-4559-a77a-76d9e25a15d5) invoked by Java Worker
[2023-01-15T07:30:00.091Z] Java Timer trigger function executed at: 2023-01-15T16:30:00.085913
[2023-01-15T07:30:00.099Z] Executed 'Functions.TimerTriggerJava' (Succeeded, Id=b0054268-981c-4559-a77a-76d9e25a15d5, Duration=78ms)

これで動くことは確認できました。他のTriggerやBindingを使うと何か問題が出てくるかもしれませんが、とりあえず。

ちなみに、処理を終了させようとCtrl+Cで終了してもAzure Functions Core Toolsは起動したままになっていて、再度起動しようとしたときに以下のエラーメッセージで起動しませんでした。

> Task :azureFunctionsRun FAILED
Azure Function App's staging directory found at: /Users/xxxxxx/work/azure_func_java/build/azure-functions/azure_func_java
Port 7071 is unavailable. Close the process using that port, or specify another port using --port [-p].

FAILURE: Build failed with an exception.

このときは、既存のプロセスをlsof -i:7071で見つけ出して終了させるとOKです。

$ lsof -i:7071
COMMAND   PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
func    94723 xxxxx  353u  IPv4 0xXXXXXXXXXXX      0t0  TCP *:7071 (LISTEN)
$ kill -9 94723

Microsoft Remote Desktopにて接続時にフルスクリーン化するのを解除する

今日は小ネタ。

MacからAzure上に作ったWindowsVMに接続するとき、Microsoft Remote Desktopを使っています。

Microsoft Remote Desktop

Microsoft Remote Desktop

apps.apple.com

これの接続時に全画面になってしまうことに微妙に不満を持っていました。

設定方法を見つけたので、そのメモを。今回使用したバージョンは、10.8.0です。

まず、Microsoft Remote Desktopを起動します。

その後、ダウンロードしたRDPファイルをMicrosoft Remote DesktopにDrag&Dropします。

Drag&Dropして現れたものにカーソルを合わせると鉛筆マークが出てくるので、そこで細かい設定ができるようになります。

目的のフルスクリーン解除については、「Display」タブの「Start session in full screen」のチェックを外せばOKでした。

Azure Static Web Apps CLIを使ってローカル開発環境を構築する(3)

先日より、Azure Static Web Appsの検証をしています。今回はその続きです。

認証プロバイダーをブロックする

先日実施した認証機能の実装の際、下記にあった認証プロバイダーをブロックするという項目に目が止まり実装することにしました。

learn.microsoft.com

これを実装するには、設定ファイルとしてstaticwebapp.config.jsonファイルをReactアプリを格納しているフォルダ直下に置く必要があります。

learn.microsoft.com

記述内容として、最初は以下の記述を実装しました。

{
  "route": "/.auth/login/twitter",
  "statusCode": 404
}

ただ、これではうまく動きません。理由を探っていたところswa startで起動した時に以下のメッセージが出ていることに気がつきます。

[swa] WARNING: Failed to read staticwebapp.config.json configuration from:
[swa]    /Users/miyohide/work/azure_static_web_apps_local/src/staticwebapp.config.json
[swa]
[swa] The following error was encountered: The property "route" is not allowed (Line: 0)
[swa] 0: {
[swa] 1:     "route": "/.auth/login/twitter",
[swa] 2:     "statusCode": 404
[swa] 3: }
[swa]
[swa] Please fix the above error and try again to load and use the configuration.
[swa] Read more: https://aka.ms/swa/config-schema

サンプル通りに記入したのになんで?と思っていたのですが、staticwebapp.config.jsonのドキュメントに書かれているサンプルをみてわかりました。

learn.microsoft.com

実際には、routesにて囲む必要がありました。具体的には以下のような記述をする必要がありました。

{
    "routes": [
        {
            "route": "/.auth/login/twitter",
            "statusCode": 404
        }
    ]
}

これを設定しておくと、/.auth/login/twitterにアクセスすると404が帰ってきました。

Azure Static Web Apps CLIを使ってローカル開発環境を構築する(2)

先日、Azure Static Web Apps CLIを使ってローカル開発環境を構築することを実施してみました。

miyohide.hatenablog.com

今回はその続きです。

認証

認証機能を実装します。以下のドキュメントに仕様が記されています。

learn.microsoft.com

これもローカル環境でもエミュレーション可能です。

learn.microsoft.com

単純に/.auth/login/ + 承認プロバイダーへのリンクをおけば良いだけです。こんな感じの実装をしました。

const NavBar = ({user}) => {
  return (
    <>
      { !user ?
        <span><a href="/.auth/login/github">GitHub Login</a></span> :
        <div><p><span>{user.userDetails}</span><span> <a href="/.auth/logout">Logout</a></span></p></div>
      }
    </>
  );
};

ログインしたユーザーの情報にアクセスする方法については以下のドキュメントに記されています。

learn.microsoft.com

こんな感じの実装をしました。

function App() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    getUserInfo();
  }, []);

  async function getUserInfo() {
    try {
      const response = await fetch('/.auth/me');
      const payload = await response.json();
      const {clientPrincipal} = payload;

      if (clientPrincipal) {
        setUser(clientPrincipal);
        console.log(`clientPrincipal = ${JSON.stringify(clientPrincipal)}`);
      }
    } catch (error) {
      console.error('No profile could be found ' + error?.message?.toString());
    }
  }

  return (
    <div className="App">
      <header className="App-header">
        <NavBar user={user} />
      </header>
      <main>
        <h1>これはAzure Static Web Appsのサンプルです。</h1>
      </main>
    </div>
  );
}

API関数からもログインしたユーザーの情報にアクセスできます。

module.exports = async function (context, req) {
    // ログインしたユーザー情報を取得する
    const header = req.headers['x-ms-client-principal'];
    let userinfo = undefined;
    if (header !== undefined) {
        const encoded = Buffer.from(header, 'base64');
        // JSON形式の文字列として取得する
        userinfo = encoded.toString('ascii');
    }

    context.res = {
        body: { text: `This is a api return value. user info = [${userinfo}]` },
    };
};

デプロイする

GitHubソースコードを置いているのであれば、Azure Static Web Appsにデプロイすることも容易にできます。ドキュメントは以下の通り。

learn.microsoft.com

Azure Portal上で作成する際、フレームワーク関連の設定をします。

これでAzure Static Web Appsを作成すると、以下のようなGitHub Actionsワークフローが生成されます。

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_MANGO_BEACH_0D47CE900 }}
          repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
          action: "upload"
          ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
          # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
          app_location: "/src" # App source code path
          api_location: "/api" # Api source code path - optional
          output_location: "build" # Built app content directory - optional
          ###### End of Repository/Build Configurations ######

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_MANGO_BEACH_0D47CE900 }}
          action: "close"

私の環境では、初回はデプロイがUpload Timed Out. Unsure if deployment was successful or not.と出て失敗しましたが、再実行すると成功しました。

GitHubと連携することで、Pull Requestとプレビュー環境とを紐づけることができます。

learn.microsoft.com

Pull Requestを作成すると、Azure Static Web Appsの環境ブレードにて「デプロイのプレビュー」が生成されます。

別のURLでPull Requestで作成したアプリが展開されるので、そこで動作を確認できます。

その他

ついでにAzure Web Appsにて気になっていた点を試してみました。APIのドキュメントにて各静的 Web アプリ環境には、一度に 1 種類のバックエンド API のみ構成できます。と記されています。

learn.microsoft.com

Azure Functionsの関数が一つしか持てないのかなと思ったりしましたが、実際複数の関数を持たせることが可能です。

Azure Static Web Appsにデプロイしても、関数が複数あることが確認できました。