Azure Static Web Apps に複数テナント×認証付きでコンテンツを公開した話

2025年9月5日掲載

キービジュアル

AIプラットフォーム開発本部の安澤です。

私たちのチームでは、日々、開発しているプロダクトの技術的な仕様書を社内関係者に共有・維持管理しています。これまでは公開範囲を柔軟に制御できることから、Google WorkspaceのGoogle Docsを利用してきました。

しかし運用を続ける中で、閲覧者による誤編集のリスクや、文書サイズが大きくなるにつれて動作が重くなり編集が困難になる、バージョン管理の煩雑さといった課題が目立つようになってきました。

こうした課題を解決するために、GitHub上でのMarkdown管理へ移行し、Markdownを HTMLに変換したうえでAzure Static Web Appsにホスティング、さらに、認証基盤としてAzure AD B2Cを組み合わせることで複数テナントのユーザに限定して公開する仕組みを構築しました。

本記事では第一弾として、それらの設計背景から具体的なTerraformモジュール定義、認証設定まわりを順を追って説明していきます。

※注意:2025年5月1日以降、Azure AD B2Cは新規顧客向けの購入が不可になっています。既存のB2Cテナントでは引き続き利用可能です。

目次

この記事では
  • 社内ドキュメントをAzure Static Web Apps上で公開する仕組みを構築した背景と目的について説明しています。
  • Terraformを用いたAzureリソース構築や、B2Cテナントとの認証連携設定の実装方法を紹介しています。
  • GitHub Actionsを利用したAzure Static Web Appsのデプロイ方法について一部触れています。

背景と目的

私たちは、生成AIパッケージの開発を担当しており、社内関係者向けに技術仕様や操作手順、設定方法などを記載した技術ドキュメントを整備・管理しています。

これまではGoogle Docsで管理していました。Google Docsは共同編集に優れており利便性は高いものの、以下のような課題が存在しました。

編集権限のジレンマ

編集権限を全員に付与すると、意図せず文章が変更されてしまうことがありました。一方で閲覧専用にすると、コードサンプルをコピーできないなど利用上の不便が生じていました。

Markdownへの統一ニーズ

エンジニアにとって親和性の高いMarkdown形式で管理したいという要望がありました。コードブロックや表の記述と相性が良く、さらにGitHub上で差分レビューを行いやすい点もメリットです。加えて、バージョン管理が容易になり、誰がいつどの部分を修正したかを明確に追跡できるようになります。

文量の多さによる管理難易度

技術ドキュメントは膨大な文量になるため、Google Docsのように1つの巨大なファイルで扱うのは非効率でした。章立てごとにMarkdownファイルを分割し、リポジトリで管理することでメンテナンス性の向上を図る必要がありました。

これらの課題を解決するため、Azure Static Web Apps上で公開する仕組みを構築しました。

B2Cテナントを選択した理由

認証制御の方式にはいくつか選択肢がありましたが、最終的にAzure AD B2Cを利用する構成としました。その理由は以下の通りです。

共有範囲の制御が必要だったため

Google Docsでは共有範囲を柔軟に設定できますが、Markdownを静的サイトとして公開する場合にも、同等のアクセス制御が求められました。

GitHub Pagesでは不十分だったため

GitHub Pagesは手軽ですが、閲覧にはGitHubアカウントが必須となり、全ての関係者が利用できるわけではありませんでした。

複数テナントからのログインを実現するため

閲覧権限を付与したいユーザー全員が同じテナントにアカウントを持っているわけではありません。そのため、複数のテナントから認証可能な仕組みが必要であり、B2C テナントが適していました。

このような要件を満たすために、Static Web Appsの認証基盤としてB2Cテナントを採用しました。構成図は下記のようになります。

TerraformによるAzure環境構築

tfstate管理用のAzure Blob Storageとリソースデプロイ用のリソースグループは事前に準備しておきます。

Static Web Appsのデプロイ

アプリ名・リソースグループ・ロケーションを指定し、さらに B2C認証に必要なクライアントIDとシークレットを環境変数として渡せるようにします。

resource "azurerm_static_web_app" "webapp" {
  name                = var.static_web_app_name
  resource_group_name = var.resource_group_name
  location            = var.location
  sku_tier            = "Standard"
  sku_size            = "Standard"
  app_settings = {
    "AADB2C_PROVIDER_CLIENT_ID"     = ""
    "AADB2C_PROVIDER_CLIENT_SECRET" = ""
  }
}

ここで定義した環境変数には後に B2C 側で発行したクライアントIDとシークレットの値で上書きされます。

