社内の「ちょっと面倒」をGASで解決!おすすめ自動化シナリオ集

INDEX

3. 自動化シナリオ集(おすすめ7選)

3-1. スプレッドシート日次レポートの自動送信

日々のデータを集計したレポートを毎朝関係者に送信する作業は、典型的な自動化の好例です。

毎朝定時にメールで送る

function sendDailyReport() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("日次レポート");
  const dataRange = sheet.getDataRange();
  const data = dataRange.getValues();

  // テキスト形式の本文
  let body = "おはようございます。\n\n";
  body += "本日の日次レポートをお送りします。\n\n";

  // HTML形式の本文
  let htmlBody = "<html><body>";
  htmlBody += "<p>おはようございます。</p>";
  htmlBody += "<p>本日の日次レポートをお送りします。</p>";
  htmlBody += "<table border='1'>";

  for (let i = 0; i < data.length; i++) {
    htmlBody += "<tr>";
    for (let j = 0; j < data[i].length; j++) {
      if (i === 0) {
        htmlBody += "<th>" + data[i][j] + "</th>";  // ヘッダー行
      } else {
        htmlBody += "<td>" + data[i][j] + "</td>";
      }
    }
    htmlBody += "</tr>";
  }

  htmlBody += "</table></body></html>";

  // メール送信
  MailApp.sendEmail({
    to: "team@example.com",
    subject: "日次レポート " + formatDate(new Date()),
    body: body,
    htmlBody: htmlBody
  });
}

// 日付を「yyyy年MM月dd日」形式にフォーマットする関数
function formatDate(date) {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  return year + "年" + month + "月" + day + "日";
}

このスクリプトを毎朝7時に実行するトリガーを設定すれば、出社前に関係者全員がレポートを受け取ることができます。

件名・本文に日付を入れる方法

上記のコードでは、formatDate(new Date()) を使って現在の日付を「yyyy年MM月dd日」形式で取得し、メールの件名に挿入しています。日付のフォーマットは必要に応じてカスタマイズできます。

3-2. フォーム回答をスプレッドシートに整理&通知

Googleフォームで回答を受け付けた際、その内容を整理して関係者に通知する仕組みを作りましょう。

Googleフォーム→整形→Slack通知 or メール送信

function onFormSubmit(e) {
  // フォーム送信時に自動実行される関数
  const responses = e.namedValues; // 回答内容を取得

  // 回答内容を整形
  let message = "新しいフォーム回答がありました\n\n";
  for (let item in responses) {
    message += item + ": " + responses[item] + "\n";
  }

  // メールで通知
  MailApp.sendEmail({
    to: "manager@example.com",
    subject: "【新規回答】お問い合わせフォーム",
    body: message
  });

  // 代替:Slack通知(Incoming Webhookを使用)
  /*
  const slackWebhookUrl = "https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ";
  const payload = { "text": message };
  const options = {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify(payload)
  };
  UrlFetchApp.fetch(slackWebhookUrl, options);
  */
}

このスクリプトはフォーム送信時のトリガーで動作させます。フォームに連携されたスプレッドシートで、「フォーム」→「スクリプトエディタ」を開き、上記のコードを入力します。そして、「編集」→「現在のプロジェクトのトリガー」からフォーム送信時にこの関数が実行されるよう設定します。

特定条件(例:緊急対応)でフラグ立て

function onFormSubmit(e) {
  const responses = e.namedValues;
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const lastRow = sheet.getLastRow();

  // 「緊急度」という質問項目があると仮定
  if (responses["緊急度"] && responses["緊急度"].toString().includes("高")) {
    // 緊急案件には背景色を赤くする
    sheet.getRange(lastRow, 1, 1, sheet.getLastColumn()).setBackground("#ffcccc");

    // 件名・内容が未定義でもエラーにならないようにフォールバック対応
    const subject = responses["件名"] ? responses["件名"].toString() : "(件名なし)";
    const content = responses["内容"] ? responses["内容"].toString() : "(内容なし)";

    // 緊急案件は関係者全員に通知
    MailApp.sendEmail({
      to: "emergency-team@example.com",
      subject: "【緊急】新規問い合わせあり",
      body:
        "緊急対応が必要な問い合わせが入りました。\n\n" +
        "件名: " + subject + "\n" +
        "内容: " + content + "\n" +
        "URL: " + SpreadsheetApp.getActiveSpreadsheet().getUrl()
    });
  }
}

