AWS Serverless Application Model (AWS SAM)使ってみる: 変換後のリソースのyamlを見てみる

AWS SAMとはAWSが提供しているサーバーレスフレームワークです。

APIGateway, Lambda, DynamoDBをすべてCloudFormationで作成するのは難易度が高いため、
簡素化したyamlAWS側でCloudFormation標準のものに変換して各リソースを作成してくれます。

なお、作成されるのは標準化されたものですので、自由に柔軟なリソース管理をしたい場合はSAMではなく、
CloudFormationで作成した方がよいと思います。
誰でも、簡単にという点ではSAMの標準にならって作成するのが一番よいと思われます。

詳細をAWS SAMのリソースの指定とAWS側での変換後のリソースで比較してみます。

Transform: AWS::Serverless-2016-10-31

これを指定することでyamlがAWS側でCloudFormation標準の書き方に変更されます
aws cloudformation create-stackコマンドのyamlでは使えません
aws cloudformation deployコマンドのyamlに使用できます

AWS::Serverless::SimpleTable

このぐらいなら、AWS::DynamoDB::Tableで問題ないですね。

To use the more advanced functionality of DynamoDB, use an AWS::DynamoDB::Table resource instead.

だそうですので、最初からAWS::DynamoDB::Tableを使えばいい気がします。

ユーザーが指定するリソース

  Table:
    Type: AWS::Serverless::SimpleTable

AWSで変換後のリソース

  Table:
    Type: AWS::DynamoDB::Table
    Properties:
      ProvisionedThroughput:
        WriteCapacityUnits: 5
        ReadCapacityUnits: 5
      AttributeDefinitions:
      - AttributeName: id
        AttributeType: S
      KeySchema:
      - KeyType: HASH
        AttributeName: id

AWS::Serverless::Function

以下を作成してくれるようです。
これなら、最初からCloudFormationで作成した方が自由で柔軟性がありそうです。
指定したいけど、SAM非対応な指定項目が出そうな気がします。

  • AmazonDynamoDBReadOnlyAccessとAWSLambdaBasicExecutionRoleのロール
  • DeploymentとProdStage
  • swaggerでのRestApi
  • test用とprod用のLambda::Permission
  • Lambda関数

ユーザーが指定するもの

  GetFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.get
      Runtime: nodejs6.10
      CodeUri: s3://keiwt-hogeless-api/api.zip
      Policies: AmazonDynamoDBReadOnlyAccess
      Environment:
        Variables:
          TABLE_NAME: !Ref Table
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /resource/{resourceId}
            Method: get

AWSで変換後のリソース

IAM

  GetFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action:
          - sts:AssumeRole
          Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com

ApiGateway

  ServerlessRestApiProdStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId:
        Ref: ServerlessRestApiDeployment77d5a05be2
      RestApiId:
        Ref: ServerlessRestApi
      StageName: Prod

  ServerlessRestApiDeployment77d5a05be2:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId:
        Ref: ServerlessRestApi
      Description: 'RestApi deployment id: 77d5a05be2514b45c5df870702eac8162155cb25'
      StageName: Stage

  ServerlessRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Body:
        info:
          version: '1.0'
          title:
            Ref: AWS::StackName
        paths:
          "/resource/{resourceId}":
            put:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PutFunction.Arn}/invocations
              responses: {}
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetFunction.Arn}/invocations
              responses: {}
            delete:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DeleteFunction.Arn}/invocations
              responses: {}
        swagger: '2.0'

Lambda

  GetFunctionGetResourcePermissionTest:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: GetFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/resource/{resourceId}
        - __Stage__: "*"
          __ApiId__:
            Ref: ServerlessRestApi

  GetFunctionGetResourcePermissionProd:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: GetFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/resource/{resourceId}
        - __Stage__: Prod
          __ApiId__:
            Ref: ServerlessRestApi

  GetFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: keiwt-hogeless-api
        S3Key: api.zip
      Tags:
      - Value: SAM
        Key: lambda:createdBy
      Environment:
        Variables:
          TABLE_NAME:
            Ref: Table
      Handler: index.get
      Role:
        Fn::GetAtt:
        - GetFunctionRole
        - Arn
      Runtime: nodejs6.10

以下はyaml全体のbeforeとafterです。


