「AWS Hands-on for Beginners Serverless #2 AWS SAM を使ってテンプレートからサーバーレスな環境を構築する」をAWS CLIでやってみる

https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-Serverless-2-2022-reg-event.html?trk=aws_introduction_page

上記、「AWS Hands-on for Beginners Serverless #2 AWS SAM を使ってテンプレートからサーバーレスな環境を構築する」 をAWS CLIでやってみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/dfe74e94-da2e-4d30-1e32-8b06f6c265ee.png)
ハンズオンから引用

:::note alert
Cloud9は新規利用できなくなりました。
今まで利用したことがあるアカウントはまだ利用できるようです。
:::

# 03 Cloud9 のセットアップ + [Option] Cloud9 で簡単な Lambda 関数を作成する
## 変数
```bash:コマンド
# Cloud9環境名
CLOUD9_ENVIRONMENT_NAME="aws-hands-on-for-beginners-serverless-2" \
&& echo ${CLOUD9_ENVIRONMENT_NAME}

# Cloud9説明
CLOUD9_ENVIRONMENT_DESC="aws-hands-on-for-beginners-serverless-2" \
&& echo ${CLOUD9_ENVIRONMENT_DESC}

# インスタンスタイプ
CLOUD9_INSTANCE_TYPE="t2.micro" \
&& echo ${CLOUD9_INSTANCE_TYPE}

# プラットフォーム
CLOUD9_IMAGE_ID="resolve:ssm:/aws/service/cloud9/amis/amazonlinux-2-x86_64" \
&& echo ${CLOUD9_IMAGE_ID}

```
```bash:出力
[cloudshell-user@ip-10-132-75-96 ~]$ # Cloud9環境名
[cloudshell-user@ip-10-132-75-96 ~]$ CLOUD9_ENVIRONMENT_NAME="aws-hands-on-for-beginners-serverless-2" \
> && echo ${CLOUD9_ENVIRONMENT_NAME}
aws-hands-on-for-beginners-serverless-2
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # Cloud9説明
[cloudshell-user@ip-10-132-75-96 ~]$ CLOUD9_ENVIRONMENT_DESC="aws-hands-on-for-beginners-serverless-2" \
> && echo ${CLOUD9_ENVIRONMENT_DESC}
aws-hands-on-for-beginners-serverless-2
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # インスタンスタイプ
[cloudshell-user@ip-10-132-75-96 ~]$ CLOUD9_INSTANCE_TYPE="t2.micro" \
> && echo ${CLOUD9_INSTANCE_TYPE}
t2.micro
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # プラットフォーム
[cloudshell-user@ip-10-132-75-96 ~]$ CLOUD9_IMAGE_ID="resolve:ssm:/aws/service/cloud9/amis/amazonlinux-2-x86_64" \
> && echo ${CLOUD9_IMAGE_ID}
resolve:ssm:/aws/service/cloud9/amis/amazonlinux-2-x86_64
```
## 作成
```bash:コマンド
# Cloud9環境作成
CLOUD9_ENVIRONMENT_ID=$(
    aws cloud9 create-environment-ec2 \
        --name ${CLOUD9_ENVIRONMENT_NAME} \
        --instance-type ${CLOUD9_INSTANCE_TYPE} \
        --image-id ${CLOUD9_IMAGE_ID} \
        --connection-type CONNECT_SSM \
        --automatic-stop-time-minutes 30 \
        --query environmentId \
        --output text
) \
&& echo ${CLOUD9_ENVIRONMENT_ID}

```
```bash:出力
[cloudshell-user@ip-10-132-75-96 ~]$ # Cloud9環境作成　(サブネット指定なし)
[cloudshell-user@ip-10-132-75-96 ~]$ CLOUD9_ENVIRONMENT_ID=$(
>     aws cloud9 create-environment-ec2 \
>         --name ${CLOUD9_ENVIRONMENT_NAME} \
>         --instance-type ${CLOUD9_INSTANCE_TYPE} \
>         --image-id ${CLOUD9_IMAGE_ID} \
>         --connection-type CONNECT_SSM \
>         --automatic-stop-time-minutes 30 \
>         --query environmentId \
>         --output text
> ) \
> && echo ${CLOUD9_ENVIRONMENT_ID}
9386580986704311a816739ecbfad97d
```
# 04 SAM で Lambda 関数を作成する ①
:::note info
以降、Cloud9で実施
:::
```bash:コマンド
date_var=$(date +%Y%m%d) \
&& echo ${date_var}
S3_BUCKET_NAME="hands-on-serverless-${date_var}" \
&& echo ${S3_BUCKET_NAME}

```
```bash:出力
admin:~/environment $ date_var=$(date +%Y%m%d) \
> && echo ${date_var}
20240921
admin:~/environment $ S3_BUCKET_NAME="hands-on-serverless-${date_var}" \
> && echo ${S3_BUCKET_NAME}
hands-on-serverless-20240921
```
## S3バケット作成
```bash:コマンド
# 作成
aws s3 mb s3://${S3_BUCKET_NAME}

```
```bash:出力
admin:~/environment $ # 作成
admin:~/environment $ aws s3 mb s3://${S3_BUCKET_NAME}
make_bucket: hands-on-serverless-20240921
```
## ディレクトリ作成
```bash:コマンド
mkdir hands-on-serverless-2
cd hands-on-serverless-2
mkdir translate-function

```
```bash:出力
admin:~/environment $ mkdir hands-on-serverless-2
admin:~/environment $ cd hands-on-serverless-2
admin:~/environment/hands-on-serverless-2 $ mkdir translate-function
```
## translate-function.py作成
```bash:コマンド
cat << EOF > translate-function/translate-function.py
import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):

    logger.info(event)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello Hands on world!')
    }
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > translate-function/translate-function.py
> import json
> import logging
> 
> logger = logging.getLogger()
> logger.setLevel(logging.INFO)
> 
> def lambda_handler(event, context):
> 
>     logger.info(event)
> 
>     return {
>         'statusCode': 200,
>         'body': json.dumps('Hello Hands on world!')
>     }
> EOF
```
## template.yaml作成
:::note warn
ハンズオンな内容からpythonのバージョンを3.8に変更
:::
```bash:コマンド
cat << EOF > template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Hands-on for Beginners - Serverless 2
Resources:
  TranslateLambda:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: translate-function-2
      CodeUri: ./translate-function
      Handler: translate-function.lambda_handler
      Runtime: python3.8
      Timeout: 5
      MemorySize: 256
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > template.yaml
> AWSTemplateFormatVersion: '2010-09-09'
> Transform: AWS::Serverless-2016-10-31
> Description: AWS Hands-on for Beginners - Serverless 2
> Resources:
>   TranslateLambda:
>     Type: AWS::Serverless::Function
>     Properties:
>       FunctionName: translate-function-2
>       CodeUri: ./translate-function
>       Handler: translate-function.lambda_handler
>       Runtime: python3.8
>       Timeout: 5
>       MemorySize: 256
> EOF
```
## パッケージング
```bash:コマンド
# パッケージング
aws cloudformation package \
     --template-file template.yaml \
     --s3-bucket ${S3_BUCKET_NAME} \
     --output-template-file packaged-template.yaml

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # パッケージング
admin:~/environment/hands-on-serverless-2 $ aws cloudformation package \
>      --template-file template.yaml \
>      --s3-bucket ${S3_BUCKET_NAME} \
>      --output-template-file packaged-template.yaml
Uploading to f802e187101b61229f32ab9c1db914b5  310 / 310.0  (100.00%)
Successfully packaged artifacts and wrote output template to file packaged-template.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /home/ec2-user/environment/hands-on-serverless-2/packaged-template.yaml --stack-name <YOUR STACK NAME>
```
## デプロイ
```bash:コマンド
# スタック名
STACK_NAME="hands-on-serverless-2" \
&& echo ${STACK_NAME}

# デプロイ
aws cloudformation deploy \
     --template-file ./packaged-template.yaml \
     --stack-name ${STACK_NAME} \
     --capabilities CAPABILITY_IAM

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # スタック名
admin:~/environment/hands-on-serverless-2 $ STACK_NAME="hands-on-serverless-2" \
> && echo ${STACK_NAME}
hands-on-serverless-2
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # デプロイ
admin:~/environment/hands-on-serverless-2 $ aws cloudformation deploy \
>      --template-file ./packaged-template.yaml \
>      --stack-name ${STACK_NAME} \
>      --capabilities CAPABILITY_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - hands-on-serverless-2
```
## リソース一覧確認
```bash:コマンド
aws cloudformation describe-stack-resources \
    --stack-name ${STACK_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ aws cloudformation describe-stack-resources \
>     --stack-name ${STACK_NAME} \
>     --no-cli-pager
{
    "StackResources": [
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambda",
            "PhysicalResourceId": "translate-function-2",
            "ResourceType": "AWS::Lambda::Function",
            "Timestamp": "2024-09-21T03:27:13.956000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambdaRole",
            "PhysicalResourceId": "hands-on-serverless-2-TranslateLambdaRole-42Dlsxf0UdTm",
            "ResourceType": "AWS::IAM::Role",
            "Timestamp": "2024-09-21T03:27:03.522000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
```
## Lambda関数のテスト
### 実行
```bash:コマンド
FUNCTION_NAME="translate-function-2" \
&& echo ${FUNCTION_NAME}

# イベントの作成
EVENT=$(cat << EOF
{
  "key1": "value1",
  "key2": "value2",
  "key3": "value3"
}
EOF
) \
&& echo ${EVENT}

# JSONフォーマットの確認
echo ${EVENT} | python -m json.tool

# テスト実行
aws lambda invoke \
  --function-name ${FUNCTION_NAME} \
  --payload `echo ${EVENT} | base64 -w 0` \
  outputfile.txt

cat outputfile.txt

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ FUNCTION_NAME="translate-function-2" \
> && echo ${FUNCTION_NAME}
translate-function-2
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # イベントの作成
admin:~/environment/hands-on-serverless-2 $ EVENT=$(cat << EOF
> {
>   "key1": "value1",
>   "key2": "value2",
>   "key3": "value3"
> }
> EOF
> ) \
> && echo ${EVENT}
{ "key1": "value1", "key2": "value2", "key3": "value3" }
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # JSONフォーマットの確認
admin:~/environment/hands-on-serverless-2 $ echo ${EVENT} | python -m json.tool
{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
}
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # テスト実行
admin:~/environment/hands-on-serverless-2 $ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ cat outputfile.txt
{"statusCode": 200, "body": "\"Hello Hands on world!\""}
```
### ログの確認
```bash:コマンド
# 最新のログストリームを取得
LATEST_LOG_STREAM=$(
    aws logs describe-log-streams \
        --log-group-name /aws/lambda/${FUNCTION_NAME} \
        --order-by LastEventTime \
        --descending \
        --limit 1 \
        --query 'logStreams[0].logStreamName' \
        --output text
) \
&& echo ${LATEST_LOG_STREAM}

# ログの確認
aws logs get-log-events \
    --log-group-name /aws/lambda/${FUNCTION_NAME} \
    --log-stream-name ${LATEST_LOG_STREAM} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # 最新のログストリームを取得
admin:~/environment/hands-on-serverless-2 $ LATEST_LOG_STREAM=$(
>     aws logs describe-log-streams \
>         --log-group-name /aws/lambda/${FUNCTION_NAME} \
>         --order-by LastEventTime \
>         --descending \
>         --limit 1 \
>         --query 'logStreams[0].logStreamName' \
>         --output text
> ) \
> && echo ${LATEST_LOG_STREAM}
2024/09/21/[$LATEST]2d58743780da4bdeb6b0f54309591ba5
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # ログの確認
admin:~/environment/hands-on-serverless-2 $ aws logs get-log-events \
>     --log-group-name /aws/lambda/${FUNCTION_NAME} \
>     --log-stream-name ${LATEST_LOG_STREAM} \
>     --no-cli-pager
{
    "events": [
        {
            "timestamp": 1726889412091,
            "message": "INIT_START Runtime Version: python:3.8.v53\tRuntime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:124ac49f727c31fa5ba440a749eef8274ca2beb4a0a8f107a0d8c69632c715a7\n",
            "ingestionTime": 1726889420747
        },
        {
            "timestamp": 1726889412230,
            "message": "START RequestId: d83323b8-6503-465c-a490-5794cd7bb5ab Version: $LATEST\n",
            "ingestionTime": 1726889420747
        },
        {
            "timestamp": 1726889412231,
            "message": "[INFO]\t2024-09-21T03:30:12.231Z\td83323b8-6503-465c-a490-5794cd7bb5ab\t{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}\n",
            "ingestionTime": 1726889420747
        },
        {
            "timestamp": 1726889412232,
            "message": "END RequestId: d83323b8-6503-465c-a490-5794cd7bb5ab\n",
            "ingestionTime": 1726889420747
        },
        {
            "timestamp": 1726889412232,
            "message": "REPORT RequestId: d83323b8-6503-465c-a490-5794cd7bb5ab\tDuration: 1.97 ms\tBilled Duration: 2 ms\tMemory Size: 256 MB\tMax Memory Used: 39 MB\tInit Duration: 139.04 ms\t\n",
            "ingestionTime": 1726889420747
        }
    ],
    "nextForwardToken": "f/38510920768226143949026995082048256680616396673389297668/s",
    "nextBackwardToken": "b/38510920765081738876034177219091720404172977701046059008/s"
}
```
# 05 SAM で Lambda 関数を作成する ②
## translate-function.py修正
```bash:コマンド
cat << EOF > translate-function/translate-function.py
import json
import boto3

translate = boto3.client('translate')

def lambda_handler(event, context):

    input_text = "こんにちは"

    response = translate.translate_text(
        Text=input_text,
        SourceLanguageCode='ja',
        TargetLanguageCode='en'
    )

    output_text = response.get('TranslatedText')

    return {
        'statusCode': 200,
        'body': json.dumps({
            'output_text': output_text
        })
    }
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > translate-function/translate-function.py
> import json
> import boto3
> 
> translate = boto3.client('translate')
> 
> def lambda_handler(event, context):
> 
>     input_text = "こんにちは"
> 
>     response = translate.translate_text(
>         Text=input_text,
>         SourceLanguageCode='ja',
>         TargetLanguageCode='en'
>     )
> 
>     output_text = response.get('TranslatedText')
> 
>     return {
>         'statusCode': 200,
>         'body': json.dumps({
>             'output_text': output_text
>         })
>     }
> EOF
```
## template.yaml修正
```bash:コマンド
cat << EOF > template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Hands-on for Beginners - Serverless 2
Resources:
  TranslateLambda:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: translate-function-2
      CodeUri: ./translate-function
      Handler: translate-function.lambda_handler
      Runtime: python3.8
      Timeout: 5
      MemorySize: 256
      Policies:
        - TranslateFullAccess
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > template.yaml
> AWSTemplateFormatVersion: '2010-09-09'
> Transform: AWS::Serverless-2016-10-31
> Description: AWS Hands-on for Beginners - Serverless 2
> Resources:
>   TranslateLambda:
>     Type: AWS::Serverless::Function
>     Properties:
>       FunctionName: translate-function-2
>       CodeUri: ./translate-function
>       Handler: translate-function.lambda_handler
>       Runtime: python3.8
>       Timeout: 5
>       MemorySize: 256
>       Policies:
>         - TranslateFullAccess
> EOF
```
## パッケージング
```bash:コマンド
# パッケージング
aws cloudformation package \
     --template-file template.yaml \
     --s3-bucket ${S3_BUCKET_NAME} \
     --output-template-file packaged-template.yaml

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # パッケージング
admin:~/environment/hands-on-serverless-2 $ aws cloudformation package \
>      --template-file template.yaml \
>      --s3-bucket ${S3_BUCKET_NAME} \
>      --output-template-file packaged-template.yaml
Uploading to 44a6c97b5e9e64448acb91573299c6ba  392 / 392.0  (100.00%)
Successfully packaged artifacts and wrote output template to file packaged-template.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /home/ec2-user/environment/hands-on-serverless-2/packaged-template.yaml --stack-name <YOUR STACK NAME>
```
## デプロイ
```bash:コマンド
# デプロイ
aws cloudformation deploy \
     --template-file ./packaged-template.yaml \
     --stack-name ${STACK_NAME} \
     --capabilities CAPABILITY_IAM

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # デプロイ
admin:~/environment/hands-on-serverless-2 $ aws cloudformation deploy \
>      --template-file ./packaged-template.yaml \
>      --stack-name ${STACK_NAME} \
>      --capabilities CAPABILITY_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - hands-on-serverless-2
```
## リソース一覧確認
```bash:コマンド
aws cloudformation describe-stack-resources \
    --stack-name ${STACK_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ aws cloudformation describe-stack-resources \
>     --stack-name ${STACK_NAME} \
>     --no-cli-pager
{
    "StackResources": [
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambda",
            "PhysicalResourceId": "translate-function-2",
            "ResourceType": "AWS::Lambda::Function",
            "Timestamp": "2024-09-21T03:36:02.138000+00:00",
            "ResourceStatus": "UPDATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambdaRole",
            "PhysicalResourceId": "hands-on-serverless-2-TranslateLambdaRole-42Dlsxf0UdTm",
            "ResourceType": "AWS::IAM::Role",
            "Timestamp": "2024-09-21T03:35:53.950000+00:00",
            "ResourceStatus": "UPDATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
```
## Lambda関数確認
```bash:コマンド
aws lambda get-function \
    --function-name ${FUNCTION_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ aws lambda get-function \
>     --function-name ${FUNCTION_NAME} \
>     --no-cli-pager
{
    "Configuration": {
        "FunctionName": "translate-function-2",
        "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function-2",
        "Runtime": "python3.8",
        "Role": "arn:aws:iam::999999999999:role/hands-on-serverless-2-TranslateLambdaRole-42Dlsxf0UdTm",
        "Handler": "translate-function.lambda_handler",
        "CodeSize": 392,
        "Description": "",
        "Timeout": 5,
        "MemorySize": 256,
        "LastModified": "2024-09-21T03:35:56.000+0000",
        "CodeSha256": "WLi5VOJXE3DVBonupV/4oeRivAA8CP8Hg6VziUwFGT0=",
        "Version": "$LATEST",
        "TracingConfig": {
            "Mode": "PassThrough"
        },
        "RevisionId": "859cc5bd-fda8-48f1-8e2b-88f66b058646",
        "State": "Active",
        "LastUpdateStatus": "Successful",
        "PackageType": "Zip",
        "Architectures": [
            "x86_64"
        ],
        "EphemeralStorage": {
            "Size": 512
        },
        "SnapStart": {
            "ApplyOn": "None",
            "OptimizationStatus": "Off"
        },
        "RuntimeVersionConfig": {
            "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:124ac49f727c31fa5ba440a749eef8274ca2beb4a0a8f107a0d8c69632c715a7"
        },
        "LoggingConfig": {
            "LogFormat": "Text",
            "LogGroup": "/aws/lambda/translate-function-2"
        }
    },
    "Code": {
        "RepositoryType": "S3",
        "Location": "https://awslambda-ap-ne-1-tasks.s3.ap-northeast-1.amazonaws.com/snapshots/999999999999/translate-function-2-baa841b6-43d7-4973-9e29-98116b67031e?versionId=wI3s6o8C1fXpwBfTdBJcfkyKv_KKyMh4&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEsaDmFwLW5vcnRoZWFzdC0xIkgwRgIhAKgozOj84fBl1hId8aD2wG2GPpJauZJwA%2Bod5dccKpC9AiEA9xCI7uNIZ7%2FA9D1PyTDjQL8lyYRQSKdbWqibpcEPB2oqyAUIhP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAEGgw5MTk5ODA5MjUxMzkiDCqRZXTQbJip5qf79CqcBXui8v516NtXFzdXu9e1DGifhWlmNd7arJ85Qqe%2Bk2X6CbP09X%2B6sY99XzSxpvHXnJf6yXsdsQsDLUgPqzdqSxZpE2kYCONPYqmkFfSzev%2Bu%2FSJYfU29W0jKsVa1Lo2354MDft0JphFhEhbH4Ll2CB5jvhP178zS3YDP%2FMwhi9jP2JeFAYan2XltSHawUgPCsvF1ZZKQVJxZ7Mc3%2F2VhYD29g4UKhHo62YLLv%2FPd2NmjD1t3E8rNQxhNq94S7w33Arex9RyUTiMF1nKGMO5E%2F2ffwfNqkFZxJ9NuO3IcNPzl0ktW32kRfvtnmVn2EUkYBf7abbzbMyIXzuJW8XWhLRhlUqGslwbO0r2UvlpoQcdpzaTh%2BYZQUQIdZ8fE%2F8Y%2B6bAz7FkRxAAId3cljGMnMYCbpaPuzIUb5HAwxvbnR%2Bz8OUV0%2F3hXEBrMQPNBiHUvaoIzcJBDeqLiqlbuiVztD9OUtNnTLTXwl536NRsW1TImA9Zor0GkMsSoYC5OBoHgLqqcTwCKWFMZ0wEBV7gbBHxlHndJKHYgVtvnQzppoJj8qbZCYmbw1wyjZGDVugfOMGg%2F06C%2BBnV%2B6%2FotGHhrBoeeAk4eyP4GpeVFQYpea5LEKbnOTXlC9EJAtNZIhvfKjF5QaeTCumIBbyLI9lAxmnbk4UleC9yUjIxqdd4BmIi0KVM3bEUW0pe7WWqD4wZxlppjHVN1McS5XlCAIEbPwrfmwRtYMIpH%2B3tjnygYwvTvCd6LKk1w91PJu%2BMCe4H5DwqDJmwzvdS3dIxMa9aPwC3sn4VnxP5QDsEknyWpQme9gYlHOOKslQHJRTjcO6nHsZzNZWDLDsdN0oG2sWLBteC7ZaaDIpBC7y6nAtBR%2B4wO7vfv5N3Qek2P4VHgMMLsuLcGOrABEu9O7Xi6m8iTJUaoNdTcmWNEsd76%2Bm55zp1fszoyd52t25kKCyp6vZO5Zy%2F6MtpzUsfwqbSZKzjiaQSWgOPsaIeYX5lCNR95JB87XrkgTcOw67PkSTpTZMJsQcTNNdcee%2B4QEoMZ3ciFOb32YRI5J5lA6bKQv5W9CmnEd97N6AvCm%2FGft6EANzMm2h3LfhAAzy2xasyTpzjyPJRPMI6YbjbT5VNHjr6aXSyeVi6vw20%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240921T034042Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIA5MMZC4DJSHAE474X%2F20240921%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=4c7d3b4d90f9a47041f2eab2f2364a3c8c0bf444ad97bffd469cc433597bf83d"
    },
    "Tags": {
        "aws:cloudformation:stack-name": "hands-on-serverless-2",
        "lambda:createdBy": "SAM",
        "aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
        "aws:cloudformation:logical-id": "TranslateLambda"
    }
}
```
## Lambda関数のテスト
### 実行
```bash:コマンド
FUNCTION_NAME="translate-function-2" \
&& echo ${FUNCTION_NAME}

# イベントの作成
EVENT=$(cat << EOF
{
  "key1": "value1",
  "key2": "value2",
  "key3": "value3"
}
EOF
) \
&& echo ${EVENT}

# JSONフォーマットの確認
echo ${EVENT} | python -m json.tool

# テスト実行
aws lambda invoke \
  --function-name ${FUNCTION_NAME} \
  --payload `echo ${EVENT} | base64 -w 0` \
  outputfile.txt

cat outputfile.txt

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ FUNCTION_NAME="translate-function-2" \
> && echo ${FUNCTION_NAME}
translate-function-2
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # イベントの作成
admin:~/environment/hands-on-serverless-2 $ EVENT=$(cat << EOF
> {
>   "key1": "value1",
>   "key2": "value2",
>   "key3": "value3"
> }
> EOF
> ) \
> && echo ${EVENT}
{ "key1": "value1", "key2": "value2", "key3": "value3" }
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # JSONフォーマットの確認
admin:~/environment/hands-on-serverless-2 $ echo ${EVENT} | python -m json.tool
{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
}
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # テスト実行
admin:~/environment/hands-on-serverless-2 $ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ cat outputfile.txt
{"statusCode": 200, "body": "{\"output_text\": \"Hi\"}"}
```
### ログの確認
```bash:コマンド
# 最新のログストリームを取得
LATEST_LOG_STREAM=$(
    aws logs describe-log-streams \
        --log-group-name /aws/lambda/${FUNCTION_NAME} \
        --order-by LastEventTime \
        --descending \
        --limit 1 \
        --query 'logStreams[0].logStreamName' \
        --output text
) \
&& echo ${LATEST_LOG_STREAM}

# ログの確認
aws logs get-log-events \
    --log-group-name /aws/lambda/${FUNCTION_NAME} \
    --log-stream-name ${LATEST_LOG_STREAM} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # 最新のログストリームを取得
admin:~/environment/hands-on-serverless-2 $ LATEST_LOG_STREAM=$(
>     aws logs describe-log-streams \
>         --log-group-name /aws/lambda/${FUNCTION_NAME} \
>         --order-by LastEventTime \
>         --descending \
>         --limit 1 \
>         --query 'logStreams[0].logStreamName' \
>         --output text
> ) \
> && echo ${LATEST_LOG_STREAM}
2024/09/21/[$LATEST]fc81690f661a46189f7cef561d26b48e
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # ログの確認
admin:~/environment/hands-on-serverless-2 $ aws logs get-log-events \
>     --log-group-name /aws/lambda/${FUNCTION_NAME} \
>     --log-stream-name ${LATEST_LOG_STREAM} \
>     --no-cli-pager
{
    "events": [
        {
            "timestamp": 1726890098322,
            "message": "INIT_START Runtime Version: python:3.8.v53\tRuntime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:124ac49f727c31fa5ba440a749eef8274ca2beb4a0a8f107a0d8c69632c715a7\n",
            "ingestionTime": 1726890106652
        },
        {
            "timestamp": 1726890098738,
            "message": "START RequestId: b3b0658a-cab8-47ad-8297-17b5ec4a7e46 Version: $LATEST\n",
            "ingestionTime": 1726890106652
        },
        {
            "timestamp": 1726890098911,
            "message": "END RequestId: b3b0658a-cab8-47ad-8297-17b5ec4a7e46\n",
            "ingestionTime": 1726890106652
        },
        {
            "timestamp": 1726890098911,
            "message": "REPORT RequestId: b3b0658a-cab8-47ad-8297-17b5ec4a7e46\tDuration: 172.36 ms\tBilled Duration: 173 ms\tMemory Size: 256 MB\tMax Memory Used: 70 MB\tInit Duration: 415.36 ms\t\n",
            "ingestionTime": 1726890106652
        }
    ],
    "nextForwardToken": "f/38510936081679556130836764117862224016226608771726835715/s",
    "nextBackwardToken": "b/38510936068544417208902227087497685953636723844704370688/s"
}
```
# 06 SAM で API Gateway のリソースを作成し、Lambda 関数と連携させる
## translate-function.py修正
```bash:コマンド
cat << EOF > translate-function/translate-function.py
import json
import boto3

translate = boto3.client(service_name='translate')

def lambda_handler(event, context):

    input_text = event['queryStringParameters']['input_text']

    response = translate.translate_text(
        Text=input_text,
        SourceLanguageCode="ja",
        TargetLanguageCode="en"
    )

    output_text = response.get('TranslatedText')

    return {
        'statusCode': 200,
        'body': json.dumps({
            'output_text': output_text
        }),
        'isBase64Encoded': False,
        'headers': {}
    }
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > translate-function/translate-function.py
> import json
> import boto3
> 
> translate = boto3.client(service_name='translate')
> 
> def lambda_handler(event, context):
> 
>     input_text = event['queryStringParameters']['input_text']
> 
>     response = translate.translate_text(
>         Text=input_text,
>         SourceLanguageCode="ja",
>         TargetLanguageCode="en"
>     )
> 
>     output_text = response.get('TranslatedText')
> 
>     return {
>         'statusCode': 200,
>         'body': json.dumps({
>             'output_text': output_text
>         }),
>         'isBase64Encoded': False,
>         'headers': {}
>     }
> EOF
```
## template.yaml修正
```bash:コマンド
cat << EOF > template.yaml
Transform: AWS::Serverless-2016-10-31
Description: AWS Hands-on for Beginners - Serverless 2
Resources:
  TranslateLambda:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: translate-function-2
      CodeUri: ./translate-function
      Handler: translate-function.lambda_handler
      Runtime: python3.8
      Timeout: 5
      MemorySize: 256
      Policies:
        - TranslateFullAccess
      Events:
        GetApi:
          Type: Api
          Properties:
            Path: /translate
            Method: get
            RestApiId: !Ref TranslateAPI
  TranslateAPI:
    Type: AWS::Serverless::Api
    Properties:
      Name: translate-api-2
      StageName: dev
      EndpointConfiguration: REGIONAL
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > template.yaml
> Transform: AWS::Serverless-2016-10-31
> Description: AWS Hands-on for Beginners - Serverless 2
> Resources:
>   TranslateLambda:
>     Type: AWS::Serverless::Function
>     Properties:
>       FunctionName: translate-function-2
>       CodeUri: ./translate-function
>       Handler: translate-function.lambda_handler
>       Runtime: python3.8
>       Timeout: 5
>       MemorySize: 256
>       Policies:
>         - TranslateFullAccess
>       Events:
>         GetApi:
>           Type: Api
>           Properties:
>             Path: /translate
>             Method: get
>             RestApiId: !Ref TranslateAPI
>   TranslateAPI:
>     Type: AWS::Serverless::Api
>     Properties:
>       Name: translate-api-2
>       StageName: dev
>       EndpointConfiguration: REGIONAL
> EOF
```
## パッケージング
```bash:コマンド
# パッケージング
aws cloudformation package \
     --template-file template.yaml \
     --s3-bucket ${S3_BUCKET_NAME} \
     --output-template-file packaged-template.yaml

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # パッケージング
admin:~/environment/hands-on-serverless-2 $ aws cloudformation package \
>      --template-file template.yaml \
>      --s3-bucket ${S3_BUCKET_NAME} \
>      --output-template-file packaged-template.yaml
Uploading to 5e32642d09e1b88190f79969845b9d2b  434 / 434.0  (100.00%)
Successfully packaged artifacts and wrote output template to file packaged-template.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /home/ec2-user/environment/hands-on-serverless-2/packaged-template.yaml --stack-name <YOUR STACK NAME>
```
## デプロイ
```bash:コマンド
# デプロイ
aws cloudformation deploy \
     --template-file ./packaged-template.yaml \
     --stack-name ${STACK_NAME} \
     --capabilities CAPABILITY_IAM

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # デプロイ
admin:~/environment/hands-on-serverless-2 $ aws cloudformation deploy \
>      --template-file ./packaged-template.yaml \
>      --stack-name ${STACK_NAME} \
>      --capabilities CAPABILITY_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - hands-on-serverless-2
```
## リソース一覧確認
```bash:コマンド
aws cloudformation describe-stack-resources \
    --stack-name ${STACK_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ aws cloudformation describe-stack-resources \
>     --stack-name ${STACK_NAME} \
>     --no-cli-pager
{
    "StackResources": [
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateAPI",
            "PhysicalResourceId": "cmk4jfofx5",
            "ResourceType": "AWS::ApiGateway::RestApi",
            "Timestamp": "2024-09-21T03:47:14.753000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateAPIDeployment6ac74e42c4",
            "PhysicalResourceId": "4v4vpc",
            "ResourceType": "AWS::ApiGateway::Deployment",
            "Timestamp": "2024-09-21T03:47:17.179000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateAPIdevStage",
            "PhysicalResourceId": "dev",
            "ResourceType": "AWS::ApiGateway::Stage",
            "Timestamp": "2024-09-21T03:47:20.768000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambda",
            "PhysicalResourceId": "translate-function-2",
            "ResourceType": "AWS::Lambda::Function",
            "Timestamp": "2024-09-21T03:47:16.241000+00:00",
            "ResourceStatus": "UPDATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambdaGetApiPermissiondev",
            "PhysicalResourceId": "hands-on-serverless-2-TranslateLambdaGetApiPermissiondev-lsmB541cYjAI",
            "ResourceType": "AWS::Lambda::Permission",
            "Timestamp": "2024-09-21T03:47:16.630000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambdaRole",
            "PhysicalResourceId": "hands-on-serverless-2-TranslateLambdaRole-42Dlsxf0UdTm",
            "ResourceType": "AWS::IAM::Role",
            "Timestamp": "2024-09-21T03:35:53.950000+00:00",
            "ResourceStatus": "UPDATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
```
## アクセス確認
```bash:コマンド
# API GatewayのREST API ID
LOGICAL_RESOURCE_ID="TranslateAPI" \
&& echo ${LOGICAL_RESOURCE_ID}

RESTAPI_ID=$(
    aws cloudformation describe-stack-resources \
        --stack-name ${STACK_NAME} \
        --logical-resource-id ${LOGICAL_RESOURCE_ID} \
        --query "StackResources[*].PhysicalResourceId" \
        --output text
) \
&& echo ${RESTAPI_ID}

# リージョン
REGION="ap-northeast-1" \
&& echo ${REGION}

# ステージ
STAGE="dev" \
&& echo ${STAGE}

# API PATH指定
API_PATH="translate" \
&& echo ${API_PATH}

# URLエンコード
encoded_query=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("こんにちは"))') \
&& echo ${encoded_query}

# アクセス確認
curl https://${RESTAPI_ID}.execute-api.${REGION}.amazonaws.com/${STAGE}/${API_PATH}?input_text=${encoded_query}

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # API GatewayのREST API ID
admin:~/environment/hands-on-serverless-2 $ LOGICAL_RESOURCE_ID="TranslateAPI" \
> && echo ${LOGICAL_RESOURCE_ID}
TranslateAPI
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ RESTAPI_ID=$(
>     aws cloudformation describe-stack-resources \
>         --stack-name ${STACK_NAME} \
>         --logical-resource-id ${LOGICAL_RESOURCE_ID} \
>         --query "StackResources[*].PhysicalResourceId" \
>         --output text
> ) \
> && echo ${RESTAPI_ID}
cmk4jfofx5
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # リージョン
admin:~/environment/hands-on-serverless-2 $ REGION="ap-northeast-1" \
> && echo ${REGION}
ap-northeast-1
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # ステージ
admin:~/environment/hands-on-serverless-2 $ STAGE="dev" \
> && echo ${STAGE}
dev
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # API PATH指定
admin:~/environment/hands-on-serverless-2 $ API_PATH="translate" \
> && echo ${API_PATH}
translate
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # URLエンコード
admin:~/environment/hands-on-serverless-2 $ encoded_query=$(python3 -c 'import urllib.parse; print(urllib.parse.quote(" こんにちは"))') \
> && echo ${encoded_query}
%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # アクセス確認
admin:~/environment/hands-on-serverless-2 $ curl https://${RESTAPI_ID}.execute-api.${REGION}.amazonaws.com/${STAGE}/${API_PATH}?input_text=${encoded_query}
{"output_text": "Hi"}
```
# 07 SAM で DynamoDB TBL を作成し、Lambda 関数を連携させる
## translate-function.py修正
```bash:コマンド
cat << EOF > translate-function/translate-function.py
import json
import boto3
import datetime

translate = boto3.client(service_name='translate')

dynamodb_translate_history_tbl = boto3.resource('dynamodb').Table('translate-history-2')

def lambda_handler(event, context):

    input_text = event['queryStringParameters']['input_text']

    response = translate.translate_text(
        Text=input_text,
        SourceLanguageCode="ja",
        TargetLanguageCode="en"
    )

    output_text = response.get('TranslatedText')

    dynamodb_translate_history_tbl.put_item(
      Item = {
        "timestamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
        "input": input_text,
        "output": output_text
      }
    )

    return {
        'statusCode': 200,
        'body': json.dumps({
            'output_text': output_text
        }),
        'isBase64Encoded': False,
        'headers': {}
    }
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > translate-function/translate-function.py
> import json
> import boto3
> import datetime
> 
> translate = boto3.client(service_name='translate')
> 
> dynamodb_translate_history_tbl = boto3.resource('dynamodb').Table('translate-history-2')
> 
> def lambda_handler(event, context):
> 
>     input_text = event['queryStringParameters']['input_text']
> 
>     response = translate.translate_text(
>         Text=input_text,
>         SourceLanguageCode="ja",
>         TargetLanguageCode="en"
>     )
> 
>     output_text = response.get('TranslatedText')
> 
>     dynamodb_translate_history_tbl.put_item(
>       Item = {
>         "timestamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
>         "input": input_text,
>         "output": output_text
>       }
>     )
> 
>     return {
>         'statusCode': 200,
>         'body': json.dumps({
>             'output_text': output_text
>         }),
>         'isBase64Encoded': False,
>         'headers': {}
>     }
> EOF
```
## template.yaml修正
```bash:コマンド
cat << EOF > template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Hands-on for Beginners - Serverless 2
Resources:
  TranslateLambda:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: translate-function-2
      CodeUri: ./translate-function
      Handler: translate-function.lambda_handler
      Runtime: python3.8
      Timeout: 5
      MemorySize: 256
      Policies:
        - TranslateFullAccess
        - AmazonDynamoDBFullAccess
      Events:
        GetApi:
          Type: Api
          Properties:
            Path: /translate
            Method: get
            RestApiId: !Ref TranslateAPI
  TranslateAPI:
    Type: AWS::Serverless::Api
    Properties:
      Name: translate-api-2
      StageName: dev
      EndpointConfiguration: REGIONAL
  TranslateDynamoDbTbl:
    Type: AWS::Serverless::SimpleTable
    Properties:
      TableName: translate-history-2
      PrimaryKey:
        Name: timestamp
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
EOF

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cat << EOF > template.yaml
> AWSTemplateFormatVersion: '2010-09-09'
> Transform: AWS::Serverless-2016-10-31
> Description: AWS Hands-on for Beginners - Serverless 2
> Resources:
>   TranslateLambda:
>     Type: AWS::Serverless::Function
>     Properties:
>       FunctionName: translate-function-2
>       CodeUri: ./translate-function
>       Handler: translate-function.lambda_handler
>       Runtime: python3.8
>       Timeout: 5
>       MemorySize: 256
>       Policies:
>         - TranslateFullAccess
>         - AmazonDynamoDBFullAccess
>       Events:
>         GetApi:
>           Type: Api
>           Properties:
>             Path: /translate
>             Method: get
>             RestApiId: !Ref TranslateAPI
>   TranslateAPI:
>     Type: AWS::Serverless::Api
>     Properties:
>       Name: translate-api-2
>       StageName: dev
>       EndpointConfiguration: REGIONAL
>   TranslateDynamoDbTbl:
>     Type: AWS::Serverless::SimpleTable
>     Properties:
>       TableName: translate-history-2
>       PrimaryKey:
>         Name: timestamp
>         Type: String
>       ProvisionedThroughput:
>         ReadCapacityUnits: 1
>         WriteCapacityUnits: 1
> EOF
```
## パッケージング
```bash:コマンド
# パッケージング
aws cloudformation package \
     --template-file template.yaml \
     --s3-bucket ${S3_BUCKET_NAME} \
     --output-template-file packaged-template.yaml

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # パッケージング
admin:~/environment/hands-on-serverless-2 $ aws cloudformation package \
>      --template-file template.yaml \
>      --s3-bucket ${S3_BUCKET_NAME} \
>      --output-template-file packaged-template.yaml
Uploading to 390d10c0b490f110117d9155f60cf0b0  552 / 552.0  (100.00%)
Successfully packaged artifacts and wrote output template to file packaged-template.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /home/ec2-user/environment/hands-on-serverless-2/packaged-template.yaml --stack-name <YOUR STACK NAME>
```
## デプロイ
```bash:コマンド
# デプロイ
aws cloudformation deploy \
     --template-file ./packaged-template.yaml \
     --stack-name ${STACK_NAME} \
     --capabilities CAPABILITY_IAM

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # デプロイ
admin:~/environment/hands-on-serverless-2 $ aws cloudformation deploy \
>      --template-file ./packaged-template.yaml \
>      --stack-name ${STACK_NAME} \
>      --capabilities CAPABILITY_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - hands-on-serverless-2
```
## リソース一覧確認
```bash:コマンド
aws cloudformation describe-stack-resources \
    --stack-name ${STACK_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ aws cloudformation describe-stack-resources \
>     --stack-name ${STACK_NAME} \
>     --no-cli-pager
{
    "StackResources": [
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateAPI",
            "PhysicalResourceId": "cmk4jfofx5",
            "ResourceType": "AWS::ApiGateway::RestApi",
            "Timestamp": "2024-09-21T03:47:14.753000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateAPIDeployment6ac74e42c4",
            "PhysicalResourceId": "4v4vpc",
            "ResourceType": "AWS::ApiGateway::Deployment",
            "Timestamp": "2024-09-21T03:47:17.179000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateAPIdevStage",
            "PhysicalResourceId": "dev",
            "ResourceType": "AWS::ApiGateway::Stage",
            "Timestamp": "2024-09-21T03:47:20.768000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateDynamoDbTbl",
            "PhysicalResourceId": "translate-history-2",
            "ResourceType": "AWS::DynamoDB::Table",
            "Timestamp": "2024-09-21T03:53:32.031000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambda",
            "PhysicalResourceId": "translate-function-2",
            "ResourceType": "AWS::Lambda::Function",
            "Timestamp": "2024-09-21T03:53:44.516000+00:00",
            "ResourceStatus": "UPDATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambdaGetApiPermissiondev",
            "PhysicalResourceId": "hands-on-serverless-2-TranslateLambdaGetApiPermissiondev-lsmB541cYjAI",
            "ResourceType": "AWS::Lambda::Permission",
            "Timestamp": "2024-09-21T03:47:16.630000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "hands-on-serverless-2",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/hands-on-serverless-2/4fe2cc10-77c9-11ef-b579-06c2182e3129",
            "LogicalResourceId": "TranslateLambdaRole",
            "PhysicalResourceId": "hands-on-serverless-2-TranslateLambdaRole-42Dlsxf0UdTm",
            "ResourceType": "AWS::IAM::Role",
            "Timestamp": "2024-09-21T03:53:36.371000+00:00",
            "ResourceStatus": "UPDATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
```
## アクセス確認
```bash:コマンド
# API GatewayのREST API ID
LOGICAL_RESOURCE_ID="TranslateAPI" \
&& echo ${LOGICAL_RESOURCE_ID}

RESTAPI_ID=$(
    aws cloudformation describe-stack-resources \
        --stack-name ${STACK_NAME} \
        --logical-resource-id ${LOGICAL_RESOURCE_ID} \
        --query "StackResources[*].PhysicalResourceId" \
        --output text
) \
&& echo ${RESTAPI_ID}

# リージョン
REGION="ap-northeast-1" \
&& echo ${REGION}

# ステージ
STAGE="dev" \
&& echo ${STAGE}

# API PATH指定
API_PATH="translate" \
&& echo ${API_PATH}

# URLエンコード
encoded_query=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("ハンズオン完走しました!"))') \
&& echo ${encoded_query}

# アクセス確認
curl https://${RESTAPI_ID}.execute-api.${REGION}.amazonaws.com/${STAGE}/${API_PATH}?input_text=${encoded_query}

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ # API GatewayのREST API ID
admin:~/environment/hands-on-serverless-2 $ LOGICAL_RESOURCE_ID="TranslateAPI" \
> && echo ${LOGICAL_RESOURCE_ID}
TranslateAPI
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ RESTAPI_ID=$(
>     aws cloudformation describe-stack-resources \
>         --stack-name ${STACK_NAME} \
>         --logical-resource-id ${LOGICAL_RESOURCE_ID} \
>         --query "StackResources[*].PhysicalResourceId" \
>         --output text
> ) \
> && echo ${RESTAPI_ID}
cmk4jfofx5
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # リージョン
admin:~/environment/hands-on-serverless-2 $ REGION="ap-northeast-1" \
> && echo ${REGION}
ap-northeast-1
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # ステージ
admin:~/environment/hands-on-serverless-2 $ STAGE="dev" \
> && echo ${STAGE}
dev
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # API PATH指定
admin:~/environment/hands-on-serverless-2 $ API_PATH="translate" \
> && echo ${API_PATH}
translate
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # URLエンコード
admin:~/environment/hands-on-serverless-2 $ encoded_query=$(python3 -c 'import urllib.parse; print(urllib.parse.quote(" ハンズオン完走しました!"))') \
> && echo ${encoded_query}
%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3%E5%AE%8C%E8%B5%B0%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F%21
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ # アクセス確認
admin:~/environment/hands-on-serverless-2 $ curl https://${RESTAPI_ID}.execute-api.${REGION}.amazonaws.com/${STAGE}/${API_PATH}?input_text=${encoded_query}
{"output_text": "I finished the hands-on race!"}
```
## DynamoDB項目確認
```bash:コマンド
TABLE_NAME="translate-history-2" \
&& echo ${TABLE_NAME}

aws dynamodb scan \
    --table-name ${TABLE_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ TABLE_NAME="translate-history-2" \
> && echo ${TABLE_NAME}
translate-history-2
admin:~/environment/hands-on-serverless-2 $ 
admin:~/environment/hands-on-serverless-2 $ aws dynamodb scan \
>     --table-name ${TABLE_NAME} \
>     --no-cli-pager
{
    "Items": [
        {
            "output": {
                "S": "I finished the hands-on race!"
            },
            "input": {
                "S": "ハンズオン完走しました!"
            },
            "timestamp": {
                "S": "20240921035541"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}
```
# 08 [Option] SAM CLI を使ってみる ①
## SAM CLIインストール
:::note warn
SAM CLIはCloud9上にインストール済みだったため省略
:::
## バージョン確認
```bash:コマンド
sam --version

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ sam --version
SAM CLI, version 1.112.0
```
## 初期化
```bash:コマンド
sam init

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ 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


You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Data processing
        3 - Hello World Example with Powertools for AWS Lambda
        4 - Multi-step workflow
        5 - Scheduled task
        6 - Standalone function
        7 - Serverless API
        8 - Infrastructure event management
        9 - Lambda Response Streaming
        10 - Serverless Connector Hello World Example
        11 - Multi-step workflow with Connectors
        12 - GraphQLApi Hello World Example
        13 - Full Stack
        14 - Lambda EFS example
        15 - Hello World Example With Powertools for AWS Lambda
        16 - DynamoDB Example
        17 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: 

Which runtime would you like to use?
        1 - aot.dotnet7 (provided.al2)
        2 - dotnet8
        3 - dotnet6
        4 - go1.x
        5 - go (provided.al2)
        6 - go (provided.al2023)
        7 - graalvm.java11 (provided.al2)
        8 - graalvm.java17 (provided.al2)
        9 - java21
        10 - java17
        11 - java11
        12 - java8.al2
        13 - nodejs20.x
        14 - nodejs18.x
        15 - nodejs16.x
        16 - python3.9
        17 - python3.8
        18 - python3.12
        19 - python3.11
        20 - python3.10
        21 - ruby3.2
        22 - rust (provided.al2)
        23 - rust (provided.al2023)
Runtime: 17

What package type would you like to use?
        1 - Zip
        2 - Image
Package type: 1

Based on your selections, the only dependency manager available is pip.
We will proceed copying the template using pip.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: 

Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]: 

Would you like to set Structured Logging in JSON format on your Lambda functions?  [y/N]: 

Project name [sam-app]: 
                                                                                                                        
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)                               

    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: python3.8
    Architectures: x86_64
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .
    Configuration file: sam-app/samconfig.toml
    
    Next steps can be found in the README file at sam-app/README.md
        

Commands you can use next
=========================
[*] Create pipeline: cd sam-app && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-app && sam validate
[*] Test Function in the Cloud: cd sam-app && sam sync --stack-name {stack-name} --watch


SAM CLI update available (1.124.0); (1.112.0 installed)
To download: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
```
## SAMテンプレートの事前検証
```bash:コマンド
cd sam-app
sam validate

```
```bash:出力
admin:~/environment/hands-on-serverless-2 $ cd sam-app
admin:~/environment/hands-on-serverless-2/sam-app $ sam validate
/home/ec2-user/environment/hands-on-serverless-2/sam-app/template.yaml is a valid SAM Template
```
## ビルド
```bash:コマンド
sam build

```
```bash:出力
admin:~/environment/hands-on-serverless-2/sam-app $ sam build
Starting Build use cache                                                                                                
Manifest file is changed (new hash: 3298f13049d19cffaa37ca931dd4d421) or dependency folder                              
(.aws-sam/deps/32f89f1c-9246-4a7e-b63e-d52456c0382f) is missing for (HelloWorldFunction), downloading dependencies and  
copying/building source                                                                                                 
Building codeuri: /home/ec2-user/environment/hands-on-serverless-2/sam-app/hello_world runtime: python3.8 metadata: {}  
architecture: x86_64 functions: HelloWorldFunction                                                                      
 Running PythonPipBuilder:CleanUp                                                                                       
 Running PythonPipBuilder:ResolveDependencies                                                                           
 Running PythonPipBuilder:CopySource                                                                                    
 Running PythonPipBuilder:CopySource                                                                                    

Build Succeeded

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

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
```
# 09 [Option] SAM CLI を使ってみる ②
## AWS環境へデプロイ
### AWS環境へデプロイ
```bash:コマンド
sam deploy --guided

```
```bash:出力
admin:~/environment/hands-on-serverless-2/sam-app $ sam deploy --guided

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 [ap-northeast-1]: 
        #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]: 
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: 
        HelloWorldFunction has no authentication. 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:
        Creating the required resources...
        Successfully created!

        Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-ktfpyr4rinwv
        A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False
                                                                                                                        
        Parameter "stack_name=sam-app" in [default.deploy.parameters] is defined as a global parameter                  
[default.global.parameters].                                                                                            
        This parameter will be only saved under [default.global.parameters] in                                          
/home/ec2-user/environment/hands-on-serverless-2/sam-app/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/45916096b7662dd4d586fe710b60ddc6  570354 / 570354  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-app
        Region                       : ap-northeast-1
        Confirm changeset            : True
        Disable rollback             : False
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-ktfpyr4rinwv
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================

        Uploading to sam-app/d3a404bc8c7a981f5668a316182fb5ca.template  1199 / 1199  (100.00%)


Waiting for changeset to be created..

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------
Operation                     LogicalResourceId             ResourceType                  Replacement                 
---------------------------------------------------------------------------------------------------------------------
+ Add                         HelloWorldFunctionHelloWorl   AWS::Lambda::Permission       N/A                         
                              dPermissionProd                                                                         
+ Add                         HelloWorldFunctionRole        AWS::IAM::Role                N/A                         
+ Add                         HelloWorldFunction            AWS::Lambda::Function         N/A                         
+ Add                         ServerlessRestApiDeployment   AWS::ApiGateway::Deployment   N/A                         
                              47fc2d5f9d                                                                              
+ Add                         ServerlessRestApiProdStage    AWS::ApiGateway::Stage        N/A                         
+ Add                         ServerlessRestApi             AWS::ApiGateway::RestApi      N/A                         
---------------------------------------------------------------------------------------------------------------------


Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:999999999999:changeSet/samcli-deploy1726892456/69844e11-1503-4ab7-a24d-32025917bb53


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

2024-09-21 04:21:51 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
---------------------------------------------------------------------------------------------------------------------
ResourceStatus                ResourceType                  LogicalResourceId             ResourceStatusReason        
---------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS            AWS::CloudFormation::Stack    sam-app                       User Initiated              
CREATE_IN_PROGRESS            AWS::IAM::Role                HelloWorldFunctionRole        -                           
CREATE_IN_PROGRESS            AWS::IAM::Role                HelloWorldFunctionRole        Resource creation Initiated 
CREATE_COMPLETE               AWS::IAM::Role                HelloWorldFunctionRole        -                           
CREATE_IN_PROGRESS            AWS::Lambda::Function         HelloWorldFunction            -                           
CREATE_IN_PROGRESS            AWS::Lambda::Function         HelloWorldFunction            Resource creation Initiated 
CREATE_IN_PROGRESS            AWS::Lambda::Function         HelloWorldFunction            Eventual consistency check  
                                                                                          initiated                   
CREATE_IN_PROGRESS            AWS::ApiGateway::RestApi      ServerlessRestApi             -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::RestApi      ServerlessRestApi             Resource creation Initiated 
CREATE_COMPLETE               AWS::ApiGateway::RestApi      ServerlessRestApi             -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   -                           
                                                            47fc2d5f9d                                                
CREATE_IN_PROGRESS            AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   -                           
                                                            dPermissionProd                                           
CREATE_COMPLETE               AWS::Lambda::Function         HelloWorldFunction            -                           
CREATE_IN_PROGRESS            AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   Resource creation Initiated 
                                                            dPermissionProd                                           
CREATE_IN_PROGRESS            AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   Resource creation Initiated 
                                                            47fc2d5f9d                                                
CREATE_COMPLETE               AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   -                           
                                                            dPermissionProd                                           
CREATE_COMPLETE               AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   -                           
                                                            47fc2d5f9d                                                
CREATE_IN_PROGRESS            AWS::ApiGateway::Stage        ServerlessRestApiProdStage    -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::Stage        ServerlessRestApiProdStage    Resource creation Initiated 
CREATE_COMPLETE               AWS::ApiGateway::Stage        ServerlessRestApiProdStage    -                           
CREATE_COMPLETE               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::999999999999:role/sam-app-HelloWorldFunctionRole-xOpzgzYhRboR                      

Key                 HelloWorldApi                                                                                   
Description         API Gateway endpoint URL for Prod stage for Hello World function                                
Value               https://utldn1vmd5.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/                         

Key                 HelloWorldFunction                                                                              
Description         Hello World Lambda Function ARN                                                                 
Value               arn:aws:lambda:ap-northeast-1:999999999999:function:sam-app-HelloWorldFunction-QPWF3lu5WH5D     
---------------------------------------------------------------------------------------------------------------------


Successfully created/updated stack - sam-app in ap-northeast-1
```
### リソース一覧確認
```bash:コマンド
# 変数
SAM_STACK_NAME="sam-app" \
&& echo ${SAM_STACK_NAME}

aws cloudformation describe-stack-resources \
    --stack-name ${SAM_STACK_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2/sam-app $ # 変数
admin:~/environment/hands-on-serverless-2/sam-app $ SAM_STACK_NAME="sam-app" \
> && echo ${SAM_STACK_NAME}
sam-app
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ aws cloudformation describe-stack-resources \
>     --stack-name ${SAM_STACK_NAME} \
>     --no-cli-pager
{
    "StackResources": [
        {
            "StackName": "sam-app",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
            "LogicalResourceId": "HelloWorldFunction",
            "PhysicalResourceId": "sam-app-HelloWorldFunction-QPWF3lu5WH5D",
            "ResourceType": "AWS::Lambda::Function",
            "Timestamp": "2024-09-21T04:22:20.846000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "sam-app",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
            "LogicalResourceId": "HelloWorldFunctionHelloWorldPermissionProd",
            "PhysicalResourceId": "sam-app-HelloWorldFunctionHelloWorldPermissionProd-27VjLwUTX2y5",
            "ResourceType": "AWS::Lambda::Permission",
            "Timestamp": "2024-09-21T04:22:21.255000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "sam-app",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
            "LogicalResourceId": "HelloWorldFunctionRole",
            "PhysicalResourceId": "sam-app-HelloWorldFunctionRole-xOpzgzYhRboR",
            "ResourceType": "AWS::IAM::Role",
            "Timestamp": "2024-09-21T04:22:11.921000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "sam-app",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
            "LogicalResourceId": "ServerlessRestApi",
            "PhysicalResourceId": "utldn1vmd5",
            "ResourceType": "AWS::ApiGateway::RestApi",
            "Timestamp": "2024-09-21T04:22:19.351000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "sam-app",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
            "LogicalResourceId": "ServerlessRestApiDeployment47fc2d5f9d",
            "PhysicalResourceId": "ivdeh7",
            "ResourceType": "AWS::ApiGateway::Deployment",
            "Timestamp": "2024-09-21T04:22:21.546000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackName": "sam-app",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
            "LogicalResourceId": "ServerlessRestApiProdStage",
            "PhysicalResourceId": "Prod",
            "ResourceType": "AWS::ApiGateway::Stage",
            "Timestamp": "2024-09-21T04:22:24.918000+00:00",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
```
### Lambda関数確認
```bash:コマンド
LOGICAL_RESOURCE_ID="HelloWorldFunction" \
&& echo ${LOGICAL_RESOURCE_ID}

FUNCTION_NAME=$(
    aws cloudformation describe-stack-resources \
        --stack-name ${SAM_STACK_NAME} \
        --logical-resource-id ${LOGICAL_RESOURCE_ID} \
        --query "StackResources[*].PhysicalResourceId" \
        --output text
) \
&& echo ${FUNCTION_NAME}

aws lambda get-function \
    --function-name ${FUNCTION_NAME} \
    --no-cli-pager

```
```bash:出力
admin:~/environment/hands-on-serverless-2/sam-app $ LOGICAL_RESOURCE_ID="HelloWorldFunction" \
> && echo ${LOGICAL_RESOURCE_ID}
HelloWorldFunction
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ FUNCTION_NAME=$(
>     aws cloudformation describe-stack-resources \
>         --stack-name ${SAM_STACK_NAME} \
>         --logical-resource-id ${LOGICAL_RESOURCE_ID} \
>         --query "StackResources[*].PhysicalResourceId" \
>         --output text
> ) \
> && echo ${FUNCTION_NAME}
sam-app-HelloWorldFunction-QPWF3lu5WH5D
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ aws lambda get-function \
>     --function-name ${FUNCTION_NAME} \
>     --no-cli-pager
{
    "Configuration": {
        "FunctionName": "sam-app-HelloWorldFunction-QPWF3lu5WH5D",
        "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:sam-app-HelloWorldFunction-QPWF3lu5WH5D",
        "Runtime": "python3.8",
        "Role": "arn:aws:iam::999999999999:role/sam-app-HelloWorldFunctionRole-xOpzgzYhRboR",
        "Handler": "app.lambda_handler",
        "CodeSize": 570354,
        "Description": "",
        "Timeout": 3,
        "MemorySize": 128,
        "LastModified": "2024-09-21T04:22:14.441+0000",
        "CodeSha256": "pNM7IcLQTYIufw6aqcdNld2OlKqp3nHICGnvwFabSWs=",
        "Version": "$LATEST",
        "TracingConfig": {
            "Mode": "PassThrough"
        },
        "RevisionId": "ecde4344-6162-45a3-bde2-35fed2663246",
        "State": "Active",
        "LastUpdateStatus": "Successful",
        "PackageType": "Zip",
        "Architectures": [
            "x86_64"
        ],
        "EphemeralStorage": {
            "Size": 512
        },
        "SnapStart": {
            "ApplyOn": "None",
            "OptimizationStatus": "Off"
        },
        "RuntimeVersionConfig": {
            "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:124ac49f727c31fa5ba440a749eef8274ca2beb4a0a8f107a0d8c69632c715a7"
        },
        "LoggingConfig": {
            "LogFormat": "Text",
            "LogGroup": "/aws/lambda/sam-app-HelloWorldFunction-QPWF3lu5WH5D"
        }
    },
    "Code": {
        "RepositoryType": "S3",
        "Location": "https://awslambda-ap-ne-1-tasks.s3.ap-northeast-1.amazonaws.com/snapshots/999999999999/sam-app-HelloWorldFunction-QPWF3lu5WH5D-4cafe2d9-bfa0-4c6e-ace5-85f911d4f1ef?versionId=AGlFg4bdX_55gF7.2sT1lAhgMhrWAGsH&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEwaDmFwLW5vcnRoZWFzdC0xIkgwRgIhAMoMdKTWxnLDNx4NJjO4YCL9lm7m6wSZYmC40NFuCi4jAiEAr5znnrclUvuMamBBI0g4ALoTMXnejgPx0F46nQVa9gkqyQUIhv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAEGgw5MTk5ODA5MjUxMzkiDFUBOddgElEh5YMYnyqdBSU6eAlk%2FyFhxYp9Y94%2B%2FRKjrS7SKIXqte4F%2F357WL8qIqEDUry1oFtgj%2BMewyzrDed6plST8MiNal1yKJRHjEB7kqZBgFDE0lC3AGHQPHdoNzD%2F5sHv4A9ieL1BihdhSsR1Xzhl7eeA0fm%2FVg74YiQ%2F1NLSPsfcYC91VcmHFU8LulKi8IW%2FLzkyAu1dzuXYAg%2BXpJL0%2FP9EEPx7%2BmRWDvJw2w7iNg%2B3r5PZbV2Xsf6hY298I5SlyGkheL9U420q9kWyzjVRHzdT4ZB0BrIy9aG3u18HfzP2T726lsYsxjX6vHZWP3IpX%2BicMs7GwltRAQb9lmuW8RD0s2Wzvis7vSfv2domuyHsZYqPCD7EarkkFfzciVnbxB9rsknpH849A4NkFSI1lkp2bfYYBjawDVSv4QZQBdqVFdveAOSnZd18H07SwAR3ke5TS7TKEDY04GGcqE5gOROwZyGLt%2BoXJQ4rJXN97E%2FDl8vEnKDSwZ59cx3Y2fiscA0cptralwxXclmLlYbbKHrKx6Gze3xAwOZ8yk4EEHjW99%2FMvwmYLegjXlPf9h2i7X8rpPDvd%2Bpnvma4EbpxkjoYkKEp1vXzIEU1rJDsA8jydMXXN5s2h%2B3QE2SPstKaWPPjAP6f%2FKmB%2FnLNzMYDD5WPVkw4chWuDx5xhlf6Xs4gF554CE6thIvM72%2FyeREKjsQMf0YzptHABJFyrbY9lGG2uspHp6D0yhWdA57dGiAEeoRzbsBel6xrhXOW7qMQWD8mBNBKcSqiTO4F%2Brp6sXs8XLMDjbZkSPVqYz0HRp40G3SKn3x%2F9GPTBGRySqq1AFmz3vvxtglzxn1iLFtLEcvHbwDTIIQQDL%2BXfQ%2F2txpUX2%2F%2BFbWiGFB4V4hm2rEsJoA%2F6SSewjD5lbm3BjqwAXlNdi1xqFIUhtokULxnHet8spsTm5EwOuvQEkwRQORKNja%2F9h37HVyIXbH72SEilUPCaO9JwX9e904lkp6gE2kVrzMzkfC1FKng5Mif6ZpwlzW67LUBK4LOB1FTCiFV2L2infzo0EHTWrfwCSAHRKvqO7QSm1bvbX05BStzWOg7ceZimwFcY%2Bw4q9CIJ5sufAfdx4OKFNf2jhOmrC3OmOXCZAdd6%2BrIGXftXBd%2FK2lM&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240921T043717Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIA5MMZC4DJ6TXSYHCW%2F20240921%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=7cdfc1caa15064ddb507f020ae3fcdeab0579682f75325bb7030e2b6153f15a1"
    },
    "Tags": {
        "aws:cloudformation:stack-name": "sam-app",
        "lambda:createdBy": "SAM",
        "aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-1:999999999999:stack/sam-app/e63d61a0-77d0-11ef-a3d7-0e6278aaa549",
        "aws:cloudformation:logical-id": "HelloWorldFunction"
    }
}
```
### API Gatewayの確認
```bash:コマンド
LOGICAL_RESOURCE_ID="ServerlessRestApi" \
&& echo ${LOGICAL_RESOURCE_ID}

RESOURCE_PATH="/hello" \
&& echo ${RESOURCE_PATH}

RESET_API_ID=$(
    aws cloudformation describe-stack-resources \
        --stack-name ${SAM_STACK_NAME} \
        --logical-resource-id ${LOGICAL_RESOURCE_ID} \
        --query "StackResources[*].PhysicalResourceId" \
        --output text
) \
&& echo ${RESET_API_ID}

RESOURCE_ID=$(
    aws apigateway get-resources \
        --rest-api-id ${RESET_API_ID} \
        --query "items[?path=='${RESOURCE_PATH}'].id" \
        --output text
) \
&& echo ${RESOURCE_ID}

# GETメソッドの詳細確認
aws apigateway get-method \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET

```
```bash:出力
admin:~/environment/hands-on-serverless-2/sam-app $ LOGICAL_RESOURCE_ID="ServerlessRestApi" \
> && echo ${LOGICAL_RESOURCE_ID}
ServerlessRestApi
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ RESOURCE_PATH="/hello" \
> && echo ${RESOURCE_PATH}
/hello
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ RESET_API_ID=$(
>     aws cloudformation describe-stack-resources \
>         --stack-name ${SAM_STACK_NAME} \
>         --logical-resource-id ${LOGICAL_RESOURCE_ID} \
>         --query "StackResources[*].PhysicalResourceId" \
>         --output text
> ) \
> && echo ${RESET_API_ID}
utldn1vmd5
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ RESOURCE_ID=$(
>     aws apigateway get-resources \
>         --rest-api-id ${RESET_API_ID} \
>         --query "items[?path=='${RESOURCE_PATH}'].id" \
>         --output text
> ) \
> && echo ${RESOURCE_ID}
j5qo59
admin:~/environment/hands-on-serverless-2/sam-app $ 
admin:~/environment/hands-on-serverless-2/sam-app $ # GETメソッドの詳細確認
admin:~/environment/hands-on-serverless-2/sam-app $ aws apigateway get-method \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET
{
    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false,
    "methodIntegration": {
        "type": "AWS_PROXY",
        "httpMethod": "POST",
        "uri": "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:999999999999:function:sam-app-HelloWorldFunction-QPWF3lu5WH5D/invocations",
        "passthroughBehavior": "WHEN_NO_MATCH",
        "timeoutInMillis": 29000,
        "cacheNamespace": "j5qo59",
        "cacheKeyParameters": []
    }
}
```
## ローカルエンドポイント作成 (sam local start-lambda)
### ローカルエンドポイント作成
```bash:コマンド
sam local start-lambda

```
```bash:出力
admin:~/environment/hands-on-serverless-2/sam-app $ sam local start-lambda
Initializing the lambda functions containers.                                                                           
Local image was not found.                                                                                              
Removing rapid images for repo public.ecr.aws/sam/emulation-python3.8                                                   
Building image........................................................................................................................................................
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.                                                       
                                                                                                                        
Mounting /home/ec2-user/environment/hands-on-serverless-2/sam-app/.aws-sam/build/HelloWorldFunction as                  
/var/task:ro,delegated, inside runtime container                                                                        
Containers Initialization is done.                                                                                      
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the        
endpoint.                                                                                                               
2024-09-21 05:13:37 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:3001
2024-09-21 05:13:37 Press CTRL+C to quit
```
:::note info
Cloud9 2個目のターミナルで実施
:::
```bash:コマンド
 aws lambda invoke \
  --function-name "HelloWorldFunction" \
  --endpoint-url "http://127.0.0.1:3001" \
  --no-verify-ssl out.txt

```
```bash:出力 (Cloud9 ターミナル2)
admin:~/environment $  aws lambda invoke \
>   --function-name "HelloWorldFunction" \
>   --endpoint-url "http://127.0.0.1:3001" \
>   --no-verify-ssl out.txt
{
    "StatusCode": 200
}
```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ sam local start-lambda
Initializing the lambda functions containers.                                                                           
Local image was not found.                                                                                              
Removing rapid images for repo public.ecr.aws/sam/emulation-python3.8                                                   
Building image........................................................................................................................................................
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.                                                       
                                                                                                                        
