Open9

Cloud9でAWS SAM

ShinKanoShinKano

AWS コンソールから行う作業

Serverless Framework 用の IAM を作成

  1. Identity & Access Management (IAM)ページにアクセス
  2. Users をクリックし、Add user をクリック
  3. 名前はなんでも良いが「Serverless-Admin」などわかりやすい名前とする
  4. Programmatic access を有効にチェックし Next をクリック
  5. Attach existing policies directly をクリック
  6. AdministratorAccessを検索して選択し Next をクリック(嫌なら必要なポリシー作成すること)
  7. 設定項目を確認の上、Create user をクリック

API Key と Secret を表示しメモしておく。次のステップで必要になる。

ShinKanoShinKano

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
ShinKanoShinKano

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
ShinKanoShinKano

CORS の許可

Globals:
  Api:
    Cors:
      AllowHeaders: "'*'"
      AllowMethods: "'POST, GET'"
      AllowOrigin: "'*'"

  Function:
    Timeout: 3
ShinKanoShinKano

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'
ShinKanoShinKano

Conditions の設定

スタックの作成または更新時に、特定のリソースが作成されるかどうか、または特定のリソースのプロパティに値が割り当てられるかどうかを制御する条件。例えば、スタックが本番環境用かテスト環境用かに依存するリソースを条件付きで作成することができます。

Parameters:
  AddGroupToScopes:
    Type: String
    AllowedValues:
      - 'true'
      - 'false'
    Default: 'false'

Conditions:
  ScopeGroups:
    # ScopeGroups =  (AddGroupToScopes == 'true') -> Bool
    !Equals [!Ref AddGroupToScopes, 'true']
ShinKanoShinKano

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

ShinKanoShinKano

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
ShinKanoShinKano

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