🐥
SNS + Lambdaで簡単なPub/Sub構成を作ってみる【Node.js + CloudFormation】
はじめに
AWS SNSとLambdaを組み合わせたシンプルなPub/Sub構成をNode.jsとCloudFormationを使って構築してみます。
前提
- AWS CLIがインストール済みであること。
- AWS CLI実行時のIAMユーザのアクセスキーおよびシークレットアクセスキーがcredentialsファイルに記載されていること。
- AWS CLI実行時のIAMユーザにAdministratorAccessのIAMロールが付与されていること
- Node.js 22系がインストールされていること
全体の流れ
- プロジェクトを作成する
- Lambdaコードを作成する
- SNS / Lambda / IAM等一括作成するためのCloudFormationを作成する
- デプロイ用スクリプトを作成する
- デプロイする
- 動作確認
ディレクトリ構成
最終的に以下のようなディレクトリ構成になります。
sns-lambda-demo/
├── app/
│ ├── publisher.js
│ └── subscriber.js
├── deploy/
│ ├── app.zip # デプロイ操作時に生成されます
│ └── deploy.sh
├── infrastructure/
│ └── template.yaml
└── package.json
1. プロジェクトを作成
mkdir sns-lambda-demo && cd $_
npm init -y
npm pkg set type="module"
mkdir -p app deploy infrastructure
2. Lambdaコードを作成
app/publisher.js
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
const client = new SNSClient({ region: process.env.AWS_REGION });
export const handler = async () => {
const command = new PublishCommand({
TopicArn: process.env.TOPIC_ARN,
Message: 'Hello from publisher Lambda!'
});
await client.send(command);
return { statusCode: 200, body: JSON.stringify('Message published') };
};
app/subscriber.js
export const handler = async (event) => {
console.log('Received SNS Event:', JSON.stringify(event, null, 2));
};
3. SNS / Lambda / IAM等一括作成するためのCloudFormationを作成する
infrastructure/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda Pub/Sub demo (Node.js 22)
Parameters:
LambdaCodeBucket:
Type: String
Description: S3 bucket that contains *app.zip*
ZipKey:
Type: String
Default: app.zip
Description: Object key of the Lambda artifact
Resources:
# SNS Topic
DemoTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Sub "${AWS::StackName}-demo-topic"
# IAM roles
PublisherLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: { Service: lambda.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: AllowSNSPublish
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: sns:Publish
Resource: !Ref DemoTopic
SubscriberLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: { Service: lambda.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# Publisher Lambda
PublisherLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-publisher"
Runtime: nodejs22.x
Handler: app/publisher.handler
Role: !GetAtt PublisherLambdaExecutionRole.Arn
Code:
S3Bucket: !Ref LambdaCodeBucket
S3Key: !Ref ZipKey
Environment:
Variables:
TOPIC_ARN: !Ref DemoTopic
# Subscriber Lambda
SubscriberLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-subscriber"
Runtime: nodejs22.x
Handler: app/subscriber.handler
Role: !GetAtt SubscriberLambdaExecutionRole.Arn
Code:
S3Bucket: !Ref LambdaCodeBucket
S3Key: !Ref ZipKey
# SNS → Lambda subscription
DemoSubscription:
DependsOn: AllowSNSInvokeLambda
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref DemoTopic
Protocol: lambda
Endpoint: !GetAtt SubscriberLambda.Arn
AllowSNSInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref SubscriberLambda
Action: lambda:InvokeFunction
Principal: sns.amazonaws.com
SourceArn: !Ref DemoTopic
4. デプロイ用スクリプトを作成する
deploy/deploy.sh
#!/usr/bin/env bash
set -eu
STACK_NAME=sns-lambda-demo
REGION=${AWS_REGION:-ap-northeast-1}
BUCKET_NAME=${STACK_NAME}-$(date +%Y%m%d%H%M%S)
ZIP_KEY=app.zip
# 1) S3 バケット用意(無ければ作成)
if ! aws s3api head-bucket --bucket "$BUCKET_NAME" 2>/dev/null; then
aws s3 mb "s3://$BUCKET_NAME" --region "$REGION"
fi
# 2) ZIP 作成(appフォルダ直下の.jsをまとめる)
zip -q -r deploy/app.zip app package.json
# 3) アップロード
aws s3 cp deploy/app.zip "s3://$BUCKET_NAME/$ZIP_KEY"
# 4) デプロイ
aws cloudformation deploy \
--template-file infrastructure/template.yaml \
--stack-name "$STACK_NAME" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides LambdaCodeBucket=$BUCKET_NAME ZipKey=$ZIP_KEY
5. デプロイする
プロジェクト直下のディレクトリまで移動した後、
実行権を付与してから、デプロイ用スクリプトを実行します。
# 1) プロジェクト直下のディレクトリまで移動
# /path/toの部分は環境に合わせて書き換えてください
cd /path/to/sns-lambda-demo
# 2) デプロイスクリプトに実行権を付与
chmod +x deploy/deploy.sh
# 3) デプロイ用スクリプトを実行
./deploy/deploy.sh
スクリプト実行後、CloudFormationのコンソールに以下のようにCREATE_COMPLETEの状態でスタックが作成されていれば成功です。
6. 動作確認
Publisher Lambdaを手動で呼び出してみます。
aws lambda invoke \
--function-name sns-lambda-demo-publisher \
--payload '{}' response.json && cat response.json
CloudWatch Logsで /aws/lambda/sns-lambda-demo-subscriber
のロググループを開き、ログメッセージ内にReceived SNS Event:
で始まるログが含まれていれば、SNS → Lambdaの受信を確認できたことになります。
まとめ
今回は、AWSのSNSとLambdaを組み合わせて、シンプルなPub/Sub構成を構築しました。
また、インフラからソースコード、デプロイ手順までを自動化し、最小限の手動操作で動作確認できる仕組みを目指しました。
この記事を通じて、Pub/Subの基本的な流れと、CloudFormationによるリソース管理のイメージをつかんでいただければ幸いです。
Discussion