Mounting /home/ec2-user/environment/hands-on-serverless-2/sam-app/.aws-sam/build/HelloWorldFunction as                  
/var/task:ro,delegated, inside runtime container                                                                        
Containers Initialization is done.                                                                                      
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the        
endpoint.                                                                                                               
2024-09-21 05:13:37 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:3001
2024-09-21 05:13:37 Press CTRL+C to quit
Invoking app.lambda_handler (python3.8)                                                                                 
Reuse the created warm container for Lambda function 'HelloWorldFunction'                                               
Lambda function 'HelloWorldFunction' is already running                                                                 
START RequestId: 49a80c64-a51c-4f66-9fcf-a9ffd864f6eb Version: $LATEST
END RequestId: 55d76972-58bd-43a5-926a-5d78725ba187
REPORT RequestId: 55d76972-58bd-43a5-926a-5d78725ba187  Init Duration: 0.02 ms  Duration: 299.86 ms     Billed Duration: 300 ms Memory Size: 128 MB     Max Memory Used: 128 MB

2024-09-21 05:31:22 127.0.0.1 - - [21/Sep/2024 05:31:22] "POST /2015-03-31/functions/HelloWorldFunction/invocations HTTP/1.1" 200 -
^C
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
```
:::note info
Cloud9 1個目のターミナルはCtrl + cでプログラムを終了
:::
### 出力確認
```bash:コマンド (Cloud9 ターミナル2)
cat out.txt

