appscript.dev
Automation Advanced Drive

Build a shared-drive migration helper

Move Northwind files between drives with structure intact — from My Drive to a Shared Drive.

Published Oct 8, 2025

Northwind’s project files have outgrown a single person’s My Drive. The problem with files owned by an individual is ownership: when that person leaves, the files go with them. A Shared Drive fixes that — files belong to the team — but Google offers no one-click “move this folder tree across” button, and dragging hundreds of nested files by hand is a long, error-prone afternoon.

This script does the move for you. Given a source folder and a target parent, it recreates the folder inside the target Shared Drive and walks the whole tree, copying every file and rebuilding every sub-folder in place. The result is a faithful copy of the structure, now owned by the team instead of a person.

What you’ll need

  • The Drive API advanced service enabled. In the Apps Script editor, open Services, add Drive API, and keep the default identifier Drive.
  • The ID of the source folder you want to migrate (from a My Drive or another location).
  • The ID of the target parent — a Shared Drive itself, or a folder inside one.
  • Edit access to both the source and the target so the script can read one and write to the other.

The script

/**
 * Migrates a folder tree into a target parent. Recreates the source
 * folder inside the target, then copies every file and sub-folder.
 *
 * @param {string} sourceFolderId The folder to migrate.
 * @param {string} targetParentId The parent (Shared Drive or folder) to copy into.
 */
function migrateFolder(sourceFolderId, targetParentId) {
  const source = DriveApp.getFolderById(sourceFolderId);

  // 1. Recreate the top-level folder inside the target.
  const newFolder = createInTarget(source.getName(), targetParentId);

  // 2. Walk the tree, copying files and rebuilding sub-folders.
  copyContents(source, newFolder.id);

  Logger.log('Migrated "' + source.getName() + '" into ' + targetParentId);
}

/**
 * Creates a new folder with the given name inside a parent. Works for
 * Shared Drive parents because supportsAllDrives is set.
 *
 * @param {string} name The new folder's name.
 * @param {string} parentId The parent folder or Shared Drive ID.
 * @return {Object} The created folder resource.
 */
function createInTarget(name, parentId) {
  return Drive.Files.create(
    { name, mimeType: MimeType.FOLDER, parents: [parentId] },
    null,
    { supportsAllDrives: true }
  );
}

/**
 * Copies every file from a source folder into a target folder, then
 * recurses into each sub-folder, rebuilding the structure as it goes.
 *
 * @param {Folder} sourceFolder The folder to copy from.
 * @param {string} targetFolderId The folder ID to copy into.
 */
function copyContents(sourceFolder, targetFolderId) {
  // Copy every file at this level into the target folder.
  const files = sourceFolder.getFiles();
  while (files.hasNext()) {
    const f = files.next();
    Drive.Files.copy(
      { name: f.getName(), parents: [targetFolderId] },
      f.getId(),
      { supportsAllDrives: true }
    );
  }

  // For each sub-folder, recreate it in the target and recurse into it.
  const subs = sourceFolder.getFolders();
  while (subs.hasNext()) {
    const sub = subs.next();
    const sub2 = createInTarget(sub.getName(), targetFolderId);
    copyContents(sub, sub2.id);
  }
}

How it works

  1. migrateFolder opens the source folder and calls createInTarget to make a matching folder inside the target parent. This is the new root of the migrated tree.
  2. It then calls copyContents, passing the source folder and the ID of the freshly created target folder.
  3. createInTarget uses Drive.Files.create with supportsAllDrives: true — the flag that lets the call write into a Shared Drive, which the plain DriveApp methods cannot reliably do.
  4. copyContents first copies every file at the current level into the target folder, again with supportsAllDrives: true so copies land inside the Shared Drive.
  5. It then loops over each sub-folder, recreates it in the target with createInTarget, and calls itself recursively. This rebuilds the entire nested structure folder by folder.

Example run

Call migrateFolder with a source folder and a Shared Drive folder as the target:

Source (My Drive)              Target (Shared Drive)
└── Client projects/           └── Client projects/   ← recreated
    ├── proposal.pdf       →       ├── proposal.pdf    ← copied
    ├── budget.xlsx        →       ├── budget.xlsx     ← copied
    └── Designs/           →       └── Designs/        ← recreated
        └── logo.png       →           └── logo.png    ← copied

The structure on the right is a fresh copy: same folders, same files, now owned by the Shared Drive instead of an individual.

Run it

This is a one-off job, run by hand when a tree needs to move:

  1. In the Apps Script editor, write a small wrapper that calls migrateFolder with your two IDs, for example migrateFolder('1srcFolderId', '1targetParentId').
  2. Select that wrapper and click Run.
  3. Approve the authorisation prompt the first time.
  4. Check the target Shared Drive — the folder tree should be there in full.

Watch out for

  • This copies, it does not move. The originals stay in the source, and the copies are owned by whoever ran the script — which, inside a Shared Drive, is the desired outcome. Delete the originals only once you have verified the copy.
  • Copying loses version history, comments, and the original file IDs. Anything linking to a file by its old URL will need re-pointing.
  • A large tree can blow the six-minute Apps Script execution limit. For thousands of files, migrate in batches or drive the recursion from a queue that survives across timed runs.
  • Each Drive.Files.copy and create counts against your daily Drive API quota. A big migration can exhaust it.
  • Shared file permissions are not copied with the file. Re-share migrated files as needed, or rely on the Shared Drive’s own membership.
  • Test on a small folder first. A recursive copy with a wrong target ID can scatter copies into the wrong place quickly.

Related