Microsoft Teams カスタムアプリのmanifest JSONがアップロードできない時の解決策と検証手順(Developer Portal・Teams Toolkit・m365 CLI対応)

Teams カスタムアプリのテスト用アップロード(サイドロード)で「アプリが一覧に出ない」「Something went wrong」だけで原因が掴めない——そんなときは、manifest.json の“事前検証”と“切り分けの型”を作るのが最短ルートです。本記事では失敗パターンを体系化し、公式ツールでの静的検証から、ZIP パッケージ構成、権限・ドメイン設定、ログ採取、CI 自動化まで、公開前に不具合を潰し切るための実践手順を詳説します。

目次

現象の整理:なぜ「アップロードに失敗する」のか

Teams へのカスタムアプリのサイドロードが失敗する主因は、概ね次の5領域に収斂します。

  • manifest.json の不備(必須フィールド欠落、誤値、スキーマ不整合、バージョン不一致、JSON 文法エラー)
  • ZIP パッケージ構成ミス(ルート直下に3ファイルが無い、アイコンサイズやファイル名不一致、余計なフォルダ)
  • テナント/クライアント側の制約(カスタムアプリ許可ポリシー、App Permission/Setup Policy、クライアント差分)
  • ホスト側設定の欠落(validDomains 不備、CSP/Frame-ancestors 不備、HTTPS 証明書、SSO 連携の整合性)
  • キャッシュ/バージョン更新忘れ(version を上げていない、アイコンや manifest のキャッシュが残存)

「曖昧なエラー」で止まる場合でも、以下の“事前検証 & 切り分け”の型に沿えば、原因領域を高速に特定できます。

静的検証で落とせる不具合はすべて落とす

まずは、コードを書き換える前に manifest を機械的に検証します。手元の感覚では、サイドロード失敗の半数以上は静的検証だけで潰せます。

公式ツールでのバリデーション

  • Teams Toolkit for Visual Studio Code:コマンドパレットから Validate Manifest。VS Code 上でスキーマに基づく型チェックと必須項目の検証を行います。
  • Microsoft 365 CLI(m365):ターミナルで manifest を検証できます。
m365 teams manifest validate --path ./manifest.json
  • Developer Portal(旧 App Studio):App validation で同等の検証と、アイコンのプレビュー、フィールド単位のエラー表示が可能です。

VS Code の JSON スキーマ検証を効かせる

manifest の先頭に $schema を付けると、エディタが補完と型検証を行います(例)。

{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
  "manifestVersion": "1.16",
  "id": "00000000-0000-0000-0000-000000000000",
  ...
}

JSON にコメントや末尾カンマは使えません。// コメントや trailing comma は厳禁です。

ZIP パッケージの正解パターン

サイドロードに使う ZIP は ルート直下 に次の3ファイルが必要です。サブフォルダを切ると失敗します。

ファイル要件チェックポイント
manifest.jsonUTF-8(BOM 無推奨)必須項目・文法・スキーマ一致
カラーアイコンPNG 192×192 px透過・アルファ有は可/ファイル名は manifest と一致
アウトラインアイコンPNG 32×32 px単色推奨/背景は透過

ZIP 作成時は OS が混入させるリソース(例:macOS の __MACOSX、.DS_Store)を含めないでください。CI で zip を生成する場合は、明示的に3ファイルのみを追加しましょう。

アップロード前に必ず通す「プリフライト」チェックリスト