example-before.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  GetFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.get
      Runtime: nodejs6.10
      CodeUri: s3://keiwt-hogeless-api/api.zip
      Policies: AmazonDynamoDBReadOnlyAccess
      Environment:
        Variables:
          TABLE_NAME: !Ref Table
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /resource/{resourceId}
            Method: get

  PutFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.put
      Runtime: nodejs6.10
      CodeUri: s3://keiwt-hogeless-api/api.zip
      Policies: AmazonDynamoDBFullAccess
      Environment:
        Variables:
          TABLE_NAME: !Ref Table
      Events:
        PutResource:
          Type: Api
          Properties:
            Path: /resource/{resourceId}
            Method: put

  DeleteFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.delete
      Runtime: nodejs6.10
      CodeUri: s3://keiwt-hogeless-api/api.zip
      Policies: AmazonDynamoDBFullAccess
      Environment:
        Variables:
          TABLE_NAME: !Ref Table
      Events:
        DeleteResource:
          Type: Api
          Properties:
            Path: /resource/{resourceId}
            Method: delete

  Table:
    Type: AWS::Serverless::SimpleTable

example-after.yaml

---
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  GetFunctionGetResourcePermissionTest:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: GetFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/resource/{resourceId}
        - __Stage__: "*"
          __ApiId__:
            Ref: ServerlessRestApi
  GetFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action:
          - sts:AssumeRole
          Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
  DeleteFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: keiwt-hogeless-api
        S3Key: api.zip
      Tags:
      - Value: SAM
        Key: lambda:createdBy
      Environment:
        Variables:
          TABLE_NAME:
            Ref: Table
      Handler: index.delete
      Role:
        Fn::GetAtt:
        - DeleteFunctionRole
        - Arn
      Runtime: nodejs6.10
  PutFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: keiwt-hogeless-api
        S3Key: api.zip
      Tags:
      - Value: SAM
        Key: lambda:createdBy
      Environment:
        Variables:
          TABLE_NAME:
            Ref: Table
      Handler: index.put
      Role:
        Fn::GetAtt:
        - PutFunctionRole
        - Arn
      Runtime: nodejs6.10
  DeleteFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action:
          - sts:AssumeRole
          Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
  ServerlessRestApiProdStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId:
        Ref: ServerlessRestApiDeployment77d5a05be2
      RestApiId:
        Ref: ServerlessRestApi
      StageName: Prod
  ServerlessRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Body:
        info:
          version: '1.0'
          title:
            Ref: AWS::StackName
        paths:
          "/resource/{resourceId}":
            put:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PutFunction.Arn}/invocations
              responses: {}
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetFunction.Arn}/invocations
              responses: {}
            delete:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DeleteFunction.Arn}/invocations
              responses: {}
        swagger: '2.0'
  PutFunctionPutResourcePermissionTest:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: PutFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/resource/{resourceId}
        - __Stage__: "*"
          __ApiId__:
            Ref: ServerlessRestApi
  GetFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: keiwt-hogeless-api
        S3Key: api.zip
      Tags:
      - Value: SAM
        Key: lambda:createdBy
      Environment:
        Variables:
          TABLE_NAME:
            Ref: Table
      Handler: index.get
      Role:
        Fn::GetAtt:
        - GetFunctionRole
        - Arn
      Runtime: nodejs6.10
  GetFunctionGetResourcePermissionProd:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: GetFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/resource/{resourceId}
        - __Stage__: Prod
          __ApiId__:
            Ref: ServerlessRestApi
  ServerlessRestApiDeployment77d5a05be2:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId:
        Ref: ServerlessRestApi
      Description: 'RestApi deployment id: 77d5a05be2514b45c5df870702eac8162155cb25'
      StageName: Stage
  DeleteFunctionDeleteResourcePermissionProd:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DeleteFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/resource/{resourceId}
        - __Stage__: Prod
          __ApiId__:
            Ref: ServerlessRestApi
  DeleteFunctionDeleteResourcePermissionTest:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DeleteFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/resource/{resourceId}
        - __Stage__: "*"
          __ApiId__:
            Ref: ServerlessRestApi
  Table:
    Type: AWS::DynamoDB::Table
    Properties:
      ProvisionedThroughput:
        WriteCapacityUnits: 5
        ReadCapacityUnits: 5
      AttributeDefinitions:
      - AttributeName: id
        AttributeType: S
      KeySchema:
      - KeyType: HASH
        AttributeName: id
  PutFunctionPutResourcePermissionProd:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: PutFunction
      SourceArn:
        Fn::Sub:
        - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/resource/{resourceId}
        - __Stage__: Prod
          __ApiId__:
            Ref: ServerlessRestApi
  PutFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action:
          - sts:AssumeRole
          Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com