> ## Content Index
> Fetch the complete content index at: https://www.powerpete.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Move Contact Center configuration between environments, from inside XrmToolBox
- URL: https://www.powerpete.com/move-contact-center-configuration-between-environments-from-inside-xrmtoolbox/
- Published: 2026-10-18T12:53:44.000Z
- Updated: 2026-10-18T12:53:44.000Z
- Description: Workstreams, routing rules, queues, rulesets and channels promoted from Dev to Test to Prod, matched by name instead of GUID, with a dependency check, a dry run, verification afterwards and a rollback. Free, open source, and installable today.
- Author: Peter Ruiter
- Tags: ALM, Community, Dynamics 365, Contact Center, Tools

Every Contact Center project hits the same wall. You build the routing in Dev, it works, and then you have to get it into Test. The workstreams, the routing configurations, the work classification rules, the route-to-queue steps, the queues, the rulesets, the templates — none of it is structure. It is data. A solution will not carry it.

So you reach for the Configuration Migration tool, and the fun begins: build a schema file, export, hand-edit `data.xml` to strip what breaks, import twice because rulesets need their contracts first, then fix the calendars afterwards. Every promotion. By hand. And when something goes wrong halfway, you get to work out what was already written and what was not.

I got tired of it, so I built a tool. It now runs where you already work: **XrmToolBox**.

## What it does

Connect to your source environment the usual way, pick a second connection as the target, and press **Compare**. From there it is four steps: review what differs, tick what to move, check every dependency, then dry-run and apply.

The thing that makes it usable is that **records are matched on what they are, not on their GUIDs**. A workstream called "Voice VIP" in Dev and a workstream called "Voice VIP" in Test are the same workstream, so you get a list of real differences instead of "everything changed".

