【入門編】LinearのWebhookとAWS Lambdaを連携させた独自監査ログシステムの構築ハンズオン – プロジェクト・ナレッジ管理活用バイブル

こんにちは!開発チームのベロシティを最大化しつつ、セキュリティやコンプライアンスの堅牢さも妥協したくない、欲張りなエンジニアの皆さん。

アジャイル開発を進める中で、こんな不安や要望を持ったことはありませんか?
「Linearのイシューが誰に、いつ、どう変更されたのか、すべての履歴を会社のコンプライアンス要件として改ざん不能な形で残したい」
「でも、監査ログのために開発者の手が止まったり、面倒な運用が増えたりするのは絶対に嫌だ」

これをスマートに解決するのが、「LinearのWebhook × AWS Lambda」による独自監査ログシステムの構築です。

Linearの圧倒的なスピード感を損なうことなく、裏側でイベントを完全にキャッチし、AWSの安全なストレージへ自動蓄積する――。これをマスターすれば、セキュリティ監査の提出前夜に慌ててログを集める必要はもうなくなります。毎日の開発が劇的に安心で快適になりますよ。

それでは、初心者の方でも迷わず手を動かせるよう、基本概念から実コードを使ったハンズオンまで、優しく徹底的に解説していきますね!

—

1. 全体像の把握:なぜこのアーキテクチャなのか?

まずは、今回作るシステムの「心臓部」がどう連携するのか、全体像を直感的に理解しましょう。

[ Linear ] –(Webhook / HTTPS)–> [ API Gateway ] –> [ AWS Lambda ] –> [ Amazon S3 / DynamoDB ]

1. Linear: イシューの作成、更新、削除などのアクションが発生すると、設定されたURLへ向けてリアルタイムにJSONデータ(イベント)を送信します。
2. API Gateway (or Lambda Function URL): LinearからのWebhookを受け取る「窓口」です。今回は最もシンプルでセキュアな AWS Lambda Function URL を採用します。
3. AWS Lambda: 受け取ったデータをパースし、必要に応じて検証(セキュリティチェック)を行ってストレージへ投げます。サーバレスなので管理コストはゼロです。
4. Amazon S3 / DynamoDB: ログの永続化先です。今回は安価でイミュータブル(変更不可)な保存ができる Amazon S3 を主役にしつつ、検索性を高めるためにDynamoDBを組み合わせるアプローチも視野に入れます。

—

2. 基礎セットアップ:AWS Lambdaの準備

まずは、Webhookを受け取る受け皿であるAWS Lambda関数を作成しましょう。
今回は、モダンな開発で好まれる Node.js (TypeScript or JavaScript) をベースに進めます。

ステップ 2-1: Lambda関数の作成

1. AWSマネジメントコンソールを開き、Lambda サービスに移動します。
2. 「関数の作成」をクリックします。
3. 以下のように設定します:

  • 関数名: `linear-audit-logger`
  • ランタイム: `Node.js 18.x` (または最新のLTS)
  • アーキテクチャ: `x86_64`

4. 「関数の作成」を押します。

ステップ 2-2: Function URL(公開エンドポイント)の設定

Linearから直接HTTPSでLambdaを叩けるようにするため、「Function URL」を有効化します。

1. 作成した関数の「設定」タブ >「関数 URL」を選択し、「関数 URL の作成」をクリックします。
2. 認証タイプ: `NONE` を選択します(※後ほどLinearからのリクエスト検証をコード内で行うため、ここではパブリックにしておきます)。
3. 保存すると、`https://.lambda-url..on.aws/` というURLが発行されます。これがLinearに教えるWebhookのエンドポイントになります。

—

3. 実装:イベントをキャッチしてS3に保存するコード

ここが一番ワクワクするポイントですね!Linearから送られてきたWebhookのペイロードを受け取り、Amazon S3にタイムスタンプ付きのJSONとして保存するコードを書きましょう。

ステップ 3-1: S3バケットの準備

1. Amazon S3 コンソールに移動し、新しいバケットを作成します(例: `my-company-linear-audit-logs-xyz`)。
2. セキュリティのため、パブリックアクセスはすべてブロックしておきます。

ステップ 3-2: Lambdaのコード実装

Lambdaの「コード」タブを開き、`index.mjs` に以下のコードを貼り付けます。

import { S3Client, PutObjectCommand } from “@aws-sdk/client-s3”;

// S3クライアントの初期化(リージョンは環境変数から自動取得)
const s3Client = new S3Client();
const BUCKET_NAME = process.env.AUDIT_BUCKET_NAME; // 環境変数からバケット名を取得

