AI

Box AI for HubsをTeamsから使えるように連携させてみた話

はじめに

こんにちは、俊介です。 2025アドベントカレンダー20日目担当です!

Cloudnative IRS Advent Calendar 2025 – Adventar

僕は最近のAIのトレンドだったり、ちょっと前のトレンドだけどお客様のやりたい事を一緒に解決したことをどのように実装したか等を書いていっています。

チームメンバーがいろんなジャンルのブログを書いていますのでぜひ覗いてみてください!

今日のお話しを簡単に

今日はLogic Appsを使って、Box AI for HubsとTeamsを連携させる為の環境構築を書こうかなと思います。
本ブログを実施することで、Boxにアクセスすることなく、Teamsから検索・回答までAIにやってもらうことができます。

どんなユーザー向け?

  • Boxをメインでドキュメント管理を行なってる企業
  • Box AI for Hubsを利用している、または利用できる企業
    • Enterprise PlusプランおよびEnterprise Advancedプラン契約してる企業が対象になります

シーケンス図

今回実装するものをシーケンス図にするとこんな感じです。

実際にやってみる

注意事項

本ブログの手順はTeamsからBox AI for Hubsにリクエストを飛ばして1検索1回答返すだけのシンプルな構成です。
AIの検索後の会話などは本ブログでは触れていません。

準備するもの

  • Teamsのアカウント
    • Bot用アカウント
    • Teamsが使えるライセンスを割り当てる
  • Azure Functions
    • Boxのアクセストークンを取得させる処理をさせます
  • Logic Apps
    • TeamsとBox AI for Hubsを連携させるプラットフォームです
  • Box Hubs
    • 今回のデータソースになります
  • Box App
    • サービスアカウントを作成してAPI操作をしていきます
    • 認証はJWT認証を採用しています
  • VS Code
    • Azure Functionsに関数(処理コード)をデプロイするのに利用します
  • Python
    • 今回の実行言語です

Box Appの作成

  1. Boxにログインして、左下の「開発者コンソール」>[Platformアプリ]>右上「Platformアプリの作成」を押下
  2. カスタムアプリを選択
  3. フォームを入力して「次へ」を押下
    アプリ名:例)AzureToBox App
    目的:統合
    カテゴリ:AI
    統合する外部システム:例)Teams
  4. 認証方法は「サーバー認証(JWT使用)」を選択して「アプリの作成」を押下
  5. [構成]タブの[アプリアクセスレベル]セクションを「アプリ + Enterpriseアクセス」に変更
  6. [公開キーの追加と管理]セクションで「公開/秘密キーペアを生成」を押下してJSONファイルをDL
    後続の作業の環境変数の設定で利用するので大事に保管
  7. [アプリケーションスコープ]セクションで「AIを管理する」にチェックを入れる
  8. 「変更を保存」を押下
  9. [承認]タブにいき、「確認して送信」を押下

▼申請するとこの状態になる

10. [管理コンソール]>[統合]>[Platformアプリマネージャ]へ移動し、対象のAppがあることを確認

11. 右上の「承認」を押下して確認画面が開くのでもう一度「承認」を押下

12. [開発者コンソール]>[Platformアプリ]>[対象アプリ]>[承認]へ画面遷移して承認ステータス有効化ステータスが変わっているのを確認

13. Box Hubに今回作成したアプリのサービスアカウントを共有者として追加する

Azure Functionsの作成

  1. Azure Portal>関数アプリ>「+作成」を押下
  2. 関数アプリのプランを選択 今回は検証なのでフレックス従量課金を選択しました
  3. 基本情報を記入
    リソースグループ:すでに作成してる場合は、トグルから選択。未作成の方は作成をしてください
    関数アプリ名:Functionの名前を記入
    リージョン:Japan East
    ランタイム スタック:Python
    バージョン:3.10以上
  4. 今回は検証なので、[基本情報]以外はスキップして「確認および作成」を押下
  5. この画面になればOKです
  6. [設定]>[環境変数]へ移動してBoxのアプリ情報を入れる変数を作成して値を入れて「適用」を押下

