• Projects
  • Service
  • About
  • branding.bz
  • Podcast
  • Tips
  • FAQ
  • Recruit
  • Download
  • Contact
  • branding.bz(ブランド構築SaaS)
  • DESIGN NOW(デザインメディア)
  • X
  • LinkedIn
  • Spotify
  • Facebook

213-0011 神奈川県川崎市高津区久本3-6-7-303

© 2026 ID INC. All rights reserved

claude-skills/スキル
SKILLOfficialdatabase

dv-solution

プラグイン
dataverse
ソース
GitHub で見る ↗
説明

**Dataverse ソリューションライフサイクル** — カスタマイズ内容のパッケージ化、環境間での配布、デプロイメント(本番環境への展開)の検証などを一連で行うことができます。 **次のような場合に使用:** - ユーザーがカスタマイズ内容をパッケージ化したい - 別の環境に配布したい - 開発環境・テスト環境・本番環境の間で作業内容を移動したい

原文を表示

Dataverse solution lifecycle — create, export, import, promote across environments, and validate deployments. Use when the user wants to package customizations, deploy to another environment, or move work between dev / test / prod.

ユースケース
  • カスタマイズ内容をパッケージ化する
  • 別の環境に配布する
  • 環境間で作業内容を移動する
  • 本番環境への展開を検証する
本文(日本語訳)

スキル: Solution

Dataverse ソリューション(Power Platform の機能パッケージ)を PAC CLI(コマンドラインツール)で作成・エクスポート・アンパック・パック・インポート・検証します。インポート後は Python SDK を使って検証も行います。

ヘッドレス環境・ネットワーク制限ホスト: オンライン処理は生の Web API(ExportSolution / ImportSolution)を使用してください。pac solution pack/unpack はローカルファイル操作(認証不要)ですが、PAC を実行できるホストが必要です — 対応マシンまたは CI ランナー(自動化実行環境)で実行してください。python scripts/auth.py --check でネットワーク接続を確認してください。詳細は dv-connect/references/headless-hosts.md を参照。

スキルの適用範囲

対象業務 代わりに使用するスキル
テーブル・列・リレーションシップ・フォーム・ビューの作成 dv-metadata
データレコードの作成・更新・削除 dv-data
レコードの照会・読み取り dv-query
Dataverse への接続・MCP 設定 dv-connect

新しいソリューションの作成

発行元とソリューション レコードの作成には Python SDK を使用してください — 生の HTTP ではなく。 発行元とソリューションは標準の Dataverse テーブルです。client.records.create() と client.records.list() は認証・ページング・エラー処理を自動で処理するため、生の urllib 呼び出しで発生しやすい URL エンコード・ヘッダーの定型文・GUID 解析バグを回避できます。

ステップ 1: 発行元を検索または作成

すべてのソリューションは発行元に属します。発行元の customizationprefix(例: contoso、sa、lit)は、すべてのカスタムテーブル・列・リレーションシップのスキーマ名に付与されます。このプレフィックスは実質的に永続的です — 既存コンポーネントは発行元を変更しても永遠にプレフィックスを保持します。

デフォルトの new プレフィックスは使用しないでください。 これは組織 ID としての価値がなく、名前衝突のリスクがあり、開発者が最善実践に従わなかったことを示します。

検索フロー — 発行元を作成する前に必ず実行してください:

import os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts"))
from auth import get_client

# get_client は User-Agent ヘッダーにプラグインの帰属情報を設定します。
# context 値を変更しないでください — サーバー側テレメトリ
# (アプリ/スキル/エージェント)の固定スキーマです。
# 秘密情報や個人情報は含めないでください。
client = get_client("dv-solution")

# 1. Microsoft 以外の発行元を照会
publishers = client.records.list(
    "publisher",
    filter="customizationprefix ne 'none' and uniquename ne 'MicrosoftCorporation' and uniquename ne 'Microsoftdynamic'",
    select=["publisherid", "uniquename", "friendlyname", "customizationprefix"],
    top=10,
)

if publishers:
    # 既存の発行元を表示して、ユーザーに選択させる
    print("Existing publishers in this environment:")
    for p in publishers:
        print(f"  {p['uniquename']} (prefix: {p['customizationprefix']}_)")
    # ユーザーに確認: 「このソリューションはどの発行元を使用しますか?」
    # または: 「'<名前>' (プレフィックス: <プレフィックス>_)を再利用しますか?」
    publisher_id = publishers[0]["publisherid"]  # ユーザー確認後
