AUTOMATION NOTE — 012

GASでGoogleフォームの選択肢を自動更新したら「マスタ管理」の壁にぶつかった話

この記事を読んだほうが良い人

  • Googleフォームの選択肢(担当者名・部署名など)を毎回手動で更新していて面倒を感じている人
  • GASでフォームを動的更新する仕組みを作ったものの、うまく機能しなくて困っている人
  • 「自動化した=運用コストゼロ」と思い込んでいて痛い目を見たことがある人 フォームの選択肢を手動で更新するのが、じわじわとストレスになっていませんか。

担当者が変わるたびにフォームを開いて、選択肢を追加して、古い名前を消して……。月に数回ならまだしも、組織の変化が多い時期だと週に何度もその作業が発生します。この記事では、GASを使ってGoogleスプレッドシートのマスタからフォームの選択肢を自動で同期する仕組みを作った経験と、その過程で直面した「マスタ管理」の問題をそのまま書きます。

背景

社内の問い合わせ受付用Googleフォームで、担当者ごとの振り分けをフォームの選択肢で実現していました。担当者は十数名、月1〜2回のペースで入れ替わりがある環境です。フォームの更新漏れによる「存在しない担当者に問い合わせが飛ぶ」事故が複数回発生したため、スプレッドシートのマスタと連動させる自動化に踏み切りました。

やったこと

スプレッドシートの構成

マスタシートはシンプルにしました。担当者マスタ というシート名で、A列に氏名を縦に並べるだけです。

A列
担当者名(ヘッダー)
田中 太郎
鈴木 花子

B列以降に部署やメールアドレスを持たせることもできますが、フォームの選択肢に必要なのは名前だけなので、最初はA列だけで動かしました。なお後述しますが、運用改善の過程でB1セルに「最終更新日」を入れるルールを追加しています。マスタの鮮度チェックに使うためです。

GASのコード

フォームの特定の質問(ListItem)の選択肢を、スプレッドシートの内容で上書きするスクリプトです。スクリプトエディタで新しいファイルを作り、以下をそのまま貼り付けます。

// スプレッドシートのマスタシートを読み取り、フォームの選択肢を更新する
function updateFormChoices() {
  const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID'; // マスタシートのスプレッドシートID
  const SHEET_NAME = '担当者マスタ';
  const FORM_ID = 'YOUR_FORM_ID'; // 対象フォームのID
  const QUESTION_TITLE = '担当者を選択してください'; // フォーム上の質問文と完全一致させる

  // マスタから担当者名を取得(空白行を除外)
  const sheet = SpreadsheetApp
    .openById(SPREADSHEET_ID)
    .getSheetByName(SHEET_NAME);

  const values = sheet
    .getRange('A2:A') // 1行目はヘッダーなのでA2から取得
    .getValues()
    .flat()
    .filter(v => v !== '');

  if (values.length === 0) {
    console.error('マスタに担当者が1件もいません。処理を中断します。');
    return;
  }

  // フォームの対象質問を探して選択肢を更新
  const form = FormApp.openById(FORM_ID);
  const items = form.getItems(FormApp.ItemType.LIST_ITEM); // リスト形式の質問のみ対象

  let updated = false;
  items.forEach(item => {
    if (item.getTitle() === QUESTION_TITLE) {
      item.asListItem().setChoiceValues(values);
      updated = true;
      console.log(`更新完了: ${values.length}件の選択肢をセットしました`);
    }
  });

  if (!updated) {
    console.warn(`質問「${QUESTION_TITLE}」が見つかりませんでした。質問文を確認してください。`);
  }
}

YOUR_SPREADSHEET_IDYOUR_FORM_ID の2箇所を置き換えるだけで動きます。スプレッドシートのIDはURLの /d//edit の間の文字列、フォームのIDは同様に /d//edit の間です。

QUESTION_TITLE はフォーム上の質問文と完全一致させてください。スペースの全角/半角の違いでマッチしないことがあります。

トリガーの設定

スクリプトエディタの「トリガー」から時間駆動トリガーを設定します。担当者の変更頻度に合わせますが、毎日1回(深夜0時〜1時)で十分でした。

スプレッドシート側に onEdit トリガーを仕掛けて、マスタを編集した瞬間に同期する方法もあります。ただし、編集途中(セルに入力中)でもトリガーが走るため、中途半端な状態でフォームが更新されるリスクがあります。日次バッチの方が安定します。

ハマったポイント

