June 30, 2026
4 min Read
NetSuite-to-NetSuite Data Migration: Lessons From a 3rd Implementation
Introduction
Moving data from one NetSuite instance to another sounds straightforward until you learn NetSuite doesn't have a systematic way to push data from one instance to another. The source and target systems are familiar, yet the migration still requires significant effort to ensure that detailed transactions migrate cleanly and accurately.
We worked with a Canadian healthcare agency on their third NetSuite implementation in two years, a pace driven by successive private equity ownership changes, each of which triggered a new NetSuite instance. By that point, the team was dealing with migration fatigue, and the margin for error on the data side was narrow. The lessons from that project apply to any NetSuite-to-NetSuite engagement.
The Most Important Decision You Make Early: External IDs
In a NetSuite-to-NetSuite migration, every record in the legacy instance is assigned an internal ID by the system. That internal ID is not transferable. The new instance will assign its own internal IDs when records are created.
The decision that makes everything downstream easier is this: assign the legacy internal ID as the external ID in the new instance. Do this for every record type you migrate: projects, customers, vendors, employees, transactions.
When you do this, every subsequent mapping step uses the legacy internal ID as a stable, human-readable reference. Cross-referencing records between the two instances becomes a lookup rather than manual reconciliation. Skipping this step is a common reason NetSuite-to-NetSuite projects get complex fast. This easy solution will save you and your team effort on the mapping exercise between the two systems.
Getting the Data Out: Analytics Workbook and the Right Tooling
For this engagement, we used the Analytics Workbook to pull data that required joining across record types. The Workbook supports multiple join fields that saved searches cannot, and for complex transaction histories with project and timecard relationships, that capability mattered. The Analytics Workbook served as the backbone of the general ledger for the financial data migration.
Import Project Tasks with Celigo CloudExtend
The native project task import tool has real limitations. You cannot set the external ID on a project task, run an update load, or save your uploads for reuse. Together, those gaps make the native tool unworkable for a migration of this scope. Instead, I used Celigo CloudExtend to create the project tasks, paired with custom Python scripting to transform the legacy data into the upload files the tool needed. Here is the general approach:
- Build a consolidated project task file from the legacy instance, pulling the relevant source data together in one place.
- Establish a unique identifier for each task. NetSuite won't let you set the project task ID directly, so a combination of a custom field and the external ID preserved the original ordering and gave each task a reliable key.
- Create the base project tasks first. All records must load before any parent-child relationships can be assigned.
- Export the newly created tasks to capture their internal IDs, since the tool maps relationships on internal IDs rather than external IDs.
- Use those internal IDs to assign the parent-child hierarchy between tasks.
- Add resource assignments to each task once the hierarchy is in place.
Two limitations are worth flagging for anyone attempting this. First, the tool overrides the resource list, so any task with more than one resource only imports a single resource. Those have to be adjusted manually afterward. Second, you cannot assign a resource to a task NetSuite has flagged as a Milestone, which it does automatically based on budgeted hours at the task level. Check for that before you start assigning resources.
What We Migrated and Why It Mattered
For this healthcare agency, continuity was the requirement. The organization ran project-based work, and the history of those projects, their tasks, the timecards logged against them, and the associated transactions had to move intact. Losing that history would have meant recreating it manually or accepting gaps that would surface in reporting and client billing.
Migrating projects and project tasks requires attention to parent-child relationships. A project task is only meaningful in the context of its parent project, and loading them out of sequence or without the correct foreign key references produces orphaned records that are hard to diagnose later. Timecards include employee and project references, as well as date logic. Transactions carry all of the above plus GL coding.
The sequencing of these loads follows from the dependencies: master records first, then parent-level projects, then tasks, then timecards, then transactions. That order is not optional.
Migration Fatigue Is Real, and the Data Workstream Is Where It Shows
By a third implementation, teams have seen what a poorly managed migration does to a go-live. They are skeptical of timelines, cautious about commitments, and exhausted by the gap between what the implementation team promises and what the data actually does.
The answer to migration fatigue is not a better slide deck. It is a separate, owned data workstream that runs in parallel with configuration, reports progress in terms that the finance and project teams can verify, and does not ask the configuration consultants to also be data migration specialists.
OptimalData provides a data migration solution: our team handles data extraction, mapping, and uploads, so the partner can keep consultants focused on configuration and the client team can focus on cleanup based on their business needs.
If you are planning a NetSuite-to-NetSuite engagement and want to talk through the data architecture before the project kicks off, let's chat.