else:
    # カスタム発行元がない — ユーザーにプレフィックスを聞く
    # 「どの発行元プレフィックスを使用しますか? (例: 'contoso', 'sa', 'lit' — 小文字 2~8 文字)」
    publisher_id = client.records.create("publisher", {
        "uniquename": "<publisheruniquename>",
        "friendlyname": "<Publisher Display Name>",
        "customizationprefix": "<prefix>",   # ユーザー入力から、'new' ではなく
        "description": "<description>",
    })

ルール:

  • 新しい発行元を作成するか、プレフィックスを選択する前に、必ずユーザーに確認してください。 プレフィックスをハードコードしないでください。
  • プレフィックスは、ソリューション内で既に作成されているテーブルと一致する必要があります — プレフィックスを混在させることはできません。
  • 1 つの発行元は多数のソリューションを所有できます。可能な限り既存の発行元を再利用してください。

ステップ 2: ソリューション レコードを作成

SDK を使用してソリューション レコードを作成します(生の Web API より優先):

import os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts"))
from auth import get_client

# get_client は User-Agent ヘッダーにプラグインの帰属情報を設定します。
# context 値を変更しないでください — サーバー側テレメトリ
# (アプリ/スキル/エージェント)の固定スキーマです。
# 秘密情報や個人情報は含めないでください。
client = get_client("dv-solution")

# ソリューション レコードを作成
solution_id = client.records.create("solution", {
    "uniquename": "<UniqueName>",
    "friendlyname": "<Display Name>",
    "version": "1.0.0.0",
    "publisherid@odata.bind": "/publishers(<publisher_guid>)",
})
print(f"Created solution: {solution_id}")

必須フィールド:

テーブル: solution
フィールド: uniquename    = "<UniqueName>"
          friendlyname  = "<Display Name>"
          version       = "1.0.0.0"
          publisherid   = <ステップ 1 の発行元 GUID>

注: pac solution create コマンドはありません。PAC CLI はエクスポート・インポート・パック・アンパックを処理しますが、ソリューション レコード作成は処理しません。SDK または Web API を使用してレコードを作成してください。

ステップ 3: コンポーネントを追加

pac solution add-solution-component を使用して、テーブル・フォーム・ビュー・その他のコンポーネントを追加します:

pac solution add-solution-component \
  --solutionUniqueName <UniqueName> \
  --component <ComponentSchemaName> \
  --componentType <TypeCode> \
  --environment <url>

注: PAC CLI はここではキャメルケース(単語の境目を大文字にする形)の引数(--solutionUniqueName、--componentType)を使用します。

一般的なコンポーネント タイプ コード:

タイプ コード コンポーネント
1 Entity (テーブル)
2 Attribute (列)
26 View (ビュー)
60 Form (フォーム)
61 Web Resource (Web リソース)
300 Canvas App (キャンバス アプリ)
371 Connector (コネクタ)

追加する各コンポーネントについてコマンドを繰り返します。

代替手段: MSCRM.SolutionName ヘッダーで自動追加

Web API 経由でメタデータを作成する場合、MSCRM.SolutionName ヘッダーを含めて、コンポーネントをソリューションに自動追加します:

headers = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json",
    "MSCRM.SolutionName": "<UniqueName>"
}

重要: このアプローチを使用した後、SDK を使用して solutioncomponent テーブルをクエリしてコンポーネントが追加されたか確認してください(現在の PAC では pac solution list-components は利用できません):

sol = client.records.list("solution",
    filter="uniquename eq '<UniqueName>'", select=["solutionid"], top=1).first()
if sol is not None:
    components = client.records.list("solutioncomponent",
        filter=f"_solutionid_value eq {sol['solutionid']}",
        select=["componenttype", "objectid"])
    print(f"{len(components)} components in the solution")

ヘッダーのスペルが間違っていたり、ソリューションが存在しない場合、コンポーネントは既定のソリューションに作成されます — サイレントに。必ず確認してください。

ソリューション名を検索

エクスポート前に、正確なユニーク名を確認します:

pac solution list --environment <url>

UniqueName 列が、他のコマンドに渡す値です。表示名にはスペースが含まれますが、ユニーク名には含まれません。

プル: エクスポート + アンパック

エクスポート前またはインポート前に、対象環境を確認してください。 pac auth list + pac org who を実行して出力をユーザーに表示し、意図した環境と一致することを確認します。開発者は複数の環境で作業します — 環境を仮定しないでください。

ソリューションをアンマネージド(開発時の正式版)としてエクスポート:

