このエントリは2026/06/16現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容からの乖離が発生する可能性があります。
問い合わせ
前回の主とは違ういつもの人から以下のような問い合わせが届いた。Logic Appsが相当お好きな様子。
社内の申請フォームに添付されたファイルを、Logic AppsからBox にアップロードする仕組みを作りたい。ただ、現在の我々の環境では、Logic Appsのコネクターがサポートする認証方式が利用できないことがわかった。そのため、Azure Functions(以下、Functions)でトークンを取得し、そのトークンを使ってLogic AppsからBoxにアクセスしようと考えている。実現性を確認したい。
これだけだとよくわからんので、もうちょっと突っ込んでみた。前提・条件は以下のとおりである、とのこと。
- FunctionsはPythonで書きたい
- コストはできるだけ低く、無料枠があると非常にうれしい
- 処理規模は 月2万回くらい、1リクエストあたり10 KB 程度
- Logic Appsのリトライは最大3回という前提で組んでいる
- Boxは社内的にOktaのSSOと統合済み
- Boxにアップロードするファイルの最大サイズは、基本は数MB 、まれに数十MBのものもある
- Boxに「誰として」アップロードするかは決めていない
「いろいろツッコミどころがあるなぁ」と感じられる内容ではある。
誤解されているところ
まず、最初に誤解を解いておかねばならないので、以下を伝えておいた。
1. トークンをLogic Appsに渡すのは厳禁
トークンを証跡などに残すべきではない。Functionsで閉じる実装にすべきなので、トークン取得とアップロードは分けずに、Functionsでまとめるべき。
2. Function appはPythonで書きたい
Pythonを使いたい時点で様々な制約が入ってくる。Pythonで新規に作るなら、従来Consumptionではなく、Flex Consumptionを選択せざるを得ない。
- FunctionsでのPythonランタイムのサポートはLinuxのみ
- Linux Consumption planは2028/09/30に退役予定
- end-of-lifeのv3ランタイムをLinux Consumptionで動かしているアプリは2026/09/30以降に停止
Azure Functionsでサポートされている言語 / Supported languages in Azure Functions
https://learn.microsoft.com/azure/azure-functions/supported-languages
Azure Functions従量課金プランのホスティング (レガシ) / Azure Functions Consumption plan hosting (legacy)
https://learn.microsoft.com/azure/azure-functions/consumption-plan
3. Logic Appsの最大リトライ回数
どこから3回という数字が出てきたのかわからないが、ドキュメントには以下のような記述があるので、多くの場合はデフォルトで最大4回。
ほとんどの操作では、既定の再試行ポリシーは指数間隔ポリシーです。これは、”指数関数的に増加する” 間隔で最大 4 回の再試行を送信します。 これらの間隔の増加係数は 7.5 秒であり、下限と上限はそれぞれ 5 秒と 45 秒です。 いくつかの操作では、固定間隔ポリシーなど、別の既定の再試行ポリシーが使用されます。 詳細については、既定の再試行ポリシーの種類を確認してください。
For most operations, the Default retry policy is an exponential interval policy that sends up to 4 retries at exponentially increasing intervals. These intervals scale by 7.5 seconds but are capped between 5 and 45 seconds.
Azure Logic Apps におけるエラーと例外の処理 / Handle errors and exceptions in Azure Logic Apps
https://learn.microsoft.com/azure/logic-apps/error-exception-handling#retry-policy-types
これらの情報を伝えつつ、以下の詳細コメントを提示した。
コメントしたこと
Box認証はサーバー間向けのClient Credentials Grant(CCG)または JWT
トークンエンドポイントは以下。
POST https://api.box.com/oauth2/token
アップロードは「サービスアカウントとして行う」のが運用上シンプル。アクセストークンの有効期間は 60 分なので、約 50 分を目安にキャッシュ更新する。
もし申請者本人のBox権限でアップロードする必要があるなら、本人が明示的にBox / Oktaで認可するOAuth 2.0方式にするのか、Box管理者が承認したPlatform Appによるuser impersonation方式にするのかを先に決める必要がある。
申請者本人のBox権限でアップロードしたい場合、実装上は2種類の方式に分かれる。
| 方式 | 内容 | 適しているケース |
|---|---|---|
| ユーザー同意型 OAuth 2.0 | 申請者本人がBox / Oktaのログイン画面で認可し、Functions側がユーザーごとのrefresh tokenを管理する | 本人の明示的な同意を必須にしたい場合、外部ユーザーや部門横断の権限境界を厳密に扱いたい場合 |
| 管理者承認型 user impersonation | Box管理者がPlatform Appを承認し、Functionsが対象ユーザーのuser access tokenを取得、または enterprise tokenにAs-Userヘッダーを付けて呼び出す | 社内のmanaged userに限定し、無人処理として本人権限相当でアップロードしたい場合 |
Logic Appsからの自動処理として継続運用するなら、管理者承認型user impersonationが現実的な選択肢。これは「Logic AppsがユーザーのBoxパスワードやtokenを渡す」方式ではなく、Functions側のBox Platform Appが管理者承認を受け、申請者とBox user IDの対応を検証したうえで、対象ユーザーとして Box API を呼び出す、というもの。
Okta SSOはブラウザアクセスでの対話フローのみ
Functionsからのバックエンド自動処理では、Box Developer Consoleで構成したJWT / CCGを使ってBoxのtoken endpoint からトークンを取得する、と考えるべきである。ただし、SSOの有無に関わらず、Box管理者によるPlatform App の承認、scope、app accessの設定は確認が必要。
シークレットはLogic Appsには絶対渡さない
Boxのclient_secretはApp Settings+Key Vault参照(Managed Identity) で管理。Logic Appsのフロー変数やログには載せてはいけない。
アップロードはサイズで自動分岐
BoxのDirect Uploadは50 MBまでで、大きいファイルはChunked Uploadを使う必要がある。「まれに数十 MB」があるとのことなので、サイズで自動的に切り替えるロジックを入れて必要がある。アップロード先のホストも通常APIのapi.box.comではなくupload.box.comと違いがあるので注意が必要。
Functions の HTTP トリガーは認証必須+冪等設計
Logic Appsからの呼び出しであることを確認できるよう、Managed Identityによる認証を設定する。リトライ回数を断定せず、同じリクエストが複数回来ても二重アップロードにならないよう冪等性を担保する。
API 利用方針
1. 申請者の識別でLogic Appsの入力値だけを信用してはいけない
Logic Appsから以下の値
- 申請者のメールアドレス
- Entra IDのobject ID
- 申請レコードID(社内申請フォーム側で採番される業務ID)
- 添付ファイル情報
を渡すことにしたとしても、Box user IDはクライアントから渡すのではなく、Functions側で管理している対応表からLookupするべきである。対応表がない場合のみ、管理者承認済みのBox tokenで
GET https://api.box.com/2.0/users?filter_term=<email>
を呼び、Box managed userを解決する。メールアドレスの重複、退職済みユーザー、外部ユーザーはエラーとして扱う。
2. Box token はユーザー単位で発行し、短時間だけキャッシュ
CCGを使う場合は
POST https://api.box.com/oauth2/token
に
grant_type=client_credentialsbox_subject_type=userbox_subject_id=<box_user_id>
を指定してuser access tokenを取得する。JWT を使う場合は、JWT assertionのsubjectをBox user IDにしてuser tokenを取得する。
どちらもBox管理者によるアプリ承認と、必要なscope / app access / advanced featureの有効化が前提。tokenは60 分有効なので、ユーザー単位に50分弱を目安にキャッシュするようにしておく。
3. アップロードAPIはuser tokenで呼び出す
50 MB 以下は
POST https://upload.box.com/api/2.0/files/content
をuser access tokenで呼ぶ。Box側ではそのユーザーの権限でアクセス判定されるため、申請者がアップロード先フォルダーに権限を持たない場合は403で失敗する。50 MBを超える可能性がある場合は、Chunked Uploadのupload session作成、分割アップロード、commitの流れに切り替える必要がある。
4. As-User ヘッダーは例外的に使う
enterprise token に As-User: <box_user_id> を付ける方式もあるが、これはBoxアプリ側でAs-User利用が明示的に許可されている場合に限る。監査や権限境界を明確にしやすいのはuser access token方式なので、まずはuser token発行を標準案にする。
5. 監査ログと冪等性を必ず残す
アップロードごとに申請レコードID、Logic Apps実行ID、Box user ID、Box file ID、アップロード先 folder ID、token 発行方式、処理結果を保存しておく。同じ申請レコードIDが再実行された場合は、既存のBox file IDを返すか、新版としてアップロードするかを仕様で決めておく。