B2Cテナント用の Entra IDアプリ

次に、B2Cテナントに対応するEntra IDアプリとシークレットを作成します。このアプリはStatic Web Appsの認証連携に利用され、ユーザーのサインインを管理します。

特にポイントとなるのは、redirect_urisの指定です。Static Web Appsは認証処理を以下のエンドポイントにリダイレクトするため、次の形式で指定する必要があります。

https://<YOUR-STATIC-WEB-APP-URL>/.auth/login/aadb2c/callback

Terraform定義は次のようになります。テナントIDにはB2CテナントのIDを指定する必要があります。

data "azuread_client_config" "current" {}

provider "azuread" {
  tenant_id = "<B2CテナントID>"
}

resource "azuread_application" "ad_application" {
  display_name     = "<EntraIDのアプリ名>"
  sign_in_audience = "AzureADandPersonalMicrosoftAccount"
  web {
    redirect_uris = ["https://<YOUR-STATIC-WEB-APP-URL>/.auth/login/aadb2c/callback"]
  }
  api {
    requested_access_token_version = 2
  }
  owners = [data.azuread_client_config.current.object_id]

  dynamic "required_resource_access" {
    for_each = var.enable_graph_permissions ? [1] : []
    content {
      resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph のアプリケーションID
      resource_access {
        id   = "37f7f235-527c-4136-accd-4a02d197296e" # openid
        type = "Scope"
      }

      resource_access {
        id   = "7427e0e9-2fba-42fe-b0c0-848c9e6a8182" # offline_access
        type = "Scope"
      }
    }
  }
}

resource "azuread_application_password" "ad_application_password" {
  application_id = azuread_application.ad_application.id
  display_name   = "<EntraIDのsecret名>" 
  end_date       = "<secretの有効期間>"
}
enable_graph_permissions = true の挙動

この設定を有効にすると、Terraform 内部でMicrosoft Graph APIの以下2つの権限が自動付与されます。

  • openid:OpenID Connect認証に必要なスコープ(ユーザーの一意識別子を取得可能)

  • offline_access:リフレッシュトークンの発行を許可し、長期ログインやバックグラウンドでのトークン更新を可能にする

Static Web AppsとB2Cの統合

B2C側で発行されたクライアントID とシークレットを、Static Web Appsの環境変数に自動設定するためにnull_resourceを用いて CLI コマンドを実行します。

resource "null_resource" "set_env_vars" {
  depends_on = [
    module.static_web_app,
    module.b2c_entraid
  ]

  provisioner "local-exec" {
    command = <<EOT
      az staticwebapp appsettings set \
        --name ${module.static_web_app.static_web_app_name} \
        --resource-group ${var.resource_group_name} \
        --setting-names AADB2C_PROVIDER_CLIENT_ID=${module.b2c_entraid.entraid_application_client_id} \
                       AADB2C_PROVIDER_CLIENT_SECRET=${module.b2c_entraid.new_secret_value}
    EOT
  }

  triggers = {
    always_run_timestamp = timestamp()
  }
}

ログインに利用するテナント側の設定

ログインユーザが属するテナントにおいてもEntra IDアプリを用意する必要があります。複数テナントを使用したい場合は、それぞれのテナントで作成する必要があります。なお、Static Web Appsをデプロイしたテナント自体をログイン用に指定することも可能です。

Terraform モジュール定義は「B2C テナント用の Entra ID アプリ」で紹介したコードを流用できますが、以下の点を変更します。

  • redirect_uris:B2Cテナント名を含め、次の形式を指定します。
https://<YOUR-B2C-TENANT>.b2clogin.com/<YOUR-B2C-TENANT>.onmicrosoft.com/oauth2/authresp
  • enable_graph_permissions:false を指定し、通常のシングルテナント認証用途とします。

  • sign_in_audience:AzureADMyOrgを指定します。

B2Cテナントでサインイン設定

B2Cテナント側では、Static Web Appsからの認証を受け付けるために、以下の設定を行います。

IDプロバイダー設定

B2Cテナントに Microsoft Entra(シングルテナント) を IDプロバイダーとして追加します。

その際、メタデータURLに以下の形式を指定してください。ここで <YOUR-TENANT> には、ユーザ認証に使用するMicrosoft Entraテナントのドメイン名を入力します。

https://login.microsoftonline.com/<YOUR-TENANT>/v2.0/.well-known/openid-configuration

その他項目の詳細な設定に関しては、「Microsoft Entra ID を ID プロバイダーとして構成する」を参照してください。

