メニュー
    (更新: 2026-05-10)
    コラム

    GAS のトリガーをスクリプトで一元管理する方法【コード付き】

    GAS のトリガーをスクリプトで一元管理する方法【コード付き】

    Google Apps Script(GAS)でスプレッドシートの自動処理を組むと、トリガー(自動実行の設定)が必要になります。GAS エディタの画面から手動で設定できますが、トリガーの数が増えると管理が煩雑になりがちです。

    この記事では、トリガーの設定・一覧・削除をスクリプトで一元管理する方法を紹介します。config.js にトリガー定義を書くだけで、カスタムメニューや clasp run からワンアクションで操作できます。

    なぜトリガーをコードで管理するのか

    GAS エディタのトリガー管理画面は、少数のトリガーであれば十分に使えます。しかし、トリガーが増えてくると次のような問題が起きやすくなります。

    • トリガーの重複 — 同じ関数に複数のトリガーが付いてしまい、処理が二重実行される
    • 設定内容が見えない — 何時に何を実行しているか、画面を開かないと把握できない
    • clasp push 後の再設定忘れ — コードを更新してデプロイしたあと、トリガーの再設定を忘れてしまう
    • チーム共有が難しい — 設定内容がエディタの画面に閉じているため、メンバー間で共有しにくい

    コードで管理すれば、これらの問題を解決できます。

    • config.js を見れば全トリガーの設定内容が一覧できる
    • Git で変更履歴を追える
    • トリガーの設定・一覧・削除がワンアクション(メニュー選択 or コマンド1行)

    コード処理の流れと主な内容

    今回のテンプレートは4ファイルで構成されています。処理の流れは以下の通りです。

    1. 定義(config.js) — トリガーの対象関数・スケジュール種別・実行時刻を宣言的に定義
    2. 管理(triggers.js) — 定義に基づいてトリガーの設定・一覧表示・削除を実行
    3. UI(menu.js) — スプレッドシートのカスタムメニューから操作できるようにする
    4. 実行(main.js) — トリガーから呼び出される実際の処理(差し替え前提のサンプル)

    プロジェクト構成

    text
    trigger-manager/
    ├── config.js          ← トリガー定義(TRIGGER_CONFIGS)・設定値
    ├── triggers.js        ← トリガー管理(設定・一覧・削除のコア処理 + UI版 + Headless版)
    ├── menu.js            ← カスタムメニュー(スプレッドシート上の操作 UI)
    ├── main.js            ← メイン処理(差し替え前提のサンプル関数)
    ├── appsscript.json    ← GAS マニフェスト(タイムゾーン: Asia/Tokyo)
    └── .clasp.json        ← clasp 設定(Script ID)

    各ファイルの役割:

    • config.jsTRIGGER_CONFIGS 配列でトリガーを定義する。設定変更時はこのファイルだけを修正すればよい
    • triggers.js — トリガーの設定・一覧表示・削除のロジック。UI 版(カスタムメニュー用)と Headless 版(clasp run 用)の2系統を提供
    • menu.jsonOpen() でスプレッドシートにカスタムメニューを追加する
    • main.js — トリガーから呼び出されるハンドラ関数。このテンプレートではログ出力のみのサンプルを配置

    GAS では同一プロジェクト内の .js ファイルがすべてグローバルスコープを共有するため、import / export なしでファイル間の関数や変数を参照できます。

    完全なコードは GitHub リポジトリで公開しています。

    hitomi-sugioka/gas-examples — trigger-managergithub.com

    対応トリガータイプ

    時間主導型(ClockTriggerBuilder)

    タイプ type GAS メソッド 設定例
    分ごと minutes everyMinutes(n) 5分ごと(1, 5, 10, 15, 30)
    時間ごと hourly everyHours(n) 1時間ごと
    日ごと daily everyDays(1) 毎日 9時
    週ごと weekly everyWeeks(1).onWeekDay() 毎週月曜 10時
    月ごと monthly onMonthDay(day) 毎月1日 9時

    イベント型(SpreadsheetTriggerBuilder)

    タイプ type GAS メソッド 発火タイミング
    表示時 onOpen .forSpreadsheet(ss).onOpen() スプレッドシートを開いたとき
    編集時 onEdit .forSpreadsheet(ss).onEdit() セルが編集されたとき
    変更時 onChange .forSpreadsheet(ss).onChange() 構造変更時(行挿入・列削除等)
    フォーム送信時 onFormSubmit .forSpreadsheet(ss).onFormSubmit() フォーム回答送信時

    シンプルトリガーとインストーラブルトリガーの違い

    GAS のトリガーにはシンプルトリガーインストーラブルトリガーの2種類があります。

    • シンプルトリガーonOpen()onEdit() のように、予約された関数名を定義するだけで自動的に動作する。設定不要だが、外部サービスの呼び出しや認証が必要な操作は実行できない
    • インストーラブルトリガーScriptApp.newTrigger() で明示的に作成する。シンプルトリガーより広い権限で実行でき、外部 API の呼び出しやメール送信なども可能

    このテンプレートの menu.js にある onOpen() はシンプルトリガーです。一方、TRIGGER_CONFIGS で定義する onOpen タイプのトリガーはインストーラブルトリガーとして作成されます。

    関数名が衝突しないよう、ハンドラ関数には myOnOpenTask のように別名を使用してください。

    config.js — トリガー定義

    TRIGGER_CONFIGS 配列にトリガーを定義します。テンプレートには時間主導型5種 + イベント型2種 = 計7件のサンプルが含まれています。

    javascript
    var TRIGGER_CONFIGS = [
      // --- 分ごと ---
      {
        functionName: 'myFrequentTask',
        label: '高頻度タスク(5分ごと)',
        type: 'minutes',
        minutes: 5
      },
      // --- 時間ごと ---
      {
        functionName: 'myHourlyTask',
        label: '毎時タスク',
        type: 'hourly',
        hours: 1,
        minute: 30
      },
      // --- 日ごと ---
      {
        functionName: 'myDailyTask',
        label: 'デイリータスク',
        type: 'daily',
        hour: 9,
        minute: 0
      },
      // --- 週ごと ---
      {
        functionName: 'myWeeklyTask',
        label: 'ウィークリータスク',
        type: 'weekly',
        hour: 10,
        weekDay: ScriptApp.WeekDay.MONDAY
      },
      // --- 月ごと ---
      {
        functionName: 'myMonthlyTask',
        label: 'マンスリータスク',
        type: 'monthly',
        hour: 9,
        monthDay: 1
      },
      // --- イベント型(スプレッドシート) ---
      {
        functionName: 'myOnOpenTask',
        label: 'スプレッドシート表示時タスク',
        type: 'onOpen'
      },
      {
        functionName: 'myOnEditTask',
        label: 'セル編集時タスク',
        type: 'onEdit'
      }
    ];

    各タイプの設定プロパティ

    共通プロパティ

    プロパティ 説明
    functionName トリガーで呼び出す関数名(main.js で定義)
    label 管理画面やログに表示するラベル
    type トリガータイプ(上記の表を参照)

    共通オプション(省略可)

    プロパティ 説明 対象タイプ
    hour 実行時刻(0〜23)。atHour() に対応 daily, weekly, monthly
    minute 分指定(0〜59)。nearMinute() に対応。±15分の揺れあり 時間主導型(minutes 以外)
    timezone タイムゾーン。inTimezone() に対応。省略時はスクリプトのタイムゾーン 時間主導型
    spreadsheetId 対象スプレッドシートID。省略時はバインド先 イベント型

    minutenearMinute() は、GAS が指定した分の前後15分の範囲で実行する仕組みです。正確な分指定ではなく、おおよその目安として使用します。

    triggers.js — トリガー管理のコア処理

    triggers.js は3層の構造になっています。

    1. コア処理_ サフィックス付き) — UI に依存しないトリガー操作の本体
    2. UI 版 — カスタムメニューから呼び出す。確認ダイアログを表示
    3. Headless 版clasp run から呼び出す。戻り値とコンソール出力で結果を返す

    ※ GAS では関数名の末尾に _(アンダースコア)を付けると、GAS エディタの関数一覧やカスタムメニューの候補に表示されなくなります。外部から直接呼び出さないヘルパー関数には _ を付けるのが慣習です。

    setupTriggerCore_() — トリガー作成の仕組み

    トリガーの作成は setupTriggerCore_() が担当します。config のタイプに応じて、時間主導型とイベント型で処理を分岐します。

    javascript
    function setupTriggerCore_(config) {
      var eventTypes = ['onOpen', 'onEdit', 'onChange', 'onFormSubmit'];
      var trigger;
      var builder;
    
      if (eventTypes.indexOf(config.type) !== -1) {
        // イベント型(スプレッドシート)
        var ss;
        if (config.spreadsheetId) {
          ss = SpreadsheetApp.openById(config.spreadsheetId);
        } else {
          ss = SpreadsheetApp.getActive();
          if (!ss) {
            throw new Error(
              config.functionName + ': イベント型トリガーを clasp run から設定する場合は '
              + 'config に spreadsheetId を指定してください。'
            );
          }
        }
        builder = ScriptApp.newTrigger(config.functionName).forSpreadsheet(ss);
    
        switch (config.type) {
          case 'onOpen':      builder.onOpen(); break;
          case 'onEdit':      builder.onEdit(); break;
          case 'onChange':     builder.onChange(); break;
          case 'onFormSubmit': builder.onFormSubmit(); break;
        }
        trigger = builder.create();
      } else {
        // 時間主導型
        builder = ScriptApp.newTrigger(config.functionName).timeBased();
    
        switch (config.type) {
          case 'minutes':
            builder.everyMinutes(config.minutes);
            break;
          case 'hourly':
            builder.everyHours(config.hours || 1);
            break;
          case 'daily':
            builder.everyDays(1);
            break;
          case 'weekly':
            builder.everyWeeks(1).onWeekDay(config.weekDay);
            break;
          case 'monthly':
            builder.onMonthDay(config.monthDay);
            break;
        }
    
        if (config.hour !== undefined) {
          builder.atHour(config.hour);
        }
        if (config.minute !== undefined) {
          builder.nearMinute(config.minute);
        }
        if (config.timezone) {
          builder.inTimezone(config.timezone);
        }
    
        trigger = builder.create();
      }
      saveStoredConfig_(trigger.getUniqueId(), config);
    
      var description = formatSchedule_(config);
      Logger.log(config.functionName + ' を設定しました: ' + description);
    
      return {
        handlerFunction: config.functionName,
        label: config.label,
        schedule: description
      };
    }

    処理の流れ:

    1. config.type がイベント型(onOpen / onEdit / onChange / onFormSubmit)かどうかを判定
    2. イベント型の場合は forSpreadsheet() でスプレッドシートを指定してビルダーを作成
    3. 時間主導型の場合は timeBased() でビルダーを作成し、タイプに応じてスケジュールを設定
    4. 共通オプション(hour / minute / timezone)があれば追加
    5. builder.create() でトリガーを作成
    6. saveStoredConfig_() で設定値を ScriptProperties に保存

    deleteTriggerCore_() — トリガーの削除

    deleteTriggerCore_()functionName でマッチングして削除します。

    javascript
    function deleteTriggerCore_(config) {
      var triggers = ScriptApp.getProjectTriggers();
      var count = 0;
      triggers.forEach(function(trigger) {
        if (trigger.getHandlerFunction() === config.functionName) {
          deleteStoredConfig_(trigger.getUniqueId());
          ScriptApp.deleteTrigger(trigger);
          count++;
        }
      });
      Logger.log(config.functionName + ': ' + count + '件削除');
      return count;
    }

    ScriptApp.getProjectTriggers() でプロジェクト内の全トリガーを取得し、getHandlerFunction() が一致するものだけを削除します。同じ関数名のトリガーが複数あっても、すべて削除されます。

    ScriptProperties による設定値の永続化

    トリガー作成時に、config の設定値(ラベル・タイプ・スケジュール等)を ScriptProperties に保存しています。

    javascript
    function saveStoredConfig_(triggerId, config) {
      var snapshot = {
        label: config.label,
        functionName: config.functionName,
        type: config.type
      };
      if (config.hour !== undefined) { snapshot.hour = config.hour; }
      if (config.minute !== undefined) { snapshot.minute = config.minute; }
      if (config.minutes !== undefined) { snapshot.minutes = config.minutes; }
      // ... 他のプロパティも同様
      var props = PropertiesService.getScriptProperties();
      props.setProperty('trigger_' + triggerId, JSON.stringify(snapshot));
    }

    保存した設定値はトリガー一覧表示(showTriggerStatus)でスケジュールの日本語表示に使われます。resolveConfigForTrigger_() が保存値を優先的に参照し、見つからなければ TRIGGER_CONFIGS から functionName で検索するフォールバック構成です。

    javascript
    function resolveConfigForTrigger_(trigger) {
      var stored = getStoredConfig_(trigger.getUniqueId());
      if (stored) return stored;
      return findTriggerConfig_(trigger.getHandlerFunction());
    }

    formatSchedule_() — スケジュール表示

    トリガーのスケジュールを日本語で返す関数です。一覧表示や確認ダイアログで使用されます。

    javascript
    function formatSchedule_(config) {
      var minuteSuffix = (config.minute !== undefined) ? '(' + config.minute + '分頃)' : '';
      var hourSuffix = (config.hour !== undefined) ? ' ' + config.hour + '時' : '';
    
      switch (config.type) {
        case 'minutes':
          return config.minutes + '分ごと';
        case 'hourly':
          return (config.hours || 1) + '時間ごと' + minuteSuffix;
        case 'daily':
          return '毎日' + hourSuffix;
        case 'weekly':
          var dayLabels = {};
          dayLabels[ScriptApp.WeekDay.MONDAY] = '月';
          // ... 他の曜日も同様
          var dayLabel = dayLabels[config.weekDay] || '?';
          return '毎週' + dayLabel + '曜' + hourSuffix;
        case 'monthly':
          return '毎月' + config.monthDay + '日' + hourSuffix;
        case 'onOpen':
          return 'スプレッドシート表示時';
        case 'onEdit':
          return 'セル編集時';
        // ...
      }
    }

    UI 版と Headless 版

    setupTrigger() / deleteTrigger() は UI 版で、SpreadsheetApp.getUi() を使って確認ダイアログを表示します。カスタムメニューから呼び出します。

    setupTriggerHeadless() / deleteTriggerHeadless() は Headless 版で、clasp run から呼び出します。引数なしで実行すると確認モード(ドライラン)になり、--params '[true]' を付けると実際に実行します。

    bash
    # 確認モード(設定内容を表示するのみ)
    clasp run setupTriggerHeadless
    
    # 実行モード(実際にトリガーを設定)
    clasp run setupTriggerHeadless --params '[true]'

    SpreadsheetApp.getUi() は Headless 実行(clasp run)では使えないため、この2系統に分けています。

    スプレッドシートを開くと「トリガー管理」メニューが自動的に追加されます。

    javascript
    /**
     * スプレッドシートを開いたときに「トリガー管理」メニューを追加する(Simple Trigger)
     */
    function onOpen() {
      SpreadsheetApp.getUi()
        .createMenu('トリガー管理')
        .addItem('トリガー一覧を確認', 'showTriggerStatus')
        .addItem('トリガーを設定', 'setupTrigger')
        .addItem('トリガーを削除', 'deleteTrigger')
        .addToUi();
    }

    この onOpen() はシンプルトリガーとして動作します。スプレッドシートを開いたときに GAS が自動的に呼び出すため、明示的なトリガー設定は不要です。

    メニュー項目 実行される関数 用途
    トリガー一覧を確認 showTriggerStatus() 設定済みトリガーのラベル・スケジュール・種別を一覧表示
    トリガーを設定 setupTrigger() TRIGGER_CONFIGS のトリガーを一括作成
    トリガーを削除 deleteTrigger() 管理対象トリガーを一括削除

    main.js — ハンドラ関数

    main.js にはトリガーから呼び出されるサンプル関数を配置しています。実際のプロジェクトでは、ここに業務ロジックを実装してください。

    javascript
    /** デイリータスク(毎日実行) */
    function myDailyTask() {
      Logger.log('デイリータスクを実行しました: ' + new Date().toLocaleString('ja-JP'));
      // TODO: ここに毎日実行したい処理を実装
    }
    
    /** セル編集時タスク(インストーラブルトリガー) */
    function myOnEditTask(e) {
      if (!e || !e.range) {
        Logger.log('myOnEditTask: イベントオブジェクトがありません(トリガー経由で実行してください)');
        return;
      }
      Logger.log('セルが編集されました: ' + e.range.getA1Notation());
      // TODO: ここにセル編集時の処理を実装
    }

    イベントオブジェクト

    イベント型トリガーで呼び出される関数には、イベントオブジェクト e が渡されます。

    type 主要プロパティ 説明
    onOpen e.source 開かれたスプレッドシート
    onEdit e.range, e.value, e.oldValue 編集されたセル範囲・編集後の値・編集前の値
    onChange e.changeType 変更種別(EDIT, INSERT_ROW 等)
    onFormSubmit e.values, e.namedValues, e.range フォーム回答の値・名前付き値・書き込み先範囲

    イベントオブジェクトはトリガー経由でのみ渡されるため、GAS エディタから手動実行した場合は undefined になります。上記の myOnEditTask のように、e の存在チェックを入れておくと安全です。

    カスタマイズ方法

    1. main.js のサンプル関数を実際の業務ロジックに書き換える
    2. 関数名を変更する場合は、config.jsTRIGGER_CONFIGSfunctionName も合わせて変更する
    3. 不要なトリガーは TRIGGER_CONFIGS から削除する
    4. 変更後は clasp push → トリガーの再設定を実行

    セットアップと使い方

    前提

    本記事は次の環境を前提とします。

    • Google アカウント(GAS プロジェクトを作成・所有できる)
    • Node.js v20.x 以降
    • @google/clasp v3.x 以降

    初回セットアップ

    1. GAS プロジェクトを作成

    新規スプレッドシートを作成し、「拡張機能」>「Apps Script」で GAS プロジェクトを作成します。

    2. clasp を設定

    GAS エディタの 設定(歯車アイコン)から Script ID を取得し、.clasp.json を作成します。

    bash
    # .clasp.json を作成し、scriptId を実際の値に書き換え
    cp .clasp.json.example .clasp.json
    
    # clasp にログイン(未ログインの場合)
    clasp login
    
    # ログイン確認(ブラウザで GAS エディタが開けば OK)
    clasp open-script

    3. コードをデプロイ

    bash
    # GAS プロジェクトにコードを反映
    clasp push

    初回 push 時に「Manifest file has been updated. Do you want to push and overwrite?」と表示されます。Yes を選択してください。

    4. 権限を承認

    初回の push 後、GAS エディタで myDailyTask を手動実行してください。「承認が必要です」ダイアログが表示されます。

    1. 権限を確認 をクリック
    2. Google アカウントを選択
    3. 「アクセスできる情報を選択してください」で すべて選択 にチェック
    4. 続行 をクリック

    ※ この承認は初回のみ必要です。appsscript.json のスコープを変更して再 push した場合は、再度承認が必要になります。

    5. トリガーを設定

    以下のいずれかの方法で、TRIGGER_CONFIGS に定義されたトリガーを一括設定します。

    方法 A: カスタムメニューから操作

    スプレッドシートを開き、「トリガー管理」メニュー →「トリガーを設定」を選択します。確認ダイアログに設定予定のトリガー一覧が表示されるので、「はい」を選ぶとトリガーが一括作成されます。

    方法 B: clasp run から操作

    clasp run を使う場合は、事前に GCP プロジェクトの設定と実行可能 API のデプロイが必要です。詳細は リポジトリの README を参照してください。

    bash
    # ステップ1: 設定内容を確認(ドライラン)
    clasp run setupTriggerHeadless

    現在のトリガー状態と設定予定のトリガーが表示されます。内容を確認したら、実際に設定を実行します。

    bash
    # ステップ2: 実際にトリガーを設定
    clasp run setupTriggerHeadless --params '[true]'

    *Headless 関数は clasp run 専用です。GAS エディタから直接実行した場合、引数を渡せないため確認モード(ドライラン)で停止します。

    トリガーの削除

    トリガーを削除したい場合は、カスタムメニューの「トリガーを削除」、または以下のコマンドで削除できます。

    bash
    # 削除対象を確認
    clasp run deleteTriggerHeadless
    
    # 実際に削除
    clasp run deleteTriggerHeadless --params '[true]'

    トリガー一覧の確認

    設定済みトリガーの一覧を確認するには、カスタムメニューの「トリガー一覧を確認」、または以下のコマンドを使用します。

    bash
    clasp run showTriggerStatusHeadless

    制限事項と注意点

    コード管理と手動管理の混在を避ける

    トリガーの管理方法はどちらか一方に統一してください。GAS エディタのトリガー管理画面からの手動操作と、コードによる管理を混在させると、トリガーの重複や削除漏れの原因になります。

    操作対象の範囲

    setupTrigger / deleteTriggerTRIGGER_CONFIGSfunctionName に一致するトリガーだけを操作します。GAS エディタのトリガー管理画面から手動追加されたトリガーとの関係は以下の通りです。

    手動追加したトリガーの関数名 一覧表示 トリガー設定 トリガー削除
    TRIGGER_CONFIGS同じ 表示される 削除して再作成される 削除される
    TRIGGER_CONFIGS異なる 表示される 影響なし(残る) 影響なし(残る)
    • GAS エディタから手動でトリガーを追加すると、同じ関数名のトリガーが重複し、処理が二重実行される原因になります
    • 手動追加したトリガーを deleteTrigger で削除するには、該当の functionNameTRIGGER_CONFIGS に定義されている必要があります

    サンプルコード全体

    完全なコードは GitHub リポジトリで公開しています。リポジトリには clasp の設定サンプルやマニフェストファイルも含まれています。

    hitomi-sugioka/gas-examples — trigger-managergithub.com

    GAS によるスプレッドシート連携の実用例として、トリガーを使った自動メール通知の記事も公開しています。

    GAS × スプレッドシートで期限通知メールを自動送信する方法/blog/gas-auto-email-notification
    Contact

    お気軽にご相談ください

    お電話でのお問い合わせ

    072-853-3553

    平日 10:00〜17:30