Skip to content

Instantly share code, notes, and snippets.

@JakubAndrysek
Last active March 31, 2026 20:42
Show Gist options
  • Select an option

  • Save JakubAndrysek/d3c16b85bf34eaca18d33df471104dd5 to your computer and use it in GitHub Desktop.

Select an option

Save JakubAndrysek/d3c16b85bf34eaca18d33df471104dd5 to your computer and use it in GitHub Desktop.
Copying files and folders from a personal Google Drive to a Shared Google Drive (aka Team Drives) using Google Apps Script

Copying files and folders from a personal Google Drive to a Shared Google Drive (aka Team Drives) using Google Apps Script

  1. Copy app script
  2. In setting enable Show appscript.json ...
  3. Add "oauthScopes": ["https://www.googleapis.com/auth/drive"] on the end of the appscript.json file.
  4. Edit sourceFolderId and targetSharedDriveId based on your Google Drive web app URL links
    • https://drive.google.com/drive/folders/dfsfsdt453rfds45341243-53425 -> dfsfsdt453rfds45341243-53425

References

{
"timeZone": "Europe/London",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/script.scriptapp"
]
}
/**
* Copying files and folders from a personal Google Drive to a Shared Google Drive (aka Team Drives) using Google Apps Script
* Author: Jakub AndrΓ½sek + https://stackoverflow.com/users/8199076/azmol
* Website: https://kubaandrysek.cz
* Email: email@kubaandrysek.cz
* GitHub: https://github.com/JakubAndrysek
* License: MIT
* File: https://gist.github.com/JakubAndrysek/d3c16b85bf34eaca18d33df471104dd5
*/
function copyDriveStructure() {
const sourceFolderId = 'gdfgsdfsads'; // update ID
const targetSharedDriveId = 'dsfgdsfsfsd'; // update ID
const sourceFolder = DriveApp.getFolderById(sourceFolderId);
const targetDrive = DriveApp.getFolderById(targetSharedDriveId);
Logger.log(`πŸ“ ROOT: ${sourceFolder.getName()}`);
// Set to false to actually copy. Set to true to dry-run/print.
copyFolder(sourceFolder, targetDrive, false, "");
}
function copyFolder(sourceFolder, targetFolder, onlyPrintFiles = true, prefix = "") {
// 1. Copy/Print files in the CURRENT directory
copyAllFilesInFolder(sourceFolder, targetFolder, onlyPrintFiles, prefix);
// 2. Iterate through subfolders
const sourceSubfolders = sourceFolder.getFolders();
while (sourceSubfolders.hasNext()) {
const sourceSubfolder = sourceSubfolders.next();
Logger.log(`${prefix}β”œβ”€β”€ πŸ“ ${sourceSubfolder.getName()}`);
let nextTargetFolder = targetFolder;
if (!onlyPrintFiles) {
try {
nextTargetFolder = targetFolder.createFolder(sourceSubfolder.getName());
} catch (error) {
Logger.log(`${prefix}β”‚ [ERROR creating folder]: ${error.message}`);
continue; // Skip this folder and move to the next one so the script doesn't crash
}
}
// 3. Recursive call for subfolders
copyFolder(sourceSubfolder, nextTargetFolder, onlyPrintFiles, prefix + "β”‚ ");
}
}
function copyAllFilesInFolder(sourceFolder, targetFolder, onlyPrintFiles = true, prefix = "") {
const sourceFiles = sourceFolder.getFiles();
while (sourceFiles.hasNext()) {
const sourceFile = sourceFiles.next();
Logger.log(`${prefix}β”œβ”€β”€ πŸ“„ ${sourceFile.getName()}`);
if (!onlyPrintFiles) {
try {
sourceFile.makeCopy(sourceFile.getName(), targetFolder);
} catch (error) {
Logger.log(`${prefix}β”œβ”€β”€ [ERROR copying file]: ${sourceFile.getName()} - ${error.message}`);
// Fails safely and continues to the next file
}
}
}
}
/**
* Resumable Google Drive Copier (Bypasses the 6-minute limit)
*/
function startOrResumeCopy() {
const sourceFolderId = 'fdsfdsfsdfs'; // update ID
const targetSharedDriveId = 'fdsfsfsdfsd'; // update ID
const userProps = PropertiesService.getUserProperties();
let queue = [];
// 1. Check if we have a saved queue from a previous timeout
const savedQueue = userProps.getProperty('COPY_QUEUE');
if (savedQueue) {
queue = JSON.parse(savedQueue);
Logger.log("πŸ”„ Waking up! Resuming from saved queue. Folders left in queue: " + queue.length);
} else {
// Start fresh
queue.push({ source: sourceFolderId, target: targetSharedDriveId, path: "ROOT" });
Logger.log("πŸš€ Starting a fresh copy...");
}
const startTime = Date.now();
const MAX_RUN_TIME = 4.5 * 60 * 1000; // 4.5 minutes (leaves a safe buffer)
// 2. Process the Queue
while (queue.length > 0) {
// Check the clock before processing the next folder
if (Date.now() - startTime > MAX_RUN_TIME) {
Logger.log("⚠️ Approaching 5 minutes. Saving state and scheduling continuation...");
userProps.setProperty('COPY_QUEUE', JSON.stringify(queue));
setupTrigger();
return; // Exit safely before Google kills the script
}
const currentTask = queue.shift(); // Get the first item in the list
Logger.log(`πŸ“ Processing: ${currentTask.path}`);
try {
const sourceFolder = DriveApp.getFolderById(currentTask.source);
const targetFolder = DriveApp.getFolderById(currentTask.target);
// A. Copy Files in this folder
const files = sourceFolder.getFiles();
while (files.hasNext()) {
const file = files.next();
try {
file.makeCopy(file.getName(), targetFolder);
Logger.log(` β”œβ”€β”€ πŸ“„ Copied: ${file.getName()}`);
} catch (e) {
Logger.log(` β”œβ”€β”€ ❌ [ERROR] File copy failed: ${file.getName()} - ${e.message}`);
}
}
// B. Find Subfolders and add them to the queue for later
const subfolders = sourceFolder.getFolders();
while (subfolders.hasNext()) {
const subfolder = subfolders.next();
try {
const newTargetFolder = targetFolder.createFolder(subfolder.getName());
queue.push({
source: subfolder.getId(),
target: newTargetFolder.getId(),
path: currentTask.path + "/" + subfolder.getName()
});
} catch (e) {
Logger.log(` β”œβ”€β”€ ❌ [ERROR] Folder creation failed: ${subfolder.getName()} - ${e.message}`);
}
}
} catch (e) {
Logger.log(`❌ [ERROR] Could not access folder in path: ${currentTask.path}`);
}
}
// 3. If the loop finishes naturally, the queue is empty. We are completely done!
Logger.log("βœ… COPY COMPLETE! Cleaning up triggers and memory.");
userProps.deleteProperty('COPY_QUEUE');
deleteTrigger();
}
/**
* Sets a time-driven trigger to run the script again in 1 minute.
*/
function setupTrigger() {
deleteTrigger(); // Ensure no duplicate triggers exist
ScriptApp.newTrigger('startOrResumeCopy')
.timeBased()
.after(1 * 60 * 1000) // 1 minute from now
.create();
}
/**
* Deletes any existing triggers so they don't run endlessly.
*/
function deleteTrigger() {
const triggers = ScriptApp.getProjectTriggers();
for (const trigger of triggers) {
if (trigger.getHandlerFunction() === 'startOrResumeCopy') {
ScriptApp.deleteTrigger(trigger);
}
}
}
/**
* Utility function to clear the queue and delete triggers manually if needed.
*/
function resetCopyQueue() {
PropertiesService.getUserProperties().deleteProperty('COPY_QUEUE');
deleteTrigger();
Logger.log("🧹 Queue cleared and triggers deleted.");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment