Taking Over an Undocumented Dynamics 365 Environment: The First Two Weeks

A working discovery method for a Dynamics 365 Customer Engagement estate, one instance or several, with no documentation and no source code.

Taking over a Dynamics 365 Customer Engagement environment nobody can explain starts by reverse engineering the system itself. Export every unmanaged solution and unpack it into source control, and record every managed one. Enumerate the registered plugin steps with message, stage, mode and rank to get the real execution order. List every cloud flow and classic workflow with its owner and the connections it runs on. Map security roles, business units and teams, extract the JavaScript web resources, and map the integrations between instances. Then use run history, trace logs and sign-in evidence to separate the code that runs from the code that merely exists, writing the runbook as you go and deciding whether to patch or rebuild.

What should you do before discovery starts?

Hold the system still and make sure the keys are yours. Discovery measures an environment, and a moving environment cannot be measured. That means one written freeze on production changes, administrative access in the hands of your own organisation, a manual backup with the date its restore window closes, and a sandbox copy of production to do the risky parts of discovery in.

The mechanics of that, including recovering tenant administration, environment ownership and the identities integrations run as when the previous partner or developer has stopped answering, are set out on our Dynamics 365 project rescue and takeover service page. This guide starts where that lockdown ends: the environment is still, you have access, and nobody can tell you how it works.

What does the first two weeks of an undocumented takeover look like?

The work runs in overlapping streams rather than strict phases, and on a multi-instance estate each stream runs per instance while the integration map is drawn across all of them. The table is the shape we plan to, not a promise: a very large estate, or several heavily integrated instances, takes longer, and the environment register and a first pac solution list on day one tell you which kind you have.

Working daysStreamOutput
1 to 2Access, freeze, sandbox copy, environment registerWho holds which rights, what exists in which tenant, restore window dates
2 to 4Solution export, unpack and layer inspectionA Git repository holding every unmanaged solution, and a list of managed ones
3 to 5Plugin steps, custom APIs and the trace logThe execution map per table and message
4 to 6Cloud flows, classic workflows, business rules and connectionsThe automation register with owners and run-as identities
5 to 7Security model and web resourcesThe security matrix and the form and command script map
6 to 8Integrations between and around the instancesThe integration map and the credential expiry register
7 to 9Usage evidenceEvery customisation marked load-bearing, dormant, dead or unknown
9 to 10Runbook, risk register and recommendationThe documentation set and a patch, rework or rebuild decision

How do you reverse engineer an undocumented Dynamics 365 environment step by step?

These are the ten steps, in the order they pay off. Each one produces a written artefact, so documentation is a by-product of discovery rather than a separate project at the end.

  • Step 1, Secure access, freeze change and copy production: Confirm your own people hold tenant, Power Platform and System Administrator rights in every environment, stop direct changes in production, take a manual backup and copy production into a sandbox. All invasive discovery, such as switching trace logging to All or disabling a step to see who complains, happens in the copy first.
  • Step 2, Build the environment register: List every environment in every tenant involved with pac admin list or the Power Platform admin center: URL, type, region, version, who administers it and what it is for. On a multi-instance estate this register is the first page of the documentation, because the instance nobody mentioned is usually where the last changes were made.
  • Step 3, Export and unpack every solution you can: Run pac solution list per environment, export each unmanaged solution with pac solution export and unpack it with pac solution unpack, which uses SolutionPackager, into a Git repository you own. Record every managed solution by name, publisher and version, and check solution layers on the tables and forms that matter to find unmanaged changes sitting on top of them.
  • Step 4, Enumerate plugin steps with stage, mode and rank: Use the Plugin Registration Tool or query sdkmessageprocessingstep to list every custom step: assembly, type, message, table, stage, mode, rank, filtering attributes, images and state. Sort by table, message, stage and rank to get the real execution order, then read the plugin trace log to see which steps actually fire and fail.
  • Step 5, List every flow and classic workflow with its owner and connections: Query the workflow table by category to separate classic workflows, business rules, actions, business process flows, cloud flows and desktop flows. For each automation record the owner, the trigger, whether it runs in real time or in the background, the connection references and the person who owns each underlying connection, and the last successful run.
  • Step 6, Map security roles, business units and teams: Export the business unit hierarchy, every custom and modified security role with its privilege depths, the teams and their types, field security profiles, hierarchy security settings and who is assigned what, including application users. Flag every System Administrator assignment by name.
  • Step 7, Extract web resources and trace them to forms and commands: Pull every JavaScript, HTML and other web resource out of the unpacked solutions, then search the form XML, ribbon definitions, command bar and site map for the libraries and function names each one registers. A script no form, command or page references is a candidate for the dead list.
  • Step 8, Map the integrations between and around the instances: List application users and match them to app registrations, service endpoints and webhooks and the steps that fire them, virtual tables and their data sources, flows that connect to another environment, and any external middleware. For each connection record direction, identity, schedule, which side owns each table and when its secret or certificate expires.
  • Step 9, Classify every customisation as load-bearing, dormant or dead: Attach usage evidence to every item on the inventory: run history, system jobs, trace log entries, sign-in logs, record counts and last modified dates, and which records the integration identities create. Nothing is deleted during discovery; dead items are listed, and removal waits until it can go through a proper release.
  • Step 10, Write the runbook and the recommendation: Turn the inventories into the documentation set: environment register, repository of unpacked solutions, execution map, automation register, security matrix, integration map with credential expiry dates, runbook for the failures that already happen, and a written recommendation on what to keep, what to rework and whether anything must be rebuilt.

