Azure DevOps を使用して Azure Static Web Apps を CLI で発行してみた

## 背景と目的

Azure Static Web Apps が GA しましたが、ドキュメントに Azure CLI のサンプルが無かったので、試してみました。

## 前提条件

コマンドの実施環境は、Mac + Azure CLI です。

```zsh:zsh
% sw_vers
ProductName:    macOS
ProductVersion: 11.3.1
BuildVersion:   20E241

% az version                            
{
  "azure-cli": "2.23.0",
  "azure-cli-core": "2.23.0",
  "azure-cli-telemetry": "1.0.6",
  "extensions": {}
}
```

## 実施内容

```zsh:zsh
# リソースプロバイダーの情報から Azure Static Web Apps が利用出来るリージョンを調べます
az provider show \
  --namespace Microsoft.Web \
  --query "resourceTypes[?resourceType=='staticSites'].locations[]" \
  --output tsv

# 実行結果
West US 2
Central US
East US 2
West Europe
East Asia

# 環境変数を設定します
region=eastasia
prefix=mnrstaticweb
organization=https://dev.azure.com/mnrsdemo/

# デフォルトのオーガニゼーションを設定します
az devops configure \
  --defaults organization=$organization

# DevOps にプロジェクトを作成します
az devops project create \
  --name ${prefix}

# GitHub から Azure Static Web Apps のサンプルプロジェクトをインポートします
az repos import create \
  --git-source-url https://github.com/staticwebdev/vanilla-api.git \
  --repository ${prefix} \
  --project ${prefix}

# ローカルにソースコードをダウンロードします
git clone $(az repos show \
  --project ${prefix} \
  --repository ${prefix} \
  --query remoteUrl \
  --output tsv)

# リソースグループを作ります
az group create \
  --name ${prefix}-rg \
  --location $region

# az staticwebapp create が DevOps 未対応のため ARM テンプレートで Azure Static Web Apps を作成します
cat << EOF > static-webapp-deployment-template.json
{
    "\$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "name": {
            "type": "String"
        },
        "location": {
            "type": "String"
        },
        "sku": {
            "type": "String"
        },
        "skucode": {
            "type": "String"
        }
    },
    "resources": [
        {
            "type": "Microsoft.Web/staticSites",
            "apiVersion": "2019-12-01-preview",
            "name": "[parameters('name')]",
            "location": "[parameters('location')]",
            "sku": {
                "Tier": "[parameters('sku')]",
                "Name": "[parameters('skuCode')]"
            },
            "properties": {}
        }
    ]
}
EOF

cat << EOF > static-webapp-deployment-parameters.json
{
  "name": {
    "value": "${prefix}-swa"
  },
  "location": {
    "value": "$region"
  },
  "sku": {
    "value": "Free"
  },
  "skucode": {
    "value": "Free"
  }
}
EOF

# Azure Static Web Apps を作成します
az group deployment create \
  --resource-group ${prefix}-rg \
  --template-file static-webapp-deployment-template.json \
  --parameters @static-webapp-deployment-parameters.json

# 作成された Azure Static Web Apps にアクセスします
curl -s https://$(az staticwebapp show \
  --resource-group ${prefix}-rg \
  --name ${prefix}-swa \
  --query defaultHostname \
  --output tsv)

# ディレクトリを移動します
cd ${prefix}

# Azure Static Web Apps にデプロイするパイプライン設定ファイルを作成します
cat <<EOF > azure-pipelines.yaml
trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
  - checkout: self
    submodules: true
  - task: AzureStaticWebApp@0
    inputs:
      app_location: '/'
      api_location: 'api'
      output_location: ''
    env:
      azure_static_web_apps_api_token: \$(deployment_token)
EOF

# 作成したパイプライン設定ファイルをリポジトリにプッシュします
git add azure-pipelines.yaml
git commit -m "add azure-pipelines.yaml"
git push

# リポジトリにある既存のパイプライン設定ファイルからパイプラインを作成します
az pipelines create \
  --name StaticWebAppPipeline \
  --repository $(az repos show \
  --project ${prefix} \
  --repository ${prefix} \
  --query remoteUrl \
  --output tsv) \
  --branch main \
  --yml-path azure-pipelines.yaml \
  --skip-first-run true

# Azure Static Web Apps のデプロイトークンをパイプラインの環境変数に登録します
subscid=$(az account show \
  --query id \
  --output tsv)
deploytoken=$(az rest \
  --method post \
  --uri "/subscriptions/$subscid/resourceGroups/${prefix}-rg/providers/Microsoft.Web/staticSites/${prefix}-swa/listsecrets?api-version=2020-12-01" \
  --query properties.apiKey \
  --output tsv)
az pipelines variable create \
  --name deployment_token \
  --value $deploytoken \
  --secret true \
  --pipeline-name StaticWebAppPipeline \
  --project ${prefix}

# パイプラインを実行して Azure Static Web Apps にデプロイします
az pipelines run \
  --name StaticWebAppPipeline \
  --project ${prefix} \
  --output table
```

## 実施結果

Azure Static Web Apps のサンプルプロジェクトである Vanilla JavaScript App が Azure DevOps の Pipelines を使ってデプロイされました。

```zsh:zsh
# 作成された Azure Static Web Apps にアクセスします
curl -s https://$(az staticwebapp show \
  --resource-group ${prefix}-rg \
  --name ${prefix}-swa \
  --query defaultHostname \
  --output tsv)

# 実行結果
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="styles.css" />
    <title>Vanilla JavaScript App</title>
  </head>

  <body>
    <main>
      <h1>Vanilla JavaScript App</h1>
      <p>Loading content from the API: <b id="message"></b></b></p>
    </main>
  </body>

  <script>
    (async function () {
      let response = await fetch(`api/message`);
      let message = await response.json();

      document.querySelector("#message").innerHTML = message.text
    })();
  </script>
</html>
```

## 参考

```zsh:zsh
# DevOps プロジェクトを削除します
az devops project delete \
  --id $(az devops project show \
  --project ${prefix} \
  --query id \
  --output tsv)

# リソースグループを削除します
az group delete \
  --name ${prefix}-rg
```

[Azure Static Web Apps is now generally available](https://azure.microsoft.com/ja-jp/updates/azure-static-web-apps-is-now-generally-available/)

[チュートリアル: Azure DevOps を使用して Azure Static Web Apps を発行する](https://docs.microsoft.com/ja-jp/azure/static-web-apps/publish-devops?wt.mc_id=azurestaticwebapps_inline_inproduct_general)

[az pipelines](https://docs.microsoft.com/ja-jp/cli/azure/pipelines?view=azure-cli-latest)