Cloud9でAWS SAM

AWS コンソールから行う作業
Serverless Framework 用の IAM を作成
- Identity & Access Management (IAM)ページにアクセス
- Users をクリックし、Add user をクリック
- 名前はなんでも良いが「Serverless-Admin」などわかりやすい名前とする
- Programmatic access を有効にチェックし Next をクリック
- Attach existing policies directly をクリック
- AdministratorAccessを検索して選択し Next をクリック(嫌なら必要なポリシー作成すること)
- 設定項目を確認の上、Create user をクリック
API Key と Secret を表示しメモしておく。次のステップで必要になる。

Cloud9の初期設定
docker-compose のインストール
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
DynamoDB Local のイメージをPull
docker pull amazon/dynamodb-local
docker-compose.yml の作成
# ./docker-compose.yml
version: "3"
services:
dynamodb-local:
container_name: dynamodb
image: amazon/dynamodb-local
build: ./
ports:
- 8000:8000
command: -jar DynamoDBLocal.jar -dbPath /data -sharedDb
volumes:
- ./data:/data
networks:
- lambda-local
networks:
lambda-local:
external: true
DynamoDB Local の 起動テスト
docker network create lambda-local
docker-compose up
# dynamodb is up-to-date
# Attaching to dynamodb ...
AWS Credencial の設定
Cloud9デフォルトのIAMから上書きする / 別の設定を作成して切り替える
aws configure
jq のインストール
curlコマンドの出力結果を綺麗に表示してくれます。
sudo yum install jq
# 使う時はパイプする
# $ curl http://127.0.0.1:3000/hello | jq

AWS SAM の開始
SAM プロジェクトを作成
このコマンドは、プロジェクト名として指定した名前のディレクトリとファイル群を作成します。
sam init
プロジェクトのビルド
sam buildコマンドは、アプリケーションの依存関係をビルドし、ソースコードを.aws-sam/build以下のフォルダにコピーします。
# template.yamlがあるディレクトリに移動する
cd {project-name}
sam build
プロジェクトのデプロイ
ガイドで設定を確認しながらデプロイします。
sam buildコマンドで構築したビルドをパッケージ化して、S3バケットにアップロードされます。さらにAWS CloudFormationスタックが作成され、それに基づいてアプリケーションがデプロイされます。
アプリケーションがHTTPエンドポイントを作成した場合、sam deployが生成する出力には、テストアプリケーションのエンドポイントURLも表示されます。curlを使って、そのエンドポイントURLを使ってアプリケーションにリクエストを送ることができます。例えば、以下のようになります。
sam deploy --guided
ローカルでのテスト
作成したリソースをデプロイすることなくテストすることができます。
start-api
コマンドは、REST APIのエンドポイントを複製しローカルで起動します。裏側ではローカルで関数を実行するためにコンテナをダウンロードしています。
sam local start-api
クリーンナップ
作成したリソースを削除します。
aws cloudformation delete-stack --stack-name {name} --region {ap-northeast-1}
サンプルLambdaイベントの作成
以下のコマンドで、Lambdaをローカル実行する時に便利なイベントオブジェクトのサンプルを作成することができる。
sam local generate-event api > post_event.json
Lambda関数のローカル実行
イベントオブジェクトなど渡したりできる
sam local invoke HogeFunction -e events/event.json

CORS の許可
Globals:
Api:
Cors:
AllowHeaders: "'*'"
AllowMethods: "'POST, GET'"
AllowOrigin: "'*'"
Function:
Timeout: 3

Parameters の設定
Parameters
はスタックの作成時や更新時にテンプレートに渡す値です。環境変数のように扱うこともできます。
繰り返し必要なパラメータをまとめておくと便利です。
Parameters:
AppName:
Type: String
Description: Name of the application
ClientDomeins:
Type: CommaDelimitedList
Description: Array of domeins alllowed to use the UserPool
AdminEmail:
Type: String
Description: Email address of admin
AddGroupToScopes:
Type: String
AllowedValues:
- 'true'
- 'false'
Default: 'false'

