PowerShell scripts that move a Dynamics 365 / Dataverse solution and its assets between environments with the Power Platform CLI (pac) - export a managed solution from dev, unpack it into version-controlled source, import it into staging, and push locally edited plugin assemblies and web resources back into dev.
The scripts themselves are generic. Everything environment-specific lives in one JSON file, so the toolkit drops into any repo that holds the actual solution code.
local edits dev env staging env
─────────── ─────── ───────────
push-plugins.ps1 ─build+push─▶ plugins
push-webresources.ps1 ─round-trip─▶ web resources
│
export-solution.ps1 ─export managed─▶ zip ─audit.ps1─▶ git
│
└────────── import-solution.ps1 ──────▶ imported
(deploy-solution.ps1 chains the last two, optionally the first two)
- Power Platform CLI (
pac) onPATH. - Windows PowerShell 5.1 or PowerShell 7+.
- For plugin builds only: Visual Studio or Build Tools (MSBuild is located automatically via
vswhere), with NuGet packages already restored - or setpluginPush.restoretotruein the map.
The toolkit expects a fixed shape: the scripts two levels down, the config at the repo root. _deploy-common.ps1 reads the map as ..\..\solution-deploy-map.json.
-
Copy
src/solutions/into the host repo at<host-repo>/src/solutions/. -
Copy
solution-deploy-map.jsonandplugin-ids.jsonto<host-repo>/(the repo root). -
Fill in the placeholders in
solution-deploy-map.json- see src/solutions/DEPLOYMENT.md for what every key does:activeEnvironment,activeSolution,webresourceSolutionsrcProfile/targetProfile-pac authprofile names for dev and stagingtargetInstance- the staging org's friendly name; the import refuses to run without itenvironments.<env>.url, and theplugins/webresourcesasset lists
-
Delete the sample rows in
plugin-ids.json(or the whole file) -push-plugins.ps1resolves and caches real assembly ids on first run. -
Create the two auth profiles once per machine:
pac auth create --name <srcProfile> --url <dev-env-url> pac auth create --name <targetProfile> --url <staging-env-url>
Paths inside the map (.csproj files, web-resource sources) are relative to the map's own folder, i.e. the host repo root - so they line up with wherever that repo keeps its plugin and script code. To put the toolkit somewhere else, pass -MapPath <path> per run.
All under src/solutions/. Each takes its defaults from the map; every identifier param overrides for one run.
| Script | Purpose |
|---|---|
deploy/deploy-solution.ps1 |
Chains the whole flow: optional asset push → export → import. |
deploy/export-solution.ps1 |
Bumps the online version, exports the managed solution from dev, then runs the audit unpack + commit. |
deploy/import-solution.ps1 |
Imports a managed zip into staging (async, plugins activated), with a mandatory target-org guard and a parsed explanation of any failure. |
deploy/push-plugins.ps1 |
MSBuilds each mapped plugin assembly and uploads it via pac plugin push. |
deploy/push-webresources.ps1 |
Pushes mapped web-resource files into dev through a solution export/pack/import round-trip. |
audit/audit.ps1 |
Unpacks an exported zip into a version-tracked source tree, optionally staging and committing it. |
misc/dump-webresources.ps1 |
Standalone: lists a solution's web resources and can add them all to another solution. |
_deploy-common.ps1 |
Shared helpers (config loader, env guards, version bump, import-failure parsing, plugin-id cache). Dot-sourced, not run directly. |
Both irreversible operations are guarded before anything is written:
- Export verifies the connected org's host matches the map's
urlbefore publishing or bumping the version. - Import refuses to run unless
targetInstanceis set, and fails if the connected org's name does not match it.import.modedefaults toupgrade, which deletes components removed since the last version - a first import into an empty target needs-Mode update.
- src/solutions/DEPLOYMENT.md - full reference: every map key, every setting, every script's params.
- src/solutions/README.md - the audit tool: why solution source is version-tracked and how to diff two releases.