この方式にすると、Box側の権限チェックは申請者本人の権限で行われる一方、実装・運用はサービスアカウント方式より重くなる。特に「誰をどのBox user IDとみなすか」「アップロード先フォルダーを本人が持っていない場合どうするか」「退職・異動で権限が消えた場合どうするか」は、API 実装前に業務ルールとして決めておく必要がある。
Power Automateだったら?
1) Cloud flow
考え方はLogic Appsと同じなので詳細は省略する。
2) Desktop flow
Desktop フローの場合は、Power Automate Desktopが「ローカル操作・ファイル取得」、Azure Functionsが「Box 認証・アップロード API 実行」を担当するように責務を分ける。具体的には、Desktop フローから Functions を HTTP 呼び出しし、Functions 側でBox token 取得 + upload + 監査ログ + 冪等性管理まで完結させる。大きいファイルがあり得るなら、Desktop フロー → Blob → Functions → Box の二段階アップロードにする。
a) Desktop フローから Functions を HTTP 呼び出し、直接Boxに送信する場合
Power Automate DesktopのHTTPアクションは、POST / GET などの HTTP request と custom headers / request body を送信できるので、Desktop フローでローカルファイルを読み取り、Functionsに送信する。
HTTP アクション / HTTP actions
https://learn.microsoft.com/power-automate/desktop-flows/actions-reference/web
例:
{ "requestRecordId": "REQ-20260616-001", "applicantEmail": "user@example.com", "fileName": "sample.pdf", "fileContentBase64": "<base64>"}
Functionsでは以下を実現できるように実装しておく。
- 呼び出し元認証
- 申請レコードIDによる冪等性チェック
- Box token 取得
- Box upload API 呼び出し
- Box file ID / version ID を返却

