How to Migrate from ServiceDesk Plus to ServiceNow: The Complete Guide
A technical guide to migrating from ServiceDesk Plus to ServiceNow. Covers API rate limits, CMDB relationship mapping, object transformations, and zero-downtime execution.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
How to Migrate from ServiceDesk Plus to ServiceNow: The Complete Guide
TLDR: Migrating from ServiceDesk Plus to ServiceNow is a relationship-heavy data migration that typically takes 3 to 10 weeks depending on record volume and CMDB complexity. The biggest risks are breaking CMDB parent-child relationships during load (ServiceNow enforces relationship depth limits that can silently truncate your CI graph) and hitting API rate limits during extraction. Native CSV exports from ServiceDesk Plus drop conversations, worklogs, and inline attachments, so API-based extraction is required for any production migration. Teams with fewer than 10,000 tickets and no CMDB data can handle this in-house. Organizations with complex CMDB topologies, multi-site configurations, or compliance requirements should use a managed migration service to avoid re-migration.
A ServiceDesk Plus to ServiceNow migration is the process of extracting ITSM data — incidents, problems, changes, assets, CMDB CIs, users, worklogs, and attachments — from ManageEngine ServiceDesk Plus and loading it into ServiceNow's data model while preserving ticket history, relationships, and operational continuity. This guide covers the real API constraints, object mapping, and execution sequence based on production migration experience.
For a side-by-side architecture comparison of these two platforms before you commit to migrating, see our ServiceNow vs ManageEngine ServiceDesk Plus: Architecture & TCO breakdown.
Why Enterprise IT Teams Move from ServiceDesk Plus to ServiceNow
The move is driven by scale, governance, or platform consolidation. ServiceDesk Plus works well for small to mid-size IT teams, but it hits specific architectural limits that ServiceNow does not.
- Workflow engine depth. ServiceNow's Flow Designer supports multi-stage, cross-module orchestration with branching, parallel approvals, SLA timers, and subflows that can invoke Integration Hub spokes. ServiceDesk Plus request workflows support sequential approval stages and field-update actions but lack conditional branching, parallel execution, and cross-module orchestration within a single flow.
- CMDB maturity. ServiceNow's Common Service Data Model (CSDM) provides a standards-based CI class hierarchy with over 400 out-of-box CI classes organized across five CSDM layers (Foundation, Product, Technical, Design, and Sell/Consume). ServiceDesk Plus uses a flat asset-centric model with user-defined CI types that becomes difficult to maintain past a few thousand CIs because relationships are not enforced by class type.
- Enterprise integration surface. ServiceNow exposes IntegrationHub (with 600+ prebuilt spokes as of 2024), bidirectional REST/SOAP APIs, and event-driven integration via Flow Designer triggers. ServiceDesk Plus Cloud restricts API throughput with endpoint-specific rate limits (detailed below) that constrain real-time integration patterns and offers a smaller integration ecosystem.
- Multi-instance consolidation. Large enterprises running ServiceDesk Plus across multiple sites often reach a point where consolidation into a single ServiceNow instance simplifies governance, reporting, and license management. ServiceNow's domain separation feature enables multi-tenant data isolation within a single instance, which ServiceDesk Plus does not offer.
The core data-model difference that makes this migration non-trivial: ServiceDesk Plus stores requests, assets, and CIs in loosely linked modules with numeric priority values and flat picklists. ServiceNow enforces a normalized relational model where every field reference resolves to a sys_id, every CI belongs to a typed class hierarchy, and relationships are stored in a dedicated cmdb_rel_ci table with typed relationship descriptors like Runs on::Runs and Hosted on::Hosts.
ServiceDesk Plus to ServiceNow Object Mapping
The mapping table below covers the core ITSM objects. Every row represents a real mapping decision your migration script must handle.
| ServiceDesk Plus Object | ServiceNow Object | Notes / Caveats |
|---|---|---|
| Requests (Incident type) | Incident (incident) |
SDP uses a single "Request" object with a type field. Filter on is_service_request = false. Map resolution codes carefully; state transitions must align with ServiceNow's ITIL state model (New=1, In Progress=2, On Hold=3, Resolved=6, Closed=7). |
| Requests (Service Request type) | Requested Item (sc_req_item) |
Service Requests map to RITMs, not directly to incident. Catalog items and variable sets must exist in ServiceNow first. Each RITM requires a parent Request (sc_request) record. |
| Problems | Problem (problem) |
Direct mapping. Problem tasks in ServiceNow are child records (problem_task) that SDP does not separate natively — SDP embeds task tracking within the problem record itself. |
| Changes | Change Request (change_request) |
Map SDP change types (Standard, Normal, Emergency) to ServiceNow's Type field. Rebuild approvals and workflows separately from data load — ServiceNow change approvals live in sysapproval_approver, not on the change record itself. |
| Assets | Asset (alm_asset) |
SDP asset types map to ServiceNow CI classes via asset-CI pairing. Do not force every asset into CMDB if it is really ownership or lifecycle data — ServiceNow distinguishes between alm_asset (financial/lifecycle) and cmdb_ci (technical/operational). |
| CIs (CMDB) | CMDB CI (cmdb_ci_*) |
SDP uses flat CI types. ServiceNow requires mapping to specific class tables (e.g., cmdb_ci_server, cmdb_ci_app_server, cmdb_ci_win_server). Load CIs first; load relationships only after target sys_ids exist. |
| Requesters | User (sys_user) |
SDP separates requesters and technicians into distinct tables. ServiceNow uses a single sys_user table with role assignments. Map email addresses as the primary coalesce value. |
| Technicians | User (sys_user) |
Merge with requesters. Assign itil role for agent access. If a person exists in both SDP tables, deduplicate by email before loading. |
| Support Groups | Group (sys_user_group) |
Ensure group membership records (sys_user_grmember) are loaded before ticket assignment references. |
| Solutions | Knowledge Article (kb_knowledge) |
SDP Solutions map to ServiceNow Knowledge Base articles. HTML cleanup is usually required — SDP stores solutions with ManageEngine-specific HTML formatting. Requires a target knowledge base and category structure to exist first. |
| Request Notes | Comments / Work Notes (sys_journal_field) |
Use the SDP show_to_requester flag to determine whether a note maps to ServiceNow's customer-visible comments or internal work notes. (manageengine.com) |
| Worklogs | Time Card (time_card) or Journal Entry |
Use Time Cards only if your ServiceNow instance has Time Card Management enabled. Otherwise, map to journal entries on the parent record. Timestamps must be preserved in UTC. |
| Attachments | Attachment (sys_attachment) |
Binary files linked via table_sys_id. ServiceNow's default maximum attachment size is 24 MB per file (configurable via com.glide.attachment.max_size property). Must be downloaded from the SDP API and uploaded via the ServiceNow Attachment API. |
| Notifications | No direct equivalent | SDP notification history does not migrate. ServiceNow generates its own notification logs via sys_email. |
Field-Level Data Type Transformations
ServiceDesk Plus stores priority as numeric values (typically 1 through 4, where 1 = High in default configuration). ServiceNow priority is also numeric (1–4) but resolves through an Impact × Urgency matrix lookup. Each combination maps to a priority value via the dl_u_priority table or data lookup rules. Your transform script must map SDP priority integers to the correct ServiceNow Impact/Urgency combination — not just copy the number — because a direct numeric copy will produce incorrect priority calculations when ServiceNow re-evaluates.
SDP request notes carry an HTML description plus visibility flags like show_to_requester, while ServiceNow incident records expose separate journal fields: comments (customer-visible, field name comments) and work_notes (internal, field name work_notes). SDP requests also use nested JSON objects for requester, technician, group, status, and priority. ServiceNow resolves many of those values through reference fields (storing sys_id pointers) and choice lists (sys_choice records). (manageengine.com)
SDP custom fields use simple types: single-line text, multi-line text, dropdown, checkbox, date, and numeric. ServiceNow custom fields live on dictionary entries (sys_dictionary) with explicit max lengths, reference qualifiers, and choice list dependencies. Every SDP dropdown value must be pre-loaded into ServiceNow's sys_choice table before ticket import, or the field will silently default to empty — ServiceNow does not create choice values on the fly during import.
Two practical rules help avoid mapping failures:
- Map by meaning, not by label or numeric value. Priority is a good example — copying a raw source number rarely produces the right operational behavior in ServiceNow. Document the explicit mapping (e.g., SDP Priority 1 "High" → ServiceNow Impact 2 + Urgency 1 → Priority 2) and get stakeholder sign-off.
- Preserve source identifiers. Keep the original SDP request ID, display ID, status name, and technician name in searchable custom fields (e.g.,
u_legacy_sdp_id,u_legacy_sdp_status). Agents need these during hypercare for search and reconciliation.
If you migrate every historical ServiceDesk Plus service request into Incident because it is easier, reporting gets simpler but catalog fidelity is gone. If you rebuild them as Request plus Requested Item, you preserve service request structure but increase mapping, validation, and test effort by roughly 40% because you must create catalog items, variable sets, and RITM-to-Request parent relationships. Make this decision explicitly during discovery and document its reporting impact.
What Has No Clean Equivalent in ServiceNow
- SDP Request "Mode" field (Phone Call, Email, Web Form) has no default equivalent in the Incident table. Map to ServiceNow's
contact_typefield (which supports values likephone,email,self-service,walk-in) or create a custom field. - SDP "Due by time" vs. ServiceNow SLA-driven due dates. SDP stores a static due date on the request. ServiceNow calculates due dates dynamically from SLA definitions attached to the
task_slatable. You can write the historical due date to a custom field (e.g.,u_legacy_due_date), but active SLA tracking will not honor it — ServiceNow will calculate its own due dates based on SLA definitions applied to the record. - SDP Purchase Orders and Contracts. ServiceNow has procurement modules (Procurement > Purchase Orders, Contract Management), but they use a different schema (
proc_po,ast_contract). These objects require a separate mapping effort outside the ITSM migration scope. - SDP ticket numbers. ServiceNow auto-generates ticket numbers (e.g., INC0010001) using a number sequence (
sys_number) and does not allow direct control over the Number field via API import or Import Sets. Store the original SDP ticket ID in a custom field (e.g.,u_legacy_sdp_id) for cross-reference. Index this field for query performance.
Migration Architecture: Extracting Data and Managing API Limits
ServiceDesk Plus Cloud Extraction
ServiceDesk Plus Cloud exposes a REST API (v3) with per-operation rate limits that directly constrain extraction throughput. Based on ManageEngine's published rate limit documentation (pitstop.manageengine.com):
| Operation | Throttle Limit | Duration | Lock Period if Exceeded |
|---|---|---|---|
| Request creation | 15 calls | 10 seconds | 5 minutes |
| Request update | 30 calls | 1 minute | 5 minutes |
| Request deletion | 30 calls | 1 minute | 5 minutes |
| Request GET (single and list) | 60 calls | 1 minute | 5 minutes |
| Request close | 30 calls | 1 minute | 5 minutes |
The 5-minute lock period is the real constraint. If your extraction script exceeds the limit on any operation, the API locks that endpoint for 5 full minutes — not just the current request. There is no Retry-After header; you must implement your own 310-second wait.
Pagination uses start_index and row_count inside the input_data JSON parameter. The maximum row_count is 100 per page. The default is 10, so always set this explicitly or you will make 10x more API calls than necessary. (manageengine.com)
Authentication differs between deployment models:
- ServiceDesk Plus Cloud uses OAuth 2.0 via Zoho Accounts (
accounts.zoho.com). The token scope needed isSDPOnDemand.requests.ALLfor request data andSDPOnDemand.assets.ALLfor asset and CI data. Tokens expire after 1 hour; implement automatic refresh using the refresh token. (manageengine.com) - ServiceDesk Plus On-Premises uses a
TECHNICIAN_KEYheader for API authentication. This is a static key generated per technician in Admin > Technicians > API Key Generation. On-Premises installations also offer direct database access (PostgreSQL or MSSQL depending on configuration), which bypasses API rate limits entirely. If your On-Prem instance uses MSSQL, you can extract data directly via SQL queries against theWorkOrder,WorkOrderStates,SDUser,Resources, andCItables — this is significantly faster than API extraction for large datasets.
Example extraction call with pagination and rate limit handling:
import requests
import json
import time
BASE_URL = "https://sdpondemand.manageengine.com/app/portal/api/v3"
HEADERS = {
"Authorization": "Zoho-oauthtoken YOUR_TOKEN",
"Accept": "application/vnd.manageengine.sdp.v3+json"
}
def extract_requests(start=1, page_size=100):
all_requests = []
has_more = True
index = start
while has_more:
list_info = {"list_info": {"row_count": page_size, "start_index": index}}
params = {"input_data": json.dumps(list_info)}
response = requests.get(f"{BASE_URL}/requests", headers=HEADERS, params=params)
if response.status_code == 429:
# API locked for 5 minutes on rate limit breach
print("Rate limit hit. Waiting 310 seconds.")
time.sleep(310)
continue
if response.status_code == 401:
# Token expired — refresh and retry
refresh_oauth_token()
continue
data = response.json()
all_requests.extend(data.get("requests", []))
has_more = data.get("list_info", {}).get("has_more_rows", False)
index += page_size
# Stay under 60 GET calls per minute
time.sleep(1.1)
return all_requestsServiceDesk Plus On-Premises: Direct Database Extraction
For On-Premises installations with large datasets (over 50,000 tickets), direct database extraction bypasses API rate limits entirely. ServiceDesk Plus On-Prem uses PostgreSQL (default) or MSSQL as its backend database.
Key tables for direct extraction:
| SDP Database Table | Contains |
|---|---|
WorkOrder |
All requests (incidents and service requests) |
WorkOrderStates |
Status history per request |
SDUser |
Requesters |
AaaUser / Technician |
Technicians |
Resources |
Assets |
CI |
CMDB configuration items |
CIRelationship |
CI-to-CI relationships |
WorkOrderNotes |
Request notes and conversations |
WorkLog |
Time tracking entries |
Direct database extraction requires coordination with the SDP database administrator and carries the risk of reading data in an inconsistent state if the application is actively writing. Take a database snapshot before extraction. This approach is not available for Cloud deployments.
ServiceNow Load: Import Sets and API Limits
ServiceNow's recommended bulk ingestion path is the Import Set API. You create a staging table (prefix u_), push data into it via the /api/now/import/{staging_table} endpoint, then run a Transform Map to coerce and insert records into the target table.
The insertMultiple endpoint accepts batches. ServiceNow's documentation recommends starting at 200 records per batch and scaling up while monitoring instance performance via the sys_node_stats table or Performance Analytics. The Import Set POST endpoints accept only string name-value pairs, so nested JSON from ServiceDesk Plus must be flattened before staging. For CI loads, ServiceNow explicitly recommends Import Set ... insertMultiple over the older CMDB Data Ingestion API for better scalability and stability. (servicenow.com)
Transform Map Configuration
Each staging table needs a Transform Map that defines:
- Field mappings — source column to target field
- Coalesce fields — the field(s) used to determine insert vs. update (e.g., coalesce on
u_legacy_sdp_idto prevent duplicates on re-runs) - Choice action — set to "reject" to catch unmapped choice values rather than silently inserting blanks
- Transform scripts — onBefore, onAfter, and field-level scripts for data transformation
Example Transform Map script for priority conversion:
// onBefore transform script — map SDP priority to ServiceNow Impact/Urgency
(function runTransformScript(source, map, log, target) {
var sdpPriority = source.u_sdp_priority;
// SDP: 1=High, 2=Medium, 3=Normal, 4=Low
// ServiceNow: Impact (1=High,2=Medium,3=Low) x Urgency (1=High,2=Medium,3=Low)
var priorityMap = {
'1': {impact: '1', urgency: '1'}, // High → Impact High, Urgency High
'2': {impact: '2', urgency: '2'}, // Medium → Impact Medium, Urgency Medium
'3': {impact: '2', urgency: '3'}, // Normal → Impact Medium, Urgency Low
'4': {impact: '3', urgency: '3'} // Low → Impact Low, Urgency Low
};
var mapped = priorityMap[sdpPriority] || {impact: '3', urgency: '3'};
target.impact = mapped.impact;
target.urgency = mapped.urgency;
})(source, map, log, target);ServiceNow's throughput is governed by semaphores, not a simple RPM cap. Each application node has a pool of semaphores (the default is 16 per node). When all active semaphores are busy, incoming requests queue (default queue depth of 150 for web transactions, with a separate API integration queue of 50 governed by glide.rest.throttle.max_queue). When the queue is full, ServiceNow returns HTTP 429. You may also see HTTP 202 for queued requests. Instances can have explicit inbound REST rate limit rules per hour, configured via System Web Services > Inbound > REST Rate Limits, with X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After headers. (servicenow.com)
MID Server consideration: If your ServiceNow instance uses a MID Server for integrations (common in enterprise deployments with network restrictions), route your import API calls through the MID Server to avoid firewall issues. Configure the MID Server's mid.property.max_threads to control concurrent import throughput.
The practical implication: do not run migration loads during business hours on a production instance. Schedule data loads for maintenance windows or use a sub-production instance and promote the data via XML export/import or Update Sets (for configuration) combined with data migration scripts.
For more on ServiceNow-side ingestion challenges, see 10 ServiceNow Data Migration Challenges & How to Overcome Them.
Throughput Math
Header extraction can look fast on paper, but child extraction dominates elapsed time. With row_count = 100 and 60 GET calls per minute, the theoretical ceiling for request headers is 6,000 rows per minute. That number is misleading for real migrations. If 100,000 tickets each require notes, worklogs, and attachment metadata in three extra GET calls per ticket, that is 300,000 child calls — roughly 5,000 minutes (~83 hours) of child extraction at the documented 60 GET calls per minute, before retries or lockout penalties.
For a migration of 200,000 incidents with worklogs and attachments:
- Extraction from SDP Cloud: ~24 to 48 hours (rate-limited by the 60 GET calls/minute ceiling and 5-minute lockout risk). SDP On-Prem with direct database extraction: 1 to 4 hours.
- Transform and staging: 2 to 4 hours (local processing — JSON flattening, deduplication, timestamp conversion)
- Load into ServiceNow: 4 to 8 hours at 200 records per batch with moderate business rule complexity. Disable non-essential business rules on target tables during load to improve throughput by 30–50%.
- Attachment upload: Highly variable. A 5 MB average file size across 200,000 attachments represents ~1 TB of binary data. Plan 8 to 16 hours for attachment transfer alone. ServiceNow's Attachment API accepts multipart/form-data; base64 encoding inflates payload size by ~33%.
Your retry logic should assume throttling, not treat it as an exception path:
retryable = {202, 429}
while True:
response = send_batch()
if response.status_code in retryable:
retry_after = int(response.headers.get('Retry-After', 5))
time.sleep(retry_after)
continue
if response.status_code >= 400:
log_error(response)
break
breakHonor Retry-After, cap concurrency, and log every source ID to target sys_id mapping to a reconciliation table.
Common Error Codes and Handling
| Source | HTTP Code | Meaning | Action |
|---|---|---|---|
| SDP Cloud | 429 | Rate limit exceeded | Wait 310 seconds (5-minute lockout + buffer) |
| SDP Cloud | 401 | OAuth token expired | Refresh token and retry |
| SDP Cloud | 400 | Malformed input_data parameter |
Log payload and fix JSON structure |
| ServiceNow | 429 | Semaphore queue full | Honor Retry-After header; reduce batch concurrency |
| ServiceNow | 202 | Request queued but not yet processed | Poll for completion; do not resubmit |
| ServiceNow | 403 | ACL denied — user lacks table access | Verify import user has import_set_loader and import_transformer roles |
| ServiceNow Transform | Error record in import set | Coalesce failed or reference not found | Check sys_import_state_comment on the import set row for the specific error message. Common causes: target reference field has no matching sys_id, choice value not in sys_choice, mandatory field is null |
What Data Cannot Be Migrated Natively from ServiceDesk Plus?
ServiceDesk Plus Cloud offers a built-in data export under Setup > Data Administration > Export Data. It supports CSV and XLSX formats for Users, Requests, Problems, Changes, Releases, Purchase Orders, Solutions, Attachments, Categories, Contracts, Vendors, Assets, and Conversations. (manageengine.com)
This native export has hard limitations:
- Files larger than 500 MB are automatically split into multiple zip files. Reassembling them requires custom scripting, and the split boundaries can break mid-record in CSV format — a partial row at the end of one file and the continuation at the start of the next.
- Worklogs and time entries are not included in the standard request export. They require separate API extraction via
/api/v3/requests/{id}/worklogs. - Inline attachments embedded in conversation HTML are not exported as discrete files. They are embedded as base64 or linked to SDP-hosted URLs that will break after decommissioning. You must parse conversation HTML, extract embedded images, download them separately, and re-link them in ServiceNow.
- CMDB relationship data (parent-child, HostedOn, RunsOn) is not preserved in the flat CSV export. You get a flat list of CIs with no relationship context.
- Conversation threading and chronological ordering must be reconstructed from timestamps. The CSV export does not guarantee sort order.
- Custom field values may export as internal IDs rather than display values for dropdown fields, requiring a separate lookup table export to resolve.
ManageEngine's own admin documentation tells customers to contact support for full data export — the platform itself acknowledges that full-fidelity extraction is a separate exercise from a normal UI export.
For any migration involving more than basic ticket data, the API-based extraction path is the only viable option.
Can ServiceDesk Plus CMDB History Be Preserved in ServiceNow?
Yes, but with significant constraints. ServiceDesk Plus CMDB data can be extracted via the /api/v3/cmdb endpoints and loaded into ServiceNow's cmdb_ci_* class tables. The challenge is relationships. (manageengine.com)
ServiceNow CMDB stores all CI relationships in the cmdb_rel_ci table using typed relationship descriptors. Each relationship record links a parent CI (parent) to a child CI (child) with a type reference (type) pointing to cmdb_rel_type. Common types include Runs on::Runs, Hosted on::Hosts, Depends on::Used by, and Contains::Contained by. The cmdb_rel_type table contains approximately 30 out-of-box relationship types.
ServiceDesk Plus relationships are simpler and often untyped — they store a generic "related to" association without distinguishing between dependency, containment, and hosting relationships. Your migration must:
- Extract SDP CI relationships via the API (not available in CSV export). The endpoint is
/api/v3/cmdb/ci/{ci_id}/relationships. - Map SDP relationship types to ServiceNow relationship type sys_ids from the
cmdb_rel_typetable. If SDP relationships are untyped, you must infer the type from CI classes (e.g., a relationship between acmdb_ci_app_serverandcmdb_ci_serveris likelyRuns on::Runs). - Load CIs before relationships to ensure both parent and child sys_ids exist. Attempting to create a
cmdb_rel_cirecord with a nonexistent parent or child sys_id produces an error record in the import set. - Monitor relationship depth to avoid ServiceNow's "Maximum relationship limit has been reached" error.
ServiceNow's relationship level setting (controlled by the ecmdb.levels user preference, default value of 3) determines how many layers of relationships are loaded and displayed in the dependency view. At levels 4 and 5, the system can produce incomplete data and significant slowdowns during impact analysis because the number of related CIs grows exponentially. Errors like "Maximum relationship limit has been reached" or "Maximum number of queries has been reached" are common at these depths.
For most production environments, keeping imported relationships at 2 to 3 levels deep provides the best balance of visibility and performance. If your SDP CMDB has deeply nested relationships (e.g., Application → Web Server → VM → Host → Cluster → Data Center — 6 levels), plan to flatten or restructure the hierarchy during transformation. Common strategies: collapse the VM→Host→Cluster chain into a direct Hosted on relationship from Application to Host, and capture the intermediate layers as attributes rather than separate CIs.
CMDB Health validation after load: Run ServiceNow's CMDB Health Dashboard (navigate to Configuration > CMDB Dashboard) to check for:
- Orphan CIs (CIs with no relationships — may be intentional but should be reviewed)
- Duplicate CIs (same serial number or name in the same class)
- Stale CIs (imported CIs with no discovery source that will never be updated)
- Orphan relationships (relationship records pointing to deleted or nonexistent CIs)
Do not validate a CMDB migration by opening one giant dependency view and hoping it renders. Validate by CI class, relationship type, and sampled parent-child pairs. Use GlideRecord queries to systematically check relationship integrity:
// Find orphan relationships — cmdb_rel_ci records where parent or child CI doesn't exist
var gr = new GlideRecord('cmdb_rel_ci');
gr.addQuery('parent', '');
gr.addOrCondition('child', '');
gr.query();
gs.info('Orphan relationships found: ' + gr.getRowCount());This catches broken keys earlier and avoids the platform rendering limits the system warns about.
Domain Separation Considerations
For enterprise ServiceNow instances with domain separation enabled, the migration load path changes materially:
- Every imported record must include a
sys_domainvalue, or it will land in the global domain. - Transform Maps must set the domain based on source site/location mapping.
- Users must have domain-appropriate roles, or they will not see migrated records.
- CMDB CIs can span domains, but relationships between CIs in different domains require careful ACL configuration.
- Import Set staging tables are not domain-separated by default — you must configure domain separation on custom staging tables if you need domain-aware transforms.
If your ServiceNow instance uses domain separation and your SDP deployment spans multiple sites, map each SDP site to a ServiceNow domain during the discovery phase.
Migration Approaches: DIY vs. Native vs. Managed Service
| Approach | How It Works | Best For | Risk | Complexity |
|---|---|---|---|---|
| Native CSV Export/Import | Export from SDP Data Administration, import via ServiceNow Import Sets | Small instances (<5,000 tickets), no CMDB | High — loses relationships, worklogs, attachments | Low |
| Custom API Scripts (Python/Node.js) | Write extraction scripts against SDP v3 API, transform locally, push to ServiceNow Import Set API | Mid-size (5K to 100K tickets) with dedicated dev resources who know both platforms | Medium — requires retry logic, rate limit handling, relationship ordering | High |
| Third-Party Migration Tool | Automated tool with pre-built SDP connector | Simple ticket migrations without deep CMDB | Medium — limited field mapping customization, may not handle CMDB relationships or conversation threading | Low |
| Integration Platform (e.g., ZigiOps) | No-code bidirectional sync for ongoing integration | Ongoing sync or coexistence, not one-time historical migration | Low for sync, but not designed for historical bulk migration or data remodeling | Medium |
| Managed Migration Service | Dedicated engineers handle extraction, transformation, loading, and validation | Enterprise (100K+ records), complex CMDB, compliance requirements | Low — handled by specialists with rollback capability | Low (for the customer) |
The distinction between a one-time historical re-platform and ongoing e-bonding between two live ITSM tools matters. Tools positioned around automated help desk replication solve a different problem than integration platforms designed for bidirectional sync.
Clear picks:
- Small team, under 5,000 tickets, no CMDB: Native CSV export to ServiceNow Import Sets can work. Budget 1 to 2 weeks for manual cleanup.
- Mid-size, 5,000 to 100,000 tickets with CMDB: Custom API scripts if you have an engineer who knows both platforms. Budget 4 to 6 weeks.
- Enterprise, 100K+ records, multi-site, compliance: Managed migration service. The engineering cost of building, debugging, and validating custom scripts typically exceeds the cost of hiring specialists — particularly when you factor in re-migration risk.
- Parallel run or long coexistence: Integration platform for ongoing sync, but plan a separate effort for historical data remodeling.
ServiceNow Licensing Impact
ServiceNow licensing affects what you can import and where:
- ITSM Standard does not include Performance Analytics, Agent Workspace, or Virtual Agent. If your migration plan assumes these features for validation or post-migration operations, verify your license tier.
- Custom tables (prefix
u_) for staging or legacy data may consume table quota depending on your contract. Check with your ServiceNow account team before creating staging tables you plan to keep post-migration. - CMDB CI count may be subject to licensing thresholds. ServiceNow's ITSM Pro and Enterprise tiers include CMDB, but the number of CIs under active management (with Discovery or Service Mapping) may have contractual limits.
- Integration Hub spokes and actions beyond the included quota may incur additional licensing costs if you plan to use Flow Designer for ongoing SDP-to-ServiceNow sync during a parallel run.
If a DIY script fails mid-migration and you need to recover, see Help Desk Data Migration Failed? The Engineer's Rescue Guide.
Step-by-Step Process for a Zero-Downtime Migration
The order of operations determines whether relationships survive or break. Loading tickets before users exist means every requester and assignee field will be empty or default. Loading CMDB relationships before CIs exist means orphaned relationship records.
For a complete treatment of zero-downtime migration strategy, see Zero-Downtime Help Desk Data Migration.
Phase 1: Discovery and Audit (Week 1)
- Inventory all SDP modules in use: Requests, Problems, Changes, Assets, CMDB, Solutions, Purchase Orders
- Count records per module and per status (use SDP's Reports module or API list endpoints with
row_count=1to get total counts fromlist_info.total_count) - Document all custom fields, custom statuses, and custom templates
- Identify SDP integrations (email parsing rules, Active Directory sync, third-party connectors) that will need ServiceNow equivalents
- Export a sample of 100 records per module for mapping validation
- Decide what becomes live operational data versus historical archive (common split: last 12–24 months as active, older records as read-only archive)
- Audit compliance requirements: If SOX, HIPAA, or ITIL audit trails apply, document the chain-of-custody requirements for migrated data — what hash verification is needed, what migration logs must be retained, and what sign-off process is required before decommissioning SDP
Phase 2: Build the Mapping and Transform Rules (Week 2)
- Create the object mapping table (use the one above as a starting point)
- Map every SDP custom field to a ServiceNow dictionary entry — create
u_prefixed fields in ServiceNow before loading - Pre-load all choice list values into ServiceNow's
sys_choicetable - Build Transform Maps in ServiceNow for each staging table
- Write and test coalesce rules — the logic that determines insert vs. update on each record. Recommended coalesce fields:
u_legacy_sdp_idfor tickets,emailfor users,serial_numberornamefor CIs - Test transforms with the 100-record sample from Phase 1
Phase 3: Extract from ServiceDesk Plus (Week 2 to 3)
Extraction sequence matters. Pull reference data before transactional data:
- Users (Requesters and Technicians) via
/api/v3/requestersand/api/v3/technicians - Groups (Support Groups) via
/api/v3/support_groups - Categories and Subcategories via
/api/v3/categories - Solutions (Knowledge Articles) via
/api/v3/solutions - Assets and CIs via
/api/v3/assetsand/api/v3/cmdb - Requests (Incidents and Service Requests) via
/api/v3/requestswith pagination - Worklogs per request via
/api/v3/requests/{id}/worklogs - Conversations per request via
/api/v3/requests/{id}/conversations - Attachments per request via
/api/v3/requests/{id}/attachments(download binary) - Problems via
/api/v3/problems - Changes via
/api/v3/changes - CMDB Relationships via
/api/v3/cmdb/ci/{ci_id}/relationships(extract after all CIs are extracted)
Example extraction call using curl:
curl -G '<service-domain>/app/<portal>/api/v3/requests' \
-H 'Accept: application/vnd.manageengine.sdp.v3+json' \
-H 'Authorization: Zoho-oauthtoken <token>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'input_data={"list_info":{"row_count":100,"start_index":1,"sort_field":"created_time","sort_order":"desc"}}'Phase 4: Transform and Clean (Week 3)
- Deduplicate users by email address (SDP allows the same person as both requester and technician with different internal IDs)
- Normalize status values to ServiceNow's fixed lifecycle (New=1, In Progress=2, On Hold=3, Resolved=6, Closed=7, Canceled=8)
- Convert SDP priority integers to ServiceNow Impact/Urgency pairs using the documented mapping
- Strip SDP-internal HTML from conversation bodies (ManageEngine adds platform-specific CSS classes and inline styles)
- Resolve SDP user IDs to email addresses for ServiceNow user coalescing
- Convert all timestamps to UTC (SDP stores timestamps in the instance's configured timezone; ServiceNow stores UTC internally and renders in the user's timezone)
- Generate SHA-256 hashes of extracted data files for audit trail if compliance requires chain-of-custody verification
Phase 5: Load into ServiceNow (Week 3 to 4)
Load order prevents broken references:
- Users and Groups
- Categories and Knowledge Base structure
- Knowledge Articles
- CMDB CIs (load by class, starting with base infrastructure classes:
cmdb_ci_server→cmdb_ci_app_server→cmdb_ci_service) - CMDB Relationships (via
cmdb_rel_ciimport set) - Incidents (with requester, assignee, and group references resolved via coalesce)
- Worklogs and Conversations (as journal entries on parent records)
- Attachments (binary upload linked via
table_sys_id) - Service Requests → Requests (
sc_request) → Requested Items (sc_req_item) - Problems and Changes
- Cross-record links (incident-to-problem via
problem_idfield, incident-to-change associations)
Before loading any data, apply these ServiceNow instance settings:
// Disable SLA processing for historical records
// Set the property before load, revert after
gs.setProperty('com.snc.sla.engine.enabled', 'false');
// Or more selectively, set on each imported record:
// target.sla_due = ''; // Clear SLA due date to prevent recalculationUse sysparm_suppress_auto_sys_field=true on Import Set API calls to prevent ServiceNow from overwriting sys_created_on and sys_created_by with current values — this preserves historical timestamps.
Example ServiceNow Import Set load:
curl 'https://<instance>.service-now.com/api/now/import/u_sdp_request_stage/insertMultiple' \
--request POST \
--header 'Accept:application/json' \
--header 'Content-Type:application/json' \
--header 'Authorization:Basic <base64_credentials>' \
--data '{"records":[{"u_sdp_id":"2079997951635005","u_subject":"Need an External Monitor","u_created_on":"2024-01-15 14:30:00"}]}'Phase 6: Validate and Reconcile (Week 4 to 5)
- Record count reconciliation per object type
- Spot-check 5% sample of tickets for field accuracy
- Verify all CMDB relationships resolve (no orphaned
cmdb_rel_cirecords) - Confirm attachment accessibility
- Run ServiceNow CMDB Health Dashboard to check for orphan relationships and stale CIs
- Validate worklog chronological ordering and correct public/private visibility
Validation queries for reconciliation:
// Count incidents by source verification
var gr = new GlideRecord('incident');
gr.addNotNullQuery('u_legacy_sdp_id');
gr.query();
gs.info('Migrated incidents: ' + gr.getRowCount());
// Find incidents with unresolved requester references
var gr2 = new GlideRecord('incident');
gr2.addNotNullQuery('u_legacy_sdp_id');
gr2.addNullQuery('caller_id');
gr2.query();
gs.info('Incidents with null caller: ' + gr2.getRowCount());
// Verify attachment counts match
var gr3 = new GlideRecord('sys_attachment');
gr3.addQuery('table_name', 'incident');
gr3.query();
gs.info('Incident attachments: ' + gr3.getRowCount());Phase 7: Delta Sync and Cutover
- Run a final API pass to capture any tickets created or updated in ServiceDesk Plus during the migration window. Use
created_timeandlast_updated_timefilters in the SDP API to pull only delta records. - Execute cutover during a planned maintenance window (typically a weekend)
- Redirect agents to ServiceNow
- Keep ServiceDesk Plus in read-only mode for at least 30 days post-cutover as your rollback path
If you load historical tickets into ServiceNow without suppressing notifications and SLA rules, every resolved ticket from the past several years can trigger an email to the original requester. Use sysparm_suppress_auto_sys_field=true on Import Set loads and disable SLA processing for historical records before loading. This is the single most common post-migration incident. In one observed case, a migration of 150,000 historical incidents without notification suppression generated over 80,000 outbound emails in 45 minutes before the email queue could be manually paused.
Rollback Procedure
Build a rollback plan before go-live. ServiceNow's Update Sets cover configuration, not data. Your rollback must be a data-level revert:
- Before import: Export each target table's current state using ServiceNow's XML export (
/sys_export.do?sysparm_query=...&XML). For large tables, use background scripts with GlideRecord to export in batches. - Rollback execution: If validation fails, delete all records with
u_legacy_sdp_id IS NOT NULLfrom each target table, then re-import the pre-migration XML exports. - CMDB rollback: Delete relationships first (
cmdb_rel_cirecords), then CIs. Reversing this order leaves orphan relationship records. - Timeline: A data-level rollback for 200K+ records across 8–10 tables takes 2 to 6 hours depending on business rule complexity. Test the rollback procedure in sub-production before go-live.
Go-live criteria should be concrete: agents can search by old SDP ID, record counts reconcile per object type within 0.1%, public versus internal notes are correctly mapped, attachments open, sampled CIs show the right parent-child relationships, and historical loads have not triggered notifications or automations.
How Long Does a ServiceDesk Plus to ServiceNow Migration Take?
A realistic timeline depends on record volume, CMDB complexity, and the number of custom fields.
| Scenario | Record Volume | CMDB Complexity | Timeline |
|---|---|---|---|
| Small IT team | Under 10,000 tickets, no CMDB | None | 2 to 3 weeks |
| Mid-size organization | 10,000 to 100,000 tickets, basic CMDB | 500 to 5,000 CIs, 2 levels deep | 4 to 6 weeks |
| Enterprise | 100,000+ tickets, full CMDB | 5,000+ CIs, 3+ levels, multi-site | 6 to 10 weeks |
| Enterprise with domain separation | 100,000+ tickets, full CMDB, multi-domain ServiceNow | 5,000+ CIs, 3+ levels, multi-site, multi-domain | 8 to 14 weeks |
The extraction phase alone accounts for 30 to 40% of the total calendar time due to API rate limits (Cloud) or database coordination (On-Prem). The validation phase accounts for another 20 to 25%. Teams that underestimate validation end up discovering data quality issues in production.
Most delays come from mapping decisions and validation, not from the first successful API call. Common sources of delay:
- Stakeholder disagreement on service request handling (Incident vs. RITM) — 1 to 2 weeks
- SDP custom field mapping to ServiceNow dictionary entries — 3 to 5 days
- CMDB class mapping decisions — 2 to 4 days
- Re-run after discovering data quality issues in validation — 1 to 2 weeks
Edge Cases and Known Limitations
- Duplicate users across SDP Requester and Technician tables. A single person can have both a requester record and a technician record in ServiceDesk Plus with different internal IDs. ServiceNow uses one
sys_userrecord per person. Deduplicate by email before loading. If the same email has different names in requester vs. technician records, choose the technician record as the canonical source. - SDP custom statuses vs. ServiceNow fixed statuses. ServiceDesk Plus allows custom statuses (e.g., "Waiting for Vendor", "Pending Approval"). ServiceNow Incident uses a fixed status lifecycle (1–8). Custom statuses must be mapped to ServiceNow's standard values or added to the State field's choice list via
sys_choicerecords. Adding custom states requires modifying the incident state model, which affects SLA calculations and reporting. - Attachment size limits. ServiceNow's default maximum attachment size is 24 MB (controlled by
com.glide.attachment.max_size). SDP files larger than this must be uploaded to a linked file store or compressed. Large attachments may also need multipart/form-data uploads because base64 encoding inflates file size by approximately 33%. - Time zone mismatches. SDP stores timestamps in the instance's configured timezone. ServiceNow stores all timestamps in UTC internally and renders them in the user's local timezone. All extracted timestamps must be converted to UTC before loading, or your SLA calculations and audit trails will be wrong. Use Python's
pytzlibrary or equivalent for reliable timezone conversion. - ServiceNow business rules firing on imported data. Assignment rules, notification rules, and SLA rules can fire on newly inserted records. Disable SLA processing (
com.snc.sla.engine.enabled = false) for historical records and suppress auto-generated notifications during bulk loads to prevent false alerts and email storms. Re-enable after loading and before delta sync. - Unsupported custom modules. Custom modules built in ServiceDesk Plus that have no equivalent in ServiceNow must be mapped to custom tables (
u_prefix), which may consume table quota depending on your ServiceNow contract. - Service request model fidelity. If you compress historical service requests into incidents for simplicity, you reduce build time by approximately 40% but lose catalog structure. This affects reporting (service request vs. incident volume trends), audit trails (catalog approval history), and future automation (catalog-driven workflows). Make this trade-off explicitly — it is permanent.
- Multi-language content. If your SDP instance stores ticket descriptions or knowledge articles in multiple languages, ServiceNow's internationalization model requires separate knowledge article versions per language, not inline translation. Plan for additional transform work.
Validation and Testing Checklist
| Check | Method | Pass Criteria |
|---|---|---|
| Record count by object type | Query both systems, compare totals | Counts match within 0.1% (accounting for filtered exclusions) |
| Field accuracy (5% sample) | Pull random records by u_legacy_sdp_id, compare field values side by side |
All mapped fields match source data |
| User resolution | Query incidents for null caller_id where u_legacy_sdp_id is not null |
Zero null requester fields on records that had a requester in SDP |
| CMDB relationship integrity | Run ServiceNow CMDB Health check + orphan relationship query | Zero orphan relationships |
| Attachment accessibility | Open 50 random attachments across modules | All files render correctly, file sizes match source |
| Worklog/conversation threading | Open 20 tickets with complex conversation history | Chronological order preserved, correct public/private visibility |
| Knowledge article formatting | Review 10 articles in ServiceNow portal | No broken images, tables render, internal links resolve |
| SLA accuracy (historical) | Compare SDP resolution times to ServiceNow custom fields | Historical SLA data preserved in u_legacy_sla_* fields (not recalculated by ServiceNow) |
| Notification suppression | Verify no outbound emails triggered during load | Zero unexpected notifications in sys_email table during load window |
| Legacy ID searchability | Search for 10 random SDP ticket IDs in ServiceNow global search | All records found via u_legacy_sdp_id |
Compliance Audit Trail
For SOX, HIPAA, or ITIL-audited migrations, produce the following documentation:
- Source data inventory — record counts per module, extracted date, extraction method
- Hash verification — SHA-256 hashes of all extracted data files, matched against hashes of loaded data
- Field mapping document — signed off by the data owner, documenting every source-to-target field mapping and any data transformations applied
- Validation report — results of the checklist above, with specific record IDs sampled
- Migration log — timestamped log of every API call, batch load, and transform execution with success/failure status
- Rollback test results — evidence that the rollback procedure was tested in sub-production
Build In-House or Use a Managed Service?
Build in-house when:
- Your ticket volume is under 10,000 records
- You have no CMDB data or only a flat asset list
- You have an engineer who knows both the SDP API and ServiceNow Import Sets
- You have a 2 to 3 week window where that engineer can focus entirely on migration
- Service requests can be simplified instead of rebuilt as catalog objects
- No compliance audit trail requirements
Use a managed migration service when:
- Your record count exceeds 50,000 tickets
- You have a CMDB with multi-level relationships
- You need to preserve full conversation and worklog history with correct public/private visibility
- Compliance requirements (SOX, HIPAA, ITIL audit trails) mandate a verified, documented migration with chain-of-custody logging
- Your ServiceNow instance uses domain separation
- Your engineering team is already committed to the ServiceNow implementation and cannot absorb migration work without delaying go-live
The hidden cost of DIY is re-migration. Teams that build custom migration scripts frequently end up re-running the migration due to missed edge cases, broken relationships, or field mapping errors discovered after go-live. Each re-run resets the extraction clock by 24 to 48 hours (Cloud) and forces a second round of validation.
ClonePartner has completed ITSM migrations involving complex CMDB hierarchies, multi-site ServiceDesk Plus instances, and ServiceNow Import Set orchestration. Our engineering team handles the rate-limit throttling, relationship ordering, and attachment re-hosting that cause most DIY migrations to stall or produce incomplete data.
Frequently Asked Questions
- How long does a ServiceDesk Plus to ServiceNow migration take?
- A typical migration takes 2 to 3 weeks for small IT teams with under 10,000 tickets and no CMDB, 4 to 6 weeks for mid-size organizations with basic CMDB, and 6 to 10 weeks for enterprise environments with 100,000+ tickets and complex multi-level CMDB. The extraction phase alone accounts for 30 to 40% of calendar time due to API rate limits.
- Can I migrate ServiceDesk Plus to ServiceNow without losing CMDB data?
- Yes, but only through API-based extraction. The native CSV export from ServiceDesk Plus does not preserve CMDB relationship data. You must extract CIs and their parent-child relationships via the v3 REST API, map relationship types to ServiceNow's cmdb_rel_type records, and load CIs before relationships to avoid orphaned records.
- What data cannot be migrated from ServiceDesk Plus to ServiceNow?
- SDP notification history, audit trails of field-level changes, and active SLA timer states have no direct migration path. Native CSV exports also drop worklogs, inline attachments, conversation threading, and CMDB relationships. SDP-specific modules like built-in project management and certain procurement workflows have no equivalent in ServiceNow ITSM without additional module licensing.
- How much does a ServiceDesk Plus to ServiceNow migration cost?
- Cost depends primarily on record volume, custom field count, and CMDB complexity. A managed migration service for 10,000 tickets without CMDB data typically costs $3,000 to $8,000. Enterprise migrations with 100,000+ records and full CMDB can range from $15,000 to $50,000+. DIY migrations cost less in vendor fees but consume 4 to 8 weeks of senior engineering time.
- Does ServiceNow have a native import tool for ServiceDesk Plus?
- No. ServiceNow does not offer a built-in connector or migration wizard for ServiceDesk Plus. Data must be loaded through ServiceNow's generic Import Sets and Transform Maps from CSV, XML, or API sources. ManageEngine also does not provide a native export-to-ServiceNow feature.