How do you get the solutions out when there is no source code?

The environment is the source code. Authenticate against each environment with pac auth create, run pac solution list, and for every unmanaged solution run pac solution export with managed set to false followed by pac solution unpack, adding the option that also extracts canvas app sources. Unpack uses SolutionPackager under the hood and turns one opaque ZIP into one file per component: table and column definitions, form XML, views, the site map, ribbon definitions, web resources, workflow definitions and the plugin assembly registrations. Commit that folder to a repository your organisation owns on the first day. The command reference, and the move from there to pack, import and a pipeline, is on our Power Platform ALM with the PAC CLI page, so it is not repeated here.

Three limits matter on an undocumented estate. A managed solution cannot be exported, so for ISV and previous-partner managed solutions you record name, publisher and version, and inspect what they add through the solution layers view on the components you care about. Changes made directly in production live in the default solution, which is not exported as a unit; to capture them, add the components that show an unmanaged active layer to a temporary discovery solution in the sandbox copy and export that. And secure configuration on plugin steps is never included in an export, so any secret held there has to be found and recorded separately.

On an estate with several instances, export the same solution name from each and diff the unpacked folders. Instances that were meant to be identical rarely are, and the diff is the fastest honest answer to which one is the real master.

How do you find every plugin step and the order it runs in?

Open the Plugin Registration Tool (pac tool prt launches it) or query the Web API directly, for example sdkmessageprocessingsteps with $select=name,stage,mode,rank,filteringattributes,statecode and $expand on sdkmessageid, plugintypeid and sdkmessagefilterid for the message, the class and the table. Export the result to a spreadsheet, keep only the custom steps, and add each step's pre and post images from sdkmessageprocessingstepimage. Sort by table, message, stage, mode and rank. That sorted sheet is the execution map, and it is usually the first time anyone has seen the whole of what happens when a user saves an account.

Stage valueStageWhat it tells you
10Pre-validationRuns before the main system validation checks and outside the database transaction, so its work is not rolled back if the save later fails.
20Pre-operationRuns inside the transaction before the write. Steps here typically change the record being saved or block the save.
40Post-operationRuns after the write. Synchronous steps are still inside the transaction; asynchronous steps are queued as system jobs and run later in no guaranteed order.
  • Rank is the execution order among synchronous steps in the same stage for the same message and table, lowest first. Two steps with the same rank have no guaranteed order between them, which is a common cause of intermittent faults nobody can reproduce.
  • An Update step with no filtering attributes fires on every update to that table, including updates made by other plugins, flows and integrations. These are the first place to look for loops and slow saves.
  • Real-time classic workflows run in the same pipeline stages as synchronous plugins, so they belong on the same execution map, not on a separate list.
  • Custom workflow activities are plugin types too, and custom APIs point at the plugin type that implements them. List the customapi table alongside the steps so a flow calling a custom API is traced back to its code.
  • Where there is no source, database-registered assemblies are stored in the environment and can be extracted from the pluginassembly table (or the plugin package, for newer dependent-assembly registrations) and read with a .NET decompiler. Check who owns the intellectual property first; the takeover page covers that commercial question.
  • Turn the plugin trace log to All in the sandbox copy, or to Exception in production, in System Settings. By default trace log rows are removed by a bulk deletion job after about a day, so read them daily or export them while discovery runs.

