【無料配布】その作業、Google Apps Scriptなら自動化できます。――教員のための「GAS入門」と、すぐ使えるツール配布
はじめに
毎朝、職員室にかかってくる保護者からの欠席連絡の電話。担当クラスの担任がいなければメモを取り、該当担任に伝え、出欠簿に転記し、出席停止関係であれば養護教諭にも共有する。
1件あたりは数分でも、朝の忙しい時間帯に何件も重なると、それだけで始業前の貴重な時間が消えていきます。
成績の転記、アンケートの集計、提出物の催促——学校現場には、「手作業で繰り返している業務」が驚くほどたくさんあります。
もし、それらの作業を自動でやってくれる仕組みがあったら。しかも、新しいソフトを買う必要もなく、今すでに使っているGoogleのツールだけで実現できるとしたら。
この記事では、その仕組みを可能にする Google Apps Script(GAS) について紹介します。
GASを使いこなせるようになると、効率化の世界が一気に変わります。
ぜひ一度体験していただきたいです。
記事の後半では、ツール配布の第一歩として、実際にコピー&ペーストだけで使える「保護者からの欠席・遅刻連絡を自動化するツール」も無料で配布しています。
昨今では一定の自動化がされている学校も多いと思いますが、保護者からの連絡対応はすべての教員が経験すると思いますので、第一歩としてはイメージを持っていただきやすいかと思います。
Google Apps Script(GAS)とは何か
Google Apps Script(通称GAS、「ガス」と読みます)は、Googleが無料で提供しているプログラミング環境です。
そう聞くと「プログラミングなんて自分には無理だ」と感じるかもしれません。でも、安心してください。この記事で配布しているツールは、コードをコピーして貼り付け、ボタンを1回押すだけで動きます。プログラミングの知識は必要ありません。
GASのすごいところは、普段使っているGoogleのサービスをまたいで自動化できる点にあります。
Googleフォームに入力があったら、スプレッドシートに自動で集計する。集計結果をもとに、Gmailで関係者に自動で通知する。Googleカレンダーに予定を自動で登録する。Googleドキュメントに報告書を自動で生成する。
これらはすべて、GASひとつで実現できます。フォーム、スプレッドシート、Gmail、カレンダー、ドキュメント、ドライブ——Googleの主要なサービスをすべてつなげて、「入力→集計→通知」「入力→記録→共有」といった一連の流れをまるごと自動化できるのがGASの最大の強みです。
しかも、GASはGoogleアカウントさえあれば無料で使えます。新しいソフトをインストールする必要もなく、ブラウザだけで動きます。

