mcpField
I
import-solution

import-solution

github.com/microsoftクラウド・DevOps1.1k162 回インストール今日更新
リポジトリ

import-solution について

ソリューション ZIP を対象の Dataverse 環境にインポートするスキルです。任意のステージングインポートで不足依存関係を確認でき、手動で環境間のプロモーションを行う Power Platform プロジェクト向けに、インポート後の対応事項も調べます。

主な機能

  1. 01`ImportSolutionAsync` による ZIP のインポート
  2. 02依存関係確認のためのステージングインポート
  3. 03対象環境の検証と確認
  4. 04接続参照やクラウドフローの問題を通知

活用シーン

  1. 01エクスポート済みソリューションを対象環境へインポート
  2. 02インポート前に不足依存関係を確認
  3. 03インポート後に接続のバインドが必要なソリューションを特定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/microsoft/power-platform-skills --skill import-solution

インストール後、Claude Code で/import-solutionを入力して読み込まれたか確認します。

同梱ファイルscripts/validate-import.js

詳細

プラグインの確認:node "${PLUGIN_ROOT}/scripts/check-version.js" を実行します。メッセージが出力された場合は、続行する前にユーザーに表示してください。

import-solution

ImportSolutionAsync を使用して、ソリューションの ZIP ファイルをターゲットの Dataverse 環境にインポートします。コミット前に不足している依存関係を確認するため、StageSolution を使用したオプションのステージングインポートにも対応しています。

前提条件

  • PAC CLI がインストールされ、ターゲット環境に対して認証済みであること
  • Azure CLI がインストールされ、ログイン済みであること
  • ソリューションの ZIP ファイルがディスク上に存在すること(export-solution によって生成)

フェーズ

フェーズ 0 — ALM 計画ゲート

plan-alm が最初の入口です。 ユーザーが ALM に関する意図(昇格/出荷/デプロイ/CI-CD のセットアップ/ステージングへの移動/本番環境への反映)を示した場合、オーケストレーター(/power-pages:plan-alm)を最初に実行する必要があります。このフェーズ 0 はそれを徹底するもので、計画がない場合に処理を通さないように設計されています。ユーザーが一度の確認を無期限に却下できるようにするためのものではありません。

スキップルール。 このスキルが 実行中の plan-alm オーケストレーションの一部として呼び出された場合は、フェーズ 0 全体をスキップしてフェーズ 1 に進みます。ゲートヘルパーはこれを inExecution ブロックで示します。次の場合は何も表示せず、そのままフェーズ 1 に進みます。

inExecution.status === "active"

ヘルパーは docs/.alm-plan-data.json の情報からこの状態を計算します。PLAN_STATUS === "In Execution" かつ LAST_INVOCATION_AT が過去 60 分以内の場合です。check-alm-plan.js は実行中の計画を検出するたびに LAST_INVOCATION_AT を自動的に更新します。そのため、チェーン内の各スキルが次のスキルに向けてチェーンを維持できます。1 時間を超えるデプロイ(deploy-pipeline だけでもステージごとに最大 60 分かかる場合があります)でも、チェーンが誤って非アクティブと判定されることなく、ウィンドウ内で処理を継続できます。停滞したチェーン(ハートビートが 60 分を超えて更新されていない場合)は stale-heartbeat として再分類され、フェーズ 0 のゲートが通常どおり作動するため、放棄された計画がユーザー確認を暗黙に回避することはありません。

inExecution.status が "active" 以外("not-running"、"stale-heartbeat"、"no-plan")の場合は、以下のフェーズ 0 のゲートフローを実行します。残りのヘルパーフィールドに応じて分岐してください。

ステップ 1 — ゲートヘルパーを実行します。

node "${PLUGIN_ROOT}/scripts/lib/check-alm-plan.js" --projectRoot "."

ヘルパーは { exists, deferred, stale, staleness: { reason, detail }, generatedAt, planStatus, ... } を含む JSON を返します。鮮度チェックも行う場合は、フェーズ 1 で取得した後に --envUrl、--token、--solutionId を渡してください。それ以外の場合、ヘルパーは存在確認のみを行います。これは以下のゲート判定には十分です。

ステップ 2 — 結果に応じて分岐します。

結果動作
deferred: trueユーザーはこのプロジェクトで ALM を明示的に延期しています(.alm-deferred マーカーがあります)。何も表示せず、フェーズ 1 に進みます。催促しないでください。
exists: falseユーザーはまだ plan-alm を実行していません。ステップ 3 を参照してください。
exists: true, stale: false計画は最新です。何も表示せず、フェーズ 1 に進みます。
exists: true, stale: true(理由:solution-modified)計画の生成後にソリューションが変更されました。ステップ 4 を参照してください。

ステップ 3 — 計画がありません。 ユーザーに次のように伝えます。

"このプロジェクトには ALM 計画がありません。/power-pages:plan-alm が計画を作成します。プロジェクトの状態を検出し、昇格戦略(PP Pipelines と手動のエクスポート/インポート)について確認したうえで、適切なスキル(このスキルを含む)を適切な順序でオーケストレーションします。今すぐ plan-alm を実行しますか?"

🚦 ゲート(意図 · import-solution:0.no-plan): check-alm-plan.js が exists:false を返した場合に作動する、フェイルクローズ方式の入口ゲートです。ヘルパースクリプトによって制御されます。

AskUserQuestion:

