Device Migration API Postman Collection

Disclaimer: This information is provided as-is for the benefit of the Community. Please contact Sophos Professional Services if you require assistance with your specific environment.


Overview

This sample script gives you an easy start when using Sophos Endpoint API for device migration between Sophos Central accounts.

https://docs.sophos.com/central/Customer/help/en-us/ManageYourProducts/Overview/GlobalSettings/DeviceMigration/index.html

What to do

Save the following as "Sophos Central Device Migration.postman_collection.json"

{
	"info": {
		"_postman_id": "43aeba01-038e-45dc-b4a3-fc44e60b63db",
		"name": "Sophos Central Device Migration",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
	},
	"item": [
		{
			"name": "Step 1 - Authentication",
			"item": [
				{
					"name": "Source: Authenticate",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"var jsonData = JSON.parse(responseBody);",
									"pm.collectionVariables.set(\"SourceAccessToken\", jsonData.access_token);"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [],
						"body": {
							"mode": "urlencoded",
							"urlencoded": [
								{
									"key": "grant_type",
									"value": "client_credentials",
									"type": "text"
								},
								{
									"key": "scope",
									"value": "token",
									"type": "text"
								},
								{
									"key": "client_id",
									"value": "{{SourceClientID}}",
									"type": "text"
								},
								{
									"key": "client_secret",
									"value": "{{SourceClientSecret}}",
									"type": "text"
								}
							]
						},
						"url": {
							"raw": "https://id.sophos.com/api/v2/oauth2/token",
							"protocol": "https",
							"host": [
								"id",
								"sophos",
								"com"
							],
							"path": [
								"api",
								"v2",
								"oauth2",
								"token"
							]
						}
					},
					"response": []
				},
				{
					"name": "Source: Who am I?",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"pm.test(\"Status code is 200\", function () {",
									"    pm.response.to.have.status(200);",
									"    ",
									"    pm.test(\"Save ID and api host\", function () {",
									"        var jsonData = pm.response.json();",
									"",
									"        pm.collectionVariables.set(\"SourceTenantID\", jsonData.id);",
									"        pm.collectionVariables.set(\"SourceDataRegion\", jsonData.apiHosts.dataRegion.slice(12, 16));",
									"    });",
									"});",
									""
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "bearer",
							"bearer": [
								{
									"key": "token",
									"value": "{{SourceAccessToken}}",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "https://api.central.sophos.com/whoami/v1",
							"protocol": "https",
							"host": [
								"api",
								"central",
								"sophos",
								"com"
							],
							"path": [
								"whoami",
								"v1"
							]
						},
						"description": "Returns information about the caller."
					},
					"response": []
				},
				{
					"name": "Target: Authenticate",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"var jsonData = JSON.parse(responseBody);",
									"pm.collectionVariables.set(\"TargetAccessToken\", jsonData.access_token);"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [],
						"body": {
							"mode": "urlencoded",
							"urlencoded": [
								{
									"key": "grant_type",
									"value": "client_credentials",
									"type": "text"
								},
								{
									"key": "scope",
									"value": "token",
									"type": "text"
								},
								{
									"key": "client_id",
									"value": "{{TargetClientID}}",
									"type": "text"
								},
								{
									"key": "client_secret",
									"value": "{{TargetClientSecret}}",
									"type": "text"
								}
							]
						},
						"url": {
							"raw": "https://id.sophos.com/api/v2/oauth2/token",
							"protocol": "https",
							"host": [
								"id",
								"sophos",
								"com"
							],
							"path": [
								"api",
								"v2",
								"oauth2",
								"token"
							]
						}
					},
					"response": []
				},
				{
					"name": "Target: Who am I?",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"pm.test(\"Status code is 200\", function () {",
									"    pm.response.to.have.status(200);",
									"    ",
									"    pm.test(\"Save ID and api host\", function () {",
									"        var jsonData = pm.response.json();",
									"",
									"        pm.collectionVariables.set(\"TargetTenantID\", jsonData.id);",
									"        pm.collectionVariables.set(\"TargetDataRegion\", jsonData.apiHosts.dataRegion.slice(12, 16));",
									"           ",
									"    });",
									"});",
									""
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "bearer",
							"bearer": [
								{
									"key": "token",
									"value": "{{TargetAccessToken}}",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "https://api.central.sophos.com/whoami/v1",
							"protocol": "https",
							"host": [
								"api",
								"central",
								"sophos",
								"com"
							],
							"path": [
								"whoami",
								"v1"
							]
						},
						"description": "Returns information about the caller."
					},
					"response": []
				}
			],
			"auth": {
				"type": "noauth"
			},
			"event": [
				{
					"listen": "prerequest",
					"script": {
						"type": "text/javascript",
						"exec": [
							""
						]
					}
				},
				{
					"listen": "test",
					"script": {
						"type": "text/javascript",
						"exec": [
							""
						]
					}
				}
			]
		},
		{
			"name": "Step 2 - Source: Get List of Endpoints",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"let response = pm.response.json(),\r",
							"    ids = _.map(response.items, ({ id }) => ( id )); \r",
							"\r",
							"pm.collectionVariables.set('EndpointList', JSON.stringify(ids));\r",
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "bearer",
					"bearer": [
						{
							"key": "token",
							"value": "{{SourceAccessToken}}",
							"type": "string"
						}
					]
				},
				"method": "GET",
				"header": [
					{
						"description": "(Required) Tenant ID.",
						"key": "X-Tenant-ID",
						"value": "{{SourceTenantID}}"
					}
				],
				"url": {
					"raw": "https://api-{{SourceDataRegion}}.central.sophos.com/endpoint/v1/endpoints?fields=id,hostname",
					"protocol": "https",
					"host": [
						"api-{{SourceDataRegion}}",
						"central",
						"sophos",
						"com"
					],
					"path": [
						"endpoint",
						"v1",
						"endpoints"
					],
					"query": [
						{
							"key": "fields",
							"value": "id,hostname"
						},
						{
							"key": "hostnameContains",
							"value": "WIN",
							"disabled": true
						}
					]
				},
				"description": "Get all the endpoints for the specified tenant."
			},
			"response": []
		},
		{
			"name": "Step 3 - Target: Start Receiver Task",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"var jsonData = pm.response.json();\r",
							"\r",
							"pm.collectionVariables.set(\"MigrationID\", jsonData.id);\r",
							"pm.collectionVariables.set(\"MigrationToken\", jsonData.token);"
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "bearer",
					"bearer": [
						{
							"key": "token",
							"value": "{{TargetAccessToken}}",
							"type": "string"
						}
					]
				},
				"method": "POST",
				"header": [
					{
						"description": "(Required) Tenant ID.",
						"key": "X-Tenant-ID",
						"value": "{{TargetTenantID}}"
					},
					{
						"key": "Content-Type",
						"value": "application/json"
					}
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"fromTenant\": \"{{SourceTenantID}}\",\n    \"endpoints\": {{EndpointList}}\n}"
				},
				"url": {
					"raw": "https://api-{{TargetDataRegion}}.central.sophos.com/endpoint/v1/migrations",
					"protocol": "https",
					"host": [
						"api-{{TargetDataRegion}}",
						"central",
						"sophos",
						"com"
					],
					"path": [
						"endpoint",
						"v1",
						"migrations"
					]
				},
				"description": "Adds a new local site."
			},
			"response": []
		},
		{
			"name": "Step 4 - Source: Start Migration Job",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "bearer",
					"bearer": [
						{
							"key": "token",
							"value": "{{SourceAccessToken}}",
							"type": "string"
						}
					]
				},
				"method": "PUT",
				"header": [
					{
						"description": "(Required) Tenant ID.",
						"key": "X-Tenant-ID",
						"value": "{{SourceTenantID}}"
					},
					{
						"key": "Content-Type",
						"value": "application/json"
					}
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"token\": \"{{MigrationToken}}\",\n    \"endpoints\": {{EndpointList}}\n}\n"
				},
				"url": {
					"raw": "https://api-{{SourceDataRegion}}.central.sophos.com/endpoint/v1/migrations/{{MigrationID}}",
					"protocol": "https",
					"host": [
						"api-{{SourceDataRegion}}",
						"central",
						"sophos",
						"com"
					],
					"path": [
						"endpoint",
						"v1",
						"migrations",
						"{{MigrationID}}"
					]
				},
				"description": "Adds a new local site."
			},
			"response": []
		},
		{
			"name": "Step 5 - Target: Check Migration Status",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "bearer",
					"bearer": [
						{
							"key": "token",
							"value": "{{TargetAccessToken}}",
							"type": "string"
						}
					]
				},
				"method": "GET",
				"header": [
					{
						"key": "X-Tenant-ID",
						"value": "{{TargetTenantID}}",
						"type": "default"
					}
				],
				"url": {
					"raw": "https://api-{{TargetDataRegion}}.central.sophos.com/endpoint/v1/migrations/{{MigrationID}}/endpoints",
					"protocol": "https",
					"host": [
						"api-{{TargetDataRegion}}",
						"central",
						"sophos",
						"com"
					],
					"path": [
						"endpoint",
						"v1",
						"migrations",
						"{{MigrationID}}",
						"endpoints"
					]
				},
				"description": "Returns information about the caller."
			},
			"response": []
		}
	],
	"event": [
		{
			"listen": "prerequest",
			"script": {
				"type": "text/javascript",
				"exec": [
					""
				]
			}
		},
		{
			"listen": "test",
			"script": {
				"type": "text/javascript",
				"exec": [
					""
				]
			}
		}
	],
	"variable": [
		{
			"key": "SourceClientID",
			"value": "",
			"type": "default"
		},
		{
			"key": "SourceClientSecret",
			"value": "",
			"type": "default"
		},
		{
			"key": "TargetClientID",
			"value": "",
			"type": "default"
		},
		{
			"key": "TargetClientSecret",
			"value": "",
			"type": "default"
		}
	]
}

