AIが作ったHTMLをGoogle Workspaceで社内限定公開する方法(スキル付き!)

AIが作ったHTMLをGoogle Workspaceで社内限定公開する方法(スキル付き!)

Claude Code で作成した HTML レポートを社内限定で配信する仕組みを、Google Apps Script と clasp を使って構築し、Agent Skill 化してみました。レポート作成から公開まで、AI に「社内限定で公開して」と言うだけで完結する方法について紹介します。
2026.09.24

はじめに

Claude Code で調査結果や報告資料を図表つきの HTML レポートにまとめる機会が増えてきました。次に躓くのが共有。Slackに貼ったりするのめんどくさいですよね。

claude.ai の Artifact は手軽ですが、共有リンクの公開範囲を都度気にする必要があります。そもそも個人プランだと全世界に共有されてしまいます。かといって S3 + CloudFront などで配信基盤を組むのは、レポート1枚のためには大げさです。

そこで、Google Apps Script(GAS)のウェブアプリ機能で HTML レポートを社内限定で配信できるようにして、一連の流れを Claude Code の Agent Skill にしてみました。一度作れば Claude Code に「HTMLでまとめて」「社内限定で公開して」と言うだけで、レポート作成から社内限定URLの発行・更新まで済んじゃいます。

この記事は、仕組みの概要を「サマリ」に、設定やコードを「詳細」に分けて書いています。使い方だけ知りたい方はサマリだけ読めば十分です。

サマリ

スキル一式は GitHub で公開しています。CloneしてClaude Codeなどでご自身の環境に合わせてご利用ください。

何ができるようになったか

AI(Claude Code)に次のように頼むだけで、図表入りのレポートが作られ、社内の人だけが見られるURLが発行されます。

この調査結果、HTMLレポートにまとめて社内限定で公開して

受け取ったURLを Slack やメールで共有すれば、社内の人はリンクを開くだけでレポートを読めます。

以前と比べて何が変わったか

これまで これから
レポートの作成 AIに作らせたファイルを自分で保存する AIに頼むだけ
共有の方法 ファイルを Slack に添付する、または公開範囲を確認しながら共有リンクを作る AIが返すURLを貼るだけ
見られる人 共有先の設定次第 会社の Google アカウントでログインしている人だけ
内容を直したとき ファイルを添付し直す 同じURLのまま中身が新しくなる
費用 追加費用なし(会社で使っている Google Workspace の機能だけで動く)

なぜ社外に漏れないのか

レポートは、会社で使っている Google Workspace の中に置かれます。URLを開いた人が会社の Google アカウントでログインしていなければ、Google がその時点で表示を拒否します。URLが社外に転送されても、社外の人には中身が表示されません。

「この3人だけに見せたい」といった場合に備えて、見られる人をメールアドレスで指定する仕組みも入れています。ただしこの場合は、閲覧者が最初に Google の確認画面で許可を押す手間が増えます。

使うときの注意点

ブラウザで個人の Google アカウントと会社のアカウントの両方にログインしていると、個人アカウントでページが開かれて「権限がありません」と表示されることがあります。リンクを共有するときは「会社アカウントで開いてください」と一言添えています。

詳細

全体の流れ

登場するのは、Claude Code、デプロイスクリプト(deploy.sh)、Google の3つです。誰が何をするかで分けると、流れはこうなります。

[自分]          「HTMLでまとめて社内限定で公開して」


[Claude Code]   ① HTMLレポートを1ファイルで作る(report.html)
   │            ② 公開の希望を確認し、deploy.sh を実行する

[deploy.sh]     ③ slug ごとの Apps Script プロジェクトに HTML を配置する
   │            ④ clasp で push し、デプロイする
   │               (2回目以降は既存デプロイを更新するので URL は変わらない)

[Google]        ⑤ https://script.google.com/a/macros/{ドメイン}/s/{デプロイID}/exec で配信する
   │            ⑥ 社内ドメイン以外の Google アカウントはここで拒否する

[社内の閲覧者]   リンクを開くだけで見られる(承認画面なし)

Skill は「レポート作成」と「社内限定URL公開」の2段構成で、公開はユーザーが希望した場合だけ実行します。

状態はすべてホームディレクトリ配下に置いています。

