Verwaltet den Lifecycle von Dataverse-Solutions, einschließlich Erstellung, Export, Unpacking, Packing und Import über verschiedene Umgebungen hinweg mittels PAC CLI und Python SDK.
- GitHub Stars
- 257
- Dateien
- 1
- Letzter Commit
- 11. Nov. 2026
- Lizenz
- MIT
- Erlaubte Tools
- —
Was macht der dv-solution Skill?
dv-solution ist ein Agent Skill. Verwaltet den Lifecycle von Dataverse-Solutions, einschließlich Erstellung, Export, Unpacking, Packing und Import über verschiedene Umgebungen hinweg mittels PAC CLI und Python SDK.
Zusammenfassung erzeugt aus der SKILL.md des Skills.
Über den dv-solution skill
Das Management des Lifecycles von Dataverse-Solutions ist eine typische Aufgabe für Entwickler, wenn sie Anpassungen paketieren, Workflows zwischen Entwicklungs- und Produktionsumgebungen verschieben oder Deployments validieren müssen. Dieser Skill automatisiert das Erstellen, Exportieren, Unpacking, Packen und Importieren von Solutions unter Verwendung der PAC CLI und eines Python SDK. Er ist für Engineers konzipiert, die Änderungen über verschiedene Umgebungen hinweg ausrollen müssen, während sie durch unmanaged Solution-Dateien eine saubere Source of Truth beibehalten.
Ein Agent sollte diesen Skill laden, wenn ein User einen neuen Publisher erstellen, Komponenten wie Tables oder Forms zu einer Solution hinzufügen oder eine paketierte Solution in einer Zielumgebung deployen möchte. Der Skill nutzt die PAC CLI für Dateioperationen und das Python SDK für die Interaktion mit Dataverse-Records, wie etwa das Erstellen von Solution-Records oder die Durchführung von Validierungen nach dem Import. Er zeichnet sich durch strukturierte Validierungsschritte aus, wie etwa die Prüfung, ob Tables existieren oder ob Forms veröffentlicht wurden, um sicherzustellen, dass die Deployments erfolgreich waren.
Was ein agent damit machen kann
- 1Eine neue Solution erstellenRichte eine neue Dataverse-Solution ein, indem du zuerst einen Publisher mit einem spezifischen Customization-Präfix suchst oder erstellst.
- 2Customizations zwischen Umgebungen verschiebenExportiere eine Solution aus einer Entwicklungsumgebung, entpacke sie in editierbare Dateien und packe sowie importiere sie anschließend in eine Testumgebung.
- 3Erfolg des Deployments verifizierenPrüfe nach Abschluss eines Import-Jobs, ob spezifische Tables, Forms oder Views in der Zielumgebung live und funktionsfähig sind.
- 4Komponenten zu Solutions hinzufügenFüge bestehende Tables, Views oder Canvas Apps mithilfe von Component-Type-Codes einer spezifischen Solution hinzu.
Geschrieben basierend auf dem SKILL.md des skills. Jeder use case wurde gegen ein zitat daraus geprüft.
So installierst du den dv-solution Skill
Jeder Befehl kopiert den Skill-Ordner in das Skills-Verzeichnis des Clients. Starte den Client neu oder beginne eine neue Session, um ihn zu laden.Den dv-solution Skill in Plugin installieren
/plugin marketplace add microsoft/Dataverse-skills /plugin install dataverse@dataverse-skills
Den dv-solution Skill in Claude Code installieren
~/.claude/skills/dv-solution/SKILL.md
$ npx degit microsoft/Dataverse-skills/.github/plugins/dataverse/skills/dv-solution ~/.claude/skills/dv-solutionDen dv-solution Skill in Claude Code (project) installieren
.claude/skills/dv-solution/SKILL.md
$ npx degit microsoft/Dataverse-skills/.github/plugins/dataverse/skills/dv-solution .claude/skills/dv-solutionDen dv-solution Skill in Codex installieren
~/.agents/skills/dv-solution/SKILL.md
$ npx degit microsoft/Dataverse-skills/.github/plugins/dataverse/skills/dv-solution ~/.agents/skills/dv-solutionDen dv-solution Skill in Gemini CLI installieren
~/.gemini/skills/dv-solution/SKILL.md
$ npx degit microsoft/Dataverse-skills/.github/plugins/dataverse/skills/dv-solution ~/.gemini/skills/dv-solutionDen dv-solution Skill in Claude.ai installieren
npx degit microsoft/Dataverse-skills/.github/plugins/dataverse/skills/dv-solution dv-solution && zip -r dv-solution.zip dv-solution # claude.ai → Customize → Skills → + → Create skill → Upload a skill → dv-solution.zip
Automatisierte Checks
Geluften am 14. Nov. 2026Vollständiger Bericht →Hat unsere automatischen Prüfungen bestanden. Das ist kein Sicherheitsaudit: Lesen Sie die Skripte, bevor ein Agent sie ausführt. Wie die Checks funktionieren
- Source Repository verknüpft und erreichbarmicrosoft/dataverse-skills/.github/plugins/dataverse/skills/dv-solution is public and reachablegithub.com/microsoft/dataverse-skills/tree/HEAD/.github/plug ↗Bestanden
- Open Source Lizenz (OSI)MITspdx.org/licenses/MIT.html ↗Bestanden
- Commit oder Release im letzten JahrLast commit 2026-11-11github.com/microsoft/dataverse-skills/commits ↗Bestanden
- Keine Download-und-Ausführen-Muster oder Zugangsdatendiebstahl in SkriptenNo scripts in the skill folderBestanden
- Keine versteckten Anweisungen in SKILL.mdNo override phrases, safeguard switches, credential requests or hidden charactersgithub.com/microsoft/dataverse-skills/blob/HEAD/.github/plug ↗Bestanden
- Vorab erlaubte Tools sind eingeschränktNo pre-approved tools; the agent asks before using onegithub.com/microsoft/dataverse-skills/blob/HEAD/.github/plug ↗Bestanden
- SKILL.md folgt dem Agent-Skills-FormatValid name and description; says when to use it; 333 linesgithub.com/microsoft/dataverse-skills/blob/HEAD/.github/plug ↗Bestanden
SKILL.md
Auf GitHub ansehenSkill: 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/unpackare local file operations (no auth) but need a host that can run PAC -- do them on a capable machine or CI runner. Verify egress withpython scripts/auth.py --check. Seedv-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 createcommand. 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
unpackfails 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 Unmanagedfor the development solution. Managed packages are for deployment to downstream environments (test, prod). --activate-pluginsensures any registered plugins in the solution are activated on import.- If you see "solution already exists" errors, use
--import-mode ForceUpgradeto 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.pyfor credential/token acquisition. Seedv-queryfor SDK query patterns anddv-datafor write patterns.
Dateien in diesem Skill
1dv-solution Skill-Fragen
Wie installiere ich den dv-solution Skill in Claude Code?
Führe npx degit microsoft/Dataverse-skills/.github/plugins/dataverse/skills/dv-solution ~/.claude/skills/dv-solution aus. Claude Code lädt Skills aus ~/.claude/skills (alle Projekte) oder .claude/skills (ein Projekt) und nutzt einen Skill, wenn seine Beschreibung zur Aufgabe passt.
Welche Clients können den dv-solution Skill nutzen?
Jeder Client, der das Agent Skills-Format liest: Claude Code, Codex, Gemini CLI und claude.ai (als ZIP hochladen). Skripte im Skill laufen nur dort, wo der Client Codeausführung erlaubt.
Was ist der Unterschied zwischen managed und unmanaged Solutions?
Unmanaged Solutions werden für die Entwicklung genutzt und dienen als Source of Truth. Managed Packages sind für das Deployment in nachgelagerte Umgebungen wie Test- oder Produktionsumgebungen gedacht.