3-3. スプレッドシートの重複データを自動でハイライト

データ入力時に重複をチェックし、視覚的に警告を出す仕組みを作ります。

毎日のメンテ不要に

function checkDuplicates() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const dataRange = sheet.getDataRange();
  const values = dataRange.getValues();

  // チェック対象の列(例:メールアドレスが入力されている2列目)
  const columnToCheck = 1; // 0始まりなので、B列は1

  // 重複チェック用の辞書
  const seen = {};
  const duplicateRows = [];

  // ヘッダー行をスキップ(1行目がヘッダーの場合)
  for (let i = 1; i < values.length; i++) {
    const value = values[i][columnToCheck];

    // 空のセルはスキップ
    if (!value) continue;

    if (seen[value]) {
      // 重複を発見
      duplicateRows.push(i + 1); // スプレッドシートの行番号は1始まり
    } else {
      seen[value] = true;
    }
  }

  // いったん書式をクリア
  sheet.getRange(2, 1, values.length - 1, values[0].length).setBackground(null);

  // 重複行をハイライト
  duplicateRows.forEach((rowNum) => {
    sheet.getRange(rowNum, 1, 1, values[0].length).setBackground("#ff9900");
  });
}

条件付き書式と連携してわかりやすく

GASと条件付き書式を組み合わせることで、より視覚的にわかりやすくすることができます。例えば、上記のスクリプトで重複データに特定のフラグ(例:「DUPLICATE」など)を別のセルに書き込み、そのセルに条件付き書式を適用する方法があります。

function checkDuplicatesWithFlag() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const dataRange = sheet.getDataRange();
  const values = dataRange.getValues();

  const columnToCheck = 1; // B列
  const flagColumn = 10;   // K列

  const seen = {}; // 重複チェック用

  // ヘッダー行をスキップ
  for (let i = 1; i < values.length; i++) {
    const value = values[i][columnToCheck];

    if (!value) {
      // 値が空ならフラグをクリア
      sheet.getRange(i + 1, flagColumn + 1).setValue("");
      continue;
    }

    if (seen[value]) {
      // 重複を発見したらフラグをセット
      sheet.getRange(i + 1, flagColumn + 1).setValue("DUPLICATE");
    } else {
      // 初出ならフラグをクリアして記録
      seen[value] = true;
      sheet.getRange(i + 1, flagColumn + 1).setValue("");
    }
  }
}

そして、K列に「DUPLICATE」と書かれているセルに対して条件付き書式を設定します。これにより、スクリプトが自動実行されるたびに、視覚的な警告が更新されます。

3-4. 勤怠チェックの抜け漏れアラート

勤怠管理表で、入力忘れをチェックして通知する仕組みを作りましょう。

出勤・退勤時間の未入力をチェック

function checkAttendanceRecords() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("勤怠管理");
  const lastRow = sheet.getLastRow();

  // 今日と昨日の日付を取得(時刻リセット)
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const yesterday = new Date(today);
  yesterday.setDate(yesterday.getDate() - 1);

  // 列インデックス
  const dateColumn = 0;      // A列(日付)
  const nameColumn = 1;      // B列(名前)
  const startTimeColumn = 2; // C列(出勤時間)
  const endTimeColumn = 3;   // D列(退勤時間)

  const missingRecords = [];

  // データチェック(ヘッダーを除く)
  for (let i = 1; i < lastRow; i++) {
    const rowValues = sheet.getRange(i + 1, 1, 1, 5).getValues()[0];
    const recordDate = rowValues[dateColumn];

    if (!recordDate) continue;

    recordDate.setHours(0, 0, 0, 0);
    if (recordDate.getTime() === yesterday.getTime()) {
      const name = rowValues[nameColumn];
      const startTime = rowValues[startTimeColumn];
      const endTime = rowValues[endTimeColumn];

      if (!startTime || !endTime) {
        missingRecords.push({
          name: name,
          missing: !startTime ? "出勤時間" : "退勤時間"
        });
      }
    }
  }

  // 未入力がある場合の通知処理
  if (missingRecords.length > 0) {
    let message = "昨日の勤怠記録で以下の未入力があります:\n\n";

    missingRecords.forEach(record => {
      message += `${record.name}さん: ${record.missing}が未入力です\n`;
    });

    message += "\n至急入力してください。";

    // 各本人へ通知
    missingRecords.forEach(record => {
      const email = record.name.replace(/\s/g, ".") + "@example.com";
      MailApp.sendEmail({
        to: email,
        subject: "【要対応】勤怠記録の未入力について",
        body: `${record.name}さん\n\n昨日の${record.missing}が未入力です。至急入力してください。`
      });
    });

    // 管理者へ通知
    MailApp.sendEmail({
      to: "hr@example.com",
      subject: "【勤怠管理】未入力レコードのお知らせ",
      body: message
    });
  }
}