```
```bash:出力 (Cloud9 ターミナル2)
admin:~/environment $ cat out.txt 
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
```
## ローカルエンドポイント作成 (sam local start-api)
### ローカルエンドポイント作成
```bash:コマンド (Cloud9 ターミナル1)
sam local start-api

```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ sam local start-api
Initializing the lambda functions containers.                                                                           
Local image is up-to-date                                                                                               
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.                                                       
                                                                                                                        
Mounting /home/ec2-user/environment/hands-on-serverless-2/sam-app/.aws-sam/build/HelloWorldFunction as                  
/var/task:ro,delegated, inside runtime container                                                                        
Containers Initialization is done.                                                                                      
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]                                                        
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. If you used sam build before running local
commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you    
update your AWS SAM template                                                                                            
2024-09-21 05:42:24 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:3000
2024-09-21 05:42:24 Press CTRL+C to quit
```
### アクセス確認
```bash:コマンド (Cloud9 ターミナル2)
curl http://127.0.0.1:3000/hello

```
```bash:出力 (Cloud9 ターミナル2)
admin:~/environment $ curl http://127.0.0.1:3000/hello
{"message": "hello world"}
```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ sam local start-api
Initializing the lambda functions containers.                                                                           
Local image is up-to-date                                                                                               
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.                                                       
                                                                                                                        
