skylib index

Power Platform app design · chapter 3

Solutions and environments

What a solution is on disk, which layer decides a component's behaviour at run time, and how your change reaches an environment nobody develops in.

3.1Publishers and prefixes

the identity a component keeps for its whole life

A publisher is a row in Dataverse that your solution points at. It carries a customization prefix, prepended to the logical name of a component you create inside a solution that names it, and an option value prefix, prepended to the integer values of choices you create there.

The prefix exists to avoid naming collisions, so that solutions from different publishers can be installed in one environment with few conflicts. The publisher of the solution where a component was created is the owner of that component, and once a component has shipped in a managed solution its publisher cannot be changed. Changing a publisher prefix is a thing to do before any metadata exists, because the names of metadata items cannot be changed after they are created.citedsource: Power Platform ALM docs — Solution concepts › Solution publisher prefix · artifacts-ch03.md

A solution project is a directory of XML with no environment behind it. pac solution init writes one, and your publisher is settled at that moment.

Evidence — what init wrote, and the prefixes it chose

pac solution init --publisher-name ch23lab --publisher-prefix ch23

Dataverse solution project with name 'ch23lab' created successfully in: '.../scratchpad/lab/ch23lab'
Dataverse solution files were successfully created for this project in the sub-directory Other, using solution name ch23lab, publisher name ch23lab, and customization prefix ch23.
Please verify the publisher information and solution name found in the Solution.xml file.
The command contacts no organization. It writes ch23lab.cdsproj, a .gitignore, and three files under src/Other/: Solution.xml, Customizations.xml and Relationships.xml.

The publisher, its customization prefix and an option value prefix are written into src/Other/Solution.xml at init time. The lab's project was given the prefix ch23 and the file came back carrying <CustomizationOptionValuePrefix>93714</CustomizationOptionValuePrefix>, which the command wrote without being asked for one.verifiedartifact: ch03-solution-xml · artifacts-ch03.md

src/Other/Solution.xml — as pac solution init wrote it

<UniqueName>ch23lab</UniqueName>
<Version>1.0</Version>
<!-- Solution Package Type: Unmanaged(0)/Managed(1)/Both(2)-->
<Managed>2</Managed>
<Publisher>
  <UniqueName>ch23lab</UniqueName>
  <CustomizationPrefix>ch23</CustomizationPrefix>
  <CustomizationOptionValuePrefix>93714</CustomizationOptionValuePrefix>
</Publisher>
<RootComponents />
The project's <Managed> element is a build setting. A value of 2 tells the packager to produce either kind on request; the value in an exported solution says which kind was exported (§3.2).

Guidance

Settle on one publisher for your tenant's own work before the first table exists, and keep the prefix short. A second publisher introduced later cannot take ownership of what the first one shipped, so it partitions your estate permanently.

3.2Managed and unmanaged

the same components, one element apart on disk

You develop in an unmanaged solution and deploy a managed one. Which kind it is belongs to the export rather than to the solution's identity: one solution unique name exists as both, in different environments, at the same time.

Deleting an unmanaged solution removes the container and leaves the customizations in effect, belonging to the default solution. Deleting a managed solution removes the customizations and extensions it carried. A managed solution cannot be exported — an unmanaged one is exported as managed — and a managed solution cannot be imported into the environment that holds the unmanaged original.citedsource: Power Platform ALM docs — Solution concepts › Managed and unmanaged solutions · artifacts-ch03.md

On disk the two are close together, and a development environment holds both kinds at once. The lab's solution was exported twice from the same environment at the same version, once each way.

Evidence — one environment holding both kinds, and the two exports compared

pac solution list --environment https://org85700aeb.crm.dynamics.com/

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skydude's Environment

Listing all Solutions from the current Dataverse organization...
Unique Name               Friendly Name                         Version Managed
msdyn_AIBuilderSampleData AI Sample Data                        1.0.0.6 True
ch23lab                   ch23lab                               1.0     False
Cra969a                   Common Data Services Default Solution 1.0.0.0 False
Default                   Default Solution                      1.0     False
A development environment holds both kinds at once. The solution under development is unmanaged; a first-party solution installed beside it is managed; and Default and Common Data Services Default Solution are rows in the same list.

In the lab's development environment, pac solution list reported the solution under development as unmanaged and a first-party solution installed in the same environment as managed, in one listing.verifiedartifact: ch03-solution-list-dev · artifacts-ch03.md

