import-solution
Imports a Dataverse solution zip into a target environment, with optional staged import for dependency checking before committing. Use when asked to: "import solution", "install solution", "deploy solution zip", "push solution to environment", "deploy to staging", "deploy to production", or "install site in new environment".
Skill body
Plugin check: Run
node "${CLAUDE_PLUGIN_ROOT}/scripts/check-version.js"— if it outputs a message, show it to the user before proceeding.
import-solution
Imports a solution zip into a target Dataverse environment via ImportSolutionAsync. Supports optional staged import via StageSolution to check for missing dependencies before committing.
Prerequisites
- PAC CLI installed and authenticated to the target environment
- Azure CLI installed and logged in
- Solution zip file exists on disk (produced by
export-solution)
Phases
Phase 0 — ALM plan gate
plan-almis the front door. When the user expresses an ALM intent (promote / ship / deploy / set up CI-CD / move to staging / push to prod), the orchestrator (/power-pages:plan-alm) should run first. This Phase 0 enforces that and is meant to fail closed when there’s no plan, not to be a one-time check the user can dismiss forever.
Skip rule. If this skill was invoked as part of an active plan-alm orchestration, skip Phase 0 entirely and proceed to Phase 1. The gate helper exposes this via its inExecution block — pass through silently to Phase 1 when:
inExecution.status === "active"
The helper computes this from docs/.alm-plan-data.json — PLAN_STATUS === "In Execution" AND LAST_INVOCATION_AT within the last 60 minutes. check-alm-plan.js refreshes LAST_INVOCATION_AT automatically on every invocation that finds the plan in execution, so each in-chain skill keeps the chain alive for the next one — even multi-hour deploys (deploy-pipeline alone can take 60 min per stage) survive the window without the chain incorrectly de-classifying. Stalled chains (no heartbeat for > 60 min) reclassify as stale-heartbeat and Phase 0 gates fire normally so an abandoned plan doesn’t silently bypass user confirmation.
When inExecution.status is anything other than "active" ("not-running", "stale-heartbeat", "no-plan"), run the Phase 0 gate flow below. Branch on the remaining helper fields:
Step 1 — Run the gate helper.
node "${CLAUDE_PLUGIN_ROOT}/scripts/lib/check-alm-plan.js" --projectRoot "."
The helper returns JSON with { exists, deferred, stale, staleness: { reason, detail }, generatedAt, planStatus, ... }. Pass --envUrl, --token, --solutionId once Phase 1 has acquired them if you also want a freshness check; otherwise the helper does an existence-only check, which is sufficient for the gate decision below.
Step 2 — Branch on the result.
| Result | Behavior |
|---|---|
deferred: true |
The user has explicitly deferred ALM for this project (.alm-deferred marker present). Pass through silently to Phase 1 — do not nag. |
exists: false |
The user hasn’t run plan-alm yet. See Step 3. |
exists: true, stale: false |
Plan is current. Pass through silently to Phase 1. |
exists: true, stale: true (reason: solution-modified) |
The solution changed after the plan was generated. See Step 4. |
Step 3 — No plan. Tell the user:
“No ALM plan exists for this project.
/power-pages:plan-almbuilds one — it detects the project state, asks about your promotion strategy (PP Pipelines vs Manual export/import), and orchestrates the right skills (including this one) in the right order. Want me to run plan-alm now?”
🚦 Gate (intent · import-solution:0.no-plan): Fail-closed entry gate when
check-alm-plan.jsreturnsexists:false. Helper-script-backed.
AskUserQuestion:
| Question | Header | Options |
|---|---|---|
Run /power-pages:plan-alm first? |
ALM plan gate | Yes — run /power-pages:plan-alm now (Recommended), Continue without a plan (advanced — I know what I’m doing), Cancel |
- Yes (Recommended) → invoke
/power-pages:plan-alm. plan-alm’s Phase 7 dispatches back into this skill at the appropriate stage. - Continue without a plan → set
BYPASSED_PLAN_GATE = trueand proceed to Phase 1. - Cancel → exit cleanly.
Step 4 — Stale plan. Tell the user:
“ALM plan exists from
{generatedAt}but the source solution has been modified since (at{solution.modifiedon}). Components may have changed. Re-runningplan-almwill refresh the analysis and the rendered HTML.”
🚦 Gate (intent · import-solution:0.stale-plan): Fail-closed entry gate when
check-alm-plan.jsreturnsstale:true. Helper-script-backed.
AskUserQuestion:
| Question | Header | Options |
|---|---|---|
| Refresh the plan first? | ALM plan freshness | Refresh — re-run /power-pages:plan-alm (Recommended), Continue with the existing plan, Cancel |
- Refresh (Recommended) → invoke
/power-pages:plan-alm. After completion, re-run the Phase 0 helper once to confirm freshness; if still stale, surface the detail and proceed to Phase 1 anyway (don’t infinite-loop). - Continue → set
STALE_PLAN_ACK = trueand proceed to Phase 1. - Cancel → exit cleanly.
Why this gate exists. Direct invocation of import-solution deploys a zip into a target environment without the orchestrator’s deployment-strategy selection or post-import validation steps. Users running this skill standalone often skip the staged-import dependency check, miss env var override values for the target environment, and have no plan-tracked record of which environment received which artifact version. The gate ensures plan-alm either ran (so the strategy was selected, per-stage values were captured in deployment-settings.json, and the deployment is reproducible) or the user explicitly chose to bypass it.
Phase 1 — Verify Prerequisites
Create all tasks upfront at the start of this phase.
Tasks to create:
- “Verify prerequisites”
- “Locate solution file”
- “Configure import”
- “Stage solution (dependency check)”
- “Import solution”
- “Verify import”
- “Detect cloud flows”
- “Present summary”
Note: If the import fails with an
AttachmentBlockederror, a Phase 5b remediation flow runs inline — no additional task is needed (it continues within the “Import solution” task). The “Detect cloud flows” task is skipped automatically if no Workflows/*.json files are present in the solution zip.
Steps:
- Run
pac env who— extractenvironmentUrl(verify this is the target environment) - Run
az account get-access-token --resource "{environmentUrl}" --query accessToken -o tsv— capture token - Verify API access:
GET {environmentUrl}/api/data/v9.2/WhoAmI - Present target environment URL and ask user to confirm this is correct before proceeding.
Important: Confirm the target environment with the user — importing to the wrong environment can be disruptive.
If any check fails, stop (reference ${CLAUDE_PLUGIN_ROOT}/references/dataverse-prerequisites.md).
Phase 1.5 — Ground in current ALM documentation
Reference:
${CLAUDE_PLUGIN_ROOT}/references/alm-docs-grounding.md
Cap this step at ~30 seconds. If MCP search / fetch errors out, log a one-line note and continue — this skill must remain runnable offline.
- Run
microsoft_docs_searchwith the query:Power Pages solution import staging missing dependencies ImportSolutionAsync ALM. - Fetch
https://learn.microsoft.com/en-us/power-platform/alm/solution-concepts-alm(and at most one sister page on staged imports or dependency handling) in parallel viamicrosoft_docs_fetch. - Extract a one-paragraph summary of what Microsoft Learn currently says about staging vs direct import, dependency resolution, and component-level error handling. Compare against
${CLAUDE_PLUGIN_ROOT}/references/solution-api-patterns.mdand flag any divergence inImportSolutionAsync/StageSolutionsignatures. - Use the summary to inform Phase 2+ decisions. Do not silently change skill behavior — surface any divergence to the user as a soft warning before Phase 4 (the actual import).
Phase 2 — Locate Solution File
- If a zip path was provided as an argument, use it directly
- Otherwise, search for solution zips:
glob('**/*.zip', { ignore: ['**/node_modules/**'] }) - For each found zip, verify it contains
solution.xml:- Use
Bash:unzip -l "{zipPath}" 2>/dev/null | grep -qi solution.xml
- Use
-
If multiple valid zips found, ask user to choose:
🚦 Gate (plan · import-solution:2.multiple-zips): More than one valid solution zip was discovered under the project root. User picks which one to import. Cancel exits before any target-env interaction.
Use
AskUserQuestionwith one option per valid zip — show filename + size + modified date so the user can identify the right one (most-recent export is usually the intended target). - If no valid zip found: stop and explain — run
export-solutionfirst or provide the zip path
Step 5a — Pre-import content inspection (run after zip is confirmed, before presenting to user):
Inspect the zip to surface post-import manual requirements. Run all checks in sequence:
# 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
Build a postImportWarnings list from the results:
| Finding | Warning to surface |
|---|---|
connectionreference count > 0 |
“⚠️ Connection references: This solution includes N connection(s) (e.g. Dataverse connector). After import, you must bind each connection reference to a live connection in the target environment, or cloud flows that depend on them will be disabled.” |
bots/ folder present |
“⚠️ Copilot Studio bot: This solution includes a bot. After import, the bot must be republished in the target environment to complete provisioning.” |
| Hardcoded org URL found | “⚠️ Hardcoded environment URL: The cloud flow contains a hardcoded environment URL ({foundUrl}). This will cause the flow to call the source environment after import. Edit the flow in the target environment to update the URL.” |
If postImportWarnings is non-empty, display all warnings inline (not via AskUserQuestion) before presenting the zip details. The user should see these before confirming.
Present the selected zip file details (name, size, path), any pre-import warnings, and confirm with user.
Phase 3 — Configure Import
Step 3.0 — Version-skew advisory (read-only check before any prompt).
Before asking the user about staged/direct import, query the target environment for the solution unique name carried in the zip and compare the installed version against the zip’s version. The goal is to surface “you’re about to import the same version that’s already installed” before the user clicks through — this is the most common silent-failure pattern for the manual export/import path, because the source-side bump is what produces an unambiguously promotable artifact.
- Extract the zip’s
uniqueName+versionfromsolution.xmlinside the zip:unzip -p "{zipPath}" solution.xml 2>/dev/null | head -50Parse
<UniqueName>and<Version>from the XML. Store asZIP_SOLUTION_NAMEandZIP_SOLUTION_VERSION. - Query the target for the installed solution:
GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{ZIP_SOLUTION_NAME}'&$select=solutionid,uniquename,version,ismanagedStore the result as
INSTALLED(ornullif the filter returns an emptyvaluearray). -
Compare versions via the shared helper, then branch on the result.
Precondition — skip this entire step when
INSTALLEDisnull. That’s the first-time-install case: there’s nothing on the target to compare against. Do NOT call the helper withnullsubstituted into'{INSTALLED.version}'— it would throw “version is required” and leave the agent without a branch. Jump straight to the staged/direct prompt below and treat the import as a fresh install.Otherwise (
INSTALLEDis non-null), compare versions:Critical: do not compare version strings with raw
>/</===— Dataverse versions are 4-segment integer tuples (1.0.0.9vs1.0.0.10) and lexical comparison reports1.0.0.10as lower than1.0.0.9, flipping the skew gate on the 10th deploy of the day. Use the canonical helper instead:node -e "console.log(require('${CLAUDE_PLUGIN_ROOT}/scripts/lib/bump-solution-version').compareVersions('{ZIP_SOLUTION_VERSION}', '{INSTALLED.version}'))"The helper returns
-1when ZIP < INSTALLED,0when equal,1when ZIP > INSTALLED. Same segment-wise integer rules asbumpPatchSegment(pad-with-zero, max-4-segments, reject non-integer). Capture stdout, trim, and store the integer asVERSION_CMP. If the helper throws (malformed version on either side), surface the stderr to the user and stop — the version comparison is a precondition for safe import.INSTALLEDVERSION_CMPBehavior null(helper not called — see precondition above) First-time install. Continue silently to the staged/direct prompt below. not null 1(zip is strictly greater)Normal upgrade. Report: “Target has v{INSTALLED.version} installed; this zip is v{ZIP_SOLUTION_VERSION}. Importing will upgrade.” not null 0(zip equals installed)Surface the warning below (same-version skew — applies to both managed and unmanaged). not null -1(zip is strictly less)Surface the warning below (downgrade — applies to both managed and unmanaged). 🚦 Gate (consent · import-solution:3.0.version-skew): Zip version is equal-to or lower-than the installed solution’s version on the target. Importing produces unpredictable upgrade semantics; the source
export-solutionis supposed to bump the version on every export. Re-export with bumped version, force the import anyway, or cancel.Warning prompt —
AskUserQuestion:“The target environment already has
{ZIP_SOLUTION_NAME}v{INSTALLED.version}installed ({INSTALLED.ismanaged ? ‘managed’ : ‘unmanaged’}). The zip you are about to import carries version{ZIP_SOLUTION_VERSION}.Importing the same or a lower version is unreliable:
- Managed: no upgrade lineage; the platform may apply or reject the import depending on internal heuristics.
- Unmanaged: behavior depends entirely on
OverwriteUnmanagedCustomizations: true, and any in-target edits that happen to match the zip’s component IDs get silently overwritten without a version change to point to.
/power-pages:export-solutionalways bumps the source version before producing a zip (since 2026-05-25). If this zip was produced before that change, re-exporting will give you a clean, strictly-greater version.How would you like to proceed?”
Question Header Options What to do? Version skew Re-export with a bumped version (Recommended — invokes /power-pages:export-solution), Import anyway (proceed at your own risk), Cancel - Re-export: invoke
/power-pages:export-solution. After it completes, restart this skill from Phase 2 with the freshly-produced zip. - Import anyway: set
SKEW_ACK = trueand proceed to the staged/direct prompt below. Record the acknowledged skew indocs/alm/last-import.jsonunderversionSkew: { zipVersion, installedVersion, isManaged, acknowledged: true }for the audit trail. - Cancel: stop the skill cleanly. No target-env writes happened in Step 3.0 — it was read-only.
- If the comparison is the normal upgrade case (or the zip’s solution isn’t installed yet), continue silently — do not present a prompt, just record the version delta in the eventual
docs/alm/last-import.jsonfor the summary.
🚦 Gate (plan · import-solution:3.config): Staged vs Direct import, overwrite options. Cancel exits before any target-env mutation.
Ask user (via AskUserQuestion):
Key Decision Point: Staged vs Direct import
- Staged (Recommended for managed solutions): Runs
StageSolutionfirst to check for missing dependencies. Shows issues before committing the import. Safer — the stage step is fully reversible.- Direct: Skips staging and imports immediately. Faster but may fail mid-import if dependencies are missing.
Also ask:
- Overwrite unmanaged customizations? (default: Yes) — needed when target has customized the same components
- Publish workflows after import? (default: Yes)
Phase 4 — Stage Solution (Conditional)
Only run this phase if the user chose staged import in Phase 3.
Refer to ${CLAUDE_PLUGIN_ROOT}/references/solution-api-patterns.md Section 5a.
- Base64-encode the zip file:
node "${CLAUDE_PLUGIN_ROOT}/scripts/encode-solution-file.js" --zipPath "{zipPath}" POST {envUrl}/api/data/v9.2/StageSolutionwithCustomizationFile: {base64}- Parse
StageSolutionResults:- Extract
StageSolutionUploadId(used in Phase 5 instead of re-encoding the file) - Check
MissingDependenciesarray
- Extract
- If
MissingDependenciesis non-empty:- List each missing dependency with its type and name
- Ask user: “These dependencies are missing in the target environment. Proceed anyway (may fail) or cancel to install dependencies first?”
- If cancel: stop and advise installing missing dependencies
- If
MissingDependenciesis empty: report “No missing dependencies found. Ready to import.”
Phase 5 — Import Solution
Refer to ${CLAUDE_PLUGIN_ROOT}/references/solution-api-patterns.md Section 5b.
- Prepare request body (always use
CustomizationFile—ImportSolutionAsyncdoes not acceptStageSolutionUploadId):- Encode the zip:
node "${CLAUDE_PLUGIN_ROOT}/scripts/encode-solution-file.js" --zipPath "{zipPath}" - Use
{ CustomizationFile: "{base64}", OverwriteUnmanagedCustomizations: {choice}, PublishWorkflows: {choice} }
- Encode the zip:
POST {envUrl}/api/data/v9.2/ImportSolutionAsync- Extract
AsyncOperationIdandImportJobKey(note: field isImportJobKey, notImportJobId) - Report: “Import job started:
{AsyncOperationId}. Polling for completion…”
Run scripts/poll-async-operation.js:
node "${CLAUDE_PLUGIN_ROOT}/scripts/poll-async-operation.js" \
--asyncJobId "{AsyncOperationId}" \
--envUrl "{envUrl}" \
--token "{token}" \
--intervalMs 8000 \
--maxAttempts 75
Handle poll result:
Succeeded: proceed to Phase 6FailedwithAttachmentBlocked/ error code-2147188706: proceed to Phase 5b belowFailed(other): show error message, query import job for component-level errors, stopTimeout: inform user, advise checking admin center
Phase 5b — Resolve Attachment Restrictions (conditional)
Only run this phase if Phase 5 poll failed with AttachmentBlocked (-2147188706 or message contains AttachmentBlocked or not a valid type).
5b.1 Identify Blocked Extensions in the Solution Zip
List all files in the zip and extract unique extensions:
unzip -l "{zipPath}" | awk '{print $4}' | grep '\.' | sed 's/.*\.//' | sort -u
Get the current blocked attachments list from the environment:
pac env list-settings
Find the blockedattachments row in the output — it contains a semicolon-separated list (e.g., ade;adp;js;zip;...).
Compute the intersection: which extensions from the solution zip appear in the blocked list. These are the types that need to be unblocked.
5b.2 Explain the Issue
Tell the user:
“The solution import failed because the target environment blocks certain file types that are included in this solution. The following file extensions in the solution are currently blocked:
{comma-separated list}. This is an environment-level security setting. To import this solution, these restrictions need to be temporarily relaxed.”
5b.3 Ask for Permission
🚦 Gate (consent · import-solution:5b.blocked-attachments): Reactive
AttachmentBlockedremediation — modify env-levelblockedattachmentssetting (tenant-wide impact). Reversible from PPAC. Fires fresh on every skill invocation that hits the failure. Whenplan-almManual path orchestrates multi-target imports (Staging then Production), it invokesimport-solutiononce per target — if both targets block the same extensions, the gate fires once per target (each is a separate skill invocation against a separate env). Consent for Staging does NOT cover Production.
Invoke AskUserQuestion immediately — do NOT present this as a chat message. The user must answer live before the skill proceeds.
| Question | Header | Options |
|---|---|---|
The solution contains file types ({list}) that are blocked by this environment’s attachment security settings. Would you like to remove the block for these specific types so the solution can be imported? |
Unblock Attachment Types | Yes, unblock {list} for this import (Recommended), No, do not change environment settings |
If “No”: Stop and tell the user: “The import cannot proceed while these file types are blocked. To unblock manually: Power Platform Admin Center → Environments → {env} → Settings → Product → Features → Blocked Attachments.”
If “Yes”: Proceed to 5b.4.
5b.4 Update Blocked Attachments
- Parse the
blockedattachmentsvalue (semicolon-separated) - Remove only the extensions identified in 5b.1 — preserve all others
- Update the setting:
pac env update-settings --name blockedattachments --value "{updated-list-with-types-removed}" - Confirm the update succeeded.
5b.5 Retry Import
Re-encode the zip and retry ImportSolutionAsync (repeat Phase 5 steps 1–4 and poll again).
- If
Succeeded: proceed to Phase 6 - If failed again with a different error: show the new error message and stop — do not retry further
Phase 6 — Verify Import
- Query solution to confirm it exists and version matches:
GET {envUrl}/api/data/v9.2/solutions?$filter=uniquename eq '{solutionName}'&$select=solutionid,uniquename,version,ismanaged - Query import job for component results (use
ImportJobKeyfrom the import response):GET {envUrl}/api/data/v9.2/importjobs({ImportJobKey})?$select=solutionname,completedon,progress,data- Parse the
dataXML field for per-component results (look forresult="failure"entries) - Count: imported successfully / warnings / failures
- Parse the
- Ensure
docs/alm/exists, then writedocs/alm/last-import.jsonmarker (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 }The
statusfield drivesrefresh-alm-plan-data.js’s step-sync (completedvsfailed) for the rendered ALM plan’s per-stage checklist. Set it to:Succeededwhen the import job’sstatecodeis 3 (Succeeded) AND component-results showfailure === 0.Partialwhenstatecodeis 3 butcomponentResults.failure > 0(the solution landed but some components didn’t import — usually managed-property conflicts or dependency gaps).Failedwhenstatecodeis 4 (Failed) OR the import never reached terminal state OR all components failed. Without this field, every import showscompletedin the rendered plan regardless of actual outcome.
If the user acknowledged a same-version or downgrade import in Step 3.0 (
SKEW_ACK = true), setversionSkewto:{ "zipVersion": "<ZIP_SOLUTION_VERSION>", "installedVersion": "<INSTALLED.version>", "isManaged": <bool>, "acknowledged": true }Otherwise leave
versionSkew: null.
Phase 6b — Set Environment Variable Values (if any)
After a successful import, env var definitions travel in the solution but their values do not. The target environment will have the definition records but blank values until explicitly set.
Query the target environment for any env var definitions from this solution:
GET {envUrl}/api/data/v9.2/environmentvariabledefinitions?$filter=introducedversion ne null&$select=schemaname,displayname,type,defaultvalue,environmentvariabledefinitionid
Filter to only those whose schemaname starts with the publisher prefix (from .solution-manifest.json), or cross-reference with solutioncomponents if available.
For each definition found, check if a value already exists in the target:
GET {envUrl}/api/data/v9.2/environmentvariablevalues?$filter=_environmentvariabledefinitionid_value eq '{id}'&$select=value
🚦 Gate (plan · import-solution:6b.env-vars): Imported env var definitions need per-target values. User supplies values or skips (uses default). Without values, runtime reads default which may be dev-only.
If any definitions have no existing value, present them to the user via AskUserQuestion:
“The imported solution contains {N} environment variable(s) with no value set in this environment. Enter the target value for each (leave blank to skip and use the default):
{schemaname}({displayname}) — default:{defaultvalue ?? 'none'}- …”
For each value the user provides, POST an environmentvariablevalue record:
POST {envUrl}/api/data/v9.2/environmentvariablevalues
{
"schemaname": "{schemaname}",
"value": "{userValue}",
"[email protected]": "/environmentvariabledefinitions({id})"
}
Note on Secret type (type 100000005): Secret values are stored encrypted. The POST behaves the same but the value will be masked in the UI. The user should provide the actual secret value for the target environment (e.g. the OAuth client secret for the production tenant’s app registration — different from the dev value).
If the user skips all values: inform them the site may not function correctly until values are set, and provide the direct Power Platform URL to set them manually:
https://{targetEnvHost}/main.aspx?appid=...&etn=environmentvariabledefinition
6b.verify — Confirm values landed. After the per-variable POSTs complete, verify each environmentvariablevalues record actually exists on the target. The shared helper scripts/lib/verify-env-var-values.js does this read-only check and returns a structured JSON result per schema (landed / missing-value-record / missing-definition / value-mismatch / query-error):
node "${CLAUDE_PLUGIN_ROOT}/scripts/lib/verify-env-var-values.js" \
--envUrl "{targetEnvUrl}" \
--schemaNames "{comma-separated schema names that the user supplied values for}"
Capture stdout as JSON. If summary.missing > 0 or summary.error > 0, surface a single warning to the user with the affected schema names — but do not block the import summary, the import itself succeeded. The same helper is used at deploy time (deploy-pipeline Phase 7.6.5) and at configure time (configure-env-variables Phase 7); centralizing the check keeps the user-visible message consistent across skills.
Phase 6c — Detect Cloud Flows (if any)
After import succeeds, check whether the solution contains cloud flow JSON files. Use the zip path located in Phase 2.
unzip -l "{zipPath}" | grep -i "^.*Workflows/.*\.json"
If no matching files are found, skip this phase entirely.
If cloud flow files are found:
- Extract the flow name from each path (pattern:
Workflows/FlowName-GUID.json— strip the path prefix and GUID suffix to get the display name) -
Inform the user:
“This solution contains {N} cloud flow(s). Cloud flows must be registered with the Power Pages site in the target environment after import.”
Flows detected:
{FlowName1}{FlowName2}…
To register: Power Pages Management → target environment → Edit site → Set up → Cloud flows → register each flow listed above.
Direct link:
https://make.powerpages.microsoft.com/🚦 Gate (plan · import-solution:6c.cloud-flow-register): Cloud flows in imported solution need manual registration in target env. Acknowledge / defer.
-
Invoke
AskUserQuestion:Question Header Options Have you registered the cloud flow(s) listed above with the Power Pages site in {targetEnvUrl}?Cloud Flow Registration Flows registered — continue, I’ll register them later - Record the user’s response as
cloudFlowStatus:"Registered"or"Pending registration". This status is shown in the Phase 7 summary row — either answer allows the skill to continue.
Phase 6d — Check Site Activation (if Power Pages solution)
Only run this phase if the solution contains Power Pages website components (componentType 10374):
GET {envUrl}/api/data/v9.2/solutioncomponents?$filter=_solutionid_value eq '{solutionId}' and componenttype eq 10374&$select=objectid
If no componentType 10374 records found, skip this phase entirely.
If found, run the shared activation status check (PAC CLI is already authenticated to the target environment):
node "${CLAUDE_PLUGIN_ROOT}/scripts/check-activation-status.js" --projectRoot "."
Evaluate the result:
activated: true: StoresiteUrlfor the Phase 7 summary. No further action needed.
🚦 Gate (plan · import-solution:6d.activate): Site imported but not activated in target env. Offer to invoke activate-site now or defer.
-
activated: false: Ask the user viaAskUserQuestion:Question Header Options The Power Pages site was imported but is not yet activated (provisioned) in {envUrl}. Activate it now to make it publicly accessible.Activate Site Yes, activate now (Recommended), No, I’ll activate later - If “Yes”: Invoke
/power-pages:activate-site. The activate-site skill will handle subdomain selection, confirmation, and provisioning. - If “No”: Note in the Phase 7 summary that activation is pending and remind the user to run
/power-pages:activate-sitewhen ready.
- If “Yes”: Invoke
-
errorpresent: Skip silently — do not block the summary. Note in Phase 7 that activation status could not be determined.
Phase 7 — Present Summary
Display a summary table:
| Item | Value |
|---|---|
| Solution | {solutionName} v{version} |
| Target environment | {envUrl} |
| Managed | Yes / No |
| Components imported | N success, N warning, N failure |
| Env var values set | N of N |
| Cloud flows | {cloudFlowStatus} (Registered / Pending registration / Not applicable) |
| Site activation | Activated at {siteUrl} / Pending / Not applicable |
| Import job | {importJobId} |
Record Skill Usage
Reference:
${CLAUDE_PLUGIN_ROOT}/references/skill-tracking-reference.md
Follow the skill tracking instructions in the reference to record this skill’s usage. Use --skillName "ImportSolution".
Refresh the ALM plan (if one exists)
node "${CLAUDE_PLUGIN_ROOT}/scripts/lib/refresh-alm-plan-data.js" \
--projectRoot "." \
--phase import-solution \
--stageName "{targetLabel}" \
--render
{targetLabel} is the Manual-path target stage (e.g. Staging, Production) the just-completed import was for — usually carried in by the orchestrator (plan-alm Phase 7) or derivable from the target env URL. The helper reads docs/alm/last-import.json, captures the import outcome (status, version, component count, component failures) into planData.manualImports[targetLabel], and re-renders docs/alm-plan.html so the matching Import to {targetLabel} checklist step shows an IMPORTED / FAILED badge with version + component count inline. Subsequent imports to OTHER targets each get their own entry — the renderer surfaces a per-target history rather than overwriting on each call.
If --stageName is omitted the helper falls back to matching docs/alm/last-import.json’s targetEnvironment URL against planData.stages[].envUrl. When the match fails (rare — usually a stage-label/env-URL mismatch in planData), the import is captured under a synthetic key so it isn’t silently lost; pass --stageName explicitly to keep the rendered plan clean. When docs/.alm-plan-data.json is absent, the helper returns ok:false as a soft no-op.
Key Decision Points (Wait for User)
- Phase 1: Confirm target environment — import is not easily undoable for managed solutions
- Phase 2: Select zip file if multiple found
- Phase 3 Step 3.0: Version-skew advisory — only fires when the target already has the solution installed AND the zip’s version is
≤the installed version. Offers re-export with bumped version (Recommended), import-anyway (acknowledged inlast-import.json), or cancel. Read-only check; cancelling here makes no target-env writes. - Phase 3: Staged vs direct import; overwrite customizations
- Phase 4: Proceed despite missing dependencies
- Phase 5b: Consent to unblock attachment types — never modify environment settings without explicit approval
- Phase 6b: Env var values — always prompted if solution contains env var definitions with no existing value in the target; Secret type definitions require the user’s target-environment-specific secret value
- Phase 6c: Cloud flow registration — non-blocking; user may register later; status recorded in summary
- Phase 6d: Site activation — only if Power Pages website components present and site not yet activated
Error Handling
- Component-level import failures: report in summary, do not block overall completion
- If import async operation fails with
AttachmentBlocked(-2147188706): run Phase 5b remediation flow (identify blocked types, get consent, unblock, retry) - If import async operation fails with other error: show
friendlyMessagefrom async operation record, stop - Never attempt rollback — report what succeeded and what failed
- Never modify environment settings (
blockedattachments) without explicit user approval
Progress Tracking Table
| Task subject | activeForm | Description |
|---|---|---|
| Verify prerequisites | Verifying prerequisites | Confirm PAC CLI auth, acquire token, verify target environment with user |
| Locate solution file | Locating solution file | Find and validate solution zip, confirm Solution.xml present |
| Configure import | Configuring import | Step 3.0: extract zip uniqueName+version from solution.xml, query target’s installed version, surface a version-skew advisory if zip version ≤ installed (offer re-export / import-anyway / cancel); then ask: staged vs direct, overwrite customizations, publish workflows |
| Stage solution (dependency check) | Staging solution | Run StageSolution to check for missing dependencies before committing |
| Import solution | Importing solution | POST ImportSolutionAsync, poll until complete; if AttachmentBlocked: identify blocked types, get user consent, unblock via pac env update-settings, retry |
| Verify import | Verifying import | Confirm solution version in target, parse component results, write docs/alm/last-import.json |
| Detect cloud flows | Detecting cloud flows | List Workflows/*.json entries in zip; if found, prompt user to register flows with Power Pages site; record status (Registered / Pending registration) |
| Check site activation | Checking site activation | If solution has componentType 10374: run check-activation-status.js; if not activated, ask user and invoke /power-pages:activate-site |
| Present summary | Presenting summary | Show component counts (success/warning/failure), cloud flow registration status, site activation status, env var values set |