Boxアプリを作成して、「公開/秘密キーペアを生成」でDLした時の情報を入れます。
※BOX_PRIVATE_KEYは復号した状態のキー情報を入れます。

  • BOX_CLIENT_ID
  • BOX_CLIENT_SECRET
  • BOX_ENTERPRISE_ID
  • BOX_PRIVATE_KEY
  • BOX_PUBLIC_KEY_ID

VS CodeでAzure Functionsに処理コードの関数をデプロイ

VS Codeの拡張子を使ってAzure Functiins関数アプリに処理コードをデプロイします

  1. VS Codeを開いて「Azure Functions」の拡張子をインストール
  2. Azureにログイン
  3. 「Create Function Project…」を押下して関数の土台を作成しいく
  4. プロジェクトフォルダの場所を選択(今回は新規で作成して「Select」を押下)
  5. 言語の選択は「Python」を選択
  6. 言語のバージョンは「python 3.10.13」を選択
  7. 今回Logic Appsから呼び出すので「HTTP trigger」を選択
  8. 関数名を入れてEnter(Azure Functionのポータル上にも表示される)
  9. Logic Appsからリクエストを飛ばす際の認証方法を選択(今回はFunctionを選択)
  10. 今開いてるVS Codeの画面をそのまま使うので、「Open in curent window」を押下
  11. function_app.pyに下記を貼り付けて保存
    ▼変更箇所
    16行目:8. で設定した関数名を入れてください
    17行目:8. で設定した関数名を入れてください
import os
import json
import logging
import jwt
import requests
import uuid
import time

import azure.functions as func

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

@app.route(route="<関数名>")
def <関数名>(req: func.HttpRequest) -> func.HttpResponse:
    logger.info('Python HTTP trigger function processed a request.')

    try:
        # 環境変数からJWT認証に必要なパラメータを取得
        client_id = os.environ.get('BOX_CLIENT_ID')
        client_secret = os.environ.get('BOX_CLIENT_SECRET')
        private_key = os.environ.get('BOX_PRIVATE_KEY')
        public_key_id = os.environ.get('BOX_PUBLIC_KEY_ID')
        enterprise_id = os.environ.get('BOX_ENTERPRISE_ID')

        if not all([client_id, client_secret, private_key, public_key_id, enterprise_id]):
            missing_vars = [
                name for name, value in {
                    'BOX_CLIENT_ID': client_id,
                    'BOX_CLIENT_SECRET': client_secret,
                    'BOX_PRIVATE_KEY': private_key,
                    'BOX_PUBLIC_KEY_ID': public_key_id,
                    'BOX_ENTERPRISE_ID': enterprise_id
                }.items() if not value
            ]
            logger.error(f"Missing required environment variables: {', '.join(missing_vars)}")
            return func.HttpResponse(
                f"Missing one or more required environment variables: {', '.join(missing_vars)}. Please check your Azure Function App settings.",
                status_code=400
            )

        private_key = private_key.replace('\\n', '\n')

        # JWTクレームの作成
        jwt_body_claim = {
            "iss": client_id,
            "sub": enterprise_id,
            "box_sub_type": "enterprise",
            "aud": "https://api.box.com/oauth2/token",
            "jti": uuid.uuid4().hex,
            "exp": int(time.time()) + 60
        }

        logger.info(f"Generated JWT Claims: {jwt_body_claim}")

        # JWTのエンコード (RS512アルゴリズムを使用)
        jwt_token = jwt.encode(
            jwt_body_claim,
            private_key,
            algorithm="RS512",
            headers={"kid": public_key_id}
        )
        logger.info("JWT Token successfully encoded.")

        token_url = "https://api.box.com/oauth2/token"
        payload = {
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "assertion": jwt_token,
            "client_id": client_id,
            "client_secret": client_secret
        }

        logger.info(f"Requesting access token from {token_url}...")
        response = requests.post(token_url, data=payload)


        if response.status_code != 200:
            logger.error(f"Box API responded with status {response.status_code}. Response content: {response.text}")
            return func.HttpResponse(
                f"Failed to retrieve access token from Box API. Status: {response.status_code}, Details: {response.text}",
                status_code=response.status_code
            )


        token_data = response.json()
        access_token = token_data.get('access_token')

        if access_token:
            logger.info("Access token successfully obtained.")
            return func.HttpResponse(
                json.dumps({"access_token": access_token}),
                mimetype="application/json",
                status_code=200
            )
        else:
            logger.error(f"Access token not found in Box API response: {token_data}")
            return func.HttpResponse(
                "Failed to retrieve access token.",
                status_code=500
            )

    except requests.exceptions.RequestException as e:
        logger.error(f"HTTP Request to Box API failed: {e}", exc_info=True)
        return func.HttpResponse(
            f"Error communicating with Box API: {e}",
            status_code=500
        )
    except jwt.PyJWTError as e:
        logger.error(f"JWT encoding failed: {e}", exc_info=True)
        return func.HttpResponse(
            f"Error encoding JWT for Box authentication: {e}",
            status_code=500
        )
    except Exception as e:
        logger.error(f"An unexpected error occurred in the function: {e}", exc_info=True)
        return func.HttpResponse(
            f"An internal server error occurred: {e}",
            status_code=500
        )