パス 内容
~/.gas-reports/<slug>/ slug ごとの Apps Script プロジェクト(.clasp.json / .deployment / src/
~/.gas-reports/registry.tsv slug → 公開URL の台帳(slug / URL / 最終更新日 / タイトル)

slug(weekly-report のような英小文字の識別子)は公開URLと1対1で対応しています。「前に公開したレポートを更新して」と頼まれたときは、台帳から slug を引けば同じURLのまま差し替えられます。

なぜ Google Apps Script なのか

社内共有の要件は次の4つでした。

  • 社内(Google Workspace の自ドメイン)限定で閲覧できること
  • 閲覧者はリンクを開くだけでよいこと(OAuth の承認画面を出さない)
  • 内容を更新してもURLが変わらないこと(配布済みリンクを配り直さない)
  • 追加のインフラ・ランニングコストがないこと

候補を比較するとこうでした。

手段 ドメイン限定 閲覧の手軽さ URL固定更新 追加インフラ
claude.ai Artifact △(共有設定次第) 不要
S3 + CloudFront + 認証 △(認証実装次第) 必要
社内 Wiki に貼る 不要(ただしHTMLの表現力が失われる)
GAS ウェブアプリ ◎(Google側で遮断) ◎(リンクを開くだけ) ◎(デプロイ更新) 不要

GAS ウェブアプリは、マニフェスト(appsscript.json)の webapp.accessDOMAIN にするだけで、自ドメインの Google アカウント以外は Google 側で遮断されます。アプリのコードに認証処理を書く必要はありません。

アクセス制御は2段階

アクセス制御は「マニフェストでのドメイン制限」と「コードでのユーザー絞り込み」の2段階にしました。

1段目はマニフェストです。

{
  "timeZone": "Asia/Tokyo",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "access": "DOMAIN",
    "executeAs": "USER_DEPLOYING"
  }
}

2段目は Code.gsALLOWED_USERS です。空配列ならドメイン内の全員に公開、メールアドレスを列挙すればその人たちだけに絞れます(以下は読みやすさのため簡略化した抜粋です)。

// 空配列 = ドメイン内の全員に公開。絞る場合はメールアドレスを列挙する。
const ALLOWED_USERS = [];

function doGet() {
  if (!isAllowed_()) {
    return HtmlService.createHtmlOutput(
      '<p>このページの閲覧権限がありません。</p>'
    ).setTitle('アクセス権限がありません');
  }

  return HtmlService.createHtmlOutputFromFile('index')
    .setTitle('__REPORT_TITLE__')
    .addMetaTag('viewport', 'width=device-width, initial-scale=1');
}

function isAllowed_() {
  // 絞り込みなしのときは Session を参照しない。参照すると閲覧者全員に
  // OAuth 承認画面が出てしまうため。
  if (ALLOWED_USERS.length === 0) return true;
  return ALLOWED_USERS.indexOf(Session.getActiveUser().getEmail()) !== -1;
}

一番こだわったのは、ALLOWED_USERS が空のあいだは Session.getActiveUser() を呼ばないことです。

Session.getActiveUser() を呼ぶと userinfo.email の OAuth スコープが要求され、閲覧者全員に「未検証アプリ」の警告つき承認画面が表示されるようになります。レポートのリンクを開いただけで承認画面が出てくると、閲覧者は何を許可するのか分からないまま判断を求められます。

社内全員に見せてよいレポートでは Session を呼ばないので、閲覧者はリンクを開くだけで済みます。特定メンバーに絞りたいときだけ ALLOWED_USERS を埋めて、承認画面が出ることを受け入れます。

なお、現状の deploy.sh は実行のたびに Code.gs をテンプレートから作り直すため、ALLOWED_USERS を書き足しても次のデプロイで空に戻ります。当面は、生成された Code.gs を直接編集して clasp で反映する手順を SKILL.md に書いています。絞り込みを日常的に使うなら、deploy.sh から値を渡せるように改修が必要です。

URLが変わらない更新デプロイ

GAS ウェブアプリのURLにはデプロイIDが含まれています。

https://script.google.com/a/macros/{ドメイン}/s/{デプロイID}/exec