b) 大きいファイルがあり得るなら、Desktop フロー → Blob → Functions → Box の二段階アップロード
Desktop フローから大きなファイルを直接 Functions に投げると、base64 化でサイズが増え、HTTP timeout やメモリ負荷も増えるため、以下のような構成が推奨される。この構成では、Desktop フローは Box tokenもBox APIも扱わず、ローカルファイルとAzure Blob Storageへの一時アップロードのみ。
さらにFunctionsはファイル本体を長時間受け続ける必要がなく、BlobからBoxへストリーミングまたはchunked uploadできる。このときに使うBlob storageはAzure Functionsでデフォルトで使うものでよい。
- FunctionsがPower Automate Desktopに 短時間だけ有効なBlob upload用SAS URLを発行
- DesktopフローがそのURLにファイルをアップロード
- Blob の着信をトリガーにして Functions が Box へアップロード

この方式では、以下のようなFunctionを作成することになるだろう(2個は必須、1個は任意)。
| Function | Trigger | 役割 |
|---|---|---|
| Upload Init API | HTTP trigger | Desktop フローを認証し、申請レコードID・ファイル名・サイズを検証し、Blob upload 用の短時間 SAS URL を返す |
| Blob Processor | Blob trigger または Event Grid trigger | Blob 着信を検知し、Box token を取得して Box upload API を実行する |
| Status API (Optional) | HTTP trigger | Desktop フローや運用者が correlationId / 申請レコードIDで処理結果を確認する |
注意が必要な点は以下のあたり。
- SAS URLはAzure Blob Storageへの一時アップロード権限であり、Box APIのtokenではない。
- Box tokenはFunctions内でのみ取得・使用し、Desktopフローには渡さない。
- Boxのchunked uploadは、Functions→Box間の処理であり、Desktopフロー → Blob Storage間のアップロードとは別。
- Blob着信後のBox uploadは非同期での動作なので、Desktopフローは即時にBox file IDを受け取れない場合がある。必要であればStatus APIを用意し、処理完了をポーリングする仕組みが必要。
- Blob trigger / Event Grid triggerの重複実行に備え、申請レコードID・blobName・correlationIdで冪等性を担保することを推奨する。
- 一時Blobは処理成功後に削除するか、短い lifecycle ruleで自動削除する。ただしFunctionsランタイムが使う azure-webjobs-* などのコンテナーとは別のコンテナーを使う必要がある。
ライフサイクル管理ポリシーを構成する / Configure a lifecycle management policy
https://learn.microsoft.com/azure/storage/blobs/lifecycle-management-policy-configure