How do you inventory flows and classic workflows and who they run as?

Every process type lives in the same Dataverse table, workflow, distinguished by its category, so one query gives the full list instead of six screens in three portals. Flows created outside solutions may not appear in the solution export, so reconcile the query against the Power Automate admin view of the environment before you trust the count.

For every automation record the owner, the trigger and filtering columns, whether it is on, and its identity. A cloud flow runs on the connections behind its connection references, and each connection belongs to a person; when that person's account is disabled the flow stops, which is how most inherited estates lose automations without anyone deciding to. Background classic workflows run as their owner, and real-time ones run as either the owner or the calling user depending on a setting you should record.

CategoryProcess typeWhere the evidence of use is
0Classic workflow (background or real-time)System Jobs for background runs, unless completed jobs are set to delete automatically; real-time runs leave no job and need a test or audit data
2Business ruleNo run history; test the form or table behaviour in the sandbox copy
3Action (classic custom process action)Callers in scripts, flows, plugins and integrations found by searching the unpacked solutions
4Business process flowInstance records in the process table behind each flow, and their active stage
5Cloud flowRun history in Power Automate, retained for 28 days
6Desktop flowDesktop flow run records and the machine or machine group it runs on

How do you map security roles, business units and teams?

Start from the business unit tree, because roles belong to business units and every role is copied into each one. Then list the custom and modified roles with their privilege depths per table (user, business unit, parent and child business units, organisation), the teams with their type (owner, access, or linked to a Microsoft Entra ID security group or Microsoft 365 group), the users and teams each role is assigned to, field security profiles, and whether hierarchy security or record ownership across business units is switched on.

The output is a security matrix: rows are people and groups, columns are the tables that matter, cells are what they can do. It exposes the three findings we see most on inherited estates: System Administrator handed out as a fix for a permissions error, business units that mirror an organisation chart that no longer exists, and large volumes of manual record sharing working around a model that never fitted. The rated review of access against what people actually need belongs to a Dynamics 365 health check; discovery only has to write down what is there.

How do you extract web resources and find out what calls them?

The unpacked solution already holds the web resources as files, so extraction is done; the work is tracing. Search the form XML for form libraries and event handlers, the ribbon definitions and the modern command bar for commands that call a function, and the site map and dashboards for HTML web resources. Build a map of script, function, form or command, and event (on load, on save, on change of a named column). Anything referenced nowhere goes on the dead candidate list.

Then read the scripts for the patterns that break on platform updates: the deprecated Xrm.Page object, calls to retired endpoints, and direct manipulation of the page DOM, which has never been supported. pac solution check flags much of this automatically. To see which scripts actually run and how long they take, open the form with Monitor for model-driven apps or the browser developer tools in the sandbox copy.

How do you map integrations when you have inherited several Dynamics 365 instances?

Three instances with no documentation are rarely three independent systems. They are usually one process split across environments by history: a sales instance feeding a service instance, a regional copy synchronised back to a central one, or a production instance and an old one that something still writes to. The integration map is the document that makes the estate understandable, and it is drawn per table, not per system: for each table, which instance is the master, what copies it where, as which identity, how often, and what happens on a conflict.

MechanismWhere to find itWhat to record
Application usersUsers with an application ID in each environment, matched to app registrations in Microsoft Entra IDWhich system uses it, its roles, and when its client secret or certificate expires
Service endpoints and webhooksThe serviceendpoint table and the plugin steps registered against each endpointContract type (Service Bus queue or topic, Event Hub, webhook), target, message and table that fires it
Virtual tablesTables marked as virtual, with their data provider and data source recordsWhich external or other-instance data appears as a table, and who can see it
Cross-environment flowsCloud flows whose Dataverse connection points at a different environment URLDirection, trigger, the person who owns the connection, and whether it can loop
Two-way synchronisationMiddleware jobs, and records created or modified by an integration identity in both instancesWhich side wins for each column, and what stops an update echoing back and forth
External middleware and exportsAzure Logic Apps, Data Factory, third-party integration platforms, Azure Synapse LinkThe schedule, the owner of the subscription, and the error handling
  • Group records in each instance by created by and modified by. The integration identities that account for large volumes are the live integrations, whatever the documentation claims.
  • Where your Microsoft Entra ID licensing provides service principal sign-in logs, they show which app registrations still authenticate and when. An application user that has not signed in for months is a dormant integration, not a dead one, until its schedule is known.
  • Two instances that both write the same table with no agreed master are the most dangerous finding on a multi-instance estate. Record it first and fix it before anything cosmetic.
  • The identities themselves, and taking ownership of their secrets, are part of the vendor takeover on the rescue and takeover page.