12. requirements.txtに下記のコードを貼り付けて保存

azure-functions
PyJWT
cryptography
requests

13. 対象のAzure Functionの名前上で右クリックして「Deploy to Function App…」を押下

14. デプロイしても問題ないか確認がポップアップされるので「Deploy」を押下

15. 正常に終了してればOKです

16. [Azure Portal]>[関数アプリ]>[該当の関数アプリ]>Azure Functionsのポータルに関数が表示されているか確認

Boxのアクセストークンが取得できるかテスト

  1. 先ほど表示された関数名を押下して開く
  2. [コードとテスト]>「テスト/実行」>「実行」を順番に押下
  3. 下記が返ってくればBox APIを叩く準備は完了
  4. 関数のURLの取得からURLをコピーしておく この後のLogic Appsで使います

Logic Appsでフローを作成する

シンプルなものを作成します。

  1. トリガー設定
    connection:Teams
    トリガー名:自分が @mentioned である場合
    ※Teamsの認証は、Bot用に作成したアカウントで認証してください
  2. メッセージの中身を取得
    connection:Teams
    アクション名:メッセージ詳細を取得する
  3. Box API叩く為のアクセストークン取得する為にAzure Functionsへリクエスト
    connection:HTTP
    アクション名:HTTP
    URL:先ほどコピーした関数URLを貼り付け
  4. Box AIにリクエスト
    connection:HTTP
    アクション名:HTTP
    Authorization:1個前のHTTPで取得した結果のアクセストークンを利用
    prompt:2個前のメッセージ詳細で取得した結果のユーザーが入力したテキストを利用
    hubsのid:検索をかけたいBox HubのIDを設定


  5. Teamsに返す為にHTMLに整形
    connection:Data Operations
    アクション名:Compose
    Input:Box AIの結果を使ってカスマイズして好みの形にHTML形式で記載
  6. ユーザー投稿のスレッドにAIの回答を投稿する
    connection:Teams
    アクション名:チャネル内のメッセージで応答します
  7. 保存

実行

  1. Botアカウントに対してメンション付きで質問を投げる
  2. スレッドに返ってきたらOK

所感

  • 都度Boxにアクセスしなくてよくなったので便利ではある
  • Boxが用意してくれてるAIなので、始めやすさは○
  • Hub毎にこのシステムが欲しいってなると、TeamsはBot用のTeamsアカウント多くなりそうで大変そう
    • SlackはApp Bot作成できるのでそこは大丈夫だけど

さいごに

ここまで読んでくださってありがとうございました!
今回はBoxにアクセスをしなくても、TeamsからBox AI for Hubsを利用できるように構築してみました!
まだまだ改善の余地はありますし、やってみたブログなので処理も省かせて頂きました。(例えば検索後の会話機能とか)
このシステムは他のお客様でも需要あるのかなと思うので色々と試してご案内出来ればなと思います。
ラストのネタは何にしよう。…また会う日まで

俊介

初めまして!新米エンジニアの丸林です!将来はスペシャリストと言えるように頑張ります!!少しでも皆様に良い情報をお届け出来るように心がけて行きますのでよろしくお願い致します!!