【完全版】GASトリガー設定ガイド|種類・時間指定・定期実行を徹底解説

「VBAのマクロを手動で実行するのが面倒…決まった時間に自動で動かせないかな?」そんなExcelユーザーの悩みを、GASのトリガー機能が解決します!
- GASトリガーの基本概念と、VBA OnTime・Windows タスクスケジューラとの違い
- シンプルトリガーとインストーラブルトリガーの種類と使い分け
- GUI操作とスクリプトの両方からトリガーを設定する手順
- 時間指定・定期実行のパターン集(毎日・毎週・平日のみ・月末など)
- イベント駆動型トリガー(onEdit・onChange・onFormSubmit)の活用法
- トリガーの管理・確認・削除・オーナー変更の方法
- 実務ユースケース3選(月次レポート自動化・フォーム通知・日次データ集計)
イベントのソースを選択すると、設定項目と実行タイミングが変わります。(実際のGASエディタの動作をシミュレーションしています)
実行パターンを選択
トリガーイベントを選択
トリガーイベント
GASのトリガーとは?VBAタスクスケジューラとの違い
GASのトリガーとは、Google Apps Scriptで特定の条件(時間経過またはユーザーの操作)を満たした時に、自動的に関数を実行する仕組みです。Excel VBAのOnTimeメソッドやWindowsのタスクスケジューラとは異なり、Google Workspace(スプレッドシート・ドキュメント・フォーム)と深く統合されており、より直感的かつ強力な自動化が可能です。GASの基本操作がまだ不安な方は、GAS入門完全ガイドを先にご覧ください。
トリガーの基本概念
トリガーは「条件」と「実行関数」の組み合わせです。たとえば「毎日午前9時に関数Aを実行する」「スプレッドシートのセルが編集されたら関数Bを実行する」といった設定ができます。VBAと異なり、GASではGoogle Appsが動作している限りトリガーが有効であり、ローカルマシンの電源をオンにしておく必要がありません。
トリガーは以下の4つのタイプに分類されます。
- 時間ベース(Time-driven): 指定時刻や定期間隔で実行
- イベントベース(Event-driven): ユーザーの操作(編集・送信など)を契機に実行
- カレンダーベース: Googleカレンダーの変更をトリガーに実行
- フォーム送信ベース: Googleフォームの送信時に実行
VBA OnTime・タスクスケジューラとの対比表
| 項目 | VBA OnTime | タスクスケジューラ | GAS トリガー |
|---|---|---|---|
| 実行環境 | ローカルPC(Excel起動必須) | ローカルPC(常時実行) | Google Cloud(常時稼働) |
| 最小実行間隔 | 1秒 | 1分 | 1分(推奨) |
| イベント駆動 | ×(時間ベースのみ) | ×(時間ベースのみ) | ○(編集・送信など) |
| Google Appsとの連携 | △(VBAオブジェクトモデル) | △(スクリプト経由) | ○(深い統合) |
| 権限管理 | 実行ユーザーのみ | 実行ユーザーのみ | ○(オーナー、編集者など) |
| セットアップの容易さ | コード必須 | GUI+スクリプト | ○(GUIまたはコード) |
| エラー通知 | ログに記録のみ | イベントビューア | ○(自動メール通知) |
GASトリガーの3つのメリット
- クラウド依存で常時稼働: ローカルマシンを稼働させ続ける必要がなく、電力コストとメンテナンスコストが削減できます
- イベント駆動型で効率的: 時間ベースだけでなく、実際のユーザー操作を契機に実行でき、無駄な処理を削減できます
- 権限管理と監査ログ: トリガーの実行履歴がGoogle Cloud Consoleに記録され、監査とセキュリティ対応が容易です
VBA OnTimeからGASへの移行を検討している方へ:GASはローカルマシンの電源切断時にも確実に実行されるため、月次レポートや日次バックアップなどの重要な自動化には最適です。タスクスケジューラとの組み合わせも可能(たとえば、タスクスケジューラでGASの実行トリガーをHTTP POSTで呼び出す)です。
Q: GASのトリガーはいくつまで設定できますか?
A: 1つのスプレッドシートあたり最大20個のトリガーを設定できます。複数のスプレッドシートを使う場合、それぞれ20個ずつ設定可能です。ただし、プロジェクト全体のメモリ使用量とAPI呼び出し数には制限があるため、実務上は10~15個程度が推奨です。
トリガーの種類一覧(シンプル vs インストーラブル)
GASのトリガーは「シンプルトリガー」と「インストーラブルトリガー」の2種類に分けられます。両者はGAS固有の概念で、設定方法と権限、実行条件が異なります。
シンプルトリガー
シンプルトリガーは、イベントハンドラー関数(onOpen、onEdit、onSubmit、onInstall、onUninstall)を自動的に実行するトリガーです。関数名が決められており、GASが動作している間は自動的に有効になります。
シンプルトリガーの種類:
onOpen(): スプレッドシート・ドキュメント・フォームを開いた時に実行onEdit(): スプレッドシート内の任意のセルが編集された時に実行onSubmit(): Googleフォームが送信された時に実行onInstall(): スクリプトがインストールされた時に実行onUninstall(): スクリプトがアンインストールされた時に実行
シンプルトリガーの特徴:
- 権限承認が不要(スクリプトエディタが自動的に権限を判定)
- 時間ベースのトリガーは設定不可(イベントベースのみ)
- 外部APIの呼び出しに制限あり(URLFetch、Mail送信など一部不可)
インストーラブルトリガー
インストーラブルトリガーは、ScriptApp.newTrigger() メソッドで明示的に設定するトリガーです。GUI操作またはスクリプトから作成でき、時間ベース・イベントベース両方に対応しています。
インストーラブルトリガーの特徴:
- 時間ベース(毎日・毎週・毎月の定期実行)が可能
- より詳細なイベント設定(onChange、onFormSubmitなど)が可能
- 初回実行時に権限承認が必要
- 外部API(Slack、LINE、メール自動送信)が利用可能
- GUI操作で容易に設定・管理できる
両者の違い比較表
| 比較項目 | シンプルトリガー | インストーラブルトリガー |
|---|---|---|
| セットアップ方法 | 関数名を定義するだけ(onOpen など) | GUIまたは ScriptApp.newTrigger() |
| 権限承認 | 不要 | 初回実行時に必須 |
| 時間ベース | × | ○ |
| イベントベース | ○(限定的) | ○(詳細設定可) |
| 外部API利用 | △(一部制限) | ○(全API利用可) |
| GUI設定 | × | ○ |
| 複数トリガー設定 | △(同一関数のみ) | ○(異なる関数複数可) |
| 削除方法 | 関数定義を削除 | トリガーパネルから削除 |
Q: onEditとonChangeの違いは何ですか?
A: onEdit(シンプルトリガー)は、スプレッドシート内の任意のセルが手動で編集された時に実行されます。一方、onChange(インストーラブルトリガー)は、セルの値が何らかの理由で変更された時に実行され、スクリプト自体による変更もトリガーされます。詳細は「イベント駆動型トリガーの使い方」で説明します。
GUI操作でトリガーを設定する手順
GUI操作はトリガー設定の最も直感的な方法です。スクリプトエディタから数回のクリックで、時間ベース・イベントベースの両方が設定できます。
スクリプトエディタからトリガー画面を開く
- Google スプレッドシート・ドキュメント・フォームを開く
- メニューから「拡張機能」→「Apps Script」をクリック
- スクリプトエディタが開いたら、左側ナビゲーションから「トリガー」(時計アイコン)をクリック
- 「トリガーを追加」ボタン(右下の「+」)をクリック
「トリガーを追加」の各設定項目を解説
トリガー追加ダイアログが表示されたら、以下の4つの項目を設定します。
1. 実行する関数を選択
ドロップダウンメニューから、トリガーで実行したい関数を選択します。たとえば、日次レポートを自動送信したければ「sendDailyReport」を選択します。
2. 実行するデプロイを選択
通常は「Head」(最新版)を選択します。複数バージョンを管理している場合のみ、特定のデプロイを選択することもあります。
3. イベントソースを選択
- 時間駆動: 指定時刻または定期間隔で実行
- スプレッドシートから: onEdit、onChange、onOpenなどのイベント
- カレンダーから: Googleカレンダーの更新
- フォームから: Googleフォームの送信
4. トリガーの種類を選択
時間駆動の場合:
- 時間の間隔: 毎日、毎週など
- 時間範囲: 開始時刻を指定(「午前0時~1時」のような1時間単位)
スプレッドシートイベントの場合:
- on edit: セル編集時(手動のみ)
- on change: 値の変更時(スクリプトの変更も含む)
- on open: スプレッドシート開時
権限の承認ダイアログの対処
インストーラブルトリガーを初めて設定する際、「このアプリはGoogleによって確認されていません」という権限承認ダイアログが表示されます。
対応手順:
- 「詳細」をクリック
- 「(安全ではない可能性がある)【プロジェクト名】に移動」をクリック
- 表示されたGoogleアカウント選択画面で、自分のアカウントを選択
- 「このアプリはGoogleによって確認されていません」というページで「続行」をクリック
- スコープの許可確認が出たら「許可」をクリック
この認可は1度だけで済み、以後は同じトリガーを再実行する際に再度の認可は不要です。
スクリプトからトリガーを設定する方法
ScriptApp.newTrigger() を使うことで、スクリプトコードから直接トリガーを作成できます。この方法は自動化度が高く、複数のトリガーをまとめて管理する場合に効果的です。
基本構文とメソッドチェーン
// 毎日午前9時に実行するトリガーの基本形
function createDailyTrigger() {
ScriptApp.newTrigger('sendDailyReport')
.timeBased()
.atHour(9)
.everyDays(1)
.create();
}
// スプレッドシート編集時に実行するトリガー
function createEditTrigger() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
ScriptApp.newTrigger('onEditFunction')
.forSpreadsheet(spreadsheet)
.onEdit()
.create();
}
// Googleフォーム送信時に実行するトリガー
function createFormTrigger() {
var form = FormApp.openById('フォームID');
ScriptApp.newTrigger('onFormSubmitFunction')
.forForm(form)
.onFormSubmit()
.create();
}
メソッドチェーンの流れ:
- ScriptApp.newTrigger(‘関数名’): トリガーオブジェクトを作成
- .timeBased() / .forSpreadsheet() など: トリガータイプを指定
- .atHour(9) など: トリガーの詳細条件を指定
- .create(): トリガーを実際に作成
コードで設定する3つのメリット
- 複数トリガーのまとめ管理: 1つの関数で複数のトリガーを一括作成・削除でき、スクリプトのセットアップが自動化できます
- 条件分岐による動的設定: if文を使って、状況に応じて異なるトリガーを作成できます
- バージョン管理: トリガー設定をスクリプトに記載することで、Gitなどのバージョン管理システムで履歴追跡が可能になります
VBA OnTime との対比コード
VBA の OnTime 例:
' VBA: ローカル実行、Excelが起動していないと動作しない
Sub ScheduleReportVBA()
Dim nextTime As Date
nextTime = DateAdd("h", 24, Now())
Application.OnTime nextTime, "SendReport"
End Sub
Sub SendReport()
' レポート生成処理
' Excelが起動していないと実行されない
End Sub
GAS の同等の実装:
// GAS: クラウド実行、24時間稼働
function createDailyReportTrigger() {
// 既存トリガーを削除(重複を防ぐ)
var triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
if (trigger.getHandlerFunction() === 'sendReport') {
ScriptApp.deleteTrigger(trigger);
}
});
// 毎日午前9時に実行するトリガーを作成
ScriptApp.newTrigger('sendReport')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
function sendReport() {
// レポート生成処理
// Google Cloudが常時稼働しているため、自動実行される
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName('Report');
// ... 処理 ...
}
VBAと異なり、GASではタイムゾーン指定(.inTimezone())が重要です。デフォルトではGoogle Cloudのサーバーのタイムゾーン(通常UTC)で実行されるため、日本時間で正確に実行させるには明示的にタイムゾーンを指定する必要があります。
VBAからの移行時、「OnTimeで毎時間実行」していたスクリプトをGASに移す際は、everyHours(1) で実装できます。ただし、GASの最小実行間隔は1分(推奨値)ですが、実際には15分~1時間単位での実行がシステム負荷の観点から推奨されています。秒単位の即座実行が必要な場合は、onEditなどのイベント駆動トリガーを検討してください。
時間指定・定期実行のパターン集
時間ベースのトリガーは、毎日・毎週・毎月など様々なパターンが設定可能です。実務で最も一般的なパターンをコード例とともに紹介します。
毎日午前9時
毎週月曜日午前10時
毎月1日午前9時
1時間おき(24回/日)
1分おき(推奨外)
毎日・毎週・毎月の基本パターン
// 毎日午前9時
function dailyAt9AM() {
ScriptApp.newTrigger('dailyTask')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
// 毎週月曜日午前10時
function weeklyMondayAt10AM() {
ScriptApp.newTrigger('weeklyTask')
.timeBased()
.atHour(10)
.everyWeeks(1)
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.inTimezone('Asia/Tokyo')
.create();
}
// 毎月15日午後3時
function monthlyOn15th() {
ScriptApp.newTrigger('monthlyTask')
.timeBased()
.atHour(15)
.onMonthDay(15)
.inTimezone('Asia/Tokyo')
.create();
}
// 毎日午前9時~10時の間(時間範囲指定)
function dailyRange() {
ScriptApp.newTrigger('dailyTask')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
分単位で正確に実行する方法
GAS のトリガーは GUI では1時間単位の時間範囲指定(午前9時~10時など)しかできませんが、スクリプトから設定すれば、より細かい制御が可能です。
// スクリプトからの実行時間微調整
// ただし、GAS の時間駆動トリガーは実際には 15分~1時間の誤差がある
function minutePrecisionApproach() {
// アプローチ1: 毎分チェック(推奨外:リソース浪費)
ScriptApp.newTrigger('checkAndExecute')
.timeBased()
.everyMinutes(1)
.inTimezone('Asia/Tokyo')
.create();
}
function checkAndExecute() {
var now = new Date();
var hours = now.getHours();
var minutes = now.getMinutes();
// 午前9時15分の場合のみ実行
if (hours === 9 && minutes === 15) {
myTaskFunction();
}
}
// アプローチ2: onEdit など、イベント駆動で秒単位の精度を実現
function onEdit(e) {
var now = new Date();
var hours = now.getHours();
var minutes = now.getMinutes();
var seconds = now.getSeconds();
// セルが編集されたら、その時刻で処理を実行
Logger.log('編集時刻: ' + hours + ':' + minutes + ':' + seconds);
}
注意点: GASの時間駆動トリガーは、Google Cloudのシステム負荷に応じて実行時刻が15分~1時間程度ずれることがあります。金融データの取得など、厳密な時刻が必要な場合は、イベント駆動トリガーまたは外部スケジューラ(例:Cloud Scheduler)の併用を検討してください。
平日のみ・曜日指定・月末実行
// 平日(月~金)のみ実行
function weekdaysOnly() {
ScriptApp.newTrigger('weekdayTask')
.timeBased()
.atHour(9)
.everyWeekDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
// 金曜日のみ午後5時
function fridayAt5PM() {
ScriptApp.newTrigger('fridayTask')
.timeBased()
.atHour(17)
.everyWeeks(1)
.onWeekDay(ScriptApp.WeekDay.FRIDAY)
.inTimezone('Asia/Tokyo')
.create();
}
// 月末実行(実装:毎日チェックして月末判定)
function createMonthEndTrigger() {
ScriptApp.newTrigger('checkMonthEnd')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
function checkMonthEnd() {
var now = new Date();
var nextDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
// 明日が1日(つまり今日が月末)ならば実行
if (nextDay.getDate() === 1) {
monthEndTask();
}
}
function monthEndTask() {
Logger.log('月末処理を実行します');
// 月次レポート生成など
}
Q: GASで1分おきにトリガーを設定できますか?
A: 理論上は可能ですが、推奨されません。1分おきのトリガーはリソースを消費し、Google APIの呼び出し上限に すぐに達してしまいます。実務では15分~1時間単位が推奨されます。リアルタイム処理が必要な場合は、Pub/Sub や Cloud Functions の使用を検討してください。
イベント駆動型トリガーの使い方
イベント駆動型トリガーは、ユーザーの操作またはGoogle Apps の変更を契機に自動実行されます。時間ベースより柔軟で、不要な処理実行を削減できるため、実務ではこちらが多く使われます。
onEdit / onChange / onFormSubmit の違い
| イベント | トリガー | 対象 | 説明 |
|---|---|---|---|
onEdit() | シンプル | スプレッドシート | ユーザーが手動でセルを編集した時のみ。スクリプトの自動変更は含まない。 |
onChange() | インストーラブル | スプレッドシート | セルの値が何らかの理由で変更された時。スクリプトの自動変更も含む。 |
onFormSubmit() | インストーラブル | フォーム | Googleフォームが送信された時。スプレッドシートへの自動記入が完了してから実行。 |
onOpen() | シンプル | スプレッドシート・ドキュメント | ファイルを開いた時。メニュー追加など初期化処理に使用。 |
onInstall() | シンプル | 全体 | スクリプトをインストール時に1度だけ実行。 |
// onEdit イベント(シンプルトリガー)
function onEdit(e) {
var range = e.range;
var sheet = e.source.getActiveSheet();
var value = e.value;
Logger.log('セル: ' + range.getA1Notation() + ' を編集しました');
Logger.log('値: ' + value);
// 編集されたシートが「入力」シートの場合のみ処理
if (sheet.getName() === '入力') {
// 自動計算など
}
}
// onChange イベント(インストーラブルトリガー)
function onFormSubmit(e) {
// フォーム送信時、自動的にスプレッドシートに記入される
// その後、このハンドラが実行される
var range = e.range;
var values = range.getValues();
var sheet = e.source.getActiveSheet();
Logger.log('フォーム送信されました。行数: ' + range.getNumRows());
// 送信内容に基づいて自動返信メール送信など
sendAutoReplyEmail(values);
}
特定のセル・シートだけに反応させる方法
// 特定のセル範囲(A1:C10)の編集のみを監視
function onEdit(e) {
var range = e.range;
var sheet = e.source.getActiveSheet();
// A1:C10 範囲内の編集のみ処理
if (sheet.getName() === 'Sheet1' &&
range.getRow() >= 1 && range.getRow() <= 10 &&
range.getColumn() >= 1 && range.getColumn() <= 3) {
Logger.log('監視対象のセルが編集されました');
// 処理を実行
}
}
// 特定のシートのみを監視
function onEdit(e) {
var sheet = e.source.getActiveSheet();
var sheetName = sheet.getName();
if (sheetName === '数据' || sheetName === 'Data') {
// 指定したシートが編集された場合のみ処理
var range = e.range;
Logger.log('Sheet ' + sheetName + ' のセル ' + range.getA1Notation() + ' が編集されました');
}
}
// 複数シート対応
function onEdit(e) {
var sheet = e.source.getActiveSheet();
var allowedSheets = ['入力', 'データ', '集計'];
if (allowedSheets.includes(sheet.getName())) {
processEdit(e);
}
}
function processEdit(e) {
var range = e.range;
var value = e.value;
Logger.log('編集: ' + value);
}
カレンダー更新トリガー
// Googleカレンダーの変更を監視するトリガーを作成
function createCalendarChangeTrigger() {
var calendar = CalendarApp.getCalendarById('カレンダーID@gmail.com');
ScriptApp.newTrigger('onCalendarChange')
.forUserCalendar('カレンダーID@gmail.com')
.onChange()
.create();
}
// カレンダー変更時に実行
function onCalendarChange(e) {
Logger.log('カレンダーが変更されました');
// 変更イベントの詳細情報は限定的(セキュリティ上の理由)
// 全イベントを取得して変更を検出する方法
var calendar = CalendarApp.getDefaultCalendar();
var events = calendar.getEventsForDay(new Date());
Logger.log('本日のイベント数: ' + events.length);
events.forEach(function(event) {
Logger.log('イベント: ' + event.getTitle() + ' (' + event.getStartTime() + ')');
});
}
カレンダートリガーはセキュリティ上、詳細な変更情報は提供されません。変更検出には前回保存したスナップショットとの比較が必要です。フォーム送信トリガーの実装例は「GASでGoogleフォーム連携する方法」、通知先としてSlack/LINEを使う方法は「GASでSlack・LINE自動通知」で詳しく解説しています。
トリガーの管理(確認・一時停止・削除)
トリガーは作成後、確認・一時停止・削除が必要な場合があります。特に長期間運用するスクリプトでは、定期的なメンテナンスが重要です。
設定済みトリガーの確認・一時停止方法
// 設定済みトリガーを一覧表示
function listAllTriggers() {
var triggers = ScriptApp.getProjectTriggers();
Logger.log('登録されているトリガー数: ' + triggers.length);
triggers.forEach(function(trigger, index) {
Logger.log('==== トリガー ' + (index + 1) + ' ====');
Logger.log('関数: ' + trigger.getHandlerFunction());
Logger.log('トリガータイプ: ' + trigger.getTriggerSourceId());
Logger.log('イベントタイプ: ' + trigger.getEventType());
Logger.log('トリガーID: ' + trigger.getUniqueId());
});
}
// 特定の関数に関連するトリガーを削除(一時停止)
function disableTriggerByFunction(functionName) {
var triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
if (trigger.getHandlerFunction() === functionName) {
ScriptApp.deleteTrigger(trigger);
Logger.log('トリガー「' + functionName + '」を削除しました');
}
});
}
// すべてのトリガーを一時停止
function disableAllTriggers() {
var triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
ScriptApp.deleteTrigger(trigger);
});
Logger.log('すべてのトリガーを削除しました');
}
注意: GASではトリガーの「一時停止」機能がないため、削除して後で再度作成する方法を使います。GUI からも同様に、トリガーパネルで該当トリガーの削除アイコン(ゴミ箱)をクリックします。
GUIとスクリプトからの削除方法
GUI からの削除:
- スクリプトエディタで「トリガー」(時計アイコン)をクリック
- 削除したいトリガーの行の右側にある「…」をクリック
- 「削除」を選択
- 確認ダイアログで「削除」をクリック
// スクリプトから特定トリガーを削除
function deleteTriggerById(triggerUniqueId) {
var triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
if (trigger.getUniqueId() === triggerUniqueId) {
ScriptApp.deleteTrigger(trigger);
Logger.log('トリガーID: ' + triggerUniqueId + ' を削除しました');
}
});
}
// すべてのトリガーを確認してから削除
function safeDeleteAllTriggers() {
var triggers = ScriptApp.getProjectTriggers();
if (triggers.length === 0) {
Logger.log('削除するトリガーがありません');
return;
}
Logger.log('削除対象: ' + triggers.length + ' 個のトリガー');
triggers.forEach(function(trigger) {
Logger.log('削除: 関数「' + trigger.getHandlerFunction() + '」のトリガー');
});
// メール送信などの確認を要する処理の場合はここで中断することも検討
triggers.forEach(function(trigger) {
ScriptApp.deleteTrigger(trigger);
});
}
退職・引き継ぎ時のオーナー変更
スクリプトオーナーが退職する場合、トリガーのオーナーを変更する必要があります。GASではオーナー変更が複雑なため、以下の手順が推奨されます。
- ステップ1: 新しいオーナー(引き継ぎ者)がスプレッドシートへアクセス権を取得
- ステップ2: 新しいオーナーが同じスクリプトをコピーして新しいプロジェクトとして再作成
- ステップ3: 新しいプロジェクトでトリガーを再設定
- ステップ4: テスト実行して動作確認
- ステップ5: 旧スクリプトのトリガーをすべて削除
// スクリプト引き継ぎ時の一括削除スクリプト
function transferOwnershipCleanup() {
// 旧オーナーがこのスクリプトを実行
var triggers = ScriptApp.getProjectTriggers();
Logger.log('引き継ぎ準備: ' + triggers.length + ' 個のトリガーを削除します');
triggers.forEach(function(trigger) {
Logger.log('削除対象: 関数「' + trigger.getHandlerFunction() + '」のトリガー');
ScriptApp.deleteTrigger(trigger);
});
Logger.log('すべてのトリガーを削除しました。新しいオーナーが新スクリプトでトリガーを再設定してください。');
}
引き継ぎ時の最大のトラブルは「古いトリガーが継続実行されて、新旧スクリプトが両方実行される」というケースです。必ず旧スクリプトのすべてのトリガーを削除してから新スクリプトに移行してください。Google Drive で旧スクリプトプロジェクトを削除するのが最も安全です。
トリガーの制限と注意点
GAS のトリガーには利用制限があり、これを超過するとエラーが発生します。実務では、これらの制限を考慮した設計が必須です。
上限数と実行時間制限
トリガー上限:
- 1プロジェクトあたり最大20個のトリガー
- 1アカウントあたり最大 4,000個のトリガー(全プロジェクト合計)
- 1日のトリガー実行回数制限: Free プランで 100回/日、Workspace プランで 1,000回/日以上
実行時間制限:
- 1トリガーあたり最大 6分(GAS スタンダード実行時間)
- 複数トリガーの総実行時間制限: 1日最大 1 時間(Free プラン)
- 時間駆動トリガーの実行遅延: 15分~1時間の幅が生じる
二重起動の防止方法
// トリガーの二重起動を防ぐパターン
// 方法1: グローバル変数とタイムスタンプを使用
var lastExecutionTime = 0;
function preventDuplicateExecution() {
var now = Date.now();
var minInterval = 60000; // 60秒以上の間隔を強制
if (now - lastExecutionTime < minInterval) {
Logger.log('直前の実行から60秒以内のため、スキップします');
return;
}
lastExecutionTime = now;
myTaskFunction();
}
// 方法2: PropertiesService を使用(推奨)
function preventDuplicateWithProperties() {
var scriptProperties = PropertiesService.getScriptProperties();
var lastTime = parseInt(scriptProperties.getProperty('lastExecutionTime') || '0');
var now = Date.now();
var minInterval = 60000;
if (now - lastTime < minInterval) {
Logger.log('二重実行の検出。スキップします。');
return;
}
scriptProperties.setProperty('lastExecutionTime', String(now));
myTaskFunction();
}
// 方法3: トリガーの前に重複チェック用ロックを実装
function lockBasedExecution() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(1000)) { // 1秒のタイムアウト
Logger.log('他のトリガーが実行中のため、スキップします');
return;
}
try {
myTaskFunction();
} finally {
lock.releaseLock();
}
}
function myTaskFunction() {
Logger.log('タスク実行');
// 実際の処理
}
権限とセキュリティの注意点
- 共有スプレッドシート: スプレッドシートの編集者が onEdit トリガーを実行できます。機密情報にアクセスするトリガーは、編集者の権限を制限してください
- 外部API利用: Slack・Line・外部APIを呼び出すトリガーの場合、APIキーなどの認証情報は絶対にスクリプト内に埋め込まないでください。PropertiesService や Secret Manager を使用してください
- ログ出力: Logger.log() に機密情報を出力しないでください。実行ログはプロジェクトの編集者すべてが見られます
- トリガーのオーナー権限: トリガーはオーナーの権限で実行されます。スプレッドシート共有時、他ユーザーが作成したトリガーはそのユーザーの権限で実行されるため、注意が必要です
よくあるエラーと対処法
トリガーの設定後、様々なエラーが発生する可能性があります。以下は実務で遭遇しやすいエラーと対処方法です。
トリガーが実行されない時のチェックリスト
- ✓ トリガーパネルでトリガーが登録されているか確認: スクリプトエディタ → トリガーメニューで確認
- ✓ 実行関数が存在するか確認: 関数名のタイポがないか、関数定義が正しいか確認
- ✓ タイムゾーン設定: inTimezone() で正しいタイムゾーン(Asia/Tokyo など)が指定されているか
- ✓ 権限確認: インストーラブルトリガーが権限承認されているか
- ✓ API呼び出し制限: 1日の実行回数制限に達していないか
- ✓ 実行時間制限: スクリプトが 6分以内に完了しているか
- ✓ onEdit時にスクリプト自身が編集していないか: 無限ループ防止
デバッグ手法: 実行ログを確認してください。スクリプトエディタの「実行ログ」メニュー、またはメニューの「表示」→「ログ」で、トリガーの実行履歴とエラーが表示されます。
トリガーが追加できない場合
エラー: 「トリガーを追加できません」
- 原因1: 1プロジェクトあたりの上限(20個)に達している → 不要なトリガーを削除
- 原因2: 1アカウント全体の上限(4,000個)に達している → 別のアカウントで新規プロジェクトを作成
- 原因3: スプレッドシートの権限がない → 所有者に確認
- 原因4: ブラウザのキャッシュ問題 → ブラウザを再起動、またはシークレットモードで試行
時間がずれる問題の解決策
// トリガー実行時間のズレ測定と補正
function createTimedTrigger() {
ScriptApp.newTrigger('timedTask')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
function timedTask() {
var now = new Date();
var expectedTime = 9 * 60; // 9時 = 540分
var actualTime = now.getHours() * 60 + now.getMinutes();
var drift = actualTime - expectedTime;
Logger.log('予定時刻: 09:00');
Logger.log('実際の実行時刻: ' +
String(Math.floor(actualTime / 60)).padStart(2, '0') + ':' +
String(actualTime % 60).padStart(2, '0'));
Logger.log('ズレ: ' + drift + '分');
if (Math.abs(drift) > 30) {
Logger.log('警告: 実行時刻が30分以上ずれています');
// アラート送信など
}
// 実際の処理
myTask();
}
// ズレ対策: 複数トリガーの分散実行
function createMultipleTriggers() {
// 同じ時刻に複数トリガーを作成すると、1つが失敗した場合に備えられる
for (var hour = 8; hour < 10; hour++) {
ScriptApp.newTrigger('timedTask')
.timeBased()
.atHour(hour)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
Logger.log('複数トリガーを作成しました(8時と9時)');
}
function myTask() {
Logger.log('実際の処理を実行');
}
時間ズレへの対処:
- 複数の時間帯にトリガーを設定して、分散実行させる
- Cloud Scheduler(Google Cloud Platform)を使用して、より正確なスケジューリングを行う
- 実行時刻の確認ログを記録して、実際の実行時刻を監視する
Q: トリガーが実行されない時はどうすればいい?
A: 以下の順序でチェックしてください。(1) スクリプトエディタのトリガーパネルでトリガーが登録されているか確認、(2) 実行ログを確認してエラーが記録されているか確認、(3) タイムゾーン設定が正しいか確認(Asia/Tokyo など)、(4) 権限が承認されているか確認。それでも解決しない場合は、トリガーを削除して再度作成してください。
実務ユースケース3選(VBA→GAS移行パターン)
VBAからGASへの移行を検討している方向けに、実務でよく見かけるユースケースを3つ紹介します。VBAのOnTimeやタスクスケジューラからの置き換え方が参考になるはずです。
月次レポート自動生成
// VBA では毎月1日午前9時にレポート生成(OnTime で毎日チェック)
// GAS では以下のように実装
function createMonthlyReportTrigger() {
ScriptApp.newTrigger('generateMonthlyReport')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
function generateMonthlyReport() {
var today = new Date();
// 月初(1日)のみ実行
if (today.getDate() !== 1) {
Logger.log('月初ではないため、レポート生成をスキップします');
return;
}
Logger.log('月次レポートを生成開始');
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName('データ');
var reportSheet = ss.getSheetByName('レポート');
// 前月のデータを集計
var lastMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1);
var monthLabel = Utilities.formatDate(lastMonth, 'Asia/Tokyo', 'yyyy年MM月');
// 集計処理
var data = dataSheet.getRange('A:D').getValues();
var totalAmount = 0;
data.forEach(function(row) {
if (row[0] && new Date(row[0]).getMonth() === lastMonth.getMonth()) {
totalAmount += row[3] || 0; // D列が金額
}
});
// レポートシートに記入
reportSheet.appendRow([monthLabel, totalAmount, new Date(), 'Generated by GAS']);
// 管理者にメール送信
GmailApp.sendEmail('admin@example.com',
monthLabel + ' レポートが生成されました',
'合計金額: ¥' + totalAmount + '\n自動生成タイムスタンプ: ' + new Date()
);
Logger.log('月次レポート生成完了');
}
フォーム回答の自動通知
// Googleフォーム送信時に、即座に回答を通知する
function createFormSubmitTrigger() {
var form = FormApp.openById('フォームID');
ScriptApp.newTrigger('onFormSubmitted')
.forForm(form)
.onFormSubmit()
.create();
}
function onFormSubmitted(e) {
Logger.log('フォームが送信されました');
var response = e.response;
var itemResponses = response.getItemResponses();
var message = '新しいフォーム回答があります\n\n';
var jsonData = {};
itemResponses.forEach(function(itemResponse) {
var question = itemResponse.getItem().getTitle();
var answer = itemResponse.getResponse();
message += question + ': ' + answer + '\n';
jsonData[question] = answer;
});
// Slack に通知(Incoming Webhook を使用)
var webhookUrl = 'YOUR_SLACK_WEBHOOK_URL';
var payload = {
text: 'フォーム回答通知',
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: '*新しいフォーム回答*\n\n' + message
}
}
]
};
UrlFetchApp.fetch(webhookUrl, {
method: 'post',
payload: JSON.stringify(payload),
contentType: 'application/json'
});
// CSV形式でスプレッドシートに記録
var spreadsheet = SpreadsheetApp.openById('スプレッドシートID');
var sheet = spreadsheet.getSheetByName('回答');
sheet.appendRow([
new Date(),
...itemResponses.map(function(r) { return r.getResponse(); })
]);
}
日次データ取得と集計の自動化
// 毎日午前9時に外部APIからデータを取得して集計
function createDailyAggregationTrigger() {
ScriptApp.newTrigger('dailyDataAggregation')
.timeBased()
.atHour(9)
.everyDays(1)
.inTimezone('Asia/Tokyo')
.create();
}
function dailyDataAggregation() {
Logger.log('日次データ集計を開始');
var apiUrl = 'https://api.example.com/data';
var apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');
try {
// 外部APIからデータ取得
var response = UrlFetchApp.fetch(apiUrl, {
headers: {
'Authorization': 'Bearer ' + apiKey
},
muteHttpExceptions: true
});
if (response.getResponseCode() !== 200) {
throw new Error('API呼び出しエラー: ' + response.getResponseCode());
}
var data = JSON.parse(response.getContentText());
// スプレッドシートに追記
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('日次データ');
data.records.forEach(function(record) {
sheet.appendRow([
new Date(),
record.id,
record.value,
record.status
]);
});
// データサイズが1000行を超えたら、古いデータを削除
var rows = sheet.getDataRange().getNumRows();
if (rows > 1000) {
sheet.deleteRows(2, rows - 1000);
Logger.log('古いデータを削除しました');
}
Logger.log('日次データ集計完了: ' + data.records.length + '件を追加');
} catch (error) {
Logger.log('エラー: ' + error.toString());
// エラー通知
GmailApp.sendEmail('admin@example.com',
'【警告】日次データ集計に失敗しました',
'エラー内容: ' + error.toString()
);
}
}
これら3つのユースケースは、VBAのOnTimeやタスクスケジューラから直接置き換え可能な典型的なパターンです。より詳しい実装方法については、「GASでGmailを自動送信する方法」「GASでSlack・LINE自動通知」「GAS×ChatGPT連携ガイド」も合わせてご確認ください。
よくある質問(FAQ)
Q1: GASのトリガーはいくつまで設定できますか?
1プロジェクトあたり最大20個、1アカウント全体で最大4,000個のトリガーが設定可能です。実務上は10~15個程度が推奨されます。
Q2: GASで1分おきにトリガーを設定できますか?
理論上は可能ですが、リソース消費が多く、API呼び出し上限に達しやすいため推奨されません。15分~1時間単位が推奨です。
Q3: GASのトリガーの編集時(onEdit)と変更時(onChange)の違いは何ですか?
onEdit はユーザーの手動編集のみ、onChange はスクリプト自身による変更も含みます。
Q4: GoogleフォームでGASをトリガーするには?
ScriptApp.newTrigger('関数名').forForm(form).onFormSubmit().create() で実装できます。
Q5: トリガーが実行されない時はどうすればいい?
トリガーパネルで登録確認、実行ログ確認、タイムゾーン確認、権限確認の順でチェックしてください。
Q6: VBAのOnTimeとGASトリガーの違いは何ですか?
VBAはローカルPC起動必須、GASはクラウド常時稼働、イベント駆動対応、タイムゾーン管理など大きな違いがあります。
Q7: トリガーのオーナーが退職したらどうなる?
トリガーはオーナーの権限で実行されるため、スクリプトをコピーして新しいオーナーで再セットアップする必要があります。
まとめ
GASのトリガーは、VBA OnTimeやWindows タスクスケジューラを大きく上回る柔軟性と信頼性を持つ自動化機能です。シンプルトリガー(onEdit、onOpen など)とインストーラブルトリガー(ScriptApp.newTrigger)の両方を組み合わせることで、時間ベース・イベントベース両方の自動化が可能になります。
実務では以下の3つのポイントが重要です。
- 正確なタイムゾーン設定: inTimezone('Asia/Tokyo') を忘れずに指定
- 権限とセキュリティの確認: 外部API利用時は PropertiesService で認証情報を管理
- 定期的なメンテナンス: トリガーの実行ログを確認し、エラー発生時は即座に対応
VBAからの移行を検討している方は、本記事で紹介した「月次レポート自動生成」「フォーム回答通知」「日次データ集計」のユースケースを参考に、まずは1つのトリガーから試してみることをお勧めします。より詳しい実装方法については、「GAS入門完全ガイド」をご覧ください。
LINEでExcelを気軽に学べる