How do you tell load-bearing customisations from dead ones?

Existence is not evidence. An inherited environment is full of steps, flows and scripts that are registered, switched on and doing nothing that matters, next to a few that the business would stop without. Every item on the inventories gets a usage verdict with the evidence written next to it, because the evidence expires: flow run history keeps 28 days, trace logs keep about a day by default, and the person who remembers why something exists may not be there next month.

VerdictEvidenceWhat happens next
Load-bearingRuns regularly and changes data or blocks behaviour that a business process depends onDocument it fully, protect it, and give it a named owner in the runbook
Live but redundantRuns, but duplicates another writer on the same columns or does what configuration already doesCandidate for consolidation after discovery, one writer per rule
DormantSwitched on but no run evidence in the retention window, or tied to a seasonal or year-end processAsk the business, check the calendar, keep it until proven otherwise
DeadSwitched off, references a removed table or column, triggers on something that never happens, or is referenced nowhereListed for removal through a proper managed release, never deleted in production during discovery
UnknownNo evidence either wayTest in the sandbox copy by disabling it and running the processes it could affect
  • Record counts and the latest modified date per table show which custom tables are in use at all. A custom table with no rows created in the last year takes its forms, views, scripts and steps down the verdict list with it.
  • Column fill rates on the columns a plugin or flow writes show whether that writer does anything useful.
  • Where auditing is on, the audit log shows which identity changed a column and when, which settles most arguments about who writes what.
  • Disabling a component to see who complains is a legitimate test only in the sandbox copy, or in production for something low risk, reversible and agreed in writing. Our guide to the Dynamics 365 customisation mistakes we get called in to fix covers what to do with the debt once it is classified.

What documentation and runbook should discovery leave behind?

A documentation set written for whoever runs the system next, whether that is your team, another partner or us. Every document is generated from something in the environment, dated, and kept in the same repository as the unpacked solutions so it can be regenerated rather than going stale.

  • Environment register: every environment in every tenant, its purpose, region, administrators, backup and restore window.
  • Solution repository: every unmanaged solution unpacked in Git, the managed solutions listed with publisher and version, and the known unmanaged layers in production.
  • Execution map: per table and message, every plugin step, real-time workflow, business rule and script, in the order they run.
  • Automation register: every flow and classic workflow with owner, trigger, run-as identity, connection owners and last run.
  • Security matrix: business units, roles, teams, field security and every System Administrator by name.
  • Integration map: per table, the master instance, every copy, the identity, schedule and conflict rule, plus a credential expiry register with dates.
  • Usage verdicts: load-bearing, redundant, dormant, dead or unknown, with the evidence for each.
  • Runbook: for the failures that already happen, what the symptom looks like, where to look (trace log, run history, system jobs), what to restart or replay, and who to tell. Written as incidents occur during the two weeks, not from memory at the end.
  • Risk register and recommendation: what to keep, what to rework, what to rebuild, and in what order.

Which findings justify stopping and rebuilding rather than patching?

Most inherited estates should be patched, not rebuilt: the investment is real and a rebuild carries its own risk. A rebuild is usually a component, a process or an integration rather than the whole system. These are the findings that justify one, and each should be written in the recommendation with its evidence.

  • Core business logic lives only in plugin assemblies with no source, the previous party claims the intellectual property, or the decompiled code cannot be safely changed. Rewrite that component from the documented behaviour.
  • Several writers set the same columns in conflicting ways and the outcome depends on steps with equal rank. Redesign the process with one home for each rule rather than adjusting ranks.
  • Two or more instances write the same tables in both directions with no agreed master. Redesign the integration, and ask whether the estate should be consolidated into fewer environments.
  • Production holds so many unmanaged layers over managed solutions that no release can be trusted. Rebuild clean solutions in a new development environment and move production onto managed releases, as the PAC CLI ALM guide describes.
  • The security model cannot be explained, most users hold System Administrator, and the business unit tree no longer matches the organisation. Rebuild the security model, which can be done in place.
  • The data model is fundamentally wrong for the business, such as generic catch-all tables with meaning encoded in text columns, so every report and process fights it.
  • The platform itself is the problem: the licensing never paid back for how the system is really used. Then a rebuild is a platform decision, and a custom-built CRM is a legitimate outcome.