質問ヘッダー選択肢
最初に /power-pages:plan-alm を実行しますか?ALM 計画ゲートはい — 今すぐ /power-pages:plan-alm を実行する(推奨)、計画なしで続行する(上級者向け — 理解したうえで実行します)、キャンセル
  • はい(推奨) → /power-pages:plan-alm を呼び出します。計画を作成して戻ります。plan-alm はプランナーであり、デプロイは行いません。その後、このスキルはフェーズ 0 の確認を再実行し(この時点で exists:true)、フェーズ 1 に進みます。
  • 計画なしで続行する → BYPASSED_PLAN_GATE = true を設定し、フェーズ 1 に進みます。
  • キャンセル → 正常に終了します。

ステップ 4 — 古い計画。 ユーザーに次のように伝えます。

「{generatedAt} 時点の ALM プランが存在しますが、その後、ソースソリューションが変更されています({solution.modifiedon} 時点)。コンポーネントが変更された可能性があります。plan-alm を再実行すると、分析とレンダリング済み HTML が更新されます。」

🚦 ゲート(意図 · import-solution:0.stale-plan): check-alm-plan.js が stale:true を返した場合に、フェイルクローズで処理を開始するゲートです。ヘルパースクリプトにより実行されます。

AskUserQuestion:

質問ヘッダー選択肢
先にプランを更新しますか?ALM プランの鮮度更新 — /power-pages:plan-alm を再実行(推奨)、既存のプランで続行、キャンセル
  • 更新(推奨) → /power-pages:plan-alm を実行します。完了後、Phase 0 のヘルパーをもう一度実行して鮮度を確認します。それでも古い場合は詳細を提示し、Phase 1 に進みます(無限ループにはしません)。
  • 続行 → STALE_PLAN_ACK = true を設定して Phase 1 に進みます。
  • キャンセル → 正常に終了します。

このゲートがある理由。 import-solution を直接呼び出すと、オーケストレーターによるデプロイ戦略の選択や、インポート後の検証手順を経ずに、zip がターゲット環境にデプロイされます。このスキルを単独で実行するユーザーは、段階的インポートの依存関係チェックを省略しがちで、ターゲット環境の環境変数オーバーライド値を見落とし、どの環境にどのアーティファクトバージョンをデプロイしたかをプランに記録できません。このゲートにより、plan-alm が実行され(デプロイ戦略が選択され、段階ごとの値が deployment-settings.json に記録され、デプロイを再現できる状態になる)、またはユーザーが明示的にその手順を省略することを選択した状態になります。

Phase 1 — 前提条件の確認

この Phase の開始時に、すべてのタスクをあらかじめ作成します。

作成するタスク:

  1. 「前提条件を確認」
  2. 「ソリューションファイルを特定」
  3. 「インポートを構成」
  4. 「ソリューションをステージング(依存関係チェック)」
  5. 「ソリューションをインポート」
  6. 「インポートを検証」
  7. 「クラウドフローを検出」
  8. 「概要を提示」