export const handler = async (event) => {
console.log(“Received event:”, JSON.stringify(event, null, 2));

try {
// Lambda Function URL経由のリクエストボディは event.body に入っています
if (!event.body) {
return { statusCode: 400, body: “Invalid Request: No body” };
}

const linearPayload = JSON.parse(event.body);

// LinearからのWebhookイベントの種類を特定 (e.g., “Issue”, “Comment”)
const eventType = linearPayload.type || “unknown”;
const action = linearPayload.action || “unknown”;
const timestamp = new Date().toISOString().replace(/[:.]/g, “-“);

// S3に保存する際のファイルパス(プレフィックス)を設計
// 例: logs/Issue/create/2023-10-25T12-00-00-000Z-.json
const objectKey = `logs/${eventType}/${action}/${timestamp}-${linearPayload.data?.id || ‘no-id’}.json`;

// S3へ保存するコマンドの定義
const putCommand = new PutObjectCommand({
Bucket: BUCKET_NAME,
Key: objectKey,
Body: JSON.stringify(linearPayload, null, 2),
ContentType: “application/json”,
});

// S3にアップロード実行
await s3Client.send(putCommand);
console.log(`Successfully saved audit log to s3://${BUCKET_NAME}/${objectKey}`);

// LinearのWebhookは成功(2xx)を返さないとリトライし続けるため、200を返す
return {
statusCode: 200,
body: JSON.stringify({ message: “Audit log recorded successfully” }),
};

} catch (error) {
console.error(“Error processing Linear webhook:”, error);
// エラー時もLinear側で無駄なリトライループを生まないためにステータスを考慮しますが、
// ログロストを防ぐためにデバッグしやすくしておきます。
return {
statusCode: 500,
body: JSON.stringify({ error: “Internal Server Error” }),
};
}
};

ステップ 3-3: 権限(IAMロール)と環境変数の設定

1. 環境変数: Lambdaの「設定」>「環境変数」から、`AUDIT_BUCKET_NAME` というキーで、先ほど作成したS3バケット名を設定します。
2. IAM権限: LambdaがS3に書き込めるよう権限を与えます。Lambdaの「設定」>「アクセス権限」から実行ロールを開き、S3への `PutObject` 権限を持つポリシーを追加してください。

—

4. 精度高い「Hello World」!動作確認のやり方

コードの準備ができたら、実際にLinearとつないで動作確認(HelloWorld)をしてみましょう!

ステップ 4-1: Linear側でWebhookを設定する

1. Linearのワークスペース設定(Settings)を開きます。
2. 「API」 または 「Webhooks」 メニューに移動します。
3. 「Create webhook」をクリックします。
4. 設定項目に入力します:

  • URL: 先ほど取得した AWS Lambda の Function URL を入力。
  • Resource types: まずは動作確認用に `Issue` にチェックを入れます(すべての変更をキャッチできます)。

5. 「Create webhook」で保存します。

ステップ 4-2: テストイベントを発生させる

1. Linearの適当なプロジェクトに戻り、新しいイシューを作成 してみてください。
2. すぐにイシューのタイトルを書き換えてみたり、担当者を変えてみたりします。

ステップ 4-3: S3とCloudWatch Logsで確認する

1. AWS CloudWatch Logs: Lambdaの「モニター」タブからログストリームを確認します。「Received event:」というログが出力され、Linearからのリクエストデータが綺麗にパースされていれば大成功です!
2. Amazon S3: 先ほど作成したS3バケットを開いてみてください。`logs/Issue/create/…` や `logs/Issue/update/…` というフォルダ構造で、JSONファイルがズラリと保存されているはずです。

中身のJSONを覗いてみると、誰が(actor)、どのイシュー(data)を、どう変更したのかが、一物一価の動かぬ証拠(監査ログ)として完璧に残っていることが確認できます。

—

5. ベロシティを落とさず、さらに先へ(先輩からのアドバイス)

おめでとうございます!これで、LinearのイベントをセキュアにキャッチしてS3に永久保存する、プロダクションレベルの監査ログシステムの基礎が完成しました。

さらに現場の運用を洗練させるためのプロの知見をいくつか置いておきます。

  • Webhookシークレットの検証:

本番環境では、Linearから送られてくるリクエストが「本当にLinearからのものか」を検証するため、Webhookの署名(Signatureヘッダー)をLambda内で検証するロジックを追加すると、セキュリティレベルが一段と上がります。

  • Athenaによるログ分析:

S3に溜まったJSONファイル群は、Amazon Athena を使うと、そのままSQLで「今週、誰が一番イシューをクローズしたか」や「機密ラベルがついたイシューの変更履歴」などを一瞬で検索できるようになります。

「面倒なコンプライアンス対応」をクラウドの力で自動化してしまえば、エンジニアはプロダクトの価値を届けるコーディングに集中できます。
ぜひ今日のハンズオンを試して、あなたのチームの基盤をより強固で美しいものにしてくださいね!

タイトルとURLをコピーしました