GASが使える条件:学校のGoogleアカウントを確認しよう
GASを使うために必要なのは、Googleアカウントだけです。個人の @gmail.com アカウントでも動きますが、学校現場で運用する場合は Google Workspace for Education(旧G Suite for Education)の環境が整っているかどうかがポイントになります。
多くの自治体や学校では、GIGAスクール構想に伴って独自のGoogleアカウント(例えば @city-edu.jp や @school.ed.jp のようなドメイン)が配布されています。このアカウントが使える環境であれば、GASはすぐに利用可能です。
Google Workspace for Education の環境があると、何が良いのか。生徒全員がGoogleアカウントを持っているため、フォームの回答者を学内に限定できます。教員間でのスプレッドシートやスクリプトの共有が簡単に行えます。そして、組織のドメイン内で安全にデータを管理できます。
つまり、GIGAスクール構想ですでに整備された環境が、そのままGASによる業務自動化の土台になります。追加コストはゼロです。
自校でGoogle Workspace for Educationが導入されているかどうかわからない場合は、情報担当の先生やICT支援員の方に確認してみてください。「自分たちの学校のメールアドレスが @gmail.com 以外の独自ドメインかどうか」が一番簡単な判断基準です。
GASで広がる自動化の世界
GASを使えば、学校現場の「手作業で繰り返している業務」の多くを自動化できます。いくつか例を挙げてみます。
出欠・連絡管理。 保護者からの欠席連絡をフォームで受け付け、担任に自動通知し、一覧をスプレッドシートに集計する。電話対応とメモ転記がなくなります。
アンケート集計。 学校評価アンケートや授業アンケートの回答を、クラス別・項目別に自動集計してグラフ化する。手作業での集計作業が不要になります。
成績処理。 到達度テストの自己採点入力をフォームで受け付け、クラス別平均点を自動算出し、未入力者リストを毎朝メールで通知する。
備品管理。 赤本や教材の貸し出しをフォームで記録し、返却期限が近づいたら自動でリマインドメールを送る。
面談予約。 保護者面談の希望日時をフォームで受け付け、重複をチェックし、確定通知を自動送信する。
これらに共通しているのは、「フォームで入力→スプレッドシートに集計→メールやカレンダーで通知・共有」という基本フローです。GASは、このフローを一度組んでしまえば、あとは自動で動き続けます。
ツールの準備:マスタデータを整備しよう
自動化ツールを運用するうえで、最初に用意しておくと便利なのが「マスタデータ」です。具体的には、生徒のアカウント情報(メールアドレス・クラス・出席番号・氏名)と、教員のアカウント情報(担当クラス・氏名・メールアドレス)の一覧です。
これらのデータがスプレッドシートにまとまっていれば、フォームの入力内容をメールアドレスで照合して正しい情報に補正したり、回答者のクラスに応じて担任に自動通知を振り分けたりといった処理が可能になります。
生徒や教員のGoogleアカウント一覧は、Google Workspace for Educationの管理者(多くの場合、情報部や情報担当の先生)が管理しています。マスタデータの準備を始める際は、まず情報担当の先生に「生徒と教員のアカウント一覧をスプレッドシートで共有していただけませんか」と相談してみてください。すでにCSVなどで管理されていることも多いので、意外とすぐに手に入る場合があります。
実践例:保護者からの欠席・遅刻連絡を自動化する
ここからは、実際に動くツールを使って、GASによる自動化を体験してみましょう。
今回の例は「保護者からの欠席・遅刻連絡の自動化」です。保護者がスマートフォンからフォームに入力すると、スプレッドシートに自動集計され、該当クラスの担任にメールで即時通知が届きます。さらに、毎朝指定時刻に、その日の欠席状況をまとめたレポートが管理者に自動送信されます。
このツールの仕組み
このツールは3つの要素だけで動きます。
Googleフォームが保護者からの入力窓口です。クラス・生徒名・欠席か遅刻か出席停止か・理由を選択して送信するだけ。このフォーム自体もGASが自動で作成します。
Googleスプレッドシートがデータの保管と集計を担います。フォームの回答が自動で蓄積され、本日の欠席状況が見やすく整理されます。
**Google Apps Script(GAS)**が全体の司令塔です。フォーム送信をきっかけに担任への通知メールを送り、毎朝のレポートも自動で生成・送信します。さらに、保護者が連絡種別で「出席停止」を選んだ場合は、担任だけでなく養護教諭にも自動で通知が届きます。感染症対応など、迅速な情報共有が求められる場面で特に威力を発揮します。
導入手順
Step 1:スプレッドシートを準備する
Googleドライブに新しいスプレッドシートを作成し、名前を「欠席連絡管理」としてください。

このスプレッドシートに「担任一覧」という名前のシートを作り、以下の形式で各クラスの担任情報を入力します。A列にクラス番号、B列に担任名、C列にメールアドレスを入れてください。

Step 2:コードを貼り付ける
スプレッドシートの上部メニューから「拡張機能」→「Apps Script」を開きます。

エディタが開いたら、最初から書いてあるコードをすべて削除し、以下のコードを貼り付けて保存してください。