clasp create-deployment で毎回新しいデプロイを作ると、更新のたびにURLが変わり、配布済みのリンクの先には古い内容が残り続けます。これを避けるには、既存デプロイを clasp update-deploymentcreate-deployment -i <id> と同じ)で更新します。本記事は @google/clasp 3.3.0 で動作確認しています。

deploy.sh では、初回デプロイ時にIDを .deployment ファイルへ保存し、2回目以降はそのIDで更新するようにしました(エラー処理を省いた抜粋です)。

"$CLASP" push --force

if [ -f "$PROJ_DIR/.deployment" ]; then
  DEPLOYMENT_ID="$(tr -d '[:space:]' < "$PROJ_DIR/.deployment")"
  echo "→ 既存デプロイを更新しています(URL は変わりません): $DEPLOYMENT_ID"
  "$CLASP" update-deployment "$DEPLOYMENT_ID" --description "$(date '+%Y-%m-%d') 更新"
else
  echo "→ 初回デプロイを作成しています..."
  OUT="$("$CLASP" create-deployment --description "$TITLE" 2>&1)"
  DEPLOYMENT_ID="$(printf '%s' "$OUT" | grep -oE 'AKfyc[A-Za-z0-9_-]+' | head -1)"
  printf '%s\n' "$DEPLOYMENT_ID" > "$PROJ_DIR/.deployment"
fi

これで、同じ slug で再実行すれば同じURLのまま中身だけが差し替わります。週次レポートのように繰り返し更新する用途では、この性質が重要です。

もうひとつ、<!DOCTYPE> を含まない HTML(claude.ai の Artifact 形式など)を受け取ったときは、外側の <html> と一緒に <base target="_top"> を補っています。HTML Service は配信するページをサンドボックスの iframe 内に描画するため、これがないとレポート内のリンクが iframe の中で開いてしまいます。

Claude Code Skill にする

最後に、ここまでの仕組みを Claude Code の Agent Skill にします。公開しているリポジトリの構成は次のとおりで、~/.claude/skills/html-report/ に clone して使います。

~/.claude/skills/html-report/
├── SKILL.md          # スキル本体(手順・設計原則・トラブルシューティング)
├── scripts/
│   └── deploy.sh     # 公開・更新スクリプト
├── templates/
│   ├── Code.gs       # doGet / ALLOWED_USERS のテンプレート
│   └── appsscript.json
├── package.json      # clasp を devDependency として同梱
├── pnpm-lock.yaml
├── README.md
├── LICENSE
└── .gitignore

clasp 本体は pnpm installnode_modules/ に入ります(リポジトリには含めていません)。

SKILL.md の frontmatter はこうなっています(読みやすさのため整形し、トリガーの一部を省略した抜粋です)。

---
name: html-report
description: 図・表を活用した自己完結HTMLレポートを作成し、希望があれば社内限定URL
  (Google Apps Script ウェブアプリ・自社の Google Workspace ドメイン限定)として公開する。
  同じレポートを更新しても公開URLは変わらない。Triggers: "HTMLでまとめて",
  "HTMLレポートにして", "社内URLで共有", "社内限定で公開", "/html-report"
---

description に具体的な依頼の言い回しを書いておくと、/html-report と明示的に呼ばなくても、会話の中の「社内限定で公開して」のような依頼から Claude がこのスキルを選びます。

公開URLに入るドメインは、clasp でログインしているアカウントのメールアドレスから deploy.sh が自動で取得します。別のドメインを使う場合は環境変数 GAS_DOMAIN で指定できます。

clasp はグローバルインストールせず、スキルディレクトリの node_modules に入れました。スキル一式をコピーして pnpm install を1回実行すれば動きます(初回の clasp login だけはブラウザでの承認が必要です)。

まとめ

GAS ウェブアプリの webapp.access: DOMAIN を使うと、認証コードも追加インフラもなしで社内限定のHTML配信ができます。clasp update-deployment で既存デプロイを更新すれば公開URLも固定できるので、繰り返し更新するレポートの配布先として十分使えます。閲覧者に承認画面を出さないためには、ALLOWED_USERS が空のあいだは Session を呼ばないことだけ守ってください。

これを Claude Code の Agent Skill にしたことで、レポートの作成から配信までを依頼1回で済ませられるようになりました。社内向けのスライドやダッシュボードの配信にも同じ方法が使えます。

参考リンク

Google 公式


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AI白書

関連記事