pac solution export \
  --name <UniqueName> \
  --path ./solutions/<UniqueName>.zip \
  --managed false \
  --environment <url>

編集可能なソースファイルにアンパック:

pac solution unpack \
  --zipfile ./solutions/<UniqueName>.zip \
  --folder ./solutions/<UniqueName> \
  --packagetype Unmanaged

Windows ファイルロック競争状態。 エクスポート と アンパック を別々のコマンドとして実行してください(上記参照)。すぐに繋げるとエクスポート直後の一時的な ZIP ファイルロックに当たることがあります。unpack がロック / 「使用中」エラーで失敗した場合は、少し待ってから再実行し、ZIP を削除する前にアンパック フォルダに期待されるコンポーネントが含まれていることを確認してください。

ZIP を削除 — アンパック フォルダが正式版です:

rm ./solutions/<UniqueName>.zip

コミット:

git add ./solutions/<UniqueName>
git commit -m "chore: pull <UniqueName> baseline"
git push

プッシュ: パック + インポート

ソースファイルを ZIP に戻してパック:

pac solution pack \
  --zipfile ./solutions/<UniqueName>.zip \
  --folder ./solutions/<UniqueName> \
  --packagetype Unmanaged

インポート(大規模ソリューションは非同期を推奨):

pac solution import \
  --path ./solutions/<UniqueName>.zip \
  --environment <url> \
  --async \
  --activate-plugins

インポート ステータスのポーリング

非同期インポート後、ジョブを確認:

pac solution list --environment <url>

インポート後の検証

ソリューションをインポートした後、コンポーネントがアクティブか確認します。Python SDK を使用して直接確認 — 外部スクリプトは不要です。

テーブルの存在確認

info = client.tables.get("<logical_name>")
if info:
    print(f"[PASS] Table '{info.logical_name}' exists")
else:
    print(f"[FAIL] Table '<logical_name>' not found")

フォームが公開されているか確認

forms = client.records.list(
    "systemform",
    filter="objecttypecode eq '<entity>' and type eq <form_type_code>",
    select=["name", "formid"],
    top=5,
)
# Form type codes: 2 = main, 7 = quick create

ビューの存在確認

views = client.records.list(
    "savedquery",
    filter="returnedtypecode eq '<entity>'",
    select=["name", "savedqueryid", "statuscode"],
    top=10,
)

ユーザーのロール割り当て確認(N:N 多対多関係の展開)

records.list は $expand をそのまま通すため、SDK で N:N ナビゲーション プロパティを直接読み取ります:

users = list(client.records.list(
    "systemuser",
    filter="internalemailaddress eq '<email>'",   # フォールバック: domainname eq '<upn>'
    select=["fullname"],
    expand=["systemuserroles_association($select=name)"],
    top=1,
))
roles = [r["name"] for r in users[0].get("systemuserroles_association", [])] if users else []

別の方法として、マネージド Dataverse CLI エスケープハッチ(dataverse api request — urllib ではなく)、または FetchXML と link-entity:

dataverse api request --target dataverse --method GET \
  --path "/api/data/v9.
原文(English)を表示

Skill: Solution

Create, export, unpack, pack, import, and validate Dataverse solutions via PAC CLI. Includes post-import validation using the Python SDK.

Headless / restricted-egress hosts: use the raw Web API (ExportSolution / ImportSolution) for the online steps. pac solution pack/unpack are local file operations (no auth) but need a host that can run PAC -- do them on a capable machine or CI runner. Verify egress with python scripts/auth.py --check. See dv-connect/references/headless-hosts.md.

Skill boundaries

Need Use instead
Create tables, columns, relationships, forms, views dv-metadata
Create, update, or delete data records dv-data
Query or read records dv-query
Connect to Dataverse / set up MCP dv-connect

Create a New Solution

Use the Python SDK for publisher and solution record creation — not raw HTTP. Publishers and solutions are standard Dataverse tables. client.records.create() and client.records.list() handle auth, pagination, and error handling automatically, avoiding the URL encoding, header boilerplate, and GUID-parsing bugs that raw urllib calls introduce.

Step 1: Find or Create the Publisher

Every solution belongs to a publisher. The publisher's customizationprefix (e.g., contoso, sa, lit) is prepended to every custom table, column, and relationship schema name. This prefix is effectively permanent — existing components keep their prefix forever, even if you change the publisher later.

Never use the default new prefix. It provides no organizational identity, risks naming collisions, and signals the developer did not follow best practices.