以下コード(そのままコピペしてください)
// ==============================================
// 設定値(ここだけ自校に合わせて変更してください)
// ==============================================
const CONFIG = {
FORM_TITLE: "欠席・遅刻連絡フォーム",
HOMEROOM_SHEET_NAME: "担任一覧",
RAW_SHEET_NAME: "フォームの回答",
SUMMARY_SHEET_NAME: "本日の欠席状況",
ADMIN_EMAIL: "admin@example.com", // 毎朝のレポート送信先
NURSE_EMAIL: "nurse@example.com", // 養護教諭のメールアドレス
CLASS_MIN: 1,
CLASS_MAX: 9, // クラス数に合わせて変更
REPORT_HOUR: 7 // レポート送信時刻(24時間表記)
};
// ==============================================
// 1. フォーム自動生成
// 最初に1回だけ実行してください
// ==============================================
function createAbsenceForm() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const form = FormApp.create(CONFIG.FORM_TITLE);
form.setDescription(
"お子様の欠席・遅刻についてご連絡ください。\n" +
"送信いただいた内容は担任に自動で通知されます。"
)
.setConfirmationMessage("ご連絡ありがとうございます。担任に通知されました。")
.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
// ① 連絡日
form.addDateItem()
.setTitle("連絡日")
.setRequired(true);
// ② クラス
const classChoices = [];
for (let i = CONFIG.CLASS_MIN; i <= CONFIG.CLASS_MAX; i++) {
classChoices.push(String(i));
}
form.addListItem()
.setTitle("クラス")
.setChoiceValues(classChoices)
.setRequired(true);
// ③ 生徒氏名
form.addTextItem()
.setTitle("生徒氏名")
.setRequired(true);
// ④ 連絡種別
form.addMultipleChoiceItem()
.setTitle("連絡種別")
.setChoiceValues(["欠席", "遅刻", "出席停止", "その他"])
.setRequired(true);
// ⑤ 理由
form.addMultipleChoiceItem()
.setTitle("理由")
.setChoiceValues(["体調不良", "通院・検査", "家庭の事情", "忌引", "その他"])
.setRequired(true);
// ⑥ 補足(任意)
form.addParagraphTextItem()
.setTitle("補足事項(任意)")
.setRequired(false);
// ⑦ 保護者氏名
form.addTextItem()
.setTitle("保護者氏名")
.setRequired(true);
// 回答シート名を整える
SpreadsheetApp.flush();
Utilities.sleep(3000);
const sheets = ss.getSheets();
for (const sheet of sheets) {
if (sheet.getName() !== CONFIG.HOMEROOM_SHEET_NAME &&
sheet.getFormUrl()) {
sheet.setName(CONFIG.RAW_SHEET_NAME);
break;
}
}
ensureSheet_(ss, CONFIG.SUMMARY_SHEET_NAME);
SpreadsheetApp.getUi().alert(
"フォーム作成完了!\n\n" +
"保護者配布用URL:\n" + form.getPublishedUrl() + "\n\n" +
"次に setupTriggers() を実行してください。"
);
}
// ==============================================
// 2. トリガー設定
// createAbsenceForm の後に1回だけ実行
// ==============================================
function setupTriggers() {
ScriptApp.getProjectTriggers().forEach(t => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger("onAbsenceSubmit")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit()
.create();
ScriptApp.newTrigger("sendMorningReport")
.timeBased()
.everyDays(1)
.atHour(CONFIG.REPORT_HOUR)
.nearMinute(0)
.create();
SpreadsheetApp.getUi().alert(
"トリガー設定完了!\n" +
"・フォーム送信時 → 担任にメール通知\n" +
"・フォーム送信時(出席停止の場合)→ 養護教諭にもメール通知\n" +
"・毎朝" + CONFIG.REPORT_HOUR + "時 → 管理者にレポート送信"
);
}
// ==============================================
// 3. フォーム送信時:担任への即時通知
// 出席停止の場合は養護教諭にも自動通知
// ==============================================
function onAbsenceSubmit(e) {
const response = e.namedValues;
const cls = response["クラス"][0];
const studentName = response["生徒氏名"][0];
const type = response["連絡種別"][0];
const reason = response["理由"][0];
const note = response["補足事項(任意)"] ? response["補足事項(任意)"][0] : "";
const parentName = response["保護者氏名"][0];
const contactDate = response["連絡日"][0];
// --- メール本文を作成 ---
const subject = "【" + type + "連絡】" + cls + "組 " + studentName;
let body = "";
body += "━━━━━━━━━━━━━━━━━━━━\n";
body += " " + type + "連絡が届きました\n";
body += "━━━━━━━━━━━━━━━━━━━━\n\n";
body += "■ 連絡日:" + contactDate + "\n";
body += "■ クラス:" + cls + "組\n";
body += "■ 生徒氏名:" + studentName + "\n";
body += "■ 種別:" + type + "\n";
body += "■ 理由:" + reason + "\n";
if (note) body += "■ 補足:" + note + "\n";
body += "■ 保護者氏名:" + parentName + "\n";
body += "\n────────────────────\n";
body += "※このメールはフォーム送信により自動通知されています。\n";
body += "詳細はスプレッドシートをご確認ください。\n";
body += SpreadsheetApp.getActiveSpreadsheet().getUrl();
// --- ① 担任に通知 ---
const teacherEmail = getTeacherEmail_(cls);
if (teacherEmail) {
GmailApp.sendEmail(teacherEmail, subject, body);
}
// --- ② 出席停止の場合は養護教諭にも通知 ---
if (type === "出席停止") {
const nurseSubject = "【出席停止連絡】" + cls + "組 " + studentName + "(養護教諭通知)";
let nurseBody = "";
nurseBody += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
nurseBody += " 出席停止の連絡が届きました(養護教諭用)\n";
nurseBody += "━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
nurseBody += "以下の生徒について、保護者より出席停止の連絡がありました。\n";
nurseBody += "必要に応じてご確認・ご対応をお願いいたします。\n\n";
nurseBody += "■ 連絡日:" + contactDate + "\n";
nurseBody += "■ クラス:" + cls + "組\n";
nurseBody += "■ 生徒氏名:" + studentName + "\n";
nurseBody += "■ 理由:" + reason + "\n";
if (note) nurseBody += "■ 補足:" + note + "\n";
nurseBody += "■ 保護者氏名:" + parentName + "\n";
nurseBody += "\n────────────────────\n";
nurseBody += "※このメールはフォーム送信により自動通知されています。\n";
nurseBody += "詳細はスプレッドシートをご確認ください。\n";
nurseBody += SpreadsheetApp.getActiveSpreadsheet().getUrl();
GmailApp.sendEmail(CONFIG.NURSE_EMAIL, nurseSubject, nurseBody);
}
// --- 集計シートも更新 ---
refreshTodaySummary_();
}
// ==============================================
// 4. 毎朝のレポート送信
// ==============================================
function sendMorningReport() {
refreshTodaySummary_();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const rawSheet = ss.getSheetByName(CONFIG.RAW_SHEET_NAME);
if (!rawSheet || rawSheet.getLastRow() < 2) return;
const today = new Date();
const todayStr = Utilities.formatDate(today, "Asia/Tokyo", "yyyy/MM/dd");
const data = rawSheet.getDataRange().getValues();
const todayRecords = {};
for (let i = 1; i < data.length; i++) {
const contactDate = Utilities.formatDate(new Date(data[i][1]), "Asia/Tokyo", "yyyy/MM/dd");
if (contactDate === todayStr) {
const cls = String(data[i][2]);
if (!todayRecords[cls]) todayRecords[cls] = [];
todayRecords[cls].push({
name: data[i][3],
type: data[i][4],
reason: data[i][5]
});
}
}
let html = "<h2>本日の欠席・遅刻状況(" + todayStr + ")</h2>";
let totalCount = 0;
for (let c = CONFIG.CLASS_MIN; c <= CONFIG.CLASS_MAX; c++) {
const cls = String(c);
html += "<h3>" + cls + "組</h3>";
if (todayRecords[cls] && todayRecords[cls].length > 0) {
html += "<table border='1' cellpadding='5' cellspacing='0' style='border-collapse:collapse;'>";
html += "<tr style='background:#4472C4;color:#fff;'><th>生徒名</th><th>種別</th><th>理由</th></tr>";
for (const r of todayRecords[cls]) {
html += "<tr><td>" + r.name + "</td><td>" + r.type + "</td><td>" + r.reason + "</td></tr>";
totalCount++;
}
html += "</table>";
} else {
html += "<p style='color:green;'>連絡なし</p>";
}
}
html += "<hr><p><strong>合計:" + totalCount + "件</strong></p>";
html += "<p style='font-size:small;color:gray;'>このメールは自動送信されています。<br>";
html += "<a href='" + ss.getUrl() + "'>スプレッドシートを開く</a></p>";
GmailApp.sendEmail(
CONFIG.ADMIN_EMAIL,
"【欠席状況】" + todayStr + "(" + totalCount + "件)",
"HTMLメールに対応したクライアントでご覧ください。",
{ htmlBody: html }
);
}
// ==============================================
// 本日の集計シート更新
// ==============================================
function refreshTodaySummary_() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const rawSheet = ss.getSheetByName(CONFIG.RAW_SHEET_NAME);
const summarySheet = ss.getSheetByName(CONFIG.SUMMARY_SHEET_NAME) || ss.insertSheet(CONFIG.SUMMARY_SHEET_NAME);
if (!rawSheet || rawSheet.getLastRow() < 2) return;
const today = new Date();
const todayStr = Utilities.formatDate(today, "Asia/Tokyo", "yyyy/MM/dd");
const data = rawSheet.getDataRange().getValues();
summarySheet.clearContents();
let r = 1;
summarySheet.getRange(r, 1).setValue("本日の欠席・遅刻状況(" + todayStr + ")")
.setFontWeight("bold").setFontSize(13);
r += 2;
const header = ["クラス", "生徒氏名", "種別", "理由", "補足", "保護者氏名"];
summarySheet.getRange(r, 1, 1, header.length).setValues([header])
.setBackground("#4472C4").setFontColor("#FFFFFF").setFontWeight("bold");
r++;
let count = 0;
for (let i = 1; i < data.length; i++) {
const contactDate = Utilities.formatDate(new Date(data[i][1]), "Asia/Tokyo", "yyyy/MM/dd");
if (contactDate === todayStr) {
summarySheet.getRange(r, 1, 1, 6).setValues([[
data[i][2] + "組", data[i][3], data[i][4], data[i][5], data[i][6] || "", data[i][7]
]]);
r++;
count++;
}
}
r++;
summarySheet.getRange(r, 1).setValue("合計:" + count + "件").setFontWeight("bold");
const widths = [80, 120, 80, 120, 200, 120];
widths.forEach((w, idx) => summarySheet.setColumnWidth(idx + 1, w));
summarySheet.setFrozenRows(3);
}
// ==============================================
// ユーティリティ
// ==============================================
function getTeacherEmail_(cls) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(CONFIG.HOMEROOM_SHEET_NAME);
if (!sheet) return null;
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (String(data[i][0]) === String(cls)) return String(data[i][2]);
}
return null;
}
function ensureSheet_(ss, sheetName) {
let sheet = ss.getSheetByName(sheetName);
if (!sheet) sheet = ss.insertSheet(sheetName);
return sheet;
}
Step 3:フォームを自動生成する
Apps Scriptエディタ上部の関数プルダウンから「createAbsenceForm」を選び、「▶ 実行」をクリックします。
初回は「承認が必要です」というダイアログが出ます。「権限を確認」→ アカウント選択 →「許可」と進めてください。GASがフォーム作成やメール送信を行うための正常な手順です。

