M365 Show -  Microsoft 365 Digital Workplace Daily
M365 Show with Mirko Peters - Microsoft 365 Digital Workplace Daily
Deploying Dynamics 365 Customizations with ALM Pipelines
0:00
-21:46

Deploying Dynamics 365 Customizations with ALM Pipelines

Ever spent hours perfecting a Dynamics 365 solution—only to have it break during deployment? You're not alone. Most of us trip over the same hidden traps when moving from dev to prod. But what's actually causing these failures—and how can you package solutions to avoid them? Stay with me, because we're unpacking the advanced methods that separate smooth deployments from disaster.

Why Solution Packaging Breaks Even When It Looks Right

You get the green checkmark in dev and think you're finally in the clear. Then, you hit test or prod and everything comes apart. It’s the classic Dynamics 365 story—one that way too many teams keep replaying. Let’s spell out the real issue: throwing every customization into your solution package doesn’t guarantee a safe landing. The “include everything and hope for the best” approach works fine in tiny, one-person projects. The moment multiple people get involved, or your business processes start layering up, things start going off the rails.

On paper, Dynamics 365 solutions look straightforward. You group up your plug-ins, flows, entities, all the bits and pieces you’ve built. You check those reassuring green checks, maybe run a basic validation, and then hit export with a sense of accomplishment. But a lot of the real work isn’t visible until you place that solution inside another environment. Test or prod looks and behaves differently than dev, and suddenly functions you took for granted start breaking down. One of the most frustrating cases: spending a day tweaking a plug-in—deploying, re-registering, testing in dev—and then watching it quietly disappear as soon as you import your solution to a higher environment. The same can happen with business rules. If layering isn’t done right, those critical rules can get swallowed up, leaving users in prod with a version of Dynamics that behaves nothing like what you signed off on in dev.

A major headache comes from dependencies you can’t always see. You might think that ‘Add Existing’ button pulled in everything you needed, but Dynamics does a pretty lousy job surfacing what lives underneath the surface. Let’s say you build a slick sales process flow that links to a custom entity. If you don’t package up that referenced entity, you’ll watch the flow break down in prod, even though it sailed through dev perfectly. One study found that nearly half—47%, to be exact—of failed Dynamics 365 deployments come down to overlooked dependencies or confusion about solution types. It’s not just developers falling into this trap. Admins and functional consultants working with apps, automations, even simple field changes, hit the same walls.

You also see a whole lot of confusion about managed and unmanaged solutions, and that’s a bigger deal than most teams realize. It’s easy to assume you can just “fix it later” if something gets overwritten, but unmanaged imports love to wipe out previous work. A plug-in you registered, a web resource you customized, or a view you spent hours designing can all vanish. Even worse, sometimes you get orphaned components without version tracking, which means you’re left hunting for ghosted rules or flows that have no clear owner.

A problem that gets less attention, but causes plenty of trouble, is overwrites. Unmanaged solutions will merge right into whatever’s already there, which can mean your business logic from dev suddenly replaces whatever was in prod—whether you wanted that or not. Managed solutions bring a bit more discipline, but if your team misses the switch and drops unmanaged content into production, you inherit a Frankenstein environment. Suddenly, nobody wants to touch updates, because every change could break something new.

Let’s ground this with a real scenario—because we’ve all lived some version of this. Imagine rolling out a CRM customization for a sales team. The business process flow you built uses custom fields and references a contact entity you assume exists everywhere. In test, the flow errors out. After hours buried in logs, you realize the referenced entity never made it onto your packing list. Now the sales team is down, management is unhappy, and you’re left unpicking dependencies by hand. It’s hardly a one-off story.

A lot of teams get caught up blaming tools or even individual mistakes, but the pattern repeats across companies—tech, finance, healthcare, the works. And honestly, much of it comes from putting trust in that green check without spending time digging into how components interact. Even seasoned Dynamics pros can forget that environments have their own quirks. Sandbox data, for example, might cover up a missing dependency you’ll only notice in prod when something fails.

There are also those late-night moments where, despite all your careful packaging, something important just… isn’t there. Maybe a Power Automate flow that references an old field or an API key from dev. Or, the classic case: importing a solution and wiping out critical business rules because your layer order got scrambled. This hits even harder if managed and unmanaged solutions are mixed. Once a customization is lost, you’re often left rebuilding from scratch or digging through backups.

So, what actually separates smooth deployments from recurring fires? It’s the teams who don’t treat solution packaging as a checklist item. Instead, they dig into how every component fits together, what each dependency means, and how changes will ripple as the solution moves. Packaging in Dynamics 365 is closer to city planning than simple file transfer. Missing dependencies, unmanaged chaos, and careless overwrites pile up fast if you just trust the platform to handle everything.