pac solution export --environment https://org85700aeb.crm.dynamics.com/ --name ch23lab --path ./out/ch23lab.zip --overwrite · pac solution export --environment https://org85700aeb.crm.dynamics.com/ --name ch23lab --path ./out/ch23lab_managed.zip --managed --overwrite

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skydude's Environment

Starting Solution Export...
Solution export succeeded.

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skydude's Environment

Starting Solution Export...
Solution export succeeded.
The two exports differ at the command line by the --managed flag. The solution name, the environment and the version are the same in both, and the runs produce two files.

Each exported zip holds three members: [Content_Types].xml, customizations.xml and solution.xml. Between the lab's two exports the unique name, the version, the customization prefix and the root component list are identical; <Managed> reads 0 in one and 1 in the other, and customizations.xml differs by 134 bytes.verifiedartifact: ch03-export-managed · artifacts-ch03.md

The exported customizations.xml records the platform build it came from, in the root element's attributes: the lab's exports carry OrganizationVersion="9.2.26065.171" and OrganizationSchemaType="Standard".verifiedartifact: ch03-export-managed · artifacts-ch03.md

Caution

The two deletions are not symmetrical. Delete an unmanaged solution by accident and you have lost a container; delete a managed one and you have lost the tables it carried and the rows in them.

Guidance

Treat the unmanaged export as source and the managed export as a build artifact. Check the unmanaged one into source control unpacked (§3.3), and keep the managed zip out: you can rebuild it from the unmanaged one, and a repository holding both invites the two to disagree.

3.3The layer stack and its merge order

which layer decides what the user sees

A solution holds a layer of a component, not the component. How the component behaves is decided by the arrangement of its layers.

Layering is implemented at the level of the component. An environment has an unmanaged layer, which imported unmanaged solutions and ad-hoc customizations share as one; and managed layers, which imported managed solutions and the system solution occupy, ordered so that the last installed sits above the one installed before it. Uninstalling a managed solution brings the managed layer below it into effect, and the system layer sits at the base.citedsource: Power Platform ALM docs — Solution layers and merge behavior › the two layers · artifacts-ch03.md

Model-driven app, form and site map components are merged across layers; every other component type resolves by "top level wins", where the layer at the top of the stack decides how the component behaves at run time.citedsource: Power Platform ALM docs — Solution layers and merge behavior › merge behavior · artifacts-ch03.md

Because a solution carries a layer and not a component, its export is smaller than the component it names. The lab's solution took the account table as a root component and left it as it found it.

To read a layer you unpack the export. pac solution unpack turns the three-file zip into one file per component, which is the form you commit a solution to source control in.

Evidence — a solution that owns no layer, and the tree its unpack produced

The lab solution's export of the account table is 306 bytes: an Entity.xml holding an entity element marked unmodified="1" with an empty attributes element, beside a root component entry naming the table. The table itself has hundreds of columns in the environment, and the solution carries none of them.verifiedartifact: ch03-account-unmodified · artifacts-ch03.md

unpacked/Entities/Account/Entity.xml — the whole file

<?xml version="1.0" encoding="utf-8"?>
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Name LocalizedName="Account" OriginalName="">Account</Name>
  <EntityInfo>
    <entity Name="Account" unmodified="1">
      <attributes />
    </entity>
  </EntityInfo>
  <RibbonDiffXml />
</Entity>
An empty entity element records that this solution owns no layer of this table. The table's own definition is in the layers underneath this solution's.

pac solution unpack --zipfile out/ch23lab.zip --folder unpacked --packagetype Unmanaged

Unpacking Solution...

Extracting .../out/ch23lab.zip to .../unpacked

Skipping localization
Processing Component: Entities
 - Account
 - ch23_Widget
Processing Component: EntityRelationships

Unmanaged Extract complete.


Unpacked Solution.
The zip's customizations.xml is one file of tens of thousands of bytes; the tree it becomes has an entity definition and a ribbon diff for each table, a relationships file for each referenced table, and one file per saved query.

The unpacked tree of the lab's solution holds twenty files. Among them are seven under Entities/ch23_Widget/SavedQueries/ — views the platform generated for the new table, which the solution project never asked for and which arrive in source control with it.verifiedartifact: ch03-unpack-tree · artifacts-ch03.md

Caution

A quick fix in a downstream environment becomes a permanent one, because the unmanaged layer is shared and singular. Your ad-hoc edit in test lands above every managed layer, and the next managed import cannot displace it — that import changes a layer underneath the one deciding.experientialbasis: downstream environments where a hotfix applied in place survived every later managed import, because the edit landed in the shared unmanaged layer the cited rule above describes