Mounting /home/ec2-user/environment/hands-on-serverless-2/sam-app/.aws-sam/build/HelloWorldFunction as                  
/var/task:ro,delegated, inside runtime container                                                                        
Containers Initialization is done.                                                                                      
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]                                                        
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. If you used sam build before running local
commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you    
update your AWS SAM template                                                                                            
2024-09-21 05:42:24 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:3000
2024-09-21 05:42:24 Press CTRL+C to quit
Invoking app.lambda_handler (python3.8)                                                                                 
Reuse the created warm container for Lambda function 'HelloWorldFunction'                                               
Lambda function 'HelloWorldFunction' is already running                                                                 
END RequestId: 2b933c1a-8f28-47fa-85d6-97c33441f055
REPORT RequestId: 2b933c1a-8f28-47fa-85d6-97c33441f055  Init Duration: 0.02 ms  Duration: 93.39 ms      Billed Duration: 94 ms  Memory Size: 128 MB     Max Memory Used: 128 MB

No Content-Type given. Defaulting to 'application/json'.                                                                
2024-09-21 05:44:42 127.0.0.1 - - [21/Sep/2024 05:44:42] "GET /hello HTTP/1.1" 200 -
^C
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
```
:::note info
Cloud9 1個目のターミナルはCtrl + cでプログラムを終了
:::
### app.py更新
```bash:コマンド (Cloud9 ターミナル1)
cat << EOF > hands-on-serverless-2/sam-app/hello_world/app.py 
import json

