この記事は何
前回までコンソールでポチポチ作っていた運動記録の構成を、AWS SAMでコード化(IaC化)し、sam deploy一発で立ち上がるところまで持っていった記録です。2分で読めます。
背景
これまで運動記録AppSheet + 手動で管理していましたが、「同じ構成を誰でも再現できる形にしたい」「インフラをコードで管理する感覚を身につけたい」と思い、SAMでIaC化に挑戦しました。
構成
- AWS Lambda:筋トレのボリュームを計算
- Amazon DynamoDB:運動記録を保存
- AWS SES:土曜日21時に今週の筋トレの総ボリューム数と有酸素運動の合計時間と走行距離をメール通知
IAMポリシーを自分でJSONで書かずに済むのがSAMの利点でした。
以下はtemplate.yamlのAWS Lambda、DynamoDB、Lambda+SESの部分です。
# ── 記録用 Lambda(筋トレ)───────────────────────
WorkoutFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: workout_lambda_SAM_Test
CodeUri: functions/workout/
Handler: lambda_function.lambda_handler
Environment:
Variables:
TABLE_NAME: WorkoutRecords_SAM_Test
Policies:
- DynamoDBCrudPolicy:
TableName: WorkoutRecords_SAM_Test
Events:
Api:
Type: HttpApi
Properties:
ApiId: !Ref WorkoutExerciseApi
Path: /workout
Method: ANY
# ── 記録用 Lambda(有酸素)───────────────────────
ExerciseFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: exercise_lambda_SAM_Test
CodeUri: functions/exercise/
Handler: lambda_function.lambda_handler
Environment:
Variables:
TABLE_NAME: ExerciseRecords_SAM_Test
Policies:
- DynamoDBCrudPolicy:
TableName: ExerciseRecords_SAM_Test
Events:
Api:
Type: HttpApi
Properties:
ApiId: !Ref WorkoutExerciseApi
Path: /exercise
Method: ANY
# ── DynamoDB テーブル ─────────────────────────────
WorkoutTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: WorkoutRecords_SAM_Test
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: user_id
AttributeType: S
- AttributeName: record_id
AttributeType: S
- AttributeName: body_part
AttributeType: S
- AttributeName: date
AttributeType: S
KeySchema:
- AttributeName: user_id
KeyType: HASH
- AttributeName: record_id
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: body_part-date-index
KeySchema:
- AttributeName: body_part
KeyType: HASH
- AttributeName: date
KeyType: RANGE
Projection:
ProjectionType: ALL
StreamSpecification:
StreamViewType: NEW_IMAGE
ExerciseTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ExerciseRecords_SAM_Test
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: user_id
AttributeType: S
- AttributeName: record_id
AttributeType: S
KeySchema:
- AttributeName: user_id
KeyType: HASH
- AttributeName: record_id
KeyType: RANGE
StreamSpecification:
StreamViewType: NEW_IMAGE
# ── 週次レポート Lambda(筋トレ)─────────────────
WorkoutWeeklyReportFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: workout_weekly_report_SAM_Test
CodeUri: functions/workout_weekly_report/
Handler: lambda_function.lambda_handler
Environment:
Variables:
TABLE_NAME: WorkoutRecords_SAM_Test
SES_SENDER: !Ref SesSender
SES_RECIPIENT: !Ref SesRecipient
Policies:
- DynamoDBReadPolicy:
TableName: WorkoutRecords_SAM_Test
- Statement:
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: '*'
Events:
WeeklySchedule:
Type: ScheduleV2
Properties:
ScheduleExpression: cron(0 21 ? * SAT *)
ScheduleExpressionTimezone: Asia/Tokyo
# ── 週次レポート Lambda(有酸素)─────────────────
ExerciseWeeklyReportFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: exercise_weekly_report_SAM_Test
CodeUri: functions/exercise_weekly_report/
Handler: lambda_function.lambda_handler
Environment:
Variables:
TABLE_NAME: ExerciseRecords_SAM_Test
SES_SENDER: !Ref SesSender
SES_RECIPIENT: !Ref SesRecipient
Policies:
- DynamoDBReadPolicy:
TableName: ExerciseRecords_SAM_Test
- Statement:
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: '*'
Events:
WeeklySchedule:
Type: ScheduleV2
Properties:
ScheduleExpression: cron(0 21 ? * SAT *)
ScheduleExpressionTimezone: Asia/Tokyo
やったこと
-
sam initで雛形を作成 -
template.yamlにLambda + DynamoDBを定義 -
sam build→sam deploy - Lambdaを叩いてDynamoDBに記録が入ることを確認
動作結果
sam remote invoke でLambdaを叩き、DynamoDBにレコードが1件書き込まれることを確認)。
つまずいた点
- Python 3.14を指定していたが手元は3.13だったため、ビルドが失敗した。テンプレートのPythonバージョンを変えて解決した。
今後やりたいこと
- DynamoDB Streams の追加(運動当日の夜に頑張ったねメールがあると嬉しいから)
まとめ・所感
コンソールで作った構成を、コードから再現可能な形にできました。sam delete → sam deploy で消して戻せることも確認済みです。
最初はSAMの構文意味がわからなかったけど、公式ドキュメントを参照しながら読むと段々と内容を理解できてきてある程度読めるようになった。今後は公式ドキュメントをベースに内容の理解に努めていきたい。