Who can run this discovery for you?

We recommend the right solution - whether that's Microsoft Dynamics 365, Power Platform, or a custom-built CRM. Some businesses need the Microsoft ecosystem. Others need full control without licensing. We deliver both. Solzet's senior consultants and full-stack developers have 8+ years of Dynamics 365 Customer Engagement and Power Platform work across Sales, Customer Service, Field Service and Customer Insights, and this discovery is the first deliverable of every takeover engagement we run, directly or white-label for Microsoft partners. If what you need is an independent, rated verdict rather than a takeover, that is our health check and technical audit. Solzet works on Customer Engagement and Power Platform only; we do not take over Dynamics 365 Finance, Business Central, NAV or Finance and Operations estates, and we will say so on the first call.

What do people ask us?

What should I do first if I inherited Dynamics 365 with no documentation and no source code?

Make sure your own organisation holds tenant, Power Platform and System Administrator rights, freeze changes to production, take a manual backup and copy production into a sandbox. Then export every unmanaged solution with pac solution export, unpack it with pac solution unpack into a Git repository you own, and start the inventories: plugin steps with stage and rank, flows and workflows with their owners and connections, security, web resources and integrations. The environment is the source of truth, so discovery reads it rather than looking for documents that do not exist.

Can you recover plugin source code from a Dynamics 365 environment?

Not the original source, but the compiled assembly. In Dynamics 365 online, plugin assemblies are stored in the environment and can be extracted and read with a .NET decompiler, which is usually enough to document behaviour and, if needed, rewrite the component. Decompiled code loses comments and original names in places, so it is a basis for documentation rather than something to keep maintaining. Check the contract first, because the previous partner may claim the intellectual property.

Can I export a managed solution to see what is inside it?

No. Managed solutions cannot be exported from an environment they were imported into. Record each one by name, publisher and version, use the solution layers view on the tables, forms and other components you care about to see what it contributes and what sits above it, and ask the publisher or previous partner for the unmanaged source. Unmanaged solutions, and unmanaged changes gathered into a temporary solution, can be exported and unpacked.

How do I find the order Dynamics 365 plugins run in?

List every custom step from the Plugin Registration Tool or the sdkmessageprocessingstep table with its message, table, stage, mode, rank and filtering attributes, then sort by table, message, stage and rank. Pre-validation (10) runs first, then pre-operation (20), then post-operation (40). Within a stage, synchronous steps run in ascending rank; steps with equal rank have no guaranteed order, and asynchronous steps run later as system jobs. Add real-time classic workflows to the same map, and confirm with the plugin trace log.

How do you take over three Dynamics 365 instances with no documentation?

The same method per instance, with one extra document drawn across all of them: the integration map. Build an environment register first, export and diff the same solutions across instances to find which one is really the master, then map application users, service endpoints, webhooks, virtual tables and cross-environment flows per table. The most urgent finding is usually two instances writing the same data in both directions with no agreed master.

How long does discovery of an undocumented Dynamics 365 environment take?

We plan it as two weeks for a typical Customer Engagement estate, producing the documentation set and a recommendation at the end. A very large estate, heavy custom code, or several integrated instances take longer. The environment register, a pac solution list and a count of custom plugin steps and flows on the first day are enough to scope it honestly.

Is this the same as a Dynamics 365 health check?

No. Discovery is about owning a system: it produces the inventories, the runbook and the documentation so someone can safely run and change it. A health check is an independent, rated assessment of how well the system is built across configuration, security, performance, data and process fit. They overlap, and a takeover often starts with discovery and ends with the kind of verdict a health check gives.

When should we rebuild instead of documenting and patching?

When the evidence shows core logic with no usable source, conflicting writers on the same data, bidirectional synchronisation between instances with no master, production too layered with unmanaged changes to release safely, an unexplainable security model, or a data model that fights the business. Usually that means rebuilding a component, not the system. Where licensing never paid back for how the system is used, moving to a custom-built CRM can be the right answer, and Solzet delivers both.

Which solution is right for your business?

Tell us what you need. A senior consultant replies within one business day with a recommendation - Dynamics 365, Power Platform, or a custom-built CRM - not a sales script.