Guidance

When a component behaves in a way its solution does not explain, read the layers before you read the solution. What resolves it is knowing which layer is on top; the solution's own definition of the component describes a layer that may be underneath the answer.

3.4Segmentation, updates and upgrades

what a second import does to what the first one left

What you put in a solution decides which layers it introduces. How you import it decides what happens to the components the previous version carried. You make the two choices separately.

3.4.1Segmentation

When you add a table to a solution you choose how much of the table comes with it. Take the whole table and you introduce layers over components nobody edited.

The documented rule runs both ways. Include all objects belongs to an unmanaged table that does not yet exist in the target environment, and a table that has never been imported there needs it or the import fails on a missing dependency. For a table already downstream, adding components that are not new or changed can cause unexpected behaviour in the existing components, which now sit below the layer the update introduced; the documented example is a view whose existing customizations might become inactive.citedsource: Power Platform ALM docs — Use table segmentation in solutions · artifacts-ch03.md

A solution may be up to 95 MB in size, and a component inside a managed solution cannot be edited in place — editing a managed component means adding it to an unmanaged solution first, which creates a dependency, and the managed solution cannot be uninstalled until that dependency is removed.citedsource: Power Platform ALM docs — Solution concepts › Solution components · artifacts-ch03.md

Evidence — adding one table to a solution by name

pac solution add-solution-component --environment https://org85700aeb.crm.dynamics.com/ --solutionUniqueName ch23lab --component account --componentType 1 --AddRequiredComponents false

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skydude's Environment
Attempting to add the solution component(s) to the ch23lab solution in the skydude's Environment environment.
The account component has been added to the ch23lab solution.
The output names the component that was added and the solution it went into. It does not report which of the table's assets came with it.

A table already in the environment is added to a solution as a root component by name and component type, with component type 1 naming the entity; the lab's run added account to ch23lab with --AddRequiredComponents false.verifiedartifact: ch03-add-component · artifacts-ch03.md

Guidance

Segment by default for a table that is already downstream, and take the whole table only on its first trip. The two wrong choices fail differently: on a new table the import fails loudly, and on an existing table it succeeds and disables somebody else's customization quietly.

3.4.2Update, upgrade and patch

You can replace a managed solution that is already installed downstream in more than one way, and the ways differ in what they do to components the new version no longer carries.

The documented import actions differ in exactly that respect. Upgrade is the default; it rolls up previous patches in one step and deletes components associated with the previous version that the newer version does not carry. Stage for upgrade defers that deletion until the upgrade is applied later, and the documentation says to choose it when both the old and the new solution should be installed concurrently so that data can be migrated first. Update replaces the solution and leaves components the newer version omits in place.citedsource: Power Apps docs — Update a solution › solution action options · artifacts-ch03.md

A patch carries the changes for a parent managed solution and cannot delete components, and neither can an update.citedsource: Power Platform ALM docs — Solution concepts › Solution lifecycle · artifacts-ch03.md

Patches and a staged upgrade are layers within a managed solution. The base layer sits at the bottom and carries the publisher and the managed properties; patches stack on the base, most recent above previous; a staged upgrade, named with an _Upgrade suffix, sits on top of both; and the top layer defines the component's run-time behaviour. The same page recommends against using patches.citedsource: Power Platform ALM docs — Solution layers and merge behavior › layering within a solution · artifacts-ch03.md

A solution's version has the form major.minor.build.revision, and an update needs a higher value in one of those positions than the parent solution has.citedsource: Power Apps docs — Update a solution › version numbers · artifacts-ch03.md

The documentation describes staging as leaving the base solution installed beside a second solution suffixed _Upgrade, completed later by applying the upgrade — and notes that the single-step upgrade has since been optimised to use neither a temporary _Upgrade solution nor an uninstall of the original.citedsource: Power Apps docs — Update a solution › completing a staged upgrade · artifacts-ch03.md

pac solution import --force-overwrite is the CLI's name for the overwrite-customizations option, which removes unmanaged customizations previously made to the components the solution carries. Components that support merge behaviour — forms, site map, ribbon and app modules — are unaffected by it.citedsource: Power Apps docs — Update a solution › Overwrite customizations option · artifacts-ch03.md

You set the version on the solution in the source environment. In the lab it was raised there, exported, and imported downstream as an upgrade.

