sttsのソースコードMemoブログ

色々やってみた結果のMemo

AWS SAMでカスタムドメイン設定をした記録

template.ymlは下記とした

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  customdomain-dynamodb-app

  Customdomain API-gateway and DynamoDB my Example

Parameters:
  DomainName:
    Type: String
    Default: api.my.example.com
  HostedZoneName:
    Type: String
    Default: my.example.com. # don't miss the  dot at the end

Globals:
  Api:
   Cors:
     AllowMethods: "'*'"
     AllowHeaders: "'*'"
     AllowOrigin: "'*'"
  Function:
    Timeout: 5
    Environment:
      Variables:
        TABLE_NAME: !Ref TodoDyanmoTable # Lambda実行時の環境変数TABLE_NAMEで参照可能になる 

Resources:
  TodoApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: v1
      Auth:         # Lambdaオーソライザーを使う
        # 参考DefaultAuthorizerは使えない: https://nibral.hateblo.jp/entry/2019/04/24/103036
        #DefaultAuthorizer: MyLambdaTokenAuthorizer
        Authorizers:
          MyLambdaTokenAuthorizer:
            FunctionPayloadType: REQUEST
            FunctionArn: !GetAtt MyAuthFunction.Arn
            Identity:
              Headers:
                - Authorization
              ReauthorizeEvery: 1 # 認証結果を1秒間だけ結果をキャッシュする模様


  APIDomainName:
    Type: AWS::ApiGateway::DomainName
    Properties:
      CertificateArn: arn:aws:acm:us-east-1:XXXX:certificate/XXXXXXXXX # 証明書のarn バージニア北部で作る
      DomainName: !Ref DomainName
  APIBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      DomainName: !Ref APIDomainName
      RestApiId: !Ref TodoApiGateway
      Stage: v1

  HelloTodoFunction: # GET用
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_todo/
      Handler: app.get_lambda_handler
      Runtime: python3.7
      Policies:
       - DynamoDBCrudPolicy:
           TableName: !Ref TodoDyanmoTable
      Events:
        HelloTodo:
          Type: Api
          Properties:
            Path: /todo
            Method: get
            Auth:
              Authorizers: MyLambdaTokenAuthorizer
            RestApiId:
              Ref: TodoApiGateway
  POSTTodoFunction: # POST用
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_todo/
      Handler: app.post_lambda_handler
      Runtime: python3.7
      Policies:
       - DynamoDBCrudPolicy:
           TableName: !Ref TodoDyanmoTable
      Events:
        HelloTodo:
          Type: Api
          Properties:
            Path: /todo
            Method: post
            Auth:
              Authorizers: MyLambdaTokenAuthorizer
            RestApiId:
              Ref: TodoApiGateway
  TodoDyanmoTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PROVISIONED # 無料枠はこっち
      AttributeDefinitions:
        - AttributeName: "user" # 属性定義
          AttributeType: "S"    # 文字列
        - AttributeName: "id"
          AttributeType: "N"    # 数値
      KeySchema:
        - AttributeName: "user"
          KeyType: "HASH" # ハッシュキー
        - AttributeName: "id"
          KeyType: "RANGE" # レンジキー
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
  MyAuthFunction: # Token認証用lambda
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_todo/
      Handler: app.auth_request_lambda_handler
      Runtime: python3.7

Outputs:
  HelloTodoFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloTodoFunction.Arn

参考にしたサイト↓ではroute53の設定も入っていたが上手く動作しなかったため手動route53をで設定した。

https://github.com/awslabs/serverless-application-model/issues/40

route53の設定は下記を参照(route53にはAレコードでエリアス、cloudfront.netで終わるものを設定する)

https://www.beex-inc.com/blog/apigateway-using-custom-domain/

 

app.pyは下記とした。

import os
import boto3
from boto3.dynamodb.conditions import Key, Attr
import json
import decimal
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# import requests