Done well, the packaging process is about understanding relationships and moving parts—not just checkboxing “Add Existing” everywhere. That’s what saves you from deployments that collapse after a false sense of security. The next logical step? Mapping out those dependencies before migration, instead of chasing down missing parts after the fact.

Dependency Nightmares: Mapping Before You Migrate

If you’ve ever watched a progress bar crawl along during a solution import, only for Dynamics 365 to throw a full page of red errors at you, you know the pain is real. Nine times out of ten, those angry messages are shouting about missing dependencies—fields, tables, flows, or references you forgot about. It can feel personal, but it’s just Dynamics keeping strict tabs on relationships you can’t always see on the surface. People often want to blame a glitch, but almost every failed import traces back to something not included in the package.

What trips teams up is how invisible these dependencies are until it’s too late. Let’s say you work on a custom solution with a couple of plug-ins, some cloud flows, and a new entity or two. On the surface, your build looks nice and tidy. But there’s a little-used field somewhere that a Power Automate flow depends on, or maybe a form script quietly refers to another table that’s only in your dev environment. It all “just works” when you’re developing, and no one thinks twice—until you run the import and hit errors for objects you didn’t even remember existed. You end up playing Dependency Whack-a-Mole: fix one, and three more pop up.

Complexity multiplies this pain. The bigger and more customized your Dynamics 365 solution gets, the more tangled that web becomes. What started as a clean app for one business process snowballs into dozens of custom connectors, linked flows, calculated fields, and integrations. When a team changes—people leave, or a new admin takes over—the knowledge about what connects to what evaporates fast. People assume that previous work was neatly packaged up, but assumptions are how production goes down on a Friday night.

Let’s put faces to this. Picture the developer who spends a day wiring up a custom connector. Everything tests fine in dev, the solution coolly exports, but in the test environment that same connector refuses to work. You dig around, retrace your steps, and finally realize: the connector needs a reference to a table never included in the exported solution. It’s not the connector’s fault, it’s not even the import process—it’s the missing dependency hiding in plain sight. The sad part is, most of us have a similar story.

And it’s not just huge solutions with hundreds of entities where this goes wrong. Even a single missed lookup field—maybe something created weeks ago for a one-off pilot—can crash the whole import. These aren’t abstract problems. Unmapped dependencies are the single most common source of solution deployment headaches. Dynamics doesn't forget what’s linked; it just silently expects you to connect all the dots yourself.

There’s all this advice floating around about using the Solution Checker and built-in dependency analysis tools. The reality? Most teams skip them. Deadlines loom, and the pressure to deliver trumps the urge to double-check dependencies. Maybe you run it once and skim the output, or you mean to go back and check later. But that five-minute check you ignore now will almost certainly cost you hours chasing errors later. Admins who’ve been burned before know to treat dependency checks as ritual—right up there with good backups and commenting your code.

The best analogy is a recipe. You can wing it, but forget a single ingredient and the whole dish turns out wrong. Before you even think about moving a solution, lay out what’s inside. List every component, script, table, and referenced object. If something depends on a field or entity created in another solution or from a previous version, ask whether that dependency is included—or if your migration’s about to break in half. It’s old-fashioned, but diagramming the high points of your configuration catches things automated tools don’t flag, like a legacy script somewhere in the form events.

Let’s talk about what this looks like in the wild. There was a finance team juggling multiple environments and a half-dozen custom workflows. They finished an upgrade, tried importing the solution into test, and immediately got errors. The culprit wasn’t a missing entity or a complex automation—it was a business rule in one form that referenced a field nobody used anymore. That field wasn’t included in the solution export, but the business rule still pointed to it. They spent days peeling apart the logs, rebuilding forms, and re-importing, all to discover a single oversight. It’s not glamorous, but it’s the norm.

The upshot to all this is pretty clear. By mapping dependencies up front—and I mean really digging into what everything touches—you don’t just keep your solution cleaner. You dodge those middle-of-the-night fire drills when production goes down, and you give every import a fighting chance to succeed the first time. It doesn’t matter if you’re building something small or rolling out a massive CRM overhaul. Dependency mapping is what lets your customizations cross environments in one piece.

Of course, dependencies set the stage, but the real drama unfolds when you pick the wrong type of solution for the wrong environment. That leads us straight into the world of managed versus unmanaged, where choosing the wrong layer isn’t just a best practices slip—it’s something that can leave you untangling your environment for months.

