The scripts we used to migrate Jira Assets (Insight) from Data Center to Cloud — zero data loss.
Atlassian doesn't provide a turnkey migration path for Assets data. The documentation is sparse, the APIs are complex, and legacy Datacenter data brings its own surprises.
Atlassian's Assets API documentation is notoriously difficult to decipher. Edge cases are everywhere, and the only way to figure things out is trial and error.
Assets data has deep relational structures—cross-schema references, circular dependencies, and objects that can't exist without each other.
Every test run was long and tedious. Errors ranged from subtle data mismatches to hard API failures. There were no shortcuts—just persistence.
A plan-driven architecture that pre-computes the entire execution order before touching Cloud. Every step is checkpointed for resumability.
Pull schemas, objects, attributes, references, and attachments from Datacenter via REST API.
Build a dependency graph, run topological sort, and detect circular references before creating anything.
Create objects in Cloud in the correct order. Reference fields are resolved in a second pass after all objects exist.
Upload attachments and re-link Jira tickets to their migrated asset objects.
The features that made the difference between a migration that sort-of-works and one that actually completes.
When Object A depends on B and B depends on A, neither can go first. A dedicated tracker defers these to a post-processing phase after all objects exist.
Migration state is checkpointed to disk. If it crashes at object 8,000 of 20,000, it picks up right where it left off—no duplicates, no data loss.
Distribute API calls across up to 5 tokens for higher throughput while respecting rate limits. Essential for large enterprise datasets.
Datacenter and Cloud don't always agree on naming. The schema mapper handles underscores, case differences, partial matches, and pipe patterns automatically.
File attachments are downloaded from Datacenter and re-uploaded to their corresponding Cloud objects with retry logic for transient failures.
Full validation pass without creating anything in Cloud. Catches attribute mismatches, missing references, and configuration errors before the real run.
Every step, in the order you actually run them: credentials and the two things people get wrong first, extracting from Data Center, the migration run itself, attachments and ticket connections, and what to do when it fails at object 8,000 of 20,000. 17 sections.
Written against the toolkit's own source — every flag is read from its configuration manager, every environment variable from its.env.example, and every utility from what is actually in the repository.
Node 18 or newer, Assets enabled on the Cloud target, REST access to Data Center, and bash + curl + jq for the extraction scripts. Plus two credentials most people get wrong on the first try.
| What | Why | Check it |
|---|---|---|
| Node.js 18 or newer | The migration engine and every Node utility. | node --version |
| Jira Cloud with Assets enabled | The target. Assets must already be provisioned — the toolkit migrates into a workspace, it cannot create one. | Open Assets in the Cloud UI |
| Jira Data Center with REST access | The source. Extraction is read-only. | curl -u user:pass <dc>/rest/api/2/myself |
| bash, curl, jq | The Data Center extraction scripts are shell, not Node. macOS, Linux or WSL. | jq --version |
| Assets admin on the Cloud target | Creating object types and objects requires it. A Jira admin who is not an Assets admin gets 403 several minutes into the run. | Assets → Configuration |
403 — so a permission problem looks like a migration problem for the first ten minutes.The token is base64 of email:api_token — not the raw token. The workspace id is a UUID you read from a REST endpoint in your browser.
id.atlassian.com/manage-profile/security/api-tokens.echo -n "you@example.com:your-api-token" | base64. The result — not the raw token — is CLOUD_API_TOKEN. Missing the email and the colon is the single most common setup error, and it presents as a 401 that looks like an expired token.https://your-domain.atlassian.net/rest/servicedeskapi/assets/workspace in a browser where you are logged in. The workspaceId field is the UUID you need for WORKSPACE_ID.cd asset-migration-script
cp .env.example .env
CLOUD_BASE_URL=your-domain.atlassian.net # no https://
CLOUD_API_TOKEN=<base64 of email:api_token>
WORKSPACE_ID=<uuid from the endpoint above>
DATACENTER_PATH= # defaults to ../datacenter_assetsasset-migration-script/.env drives the core migration and the ticket connector. standalone-utilities/.env drives src_misc and upload_attachment_assets. get_datacenter uses neither — you edit main/datacenter_common.sh directly — and automation-service takes CLI arguments only.No migration is perfect. Here's what this toolkit prioritizes and what it intentionally leaves behind.
The history trade-off is deliberate—it ensures the core asset data migrates with zero loss rather than risking partial failures trying to move everything.
The toolkit is open source and free. If you need help running a migration or want to talk through the approach, reach out.