項目確認ポイントNG の例OK の例
manifestVersionサポート対象か(例:1.16)。古い 1.5 固定のまま1.16 に更新し、スキーマも一致
id / packageName / versionGUID/逆ドメイン表記/セマンティックバージョン。同じ version のまま再アップロード1.2.3 → 1.2.4 に更新
アイコンサイズ・拡張子・ファイル名一致。icon.png と manifest の参照名が不一致color.png / outline.png を参照どおり配置
developer名称・URL・プライバシー・利用規約の有無。空文字・ダミー URL実ドメインの https URL を設定
validDomainsタブ/認証/静的ファイルの配信元を網羅。末尾スラッシュ混在、ワイルドカード記法"example.com", "cdn.example.com"
scopes × 機能Bot/Tab/ME の対象範囲が矛盾しない。個人タブなのに team のみ["personal"] のみ、など整合
URL とプロトコルすべて https/リダイレクト先も含め https。http://localhost を本番 manifest に残存開発用と本番用で manifest を分離
SSO・AAD 整合webApplicationInfo の App ID / リソース一致。ID の桁数/ハイフン違いアプリ登録の値をコピペで正確に反映
CSP / フレーム埋め込みframe-ancestors に Teams 系ドメイン。X-Frame-Options: DENYContent-Security-Policy: frame-ancestors https://*.teams.microsoft.com https://*.office.com https://*.skype.com;

アップロード先での切り分け:Web ↔ デスクトップ ↔ モバイル

同じ ZIP を以下の順序で試験し、再現範囲を絞り込みます。

  1. Teams Web 版(ブラウザ):キャッシュや更新が軽く、エラーメッセージが出やすい。
  2. Teams デスクトップ クライアント:Web と差が出たらクライアント固有の要因(Runtime、キャッシュ)を疑う。
  3. Teams モバイル:モバイル限定の制約(一部タブの非対応など)を確認。

Web で成功・デスクトップで失敗の場合は、アプリのキャッシュクリア(サインアウト→再サインイン、アプリの再読み込み)とアイコンのバージョン衝突を最優先で確認します。

管理ポリシーと権限:サイドロードの前提を満たしているか

テナントやユーザーに適用されるポリシーで、カスタムアプリのアップロード自体が禁止されているケースは珍しくありません。次を確認してください。

  • アプリ許可ポリシー:サードパーティ/カスタムアプリが許可になっているか。
  • アプリ設定ポリシー(セットアップ):ユーザーが「アプリのアップロード」を実行できるか。
  • ストアの可視性:組織ストア/個人アップロードの経路が UI 上に表示されているか。

ポリシー変更は反映に時間差が出る場合があります。待機に頼らず、別ユーザー/別ポリシーでの再現比較が有効です。

「Something went wrong」から原因を掘り起こすログ採取術

  • 開発者コンソール:Teams で Ctrl + Alt + Shift + I。Console と Network で manifest 解析、アイコン取得、タブ URL ロード、CSP エラーの痕跡を確認。
  • ログの迅速保存:Ctrl + Alt + Shift + 1 でクライアントログをダウンロード(成功時は保存通知)。
  • Developer Portal の検証結果:App validation のエラーログでフィールド単位の指摘を参照。
  • Teams 管理センターのアプリ診断:アップロード失敗のイベント履歴と失敗コードを確認。

ログにはテナント情報や App ID が含まれるため、共有時は必ずマスクしましょう(ドメイン・GUID・内部 URL)。

代表的なエラーと即効リカバリー

症状一次切り分け修正方針(最短)
アプリが一覧に出ないポリシーで非表示/ZIP ルート構成ミス許可ポリシーの見直し、ZIP を 3ファイル直下で再作成
Something went wrong(汎用)manifest の JSON 文法/必須フィールドm365 と Toolkit の二段バリデーション、$schema 有効化
タブ起動で白画面CSP frame-ancestors/HTTPSTeams/Office ドメインを許可、自己署名証明書を回避
SSO が動かないwebApplicationInfo 不一致AAD App ID と Application ID URI を manifest と一致させる
アイコンだけ更新されないキャッシュ/version 未更新version をインクリメント、再サインイン・再読み込み

実例で学ぶ:最小構成 manifest(個人タブ)