注:インポートが AttachmentBlocked エラーで失敗した場合、Phase 5b の修復フローがその場で実行されます。追加のタスクは必要ありません(「ソリューションをインポート」タスク内で続行されます)。ソリューション zip に Workflows/*.json ファイルが含まれていない場合、「クラウドフローを検出」タスクは自動的にスキップされます。

手順:

  1. pac env who を実行し、environmentUrl を抽出します(これがターゲット環境であることを確認してください)。
  2. az account get-access-token --resource "{environmentUrl}" --query accessToken -o tsv を実行し、トークンを取得します。
  3. API アクセスを確認します:GET {environmentUrl}/api/data/v9.2/WhoAmI
  4. ターゲット環境の URL を提示し、続行する前にこれで正しいかユーザーに確認します。

重要:ターゲット環境をユーザーに確認してください。誤った環境へのインポートは、業務に支障をきたす可能性があります。

いずれかのチェックに失敗した場合は停止します(${PLUGIN_ROOT}/references/dataverse-prerequisites.md を参照してください)。

Phase 1.5 — 最新の ALM ドキュメントを確認

参照:${PLUGIN_ROOT}/references/alm-docs-grounding.md

この手順は約30秒以内に完了してください。MCP の検索/取得でエラーが発生した場合は、1行のメモを記録して続行します。このスキルはオフラインでも実行できる必要があります。

  1. 次のクエリで microsoft_docs_search を実行します:Power Pages solution import staging missing dependencies ImportSolutionAsync ALM。
  2. https://learn.microsoft.com/en-us/power-platform/alm/solution-concepts-alm(および段階的インポートまたは依存関係の処理に関する関連ページを最大1件)を microsoft_docs_fetch で並行して取得します。
  3. ステージングと直接インポート、依存関係の解決、コンポーネントレベルのエラー処理について、Microsoft Learn の最新の記載内容を1段落にまとめます。${PLUGIN_ROOT}/references/solution-api-patterns.md と比較し、ImportSolutionAsync/StageSolution のシグネチャに相違があれば指摘します。
  4. この要約を Phase 2 以降の判断に活用します。スキルの動作を黙って変更してはいけません。相違があれば、Phase 4(実際のインポート)の前に、注意事項としてユーザーに伝えます。
フェーズ 2 — ソリューションファイルの特定
  1. 引数として zip のパスが指定されている場合は、そのパスを直接使用します

  2. それ以外の場合は、ソリューションの zip を検索します:glob('**/*.zip', { ignore: ['**/node_modules/**'] })

  3. 見つかった各 zip に solution.xml が含まれていることを確認します:

    • Bash を使用します:unzip -l "{zipPath}" 2>/dev/null | grep -qi solution.xml
  4. 有効な zip が複数見つかった場合は、ユーザーに選択を求めます:

    🚦 ゲート(plan · import-solution:2.multiple-zips): プロジェクトルート配下で、有効なソリューション zip が複数見つかりました。ユーザーにインポートする zip を選んでもらいます。キャンセルした場合は、ターゲット環境とのやり取りを行う前に終了します。

    有効な zip ごとに 1 つの選択肢を用意して AskUserQuestion を使用します。ユーザーが適切な zip を特定できるよう、ファイル名、サイズ、更新日時を表示します(通常、最も新しいエクスポートが対象です)。

  5. 有効な zip が見つからない場合は、処理を停止して説明します。先に export-solution を実行するか、zip のパスを指定してください

ステップ 5a — インポート前のコンテンツ検査(zip の確認後、ユーザーに提示する前に実行):

インポート後に手動で必要となる作業を示すため、zip を検査します。次のすべてのチェックを順番に実行します:

# 1. Connection references (require user-binding post-import)
unzip -p "{zipPath}" customizations.xml 2>/dev/null | grep -c '<connectionreference '

# 2. Bots (require republishing in target environment)
unzip -l "{zipPath}" 2>/dev/null | grep -qi "bots/" && echo "found" || echo "none"

# 3. Cloud flows with hardcoded environment URLs (will silently fail in target)
unzip -p "{zipPath}" "Workflows/*.json" 2>/dev/null | grep -o '"organization":\s*"https://[^"]*"' | head -5

結果から postImportWarnings リストを作成します:

検出内容表示する警告
connectionreference の数が 0 より多い"⚠️ 接続参照:このソリューションには N 個の接続(Dataverse コネクタなど)が含まれています。インポート後、各接続参照をターゲット環境の有効な接続にバインドする必要があります。そうしない場合、それらに依存するクラウドフローは無効になります。"
bots/ フォルダーが存在する"⚠️ Copilot Studio ボット:このソリューションにはボットが含まれています。プロビジョニングを完了するには、インポート後、ターゲット環境でボットを再公開する必要があります。"
ハードコードされた組織 URL が見つかった"⚠️ ハードコードされた環境 URL:クラウドフローにハードコードされた環境 URL({foundUrl})が含まれています。このため、インポート後もフローはソース環境を呼び出します。ターゲット環境でフローを編集し、URL を更新してください。"

postImportWarnings が空でない場合は、すべての警告を zip の詳細を提示する前にインラインで表示します(AskUserQuestion は使用しません)。ユーザーが確認する前にこれらの警告を確認できるようにします。

選択した zip ファイルの詳細(名前、サイズ、パス)と、インポート前の警告があればそれを提示し、ユーザーに確認を求めます。

フェーズ 3 — インポートの設定

ステップ 3.0 — バージョンの不一致に関する注意(プロンプトを表示する前に読み取り専用チェックを実行)。

ステージングインポートまたは直接インポートについてユーザーに尋ねる前に、ターゲット環境に問い合わせ、zip に含まれるソリューションの一意の名前を持つソリューションを検索して、インストール済みバージョンと zip 内のバージョンを比較します。ユーザーが操作を進める前に「インストール済みのものと同じバージョンをインポートしようとしています」と知らせることが目的です。これは手動のエクスポート/インポート手順で最もよく見られる、気づきにくい失敗パターンです。昇格可能であることが明確な成果物を作成するのは、ソース側でバージョンを上げる操作だからです。

  1. zip 内の solution.xml から zip の uniqueName と version を抽出します:

    unzip -p "{zipPath}" solution.xml 2>/dev/null | head -50
    

    XML から <UniqueName> と <Version> を解析します。ZIP_SOLUTION_NAME および ZIP_SOLUTION_VERSION として保存します。

  2. ターゲット環境にインストール済みのソリューションを問い合わせます:

    GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{ZIP_SOLUTION_NAME}'&$select=solutionid,uniquename,version,ismanaged
    

    結果を INSTALLED として保存します(フィルターの value 配列が空の場合は null)。

  3. 共有ヘルパーを使ってバージョンを比較し、その結果に応じて分岐します。

    前提条件 — INSTALLED が null の場合、このステップ全体をスキップします。 これは初回インストールのケースです。ターゲットには比較対象がありません。'{INSTALLED.version}' に null を代入してヘルパーを呼び出してはいけません。「version is required」エラーが発生し、エージェントが分岐処理を行えなくなります。下記のステージング/直接インポートのプロンプトに進み、初回インストールとして扱います。

    それ以外の場合(INSTALLED が null ではない場合)は、バージョンを比較します。

    重要:バージョン文字列をそのまま >、<、=== で比較してはいけません。Dataverse のバージョンは 4 セグメントの整数タプル(1.0.0.9 と 1.0.0.10 など)であり、文字列として比較すると 1.0.0.10 が 1.0.0.9 より小さいと判定され、1 日 10 回目のデプロイでバージョン不一致の判定が逆転します。代わりに、標準のヘルパーを使用してください:

    node -e "console.log(require('${PLUGIN_ROOT}/scripts/lib/bump-solution-version').compareVersions('{ZIP_SOLUTION_VERSION}', '{INSTALLED.version}'))"
    

    このヘルパーは、ZIP < INSTALLED の場合は -1、等しい場合は 0、ZIP > INSTALLED の場合は 1 を返します。bumpPatchSegment と同じセグメント単位の整数ルール(ゼロ埋め、最大4セグメント、整数以外は拒否)です。標準出力を取得してトリムし、整数を VERSION_CMP に保存します。ヘルパーが例外をスローした場合(どちらかのバージョンの形式が不正な場合)は、標準エラー出力をユーザーに提示して停止します。バージョン比較は安全にインポートするための前提条件です。

    INSTALLEDVERSION_CMP動作
    null(ヘルパーは呼び出されません。上記の前提条件を参照)初回インストールです。以下のステージング/直接インポートのプロンプトに進みます。
    null 以外1(zip のほうが厳密に大きい)通常のアップグレードです。次のように通知します:"ターゲットには v{INSTALLED.version} がインストールされています。この zip は v{ZIP_SOLUTION_VERSION} です。インポートするとアップグレードされます。"
    null 以外0(zip とインストール済みのバージョンが同じ)以下の警告を表示します(同一バージョンの不一致。管理対象/非管理対象の両方に適用されます)。
    null 以外-1(zip のほうが厳密に小さい)以下の警告を表示します(ダウングレード。管理対象/非管理対象の両方に適用されます)。

    🚦 ゲート(同意 · import-solution:3.0.version-skew): ターゲット上のソリューションより、zip のバージョンが同じか低くなっています。インポートすると、予測不能なアップグレード動作が発生します。ソースの export-solution では、エクスポートのたびにバージョンを上げることになっています。バージョンを上げて再エクスポートするか、強制的にインポートするか、キャンセルしてください。

    警告プロンプト — AskUserQuestion:

    "ターゲット環境には、すでに {ZIP_SOLUTION_NAME} v{INSTALLED.version} がインストールされています({INSTALLED.ismanaged ? '管理対象' : '非管理対象'})。これからインポートする zip のバージョンは {ZIP_SOLUTION_VERSION} です。

    同じバージョンまたは低いバージョンをインポートするのは不確実です。

    • 管理対象:アップグレードの系譜がないため、プラットフォームは内部のヒューリスティックに応じてインポートを適用または拒否する場合があります。
    • 非管理対象:動作は OverwriteUnmanagedCustomizations: true のみに依存します。また、zip 内のコンポーネント ID と一致するターゲット内の編集内容は、変更を示すバージョンがないまま、通知なく上書きされます。

    /power-pages:export-solution は、zip の生成前にソースのバージョンを必ず上げます(2026-05-25 以降)。この zip がその変更より前に生成された場合は、再エクスポートすると、確実に大きいバージョンになります。

    どのように進めますか?"

    質問ヘッダー選択肢
    どうしますか?バージョンの不一致バージョンを上げて再エクスポートする(推奨 — /power-pages:export-solution を呼び出します)、このままインポートする(自己責任で進めてください)、キャンセル
    • 再エクスポート:/power-pages:export-solution を呼び出します。完了後、新しく生成された zip を使って、フェーズ2からこのスキルを再開します。
    • このままインポート:SKEW_ACK = true を設定し、以下のステージング/直接インポートのプロンプトに進みます。監査証跡として、確認したバージョンの不一致を docs/alm/last-import.json の versionSkew: { zipVersion, installedVersion, isManaged, acknowledged: true } に記録します。
    • キャンセル:スキルを正常に停止します。ステップ3.0ではターゲット環境への書き込みは行われていません。読み取り専用です。
  4. 比較結果が通常のアップグレードの場合(またはソリューションがまだインストールされていない場合)は、プロンプトを表示せずに続行し、最終的な docs/alm/last-import.json にバージョン差分を記録します。

🚦 ゲート(計画 · import-solution:3.config): ステージングと直接インポート、上書きオプションを選択します。キャンセルすると、ターゲット環境に変更を加える前に終了します。

AskUserQuestion を使ってユーザーに質問します:

重要な判断ポイント:ステージングと直接インポート

  • ステージング(管理対象ソリューションに推奨):まず StageSolution を実行して、依存関係の不足を確認します。インポートを確定する前に問題を表示します。ステージングの手順は完全に取り消し可能なため、より安全です。
  • 直接:ステージングを省略して、すぐにインポートします。より速い一方、依存関係が不足していると、インポートの途中で失敗する場合があります。 さらに、以下も確認します:
  • 管理対象外のカスタマイズを上書きしますか?(デフォルト:はい)— インポート先で同じコンポーネントがカスタマイズされている場合に必要です
  • インポート後にワークフローを公開しますか?(デフォルト:はい)
フェーズ 4 — ソリューションのステージング(条件付き)

フェーズ 3 でユーザーがステージングインポートを選択した場合にのみ、このフェーズを実行します。

${PLUGIN_ROOT}/references/solution-api-patterns.md のセクション 5a を参照してください。

  1. zip ファイルを Base64 エンコードします:
    node "${PLUGIN_ROOT}/scripts/encode-solution-file.js" --zipPath "{zipPath}"
    
  2. POST {envUrl}/api/data/v9.2/StageSolution を実行し、CustomizationFile: {base64} を指定します
  3. StageSolutionResults を解析します:
    • StageSolutionUploadId を抽出します(フェーズ 5 では、ファイルの再エンコードの代わりに使用します)
    • MissingDependencies 配列を確認します
  4. MissingDependencies が空でない場合:
    • 不足している依存関係を、それぞれの種類と名前とともに一覧表示します
    • ユーザーに確認します:「これらの依存関係がターゲット環境にありません。このまま続行しますか(失敗する可能性があります)、それともキャンセルして先に依存関係をインストールしますか?」
    • キャンセルする場合:停止し、不足している依存関係をインストールするよう案内します
  5. MissingDependencies が空の場合:「不足している依存関係は見つかりませんでした。インポートの準備ができました。」と報告します
フェーズ 5 — ソリューションのインポート

${PLUGIN_ROOT}/references/solution-api-patterns.md のセクション 5b を参照してください。

  1. リクエストボディを準備します(常に CustomizationFile を使用します — ImportSolutionAsync は StageSolutionUploadId を受け付けません):

    • zip をエンコードします:node "${PLUGIN_ROOT}/scripts/encode-solution-file.js" --zipPath "{zipPath}"
    • { CustomizationFile: "{base64}", OverwriteUnmanagedCustomizations: {choice}, PublishWorkflows: {choice} } を使用します
  2. POST {envUrl}/api/data/v9.2/ImportSolutionAsync を実行します

  3. AsyncOperationId と ImportJobKey を抽出します(注:フィールド名は ImportJobId ではなく ImportJobKey です)

  4. 次のように報告します:「インポートジョブを開始しました:{AsyncOperationId}。完了するまでポーリングしています……」

scripts/poll-async-operation.js を実行します:

node "${PLUGIN_ROOT}/scripts/poll-async-operation.js" \
  --asyncJobId "{AsyncOperationId}" \
  --envUrl "{envUrl}" \
  --intervalMs 8000 \
  --maxAttempts 75

ポーリング結果を処理します:

  • Succeeded:フェーズ 6 に進みます
  • AttachmentBlocked/エラーコード -2147188706 を伴う Failed:以下のフェーズ 5bに進みます
  • その他の Failed:エラーメッセージを表示し、インポートジョブを照会してコンポーネントレベルのエラーを確認し、停止します
  • Timeout:ユーザーに通知し、管理センターで確認するよう案内します
フェーズ 5b — 添付ファイルの制限を解決する(条件付き)

フェーズ 5 のポーリングが AttachmentBlocked(-2147188706、またはメッセージに AttachmentBlocked か not a valid type が含まれる)で失敗した場合にのみ、このフェーズを実行します。

5b.1 ソリューションの zip 内でブロックされている拡張子を特定する

zip 内のすべてのファイルを一覧表示し、重複のない拡張子を抽出します:

unzip -l "{zipPath}" | awk '{print $4}' | grep '\.' | sed 's/.*\.//' | sort -u

環境から現在ブロックされている添付ファイルの一覧を取得します:

pac env list-settings

出力内の blockedattachments 行を確認します。ここにはセミコロン区切りの一覧(例:ade;adp;js;zip;...)が含まれています。

共通部分を算出します。ソリューションの zip 内にある拡張子のうち、ブロックリストにも含まれているものを特定します。これらがブロック解除の必要な種類です。

5b.2 問題を説明する

ユーザーに次のように伝えます:

「ターゲット環境で、このソリューションに含まれる特定のファイル形式がブロックされているため、ソリューションのインポートに失敗しました。ソリューション内で現在ブロックされているファイル拡張子は次のとおりです:{comma-separated list}。これは環境レベルのセキュリティ設定です。このソリューションをインポートするには、これらの制限を一時的に緩和する必要があります。」

5b.3 許可を求める

🚦 Gate (同意 · import-solution:5b.blocked-attachments): AttachmentBlocked の修復に対応します。環境レベルの blockedattachments 設定を変更します(テナント全体に影響します)。PPAC から元に戻せます。失敗が発生するたびに、スキルの呼び出しごとに再度表示されます。 plan-alm の手動パスで複数のターゲットへのインポート(Staging の後に Production)を調整する場合、import-solution はターゲットごとに 1 回呼び出されます。同じ拡張子が両方のターゲットでブロックされている場合、別々の環境に対する別々のスキル呼び出しとなるため、ゲートはターゲットごとに 1 回表示されます。Staging への同意は Production には適用されません。

直ちに AskUserQuestion を呼び出してください。チャットメッセージとして提示してはいけません。スキルを続行する前に、ユーザーからその場で回答を得る必要があります。

質問ヘッダー選択肢
ソリューションに、この環境の添付ファイルのセキュリティ設定でブロックされているファイル形式({list})が含まれています。ソリューションをインポートできるように、これらの特定の形式のブロックを解除しますか?添付ファイル形式のブロック解除はい、このインポートでは {list} のブロックを解除する(推奨)、いいえ、環境設定を変更しない

「いいえ」の場合:停止し、次のようにユーザーに伝えてください。「これらのファイル形式がブロックされている間は、インポートを続行できません。手動でブロックを解除するには、Power Platform 管理センター → 環境 → {env} → 設定 → 製品 → 機能 → ブロックされた添付ファイルに移動してください。」

「はい」の場合:5b.4 に進みます。

5b.4 ブロックされた添付ファイルを更新する
  1. blockedattachments の値を解析します(セミコロン区切り)
  2. 5b.1 で特定した拡張子のみを削除し、その他はすべて保持します
  3. 設定を更新します。
    pac env update-settings --name blockedattachments --value "{updated-list-with-types-removed}"
    
  4. 更新が成功したことを確認します。
5b.5 インポートを再試行する

zip を再エンコードし、ImportSolutionAsync を再試行します(フェーズ 5 の手順 1~4 を繰り返し、再度ポーリングします)。

  • Succeeded の場合:フェーズ 6 に進みます
  • 別のエラーで再び失敗した場合:新しいエラーメッセージを表示して停止します。それ以上再試行してはいけません
フェーズ 6 — インポートを検証する
  1. ソリューションが存在し、バージョンが一致することを確認するためにクエリを実行します。

    GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{solutionName}'&$select=solutionid,uniquename,version,ismanaged
    
  2. インポートジョブにコンポーネントの結果を問い合わせます(インポート応答の ImportJobKey を使用します)。

    GET {envUrl}/api/data/v9.2/importjobs({ImportJobKey})?$select=solutionname,completedon,progress,data
    
    • data XML フィールドを解析し、コンポーネントごとの結果を確認します(result="failure" のエントリーを探します)
    • 成功してインポートされた数、警告数、失敗数を数えます
  3. docs/alm/ が存在することを確認してから、docs/alm/last-import.json マーカーを書き込みます(node -e "require('fs').mkdirSync('docs/alm',{recursive:true})")。

    {
      "importedAt": "<ISO timestamp>",
      "solutionName": "<name>",
      "version": "<version>",
      "targetEnvironment": "<envUrl>",
      "asyncOperationId": "<id>",
      "importJobId": "<ImportJobKey value>",
      "status": "<Succeeded|Failed|Partial>",
      "componentResults": { "success": N, "warning": N, "failure": N },
      "versionSkew": null
    }
    

    status フィールドは、描画された ALM プランのステージごとのチェックリストについて、refresh-alm-plan-data.js のステップ同期(completed と failed)を制御します。次のように設定します。

    • インポートジョブの statecode が 3(Succeeded)で、コンポーネントの結果が failure === 0 の場合は Succeeded に設定します。
    • statecode が 3 でも componentResults.failure > 0 の場合は Partial に設定します(ソリューションは配置されましたが、一部のコンポーネントのインポートに失敗しています。通常はマネージドプロパティの競合や依存関係の不足が原因です)。
    • statecode が 4(Failed)の場合、インポートが終端状態に到達しなかった場合、またはすべてのコンポーネントが失敗した場合は Failed に設定します。このフィールドがないと、実際の結果にかかわらず、描画されたプランではすべてのインポートが completed と表示されます。

    ステップ 3.0 でユーザーが同一バージョンまたはダウングレードのインポートを承認した場合(SKEW_ACK = true)、versionSkew を次のように設定します。

    { "zipVersion": "<ZIP_SOLUTION_VERSION>", "installedVersion": "<INSTALLED.version>", "isManaged": <bool>, "acknowledged": true }
    

    それ以外の場合は、versionSkew: null のままにします。

フェーズ 6b — 環境変数の値を設定する(該当する場合)

インポートが成功すると、環境変数の定義はソリューションに含まれますが、値は含まれません。対象環境には定義レコードが作成されますが、明示的に設定するまで値は空のままです。

対象環境に、このソリューションの環境変数の定義があるかどうかを照会します:

GET {envUrl}/api/data/v9.2/environmentvariabledefinitions?$filter=introducedversion ne null&$select=schemaname,displayname,type,defaultvalue,environmentvariabledefinitionid

.solution-manifest.json の発行元プレフィックスで始まる schemaname のものだけに絞り込むか、利用可能であれば solutioncomponents と照合します。

見つかった定義ごとに、対象環境に値がすでに存在するか確認します:

GET {envUrl}/api/data/v9.2/environmentvariablevalues?$filter=_environmentvariabledefinitionid_value eq '{id}'&$select=value

🚦 ゲート(plan · import-solution:6b.env-vars): インポートされた環境変数の定義には、対象ごとの値が必要です。ユーザーが値を指定するか、スキップします(デフォルト値を使用)。値がない場合、実行時にはデフォルト値が読み込まれますが、これは開発環境専用の値である可能性があります。

既存の値が設定されていない定義がある場合は、AskUserQuestion を使ってユーザーに提示します:

「インポートしたソリューションには、この環境で値が設定されていない環境変数が {N} 個含まれています。それぞれの対象値を入力してください(空欄のままにするとスキップされ、デフォルト値が使用されます):

  1. {schemaname}({displayname})— デフォルト:{defaultvalue ?? 'none'}
  2. ...」

ユーザーが指定した値ごとに、environmentvariablevalue レコードを POST します:

POST {envUrl}/api/data/v9.2/environmentvariablevalues
{
  "schemaname": "{schemaname}",
  "value": "{userValue}",
  "[email protected]": "/environmentvariabledefinitions({id})"
}

Secret 型(type 100000005)について: Secret の値は暗号化して保存されます。POST の動作は同じですが、UI では値がマスクされます。ユーザーには対象環境の実際のシークレット値を指定してもらいます(例:本番テナントのアプリ登録用 OAuth クライアントシークレット。開発環境の値とは異なります)。

ユーザーがすべての値をスキップした場合、値が設定されるまではサイトが正しく機能しない可能性があることを伝え、手動で設定するための Power Platform の直接 URL を提示します: https://{targetEnvHost}/main.aspx?appid=...&etn=environmentvariabledefinition

6b.verify — 値が反映されたことを確認します。 変数ごとの POST が完了したら、各 environmentvariablevalues レコードが実際に対象環境に存在することを確認します。共通ヘルパーの scripts/lib/verify-env-var-values.js が、この読み取り専用の確認を実行し、スキーマごとに構造化された JSON 結果(landed / missing-value-record / missing-definition / value-mismatch / query-error)を返します:

node "${PLUGIN_ROOT}/scripts/lib/verify-env-var-values.js" \
  --envUrl "{targetEnvUrl}" \
  --schemaNames "{comma-separated schema names that the user supplied values for}"

標準出力を JSON として取得します。summary.missing > 0 または summary.error > 0 の場合は、該当するスキーマ名を含めてユーザーに警告を 1 件だけ提示します。ただし、インポートの概要を妨げてはいけません。インポート自体は成功しています。同じヘルパーをデプロイ時(deploy-pipeline フェーズ 7.6.5)と設定時(configure-env-variables フェーズ 7)にも使用します。確認処理を一元化することで、スキル間でユーザーに表示されるメッセージの一貫性を保てます。

フェーズ 6c — クラウドフローを検出する(存在する場合)

インポートが正常に完了したら、ソリューションにクラウドフローの JSON ファイルが含まれているか確認します。フェーズ 2 で確認した zip のパスを使用します。

unzip -l "{zipPath}" | grep -i "^.*Workflows/.*\.json"

一致するファイルが見つからない場合は、このフェーズ全体をスキップします。

クラウドフローのファイルが見つかった場合:

  1. 各パスからフロー名を抽出します(パターン:Workflows/FlowName-GUID.json — パスのプレフィックスと GUID のサフィックスを取り除き、表示名を取得します)

  2. ユーザーに次の内容を伝えます:

    「このソリューションには {N} 個のクラウドフローが含まれています。クラウドフローは、インポート後にターゲット環境の Power Pages サイトに登録する必要があります。」

    検出されたフロー:

    • {FlowName1}
    • {FlowName2} ...

    登録方法:Power Pages Management → ターゲット環境 → サイトの編集 → セットアップ → クラウドフロー → 上記の各フローを登録します。

    直接リンク:https://make.powerpages.microsoft.com/

    🚦 ゲート(計画 · import-solution:6c.cloud-flow-register): インポートしたソリューション内のクラウドフローは、ターゲット環境に手動で登録する必要があります。確認する/後で対応する。

  3. AskUserQuestion を呼び出します:

    質問ヘッダーオプション
    上記のクラウドフローを {targetEnvUrl} の Power Pages サイトに登録しましたか?クラウドフローの登録フローを登録済み — 続行する、後で登録する
  4. ユーザーの回答を cloudFlowStatus として記録します(値は "Registered" または "Pending registration")。このステータスはフェーズ 7 の概要行に表示されます。どちらの回答でもスキルを続行できます。

フェーズ 6d — サイトの有効化を確認する(Power Pages ソリューションの場合)

ソリューションに Power Pages の Web サイトコンポーネント(componentType 10374)が含まれている場合に限り、このフェーズを実行します。

GET {envUrl}/api/data/v9.2/solutioncomponents?$filter=_solutionid_value eq '{solutionId}' and componenttype eq 10374&$select=objectid

componentType 10374 のレコードが見つからない場合は、このフェーズ全体をスキップします。

見つかった場合は、共有の有効化ステータス確認を実行します(PAC CLI はすでにターゲット環境に認証されています)。

node "${PLUGIN_ROOT}/scripts/check-activation-status.js" --projectRoot "."

結果を評価します。

  • activated: true:フェーズ 7 の概要用に siteUrl を保存します。以降の操作は不要です。

🚦 ゲート(計画 · import-solution:6d.activate): サイトはインポートされましたが、ターゲット環境で有効化されていません。今すぐ activate-site を呼び出すか、後で対応するかを提示します。

  • activated: false:AskUserQuestion を使ってユーザーに尋ねます:

    質問ヘッダーオプション
    Power Pages サイトはインポートされましたが、まだ {envUrl} で有効化(プロビジョニング)されていません。サイトを一般公開するため、今すぐ有効化しますか?サイトの有効化はい、今すぐ有効化する(推奨)、いいえ、後で有効化する
    • 「はい」の場合:/power-pages:activate-site を呼び出します。activate-site スキルが、サブドメインの選択、確認、プロビジョニングを処理します。
    • 「いいえ」の場合:フェーズ 7 の概要に、有効化が保留中であることを記載し、準備ができたら /power-pages:activate-site を実行するようユーザーに伝えます。
  • error がある場合:何も通知せずにスキップし、概要の表示を妨げないようにします。フェーズ 7 で、有効化ステータスを確認できなかったことを記載します。

フェーズ 7 — 概要を表示する

次の概要テーブルを表示します:

項目値
ソリューション{solutionName} v{version}
ターゲット環境{envUrl}
マネージドはい/いいえ
インポートされたコンポーネントN 件成功、N 件警告、N 件失敗
設定された環境変数の値N / N
クラウドフロー{cloudFlowStatus}(登録済み/登録保留中/該当なし)
サイトの有効化{siteUrl} で有効化済み/保留中/該当なし
インポートジョブ{importJobId}
スキルの使用状況を記録する

参照先:${PLUGIN_ROOT}/references/skill-tracking-reference.md

参照先のスキル追跡手順に従って、このスキルの使用状況を記録します。--skillName "ImportSolution" を使用してください。

ALM プランを更新する(存在する場合)
node "${PLUGIN_ROOT}/scripts/lib/refresh-alm-plan-data.js" \
  --projectRoot "." \
  --phase import-solution \
  --stageName "{targetLabel}" \
  --render

{targetLabel} は、今回完了したインポートの対象となる手動パスのステージ(例:Staging、Production)です。ユーザーが指定するか、対象環境の URL から特定できます。このヘルパーは docs/alm/last-import.json を読み取り、インポート結果(ステータス、バージョン、コンポーネント数、コンポーネントの失敗)を planData.manualImports[targetLabel] に記録し、docs/alm-plan.html を再レンダリングします。これにより、該当する Import to {targetLabel} のチェックリスト項目に、バージョンとコンポーネント数を併記した IMPORTED/FAILED バッジが表示されます。以降、別の対象へのインポートはそれぞれ個別のエントリとして記録されます。レンダラーは呼び出しごとに上書きするのではなく、対象ごとの履歴を表示します。

--stageName を省略すると、ヘルパーは docs/alm/last-import.json の targetEnvironment URL と planData.stages[].envUrl を照合します。一致しない場合(まれなケースで、通常は planData のステージラベルと環境 URL の不一致が原因です)、インポートは合成キーの下に記録されるため、暗黙に失われることはありません。レンダリングされたプランを整った状態に保つには、--stageName を明示的に指定してください。docs/.alm-plan-data.json がない場合、ヘルパーはソフトな no-op として ok:false を返します。

次のステップをユーザーに案内する(ユーザー主導の順序設定)。 ヘルパーの stdout JSON には nextStep: { name, skill: string | null } | null が含まれます。null 以外の場合は skill に応じて対応します。skill が null 以外の場合は、ユーザーに "Plan updated. Next in your plan: {nextStep.name} → run {nextStep.skill} when you're ready." と伝えます。skill が null の場合(Finalize などの内部ステップで、ユーザーが実行するコマンドがない場合)は、ステップ名だけを伝えます。「プランを更新しました。プランの次のステップ:{nextStep.name}。」 とし、run null は絶対に出力しないでください。(通常は、次の対象へのインポート、またはこの対象でのサイトの有効化です。)null の場合、またはヘルパーが ok:false を返した場合は、次のステップについて何も伝えません。次のスキルを自動的に呼び出してはいけません。実行はユーザーが主導します。

主な判断ポイント(ユーザーの応答を待つ)

  1. フェーズ 1:対象環境を確認します。マネージドソリューションのインポートは簡単には取り消せません
  2. フェーズ 2:複数の zip ファイルが見つかった場合は、ファイルを選択します
  3. フェーズ 3、ステップ 3.0:バージョンの不一致に関する注意 — 対象にソリューションがすでにインストールされており、zip のバージョンがインストール済みバージョン以下の場合にのみ表示されます。バージョンを上げて再エクスポート(推奨)、そのままインポート(last-import.json に了承済みとして記録)、またはキャンセルを選択できます。読み取り専用の確認です。ここでキャンセルしても対象環境への書き込みは行われません
  4. フェーズ 3:ステージングインポートか直接インポートかを選択し、カスタマイズを上書きするか決定します
  5. フェーズ 4:不足している依存関係がある状態で続行するか決定します
  6. フェーズ 5b:添付ファイルの種類のブロック解除に同意します。明示的な承認なしに環境設定を変更してはいけません
  7. フェーズ 6b:環境変数の値を設定します。ソリューションに環境変数の定義が含まれ、対象に既存の値がない場合は、必ずユーザーに確認します。Secret 型の定義には、対象環境固有のシークレット値が必要です
  8. フェーズ 6c:クラウドフローを登録します。これはブロッキングではなく、ユーザーは後から登録できます。ステータスは概要に記録されます
  9. フェーズ 6d:Power Pages の Web サイトコンポーネントが存在し、サイトがまだ有効化されていない場合にのみ、サイトを有効化します

エラー処理

  • コンポーネント単位のインポート失敗は概要に報告し、全体の完了を妨げないでください
  • インポートの非同期処理が AttachmentBlocked (-2147188706) で失敗した場合:フェーズ 5b の修復フローを実行します(ブロックされた種類の特定、同意の取得、ブロック解除、再試行)
  • インポートの非同期処理がその他のエラーで失敗した場合:非同期処理レコードの friendlyMessage を表示して終了します
  • ロールバックを試みてはいけません。成功した内容と失敗した内容を報告します
  • 明示的なユーザー承認なしに環境設定(blockedattachments)を変更してはいけません

進捗状況の追跡テーブル

タスクの内容実行中の表示説明
前提条件の確認前提条件を確認中PAC CLI の認証を確認し、トークンを取得して、ユーザーとともに対象環境を確認します
ソリューションファイルの特定ソリューションファイルを特定中ソリューションの zip を見つけて検証し、Solution.xml が存在することを確認します
インポートの構成インポートを構成中ステップ 3.0:solution.xml から zip の uniqueName と version を抽出し、対象にインストール済みのバージョンを照会します。zip のバージョンがインストール済みのバージョン以下の場合は、バージョンの不一致に関する注意を表示します(再エクスポート/そのままインポート/キャンセルを提示)。その後、ステージングか直接実行か、カスタマイズを上書きするか、ワークフローを公開するかを尋ねます
ソリューションのステージング(依存関係の確認)ソリューションをステージング中コミットする前に、StageSolution を実行して不足している依存関係を確認します
ソリューションのインポートソリューションをインポート中ImportSolutionAsync に POST し、完了するまでポーリングします。AttachmentBlocked の場合は、ブロックされた種類を特定し、ユーザーの同意を得て、pac env update-settings でブロックを解除してから再試行します
インポートの検証インポートを検証中対象内のソリューションのバージョンを確認し、コンポーネントの結果を解析して、docs/alm/last-import.json に書き込みます
クラウドフローの検出クラウドフローを検出中zip 内の Workflows/*.json エントリを一覧表示します。見つかった場合は、Power Pages サイトにフローを登録するかユーザーに尋ね、ステータス(Registered/Pending registration)を記録します
サイトの有効化状況の確認サイトの有効化状況を確認中ソリューションに componentType 10374 が含まれる場合は、check-activation-status.js を実行します。有効化されていない場合はユーザーに確認し、/power-pages:activate-site を呼び出します
概要の提示概要を提示中コンポーネント数(成功/警告/失敗)、クラウドフローの登録状況、サイトの有効化状況、設定された環境変数の値を表示します

コメント