AWS Toolkit とsam を使ってLambdaを実行（ローカル実行、API Gateway実行）

前回は、ローカルからリモート実行の記事を書きました。
今回は、よくあるAPI Gateway + AWS Lambdaの開発をVSCodeを使ってやる方法です。

AWS SAM を使いローカルからクラウドへdeploy、クラウド側での実行、ローカルでの実行を行ってみます。
`sam build`, `sam deploy` によるエラーに遭遇します、エラーが出ると理解が深まります。

VSCode + Remote Containerでの環境構築ができず、Windows PCのローカル環境使っています。
※エラーの回避方法ないか模索中（systemctlコマンドは通るが service docker start ができない）

ローカル環境に、sam cli や python は入れたくない、複数バージョン管理が手間だったり、他に依存したりで・・・。

## 環境準備
- Windows10, 64bitとして進める

### 前提
- Docker Desktop for Windows,VSCode がインストールされていること
    - インストールしていない場合は https://docs.docker.com/docker-for-windows/install/
    - AWS公式みると、古いWindows ではdocker toolbox 使う。(https://docs.docker.com/toolbox/)
    - AWS公式で、'If you're using Windows 10 in Hyper-V mode, see' https://docs.docker.com/docker-for-windows/#file-sharing
- Windows10に入れたVSCodeに拡張機能 AWS Toolkit をインストール
    - https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/setup-toolkit.html

### Windows10に Python, SAM CLIのインストール
1. Pythonをインストール
    - Python3.8の3.8.11インストーラーが見つからない、https://www.python.org/downloads/windows/ 
    - Python3.8.10 を使うことにする。ダウンロード（https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe）
1.  AWS SAM CLI をインストール
    - ※ 他への依存性があるのと、複数バージョンは混在できないと思う。`C:\Program Files\Amazon\AWSSAMCLI`にインストールされる。
    - https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
    - Windows向け ダウンロード（https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install-windows.html）
    - AWS_SAM_CLI_64_PY3.msi がインストーラー

### インストール後の確認
- `sam --version` 
- PATH　パスが通らない時：OS再起動しないとPATHには設定されない。OS再起動しないで続行する場合は、自前でPATH設定して対応

```powershell
PS > $ENV:Path
PS > $ENV:Path+=";C:\Program Files\Amazon\AWSSAMCLI\bin"
```

- 以下にコマンドプロンプトの場合を載せておくが、VSCode内のターミナルではpowershell（PS）がデフォルトになる

```bat
cmd $ echo %path%
cmd $ set PATH=%PATH%;"C:\Program Files\Amazon\AWSSAMCLI\bin"
```

### AWS credentials
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started-set-up-credentials.html

- ここ多くの人はスキップ（既にやってる）
- `C:\Users\アカウント\.aws` ディレクトリに、credentialsファイルは既にあるのでスキップ

---

## ここから本編、Hello World のアプリケーションを作成

公式のサンプル

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html

### やっていく順序は以下のStepの順序

```powershell
#Step 1 - Download a sample application
PS> sam init

#Step 2 - Build your application
PS> cd sam-app
PS> sam build

#Step 3 - Deploy your application
PS> sam deploy --guided
```

### それぞれのStepでの例

<details><summary>sam init の実行例</summary><div>

```powershell
C:\usr\iot\local-windows-sam>sam init

        SAM CLI now collects telemetry to better understand customer needs.

        You can OPT OUT and disable telemetry collection by setting the
        environment variable SAM_CLI_TELEMETRY=0 in your shell.
        Thanks for your help!

        Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1
What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

Which runtime would you like to use?
        1 - nodejs14.x
        2 - python3.8
        3 - ruby2.7
        4 - go1.x
        5 - java11
        6 - dotnetcore3.1
        7 - nodejs12.x
        8 - nodejs10.x
        9 - python3.7
        10 - python3.6
        11 - python2.7
        12 - ruby2.5
        13 - java8.al2
        14 - java8
        15 - dotnetcore2.1
Runtime: 2

Project name [sam-app]: sam-app

Cloning from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
        1 - Hello World Example
        2 - EventBridge Hello World
        3 - EventBridge App from scratch (100+ Event Schemas)
        4 - Step Functions Sample App (Stock Trader)
        5 - Elastic File System Sample App
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: python3.8
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./sam-app/README.md
```
</div></details>


<details><summary>sam build の実行例</summary><div>

```
PS > sam build
Building codeuri: C:\usr\iot\local-windows-sam\sam-app\hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction']
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam\build
Built Template   : .aws-sam\build\template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided


```

</div></details>


<details><summary>sam deploy --guided の実行例 </summary><div>

```powershell

PS > sam deploy --guided --profile sam-cli-workshop-user-ssugimoto --region us-west-2

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Found
        Reading default arguments  :  Success

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]:
        AWS Region [us-west-2]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [Y/n]: 
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]:
        HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: 
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 

        Looking for resources needed for deployment: Found!

                Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1r2lcvc8co1oz
                A different default S3 bucket can be set in samconfig.toml

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

Uploading to sam-app/8e58a457d00a2ca022ac1dd4e0854ce9  443076 / 443076  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-app
        Region                       : us-west-2
        Confirm changeset            : True
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-1r2lcvc8co1oz
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================
Uploading to sam-app/eb49a69d21ad708d76b312beecbaba31.template  1130 / 1130  (100.00%)

Waiting for changeset to be created..

CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                        LogicalResourceId                                ResourceType                                     Replacement
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   
* Modify                                         HelloWorldFunction                               AWS::Lambda::Function                            False
* Modify                                         ServerlessRestApi                                AWS::ApiGateway::RestApi                         False
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   

Changeset created successfully. arn:aws:cloudformation:us-west-2:479448073833:changeSet/samcli-deploy1628696755/25217138-8763-4c8d-b095-d250a2205bd4


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2021-08-12 00:46:12 - Waiting for stack create/update to complete

CloudFormation events from changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                                   ResourceType                                     LogicalResourceId                                ResourceStatusReason
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   
UPDATE_IN_PROGRESS                               AWS::Lambda::Function                            HelloWorldFunction                               -
UPDATE_COMPLETE                                  AWS::Lambda::Function                            HelloWorldFunction                               -
UPDATE_COMPLETE                                  AWS::CloudFormation::Stack                       sam-app                                          -
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS              AWS::CloudFormation::Stack                       sam-app                                          -
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

CloudFormation outputs from deployed stack
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   
Key                 HelloWorldFunctionIamRole
Description         Implicit IAM Role created for Hello World function
Value               arn:aws:iam::479448073833:role/sam-app-HelloWorldFunctionRole-2549BPE01P17

Key                 HelloWorldApi
Description         API Gateway endpoint URL for Prod stage for Hello World function
Value               https://h0ju25ugt2.execute-api.us-west-2.amazonaws.com/Prod/hello/

Key                 HelloWorldFunction
Description         Hello World Lambda Function ARN
Value               arn:aws:lambda:us-west-2:479448073833:function:sam-app-HelloWorldFunction-Gks4AwHw1UQ6
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   

Successfully created/updated stack - sam-app in us-west-2



```
</div></details>

### sam コマンドでのエラー時の対応例
#### エラー① PythonのPATHが通らない、`sam build` で発生
- Pythonインストール時にPATHへの追加を設定しなかった場合に発生
- Pythonが複数バージョンインストールされていてPATHの記載順の優先で別バージョンになっている場合

```powershell
PS >cd sam-app
PS >sam build
Building codeuri: C:\usr\iot\local-windows-sam\sam-app\hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction']
Traceback (most recent call last):
  File "runpy.py", line 194, in _run_module_as_main
  File "runpy.py", line 87, in _run_code
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\__main__.py", line 12, in <module>
    cli(prog_name="sam")
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\core.py", line 782, in main
    rv = self.invoke(ctx)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\decorators.py", line 73, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\click\core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\telemetry\metric.py", line 153, in wrapped
    raise exception  # pylint: disable=raising-bad-type
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\telemetry\metric.py", line 122, in wrapped
    return_value = func(*args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\utils\version_checker.py", line 42, in wrapped
    actual_result = func(*args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\cli\main.py", line 90, in wrapper
    return func(*args, **kwargs)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\commands\build\command.py", line 210, in cli
    do_cli(
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\commands\build\command.py", line 318, in do_cli
    artifacts = builder.build()
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\build\app_builder.py", line 169, in build
    return build_strategy.build()
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\build\build_strategy.py", line 42, in build
    result.update(self._build_functions(self._build_graph))
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\build\build_strategy.py", line 53, in _build_functions
    function_build_results.update(self.build_single_function_definition(build_definition))
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\build\build_strategy.py", line 123, in build_single_function_definition
    result = self._build_function(
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\build\app_builder.py", line 541, in _build_function
    return self._build_function_in_process(
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\samcli\lib\build\app_builder.py", line 592, in _build_function_in_process
    builder.build(
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\aws_lambda_builders\builder.py", line 125, in build
    return workflow.run()
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\aws_lambda_builders\workflow.py", line 58, in wrapper
    valid_path = binary_checker.validator.validate(executable_path)
  File "C:\Program Files\Amazon\AWSSAMCLI\runtime\lib\site-packages\aws_lambda_builders\workflows\python_pip\validator.py", line 43, in validate
    p = subprocess.Popen(
  File "subprocess.py", line 858, in __init__
  File "subprocess.py", line 1311, in _execute_child
OSError: [WinError 15612] アプリケーションを開始できません。問題を解決するには、アプリケーションを再インストールしてください。
```

##### 対応、python3へのPATH設定がないのでエラーになっている

```
PS > $ENV:Path+=";C:\Users\アカウント\AppData\Local\Programs\Python\Python38"
PS > python -version
Python

```

#### エラー② `InvalidClientTokenId` がエラーに出る。`sam deploy` で発生

```
sam --deploy 
...
..

Creating the required resources...
Error: Failed to create managed resources: An error occurred (InvalidClientTokenId) when calling the CreateChangeSet operation: The security token included in the request is invalid.

```

のように、`InvalidClientTokenId` が表示されるときは、awsのクレデンシャル（credentials）の`[default]`が使われ、意図したAWS認証プロファイルが使われていない可能性がある。

##### 対応、sam コマンドの引数にプロファイルとリージョン指定を追加する

対応例）クレデンシャルファイルが以下のような場合に、AWS認証プロファイル「sam-cli-workshop-user-ssugimoto」を使いたい場合

```txt:credentials
[default]
aws_access_key_id =xxxx
aws_secret_access_key=yyyy

[sam-cli-workshop-user-ssugimoto]
aws_access_key_id=xxx2
aws_secret_access_key=yyy2
```

- 実行するコマンド

```powershell
PS > sam deploy --guided --profile sam-cli-workshop-user-ssugimoto --region us-west-2
```


#### エラー③ `PythonPipBuilder:Validation - Binary validation failed for python` PythonのPATHがない

```powershell
PS > sam build
Building codeuri: C:\usr\iot\local-windows-sam\sam-app\hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction']

Build Failed
Error: PythonPipBuilder:Validation - Binary validation failed for python, searched for python in following locations  : ['C:\\Users\\アカウント\\AppData\\Local\\Microsoft\\WindowsApps\\python.EXE'] which did not satisfy constraints for runtime: python3.8. Do you have python for 
runtime: python3.8 on your PATH?

```

##### 対応、PATH設定をする

<details><summary>一時的、暫定対処</summary><div>

```powershell
PS > $ENV:Path+=";C:\Users\アカウント\AppData\Local\Programs\Python\Python38"
PS > sam build
Building codeuri: C:\usr\iot\local-windows-sam\sam-app\hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction']
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam\build
Built Template   : .aws-sam\build\template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

```
</div></details>

<details><summary>VSCodeへの設定（推奨）</summary><div>

- 下記の設定ではVSCode内のターミナル用のため、Windows の Powershellやcmdを別で起動する場合は、.batや .ps1 で環境変数をセットすることが必要。
- PATHの行がWindows向けのPython3.8へのVSCode内のターミナルを開いた時の設定例、python.exe,python38.dllのファイルがあるディレクトリを指定する

```
 └─.vscode
    ├── settings.json
    └── launch.json
```

```json:settings.json
{
    "files.eol": "\n",
    "terminal.integrated.env.windows": {
        "PATH":"${env:USERPROFILE}\\AppData\\Local\\Programs\\Python\\Python38;${env:PATH}",
    },
    "terminal.integrated.env.osx": {
      "PATH": "${workspaceRoot}/node_modules/.bin:${env:PATH}"
    }
}

```

</div></details>


### AWSクラウド側にdeployされたものの確認

#### 関数
- ![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/3fc585f3-e86e-45e7-517b-a8a46d594b56.png)

- ![01-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/ca609bbb-1d13-16ff-4013-0daed99781d8.png)

#### cloudformation stack
- ![02-stack-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/c9a354be-48f3-20fe-9c2d-e461239379f1.png)
- ![02-stack-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/4c5c3d4f-d2a1-d5f2-1ce9-416968630b80.png)

#### APIの実行
- API Gatewayの画面でテスト実行
![03-api-gateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/5bd3f899-f5c5-c186-0a5f-4733b4224936.png)

- URLでGETリクエスト
ブラウザのアドレスに入力 https://xxxxx.execute-api.us-west-2.amazonaws.com/Prod/hello/

![03-api-gateway-res.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/54566dbe-60c6-0e62-bea0-0ec938ac64a2.png)


#### ローカル windows10での実行
- `sam local start-api` コマンドでローカルにAPIサーバ機能が起動します
- このコマンドでは、Dockerが必須です。
- APIサーバにリクエストがくると、Lambdaが動くコンテナが起動してリクエストを処理します
- リクエストのたびにLambdaコンテナが起動するため、さくさく動かすにはCPUスペックが必要なのと、コンテナ初期化が速い言語（Pyhonやnode）でないともっさり動きます
    - ![04-samlocal-start-api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/fa4a7524-cda3-214f-3f17-1641bcc6dd9a.png)
    - ![04-webでローカルリクエスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/2ec423ff-7d89-c3eb-7196-ba8783c4d6c3.png)


### 作成したスタックの削除

```
PS > aws cloudformation delete-stack --stack-name sam-app --profile sam-cli-workshop-user-ssugimoto --region us-west-2
```
- `./sam-app/samconfig.toml` にstack_name, region, prodile, s3_bucket の記載がされています