実行が完了すると、保護者に配布するフォームのURLが表示されます。


ここで注目していただきたいのは、フォームそのものをGASが自動で作ったという点です。質問項目、選択肢、必須設定——すべてコードに書かれた通りに、ボタンひとつで生成されます。フォームを手で作る手間すら、GASは自動化してくれます。
Step 4:自動通知を設定する
同じくApps Scriptエディタで、関数プルダウンから「setupTriggers」を選んで実行します。
これで設定は完了です。保護者がフォームを送信するたびに担任へ即時メールが届き、毎朝指定時刻にその日の欠席状況レポートが管理者に届く仕組みが動き始めます。


Step 5:動作を確認する
フォームを開いてテストデータを1件送信してみてください。



スプレッドシートにデータが入り、集計シートに本日分が表示され、担任のメールアドレスに通知が届いていれば成功です。
自校に合わせるには
コード冒頭の設定値ブロックだけを変更すれば対応できます。CLASS_MAXをクラス数に、ADMIN_EMAILをレポート送信先に、NURSE_EMAILを養護教諭のメールアドレスに、REPORT_HOURを希望の時刻に書き換えてください。それ以外のコードは一切触る必要はありません。このフローは、あらゆる業務に応用できます
今回は欠席連絡を例にしましたが
「フォームで入力→スプレッドシートに集計→メールで通知」という基本フローは、そのまま他の業務にも転用できます。
行事参加の出欠確認。授業アンケートや学校評価の集計。到達度テストの自己採点入力と未入力者への催促。備品や書籍の貸し出し管理。面談予約の受付と確定通知。
フォームの質問項目と集計のロジックを差し替えるだけで、同じ仕組みがそのまま使えます。「こんな業務も自動化できる?」という疑問があれば、ぜひnoteのコメント欄やXアカウント(@FOS_TTlabo)までお声がけください。一緒に考えます。
おわりに
GASは、特別なスキルがなくても、今ある環境だけで、学校現場の「繰り返しの手作業」を自動化できる仕組みです。この記事で配布したツールも、コピー&ペーストだけで動くように作っています。
自動化で生まれた時間は、教室で子どもたちと向き合う時間に変わります。
先生のじかんラボでは、こうした現場発のツールを教員チームで開発し、無料で公開していきます。同じ想いを持つ先生方の参加も、現場の困りごとのご相談も、いつでもお待ちしています。
