Azure Static Web Appsで短時間でWEBアプリを公開する

## 前置き
2021年 5月にAzure Static Web Appsが正式にGA（一般公開）されました。
私は普段よく業務でVue.jsを使っているのですが、Azureでホスティングする際、今まではストレージアカウントのBlob StorageやApp Serviceを利用していましたが、この度専用のサービスがリリースされるということで使い方を試してみました。

## 前提条件
本記事ではVue.jsを使用してWebアプリケーションを構築します。
また、素早くアプリケーションを構築するため[Vue CLI](https://cli.vuejs.org/guide/)を使用します。
バージョン管理ツールはGitHubを使用します。

それぞれのバージョンについては下記の通りです。
Vue.js:2.6.14
Vue CLI:4.5.13
npm:7.18.1

## Azure Static Web Appsとは

Azure Static Web Apps は、GitHubやAzure DevOpsのリポジトリから
Webアプリケーションを自動的にビルド&デプロイするサービスです。

主要な機能は以下の通りです。

- HTML、CSS、JS、画像等の静的コンテンツのWebホスティング
- リポジトリの変更によってビルドとデプロイがトリガーされるGitHub統合およびAzure DevOps統合
- Azure Functionsとの統合を標準サポート
- APIの呼び出し時のCORSの構成が不要
- リポジトリの変更をトリガーにビルドからデプロイまで実行されるCI/CD環境を提供
- グローバルに分散された静的コンテンツがユーザーに近いリージョンに配置される
- 自動的に更新される無料のSSL証明書を使用可能
- カスタムドメインを設定可能
- Azure AD、GitHub、Twitter等の認証プロバイダーとの統合を標準サポート
- カスタマイズ可能な認可ロールの定義と割り当て
- 提供するコンテンツとルートを完全に制御できるようにするバックエンド ルーティング規則
- プルリク時に、デプロイ前のサイトのプレビュー状態を確認可能 

## Static Web Appsの構成図
下図はStatic Web Appsの構成図になります。
リポジトリのコード変更をトリガーに、Build&Deployのワークフローが実行されてStaic Web Appsに反映されるイメージです。

![stapp構成図例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/63b9e46c-bbb9-c4ec-a29a-c8e786156a71.png)



## WEBアプリケーションをホスティングする

まずはWEBアプリのホスティングを試してみます。

- GitHubにリポジトリ（リポジトリ名:static-web-apps）を作成する。  
- ローカルにリポジトリをクローンする。  
- Vue CLIを使って土台となる環境を準備する。  
※vuetifyは必要であれば追加する。

```
cd static-web-apps
vue create .
vue add vuetify
npm i
```

- 作成した土台をローカルホストで確認する。  
`npm run serve`
Vue CLIで作成した土台となるアプリケーション
![vuetifyのテンプレート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/da51a009-3926-46d0-487f-4f5924368d01.png)

- mainブランチにソースをpushする。

```
git add --all
git commit
git push origin main
```

- Static Web Appsのリソースを作成する。
![stapp作成（認証前）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/fea6d5f5-7f65-bd5e-a88b-ea0447ca0430.png)
- Azure Functions APIとステージング環境のリージョンはEast Asiaを選択する。
- プランはFreeプランを選択する。
- 「GitHubアカウントでサインイン」ボタンを押下する。
![github連携認証.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/05f0b760-2657-5121-fb15-0166b85a2835.png)
- 「Authorize Azure-App-Service-Static-Web-Apps」のボタンを押下する
![stapp作成（github認証後）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/45dca6c0-ae2c-bc1d-971e-b9a9a43569a3.png)

各項目に以下の値をそれぞれ設定する。
組織：リポジトリを作成した組織
リポジトリ：作成したリポジトリ
分岐：main
ビルドのプリセット：Vue.js
アプリの場所：/
APIの場所（※後で使用します）：api
出力先：dist

- GitHub Actionsのワークフローの状態を確認する。
![ワークフロー確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/48f1f1e5-59fb-3981-0ce4-9db78dfc2d95.png)
- ワークフローが完了後Static Web Appsの[概要]から発行されたURLを確認する。
![概要ページからURL確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/a345d424-104d-817f-69db-f3b16245ca5c.png)
- 正常にアプリケーションが表示されたらホスティング完了です。

## Azure Functionsと統合する
次にStatic Web AppsはAzure Functionsとの統合を標準でサポートしているので、
APIを実装してWebアプリと疎通できるか試してみます。

- ルート階層でapiディレクトリを作成する。
- VsCodeのAzure Functionsの拡張機能を使ってHttpTrigerの関数を作成する。  
F1キーでコマンドパレットを開いてcreate new projectからapiディレクトリを指定する。  
javascript → HttpTrigger → 関数名入力 → Anonymousの順番で設定を進めます。
- 今回はGETのAPIのみ作成するので、`api/HttpTrigger1/function.json`のmethodsからpostを削除する。

``` json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post" ←削除
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
```
- APIのURLの設定のため`api/HttpTrigger1/function.json`のrouteを設定する。

``` json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get"
      ],
      "route": "msg" ←追加
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
```
※この場合のAPIのurlは`/api/msg`になる

- ローカル環境でAPIを実行するために以下の設定を行う  

○ `api/local.settings.json`にCORSの設定を追記

```
"Host": {
  "CORS": "http://localhost:8080"
}
```
○ vue.config.jsにプロキシ設定を追記

```
devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:7071'
      }
    }
  }
```
- API実行に必要なライブラリをインストールする

```
npm i vue-axios
npm i axios
```
- アプリケーション側を編集してAPIを呼び出す処理を追記する

main.jsにaxiosのimport設定を追記  

``` main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
import axios from 'axios' // 追記
import VueAxios from 'vue-axios' // 追記

Vue.config.productionTip = false

Vue.use(VueAxios, axios) // 追記

new Vue({
  vuetify,
  render: h => h(App)
}).$mount('#app')

```
HelloWorld.vueの内容を編集してAPIを実行するUIに変更

``` HelloWorld.vue
<template>
  <v-container fluid>
    <v-row justify="center">
      <v-col cols="4">
        <v-card>
          <v-card-text>
            <v-row>
              <v-col cols="12">
                <v-text-field
                  v-model="name"
                  outlined
                  hide-details
                >
                </v-text-field>
              </v-col>
              <v-col v-show="msg" cols="12">
                {{ msg }}
              </v-col>
            </v-row>
          </v-card-text>
          <v-card-actions>
            <v-btn
              @click="getMsg"
              color="primary"
              dark
              block
            >
              メッセージ取得
            </v-btn>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
  export default {
    name: 'HelloWorld',

    data: () => ({
      name: '',
      msg: ''
    }),
    methods: {
      async getMsg () {
        this.msg = await this.axios.get(`api/msg?name=${this.name}`).then(res => res.data)
      }
    }
  }
</script>

```
- ローカル環境でAPIが実行可能か確認する。  
2つコンソールを起動して片方で`npm run serve`を実行してアプリケーションを起動する。
もう片方はapi配下に移動後に`func start`を実行してAPIを起動する。

編集後のアプリケーション
テキストボックスにいれた文字列をクエリーに設定して、GETのAPIを実行した結果を画面に表示するアプリケーション
![API実行用UI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/b080cefe-e300-9ff4-4eaa-ed412eb2ef71.png)

- mainブランチに編集後のソースをpushする。
- GitHub Actionsのワークフローの状態を確認する。
- ワークフロー完了後、Static Web Appsの[関数]に追加したAPIが表示されているか確認する。

![関数ページ確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/6043d3b3-c58e-cea7-8b6a-0060c4102dfd.png)

- static web appsの[概要]から発行されたURLを確認する。  
※APIが正しく実行できるかを確認する。

## ステージング環境を準備する

Static Web Appsでは本番環境にデプロイする前に、
ステージング環境のプレビューURLを発行して動作確認を行うことができるのでこの機能も試してみます。

- stagingブランチを作成する

```
git branch staging
git switch staging
```
- stagingブランチでアプリケーションのソースを変更する。  

ボタンの色をピンク色に変更してみます。

``` HelloWorld.vue
<v-btn
  @click="getMsg"
  color="pink" ←ボタンの色をピンク色に変更
  dark
  block
>
  メッセージ取得
</v-btn>
```

- stagingブランチにソースをpushする。
- mainブランチに対するプルリクエストを作成する。
![プルリク作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/acf18358-fedc-7743-9138-6f4d6d6b46b3.png)

- GitHubアクションのワークフローの状態を確認する。
- プルリクエストページに表示されるプレビューURLを参照する。
![プルリク上でのプレビューURL確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/7ef69c9c-5e28-4b0d-ba99-c295bd7851fe.png)


プレビューURLの結果（ボタンの色を変更している）
![プレビューURL結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/7243d0d9-f7dd-71db-2e45-937ecb43fd72.png)

※Static Web Appsの[環境]からもstaging環境を参照することができます。
![環境ページでステージング確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1806757/0c86b34d-24fb-3443-a1ca-0332a306e24a.png)

## 価格設定

参考までにStatic Web Appsの価格設定のURLを記載します。
カスタム認証や、SLAが不要であれば基本的には無料枠で必要な機能を試すことが可能です。

[Static Web Apps の価格](https://azure.microsoft.com/ja-jp/pricing/details/app-service/static/)

## 参考

[Azure Static Web Apps のドキュメント
](https://docs.microsoft.com/ja-jp/azure/static-web-apps/)