ユーザーフローの作成

次に、B2Cのユーザーフロー(サインアップ+サインイン)を作成し、有効化します。

作成時には、IDプロバイダーとして前ステップで追加したIDプロバイダーを選択します。

ユーザーフローの詳細な作成手順に関しては「サインアップとサインイン ユーザー フローを作成する」を参照してください。

アクセス許可を付与

B2Cテナントで作成した Entraアプリには、必要なAPIアクセス許可を付与します。

APIアクセス許可の構成されたアクセス許可の項目で「管理者の同意を与えます」のチェックを押下してください。

OpenId Connect IDプロバイダーとして追加

最後に、Azure AD B2CをOpenID Connect IDプロバイダー として staticwebapp.config.jsonに定義します。ここで <POLICY_NAME> には、作成したユーザーフローの名前を指定します。

{
  "auth": {
    "identityProviders": {
      "customOpenIdConnectProviders": {
        "aadb2c": {
          "registration": {
            "clientIdSettingName": "AADB2C_PROVIDER_CLIENT_ID",
            "clientCredential": {
              "clientSecretSettingName": "AADB2C_PROVIDER_CLIENT_SECRET"
            },
            "openIdConnectConfiguration": {
              "wellKnownOpenIdConfiguration": "https://<TENANT_NAME>.b2clogin.com/<YOUR-B2C-TENANT>.onmicrosoft.com/<POLICY_NAME>/v2.0/.well-known/openid-configuration"
            }
          },
          "login": {
            "nameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
            "scopes": [],
            "loginParameterNames": []
          }
        }
      }
    }
  },
  "routes": [
    {
      "route": "/*",
      "allowedRoles": ["authenticated"]
    }
  ],
  "responseOverrides": {
    "401": {
      "statusCode": 302,
      "redirect": "/.auth/login/aadb2c"
    }
  }
}

この設定により、アプリケーションのルートは認証済みユーザーのみアクセス可能となり、未認証のユーザーは自動的に B2Cのログイン画面へリダイレクトされます

GitHub Actionsによるデプロイ

ドキュメントはMarkdownで日々更新されるため、常に最新の状態を公開できる仕組みが欠かせません。更新のたびに手動でデプロイを行うのは現実的ではないため、リポジトリへの更新をトリガーに自動デプロイが実行されるよう、GitHub Actionsを採用しました。

デプロイトークンの準備

Azure Portal上で作成したStatic Web Appsの [概要] →[ デプロイ トークンの管理] からデプロイトークンを取得し、GitHubのSecretsに以下のキーで登録します。

AZURE_STATIC_WEB_APPS_API_TOKEN
構成ファイルの配置

認証やルーティングに必要なstaticwebapp.config.jsonは、デプロイ対象フォルダ(本記事では dist/)に格納しておく必要があります。

GitHub Actionsワークフロー

以下のように、Azure/static-web-apps-deploy@v1 アクションを利用してデプロイを実行します。

ここではビルド済みアーティファクトをそのまま配置するため、skip_app_build: trueを指定しています。

- name: Deploy to Azure Static Web Apps
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          action: "upload"
          app_location: "/dist"
          skip_app_build: true

※ ドキュメントはMarkdownで管理しており、デプロイ前にPythonスクリプトでMarkdownをHTMLに変換しています。この処理はGitHub Actions内で事前に実行され、その成果物をdist/ 配下にダウンロードした上でデプロイを行っています。

おわりに

この記事では、社内ドキュメントをAzure Static Web Appsで公開する仕組みについて紹介しました。これまでのGoogle Docs運用で感じていた課題から、Terraformを使った環境構築、B2Cテナントとの認証連携、そして GitHub Actionsによるデプロイまで、一連の流れをまとめてみました。

第二弾では、もう少し実装寄りの具体的な工夫について書く予定です。MarkdownをHTMLに変換する際のPythonスクリプト(目次の自動生成や見出し・図表番号の自動採番など)、Static Web Apps上でページ遷移を実現する方法、そして今回触れきれなかったGitHub Actionsワークフローの詳細などを取り上げます。第一弾の仕組みをベースに、より実用的なテクニックを共有できればと思います。

最後までお読みいただき、ありがとうございました。

関連サービス

セキュアなAzure OpenAI Service環境をパッケージとして提供するサービスです。よりスムーズに生成AIの導入を実現することができます。

MSP(Managed Service Provider)サービスは、お客さまのパブリッククラウドの導入から運用までをトータルでご提供するマネージドサービスです。

おすすめの記事

条件に該当するページがございません