{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
  "manifestVersion": "1.16",
  "version": "0.1.0",
  "id": "11111111-1111-1111-1111-111111111111",
  "packageName": "com.example.minimaltab",
  "developer": {
    "name": "Example Inc.",
    "websiteUrl": "https://www.example.com",
    "privacyUrl": "https://www.example.com/privacy",
    "termsOfUseUrl": "https://www.example.com/terms"
  },
  "name": { "short": "Minimal Tab", "full": "Minimal Personal Tab" },
  "description": {
    "short": "最小構成の個人タブ",
    "full": "検証用の最小構成個人タブ。HTTPS と validDomains のみを要件に含む。"
  },
  "icons": { "color": "color.png", "outline": "outline.png" },
  "accentColor": "#404040",
  "staticTabs": [{
    "entityId": "home",
    "name": "Home",
    "contentUrl": "https://app.example.com/home",
    "websiteUrl": "https://app.example.com",
    "scopes": ["personal"]
  }],
  "validDomains": ["app.example.com"]
}

この段階では Bot やメッセージ拡張は含めません。段階的に機能を増やすことで、障害範囲を限定できます。

CSP とフレーム埋め込みの落とし穴

Teams のタブは iFrame でホストされるため、Content-Security-Policy の frame-ancestors で Teams ドメインを許可していないと白画面になります。代表例:

Content-Security-Policy: frame-ancestors https://*.teams.microsoft.com https://*.skype.com https://*.office.com;

X-Frame-Options は DENY/SAMEORIGIN を使わないでください。Azure Front Door/CDN を挟む場合も、最終応答ヘッダに適切な値が出ているかを実応答で確認します。

ドメインと HTTPS:validDomains と証明書の整合

  • validDomains はホスト名だけを列挙(スキーム/パスは不要)。
  • ワイルドカード不可。*.example.com は使えないため、必要なサブドメインを明示列挙。
  • 本番は必ず公開 CA の証明書。自己署名は開発中のローカルでのみ許容に留める。
  • 認証用や静的配信用の別ホスト(auth.example.com、cdn.example.com)も漏れなく記載。

SSO(Azure AD)連携の“最低限”整えるチェック

  • manifest の webApplicationInfo.id は Azure AD アプリ登録の アプリケーション(クライアント)ID と一致。
  • 必要に応じて Application ID URI / スコープ名の整合(Graph や自前 API)。
  • タブ SSO のリダイレクト URI は SPA/WEB の正しい種類で登録。
  • 有効な発行者・オーディエンス(aud)で受け取れているかを実トークンで検証。

SSO 失敗は UI では“沈黙”しやすく、Network タブの 401/403 と WWW-Authenticate の詳細が頼りです。

Web とデスクトップで差が出るときの対策

  • キャッシュ:デスクトップはキャッシュが強く、version を上げずに差し替えると古い manifest が残りがちです。
  • ランタイム差:ブラウザ拡張や実験的フラグの影響で Web のみ挙動が変わることがあります。シークレットウィンドウで再現を確認。
  • OS 差:Windows/Mac で描画・フォント・IME の副作用が出ることも。最小構成のタブで比較検証しましょう。

CI による自動検証:人手のバラツキを排除する

手元での検証に加え、プルリクごとに manifest を自動検証・パッケージングすると、ヒューマンエラーを抑えられます。例(GitHub Actions):

name: Validate & Package Teams App
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Install m365 CLI
        run: npm i -g @pnp/cli-microsoft365
      - name: Validate manifest
        run: m365 teams manifest validate --path ./appPackage/manifest.json
      - name: Package zip
        run: |
          cd appPackage
          zip -j ../dist/app.zip manifest.json color.png outline.png
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: teams-app
          path: dist/app.zip

これでレビュー段階で 必ず検証が走り、ZIP も“正しい構成”で生成されます。

デバッグを加速する小技集

  • バージョンを自動採番:ビルド番号やコミットハッシュで version を毎回更新し、キャッシュ不整合を抑止。
  • 開発用 manifest と本番用 manifest を分離:validDomains と URL を切り替え、混入を防止。
  • 「最小再現」manifest を持つ:Bot/ME を外した最小タブで、環境依存とアプリ依存を切り分け。
  • ネットワーク記録の保存:HAR で 4xx/5xx、CSP ヘッダ、リダイレクトチェーンを共有できる形に。

