【Lambda】SAMではまったこと【Node.js】


SAMを触ってみてはまったことなどをメモします。

## template.yamlのRoleでエラー 1
`sam init`でHello Worldプロジェクトを作成し、
`sam build` と `sam deploy`がうまくいくところまで確認できた後、
template.yamlにRoleを追加したらエラーになりました。

```yaml
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Role: "arn:aws:iam::xxxxxxxxxx:role/xxxxxxxxxx" # ここを追加
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs16.x
      Timeout: 120
      Architectures:
```


buildはできましたがdeployでエラーです。
```
Waiting for changeset to be created..
Error: Failed to create changeset for the stack: sam-app, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Template error: instance of Fn::GetAtt references undefined resource HelloWorldFunctionRole
```

わかりにくいですが、Roleを設定しているのに`Output`で暗黙的なRoleを作ろうとしているのがだめみたいです。
なので、Outputの以下をコメントアウトします。

```yaml
Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  # HelloWorldFunctionIamRole:
  #   Description: "Implicit IAM Role created for Hello World function"
  #   Value: !GetAtt HelloWorldFunctionRole.Arn

```

無事にSuccessしました。


## template.yamlのRoleでエラー 2
次に、RoleをGlobalに定義しようと思い実行しましたが、deployでまたもやエラーが出ます。

```yaml
Globals:
  Function:
    Timeout: 3
    Role: "arn:aws:iam::xxxxxxxxxx:role/xxxxxxxxxx" # ここを追加
```

エラー
```
 "'Role' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'Layers', 'DeploymentPreference', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn', 'Architectures', 'EphemeralStorage', 'FunctionUrlConfig']")]
```

こちらはエラーメッセージに書いてある通りで、GlobalにRoleは定義できないみたいです。
[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html)に定義できるプロパティ一覧が書いてあります

## layerビルドでエラー `Cannot find module`

こんな構成でLayerを作り、レイヤーのLogical IDを指定してビルドしたらエラーになりました
```: sam-app
.
├── README.md
├── events
│   └── event.json
├── lambdas
│   ├── func1
│   │   ├── app.js
│   │   ├── package-lock.json
│   │   ├── package.json
│   │   └── tests
├── layers
│   └── layer1
│       └── nodejs
│           └── node_modules
│               ├── mylayer.js
│               ├── package.json
│               └── package.lock.json
├── package-lock.json
├── package.json
├── samconfig.toml
└── template.yaml
```

```yaml:template.yaml

...

Resources:
  Func1:
    Type: AWS::Serverless::Function
    Properties:
      Role: "arn:aws:iam::xxxxxxxxxx:role/xxxxxxxxxx"
      CodeUri: lambdas/func1/
      Handler: app.lambdaHandler
      Runtime: nodejs16.x
      Timeout: 120
      Architectures:
      Layers:                             # ここを追加                   
        - !Ref MyLayer
  
  MyLayer:　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　   # ここを追加
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: mylayer
      Description: layer for me
      ContentUri: layers/layer1/
      CompatibleRuntimes:
        - nodejs16.x
    Metadata:
      BuildMethod: nodejs16.x

・・・
```

実行コマンド
```
sam build MyLayer
```

エラー
```
{
	"errorType": "Runtime.ImportModuleError",
	"errorMessage": "Error: Cannot find module 'MyLayer'\nRequire stack:\n- /var/task/app.js\n- /var/runtime/index.mjs"
}
```

これ、ちょっとよくわかっていないのですが、そもそもLayerはBuildしなくていいのかもしれないです。
zipファイルをs3に保存しそれをレイヤーに反映するという仕組みを自動でやってくれているように見えます。
buildせずにdeployしたところ、エラーは起きずに正常に更新されていました。

参考にさせていただきました
- [AWS SAMによるLambda Layers ＆ Layers利用Functionの作成手順と運用観点での注意点（2021年3月, SAM 1.19版）](https://horizoon.jp/post/2021/03/05/lambda_layer_nodejs_2/)
- [Lambda Layers をnode.js(SAM)で試してみる](https://qiita.com/monamu/items/96d63dd2151a8ab7e6cf)


## layerのデプロイで `Error: No changes to deploy. `

layerを修正して`sam deploy`した時に以下のエラーになりました
```
Error: No changes to deploy. Stack aws-sam-layers-template is up to date
```
差分がないのでdeployできないとのこと。`sam build` した後でもダメです。
layerだけだと変更を検知してくれない？ようなので、lambda関数の方に何かコメントを入れるなどの修正を加えて`sam build`、`sam deploy`したところデプロイできて動くようになりました。


## ログファイル出力で`Error: Got unexpected extra argument`
ルートディレクトリに `logfile.txt`というログ出力ファイルを作成し sam local invokeする際に`--log-file logfile.txt`というオプションをつけて実行したところエラーになりました。

コマンド
```
sam local invoke Func1 --event events/event.json --debug --log-file logfile.txt
```
エラー
```
Error: Got unexpected extra argument (logfile.txt)
```

原因はよくわかってませんがオプションの順番を変えたらうまく動きました
```
sam local invoke Func1 --log-file logfile.txt --event events/event.json --debug
```
`--event`の後に`--log-file`を持ってきてもエラーになりませんでした。

以上