Konnektr Logo
How-to Guides

Migration Guide

This guide explains how to migrate your data from Azure Digital Twins to Konnektr Graph, or between different Konnektr Graph deployments.

Migration Scenarios

From Azure Digital Twins to Konnektr Graph

Migrate from Azure Digital Twins to either hosted or self-hosted Konnektr Graph.

Between Konnektr Graph Deployments

Move data between hosted and self-hosted Konnektr Graph instances.

From Other Digital Twin Platforms

Migrate from other DTDL-compatible platforms.

1. Choose Your Target Deployment

  1. Create account at ktrlplane.konnektr.io
  2. Deploy a new Konnektr Graph resource
  3. Get your endpoint: https://your-resource-id.api.graph.konnektr.io
  4. Set up Auth0 authentication

Option B: Self-Hosted Konnektr Graph

  1. Deploy using Self-Hosted Guide
  2. Configure authentication (JWT, API keys, or development mode)
  3. Get your endpoint URL

2. Migration Approaches

The Azure Digital Twins SDK approach works for all scenarios and provides the easiest migration path:

Benefits:

  • ✅ Works with both hosted and self-hosted Konnektr Graph
  • ✅ Handles all DTDL model complexities
  • ✅ Built-in retry logic and error handling
  • ✅ Same code works for Azure → Konnektr migration

Setup:

  • Source (Azure Digital Twins): Use DefaultAzureCredential
  • Target (Hosted Konnektr Graph): Use custom Auth0 TokenCredential
  • Target (Self-Hosted): Use custom JWT or API key TokenCredential

Note: See Using Azure Digital Twins SDKs for complete authentication setup.

Example Migration Scripts

copy_adt.py
# Example migration script for Python
# This script copies models, twins, and relationships from Azure Digital Twins to AgeDigitalTwins

from azure.identity import DefaultAzureCredential
from azure.digitaltwins.core import DigitalTwinsClient
import requests
import json

# Azure Digital Twins configuration
adt_url = "https://your-adt-instance.api.eus.digitaltwins.azure.net"
adt_client = DigitalTwinsClient(adt_url, DefaultAzureCredential())

# AgeDigitalTwins configuration
age_base_url = "https://your-resource-id.api.graph.konnektr.io"
age_headers = {"Authorization": "Bearer your-token"}

# Copy models
def copy_models():
    models = adt_client.list_models(include_model_definition=True)
    for model in models:
        response = requests.post(f"{age_base_url}/models", 
                               json=model.serialize(), 
                               headers=age_headers)
        print(f"Copied model: {model.id}")

# Copy twins
def copy_twins():
    query = "SELECT * FROM digitaltwins"
    twins = adt_client.query_twins(query)
    for twin in twins:
        response = requests.put(f"{age_base_url}/digitaltwins/{twin['$dtId']}", 
                              json=twin, 
                              headers=age_headers)
        print(f"Copied twin: {twin['$dtId']}")

# Copy relationships
def copy_relationships():
    query = "SELECT * FROM relationships"
    relationships = adt_client.query_twins(query)
    for rel in relationships:
        response = requests.put(f"{age_base_url}/digitaltwins/{rel['$sourceId']}/relationships/{rel['$relationshipId']}", 
                              json=rel, 
                              headers=age_headers)
        print(f"Copied relationship: {rel['$relationshipId']}")

if __name__ == "__main__":
    copy_models()
    copy_twins()
    copy_relationships()

How to Run:

  • Install azure-identity and azure-digitaltwins-core via pip
  • Run with python copy_adt.py
copy_adt.cs
// Example migration script for C#
using Azure.Core;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using System.Text.Json;

public class AdtMigration
{
    private readonly DigitalTwinsClient adtClient;
    private readonly HttpClient ageClient;
    
    public AdtMigration()
    {
        var credential = new DefaultAzureCredential();
        adtClient = new DigitalTwinsClient(
            new Uri("https://your-adt-instance.api.eus.digitaltwins.azure.net"), 
            credential);
            
        ageClient = new HttpClient();
        ageClient.BaseAddress = new Uri("https://your-resource-id.api.graph.konnektr.io/");
        ageClient.DefaultRequestHeaders.Add("Authorization", "Bearer your-token");
    }
    
    public async Task CopyModelsAsync()
    {
        var options = new GetModelsOptions { IncludeModelDefinition = true };
        await foreach (var model in adtClient.GetModelsAsync(options))
        {
            var json = JsonSerializer.Serialize(model);
            var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
            await ageClient.PostAsync("models", content);
            Console.WriteLine($"Copied model: {model.Id}");
        }
    }
    
    public async Task CopyTwinsAsync()
    {
        var query = "SELECT * FROM digitaltwins";
        await foreach (var twin in adtClient.QueryAsync<object>(query))
        {
            var json = JsonSerializer.Serialize(twin);
            var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
            await ageClient.PutAsync($"digitaltwins/{((dynamic)twin).Id}", content);
            Console.WriteLine($"Copied twin: {((dynamic)twin).Id}");
        }
    }
    
