Serverless Frameworkでやったことをまとめてみた

Serverless Framework v1系 (v1.2.1)を触ってみたので、やったことのメモ。（特にresourceまわり

インストール方法など基本的な使用方法は以下の記事が詳しいです。

- [Serverless Framework v1.0の使い方まとめ](http://qiita.com/horike37/items/b295a91908fcfd4033a2)


# やったこと
## S3バケットを作る
webサイトのホスティングが目的だったので、`WebsiteConfiguration`を指定しています。
特にハマりどころはありませんでした。

``` serverless.yml
resources:
  Resources:
    MyBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: my-bucket
        AccessControl: PublicRead
        WebsiteConfiguration:
          IndexDocument: index.html
```

### 参考
- http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html

## DynamoDB
### テーブル作成
こちらもCloudFormationのルール通りで問題ありません。

``` serverless.yml
resources:
  Resources:
    MyTable:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        TableName: MyTable
        AttributeDefinitions:
          -
            AttributeName: Id
            AttributeType: S
        KeySchema:
          -
            AttributeName: Id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
```

#### 参考
- http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html

### DynamoDB Streamsとトリガーの管理
#### Streamの作成
`StreamSpecification`を追加することでストリームを作成できます。

``` serverless.yml
resources:
  Resources:
    MyTable:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        TableName: MyTable
        AttributeDefinitions:
          -
            AttributeName: Id
            AttributeType: S
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        # ここを追加
        StreamSpecification:
          StreamViewType: NEW_IMAGE
```

#### トリガーの設定
トリガーはfunctionのevent.streamに作成したストリームのarnを指定します。

```
functions:
  streamTest:
    handler: handler.hello
    events:
      - stream:
          arn: arn:aws:dynamodb:us-east-1:999999999999:table/MyTable/stream/2016-12-01T07:09:53.720
```

arnを直書きだと変更が入った場合に面倒ですね・・。
ということで以下の用に関数`Fn::GetAtt`を使う形に書き換えてみます。

```
functions:
  streamTest:
    handler: handler.hello
    events:
      - stream:
          arn:
            "Fn::GetAtt":
              - MyTable
              - Arn
```

ですが、実行すると以下のようなエラーが発生

```
  Type Error ---------------------------------------------

     EventSourceArn.split is not a function
```

どうやら[バグ](https://github.com/serverless/serverless/issues/2365)により動作しないようです。
Kinesis Streamも同様の理由により関数による指定ができません。
現在はarnを直書きするしかないようです。

既に[PR](https://github.com/serverless/serverless/pull/2488)が出ているようなので対応を待ちましょう。

#### 参考
- http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-streamspecification.html


## ApiGatewayの認証設定
### カスタム認証

```
functions:
  # 認証用関数を定義
  authFunc:
    handler: handler.auth

  hello:
    handler: handler.hello
    events:
      - http:
          path: hogehoge
          method: get
          # 認証用の関数を指定
          authorizer: authFunc
```

カスタム認証の関数の実装については省略します。

#### 参考
- https://serverless.com/framework/docs/providers/aws/events/apigateway/#http-endpoints-with-custom-authorizers
- http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/use-custom-authorizer.html


### IAM認証
リソース名はApiGatewayMethodV1MyFuncGetの部分は対象APIのLogicalIDを指定する必要があります。
function側の指定は特に必要ありません。

``` serverless.yml
resources:
  Resources:
    ApiGatewayMethodV1MyFuncGet: # 対象のLogicalIDを指定
      Type: AWS::ApiGateway::Method
      Properties:
        HttpMethod: GET
        AuthorizationType: AWS_IAM
```

#### 参考
- http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-method.html
- http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/permissions.html

## vpcリソースへのアクセス
以下の記事が参考になりますが、1点ハマりどころがありました。

- [[Serverless Framework]Lambda FunctionをVPCの中に入れる](http://dev.classmethod.jp/etc/serverless-framework-lambda-function-run-in-vpc/)

以下の内容でデプロイ時にエラーが発生するので困っていましたが、
どうやら **初回デプロイ時** にvpcの記述があるとエラーが発生するようです。

``` serverless.yml
service: sls-sample

provider:
  name: aws
  runtime: nodejs4.3
  cfLogs: true

  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ec2:CreateNetworkInterface"
        - "ec2:DescribeNetworkInterfaces"
        - "ec2:DeleteNetworkInterface"
      Resource:
        - "*"

functions:
  hello:
    handler: handler.hello
    vpc:
      securityGroupIds:
        - sg-xxxxxx
      subnetIds:
        - subnet-xxxxxx
    events:
      - http:
          path: hogehoge
          method: get
          cors: true
```

エラー内容

```
An error occurred while provisioning your stack: HelloLambdaFunction
- Your access has been denied by EC2, please make sure
your function execution role have permission to CreateNetworkInterface.
EC2 Error Code: UnauthorizedOperation. EC2 Error Message:
You are not authorized to perform this operation..
```

以下の用に一度vpc設定部分をコメントアウトしてデプロイ後、コメントアウト解除で再デプロイすることで解決しました。

``` serverless.yml
service: sls-sample

provider:
  name: aws
  runtime: nodejs4.3
  cfLogs: true

  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ec2:CreateNetworkInterface"
        - "ec2:DescribeNetworkInterfaces"
        - "ec2:DeleteNetworkInterface"
      Resource:
        - "*"

functions:
  hello:
    handler:handler.hello
    # ここを一度コメントアウト
    # vpc:
    #   securityGroupIds:
    #     - sg-xxxxxx
    #   subnetIds:
    #     - subnet-xxxxxx
    events:
      - http:
          path: hogehoge
          method: get
          cors: true
```

**※ v1.2.1で確認していましたが、最新のv1.3.0で修正されたようです**

### 参考
- https://serverless.com/framework/docs/providers/aws/guide/functions/#vpc-configuration

## その他ハマったところ
以下の内容でデプロイを実行するとエラーが発生します。

``` serverless.yml
service: sls-sample

provider:
  name: aws
  runtime: nodejs4.3
  cfLogs: true
  
function:
  foo:
    handler: handler.foo
  foobar:
    handler: handler.foobar
```

エラー内容

```
  Serverless Error ---------------------------------------

     An error occurred while provisioning your stack: IamPolicyLambdaExecution
     - Template error: LogGroup /aws/lambda/sls-sample-dev-foo
     doesn't exist.
```

### 発生条件と解決方法
[バグ](https://github.com/serverless/serverless/issues/2614)が報告されていました。
どうやら、定義したfunction名が他のfunction名に含まれる(前方一致)場合にエラーが発生するようです。

例えば以下のようにするとエラーは発生しません。

``` serverless.yml
provider:
  name: sls-sample
  runtime: nodejs4.3
  cfLogs: true
  
function:
  foo:
    handler: handler.foo
  # 先頭に'x'を追加
  xfoobar:
    handler: handler.foobar
```

## できなかったこと
- API Gatewayのモデル作成及びマッピング

現バージョンでは対応してないようです。[plugin](https://github.com/HyperBrain/serverless-models-plugin)がありましたが
v1系は未対応のようです。
今回はaws-cli叩くスクリプトを書いて対応しましたが、もっと他にいい方があるぞという方は教えていただけるとうれしいです。

---

投稿直前に新たなpluginが公開されていることに気づきました。
こちらは今度試してみようと思います。

- [9cookies/serverless-aws-models](https://github.com/9cookies/serverless-aws-models)