def auth_request_lambda_handler(event, context):
    # 参考: https://dev.classmethod.jp/server-side/serverless/lambda-authorizer/
    logger.info('## ENVIRONMENT VARIABLES')
    logger.info(os.environ)
    logger.info('## EVENT')
    logger.info(event) 
    token = event["headers"]["Authorization"]
    logger.info('## token='+str(token) )
    if token == "1":
        logger.info('## sucess' )
        myaddisonalinfo = {
          "id": 1,
          "hoge": "foo"
        }
        tmp_str = json.dumps(myaddisonalinfo)
        base64_context = base64.b64encode(tmp_str.encode('utf8'))
        return {
            "principalId" : token,
            "policyDocument" : {
                "Version" : "2012-10-17",
                "Statement" : [
                    {
                        "Action": "*",
                        "Effect": "Allow",
                        "Resource": "arn:aws:execute-api:*:*:*/*"  
                    }
                ]
            },
            'context': {
                'additional_info': base64_context.decode('utf-8')
            }
        }
    # 1以外の時はNG
    return {
        "principalId" : 2,
        "policyDocument" : {
            "Version" : "2012-10-17",
            "Statement" : [
                {
                    "Action": "*",
                    "Effect": "Deny",
                    "Resource": "arn:aws:execute-api:*:*:*/*"  
                }
            ]
        }
    }

def decimal_default_int(obj):
    """
    json.dumps用
    https://qiita.com/ekzemplaro/items/5fa8900212252ab554a3
    https://forums.aws.amazon.com/message.jspa?messageID=689708
    """
    if isinstance(obj, decimal.Decimal):
        return int(obj)
    raise TypeError

def getTodos(user:str, dynamo):
    """
    指定されたユーザのTodosを取得
    """
    tmp_q = dynamo.query( KeyConditionExpression = Key('user').eq(user) )
    # queryした結果のItemsに本当の結果が入っている
    res = tmp_q["Items"]

    return res

def putTodos(body_dict):
    dynamo = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME'])
    item = {
        "user": body_dict["user"],
        "id": body_dict["id"],
        "todo": body_dict["todo"]
    }
    dynamo.put_item(Item = item)

def post_lambda_handler(event, context):
    """
    curl -X POST -H 'Content-Type:application/json' -d '{ "user": "foo", "id": 2, "todo": "aaabbb" }' "https://api.my.example.com/todo" -H 'Authorization:1'
    """
    logger.info('## ENVIRONMENT VARIABLES')
    logger.info(os.environ)
    logger.info('## EVENT')
    logger.info(event)

    # eventのbodyがPOSTしたパラメータ
    body = event['body']
    logger.info('## BODY')
    logger.info(body)
    logger.info('## json.loads')
    body_dict = json.loads(body)

    logger.info('## putTodos()')
    putTodos(body_dict)
    ret_dict = {
        "statusCode": 200,
        "body": '{ "result": "ok" }',
        "headers": {
             "Access-Control-Allow-Origin": "*"
          }
    }
    return ret_dict

def get_lambda_handler(event, context):
    logger.info('## ENVIRONMENT VARIABLES')
    logger.info(os.environ)
    logger.info('## EVENT')
    logger.info(event)
    #logger.info('## CONTEXT')
    #logger.info(context)
    
    # eventのqueryStringParametersがGETしたパラメータ
    q_string_p = event["queryStringParameters"]
    # 想定文字列
    # curl "https://api.my.example.com/todo?user=foo" -H 'Authorization:1'

    taget_user = q_string_p["user"]

    dynamo = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME'])
    res = getTodos(taget_user, dynamo)

    ret_dict = {
        "statusCode": 200,
        "body": json.dumps({"todos": res}, default=decimal_default_int),
"headers": { "Access-Control-Allow-Origin": "*" } } return ret_dict if __name__ == '__main__': print("-- sart --") dynamo = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME']) res = getTodos("foo", dynamo) print(res) print("---") ret_dict = { "statusCode": 200, "body": json.dumps({"todos": res}, default=decimal_default_int) } print(ret_dict)