# import requests


def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    # try:
    #     ip = requests.get("http://checkip.amazonaws.com/")
    # except requests.RequestException as e:
    #     # Send some context about this error to Lambda Logs
    #     print(e)

    #     raise e

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world - SAM CLI",
            # "location": ip.text.replace("\n", "")
        }),
    }
EOF

```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ cat << EOF > hello_world/app.py 
> import json
> 
> # import requests
> 
> 
> def lambda_handler(event, context):
>     """Sample pure Lambda function
> 
>     Parameters
>     ----------
>     event: dict, required
>         API Gateway Lambda Proxy Input Format
> 
>         Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
> 
>     context: object, required
>         Lambda Context runtime methods and attributes
> 
>         Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html
> 
>     Returns
>     ------
>     API Gateway Lambda Proxy Output Format: dict
> 
>         Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
>     """
> 
>     # try:
>     #     ip = requests.get("http://checkip.amazonaws.com/")
>     # except requests.RequestException as e:
>     #     # Send some context about this error to Lambda Logs
>     #     print(e)
> 
>     #     raise e
> 
>     return {
>         "statusCode": 200,
>         "body": json.dumps({
>             "message": "hello world - SAM CLI",
>             # "location": ip.text.replace("\n", "")
>         }),
>     }
> EOF
```
### ビルド
```bash:コマンド (Cloud9 ターミナル1)
sam build

```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ sam build
Starting Build use cache                                                                                                
Manifest is not changed for (HelloWorldFunction), running incremental build                                             
Building codeuri: /home/ec2-user/environment/hands-on-serverless-2/sam-app/hello_world runtime: python3.8 metadata: {}  
architecture: x86_64 functions: HelloWorldFunction                                                                      
 Running PythonPipBuilder:CopySource                                                                                    
 Running PythonPipBuilder:CopySource                                                                                    