Discovery flow — always run this before creating a publisher:

import os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts"))
from auth import get_client

# get_client sets a plugin attribution context on the User-Agent header.
# Do not modify the context value — it is a closed schema for server-side
# telemetry (app/skill/agent). Never include secrets or PII.
client = get_client("dv-solution")

# 1. Query for existing non-Microsoft publishers
publishers = client.records.list(
    "publisher",
    filter="customizationprefix ne 'none' and uniquename ne 'MicrosoftCorporation' and uniquename ne 'Microsoftdynamic'",
    select=["publisherid", "uniquename", "friendlyname", "customizationprefix"],
    top=10,
)

if publishers:
    # Show existing publishers and ask user which to use
    print("Existing publishers in this environment:")
    for p in publishers:
        print(f"  {p['uniquename']} (prefix: {p['customizationprefix']}_)")
    # ASK THE USER: "Which publisher should this solution use?"
    # Or: "Should I reuse '<name>' (prefix: <prefix>_)?"
    publisher_id = publishers[0]["publisherid"]  # after user confirms
else:
    # No custom publisher exists — ASK THE USER for prefix
    # "What publisher prefix should I use? (e.g., 'contoso', 'sa', 'lit' — 2-8 lowercase chars)"
    publisher_id = client.records.create("publisher", {
        "uniquename": "<publisheruniquename>",
        "friendlyname": "<Publisher Display Name>",
        "customizationprefix": "<prefix>",   # from user input, NOT 'new'
        "description": "<description>",
    })

Rules:

  • Always ask the user before creating a new publisher or choosing a prefix. Never hardcode a prefix.
  • The prefix must match any tables already created in the solution — you cannot mix prefixes.
  • One publisher can own many solutions. Reuse an existing publisher when possible.

Step 2: Create the Solution Record

Use the SDK to create the solution record (preferred over raw Web API):

import os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts"))
from auth import get_client

# get_client sets a plugin attribution context on the User-Agent header.
# Do not modify the context value — it is a closed schema for server-side
# telemetry (app/skill/agent). Never include secrets or PII.
client = get_client("dv-solution")

# Create the solution record
solution_id = client.records.create("solution", {
    "uniquename": "<UniqueName>",
    "friendlyname": "<Display Name>",
    "version": "1.0.0.0",
    "publisherid@odata.bind": "/publishers(<publisher_guid>)",
})
print(f"Created solution: {solution_id}")

The required fields:

Table:  solution
Fields: uniquename    = "<UniqueName>"
        friendlyname  = "<Display Name>"
        version       = "1.0.0.0"
        publisherid   = <publisher GUID from step 1>

Note: There is no pac solution create command. PAC CLI handles export/import/pack/unpack, not solution record creation. Use the SDK or Web API to create the record.

Step 3: Add Components

Use pac solution add-solution-component to add tables, forms, views, and other components:

pac solution add-solution-component \
  --solutionUniqueName <UniqueName> \
  --component <ComponentSchemaName> \
  --componentType <TypeCode> \
  --environment <url>

Note: PAC CLI uses camelCase args here (--solutionUniqueName, --componentType), not kebab-case.

Common component type codes:

Type Code Component
1 Entity (Table)
2 Attribute (Column)
26 View
60 Form
61 Web Resource
300 Canvas App
371 Connector

Repeat the command for each component you need to add.

Alternative: Auto-add via MSCRM.SolutionName Header

When creating metadata via the Web API, include the MSCRM.SolutionName header to auto-add components to the solution:

headers = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json",
    "MSCRM.SolutionName": "<UniqueName>"
}

Important: After using this approach, verify components were added by querying the solutioncomponent table with the SDK (pac solution list-components is not available in current PAC):

sol = client.records.list("solution",
    filter="uniquename eq '<UniqueName>'", select=["solutionid"], top=1).first()
if sol is not None:
    components = client.records.list("solutioncomponent",
        filter=f"_solutionid_value eq {sol['solutionid']}",
        select=["componenttype", "objectid"])
    print(f"{len(components)} components in the solution")

If the header was misspelled or the solution doesn't exist, components will be created in the default solution instead — silently. Always verify.

Find the Solution Name

Before exporting, confirm the exact unique name:

pac solution list --environment <url>

The UniqueName column is what you pass to other commands. Display names have spaces; unique names do not.

Pull: Export + Unpack

Confirm the target environment before exporting or importing. Run pac auth list + pac org who, show the output to the user, and confirm it matches the intended environment. Developers work across multiple environments — do not assume.