Open the file in Postman and replace any necessary fields.



Updated link
[edited by: Qoosh at 4:25 PM (GMT -7) on 11 Oct 2024]
  • No, that should not be the issue. Possible reasons I can think of are:

    • your authentication token for the source is expired, authentication tokens are valid for 60 minutes
    • you are using API credentials from the Enterprise Dashboard, with the Postman collection you need to use credentials created within the tenant 
    • "Allow device migration" is not active in the source account

    For more details see: https://developer.sophos.com/endpoint-migrations.

  • Hi, thanks for ideas.

    1 - This was all done in the space of 10 minutes within postman, so the tokens should be good

    2 - I was using enterprise dashboard credentials before. However following the postman flow and using separate tenant credentials for the source and destination tenants did not help

    3 - Device migration is enabled indefinately for all tenants

    Any other ideas?

  • Unfortunately no. I just used the Postman collection in my own test environment and successfully migrated a device from one tenant to the other.

    I only get a forbidden, at Step 4 when Device Migration is not enabled in the Source account. But in that case it will also show you the reason why its not allowed, see example below:

    {
        "error": "forbidden",
        "correlationId": "a4535e0f-2d35-46c7-b5b7-0debaab25160",
        "requestId": "6ee51443-1de3-4933-92df-2ebfdfd21001",
        "message": "Endpoint Migration is not enabled"
    }


    If it is only a small number of devices that you have to migrate then a manual migration might be an option, you could download the SophosSetup installer from the target account and run that one on the devices with the paremeter --registeronly. Just make sure that you deactivate Tamper Protection before doing so.

    If it is concerning a larger number of devices then I would recommend opening a support case.

  • Ah! I am getting the following:

    "error": "forbidden",
    "correlationId": "894035df-198e-40b4-ac54-14f01d0dbee3",
    "requestId": "d01d51f3-6c94-4ce0-8dd2-7e0fd79a8945",
    "message": "Access Denied"

    The issue we have with reinstalling with registeronly is that I don't believe that option is available on the Mac. Also you have the second step of going back to the original tenant and deleting the original instance, after it is recreated in the new tenant.

  • I have to guess here, "Access Denied" could mean that you are not using the correct credentials/permissions (for a migration you need Service Principal Super Admin) or maybe it is a licensing based issue. 

    I strongly recommend opening a support case, I have no means to analyze the error you are seeing.

  • Thank you!! Creating keys with Service Principal Super Admin was the solution. Before they were just for the management role.

  • So I got the migration to work, but the device still also shows in the original tenant, although under events it does say that it has been moved to a new account. How long until it disappears from the original tenant? Or do I have to do something?

  • Migrated devices do not disappear automatically, you either have to delete the device manually, delete it using the API or you could use the functionality for removing inactive devices (see General Settings > Removal of Inactive Devices).

  • Ok thanks. Maybe this can be a future improvement, to automatically remove the migrated device from the source tenant, either automatically or by passing an option in the creation of the migration job.