Managed vs. Unmanaged: Getting the Layers Right

Anyone who’s worked with Dynamics 365 for more than a few projects has been told the same advice: use unmanaged solutions in development, managed solutions in production. Simple enough, right? But here’s where it usually breaks down. Teams get comfortable making changes with unmanaged solutions in dev and test, then, in the rush to meet a deadline, they export straight to production without switching over to managed. At first, it doesn’t look like anything's wrong. You see your updates, your custom fields show up, maybe even the automations run—just like you hoped. But under the surface, things start to get messy, and it builds up fast.

In development, unmanaged solutions feel great because they let you move fast, tweak forms, add new options, and generally try things out without extra guardrails. It’s the Dynamics equivalent of having versioning turned off in a shared OneDrive folder—great for flexibility, lousy for long-term stability. Once you cross over to production, that same flexibility starts causing real problems. Suddenly, anyone with the right level of access can make changes that layer on top of what’s already there, sometimes in ways that Dynamics never really intended. Unmanaged solutions don’t lock down your customizations, so the environment accumulates changes, mistakes, and quick fixes, all of which stack up directly on top of the base system.

The most common issue isn’t just clutter. It’s unpredictability. Say you hotfix an entity with a few new fields to address a ticket that popped up last minute. In unmanaged mode, you’re changing the base objects—there’s no separation between what belongs to your solution and what’s just sitting in the environment. Fast forward a few weeks, and IT decides it’s time for a new release. Now, nobody can remember which entities were touched, which flows got rerouted, or what rules need to be migrated. You’re stuck sifting through audit logs and version history if you’re lucky. If you’re not, changes get lost, and hours of work just disappear.

Here’s a scenario that’s probably familiar—a team needs to remove a bunch of custom fields after a process change. With unmanaged layers, those fields don’t have a tidy uninstall button. You literally delete them, one by one, hoping you were thorough. And if you miss something, it sits there, orphaned or worse, causing problems for downstream apps. Managed solutions, on the other hand, are a bit stricter. When you import a managed solution into prod, you get layer protection. That means you can revert to a previous state, safely uninstall full chunks of functionality, and, most importantly, keep your environment clean.

One thing people underestimate: managed solutions also bring in a versioning model. Every import, every tweak, is tracked, so you know what’s been applied and in what order. It’s not magic—it’s just structure. But that structure is exactly what you want in a live environment where customers and executives expect things to keep working, upgrade after upgrade. Without it, you end up with overlapping layers of updates, sometimes with plugins, views, or even whole entities overwritten silently. It’s how environments get that “Frankenstein” feeling where nobody’s quite sure what’s running.

A mistake I still see is teams releasing unmanaged solutions because “that’s how the old project was done.” Maybe someone thinks it’ll be easier to make hotfixes directly in production. For a while, it can seem to work. Then comes a big update or a migration project. The unmanaged layers are now baked into the environment. You can’t roll back, and uninstall routines don’t work as expected. Data gets tangled up because the platform doesn’t always distinguish between temporary tweaks and permanent changes. Now, instead of clean removals, you’re deep in the weeds manually untangling what’s safe to delete. A healthcare company ran into this exact issue when an unmanaged update wiped out some of their audit tables. The only way out was a complete rollback, losing days’ worth of transactional data—not something you want to explain to a compliance team.

The fix is rarely retroactive. You can’t easily convert an unmanaged mess back to a fully tracked and layered setup. That’s why best practice is so insistent: always convert to managed before you push to production. And don’t trust that it’ll work just because it imported fine elsewhere. Dynamics environments can have subtle differences—security roles, sample data, field customizations—that only show up when you run a managed solution import in a properly matched sandbox. Sandboxing gets ignored because it feels like another step, but in reality, it’s how you catch weird layer conflicts before they impact live users.

So, if you want your Dynamics 365 deployment to behave, get your layers right from the start. Use unmanaged for rapid builds, managed for stability, and always check imports in a sandbox first. You’ll spend less time firefighting and more time delivering updates that land cleanly across the board. That’s great advice for solutions—but what about all the stuff you can’t put into a package? There’s a whole other class of settings and data that trips teams up every cycle. This is where environment-specific variables, sensitive config, and data migration tools become critical.

Data, Environmental Variables, and Automation: The Final Hurdles

You finally tick every box for your Dynamics 365 deployment—solutions are packaged, dependencies checked, imports run with no errors. But when you fire up the app in production, something just doesn’t work. Flows won’t trigger, dashboards stay empty, or API calls simply fail. Sound familiar? It’s one of the most common Dynamics 365 frustrations: you did everything right with your solution components, so why is the end result so... broken? The answer, more often than not, comes down to what your solution package doesn’t actually carry over—environment variables, connection references, and real business data. For all the power Dynamics gives you, its packaging model is still mostly about customizations, not the context they run in.