Export the solution as unmanaged (source of truth):

pac solution export \
  --name <UniqueName> \
  --path ./solutions/<UniqueName>.zip \
  --managed false \
  --environment <url>

Unpack into editable source files:

pac solution unpack \
  --zipfile ./solutions/<UniqueName>.zip \
  --folder ./solutions/<UniqueName> \
  --packagetype Unmanaged

Windows file-lock race. Run export and unpack as separate commands (as above); chaining them immediately can hit a transient ZIP file-lock right after export. If unpack fails with a lock / "in use" error, retry after a moment, and verify the unpacked folder has the expected components before deleting the zip.

Delete the zip — the unpacked folder is the source:

rm ./solutions/<UniqueName>.zip

Commit:

git add ./solutions/<UniqueName>
git commit -m "chore: pull <UniqueName> baseline"
git push

Push: Pack + Import

Pack the source files back into a zip:

pac solution pack \
  --zipfile ./solutions/<UniqueName>.zip \
  --folder ./solutions/<UniqueName> \
  --packagetype Unmanaged

Import (async recommended for large solutions):

pac solution import \
  --path ./solutions/<UniqueName>.zip \
  --environment <url> \
  --async \
  --activate-plugins

Poll Import Status

After async import, check the job:

pac solution list --environment <url>

Post-Import Validation

After importing a solution, verify that components are live. Use the Python SDK to check directly — no external scripts needed.

Check a table exists

info = client.tables.get("<logical_name>")
if info:
    print(f"[PASS] Table '{info.logical_name}' exists")
else:
    print(f"[FAIL] Table '<logical_name>' not found")

Check a form is published

forms = client.records.list(
    "systemform",
    filter="objecttypecode eq '<entity>' and type eq <form_type_code>",
    select=["name", "formid"],
    top=5,
)
# Form type codes: 2 = main, 7 = quick create

Check a view exists

views = client.records.list(
    "savedquery",
    filter="returnedtypecode eq '<entity>'",
    select=["name", "savedqueryid", "statuscode"],
    top=10,
)

Check a user's role assignment (N:N $expand)

records.list passes $expand straight through, so read the N:N navigation property directly with the SDK:

users = list(client.records.list(
    "systemuser",
    filter="internalemailaddress eq '<email>'",   # fallback: domainname eq '<upn>'
    select=["fullname"],
    expand=["systemuserroles_association($select=name)"],
    top=1,
))
roles = [r["name"] for r in users[0].get("systemuserroles_association", [])] if users else []

Alternatively, the managed Dataverse CLI escape hatch (dataverse api request — not urllib), or FetchXML with a link-entity:

dataverse api request --target dataverse --method GET \
  --path "/api/data/v9.2/systemusers?%24filter=internalemailaddress eq '<email>'&%24select=fullname&%24expand=systemuserroles_association(%24select=name)&%24top=1" \
  --environment <DATAVERSE_URL> \
  --context "app=dataverse-skills/<ver>;skill=dv-solution;agent=<agent>"

The response value[0].systemuserroles_association is the list of assigned roles (each with name).

Check import errors

jobs = client.records.list(
    "importjob",
    select=["importjobid", "solutionname", "startedon", "completedon", "progress"],
    orderby=["startedon desc"],
    top=5,
)

For detailed error history, also query msdyn_solutionhistory:

history = client.records.list(
    "msdyn_solutionhistory",
    filter="msdyn_status eq 1",  # 1 = failed
    select=["msdyn_name", "msdyn_starttime", "msdyn_exceptionmessage"],
    orderby=["msdyn_starttime desc"],
    top=5,
)

Validation error reference

Error Cause Fix
Table not found after import Component not in solution Add via pac solution add-solution-component
Form check fails immediately Publishing is async Wait 30 seconds and retry
Role not assigned User not provisioned Assign the role via pac admin assign-user or the Power Platform Admin Center
Import job at 0% Import still running Poll again in 60 seconds

Notes

  • Always use --managed false / --packagetype Unmanaged for the development solution. Managed packages are for deployment to downstream environments (test, prod).
  • --activate-plugins ensures any registered plugins in the solution are activated on import.
  • If you see "solution already exists" errors, use --import-mode ForceUpgrade to overwrite.
  • Large solutions (Sales, Customer Service) can take 10–20 minutes to import. Be patient and poll rather than re-importing.
  • All validation queries above require auth. Use scripts/auth.py for credential/token acquisition. See dv-query for SDK query patterns and dv-data for write patterns.

原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。