AWS SAMのコマンドをまとめてみた

# はじめに

AWS SAMの各コマンドをhelpを元にまとめてみました。

なお、最新情報は公式ドキュメントを参照してください。

## 環境

* Windows 10
* aws sam (Ver0.5.0)
* Python 3.6

## インストール等

こちらを参照ください。

* [AWS SAMでLambdaとAPI Gatewayをデプロイしてみた(Python)](https://qiita.com/gnk263/items/ec57a454aec53808c1d4)

# コマンド組み合わせの一覧

OPTIONSを除いたコマンドの組み合わせは下記です。

|コマンド|サブコマンド|内容|
|---|---|---|
|sam init|なし|サンプル（雛形）を作成|
|sam local|generate-event|Lambdaのイベントパラメータを作成|
||invoke|Lambdaのテスト実行|
||start-api|APIのテスト用ローカルエンドポイントを準備|
||start-lambda|Lambdaのテスト用ローカルエンドポイントを準備|
|sam validate|なし|SAMのテンプレートファイルが正しいか検証|
|sam package|なし|デプロイ用にパッケージ化|
|sam deploy|なし|Lambdaにデプロイ|
|sam logs|なし|Lambdaのログを取得|

# サンプル（雛形）を作成

## 概要

サンプルアプリケーションの作成を行います。

```bash
$ sam init [OPTIONS]
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|-l, --location TEXT|gitでテンプレートとなるファイルを取得する。TEXTにはパスを記述。|
|-r, --runtime [python3.6, python2.7, python, nodejs6.10, nodejs8.10, nodejs4.3, nodejs, dotnetcore2.0, dotnetcore1.0, dotnetcore, dotnet, go1.x, go, java8, java]|Lambdaの言語を指定する。|
|-o, --output-dir PATH|出力先のフォルダパスを指定する。|
|-n, --name TEXT|プロジェクト名を指定する。この名前のフォルダが作成される。|
|--no-input|プロンプトの入力を無効にしてデフォルトのテンプレート設定を使用する。|
|--debug|デバッグログを表示する。|
|-h, --help|ヘルプを表示する。|

## 使用例

Python3.6でサンプルを作成します。

```bash
$ sam init --runtime python3.6 --name my_project_name
```

# ローカル配置

SAMのコマンドではありませんが、必要ライブラリのインストールやbuildフォルダ以下に必要なファイルを集めておきます。

```bash
$ pip install -r requirements.txt -t hello_world/build/
$ cp hello_world/*.py hello_world/build/
```

# Lambdaのイベントパラメータを作成

## 概要

Lambda関数を実行する際のペイロード（実行時の引数）のサンプルを作成できます。
後ほど紹介する「Lambdaのテスト実行」で使用します。

```bash
$ sam local generate-event [OPTIONS] COMMANDS [ARGS]...
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|-h, --help|ヘルプを表示する。|

### COMMANDSとARGS

COMMANDSとARGSは下記となります。

|COMMANDS|内容|
|---|---|
|api|API Gatewayのイベントを作成する。|
|dynamodb|DynamoDBのイベントを作成する。|
|kinesis|Kinesisのイベントを作成する。|
|s3|S3のイベントを作成する。|
|schedule|Scheduleのイベントを作成する。|
|sns|SNSのイベントを作成する。|

このうち、S3についてはバケット名なども指定します。

```bash
$ sam local generate-event s3 --bucket <bucket> --key <key>
```

## 使用例

API Gatewayのイベントを作成します。結果を外部ファイルに保存しておくと便利です。

```bash
$ sam local generate-event api > event_api.json
```

作成したペイロードの`body`部分がAPIを叩いた際のペイロードとなります。任意のデータに変更しましょう！

```json:event_api.json(抜粋)
{
    "httpMethod": "POST",
    "body": "{ \"test\": \"body\"}",
}
```

# Lambdaのテスト実行

## 概要

Lambda関数をローカルで実行することで動作確認を行えます。

Lambda関数を実行する際のペイロードは、stdinでその場で渡すことも可能ですが、予め作成しておくと便利です。

```bash
$ sam local invoke [OPTIONS] [FUNCTION_IDENTIFIER]
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|-e, --event PATH|Lambda実行時のイベントファイル(json)を指定する。未指定時は、stdinでjsonを入力する。|
|-t, --template PATH|AWS SAMのテンプレートファイルを指定する。未指定時は`template.yaml`または`template.yml`とする。|
|-n, --env-vars PATH|環境変数を記述したjsonファイルを指定する。|
|-d, --debug-port TEXT|ローカルデバッグ用のポート番号を指定する。（詳細不明。IDEでいい感じにできる？）|
|--debug-args TEXT|デバッガに渡す追加の引数を指定する。|
|-v, --docker-volume-basedir TEXT|Dockerでリモートデバッグをいい感じにする？（詳細不明）|
|--docker-network TEXT|既存のDockerネットワークに接続する。|
|-l, --log-file TEXT|ログの出力ファイルを指定する。|
|--skip-pull-image|Lambda用の最新Dockerファイルの取得をスキップする。|
|--profile TEXT|AWSの認証プロファイルを指定する。|
|--debug|デバッグログを表示する。|
|-h, --help|ヘルプを表示する。|

環境変数ファイルは、例えば下記のようになります。

```json:env.json
{
  "MyFunction1": {
    "TABLE_NAME": "localtable",
    "BUCKET_NAME": "testBucket"
  },
  "MyFunction2": {
    "TABLE_NAME": "localtable",
    "STAGE": "dev"
  },
}
```

* https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-cli.html

### FUNCTION_IDENTIFIER

実行対象となるLambda関数のIDを指定します。`template.yaml`に記載されています。

例えば、`sam init`で作成した`template.yaml`だと、下記の`HelloWorldFunction`が該当します。

```yaml:template.yaml(抜粋)
Resources:
    HelloWorldFunction:
        Type: AWS::Serverless::Function
```

## 使用例

「Lambdaのイベントパラメータを作成」で作成したAPI Gatewayのイベントパラメータを使用して、Lambdaをローカル実行します。

```bash
$ sam local invoke "HelloWorldFunction" -e event_api.json
```

うまくいかない場合は、例えば下記のようにするのも手です。

```bash
$ echo '{"test": "aaaaa" }' | sam local invoke "HelloWorldFunction"
```

# APIのテスト用ローカルエンドポイントを準備

## 概要

Lambda関数に対するローカルHTTPサーバを作成することで、API Gatewayを経由した動作確認を行うことができます。

```bash
$ sam local start-api [OPTIONS]
```

### OPTIONS

OPTIONSは下記となります。ほぼ`sam init invoke`と同じです。

|OPTIONS|内容|
|---|---|
|--host TEXT|ローカルホストまたはIPアドレスを指定する。（デフォルトは127.0.0.1）|
|-p, --port INTEGER|ローカルポート番号を指定する。（デフォルトは3000）|
|-s, --static-dir TEXT|CSS/JavaScript/HTMLなどの静的アセットを指定する。|
|-t, --template PATH|AWS SAMのテンプレートファイルを指定する。未指定時は`template.yaml`または`template.yml`とする。|
|-n, --env-vars PATH|環境変数を記述したjsonファイルを指定する。|
|-d, --debug-port TEXT|ローカルデバッグ用のポート番号を指定する。（詳細不明。IDEでいい感じにできる？）|
|--debug-args TEXT|デバッガに渡す追加の引数を指定する。|
|-v, --docker-volume-basedir TEXT|Dockerでリモートデバッグをいい感じにする？（詳細不明）|
|--docker-network TEXT|既存のDockerネットワークに接続する。|
|-l, --log-file TEXT|ログの出力ファイルを指定する。|
|--skip-pull-image|Lambda用の最新Dockerファイルの取得をスキップする。|
|--profile TEXT|AWSの認証プロファイルを指定する。|
|--debug|デバッグログを表示する。|
|--help|ヘルプを表示する。|

## 使用例

OPTION無しでも良いのですが、ログを外部出力させてみます。

```bash
$ sam local start-api --log-file output-api.log
2018-08-26 00:22:28 Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
2018-08-26 00:22:28 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2018-08-26 00:22:28  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
```

マウントしているアドレスにアクセスします。方法は何でも良いのですが、ここではcurlコマンドを使用します。

```bash
$ curl http://127.0.0.1:3000/hello
PS C:\Users\tdd1> curl http://127.0.0.1:3000/hello

StatusCode        : 200
StatusDescription : OK
Content           : {"message": "hello world", "location": "125.xxx.xxx.xxx"}
RawContent        : HTTP/1.0 200 OK
                    Content-Length: 57
                    Content-Type: application/json
                    Date: Sat, 25 Aug 2018 15:24:25 GMT
                    Server: Werkzeug/0.14.1 Python/3.6.3

                    {"message": "hello world", "location": "125.xxx.xxx.xxx...
Forms             : {}
Headers           : {[Content-Length, 57], [Content-Type, application/json], [Date, Sat, 25 Aug 2018 15:24:25 GMT], [Se
                    rver, Werkzeug/0.14.1 Python/3.6.3]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 57
```

終了する場合は、`CTRL+C`でOKです。

ログファイルは下記のようになっています。CloudWatch Logsに残るようなやつですね。

```text:output-api.log
START RequestId: 420dcc0d-107d-4c91-a943-bbf66854fc33 Version: $LATEST
END RequestId: 420dcc0d-107d-4c91-a943-bbf66854fc33
REPORT RequestId: 420dcc0d-107d-4c91-a943-bbf66854fc33 Duration: 783 ms Billed Duration: 800 ms Memory Size: 128 MB Max Memory Used: 22 MB
```

# Lambdaのテスト用ローカルエンドポイントを準備

## 概要

Lambda関数をテストするためのローカルエンドポイントを作成します。
`sam local start-api`と似ていますが、HTTPサーバか否かが異なります。
API Gateway経由で公開しないLambdaはこちらを使えば良いと思います。

ローカルエンドポイントの作成後は、AWS CLIまたはSDKを使用して呼び出しを行います。プログラムから呼び出せるので、自動テストなどを作れます。

```bash
$ sam local start-lambda [OPTIONS]
```

### OPTIONS

OPTIONSは下記となります。ほぼ`sam init invoke`と同じです。

|OPTIONS|内容|
|---|---|
|--host TEXT|ローカルホストまたはIPアドレスを指定する。（デフォルトは127.0.0.1）|
|-p, --port INTEGER|ローカルポート番号を指定する。（デフォルトは3000）|
|-t, --template PATH|AWS SAMのテンプレートファイルを指定する。未指定時は`template.yaml`または`template.yml`とする。|
|-n, --env-vars PATH|環境変数を記述したjsonファイルを指定する。|
|-d, --debug-port TEXT|ローカルデバッグ用のポート番号を指定する。（詳細不明。IDEでいい感じにできる？）|
|--debug-args TEXT|デバッガに渡す追加の引数を指定する。|
|-v, --docker-volume-basedir TEXT|Dockerでリモートデバッグをいい感じにする？（詳細不明）|
|--docker-network TEXT|既存のDockerネットワークに接続する。|
|-l, --log-file TEXT|ログの出力ファイルを指定する。|
|--skip-pull-image|Lambda用の最新Dockerファイルの取得をスキップする。|
|--profile TEXT|AWSの認証プロファイルを指定する。|
|--debug|デバッグログを表示する。|
|--help|ヘルプを表示する。|

## 使用例

まずはLambda関数のローカルエンドポイントを作成します。

```bash
$ sam local start-lambda --log-file output-lambda.log
```

### AWS CLIで実行

下記コマンドで実行します。ステータスコードが返ってきます。

```bash
$ aws lambda invoke --function-name "HelloWorldFunction" --endpoint-url "http://127.0.0.1:3001" --no-verify-ssl out.txt
{
    "StatusCode": 200
}
```

ログファイルはそれぞれ下記となっています。

```text:output-lambda.log
START RequestId: 915b8e43-d8cf-4cef-8a65-849a9d42e746 Version: $LATEST
END RequestId: 915b8e43-d8cf-4cef-8a65-849a9d42e746
REPORT RequestId: 915b8e43-d8cf-4cef-8a65-849a9d42e746 Duration: 745 ms Billed Duration: 800 ms Memory Size: 128 MB Max Memory Used: 22 MB
```

```text:out.txt
{"statusCode": 200, "body": "{\"message\": \"hello world\", \"location\": \"125.xxx.xxx.xxx\"}"}
```

実行時のペイロードを指定することもできます。これらはAWS SAMではなく、AWS CLIの機能になります。詳細は下記を参照してください。

* https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-userapp-walkthrough-custom-events-invoke.html

### SDKで実行

下記のPythonプログラムでLambdaを実行してみます。

```python:test_lambda_invoke.py
import boto3
from botocore import UNSIGNED
from botocore.client import Config

def main():
    lambda_client = boto3.client('lambda',
                                endpoint_url="http://127.0.0.1:3001",
                                use_ssl=False,
                                verify=False,
                                config=Config(signature_version=UNSIGNED,
                                                read_timeout=30,
                                                retries={'max_attempts': 0}))
    res = lambda_client.invoke(
                            FunctionName="HelloWorldFunction",
                            InvocationType="RequestResponse")
    print(res)

if __name__ == "__main__":
    main()
```

実行時のコマンドを結果は下記です。

```text
$ python test_lambda_invoke.py
{'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'application/json', 'content-length': '96', 'server': 'Werkzeug/0.14.1 Python/3.6.3', 'date': 'Sat, 25 Aug 2018 16:09:20 GMT'}, 'RetryAttempts': 0}, 'StatusCode': 200, 'Payload': <botocore.response.StreamingBody object at 0x03599C70>}
```

output-lambda.logは似た内容なので割愛します。

# SAMのテンプレートファイルが正しいか検証

## 概要

AWS SAMのテンプレートファイルが正しく作成できているか検証します。ただし、**内容の検証ではなくYAMLファイルとしてのフォーマットを検証**するだけです。

```bash
$ sam validate [OPTIONS]
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|-t, --template PATH|AWS SAMのテンプレートファイルを指定する。未指定時は`template.yaml`または`template.yml`とする。|
|--debug|デバッグログを表示する。|
|--help|ヘルプを表示する。|

## 使用例

### OKの場合

```bash
$ sam validate
2018-08-26 01:16:19 Found credentials in shared credentials file: ~/.aws/credentials
2018-08-26 01:16:19 Starting new HTTPS connection (1): iam.amazonaws.com
D:\xxx\yyy\zzz\template.yaml is a valid SAM Template
```

### NGの場合

意図的にNGのテンプレートファイルを作成し、検証してみます。

```yaml:template_ng.yaml
#略
Resources:
    HelloWorldFunction:
        Type: AWS::Serverless::Function
        Properties #Remove ':'
            CodeUri: hello_world/build/
            Handler: app.lambda_handler
            Runtime: python3.6
#略
```

では検証してみましょう。

```bash
$ sam validate --template template_ng.yaml
(略)
yaml.scanner.ScannerError: while scanning a simple key
  in "<unicode string>", line 18, column 9:
            Properties #Remove ':'
            ^
could not find expected ':'
  in "<unicode string>", line 19, column 13:
                CodeUri: hello_world/build/
```

怒られました。

# デプロイ用にパッケージ化

## 概要

コード類をまとめてS3にアップロードし、CloudFormationでLambdaにデプロイできるSAMテンプレートファイルを生成します。

```bash
$ sam package [OPTIONS] [ARGS]...
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|--template-file|テンプレートファイルを指定する。|
|--output-template-file|作成するデプロイ用のテンプレートファイルを指定する。|
|--s3-bucket|アップロード先のS3バケット名を指定する。|
|--debug|デバッグログを表示する。|
|--help|ヘルプを表示する。|

## 使用例

予めS3バケットを作成しておきます。

```bash
$ aws s3 mb s3://mybucket
```

実行します。バケット名の指定を忘れないようにします。

```bash
$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket mybucket
```

# Lambdaにデプロイ

## 概要

S3にパッケージ化したものをLambdaにデプロイします。

```bash
$ sam deploy [OPTIONS] [ARGS]...
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|--template-file|デプロイに使用するテンプレートファイルを指定する。|
|--stack-name|CloudFormationのスタック名を指定する。|
|--capabilities|ロール作成を明示する。|
|--debug|デバッグログを表示する。|
|--help|ヘルプを表示する。|

## 使用例

実行します。stack-nameは見分けがつく名前にしておきましょう。

```bash
$ sam deploy --template-file ./packaged.yaml --stack-name mystack --capabilities CAPABILITY_IAM
```

少し時間が掛かるので待ちます。

# Lambdaのログを取得

## 概要

Lambdaのログを取得します。

```bash
$ sam logs [OPTIONS]
```

### OPTIONS

OPTIONSは下記となります。

|OPTIONS|内容|
|---|---|
|-n, --name TEXT|Lambda関数名またはLogicalIDを指定する。|
|--stack-name TEXT|CloudFormation之スタック名を指定する。|
|--filter TEXT|フィルタワードを指定する。|
|-s, --start-time TEXT|取得するログの開始時刻を指定する。(デフォルトは10分前)|
|-e, --end-time TEXT|取得するログの終了時刻を指定する。|
|-t, --tail|最新ログを取得する。(tailコマンド同様)|
|--debug|デバッグログを表示する。|
|--profile TEXT|AWSの認証プロファイルを指定する。|
|--region TEXT|リージョンを指定する。|
|-h, --help|ヘルプを表示する。|

## 使用例

```bash
$ sam logs -n HelloWorldFunction --stack-name mystack
```

他の取得方法は公式ドキュメントを見たほうがわかりやすいです。

* https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-cli.html#sam-cli-debugging-fetch-tail-filter

# 参考

* https://github.com/awslabs/aws-sam-cli
* https://githubja.com/awslabs/aws-sam-cli
* https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-cli.html
* https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-userapp-walkthrough-custom-events-invoke.html
* https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/serverless-deploy-wt.html#serverless-deploy
* https://docs.aws.amazon.com/cli/latest/index.html
* https://boto3.readthedocs.io/en/latest/reference/services/lambda.html