    public async Task CopyRelationshipsAsync()
    {
        var query = "SELECT * FROM relationships";
        await foreach (var rel in adtClient.QueryAsync<object>(query))
        {
            var sourceId = ((dynamic)rel).sourceId;
            var relationshipId = ((dynamic)rel).relationshipId;
            var json = JsonSerializer.Serialize(rel);
            var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
            await ageClient.PutAsync($"digitaltwins/{sourceId}/relationships/{relationshipId}", content);
            Console.WriteLine($"Copied relationship: {relationshipId}");
        }
    }
}

How to Run:

  • Use .NET 6+, add Azure.DigitalTwins.Core and Azure.Identity NuGet packages
  • Run with dotnet run
copy_adt.js
// Example migration script for JavaScript
const { DigitalTwinsClient } = require('@azure/digital-twins-core');
const { DefaultAzureCredential } = require('@azure/identity');
const axios = require('axios');

// Azure Digital Twins configuration
const adtUrl = 'https://your-adt-instance.api.eus.digitaltwins.azure.net';
const credential = new DefaultAzureCredential();
const adtClient = new DigitalTwinsClient(adtUrl, credential);

// AgeDigitalTwins configuration
const ageBaseUrl = 'https://your-resource-id.api.graph.konnektr.io';
const ageHeaders = { 'Authorization': 'Bearer your-token' };

async function copyModels() {
    const models = adtClient.listModels({ includeModelDefinition: true });
    for await (const model of models) {
        await axios.post(`${ageBaseUrl}/models`, model, { headers: ageHeaders });
        console.log(`Copied model: ${model.id}`);
    }
}

async function copyTwins() {
    const query = 'SELECT * FROM digitaltwins';
    const twins = adtClient.queryTwins(query);
    for await (const twin of twins) {
        await axios.put(`${ageBaseUrl}/digitaltwins/${twin.$dtId}`, twin, 
                       { headers: ageHeaders });
        console.log(`Copied twin: ${twin.$dtId}`);
    }
}

async function copyRelationships() {
    const query = 'SELECT * FROM relationships';
    const relationships = adtClient.queryTwins(query);
    for await (const rel of relationships) {
        await axios.put(`${ageBaseUrl}/digitaltwins/${rel.$sourceId}/relationships/${rel.$relationshipId}`, 
                       rel, { headers: ageHeaders });
        console.log(`Copied relationship: ${rel.$relationshipId}`);
    }
}

async function main() {
    await copyModels();
    await copyTwins();
    await copyRelationships();
}

main().catch(console.error);

How to Run:

  • Install @azure/identity and @azure/digital-twins-core via npm
  • Run with node copy_adt.js

B. Exporting to NDJSON for Import Jobs

For bulk migration, you must export your data to NDJSON format compatible with the Import Jobs API. The NDJSON file must follow a specific structure:

NDJSON Format Structure

Each section is a line-delimited JSON object. The required sections are:

{"Section": "Header"}
{"fileVersion": "1.0.0", "author": "your-name", "organization": "your-org"}
{"Section": "Models"}
<DTDL model JSON, one per line>
{"Section": "Twins"}
<Twin JSON, one per line>
{"Section": "Relationships"}
<Relationship JSON, one per line>

See Azure Digital Twins Bulk Import NDJSON Generator for a .NET sample that generates NDJSON files.

Example NDJSON Generator (.NET)

ndjson_generator.py
import json

def write_ndjson(models, twins, relationships, file_path, header=None):
    with open(file_path, 'w', encoding='utf-8') as f:
        # Header section
        f.write(json.dumps({"Section": "Header"}) + '\n')
        if header is None:
            header_obj = {"fileVersion": "1.0.0", "author": "authorName", "organization": "organization"}
        else:
            header_obj = header
        f.write(json.dumps(header_obj) + '\n')

        # Models section
        f.write(json.dumps({"Section": "Models"}) + '\n')
        for model in models:
            # model should be a dict or already serialized string
            if isinstance(model, str):
                f.write(model.strip().replace('\n', '') + '\n')
            else:
                f.write(json.dumps(model) + '\n')

        # Twins section
        f.write(json.dumps({"Section": "Twins"}) + '\n')
        for twin in twins:
            f.write(json.dumps(twin) + '\n')

        # Relationships section
        f.write(json.dumps({"Section": "Relationships"}) + '\n')
        for rel in relationships:
            f.write(json.dumps(rel) + '\n')

This function writes the NDJSON file in the required format. Pass lists of models, twins, and relationships as Python dicts or JSON strings. Each object is written on a single line.

Authentication for Konnektr Graph

When importing to Konnektr Graph, use Auth0 authentication (see Using Azure Digital Twins SDKs).

Important: Do not use JSON-LD format for import jobs. Only NDJSON with the required headers and sections is supported.


Steps

  1. Run the export script to generate export.jsonld.
  2. Upload the file to your blob storage account.
  3. Use the AgeDigitalTwins import API to import the data (see API Reference).

For more details on the import API and supported formats, see the Reference documentation.

On this page