To move Power Apps and Power Automate flows safely from development to test to production, build everything inside one unmanaged solution in a development environment, externalise every environment-specific value into environment variables and connection references, and deploy the solution as a managed artifact. Power Platform pipelines do this from inside the maker portal with pre-deployment validation, approvals and automatic backups; Azure DevOps with the Power Platform Build Tools or the pac CLI covers source control, cross-tenant targets and anything pipelines can't do yet.
Who this is for and what you will have
This guide is for Power Platform administrators, makers and developers who still export zip files by hand or fix broken connections after every import. At the end you will have a clear environment layout, a solution built with your own publisher, environment variables and connection references, a two-stage Power Platform pipeline (optionally deployed by a service principal behind an approval), and an Azure DevOps alternative for what pipelines don't cover.
If your apps sit on SharePoint lists rather than Dataverse, read SharePoint lists vs Dataverse for Power Apps first: solutions and pipelines still need a Dataverse database in every participating environment, even when the app's data lives in SharePoint.
Core concepts you need before you start
Managed and unmanaged solutions
A solution is either unmanaged or managed. Unmanaged solutions are what you develop in; Microsoft describes them as your source for Power Platform assets, and they belong in source control. Managed solutions are what you deploy to every environment that isn't the development environment for that solution: test, UAT, system integration testing and production.
The differences matter in practice:
| Behaviour | Unmanaged | Managed |
|---|---|---|
| Where it lives | Development environment | Test, UAT, production |
| Can you edit components directly | Yes | No; you must add them to an unmanaged solution, which creates an unmanaged layer |
| Can you export it | Yes, as unmanaged or as managed | No |
| Deleting the solution | Removes only the container; customizations stay in the default solution | Removes all its customizations, including data in its custom tables and columns |
You can't import a managed solution into the environment that holds the originating unmanaged solution, so you need a separate environment to test the managed build. And because deleting a managed solution deletes data in its custom tables, never uninstall one in production to "start again".
Publisher and prefix
Create your own publisher (for example, Contoso with prefix contoso) instead of using the default, and decide on the prefix before you build anything, because component names can't be changed after creation. Use a single publisher across related solutions: component ownership can move between solutions of the same publisher, but not across publishers.
Update, upgrade and patch
An upgrade removes components that are no longer in the new version and rolls up any patches; an update and a patch can't delete components. Power Platform pipelines always import as an upgrade without overwriting customizations, and that isn't configurable today.
Environment variables and connection references
These two solution components are what make the same managed artifact work in every environment.
- Environment variables hold parameters such as a SharePoint site and list, an API base URL, or a feature switch. Supported data types are Decimal number, Text, JSON, Two options, Data source and Secret (backed by Azure Key Vault). Each variable has an optional default value (part of the definition) and an optional current value (a separate record). A current value takes precedence over the default. Values are limited to 2,000 characters.
- Connection references point a solution-aware flow or canvas app at a connection without hard-coding it. Flows use connection references for all connectors; canvas apps use them only for implicitly shared connections such as SQL Server authentication. During import you supply a connection for each reference, and referencing flows can then be turned on automatically.
Prerequisites
- A Dataverse database in every environment that takes part in the deployment. Default environments, Teams environments and environments without Dataverse can't be targets of personal pipelines.
- Target environments (test, production) enabled as managed environments. Microsoft documents that licences granting premium use rights are required for all managed environments. The pipelines host and Developer-type environments don't need to be managed; Microsoft says all other environments used in pipelines must be. From October 2026, the admin deployment page notifies admins when a pipeline deploys to an unmanaged target, and future deployments to that target are blocked if managed environments aren't enabled within 30 days.
- A dedicated environment for the pipelines host if you use a custom host. Microsoft recommends a production-type environment separate from development and targets; the host and a development environment can't be the same environment.
- Makers who deploy need privileges to export from development and import into the targets. System Customizer and Environment Maker have these by default.
A typical layout looks like this:
Contoso Host (production, pipelines host)
|
| stores pipeline config, run history, solution backups
v
Contoso Dev (developer) ---> Contoso Test (managed env) ---> Contoso Prod (managed env)
unmanaged solution managed solution managed solutionStep 1: Build the solution correctly in development
- In Power Apps, select the development environment, go to Solutions, and create a new solution with your own publisher.
- Set it as the preferred solution so new apps, flows and tables land in it instead of the default solution.
- Create new canvas apps and cloud flows from inside the solution. Apps and flows created outside a solution keep using direct connections; inside a solution, flows bind to connection references.
- For existing flows that you add to the solution, open the flow details page. The flow checker shows a Use connection references warning with an action to Remove connections so connection references can be added.
- Create environment variables for every value that differs between environments: New > More > Environment variable. For SharePoint-based apps, you need separate data source environment variables for the site and the list.
- In Power Apps Studio settings, on the General tab, you can enable Automatically create environment variables when adding data sources so new data sources get variables automatically.
- Give connection references meaningful display names. By default they include the connector, the solution name and a random suffix, which is hard to read on an import screen.
Remove current values before export
Microsoft's guidance is to include environment variable definitions in the solution but not their values. Open each environment variable in the solution and, under Current Value, select ... > Remove from this solution. The value stays in development but isn't exported, so the import or pipeline prompts for a value in each target. Variables that have no default or current value always prompt during import.
Flows that run unattended in production should also fail loudly and notify an owner; build that in before the first deployment with Power Automate try-catch scopes.
Step 2: Set up a Power Platform pipeline
You have two hosting options.
| Option | What you get | Limits |
|---|---|---|
| Platform host (personal pipelines) | Provisioned automatically the first time anyone opens the Pipelines page; no setup | One development and two target environments; can't be shared or extended |
| Custom host | Dedicated host environment, shared pipelines, approvals, delegated deployments, up to seven stages | You create and administer the host |
Personal pipeline on the platform host
- Open your unmanaged solution in the development environment and select Pipelines in the left pane.
- Select Create pipeline, enter a Pipeline name, optional Description, and a Target environment. The list is filtered to environments you have import access to.
- To add a production stage, select Add stage and choose the final target environment. You must own the pipeline, and this only works on single-stage pipelines.
The platform host is created in the tenant's home geography. Pipelines that cross geographies require a tenant admin to enable cross-geo solution deployments.
Shared pipeline on a custom host
- In the Power Platform admin center, go to Deployments > New custom host and create the host environment. Alternatively, install the Power Platform Pipelines app into an existing environment from Resources > Dynamics 365 apps > Install app.
- Copy the environment IDs of the development and target environments.
- In Power Apps, select the host environment and play the Deployment Pipeline Configuration app.
- Under Environments, select New for each environment. Set Environment Type to Development Environment for source environments and Target Environment for test and production, paste the Environment Id, save, refresh, and confirm Validation Status shows Success.
- Under Pipelines, select New, name the pipeline, then in Linked Development Environments select Add Existing Development Environment.
- In Deployment Stages, select New Deployment Stage for test. Create a second stage for production and set its Previous Deployment Stage to the test stage, so nothing reaches production without passing test first.
- Grant access. Assign makers the Deployment Pipeline User role in the host and share the pipeline record with them (Read is enough to run it). Assign Deployment Pipeline Administrator to the people who manage pipelines.
An environment can be associated with only one pipelines host; see the troubleshooting section if it is already linked elsewhere.
Approvals and delegated deployments
By default, a deployment runs as the maker who requested it, and that maker owns the deployed objects. For production you usually want a delegate:
- Create an app registration and enterprise application in Microsoft Entra ID. You must be an owner of the enterprise application, not only of the app registration.
- Add it as an application user (server-to-server user) in the host and in each target environment.
- Assign it Deployment Pipeline Administrator in the host and System Administrator in the targets. Lower roles can't deploy plug-ins and other code components.
- On the production stage, select Is delegated deployment, choose Service Principal, enter the client ID, and save.
- In the host environment, create a cloud flow with the OnApprovalStarted trigger, add an approval, and finish with the Dataverse Perform an unbound action step calling
UpdateApprovalStatus(20 = approved, 30 = rejected). That action must use the service principal's connection.
Delegated deployments stay pending until this flow approves them.
Step 3: Run a deployment
- In the development environment, open the unmanaged solution and select Pipelines (or Overview > Deploy).
- Select the stage, for example Deploy to Test, and select Deploy here.
- Choose Now or Later, then Next. The pipeline runs pre-deployment validation against the target and reports missing dependencies before anything is imported.
- Provide connections for each connection reference and values for each environment variable. The pane labels each prefilled value with its source: solution value, target environment value or default value.
- Review the summary, add deployment notes, and select Deploy.
The solution is exported once, when you select Deploy, and that same artifact moves through later stages, so production always receives what was tested. Managed and unmanaged copies of every deployment are stored in the host.
Step 4: Azure DevOps when pipelines aren't enough
Pipelines can't deploy to another tenant or deploy several solutions in one request. For those cases, use the Power Platform Build Tools in Azure DevOps or the pac CLI with a deployment settings file.
Generate the settings file from an exported solution:
pac auth create --name Contoso-Dev --environment "https://contosodev.crm.dynamics.com" --applicationId 00000000-0000-0000-0000-000000000000 --clientSecret $clientSecret --tenant 00000000-0000-0000-0000-000000000000
pac solution export --name ContosoExpenses --path .\out\ContosoExpenses_managed.zip --managed
pac solution create-settings --solution-zip .\out\ContosoExpenses_managed.zip --settings-file .\settings\test.jsonFill in a value for each environment variable and a connection ID for each connection reference in the target environment. Keep one file per target in source control:
{
"EnvironmentVariables": [
{
"SchemaName": "contoso_SharePointSite",
"Value": "https://contoso.sharepoint.com/sites/expenses-test"
}
],
"ConnectionReferences": [
{
"LogicalName": "contoso_sharepointonline_expenses",
"ConnectionId": "<connection id from the target environment>",
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline"
}
]
}You can find a connection ID in the URL when you open the connection under Connections in the target environment. The connection must be owned by, or shared with, the identity that owns the connection references, or import validation fails.
Import with the CLI:
pac solution import --path .\out\ContosoExpenses_managed.zip --settings-file .\settings\test.json --async --activate-pluginsOr with the Build Tools in an Azure Pipelines YAML file:
steps:
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.tool-installer.PowerPlatformToolInstaller@2
displayName: 'Power Platform Tool Installer'
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.export-solution.PowerPlatformExportSolution@2
displayName: 'Export managed solution from Dev'
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'Contoso Dev'
SolutionName: 'ContosoExpenses'
SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/ContosoExpenses_managed.zip'
Managed: true
AsyncOperation: true
MaxAsyncWaitTime: 60
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.import-solution.PowerPlatformImportSolution@2
displayName: 'Import into Test'
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'Contoso Test'
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/ContosoExpenses_managed.zip'
AsyncOperation: true
MaxAsyncWaitTime: 60
UseDeploymentSettingsFile: true
DeploymentSettingsFile: '$(Build.SourcesDirectory)/settings/test.json'PowerPlatformSPN refers to a service connection of type Power Platform defined under Project Settings > Service Connections. Use the solution's unique name, not its display name, in SolutionName. Microsoft recommends asynchronous import for larger solutions because synchronous imports time out after four minutes.
Verification
- In the target environment, open Solutions and confirm the solution shows as managed with the expected version.
- Open each environment variable through the Default solution to see its value; values of variables in a managed solution aren't visible inside the managed solution itself.
- Check that flows are turned on and connection references show valid connections.
- Open Run history for the stage to see validation results and errors, and check solution layers for unexpected unmanaged layers.
Troubleshooting
"This solution requires another solution to be installed first." The managed solution depends on a base solution that isn't in the target. Deploy the base solution first; managed dependencies are tracked across solutions.
ConnectionAuthorizationFailed when turning on a flow. The user turning on the flow doesn't have permission to at least one of its connections. Either the connection owner turns the flow on, or the connections are shared with that user (Share > Can use).
Invalid connection on a connection reference. A red exclamation point on the flow details page means the underlying connection is broken. Update or replace the connection, then edit the connection reference.
"This environment is already associated with another pipelines host." Delete the environment record in the old host, or select Force Link in the new host. Makers then lose access to pipelines in the old host from that environment.
"The deployment stage isn't an owner of the service principal." You own the app registration but not the enterprise application. Add yourself as an owner of the enterprise application in Microsoft Entra ID.
Delegated deployment stuck in pending. All delegated deployments wait for approval. Check that the approval flow uses the OnApprovalStarted trigger and calls UpdateApprovalStatus with the delegate's connection.
Custom connector connection references fail after import. Custom connectors must be imported in a separate solution before the solution that contains their connection references and flows.
Closing checklist
- One custom publisher and prefix, chosen before any components were created.
- All apps and flows created inside the solution, with connection references instead of direct connections.
- Every environment-specific value in an environment variable; current values removed before export.
- Target environments enabled as managed environments and licensed accordingly.
- A pipeline with test as the previous stage of production, or an Azure DevOps pipeline with one deployment settings file per target.
- Production deployments run as a service principal behind an approval flow.
- No direct edits in test or production; consider Block unmanaged customizations on targets.
- Run history and solution layers checked after every production deployment.
References
- Overview of pipelines in Power Platform
- Create pipelines using the platform host
- Configure pipelines using a custom host
- Run pipelines in Power Platform
- Deploy pipelines as a service principal or pipeline owner
- Solution concepts
- Use environment variables in Power Platform solutions
- Use a connection reference in a solution
- Pre-populate connection references and environment variables for automated deployments
- Power Platform Build Tools tasks
- Power Platform CLI solution command group
- Power Platform CLI auth command group