よくある質問(FAQ)

Q. Developer Portal のプレビューでは動くのに、サイドロードで失敗します。

ZIP のルート構成とアイコン名一致、version 更新を確認。Developer Portal のプレビューは一部検証を“スキップ”するため、ZIP 作成ミスが温存されることがあります。

Q. validDomains に *.example.com は使えますか?

使えません。必要なサブドメインを個別列挙してください。

Q. http://localhost を使った開発は?

ローカル開発では許容される場面もありますが、本番 manifest からは除去し、HTTPS の動作確認を本番同等で先に済ませるべきです。

Q. 画像だけ更新が反映されません。

version を上げて再アップロードしてください。クライアントキャッシュにより、manifest の変更が流れてもアイコンだけ古いまま残ることがあります。

Q. エラーが英語で曖昧です。どこを見れば良いですか?

Ctrl + Alt + Shift + I の Console と Network に出る JSON パースエラーや CSP 違反、manifest.json 取得の 404/403 を優先的に確認します。

「チェックしたつもり」を無くす最終点検表(印刷推奨)

カテゴリ確認項目OK/NG
スキーマ$schema と manifestVersion が一致している
識別子id は GUID、packageName は逆ドメイン、version は更新
アイコン192/32px PNG、ファイル名と manifest の参照名が一致
ドメインvalidDomains にタブ/認証/CDN のホストを漏れなく列挙
HTTPS本番系 URL はすべて HTTPS(証明書は公開 CA)
機能と範囲Bot/Tab/ME の scopes が仕様と整合
SSOwebApplicationInfo と AAD 登録の ID/URI が一致
CSPframe-ancestors に Teams/Office/Skype ドメイン
ZIPルート直下が manifest.json / color.png / outline.png の 3ファイルのみ
ポリシーユーザーの許可ポリシーでカスタムアプリが許可

まとめ:サイドロードは「検証→打鍵→観察」の高速ループで

manifest の 静的検証(Teams Toolkit / m365)と ZIP 構成の固定化、Web→デスクトップ→モバイルの順に切り分ける型、そして CSP・validDomains・SSO の3点セット——この流れをひとつの“儀式”にすれば、曖昧なエラーで足止めされる時間は激減します。最後は CI に検証とパッケージングを任せ、人がやるのは原因仮説の立案と観察だけにしましょう。アップロード前に異常の種を取り除ければ、組織ストア公開や審査対応もスムーズに進みます。

付録:失敗しないためのテンプレートとコマンド集

最小タブの雛形(差分用)

{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
  "manifestVersion": "1.16",
  "version": "0.0.1",
  "id": "22222222-2222-2222-2222-222222222222",
  "packageName": "com.example.sanitycheck",
  "developer": {
    "name": "Sanity Co.",
    "websiteUrl": "https://sanity.example.com",
    "privacyUrl": "https://sanity.example.com/privacy",
    "termsOfUseUrl": "https://sanity.example.com/terms"
  },
  "name": { "short": "Sanity", "full": "Sanity Check Tab" },
  "description": { "short": "検証用", "full": "manifest チェック用最小タブ" },
  "icons": { "color": "color.png", "outline": "outline.png" },
  "accentColor": "#6264A7",
  "staticTabs": [{
    "entityId": "sanity",
    "name": "Sanity",
    "contentUrl": "https://sanity.example.com",
    "scopes": ["personal"]
  }],
  "validDomains": ["sanity.example.com"]
}

m365 CLI コマンド(よく使うもの)

# manifest の検証
m365 teams manifest validate --path ./manifest.json

# ZIP の作成(3ファイルのみ)

zip -j ./dist/app.zip ./manifest.json ./color.png ./outline.png 

問題報告時の秘匿ルール(セキュリティ)

  • 顧客ドメイン、GUID、内部 URL、トークン/クッキーは必ずマスク。
  • ログ/HAR は必要最小限を共有、保管期限と廃棄手順を明記。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次