Evidence — the version bump, and the upgrade the test environment took

pac solution online-version --environment https://org85700aeb.crm.dynamics.com/ --solution-name ch23lab --solution-version 1.0.1.0 · pac solution online-version --environment https://org85700aeb.crm.dynamics.com/ --solution-name ch23lab

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skydude's Environment

… the write printed nothing further; the read below is the same command without --solution-version …

Listing all Solutions from the current Dataverse organization...
Unique Name: ch23lab
Solution Display Name: ch23lab
Solution Version: 1.0.1.0
The write printed no confirmation; the read reports the value. A script that treats an empty stdout as a failed version bump will retry a command that already succeeded.

Setting a solution's version with pac solution online-version printed the connection banner and nothing else on success; the same command without a version argument reported the new value.verifiedartifact: ch03-online-version · artifacts-ch03.md

Importing the raised version into the test environment with --stage-and-upgrade completed in one command and left one row for the solution, at version 1.0.1.0 and marked managed. No ch23lab_Upgrade solution remained in the list afterwards.verifiedartifact: ch03-stage-and-upgrade · artifacts-ch03.md

pac solution import --path out/ch23lab_managed_1_0_1_0.zip --environment https://org5014704c.crm.dynamics.com/ --stage-and-upgrade --publish-changes · pac solution list --environment https://org5014704c.crm.dynamics.com/

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skylib-test

Solution Importing...

Solution Imported successfully.

Publishing All Customizations...

Published All Customizations.

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skylib-test

Listing all Solutions from the current Dataverse organization...
Unique Name Friendly Name                         Version Managed
ch23lab     ch23lab                               1.0.1.0 True
ch45Lab     ch45Lab                               1.0     False
Cra969a     Common Data Services Default Solution 1.0.0.0 False
Default     Default Solution                      1.0     False
The lab's test environment holds work this chapter did not create: ch45Lab is not one of its solutions. It appears here because the command listed it.

Caution

The lab's single-command result and the documentation's staged description are the same feature at two points in its history. A runbook written against the older behaviour waits for a solution that is no longer there.

Guidance

Read a solution's version back after an upgrade to confirm it, not the presence or absence of a holding solution.

3.5Environment types and a dev, test and prod strategy

what the type decides, and what naming decides

An environment is a boundary around data, apps and connections. Its type decides what the platform will let it do, not what you use it for, so a pipeline can be built out of environments whose types do not describe their roles.

The documented types are production, default, sandbox, trial, developer and Dataverse for Teams. A sandbox is a non-production environment offering copy and reset. A developer environment is created by a user holding the Developer Plan licence and is intended for use by its owner. A trial expires after 30 days.citedsource: Power Platform admin docs — Environments overview › Environment types · artifacts-ch03.md

Each tenant has one default environment, shared by its users, which cannot be deleted and which every new Power Apps user joins as a maker. The documentation states that it offers no backup guarantees and should not carry production workloads.citedsource: Power Platform admin docs — Environments overview › the default environment · artifacts-ch03.md

An environment holds zero or one Dataverse database, and an app in one environment is permitted to connect only to the data sources deployed in that same environment. An environment's resources can be accessed only by users within the tenant it was created under.citedsource: Power Platform admin docs — Environments overview › scope · artifacts-ch03.md

The CLI reports the type, so one command shows you the gap between an environment's type and the role you have given it.

The worked deployment for this chapter ran between two of them: the solution was authored unmanaged in dev, exported as managed, and imported into test.

Evidence — the three environments, and the trip from dev to test

pac admin list

Connected as skydude@itrancyngergmail.onmicrosoft.com

Listing all environments from your tenant...

Listing environment groups from your tenant...
Active Environment           Group Environment ID                       Environment Url                       Type      Organization ID
       skydude's Environment -     3caaae88-df92-e1ea-9467-7ce90a51b602 https://org85700aeb.crm.dynamics.com/ Developer c5fa366c-5190-f111-b8cf-6045bd056918
       skylib-prod           -     908e4dd8-b8c1-e164-a999-5c8dab720e1b https://orge76abca4.crm.dynamics.com/ Developer 604d6368-5890-f111-b8cf-6045bd0a1f2d
*      skylib-test           -     f6ee4b3f-fc72-e328-86b8-7f90829cf31e https://org5014704c.crm.dynamics.com/ Developer 644d6368-5890-f111-b8cf-6045bd0a1f1f
The three environments in this tenant report one type, Developer. Their names are what separate this guide's dev, test and prod.

pac admin list reports an environment's type alongside its URL. In the tenant behind this guide, the three environments named for development, test and production stand-in are of type Developer.verifiedartifact: ch03-admin-list · artifacts-ch03.md

pac solution import --path out/ch23lab_managed.zip --environment https://org5014704c.crm.dynamics.com/ --publish-changes

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skylib-test

Solution Importing...

Solution Imported successfully.

Publishing All Customizations...

Published All Customizations.
The export and the import are joined by a file. The table the solution carried was then queryable in the destination.

After the import, the table the solution carried answered a FetchXML query in the destination environment.verifiedartifact: ch03-import-test · artifacts-ch03.md

The destination's listing reports ch23lab as managed, at version 1.0.verifiedartifact: ch03-solution-list-test · artifacts-ch03.md

pac solution list --environment https://org5014704c.crm.dynamics.com/

Connected as skydude@itrancyngergmail.onmicrosoft.com
Connected to... skylib-test

Listing all Solutions from the current Dataverse organization...
Unique Name Friendly Name                         Version Managed
ch23lab     ch23lab                               1.0     True
ch45Lab     ch45Lab                               1.0     False
Cra969a     Common Data Services Default Solution 1.0.0.0 False
Default     Default Solution                      1.0     False
The destination reports the solution as managed, at the version the source exported. The capture was taken immediately after the import, before the upgrade later in this chapter raised the version.

Caution

Passing --environment to a subcommand moves the auth profile's active organization to that environment and leaves it there. The active organization is state on disk that every pac process on the machine shares, so a command naming its environment changes the target of the next command that does not.verifiedartifact: ch03-env-list-active · artifacts-ch03.md

Guidance

Name the environment on each command in a deployment script rather than selecting one at the top. An interleaved run, a second terminal or a colleague on the same machine can move the active organization after your script has selected, and the command that then lands in the wrong environment is an import.

3.6The deploy-time seam

environment variables and connection references

Environment variables and connection references let you name a value in the solution and supply it at import time. That is what makes one build artifact deployable to several environments.

An environment variable is stored as a definition and a value in two places: the default value belongs to the definition, the current value belongs to the value record, and a value cannot exist without a definition. The documented data types are decimal number, text, JSON, two options, data source and secret.citedsource: Power Apps docs — Use environment variables › definition and value · artifacts-ch03.md

The documented practice is to ship the definition in the solution and supply the value for the target environment at deployment, which leaves the definition a managed object downstream and the value an unmanaged record. A variable is limited to 2,000 characters, and a changed value may take up to an hour to reach the apps and flows that read it, because it is pushed to them asynchronously.citedsource: Power Apps docs — Use environment variables › deployment guidance and limits · artifacts-ch03.md

A connection is a stored authentication credential for a connector; a connection reference is a solution component pointing at one. Solution-aware flows and canvas apps bind to the reference rather than to the connection, and a connection is supplied for each connection reference during import so that referencing flows can be turned on afterwards. Flows use connection references for every connector; canvas apps use them only for implicitly shared, non-OAuth connections.citedsource: Power Apps docs — Use a connection reference in a solution · artifacts-ch03.md

A hand-maintained settings file drifts from the solution silently. Add a connection reference in dev, and downstream the file is missing it, the import supplies nothing for it, and the flow that uses it arrives switched off with no error at import time.experientialbasis: deployments where a flow reached production disabled because its connection reference had no connection, and the settings file was generated once and then hand-edited

You supply both through a deployment settings file, and the CLI generates that file from the solution rather than asking you to write it.

Evidence — the settings file the CLI generated from the solution

pac solution create-settings --solution-zip out/ch23lab_managed.zip --settings-file out/ch23lab.settings.json

Extracting Dataverse connection references and/or environment variables from: .../out/ch23lab_managed.zip
Deployment settings file created: .../out/ch23lab.settings.json
The settings file is generated from the solution zip. A solution with no deferred value produces one anyway.

pac solution create-settings read the lab's managed export and wrote a settings file with three keys — EnvironmentVariables, ConnectionReferences and CopilotAgents — each holding an empty list, since the solution defers no value.verifiedartifact: ch03-create-settings · artifacts-ch03.md

Guidance

Regenerate the deployment settings file from the solution on each build.

Remove a variable's value from the solution before you export when the value belongs to the development environment. Leave it in and you ship a dev-shaped value the target has to be corrected away from — and that correction is an unmanaged record above a managed definition (§3.3).