Google Driveのフォルダを一括作成する前に確認|GASのdry-run付き安全テンプレート
Gasおじ|GoogleアカウントをAIエージェント化
案件ごとに「01_受領」「02_作業中」「03_納品」のような同じフォルダを作る作業は、数が増えるほど入力ミスや作成場所の間違いが起きやすくなります。
この記事では、スプレッドシートに作成予定の階層を書き、最初はdry-run(作成せず確認だけ)で検査する無料範囲のGASを紹介します。重複、空欄、深すぎる階層を先に止め、実行結果はログへ残します。
このコードでできること・しないこと
- `FolderPlan` シートに書いた相対パスを検査する
- dry-runではGoogle Driveへフォルダを作らず、作成予定だけを記録する
- 明示的に `DRY_RUN` を `false` にした時だけフォルダを作る
- 同名フォルダが既に1つある場合は再利用し、二重作成を避ける
- 作成・再利用・停止理由を `FolderCreateLog` シートへ残す
- ファイルや既存フォルダの削除、移動、名前変更、共有設定変更は行わない
- 時間主導トリガーは作らず、手動実行だけを前提にする
Drive内に同名フォルダが複数見つかった場合は、どれを使うべきか自動判断せず停止します。
準備するシート
スプレッドシートに `FolderPlan` シートを作り、1行目を次の2列にします。
- A列: `relative_path`
- B列: `enabled`
入力例は次の通りです。
- `2026_秋キャンペーン/01_受領`、TRUE
- `2026_秋キャンペーン/02_作業中`、TRUE
- `2026_秋キャンペーン/03_納品`、TRUE
- `共通資料/参考`、FALSE
スラッシュは階層の区切りとして扱います。1回の実行は最大50行、深さは最大4階層に制限しています。
コピーして使える最小コード
const FOLDER_PLAN_CONFIG = {
ROOT_FOLDER_ID: 'ここに作成先フォルダIDを入れる',
PLAN_SHEET: 'FolderPlan',
LOG_SHEET: 'FolderCreateLog',
DRY_RUN: true,
MAX_ROWS: 50,
MAX_DEPTH: 4
};
function runFolderPlanFree() {
const config = FOLDER_PLAN_CONFIG;
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const planSheet = spreadsheet.getSheetByName(config.PLAN_SHEET);
if (!planSheet) {
throw new Error('FolderPlan シートがありません。');
}
const plans = readAndValidateFolderPlans_(planSheet, config);
const rootFolder = getRootFolderSafely_(config.ROOT_FOLDER_ID);
const logRows = [];
plans.forEach(function(plan) {
try {
if (config.DRY_RUN) {
logRows.push([new Date(), 'DRY_RUN', plan.path, '作成予定。Driveは変更していません。']);
return;
}
const result = ensureFolderPath_(rootFolder, plan.parts);
logRows.push([new Date(), result.status, plan.path, result.message]);
} catch (error) {
logRows.push([new Date(), 'HOLD', plan.path, String(error.message || error)]);
}
});
appendFolderLogs_(spreadsheet, config.LOG_SHEET, logRows);
const holdCount = logRows.filter(function(row) { return row[1] === 'HOLD'; }).length;
const summary = 'mode=' + (config.DRY_RUN ? 'DRY_RUN' : 'CREATE')
+ ', plans=' + plans.length
+ ', hold=' + holdCount;
console.log(summary);
spreadsheet.toast(summary, 'フォルダ計画', 8);
return summary;
}
function readAndValidateFolderPlans_(sheet, config) {
const lastRow = sheet.getLastRow();
if (lastRow < 2) {
throw new Error('作成予定がありません。');
}
const rowCount = lastRow - 1;
if (rowCount > config.MAX_ROWS) {
throw new Error('一度に処理できるのは' + config.MAX_ROWS + '行までです。');
}
const values = sheet.getRange(2, 1, rowCount, 2).getValues();
const seen = {};
const plans = [];
values.forEach(function(row, index) {
const enabledText = String(row[1]).trim().toUpperCase();
if (![true, false].includes(row[1]) && enabledText !== 'TRUE' && enabledText !== 'FALSE') {
throw new Error((index + 2) + '行目: enabled はTRUEまたはFALSEで指定してください。');
}
const enabled = row[1] === true || enabledText === 'TRUE';
if (!enabled) return;
const path = String(row[0] || '').trim();
if (!path) {
throw new Error((index + 2) + '行目: relative_path が空です。');
}
const parts = path.split('/').map(function(name) { return name.trim(); });
if (parts.length > config.MAX_DEPTH) {
throw new Error((index + 2) + '行目: 最大' + config.MAX_DEPTH + '階層までです。');
}
parts.forEach(function(name) {
if (!name || name === '.' || name === '..') {
throw new Error((index + 2) + '行目: 空または相対指定の階層名は使えません。');
}
if (name.length > 100 || /[\u0000-\u001f]/.test(name)) {
throw new Error((index + 2) + '行目: 階層名が長すぎるか制御文字を含みます。');
}
});
const normalized = parts.join('/');
if (seen[normalized]) {
throw new Error((index + 2) + '行目: 同じパスが重複しています。');
}
seen[normalized] = true;
plans.push({ path: normalized, parts: parts });
});
if (!plans.length) {
throw new Error('enabled がTRUEの作成予定がありません。');
}
return plans;
}
function getRootFolderSafely_(folderId) {
const id = String(folderId || '').trim();
if (!id || id === 'ここに作成先フォルダIDを入れる') {
throw new Error('ROOT_FOLDER_IDを設定してください。');
}
try {
return DriveApp.getFolderById(id);
} catch (error) {
throw new Error('作成先フォルダを確認できません。IDと権限を確認してください。');
}
}
function ensureFolderPath_(rootFolder, parts) {
let current = rootFolder;
const actions = [];
parts.forEach(function(name) {
const matches = current.getFoldersByName(name);
if (!matches.hasNext()) {
current = current.createFolder(name);
actions.push('作成:' + name);
return;
}
const found = matches.next();
if (matches.hasNext()) {
throw new Error('同名フォルダが複数あります: ' + name);
}
current = found;
actions.push('再利用:' + name);
});
return {
status: actions.some(function(action) { return action.indexOf('作成:') === 0; }) ? 'CREATED' : 'EXISTS',
message: actions.join(' / ')
};
}
function appendFolderLogs_(spreadsheet, sheetName, rows) {
let logSheet = spreadsheet.getSheetByName(sheetName);
if (!logSheet) {
logSheet = spreadsheet.insertSheet(sheetName);
logSheet.appendRow(['executed_at', 'status', 'relative_path', 'message']);
}
if (rows.length) {
logSheet.getRange(logSheet.getLastRow() + 1, 1, rows.length, 4).setValues(rows);
}
}実行手順
1. 対象スプレッドシートの「拡張機能」からApps Scriptを開き、コードを貼り付けます。
2. `ROOT_FOLDER_ID` に作成先フォルダのIDを入れます。IDはDriveでフォルダを開いた時のURL末尾にある文字列です。
3. `DRY_RUN: true` のまま `runFolderPlanFree` を手動実行します。
4. 初回の権限確認を読み、対象フォルダへアクセスしてよい場合だけ許可します。
5. `FolderCreateLog` の全行が `DRY_RUN` で、パスと件数が意図通りか確認します。
6. 問題がなければ `DRY_RUN: false` に変更し、もう一度だけ手動実行します。
7. ログの `CREATED`、`EXISTS`、`HOLD` を確認し、終わったら `DRY_RUN: true` に戻します。
少量のテスト用フォルダで確認してから実データへ適用してください。
失敗しやすい点と停止条件
- フォルダIDが違う、または閲覧・作成権限がない場合は開始前に停止します。
- TRUEの行が50件を超える場合は、分割して確認してください。
- 同じ相対パスが2行ある場合は、Driveへ触る前に停止します。
- 同じ親の中に同名フォルダが複数ある場合は、自動選択せずそのパスを `HOLD` にします。
- 作成モードで途中停止した場合、それまでに新規作成した親フォルダは残ります。ログを確認し、同じ計画をdry-runへ戻してから再確認してください。
- 実行時間やDriveの利用上限に達する場合があります。最初から大量の階層へ広げないでください。
- 共有ドライブや組織アカウントでは、管理者設定によって作成できない場合があります。
この例は既存フォルダを再利用しますが、削除・移動・名前変更・共有設定変更は行いません。作成後の整理や権限設計は人が確認してください。
公開前チェックリスト
- [ ] `DRY_RUN` が `true` になっている
- [ ] 作成先フォルダIDを別の人にも確認してもらった
- [ ] `FolderPlan` に空欄、重複、不要なTRUEがない
- [ ] dry-runログの件数と階層が想定通り
- [ ] 個人情報や機密のフォルダ名をログへ残してよいか確認した
- [ ] 初回は少量のテスト用フォルダで実行する
まとめ
フォルダの一括作成は、コードを動かすことより「どこに何を作るか」を先に確認できることが重要です。計画表、dry-run、入力検証、ログの4点をそろえると、作成場所や階層名の間違いに気づきやすくなります。
複数フォルダやサブフォルダにある既存ファイルを一覧化し、更新日や種類で確認したい場合は、次のDriveFileList Proが関連します。
DriveFileList Pro|Google Driveのファイル一覧化GASテンプレ

GasおじのTips商品一覧も確認できます。
GasおじのTips商品一覧

※この記事は学習・検証用の例です。Google公式・Google公認の商品ではありません。Google Apps ScriptとDriveには権限設定、実行時間、各種利用上限があります。実データへ適用する前に、コピー環境と少量データで確認してください。