![](https://www.powerpete.com/content/images/2026/10/image-22.png)

**The review tree shows the configuration the way it is actually built: each record under the workstream, channel or queue it belongs to.*

A line marked **uses** is a record that lives elsewhere in the tree, shown where it is used — so the queue a routing step routes to appears under that step, and under its own group as well. Each container tells you how much sits under it and how much of that is ticked: *"Voice — workstream \[Changed\] · 8 under it, 4 of 4 ticked"*. You can see what a workstream will drag along before you tick it.

Ticking follows the same idea. Tick a workstream and you get its routing configuration, its work classification and route-to-queue steps, their rulesets, its queues and templates. Untick it and they go again — except the ones another ticked record still needs.

## "What breaks if I don't move this?"

Select any record and the right-hand pane answers three questions: what differs, what it needs, and **where it is used**.

![](https://www.powerpete.com/content/images/2026/10/image-23.png)

**Default messaging queue is used by five workstreams. Double-click any row to jump to that record.*

That last one matters more than it sounds. Shared queues, templates and rulesets are exactly the records you forget about, and this tells you who is pointing at them before you decide to leave one behind.

## Nothing runs before it is checked

**Plan check** verifies every ticked record against the target: each parent, template and embedded reference must already be in the target, be created earlier in the same run, or be linked in a final pass. Anything that cannot is blocked, with the reason in plain words — and a record that is only in the plan because something else needs it says which record that is.

![](https://www.powerpete.com/content/images/2026/10/image-24.png)

**147 records: 97 new, 32 changed, 144 ready, 3 blocked, 55 links set at the end. The blocked rows say why: a session template that comes with a managed solution that is not installed in the target.*

Those three blocked records are the honest kind of blocked. One workstream names a session template that ships in a managed solution you have not installed in Test yet; no tool can conjure that. You get told now, not halfway through a write.

## Dry run, apply, verify, roll back

A **dry run** goes through every single write without writing anything, using what the compare already read, so it is fast.

![](https://www.powerpete.com/content/images/2026/10/image-25.png)

**Exactly what an apply would do, per record, with the settings count and the links that get set at the end of the run.*

**Apply** only unlocks after a dry run of that same plan. It re-reads the target first, so it never writes from a stale picture, snapshots every record it changes, and when it has finished it reads back everything it wrote and compares it with the source. The run then either says the target matches the source, or lists each record that still differs, with the setting and both values — including the cases where Dataverse itself is the reason, like a setting it refuses to accept at create time.

And if you need to undo it: **roll back** restores the changed records to how they were and deletes the created ones, children first, from the snapshot the run wrote. That includes the routing contract Dataverse provisions by itself for a new workstream.

## What it moves

Workstreams with their routing configurations, work classification and route-to-queue steps, queues, rulesets and decision contracts, assignment methods and their steps, operating hours, skills, rating models and values, capacity profiles, session and notification templates, chat/voice/SMS/WhatsApp and custom channels, quick replies and tags (with their links to the workstreams they belong to), context variables, and the contact center settings record.

What stays manual, because it cannot move as data: phone numbers, WhatsApp and SMS accounts, Copilot Studio agents, and the record routing hubs that email and case workstreams point at. The tool lists each of those under the review tab with what to set up in the target first.

## Install it today

The tool is **going through certification for the XrmToolBox Tool Library** right now. Until it lists there, installing by hand takes half a minute:

1. Download `ContactCenterALM-XrmToolBox.zip` from the [latest release](https://github.com/peterruiter/d365-contactcenter-alm/releases/latest?ref=powerpete.com).
2. Right-click the zip, **Properties**, tick **Unblock** (Windows blocks downloaded DLLs).
3. Close XrmToolBox, unzip the two DLLs into `%AppData%\MscrmTools\XrmToolBox\Plugins`, start XrmToolBox again.
4. Connect to your source environment, open **Contact Center ALM**, press **Choose target…**, then **Compare**.

It needs XrmToolBox 1.2025.10 or later. Nothing else: no app registration, no Azure, no backend — it runs on the XrmToolBox connections you already have.

**The full how-to and install guide:** [docs/xrmtoolbox.md](https://github.com/peterruiter/d365-contactcenter-alm/blob/master/docs/xrmtoolbox.md?ref=powerpete.com)

Once certification is through, it will also be one click away in **Tool Library** inside XrmToolBox. The package is already on [nuget.org](https://www.nuget.org/packages/ContactCenterALM.XrmToolBox/?ref=powerpete.com) if you prefer to look at it there first.

## Prefer a richer UI? There is a local runner

XrmToolBox is the fastest way in if you live there anyway. But the same engine also powers a proper web UI that runs on your own machine — no Azure, no app registration beyond the client's own, and a guided flow that walks you through it.

![](https://www.powerpete.com/content/images/2026/10/image-26.png)

**One exe, no install. Pick two environments and compare.*

![](https://www.powerpete.com/content/images/2026/10/image-27.png)

**The same rules in a bigger canvas: what moves, what each record needs, and what you have to set up by hand in the target.*

![](https://www.powerpete.com/content/images/2026/10/image-28.png)

**Writes in stages, in the order Dataverse needs them, with every dependency resolved before anything runs.*

![](https://www.powerpete.com/content/images/2026/10/image-29.png)

**Dry run first; Apply unlocks afterwards. Runs happen in the background — you can close the tab and come back.*

![](https://www.powerpete.com/content/images/2026/10/image-30.png)

**Light and dark, because half of us work in the dark.*

Two downloads, pick one: a **self-contained exe** of about 50 MB with .NET and PowerShell 7 inside (nothing to install, double-click it), or a **0.7 MB zip** that runs on PowerShell 7 if your laptop's endpoint protection is unhappy about unsigned executables. Both are here:

- **Download:** [the local runner release](https://github.com/peterruiter/d365-contactcenter-alm/releases/tag/localrunner?ref=powerpete.com)
- **How-to:** [docs/local-runner.md](https://github.com/peterruiter/d365-contactcenter-alm/blob/master/docs/local-runner.md?ref=powerpete.com)

It keeps a profile per client, encrypts the secrets for your Windows user, and its setup wizard checks each environment step by step — tenant, sign-in, application user, Contact Center installed, table access — and tells you exactly what to ask the client for when a check fails.

There is a third flavour as well: the same app published **inside Dynamics** as a Power Apps code app, with an Azure Function behind a custom connector, for teams that want promotions to run under a service principal rather than someone's laptop. That one is in the [repository](https://github.com/peterruiter/d365-contactcenter-alm?ref=powerpete.com) too.

## Before you use it in anger

Writing Contact Center configuration through the API is **not a Microsoft-supported path**. Dry-run first, try it on Dev and Test before you point it at Production, and keep the rollback in mind. The engine is held to the same answers by an offline test suite plus replays of real Dev-to-Test compares, but your configuration is yours, and a dry run costs you nothing.

It is free and open source (MIT). If it saves you an afternoon, or if it trips over something in your environment, tell me — [issues on GitHub](https://github.com/peterruiter/d365-contactcenter-alm/issues?ref=powerpete.com) are very welcome.