指定時間に該当者へ一括通知

このスクリプトを毎日朝10時に実行するトリガーを設定すれば、前日の勤怠入力忘れをチェックして、該当者と管理者に通知できます。

3-5. Googleカレンダーから週次予定をまとめて送る

週の始まりに今週の予定をまとめてチーム全員に共有する仕組みを作りましょう。

月曜朝に今週の予定一覧をメール送信

function sendWeeklySchedule() {
  // 今日から1週間の範囲を取得
  const today = new Date();
  const oneWeekLater = new Date(today);
  oneWeekLater.setDate(oneWeekLater.getDate() + 7);

  // カレンダーから予定を取得
  const calendar = CalendarApp.getDefaultCalendar(); // 自分のデフォルトカレンダー
  const events = calendar.getEvents(today, oneWeekLater);

  // HTML形式でメール本文を作成
  let htmlBody = "<html><body>";
  htmlBody += "<h2>今週の予定</h2>";
  htmlBody += "<table border='1' style='border-collapse: collapse;'>";
  htmlBody += "<tr><th>日付</th><th>時間</th><th>タイトル</th><th>場所</th><th>説明</th></tr>";

  // 日付ごとに予定を整理
  const eventsByDate = {};
  events.forEach(event => {
    const startTime = event.getStartTime();
    const dateKey = Utilities.formatDate(startTime, Session.getScriptTimeZone(), "yyyy-MM-dd");

    if (!eventsByDate[dateKey]) {
      eventsByDate[dateKey] = [];
    }
    eventsByDate[dateKey].push(event);
  });

  // 日付順に並べて表に追加
  Object.keys(eventsByDate).sort().forEach(dateKey => {
    const dateEvents = eventsByDate[dateKey];
    const displayDate = Utilities.formatDate(new Date(dateKey), Session.getScriptTimeZone(), "M月d日(E)");

    dateEvents.forEach(event => {
      const startTime = event.getStartTime();
      const endTime = event.getEndTime();
      const timeStr = Utilities.formatDate(startTime, Session.getScriptTimeZone(), "HH:mm") + " - " +
                      Utilities.formatDate(endTime, Session.getScriptTimeZone(), "HH:mm");

      htmlBody += "<tr>";
      htmlBody += "<td>" + displayDate + "</td>";
      htmlBody += "<td>" + timeStr + "</td>";
      htmlBody += "<td>" + event.getTitle() + "</td>";
      htmlBody += "<td>" + (event.getLocation() || "") + "</td>";
      htmlBody += "<td>" + (event.getDescription() || "").replace(/\n/g, "<br>") + "</td>";
      htmlBody += "</tr>";
    });
  });

  htmlBody += "</table>";
  htmlBody += "</body></html>";

  // メール送信
  MailApp.sendEmail({
    to: "team@example.com",
    subject: "今週の予定 " + Utilities.formatDate(today, Session.getScriptTimeZone(), "yyyy/MM/dd") + "~",
    htmlBody: htmlBody
  });
}

会議タイトル・時間・URLを自動整形

オンライン会議のURLを自動で抽出するには、説明文からURLを検出する処理を追加します。

// 説明文からURLを抽出する関数
function extractUrl(text) {
  if (!text) return "";

  const urlRegex = /(https?:\/\/[^\s]+)/g;
  const matches = text.match(urlRegex);

  if (matches && matches.length > 0) {
    const safeUrl = HtmlService.createHtmlOutput(matches[0]).getContent(); // 安全にエスケープ
    return `<a href="${safeUrl}" target="_blank">会議URL</a>`;
  }

  return "";
}

そして、メール本文作成部分に以下のようにURLの列を追加します:

const description = event.getDescription() || "";
const urlHtml = extractUrl(description);
const descriptionWithoutUrl = description.replace(/(https?:\/\/[^\s]+)/g, "").trim().replace(/\n/g, "<br>");

htmlBody += "<td>" + event.getTitle() + "</td>";
htmlBody += "<td>" + (event.getLocation() || "") + "</td>";
htmlBody += "<td>" + urlHtml + "</td>";
htmlBody += "<td>" + descriptionWithoutUrl + "</td>";

3-6. フォルダ内のファイル一覧をスプレッドシートに出力

Google ドライブの特定フォルダ内のファイル一覧を定期的に出力する仕組みを作りましょう。

共有フォルダの棚卸しに便利

function listFilesInFolder() {
  // 対象フォルダのID(フォルダURLの末尾部分)
  const folderId = "1abc123_your_folder_id_xyz";
  const folder = DriveApp.getFolderById(folderId);

  // 出力先のスプレッドシート
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("ファイル一覧") || ss.insertSheet("ファイル一覧");

  // ヘッダー行の設定
  sheet.clear();
  sheet.appendRow(["ファイル名", "タイプ", "サイズ(KB)", "最終更新日", "所有者", "URL"]);

  // フォルダ内のファイルを取得
  const files = folder.getFiles();
  const fileList = [];

  while (files.hasNext()) {
    const file = files.next();
    const owner = file.getOwner();
    fileList.push([
      file.getName(),
      file.getMimeType(),
      Math.round(file.getSize() / 1024 * 100) / 100, // KB単位に変換(小数第2位まで)
      file.getLastUpdated(),
      owner ? owner.getEmail() : "不明",
      file.getUrl()
    ]);
  }

  // データが存在する場合は一括で追加
  if (fileList.length > 0) {
    sheet.getRange(2, 1, fileList.length, 6).setValues(fileList);
  }

  // 表の体裁を整える
  sheet.autoResizeColumns(1, 6);
  sheet.getRange(1, 1, 1, 6)
    .setFontWeight("bold")
    .setBackground("#eeeeee");

  // URLをハイパーリンクにする
  const urlRange = sheet.getRange(2, 6, fileList.length, 1);
  const urlFormula = urlRange.getValues().map(row => {
    return [`=HYPERLINK("${row[0]}", "開く")`];
  });
  urlRange.setFormulas(urlFormula);
}

ファイル名・更新日・所有者など取得可能

上記のスクリプトでは、ファイル名、MIME Type(ファイルタイプ)、サイズ、最終更新日、所有者、URLを取得しています。必要に応じて取得する情報を追加・変更可能です。

例えば、サブフォルダも含めてすべてのファイルをリストアップしたい場合は、再帰的に処理するように修正できます。

3-7. Googleドライブのストレージ使用量を定期的に可視化

Googleドライブのストレージ管理は、特に複数のチームやプロジェクトがあるオフィスでは意外と厄介な問題です。知らぬ間にドライブ容量が圧迫され、急に「容量が足りません」というアラートが出て焦った経験はありませんか?そんな問題を未然に防ぐための自動化シナリオをご紹介します。

実現できること

  • 定期的にGoogleドライブの使用容量を記録
  • 部署・プロジェクト別のフォルダ容量を自動計測
  • 急激に容量を消費しているフォルダを特定
  • 使用量が一定のしきい値を超えた場合に管理者へ通知

コードサンプル