Build Succeeded

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

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
```
### ローカルエンドポイント作成
```bash:コマンド (Cloud9 ターミナル1)
sam local start-api

```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ sam local start-api
Initializing the lambda functions containers.                                                                           
Local image is up-to-date                                                                                               
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.                                                       
                                                                                                                        
Mounting /home/ec2-user/environment/hands-on-serverless-2/sam-app/.aws-sam/build/HelloWorldFunction as                  
/var/task:ro,delegated, inside runtime container                                                                        
Containers Initialization is done.                                                                                      
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]                                                        
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. If you used sam build before running local
commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you    
update your AWS SAM template                                                                                            
2024-09-21 05:57:30 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:3000
2024-09-21 05:57:30 Press CTRL+C to quit
```

```bash:コマンド (Cloud9 ターミナル2)
curl http://127.0.0.1:3000/hello

```
```bash:出力 (Cloud9 ターミナル2)
admin:~/environment $ curl http://127.0.0.1:3000/hello
{"message": "hello world - SAM CLI"}
```
```bash:出力 (Cloud9 ターミナル1)
admin:~/environment/hands-on-serverless-2/sam-app $ sam local start-api
Initializing the lambda functions containers.                                                                           
Local image is up-to-date                                                                                               
Using local image: public.ecr.aws/lambda/python:3.8-rapid-x86_64.                                                       
                                                                                                                        
Mounting /home/ec2-user/environment/hands-on-serverless-2/sam-app/.aws-sam/build/HelloWorldFunction as                  
/var/task:ro,delegated, inside runtime container                                                                        
Containers Initialization is done.                                                                                      
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]                                                        
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. If you used sam build before running local
commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you    
update your AWS SAM template                                                                                            
2024-09-21 05:57:30 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:3000
2024-09-21 05:57:30 Press CTRL+C to quit
Invoking app.lambda_handler (python3.8)                                                                                 
Reuse the created warm container for Lambda function 'HelloWorldFunction'                                               
Lambda function 'HelloWorldFunction' is already running                                                                 
END RequestId: 972d3a22-8d03-4953-862b-bc2b535d757b
REPORT RequestId: 972d3a22-8d03-4953-862b-bc2b535d757b  Init Duration: 0.02 ms  Duration: 114.26 ms     Billed Duration: 115 ms Memory Size: 128 MB     Max Memory Used: 128 MB

No Content-Type given. Defaulting to 'application/json'.                                                                
2024-09-21 05:58:29 127.0.0.1 - - [21/Sep/2024 05:58:29] "GET /hello HTTP/1.1" 200 -
^C
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
```
# 10 クリーンアップ & 落ち穂拾い & まとめ
:::note info
以降、CloudShellで実施
:::
## Cloud9
```bash:コマンド
aws cloud9 delete-environment \
    --environment-id ${CLOUD9_ENVIRONMENT_ID}

```
```bash:出力
[cloudshell-user@ip-10-132-75-96 ~]$ aws cloud9 delete-environment \
>     --environment-id ${CLOUD9_ENVIRONMENT_ID}
```
## S3
```bash:コマンド
# パッケージ用バケット
S3_BUCKET_NAME="hands-on-serverless-20240921" \
&& echo ${S3_BUCKET_NAME}

# オブジェクト一覧取得
OBJECT_LIST=$(
    aws s3api list-object-versions \
        --bucket ${S3_BUCKET_NAME} \
        --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}'
) \
&& echo ${OBJECT_LIST}

# JSONフォーマットの確認
echo ${OBJECT_LIST} | python -m json.tool

# バケットを空にする
aws s3api delete-objects \
    --bucket ${S3_BUCKET_NAME} \
    --delete "${OBJECT_LIST}"

# S3バケットを削除
aws s3api delete-bucket \
    --bucket ${S3_BUCKET_NAME}


# Managed Stack for AWS SAM CLIバケット
SAM_CLI_MNG_STACK="aws-sam-cli-managed-default" \
&& echo ${SAM_CLI_MNG_STACK}

LOGICAL_RESOURCE_ID="SamCliSourceBucket" \
&& echo ${LOGICAL_RESOURCE_ID}

S3_SAM_CLI_MNG_BUCKET_NAME=$(
    aws cloudformation describe-stack-resources \
        --stack-name ${SAM_CLI_MNG_STACK} \
        --logical-resource-id ${LOGICAL_RESOURCE_ID} \
        --query "StackResources[*].PhysicalResourceId" \
        --output text
) \
&& echo ${S3_SAM_CLI_MNG_BUCKET_NAME}

# オブジェクト一覧取得
OBJECT_LIST=$(
    aws s3api list-object-versions \
        --bucket ${S3_SAM_CLI_MNG_BUCKET_NAME} \
        --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}'
) \
&& echo ${OBJECT_LIST}

# JSONフォーマットの確認
echo ${OBJECT_LIST} | python -m json.tool

# バケットを空にする
aws s3api delete-objects \
    --bucket ${S3_SAM_CLI_MNG_BUCKET_NAME} \
    --delete "${OBJECT_LIST}"

# S3バケットを削除
aws s3api delete-bucket \
    --bucket ${S3_SAM_CLI_MNG_BUCKET_NAME}

```
```bash:出力
[cloudshell-user@ip-10-132-75-96 ~]$ # パッケージ用バケット
[cloudshell-user@ip-10-132-75-96 ~]$ S3_BUCKET_NAME="hands-on-serverless-20240921" \
> && echo ${S3_BUCKET_NAME}
hands-on-serverless-20240921
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # オブジェクト一覧取得
[cloudshell-user@ip-10-132-75-96 ~]$ OBJECT_LIST=$(
>     aws s3api list-object-versions \
>         --bucket ${S3_BUCKET_NAME} \
>         --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}'
> ) \
> && echo ${OBJECT_LIST}
{ "Objects": [ { "Key": "390d10c0b490f110117d9155f60cf0b0", "VersionId": "null" }, { "Key": "44a6c97b5e9e64448acb91573299c6ba", "VersionId": "null" }, { "Key": "5e32642d09e1b88190f79969845b9d2b", "VersionId": "null" }, { "Key": "f802e187101b61229f32ab9c1db914b5", "VersionId": "null" } ] }
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-132-75-96 ~]$ echo ${OBJECT_LIST} | python -m json.tool
{
    "Objects": [
        {
            "Key": "390d10c0b490f110117d9155f60cf0b0",
            "VersionId": "null"
        },
        {
            "Key": "44a6c97b5e9e64448acb91573299c6ba",
            "VersionId": "null"
        },
        {
            "Key": "5e32642d09e1b88190f79969845b9d2b",
            "VersionId": "null"
        },
        {
            "Key": "f802e187101b61229f32ab9c1db914b5",
            "VersionId": "null"
        }
    ]
}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # バケットを空にする
[cloudshell-user@ip-10-132-75-96 ~]$ aws s3api delete-objects \
>     --bucket ${S3_BUCKET_NAME} \
>     --delete "${OBJECT_LIST}"
{
    "Deleted": [
        {
            "Key": "390d10c0b490f110117d9155f60cf0b0",
            "VersionId": "null"
        },
        {
            "Key": "5e32642d09e1b88190f79969845b9d2b",
            "VersionId": "null"
        },
        {
            "Key": "f802e187101b61229f32ab9c1db914b5",
            "VersionId": "null"
        },
        {
            "Key": "44a6c97b5e9e64448acb91573299c6ba",
            "VersionId": "null"
        }
    ]
}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # S3バケットを削除
[cloudshell-user@ip-10-132-75-96 ~]$ aws s3api delete-bucket \
>     --bucket ${S3_BUCKET_NAME}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # Managed Stack for AWS SAM CLIバケット
[cloudshell-user@ip-10-132-75-96 ~]$ SAM_CLI_MNG_STACK="aws-sam-cli-managed-default" \
> && echo ${SAM_CLI_MNG_STACK}
aws-sam-cli-managed-default
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ LOGICAL_RESOURCE_ID="SamCliSourceBucket" \
> && echo ${LOGICAL_RESOURCE_ID}
SamCliSourceBucket
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ S3_SAM_CLI_MNG_BUCKET_NAME=$(
>     aws cloudformation describe-stack-resources \
>         --stack-name ${SAM_CLI_MNG_STACK} \
>         --logical-resource-id ${LOGICAL_RESOURCE_ID} \
>         --query "StackResources[*].PhysicalResourceId" \
>         --output text
> ) \
> && echo ${S3_SAM_CLI_MNG_BUCKET_NAME}
aws-sam-cli-managed-default-samclisourcebucket-ktfpyr4rinwv
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # オブジェクト一覧取得
[cloudshell-user@ip-10-132-75-96 ~]$ OBJECT_LIST=$(
>     aws s3api list-object-versions \
>         --bucket ${S3_SAM_CLI_MNG_BUCKET_NAME} \
>         --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}'
> ) \
> && echo ${OBJECT_LIST}
{ "Objects": [ { "Key": "sam-app/45916096b7662dd4d586fe710b60ddc6", "VersionId": "xUa0Ov7KulLQToXuwsro4kcPaGZTMIsN" }, { "Key": "sam-app/d3a404bc8c7a981f5668a316182fb5ca.template", "VersionId": "HXRokj97Q9MAGulZQTLmt7lnJpHa6cRu" } ] }
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-132-75-96 ~]$ echo ${OBJECT_LIST} | python -m json.tool
{
    "Objects": [
        {
            "Key": "sam-app/45916096b7662dd4d586fe710b60ddc6",
            "VersionId": "xUa0Ov7KulLQToXuwsro4kcPaGZTMIsN"
        },
        {
            "Key": "sam-app/d3a404bc8c7a981f5668a316182fb5ca.template",
            "VersionId": "HXRokj97Q9MAGulZQTLmt7lnJpHa6cRu"
        }
    ]
}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # バケットを空にする
[cloudshell-user@ip-10-132-75-96 ~]$ aws s3api delete-objects \
>     --bucket ${S3_SAM_CLI_MNG_BUCKET_NAME} \
>     --delete "${OBJECT_LIST}"
{
    "Deleted": [
        {
            "Key": "sam-app/45916096b7662dd4d586fe710b60ddc6",
            "VersionId": "xUa0Ov7KulLQToXuwsro4kcPaGZTMIsN"
        },
        {
            "Key": "sam-app/d3a404bc8c7a981f5668a316182fb5ca.template",
            "VersionId": "HXRokj97Q9MAGulZQTLmt7lnJpHa6cRu"
        }
    ]
}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # S3バケットを削除
[cloudshell-user@ip-10-132-75-96 ~]$ aws s3api delete-bucket \
>     --bucket ${S3_SAM_CLI_MNG_BUCKET_NAME}
```
## CloudFormation
```bash:コマンド
# パッケージ用スタック名
STACK_NAME="hands-on-serverless-2" \
&& echo ${STACK_NAME}

aws cloudformation delete-stack \
  --stack-name ${STACK_NAME}

# Managed Stack for AWS SAM CLIスタック名
SAM_CLI_MNG_STACK="aws-sam-cli-managed-default" \
&& echo ${SAM_CLI_MNG_STACK}

aws cloudformation delete-stack \
  --stack-name ${SAM_CLI_MNG_STACK}

# Managed Stack for AWS SAM CLIスタック名
SAM_STACK_NAME="sam-app" \
&& echo ${SAM_STACK_NAME}

aws cloudformation delete-stack \
  --stack-name ${SAM_STACK_NAME}

```
```bash:出力
[cloudshell-user@ip-10-132-75-96 ~]$ # パッケージ用スタック名
[cloudshell-user@ip-10-132-75-96 ~]$ STACK_NAME="hands-on-serverless-2" \
[cloudshell-user@ip-10-132-75-96 ~]$ && echo ${STACK_NAME}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ aws cloudformation delete-stack \
[cloudshell-user@ip-10-132-75-96 ~]$   --stack-name ${STACK_NAME}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # Managed Stack for AWS SAM CLIスタック名
[cloudshell-user@ip-10-132-75-96 ~]$ SAM_CLI_MNG_STACK="aws-sam-cli-managed-default" \
[cloudshell-user@ip-10-132-75-96 ~]$ && echo ${SAM_CLI_MNG_STACK}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ aws cloudformation delete-stack \
[cloudshell-user@ip-10-132-75-96 ~]$   --stack-name ${SAM_CLI_MNG_STACK}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ # Managed Stack for AWS SAM CLIスタック名
[cloudshell-user@ip-10-132-75-96 ~]$ SAM_STACK_NAME="sam-app" \
[cloudshell-user@ip-10-132-75-96 ~]$ && echo ${SAM_STACK_NAME}
[cloudshell-user@ip-10-132-75-96 ~]$ 
[cloudshell-user@ip-10-132-75-96 ~]$ aws cloudformation delete-stack \
[cloudshell-user@ip-10-132-75-96 ~]$   --stack-name ${SAM_STACK_NAME}
```