A friendly, end-to-end walkthrough for Power Platform admins and developers
If you've inherited (or grown) a Power Platform solution where every flow has its own connection reference for the same connector, you already know the pain: every new environment means re-mapping a dozen references, every connection rotation cascades into a dozen failures, and "who owns what" becomes a Tuesday-morning archaeology project.
The cleaner pattern is to consolidate to a single connection reference per connector and have every flow in the solution point at it. That way, when you swap the underlying connection, you swap it once.
This post walks through the full loop using PAC CLI: installing the tooling in VS Code, authenticating to your dev environment, cloning the solution, editing the flow JSON to point at one shared connection reference, and importing the result back without your flows turning themselves off in the process. There's a small but important warning at the end about who owns what during import — please don't skip it.
What you'll need
- Visual Studio Code installed
- Maker access to a development environment with the solution you want to refactor
- (Optional but recommended) A service principal you can use as the importer — covered in the appendix
Step 1 — Install PAC CLI in VS Code
The fastest way to get PAC CLI working is to install the Power Platform Tools extension. It bundles the CLI and wires it up to your integrated terminal automatically.
- Open VS Code.
- Open the Extensions view (
Ctrl+Shift+X). - Search for Power Platform Tools (publisher:
microsoft-IsvExpTools). - Click Install.
- Reload VS Code if prompted.
Open a new PowerShell terminal in VS Code (Ctrl+`) and confirm everything's wired up:
pac
You should see the PAC CLI banner with a version number:
Microsoft PowerPlatform CLI
Version: 1.x.x
If pac isn't recognized in a plain Windows Command Prompt or system PowerShell, that's expected — by default the extension only enables PAC inside the VS Code integrated terminal. If you want it everywhere, install the .NET Tool version or the Windows MSI alongside it. For this walkthrough, the VS Code terminal is perfectly fine.
Step 2 — Authenticate to your dev environment
PAC CLI uses authentication profiles. Each profile is a stored connection to a specific tenant and, optionally, a specific environment. Most teams end up with a profile per environment per customer.
Create a profile pointed at your dev environment:
pac auth create --name "Contoso-Dev" --environment "https://contoso-dev.crm.dynamics.com"
A browser window opens; sign in with your maker account. You can substitute the environment URL for an environment ID, a unique name, or even a partial name — PAC will resolve it.
Confirm the profile is active:
pac auth list
Output looks something like:
Index Active Kind Name Friendly Name Url User
[1] * UNIVERSAL Contoso-Dev Contoso Dev https://contoso-dev.crm.dynamics.com/ you@contoso.com
If you already have multiple profiles and need to switch, use pac auth select --index <n>. A quick pac auth who will tell you exactly which environment is currently in play — run it before any destructive command, just to be safe.
Tip — find your environment ID. Open the Power Platform admin center, pick your environment, and copy the Environment ID from the Details pane.
Step 3 — Clone the solution
pac solution clone is the right tool for this job. It pulls the solution down as a buildable project (with a cdsproj) and unpacks it into source files you can read, diff, and edit. Use clone (rather than export) when you want to change the contents of the solution, not just deploy it.
From the folder where you want the project to live:
pac solution clone --name ContosoIntegrations
--name is the solution unique name, not the display name. Find it under Solutions in the maker portal — it's the column to the right of the display name.
After it runs, you'll see a folder structure like this (the XML format that clone emits by default):
ContosoIntegrations/
├── ContosoIntegrations.cdsproj
└── src/
├── Other/
│ ├── Customizations.xml ← connection references live in here
│ ├── Solution.xml
│ └── Relationships.xml
└── Workflows/
├── FlowA-1A2B3C4D....json ← flow definition (this is what you edit)
├── FlowA-1A2B3C4D....xml
├── FlowB-5E6F7G8H....json
└── FlowB-5E6F7G8H....xml
The two places that matter for this refactor are src/Other/Customizations.xml (where every connection reference component is defined, all in one file) and src/Workflows/ (one JSON file per flow, named <FlowName>-<GUID>.json, with a matching .xml workflow descriptor).
A note on formats. If your team uses native Dataverse Git Integration — or a newer
pacCLI configured to emit the YAML source-control format — you'll see a different layout (solutions/<name>/solution.yml, separateconnectionreferences/andworkflows/folders at the repo root, etc.). The mechanics in this post are the same; you'd just editconnectionreferences/*.ymlandworkflows/<flow>/<flow>.jsoninstead ofCustomizations.xmlandWorkflows/<flow>.json. For the rest of this walkthrough I'll assume the default XML layout.
Step 4 — Update the flow JSON to use one connection reference
This is the core of the change. Today, each flow has its own connection reference logical name baked into its definition. We're going to:
- Pick (or create) the single connection reference you want all flows to share.
- Edit each flow's definition JSON so its
connectionReferencesblock points at that shared logical name.
4a. Pick the canonical connection reference
Open src/Other/Customizations.xml and find the <connectionreferences> element. Each connection reference in the solution sits inside it as a <connectionreference> node. Pick the one you want all flows to point at — say the SharePoint one with logical name contoso_sharedsharepoint. It will look something like:
<connectionreferences>
<connectionreference connectionreferencelogicalname="contoso_sharedsharepoint">
<connectionreferencedisplayname>Shared SharePoint</connectionreferencedisplayname>
<connectorid>/providers/Microsoft.PowerApps/apis/shared_sharepointonline</connectorid>
<description>Single connection reference for all SharePoint actions</description>
</connectionreference>
<connectionreference connectionreferencelogicalname="contoso_flowa_sharepoint">
<connectionreferencedisplayname>Flow A SharePoint</connectionreferencedisplayname>
<connectorid>/providers/Microsoft.PowerApps/apis/shared_sharepointonline</connectorid>
</connectionreference>
<!-- ...the other per-flow connection references you're consolidating away... -->
</connectionreferences>
Note the connectionreferencelogicalname of the one you're keeping — you'll wire every flow to that value next.
4b. Update each flow's JSON
Open one of the flow JSON files at src/Workflows/<FlowName>-<GUID>.json. You're looking for the connectionReferences node near the top of the definition. Before consolidation it typically looks like this:
{
"properties": {
"connectionReferences": {
"shared_sharepointonline": {
"runtimeSource": "embedded",
"connection": {
"connectionReferenceLogicalName": "contoso_flowa_sharepoint"
},
"api": {
"name": "shared_sharepointonline"
}
}
},
"definition": {
"...": "..."
}
}
}
Change connectionReferenceLogicalName to your canonical reference:
{
"properties": {
"connectionReferences": {
"shared_sharepointonline": {
"runtimeSource": "embedded",
"connection": {
"connectionReferenceLogicalName": "contoso_sharedsharepoint"
},
"api": {
"name": "shared_sharepointonline"
}
}
},
"definition": {
"...": "..."
}
}
}
Repeat for every flow JSON in src/Workflows/. If you have a lot of flows, a careful find-and-replace across that folder works well — just make sure the key under connectionReferences (e.g., shared_sharepointonline) matches the connector you're consolidating, and that you only replace the old logical names in connectionReferenceLogicalName, not the API name in the api.name field.
4c. Clean up the orphaned connection references
Once nothing in any flow JSON points at the per-flow references anymore, remove their <connectionreference> nodes from src/Other/Customizations.xml. While you're in there, also remove their entries from the <RootComponents> block in src/Other/Solution.xml (look for type="29" entries with the schema names of the references you're dropping — type 29 is connection references). Keeping only the canonical reference in both files is the whole point of the exercise.
Step 5 — Pack and import the solution back
You have two reasonable patterns here. Both work; pick the one that matches how mature your ALM is.
Pattern A — Import the updated solution and let the maker portal prompt for connections
From inside the cloned project folder, build and import in one shot:
pac solution import --publish-changes
(With no --path, PAC builds the current cdsproj and imports the resulting .zip.) When the importing user has connections that match the connector(s) in your remaining connection reference, the import wizard auto-fills them. Otherwise you'll be prompted to pick or create one.
Pattern B — Import with a deployment settings file (the ALM-friendly way)
This is the path you want for CI/CD or anything reproducible. Generate a deployment settings JSON from the built solution zip:
pac solution create-settings `
--solution-zip .\bin\Debug\ContosoIntegrations.zip `
--settings-file .\settings\dev.settings.json
You'll get a JSON file like this, with one entry per connection reference and one per environment variable:
{
"EnvironmentVariables": [],
"ConnectionReferences": [
{
"LogicalName": "contoso_sharedsharepoint",
"ConnectionId": "",
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline"
}
]
}
Grab the ConnectionId from the target environment — easiest path is make.powerapps.com → Connections, click the connection, and copy the GUID from the URL (the segment after /connections/<apiName>/). Paste it into the JSON:
{
"EnvironmentVariables": [],
"ConnectionReferences": [
{
"LogicalName": "contoso_sharedsharepoint",
"ConnectionId": "9f66d1d455f3474ebf24e4fa2c04cea2",
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline"
}
]
}
Then import using the settings file:
pac solution import `
--path .\bin\Debug\ContosoIntegrations.zip `
--settings-file .\settings\dev.settings.json `
--publish-changes `
--force-overwrite
Because the connection ID is wired in up front, the import doesn't have to ask you anything interactively. This is the same shape of file Power Platform Build Tools expects in Azure DevOps pipelines, so you can re-use it.
⚠️ Critical: who owns what when you import
This is the part everyone learns the hard way exactly once. Skim it, please.
When a solution containing flows is imported, Power Automate validates that the importing user can use every connection referenced by every flow. If they can't, the affected flows get imported in the off state and have to be turned on manually. With a large solution that you "just" reimported, that can mean every flow in the solution needs to be re-enabled by hand.
Two ways to avoid this:
Option 1 — The importer owns the connection. Whoever runs pac solution import should also be the owner of the connection that the (now single) connection reference points to. If you're the person doing the import, make sure you created — or have been gifted ownership of — the connection in the target environment.
Option 2 — Import as a service principal that has the connection shared with it. This is the right answer for anything beyond a single-developer dev environment. The SPN owns the flows after import, doesn't churn when humans leave or change roles, and pipelines can use it without storing user credentials. Share the connection with the SPN as Can use before you import. Setup is in the appendix.
Why "all flows turn off" happens. It's the same
ConnectionAuthorizationFailedfailure mode you'd hit turning a flow on manually — Power Automate refuses to enable a flow when the activating identity doesn't have rights to every connection it touches. The solution import is just the same check applied to N flows at once.
Troubleshooting
"pac is not recognized."
You're probably in a terminal outside VS Code. Either run the command in the VS Code integrated terminal (where the extension wires up the PATH) or install the .NET Tool so pac is available everywhere.
pac solution clone fails with "Solution not found".
You're using the display name. Switch to the unique name from the Solutions list. Also confirm pac auth who is pointed at the correct environment.
Import succeeds but the flows are turned off.
Whoever ran the import doesn't have access to the connection. See the warning above. Either share the connection with that identity (as Can use) and turn the flows on, or re-run the import as a user/SPN that does have access. Once enabled by the connection owner, co-owners can toggle the flow on and off as needed.
"ConnectionAuthorizationFailed" when activating a flow.
Same root cause — the activating user is missing access to at least one connection. Either share all connections used by the flow with that user, or have the connection owner be the one to enable the flow.
The deployment settings file has empty ConnectionId values.
That's normal — pac solution create-settings generates the file with empty slots. You fill them in per-environment. Keep one settings file per environment (dev.settings.json, test.settings.json, prod.settings.json) and check them into source control.
Pack fails after editing JSON.
Make sure the JSON is still valid (a stray comma is the usual culprit) and that you didn't change any GUIDs, only the connectionReferenceLogicalName values. Run pac solution pack against the cloned folder before importing if you want to validate without round-tripping.
Flow shows "Invalid connection" after import.
The connection reference is pointing at a connection that's in a bad state — usually expired credentials. Go to Connections in the maker portal, edit/refresh the underlying connection, and the reference will pick it back up.
Appendix — Setting up a service principal for imports
If you're moving to pipeline-driven deployments, do this once and you'll thank yourself.
A1. Register the application in Microsoft Entra
- Open the Microsoft Entra admin center.
- Go to Identity → Applications → App registrations → New registration.
- Give it a name like
pp-deployer-spn. Single tenant is fine. - After registration, note the Application (client) ID and Directory (tenant) ID.
- Under Certificates & secrets, create a new Client secret. Copy the value immediately — you can't view it again.
A2. Create the application user in Dataverse
- Open the Power Platform admin center.
- Pick your target environment → Settings → Users + permissions → Application users.
- Click New app user, find the registration from A1, pick the business unit, and assign a security role with enough permissions for what your pipeline does (often System Customizer plus a custom "deployer" role).
A3. Authenticate PAC CLI as the SPN
pac auth create `
--name "Contoso-Dev-SPN" `
--environment "https://contoso-dev.crm.dynamics.com" `
--applicationId "<APP-CLIENT-ID>" `
--clientSecret "<CLIENT-SECRET>" `
--tenant "<TENANT-ID>"
A4. Share the connection with the SPN
Back in the maker portal:
- Connections → click your shared connection (e.g., the SharePoint one).
- Share → search for the application user you created.
- Set permission to Can use → Save.
Repeat for every connection your single connection reference is going to be wired to.
Note. OAuth connections can only be explicitly shared with a user representing a service principal — not with security groups. If you have multiple SPNs (one per environment), share with each one explicitly.
A5. Import as the SPN
pac auth select to make the SPN profile active, then run your pac solution import as in Step 5. Because the SPN owns (or has been granted access to) the connection, the flows come in enabled and stay enabled across redeployments — no more "every flow is off" Monday mornings.
Wrapping up
A single shared connection reference is one of those changes that feels like a small refactor and pays you back every single deployment thereafter. The mechanical part — clone, edit JSON, import — is straightforward once you've done it once. The part that bites people is ownership: make sure the identity doing the import has access to the underlying connection, or you'll spend the next hour turning flows back on by hand.
If you want to take the next step, wire the deployment settings JSON and the SPN into an Azure DevOps pipeline using the Power Platform Build Tools. At that point you've gone from "manual refactor" to "every environment is one click."
Sources
- Microsoft Power Platform CLI — pac auth reference
- Microsoft Power Platform CLI — pac solution reference
- Install the Power Platform Tools Visual Studio Code extension
- Pre-populate connection references and environment variables for automated deployments
- Use a connection reference in a solution with Microsoft Dataverse
- Import a solution (Power Automate)
- Support for service principal owned flows
- Create an application user (Power Platform admin)
Top comments (0)