マスタが更新されていないとシステムが静かに壊れる

スクリプト自体は問題なく動いていました。毎朝きちんとフォームの選択肢を更新しています。ところが、実際の運用で「退職した担当者が選択肢に残っている」「新しい担当者が出てこない」という報告が続きました。

原因を追ったら、マスタシートが更新されていなかっただけです。

GASはスプレッドシートの内容をそのままフォームに反映するので、元のデータが間違っていれば間違ったまま同期します。エラーもアラートも出ません。updateFormChoices() のログには「5件の選択肢をセットしました」と正常終了のメッセージが残っています。システムとしては完全に正常動作です。

自動化したことで「フォームは自動で正しくなる」という誤解が生まれ、マスタの更新を誰もしなくなっていました。仕組みが裏で動いているのが見えないからこそ、ケアが抜けやすい。これが一番の誤算でした。

「誰がマスタを更新するか」が決まっていなかった

担当者の入れ替えは人事関連の話なので、情報は管理部門が持っています。ただ、スプレッドシートの更新は「システム担当っぽい人」がやるものだという空気があり、実際には誰も責任を持っていませんでした。

ツールの問題ではなく、オーナーシップの問題です。自動化する前は「フォームを更新する人=フォームのオーナー」が明確でした。GASを挟んだことで責任の所在が曖昧になりました。

結果と学び

対策として2つやりました。

1. マスタに「最終更新日」列と更新者名を追加する

B列に更新日(=TODAY() を手動で確定入力)、C列に更新者名を記入するルールにしました。スクリプト側でこの日付をチェックし、30日以上古ければSlackに警告を飛ばす処理を追加しています。

// マスタの最終更新日をチェックし、30日以上経過していたら警告を送る
function checkMasterFreshness() {
  const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID';
  const SHEET_NAME = '担当者マスタ';
  const SLACK_WEBHOOK_URL = 'YOUR_SLACK_WEBHOOK_URL';
  const THRESHOLD_DAYS = 30;

  const sheet = SpreadsheetApp
    .openById(SPREADSHEET_ID)
    .getSheetByName(SHEET_NAME);

  // B1セルに最終更新日を入れる運用にしている
  const lastUpdated = sheet.getRange('B1').getValue();
  if (!(lastUpdated instanceof Date)) {
    notifySlack(SLACK_WEBHOOK_URL, '⚠️ 担当者マスタの最終更新日が未設定です。確認してください。');
    return;
  }

  const daysSinceUpdate = Math.floor(
    (new Date() - lastUpdated) / (1000 * 60 * 60 * 24)
  );

  if (daysSinceUpdate >= THRESHOLD_DAYS) {
    notifySlack(
      SLACK_WEBHOOK_URL,
      `⚠️ 担当者マスタが${daysSinceUpdate}日間更新されていません。内容を確認してください。`
    );
  }
}

function notifySlack(webhookUrl, message) {
  const payload = JSON.stringify({ text: message });
  UrlFetchApp.fetch(webhookUrl, {
    method: 'POST',
    contentType: 'application/json',
    payload: payload,
  });
}

checkMasterFreshness() は週次(月曜朝9時)で動かしています。警告が来たらマスタを確認する、という習慣が定着しました。

2. マスタの更新オーナーを明示的に決める

管理部門の特定の担当者に「このシートはあなたが管理する」とはっきり伝え、スプレッドシートの共有設定でもその人を編集権限の筆頭に置きました。Slackの警告通知もその人のDMに直接送るようにしています。


自動化は手間を減らしますが、「インプットの正しさを担保する責任」は人間が持ち続けます。GASが毎日完璧に動いていても、食わせるデータが古ければ出力も古い。当たり前のことですが、仕組みが見えにくくなると意識から抜け落ちやすい。

Googleフォームの動的更新を実装する場合、コードと同じくらい「マスタの更新フロー設計」に時間を使うことをお勧めします。コードは1時間で書けますが、運用の合意形成は1週間かかることもあります。



コーポレートITのご相談はお気軽に

この記事で書いたような業務改善・自動化の設計から実装まで、DRASENASではコーポレートITの現場に寄り添った支援を行っています。 「まず相談だけ」でも大歓迎です。DRASENAS 公式サイトからお気軽にどうぞ。

CONTACT

御社の IT 部門、ここにあります。

「ITのことはあまりわからない」── そのような状態からで、まったく問題ございません。まずはお気軽にご相談ください。

一社ずつ、一から。