毎月の定例会議の前に、スプレッドシートの集計表を見ながらGoogleスライドの数字を打ち替えていませんか。先月のファイルをコピーして、日付を直し、グラフを貼り直す。地味なのに毎月きっちり時間を取られる作業ですよね。
手作業の貼り替えでは、前月の数字の消し忘れや桁の打ち間違いが起きがち。会議の直前に気づいて慌てた経験がある方も多いはずです。
GAS(Google Apps Script)を使えば、スプレッドシートの数字をスライドの決まった位置へ自動で入れられます。リンクしたグラフの最新化や、毎月1日の自動作成まで任せることも可能です。
この記事では、GASでスライドを自動生成するコピペ用のコードを、2つのシナリオに分けて紹介します。月次報告の数字の差し替えと、表・グラフの自動更新の2つです。うまく動かないときの切り分けもまとめたので、手順どおりに進めてみてください。
対応バージョン: Google Apps Script(V8ランタイム)。個人のGoogleアカウントでもGoogle Workspaceでも使えます(1日あたりの割り当ては異なります)。Googleスライド・スプレッドシートはブラウザ版で操作する前提です。
GASでスプレッドシートからスライドを自動生成する仕組み
まずは全体の流れをつかんでおきましょう。コードを読む前に仕組みがわかっていると、つまずいたときに原因を探しやすくなります。
全体像:テンプレ複製→置換→グラフ更新の3段
GASでのスライド自動生成は、ひな形のスライドを毎月コピーして中身を入れ替える方式です。ゼロからスライドを組み立てるわけではありません。
やることは次の3段だけです。
- テンプレートのスライドを複製する(
DriveAppのmakeCopy) - 目印の文字を数字に置き換える(
replaceAllText) - リンクしたグラフを最新にする(
SheetsChartのrefresh)
テンプレートには {{売上}} のような目印を書いておきます。書き換えるのは複製したファイルだけなので、ひな形は何度でも使い回せます。デザインはいつものスライドのまま、数字だけが毎月入れ替わるイメージですね。
毎月同じ型の資料を作る作業は、GASが得意とする典型的な業務。向き不向きの判断に迷ったら、GASが向く業務・向かない業務の見分け方も参考にしてください。
なお、Geminiに指示してスライドを作るAI機能とは別物です。AIでの作成はGemini in Google スライドの使い方で解説しています。この記事は、決まった型に正確な数字を流し込む用途向けです。
Docs版(差し込み)との違い
「テンプレートを複製して目印を置換する」という考え方は、Googleドキュメントの差し込み生成と共通です。ドキュメント版はGASでGoogleドキュメントを差し込み生成する方法で紹介しました。
スライド版で変わるのは、主に次の3点です。
- 置換には
Presentation.replaceAllTextを使う。プレゼン全体をまとめて置換でき、置き換えた件数が数値で返る - スプレッドシートにリンクしたグラフを、コードから最新の状態に置き換えられる
- 表の中身は、セルを1つずつ指定して書き込む
特に2つ目のグラフ更新は、スライドならではの機能。ドキュメント版を試したことがある方は、差分だけ読めば十分ですよ。
対応環境と前提
この記事のコードは、スプレッドシートにひも付いたスクリプトに書く前提です。スプレッドシートを開き、上部メニューの[拡張機能]→[Apps Script]からエディタを開いてください。
スライドは SlidesApp.openById でIDを指定して開きます。getActivePresentation というメソッドもありますが、スライド側にひも付いたスクリプト向けのもの。スプレッドシート側から操作するこの記事では使いません。
初回の実行時には、ドライブ・スライド・スプレッドシートへのアクセス許可を求める画面が出ます。GASを初めて触る方は、GAS入門|コピペで動くスプレッドシート自動化レシピ5選で一度動かしておくと安心です。
準備|テンプレートスライドとデータシートの作り方
自動化がうまくいくかどうかは、コードよりも準備で決まります。ここでテンプレートとデータシートの形をそろえておきましょう。
{{変数名}}の設計表
テンプレートのスライドには、数字を入れたい位置に {{変数名}} の形で目印を書きます。たとえば売上の数字を入れるテキストボックスには「今月の売上:{{売上}}」と入力しておくわけです。
スプレッドシートには「月次データ」という名前のシートを作ります。A列に変数名(波かっこなし)、B列に値を入れる縦長の形。1行目は見出しにして、2行目からデータを書きます。
| テンプレートに書く目印 | A列(変数名) | B列(値の例) |
|---|---|---|
{{対象月}} | 対象月 | 2026年9月 |
{{売上}} | 売上 | 12,480,000円 |
{{前月比}} | 前月比 | 104.2% |
{{新規件数}} | 新規件数 | 38件 |
{{担当コメント}} | 担当コメント | 大口案件の受注で増収 |
B列の値は、セルに表示されている文字のまま取り込みます。桁区切りやパーセントの表示形式は、スプレッドシート側で整えておくのがコツ。B列を集計シートへの参照式にしておけば、毎月の入力も最小限で済みます。
目印を決めるときのルールは次の4つです。
- 波かっこは半角で2つずつ(
{{と}})。全角の「{{」は別の文字として扱われる - 変数名の前後や途中にスペースを入れない
- A列の変数名とテンプレートの目印の中身を、1文字も違わずそろえる
- 作成履歴用に「作成履歴」シートも作り、1行目に「作成日時・対象月・URL」と見出しを入れる
波かっこで囲むのは、目印を本文の文字と区別するためです。{{売上}} と {{売上目標}} のように似た変数名があっても、閉じかっこまで含めて探すので取り違えません。
テキスト置換と表・グラフの扱いの違い(早見表)
スライドに入れたいものの種類によって、テンプレートの準備と使うメソッドが変わります。
| 入れたいもの | テンプレート側の準備 | 使うメソッド | 注意点 |
|---|---|---|---|
| 数字・文字 | テキストボックスに {{変数名}} | Presentation.replaceAllText | 戻り値の件数で置換の成否を確認 |
| 表の中身 | 空の表を置いておく | Table.getCell().getText().setText() | 結合セルの先頭以外は getText が null になる |
| リンク済みグラフ | スプレッドシートからリンクして挿入 | Slide.getSheetsCharts() → refresh() | スプレッドシート閲覧の権限承認が必要 |
| 新しく入れるグラフ | 不要 | Slide.insertSheetsChart() | 実行するたびにグラフが増える |
| 画像として入れるグラフ | 不要 | Slide.insertSheetsChartAsImage() | 戻り値はグラフではなく画像(Image) |
表の中にも目印を置きたくなるところですよね。ただ、Presentation.replaceAllText が表の中まで置換するかは、公式リファレンスで確認できませんでした。
そのためこの記事では、表はセルへ直接書き込む方法に分けています。数字はテキストボックス、まとまった一覧は表と覚えておくとよいですよ。
シナリオ1 毎月1日に月次報告スライドを自動で差し替える
ここからは、月次報告スライドを毎月自動で作るコードです。関数を手順ごとに1つずつ、同じエディタに貼り付けていけば完成します。
手順1 スプレッドシートから数字を取得する
最初に、設定値と「月次データ」シートを読み込む関数を貼り付けます。冒頭の4行は、自分の環境に合わせて書き換えてください。
// ===== 設定(ここだけ書き換える) =====
const TEMPLATE_ID = 'テンプレートスライドのID';
const FOLDER_ID = '保存先フォルダのID';
const SHEET_NAME = '月次データ';
const LOG_SHEET_NAME = '作成履歴';
/**
* 「月次データ」シートのA列(変数名)とB列(値)を読み込み、
* { 売上: '12,480,000円', ... } の形で返す
*/
function getReportData(sheet) {
if (!sheet) {
throw new Error('シート「' + SHEET_NAME + '」が見つかりません');
}
const lastRow = sheet.getLastRow();
if (lastRow < 2) {
throw new Error('月次データが入力されていません');
}
// 2行目からA〜B列を、表示されている文字のまま取得する
const values = sheet.getRange(2, 1, lastRow - 1, 2).getDisplayValues();
const data = {};
values.forEach(function (row) {
const key = row[0].trim();
if (key) {
data[key] = row[1];
}
});
return data;
}
テンプレートのIDは、スライドのURLの /presentation/d/ と /edit の間にある文字列です。フォルダのIDは、ドライブでフォルダを開いたときのURLの /folders/ より後ろの部分。どちらもURLからコピーして貼り付けましょう。
getDisplayValues を使っているのは、桁区切りなどの表示形式をそのまま生かすためです。getValues だと、12480000 のような生の数値で取り込まれてしまいます。
手順2 テンプレートスライドを複製する
次に、テンプレートを複製してスライドとして開く関数を追加します。
/**
* テンプレートを保存先フォルダに複製し、スライドとして開いて返す
*/
function copyTemplate(fileName) {
const folder = DriveApp.getFolderById(FOLDER_ID);
// makeCopy の戻り値はドライブの「ファイル」
const file = DriveApp.getFileById(TEMPLATE_ID).makeCopy(fileName, folder);
// ファイルのIDを使って、スライドとして開き直す
return SlidesApp.openById(file.getId());
}
makeCopy が返すのはドライブ上のファイルで、そのままではスライドの中身を編集できません。いったん getId でIDを取り出し、SlidesApp.openById に渡して開き直しています。
makeCopy は、名前と保存先フォルダの両方を指定する書き方にしました。引数なしで呼ぶと、元ファイルと同じフォルダに複製されます。毎月のファイルが散らばらないよう、報告書専用のフォルダを用意しておくと便利ですよ。
手順3 replaceAllTextでプレースホルダーを置換する
続いて、複製したスライドの目印を数字に置き換える関数です。
/**
* {{変数名}} を、月次データの値に置き換える
*/
function replacePlaceholders(presentation, data) {
Object.keys(data).forEach(function (key) {
const placeholder = '{{' + key + '}}';
// 戻り値は置き換えた件数
const count = presentation.replaceAllText(placeholder, data[key]);
if (count === 0) {
Logger.log('スライド内に見つからなかった目印: ' + placeholder);
}
});
}
Presentation.replaceAllText は、プレゼン内のすべてのスライドをまとめて置換するメソッドです。戻り値は、公式リファレンスで「変更した件数」と説明されています。
この件数が0なら、その目印はスライドのどこにも見つからなかったということ。ログに出しておけば、表記ゆれや入れ忘れにすぐ気づけます。実行後は、エディタ下部の実行ログを確認する習慣をつけましょう。
手順4 複製したスライドのURLをシートに書き戻す
最後に、作ったスライドのURLを「作成履歴」シートに記録する関数を足します。あわせて、手順1〜4を順番に呼び出すメインの関数も貼り付けましょう。
/**
* 作成日時・対象月・URLを「作成履歴」シートの末尾に追記する
*/
function writeLog(logSheet, targetMonth, url) {
if (!logSheet) {
throw new Error('シート「' + LOG_SHEET_NAME + '」が見つかりません');
}
logSheet.appendRow([new Date(), targetMonth, url]);
}
/**
* メインの関数:トリガーからはこの関数を呼び出す
*/
function createMonthlyReport() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 手順1:数字を取得する
const data = getReportData(ss.getSheetByName(SHEET_NAME));
// 手順2:テンプレートを複製する
const presentation = copyTemplate('月次報告_' + data['対象月']);
// 手順3:目印を置き換える
replacePlaceholders(presentation, data);
// 手順4:URLを控えてから保存し、シートに書き戻す
const url = presentation.getUrl();
presentation.saveAndClose();
writeLog(ss.getSheetByName(LOG_SHEET_NAME), data['対象月'], url);
}
saveAndClose は、公式リファレンスで「現在のプレゼンを保存する」と説明されているメソッドです。この記事では、保存を明示するために最後に呼ぶ形でそろえました。URLは閉じる前に取得しています。
貼り付けたら、エディタ上部の関数の選択欄で createMonthlyReport を選びます。[実行]を押し、権限を許可してください。保存先フォルダに「月次報告_2026年9月」のようなスライドができ、履歴シートにURLが入れば成功です。
毎月1日に動かすトリガー設定(最小限)
手動で動くのを確かめたら、毎月1日に自動で動くようにします。次の関数を貼り付けて、1回だけ実行してください。
/**
* 毎月1日に createMonthlyReport を動かすトリガーを作る(1回だけ実行する)
*/
function setMonthlyTrigger() {
ScriptApp.newTrigger('createMonthlyReport')
.timeBased()
.onMonthDay(1)
.create();
}
公式リファレンスに載っている「毎月1日に実行する」サンプルと同じ組み立てです。この関数を実行するたびにトリガーが1つずつ増えるので、2回以上実行しないよう注意しましょう。
1日の時点で前月の数字がそろっていない場合は、onMonthDay(1) の数字を後ろの日にずらします。29日以降は存在しない月があるので、避けておくのが無難です。実行時刻の指定やトリガーの削除方法は、GASのトリガーでスプレッドシートの集計・通知を自動化する方法で詳しく解説しています。
シナリオ2 集計表・グラフをスライドに埋め込んで自動更新
シナリオ1で数字は自動になりました。次は、グラフと表も最新にしていきましょう。ここで作る関数は、最後にシナリオ1のメイン関数へ組み込みます。
手順1 リンク済みグラフを取得してrefreshする
まず、テンプレートにスプレッドシートのグラフをリンク付きで挿入しておきます。スライドの上部メニュー[挿入]→[グラフ]→[スプレッドシートから]で選ぶ方法です。
このとき「スプレッドシートにリンク」のチェックはオンのままにしてください。オフにすると、リンクしないグラフとして貼られます。
続いて、プレゼン内のリンク済みグラフをすべて最新にする関数を追加します。
/**
* すべてのスライドのリンク済みグラフを、スプレッドシート上の最新版に置き換える
*/
function refreshAllCharts(presentation) {
let total = 0;
presentation.getSlides().forEach(function (slide) {
slide.getSheetsCharts().forEach(function (chart) {
chart.refresh();
total++;
});
});
Logger.log('更新したリンク済みグラフ: ' + total + '個');
return total;
}
getSheetsCharts はスライド1枚ごとのメソッドです。そのため getSlides で全スライドを取り出し、1枚ずつループしています。
refresh は、公式リファレンスで「Googleスプレッドシート上の最新版のグラフで置き換える」と説明されています。すでに最新なら、スライド上のグラフは変更されません。
複製したファイルにリンクが残っていれば、この関数で最新にできます。件数が0だったときの切り分けは、後半の「グラフが更新されない」で解説します。
手順2 insertSheetsChartで新規にグラフを入れる
テンプレートにグラフを置かず、スクリプトからスライドへ挿入することもできます。スプレッドシートの「グラフ」シートにある1つ目のグラフを、3枚目のスライドに入れる例です。
/**
* 「グラフ」シートの1つ目のグラフを、指定スライドにリンク付きで挿入する
*/
function insertChartFromSheet(presentation) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('グラフ');
if (!sheet) {
throw new Error('シート「グラフ」が見つかりません');
}
const charts = sheet.getCharts();
if (charts.length === 0) {
throw new Error('「グラフ」シートにグラフがありません');
}
// 3枚目のスライド(0から数えるので 2)
const slide = presentation.getSlides()[2];
// 引数は、グラフ・左からの位置・上からの位置・幅・高さ
return slide.insertSheetsChart(charts[0], 40, 100, 600, 320);
}
insertSheetsChart は、位置とサイズを省略して insertSheetsChart(charts[0]) とも書けます。戻り値は、手順1と同じ SheetsChart(スライド上のスプレッドシートのグラフ)です。
リンクせず画像として入れたいときは、insertSheetsChartAsImage に置き換えます(戻り値は画像)。
注意したいのは、実行するたびにグラフが1つずつ増える点。毎月同じスライドに入れるなら、テンプレートにリンク済みグラフを置き、手順1の refresh で更新するほうが扱いやすいですよ。
なお、どんな種類のグラフでも挿入できるかは、公式に記載が見当たりません。まずは自分のグラフで一度試してみてください。
表はセルへの書き込みで代替する
スプレッドシートの表をスライドに貼り、リンクを最新化する操作も画面上ではできます。ただ、この記事の調査範囲では、リンクした表をGASから更新するメソッドは確認できませんでした。そこで、スライド上の表のセルへ値を直接書き込む方法をとります。
テンプレートの該当スライドに、空の表を1つ置いておいてください。列の数は、スプレッドシートの集計表とそろえておきます。
// ===== 表の設定(ここだけ書き換える) =====
const TABLE_SHEET_NAME = '集計表';
const TABLE_SLIDE_INDEX = 1; // 2枚目のスライド(0から数える)
/**
* 「集計表」シートの値を、スライド上の1つ目の表に書き込む
*/
function writeTableFromSheet(presentation) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(TABLE_SHEET_NAME);
if (!sheet) {
throw new Error('シート「' + TABLE_SHEET_NAME + '」が見つかりません');
}
const values = sheet.getDataRange().getDisplayValues();
const slide = presentation.getSlides()[TABLE_SLIDE_INDEX];
const tables = slide.getTables();
if (tables.length === 0) {
throw new Error('指定したスライドに表がありません');
}
const table = tables[0];
// シートの行数が多ければ、表の最終行の下に行を足す
while (table.getNumRows() < values.length) {
table.appendRow();
}
const colCount = Math.min(values[0].length, table.getNumColumns());
for (let r = 0; r < values.length; r++) {
for (let c = 0; c < colCount; c++) {
const textRange = table.getCell(r, c).getText();
// 結合セルの先頭以外は null になるので飛ばす
if (textRange) {
textRange.setText(values[r][c]);
}
}
}
}
getCell(行, 列).getText() が返すのは、セル内の文字を扱うための TextRange です。その setText で中身を書き換えています。TextRange には getText がないため、セルの文字を読み取りたいときは asString を使いましょう。
結合したセルでは、先頭以外のセルで getText が null を返します。null のまま setText を呼ぶとエラーになるので、コードでは飛ばす処理を入れました。表の列がシートより少ない場合は、はみ出した列を書き込まない作りです。
最後に、ここまでの関数をシナリオ1のメイン関数に組み込みます。手順4の createMonthlyReport を、次のコードで丸ごと置き換えてください。
/**
* メインの関数(シナリオ2の処理を組み込んだ版)
*/
function createMonthlyReport() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const data = getReportData(ss.getSheetByName(SHEET_NAME));
const presentation = copyTemplate('月次報告_' + data['対象月']);
// 数字の置換
replacePlaceholders(presentation, data);
// 表の書き込み
writeTableFromSheet(presentation);
// リンク済みグラフの最新化
refreshAllCharts(presentation);
const url = presentation.getUrl();
presentation.saveAndClose();
writeLog(ss.getSheetByName(LOG_SHEET_NAME), data['対象月'], url);
}
関数名が同じなので、トリガーを作り直す必要はありません。表を使わない場合は writeTableFromSheet の行を消すだけで動きますよ。
GASのスライド自動生成が動かないときの対処法とFAQ
よく詰まるのは「権限承認」「置換されない」「グラフが更新されない」の3パターンです。症状から当てはまるものを探してみてください。
権限承認でエラーになる
初回の実行では、Googleアカウントへのアクセス許可を求める画面が出ます。自分で作ったスクリプトなら、アカウントを選んで内容を確認し、許可して進めましょう。
この記事のコードが使う主な権限は次のとおりです。
| 処理 | 必要になる権限 |
|---|---|
テンプレートの複製(makeCopy) | Googleドライブ |
| スライドの編集・保存 | Googleスライド |
グラフの最新化(refresh) | スプレッドシートの閲覧 |
シナリオ1を動かしたあとで refresh を足すと、改めて許可を求められることがあります。トリガーで動かす前に、必ず一度手動で実行して許可を済ませておいてください。
会社のGoogle Workspaceでは、管理者の設定で許可できない場合もあります。画面で許可が進められないときは、社内の管理者に相談するのが近道です。
プレースホルダーが置換されない
実行ログに「見つからなかった目印」が出ていたら、次の順に確認しましょう。
- 波かっこが半角か確かめる(全角の「{{」になっていないか)
- 目印の中や前後にスペースが入っていないか確かめる
- 「月次データ」のA列の変数名と、目印の中身が一致しているか見比べる
- 目印を表の中に書いていないか確かめる
表の中まで置換されるかは、公式で確認できていません。4つ目に当てはまる場合は、早見表のとおりテキストボックスに移すか、表への書き込み関数を使ってください。
ログに何も出ないのに置換されていないなら、開いているファイルを確認します。履歴シートのURLから複製したほうのスライドを開いてみましょう。テンプレート側は書き換えない作りなので、目印が残っているのは正常です。
グラフが更新されない
refreshAllCharts のログで、グラフの件数を見るのが最初の切り分けです。
- 件数が0のとき:グラフがリンクなしで貼られているか、画像として入っている可能性がある。テンプレートのグラフを、「スプレッドシートにリンク」をオンにして挿入し直す
- 件数は出るのに古いとき:スプレッドシート側のグラフ自体が古い可能性がある。グラフのデータ範囲に新しい月の行が含まれているか確認する
スライド側で何度更新しても、元のグラフが古いままでは変わりません。先にスプレッドシートを開いて確かめると早いですよ。
リンク元のスプレッドシートが別ファイルの場合、権限によっては更新できないことがあります。実行するアカウントでリンク元を開けるかどうかも見ておきましょう。
よくある質問
Q. 部署ごとに同じ型のページを何枚も作れますか?
A. できます。Slide.duplicate でページを複製し、Slide.replaceAllText でそのページだけを置換します。複製は元のページの直後に入るため、逆順で処理すると並びがそろいます。
/**
* 2枚目をひな形に、部署ごとのページを作る(ひな形のページは最後に消す)
*/
function createDepartmentSlides(presentation) {
const departments = ['営業部', '企画部', '経理部'];
const base = presentation.getSlides()[1];
// 複製は元ページの直後に入るので、逆順で作ると上から部署順に並ぶ
departments.slice().reverse().forEach(function (name) {
const page = base.duplicate();
page.replaceAllText('{{部署}}', name);
});
base.remove();
}
使うときは、createMonthlyReport の copyTemplate の行の直後に createDepartmentSlides(presentation); を足します。
Q. できたスライドをPDFにしてメールで送れますか?
A. 生成したファイルをPDF化して送る流れは、GASでスプレッドシートから請求書PDFを自動生成してメール送付する方法が参考になります。
Q. スライドの枚数が多くても大丈夫ですか?
A. スクリプト1回の実行時間は、個人アカウントでもGoogle Workspaceでも6分が上限です。月次報告1本で数枚〜十数枚の規模なら、通常は収まるでしょう。
なお、スライドのファイルを作成できる数にも1日の上限があります。個人アカウントは250件、Workspaceは1,500件です(2026年10月時点)。割り当ては予告なく変わることがあるため、最新の値はGoogleの公式ページで確認してください。
Q. ほかにGASで自動化できる身近な作業は?
A. 同じ時間主導型のトリガーを使う例として、Googleフォームの受付終了を自動化する方法も紹介しています。
まとめ|GASでスライド自動生成して月次報告の手作業をなくそう
GASを使ったスライド自動生成は、テンプレートの複製・目印の置換・グラフの更新の3段で組み立てます。スプレッドシートに数字を入れておけば、毎月の報告スライドが自動でできあがる仕組みです。
この記事のポイントを振り返っておきましょう。
- テンプレートに
{{変数名}}を書き、Presentation.replaceAllTextで置換する。戻り値の件数で置換漏れに気づける - テンプレートは
makeCopyで複製し、SlidesApp.openByIdで開き直してから編集する - リンク済みグラフは、スライドごとに
getSheetsChartsで取り出してrefreshで最新化する - 表はセルの
getText().setText()で書き込む。結合セルの null に注意する onMonthDay(1)のトリガーで毎月1日に自動実行。トリガー関数は1回だけ実行する
まずはシナリオ1のコードを手動で1回動かし、数字が差し替わるのを確かめてみてください。うまくいったら、グラフ・表・トリガーと少しずつ足していけば大丈夫ですよ。