function trackDriveStorageUsage() {
  // スプレッドシートを取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('ストレージ使用量') || ss.insertSheet('ストレージ使用量');

  // 今日の日付を取得
  const today = new Date();
  const formattedDate = Utilities.formatDate(today, 'JST', 'yyyy/MM/dd');

  // 計測するフォルダIDのリスト
  const folderIds = {
    '全社共有': 'xxxxxxxxxxxxxxxxxxxxxxxx',
    'マーケティング部': 'yyyyyyyyyyyyyyyyyyyyyyyy',
    '開発部': 'zzzzzzzzzzzzzzzzzzzzzzzz',
    'プロジェクトA': 'aaaaaaaaaaaaaaaaaaaaaaaa'
  };

  // スプレッドシートのヘッダー設定(初回のみ)
  if (sheet.getLastRow() === 0) {
    const headers = ['日付', '総容量(MB)'];
    Object.keys(folderIds).forEach(name => {
      headers.push(`${name}(MB)`);
    });
    sheet.appendRow(headers);
  }

  // 総容量の取得
  const storageLimit = DriveApp.getStorageLimit();
  const storageUsed = DriveApp.getStorageUsed();
  const totalUsageMB = Math.round(storageUsed / 1024 / 1024);

  // 各フォルダの容量取得
  const rowData = [formattedDate, totalUsageMB];
  Object.entries(folderIds).forEach(([name, id]) => {
    try {
      const folder = DriveApp.getFolderById(id);
      const folderSize = calculateFolderSize(folder);
      const folderSizeMB = Math.round(folderSize / 1024 / 1024);
      rowData.push(folderSizeMB);
    } catch (e) {
      rowData.push('エラー');
      Logger.log(`${name}フォルダの容量計算中にエラー: ${e.message}`);
    }
  });

  // データをシートに追加
  sheet.appendRow(rowData);

  // シートを整形
  sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).setNumberFormat('@');
  sheet.getRange(2, 2, sheet.getLastRow() - 1, sheet.getLastColumn() - 1).setNumberFormat('#,##0');

  // グラフを作成(初回のみ)
  if (sheet.getCharts().length === 0 && sheet.getLastRow() > 2) {
    createStorageChart(sheet);
  }

  // 容量アラート(総容量の80%を超えた場合)
  const storagePercentage = (storageUsed / storageLimit) * 100;
  if (storagePercentage > 80) {
    sendStorageAlert(totalUsageMB, storagePercentage);
  }
}

// フォルダサイズを再帰的に計算する関数
function calculateFolderSize(folder) {
  let totalSize = 0;

  // フォルダ内のファイルサイズを加算
  const files = folder.getFiles();
  while (files.hasNext()) {
    const file = files.next();
    totalSize += file.getSize();
  }

  // サブフォルダも再帰的に計算
  const subFolders = folder.getFolders();
  while (subFolders.hasNext()) {
    const subFolder = subFolders.next();
    totalSize += calculateFolderSize(subFolder);
  }

  return totalSize;
}

// ストレージ使用量のグラフを作成
function createStorageChart(sheet) {
  const lastRow = sheet.getLastRow();
  const lastCol = sheet.getLastColumn();

  const dataRange = sheet.getRange(1, 1, lastRow, lastCol);
  const chart = sheet.newChart()
    .setChartType(Charts.ChartType.LINE)
    .addRange(dataRange)
    .setPosition(lastRow + 2, 1, 0, 0)
    .setOption('title', 'ドライブストレージ使用量の推移')
    .setOption('legend', { position: 'bottom' })
    .setOption('width', 800)
    .setOption('height', 400)
    .build();

  sheet.insertChart(chart);
}

// アラートメールを送信
function sendStorageAlert(usageMB, percentage) {
  const recipient = 'admin@yourcompany.com'; // 管理者のメールアドレス
  const subject = '【警告】Googleドライブの容量が80%を超えています';
  const body = `
現在のGoogleドライブ使用量が全体容量の${Math.round(percentage)}%に達しています。

使用容量: ${usageMB.toLocaleString()} MB

早めのクリーンアップをご検討ください。

詳細はこちらのスプレッドシートでご確認いただけます:
${SpreadsheetApp.getActiveSpreadsheet().getUrl()}
  `;

  MailApp.sendEmail(recipient, subject, body);
}

設定のポイント

  1. 計測対象フォルダの選定
  • 部署別、プロジェクト別など、管理上意味のある単位でフォルダを選びましょう
  • 各フォルダのIDは、ブラウザでフォルダを開いたときのURLから取得できます
  1. 実行頻度の設定
  • 毎日だと負荷が高いので、週1回や月2回程度がおすすめ
  • 大規模なフォルダを対象にする場合は、スクリプトのタイムアウト(6分)に注意
  1. アラートしきい値の調整
  • 組織の状況に合わせて、80%以外の値に変更可能
  • 複数のしきい値(70%、80%、90%など)で段階的に通知することも効果的

活用アイデア

  • 容量増加率のグラフ化:前回との差分を計算して、増加スピードを可視化
  • 大容量ファイルのリスト作成:一定サイズ以上のファイルを抽出して報告
  • 未使用ファイルの特定:最終アクセス日時を取得して長期未使用のファイルをリストアップ