Let’s zoom in on environment variables and connection references first. These are the settings that let your solution run differently in dev, test, and production, but Dynamics treats them as configuration, not part of the export. So, here’s what catches teams out every single week: you build a beautiful Power Automate flow, set the connection string to your dev database, and push the whole thing to production—without swapping the connection or the variable for the live system. The flow just sits there, doing nothing, because it’s still pointing to dev or, worse, it’s failing silently with a connection error that’s buried in a somewhere-in-the-cloud log. No alert, no warning—just lost automation and head-scratching at the next status meeting.

It isn’t limited to Power Automate, either. Connection references also crop up in canvas apps, integrations, and even plug-ins. Developers get so used to clicking through their trusted configuration in one environment that they forget, in production, those settings can (and often must) change. Fail to update them? The solution appears functional, but users get errors or blank screens for reasons no log file in Dynamics is going to explain. Then there’s the risk of hard-coded config. Copying an environment with a dev API key into production might sound harmless until you realize those keys have different permissions, rate limits, and access to sensitive data. One missed variable, and a flow can accidentally expose or write back the wrong data. In the worst cases, sensitive data leaks into public test datasets or old dev records are accidentally purged.

Environment variables are valuable—if you use them with intention. But setting them wrong is like giving someone the right key for the wrong door. Maybe your app gets data from the wrong SharePoint site, or an integration runs in the wrong region, or secret credentials end up on the wrong integration. And because environment variables are managed separately from solution packages, there’s no failsafe—just a layer of extra work that’s easy to overlook in the rush to go live. Documenting every variable, mapping out what each value should be per environment, and treating this as part of the release, not an afterthought, is what separates reliable deployments from the ones that create late-night Slack messages.

Now, add data migration to the pile of things that trip up even experienced teams. When you deploy a Dynamics 365 solution, you’re only moving the customization—the tables, fields, entities, views, and so on. You’re not taking any lookup tables, reference data, or business-specific records unless you explicitly do it with migration tools. It’s like shipping a new point-of-sale system with no products or customer records—your environment looks right but is missing the fuel that makes it run. The classic example is importing a solution that relies on a set of business rules for certain record types, only to find those rules don’t function because the lookup records never made it over. That breaks automations, context-sensitive logic, and anything that relies on those missing records. Even worse, teams often realize the data is missing only after users begin reporting errors or missing options, meaning downtime for real work.

Best practice here is to always use a configuration migration tool alongside your solution import. You get a file with all your reference data, which you move to each environment in the right order: first the solution, then the config data. If you skip this step, you find out the hard way, sometimes only when a key workflow throws an error because it can’t find a lookup value. Documentation matters too—tracking which records must exist, what values should be set in each environment, and how to map data between different systems. It feels tedious but is one of the only ways to avoid going live with half a solution.

There’s also a timing factor. Automation in Dynamics deployment isn’t just “set it and forget it” pipelines. Order matters. You import your solution at the right stage, configure your environment variables while referencing the appropriate settings doc, and only then begin the migration of data. Skip the order, and you risk running automations on half-configured systems or populating references before entities are set up.

When you handle variables, data, and automation like first-class citizens—on the checklist, every time—you see fewer issues and more reliable rollouts. Dynamics 365 isn’t looking out for missed settings or absent data. That’s on the deployment team, and even the best-built solution falls apart if those final hurdles aren’t cleared. And it’s usually not until you hit the wall once or twice that the lesson sticks—these “invisible” steps end up being just as critical as anything you package inside your solution. So, the hard truth is, if you want your deployments to work outside dev, every automation, variable, and data point deserves a seat at the table. Teams that get this down stop treating deployment as a single step and start treating it as the sequence it really is: import, configure, migrate, then validate. The ones who don’t? They’re already scheduling that next late-night troubleshooting call.

Conclusion

If your Dynamics 365 deployments keep falling apart, it’s probably not just bad luck. Most failures come from not tackling the gaps between your solution package and a truly production-ready environment. Treat every deployment as a living system—map out those dependencies, pay careful attention to your layering strategy, and automate only when you know every step is covered. It isn’t magic or advanced theory; it’s solid groundwork. If something in this process saved you from a future middle-of-the-night chaos session, hit subscribe, stick around, and drop your own deployment disaster (or success) story in the comments so we can all learn.

Discussion about this episode

User's avatar