Conditions の設定
スタックの作成または更新時に、特定のリソースが作成されるかどうか、または特定のリソースのプロパティに値が割り当てられるかどうかを制御する条件。例えば、スタックが本番環境用かテスト環境用かに依存するリソースを条件付きで作成することができます。
Parameters:
AddGroupToScopes:
Type: String
AllowedValues:
- 'true'
- 'false'
Default: 'false'
Conditions:
ScopeGroups:
# ScopeGroups = (AddGroupToScopes == 'true') -> Bool
!Equals [!Ref AddGroupToScopes, 'true']

Cognito の設定
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam101
Sample SAM Template for sam101
Parameters:
AppName:
Type: String
Description: Name of the application
ClientDomeins:
Type: CommaDelimitedList
Description: Array of domeins alllowed to use the UserPool
AdminEmail:
Type: String
Description: Email address of admin
AddGroupToScopes:
Type: String
AllowedValues:
- 'true'
- 'false'
Default: 'false'
Conditions:
ScopeGroups:
!Equals [!Ref AddGroupToScopes, 'true']
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Api:
Cors:
AllowHeaders: "'*'"
AllowMethods: "'*'"
AllowOrigin: "'*'"
Function:
Timeout: 3
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
# !Sub について https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html#w2aac33c28c59b7
UserPoolName: !Sub ${AppName}-UserPool
Policies:
PasswordPolicy:
MinimumLength: 8
AutoVerifiedAttributes:
- email
UsernameAttributes:
- email
Schema:
- AttributeDataType: String
Name: email
Required: false
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref UserPool
ClientName: !Sub ${AppName}-UserPoolClient
GenerateSecret: false # set to false for web clients
SupportedIdentityProviders:
- COGNITO
CallbackURLs: !Ref ClientDomeins
AllowedOAuthFlowsUserPoolClient: true
AllowedOAuthFlows:
- code
- implicit # for testing with postman
AllowedOAuthScopes:
- email
- openid
- profile
UserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Sub ${AppName}-${AWS::AccountId}
UserPoolId: !Ref UserPool
AdminUserGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
GroupName: Admins
Description: Admin user group
Precedence: 0
UserPoolId: !Ref UserPool
AdminUser:
Type: AWS::Cognito::UserPoolUser
Properties:
Username: !Ref AdminEmail
DesiredDeliveryMediums:
- EMAIL
ForceAliasCreation: true
UserAttributes:
- Name: email
Value: !Ref AdminEmail
UserPoolId: !Ref UserPool
AddUserToGroup:
Type: AWS::Cognito::UserPoolUserToGroupAttachment
Properties:
GroupName: !Ref AdminUserGroup
Username: !Ref AdminUser
UserPoolId: !Ref UserPool
Outputs:
UserPoolId:
Description: "UserPoolId"
Value: !Ref UserPool
Export:
Name: !Sub ${AppName}:UserPoolId
UserPoolClientId:
Description: "Application client Id"
Value: !Ref UserPoolClient
AuthUrl:
Description: "URL used fro authentication"
Value: !Sub https://${UserPoolDomain}.auth.${AWS::Region}.amazoncognito.com

DynamoDB テーブルの定義
- PK、SK には具体的な名前はつけないで、Prefixをつけ管理できるようにしておく。
Resources:
HogeHogeTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: employerTable
AttributeDefinitions:
-
AttributeName: "PK"
AttributeType: "S"
-
AttributeName: "SK"
AttributeType: "S"
KeySchema:
-
AttributeName: "PK"
KeyType: "HASH"
-
AttributeName: "SK"
KeyType: "RANGE"
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1

DynamoDBを操作するLambdaハンドラ
以下のようにすることで簡単にDB操作権限を付与できる
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref ParameterTable
Resources:
PostOrganizations:
Type: AWS::Serverless::Function
Properties:
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref ParameterTable
CodeUri: organizations/
Handler: post.lambdaHandler
Runtime: nodejs14.x
Environment:
Events:
CreateOrganization:
Type: Api
Properties:
Path: /organizations
Method: post