0. 概要
CloudFormationとLambda、API Gateway、WAFv2の連携に慣れるため、デモ環境を構築しました。
・WAFは開発用のため特定IP(自分)のみアクセス可能のルールを設定
・API経由でセンサーデータ(速度や温度)を受信
・CloudWatch Logsに記録
※WAFv2(AWS::WAFv2::WebACLAssociation)は、API Gatewayに直接紐付けできないので、今後CloudFrontか、ALBを追加予定
参考記事:
ウェブ ACL と AWS リソースの関連付けまたは関連付け解除
関連付け可能なリソース
● リージョナル
・API Gateway REST API
・ALB
・AppSync GraphQL API
・Cognito ユーザープール
・App Runner
・AWS Verified Access インスタンス
● グローバル
・CloudFront
1. 構成
CloudFormationで以下のリソースを作成
工程 | AWSリソース |
---|---|
Lambda実行コードの配置 | S3 |
Lambda関数作成 | Lambda |
APIエンドポイント作成 | API Gateway |
IP制限ルールの設定 | WAFv2 |
Lambda実行ロール | IAM Role |
ネスト構成
stack-main.yaml
:親テンプレート。S3上の子テンプレートを呼び出しますstack-api-lambda.yaml
:子テンプレート。API Gateway、Lambda、WAFを定義
通信フロー
【通信イメージ】
[Client] → [WAFv2] → [API Gateway] → [Lambda Function] → [API Gateway] → [Client]
↓
[CloudWatch Logs]へログ出力
【通信フロー図】
┌────────────┐
│ Client │
└────┬───────┘
│
① HTTP POST リクエスト送信
▼
┌────────────┐
│ WAFv2 │
└────┬───────┘
│
② リクエストを検査(IP許可制)
▼
┌────────────────────┐
│ API Gateway │
└────────┬────────────┘
│
③ Lambdaにリクエストを転送
▼
┌────────────────────┐
│ Lambda Function │
└────────┬────────────┘
│
④ CloudWatch Logs にログ出力(非同期)
▼
┌────────────────┐
│ CloudWatchLogs │
└────────────────┘
▲
│
⑤ Lambdaが戻り値(レスポンス)をAPI Gatewayに返却
▲
⑥ API GatewayがHTTPレスポンスとしてClientに返却
▲
┌────────────┐
│ Client │
└────────────┘
2. フォルダ構成
cfn-demo-api_lambda_waf/
├── lambda/ # Lambda関数のソースコードとZIPファイル
│ ├── ingest_vehicle_sensor.py # センサーデータ受信用Lambda関数(Python)
│ └── ingest_vehicle_sensor.zip # 上記スクリプトをZIP化したデプロイ用ファイル
├── output.json # デプロイ後の出力情報(Outputsのログなど)
├── parameters/ # CloudFormationスタックに渡すパラメータファイル置き場
│ └── dev-params.json # 開発環境用パラメータ(S3バケット名やIP制限など)
├── results/ # 実行結果やログなどを出力する(現在は空ディレクトリ)
├── scripts/ # スタック操作用のシェルスクリプト群
│ ├── check-status.sh # スタックの状態確認用スクリプト
│ ├── delete.sh # スタック削除スクリプト
│ ├── deploy-staging.sh # ステージング環境用のデプロイスクリプト
│ └── deploy.sh # 開発環境用のデプロイスクリプト
├── templates/ # CloudFormationテンプレート群
│ ├── stack-api-lambda.yaml # 子テンプレート:Lambda + API Gateway + WAF 定義
│ └── stack-main.yaml # 親テンプレート:子テンプレートをネスト呼び出し
├── test-payload-utf8.json # UTF-8形式のテストリクエストペイロード(API Gateway用)
└── test-payload.json # 標準形式のテストペイロード(POSTリクエスト用)
3. Lambda関数
## Lambda関数(要約)
def lambda_handler(event, context):
# JSONで受信されたセンサーデータをCloudWatchに記録
# 受信項目:sensor_id / timestamp / temperature / speed
Code language: PHP (php)
Lamda関数(全体)
import json
import logging
from datetime import datetime, timezone
# CloudWatch Logs へ出力するためのロガー設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""
センサーから送信されたデータをAPI Gateway経由で受信し、
その内容をCloudWatch Logsに記録するLambda関数。
入力フォーマット想定(POSTリクエストのbody):
{
"sensor_id": "sensor-001",
"timestamp": "2025-03-23T12:34:56Z",
"temperature": 22.5,
"speed": 60.2
}
"""
try:
# API Gateway経由の場合、bodyは文字列として渡される
body = event.get("body")
if body is None:
raise ValueError("Missing request body")
# JSON文字列をPythonオブジェクトに変換
payload = json.loads(body)
# 必須項目の抽出(存在しない場合はデフォルトやNone)
sensor_id = payload.get("sensor_id", "unknown")
temperature = payload.get("temperature")
speed = payload.get("speed")
timestamp = payload.get("timestamp", datetime.now(timezone.utc).isoformat())
# ログに出力する情報をまとめる
log_data = {
"sensor_id": sensor_id,
"timestamp": timestamp,
"temperature": temperature,
"speed": speed,
"source_ip": event.get("requestContext", {}).get("identity", {}).get("sourceIp", "N/A")
}
# CloudWatch Logs に情報を記録
logger.info(f"Received sensor data: {json.dumps(log_data)}")
# 正常応答を返す(API Gateway経由でクライアントに返される)
return {
"statusCode": 200,
"body": json.dumps({
"message": "Sensor data received successfully",
"sensor_id": sensor_id
})
}
except Exception as e:
# エラーログを出力し、HTTP 400で応答
logger.error(f"Error processing sensor data: {e}")
return {
"statusCode": 400,
"body": json.dumps({"error": str(e)})
}
4. パラメータ例(dev-params.json)
[
{ "ParameterKey": "BucketName", "ParameterValue": "cfn-demo-jin" },
{ "ParameterKey": "BucketPrefix", "ParameterValue": "cfn-demo-api_lambda_waf" },
{ "ParameterKey": "StageName", "ParameterValue": "dev" },
{ "ParameterKey": "AllowedIP", "ParameterValue": "xxx.xxx.xxx.xxx/32" } ←開発用に自分のGlobalIPをWAFで許可
]
Code language: JSON / JSON with Comments (json)
5. 補足
- API Gatewayは
POST /vehicle-data-demo_01
エンドポイントを提供 - WAFは
AllowedIP
に指定されたIPのみ許可(それ以外はブロック) - LambdaコードはS3からデプロイ(事前にZIPファイルをアップロード)