「AWS Hands-on for Beginners Serverless #1 サーバーレスアーキテクチャで翻訳 Web API を構築する」をAWS CLIでやってみる

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

上記、「AWS Hands-on for Beginners Amazon EC2 Auto Scaling スケーリング基礎編」 をAWS CLIでやってみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/13e42ce8-b21f-5b8b-a1e4-d69b56228dd3.png)
ハンズオンから引用

:::note warn
ハンズオンではPython3.7を使用していたが、サポート修了のためPython3.8を使用
:::
# 1. AWS Lambda ハンズオン① Lambda を単体で使ってみる
## 関数設定
```bash:コマンド
FUNCTION_NAME="translate-function" \
&& echo ${FUNCTION_NAME}

RUNTIME="python3.8" \
&& echo ${RUNTIME}

POLICY_NAME="translate-function-policy" \
&& echo ${POLICY_NAME}

ROLE_NAME="translate-function-role" \
&& echo ${ROLE_NAME}

MEMORY_SIZE=256 \
&& echo ${MEMORY_SIZE}

TIMEOUT=10 \
&& echo ${TIMEOUT}
```
```bash:出力
[cloudshell-user@ip-10-130-40-130 ~]$ FUNCTION_NAME="translate-function" \
> && echo ${FUNCTION_NAME}
translate-function
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ RUNTIME="python3.8" \
> && echo ${RUNTIME}
python3.8
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ POLICY_NAME="translate-function-policy" \
> && echo ${POLICY_NAME}
translate-function-policy
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ ROLE_NAME="translate-function-role" \
> && echo ${ROLE_NAME}
translate-function-role
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ MEMORY_SIZE=256 \
> && echo ${MEMORY_SIZE}
256
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ TIMEOUT=10 \
> && echo ${TIMEOUT}
10
```
## IAMポリシー作成
```bash:コマンド
# ポリシードキュメントの作成
POLICY_DOCUMENT_JSON=$(cat << EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:ap-northeast-1:999999999999:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:999999999999:log-group:/aws/lambda/translate-function:*"
            ]
        }
    ]
}
EOF
) \
&& echo ${POLICY_DOCUMENT_JSON}

# ポリシーの作成
aws iam create-policy \
    --policy-name ${POLICY_NAME} \
    --policy-document "${POLICY_DOCUMENT_JSON}"

# ARN取得
POLICY_ARN=$(
    aws iam list-policies \
        --query "Policies[?PolicyName=='${POLICY_NAME}'].Arn" \
        --output text
) \
&& echo ${POLICY_ARN}
```
```bash:出力
[cloudshell-user@ip-10-130-40-130 ~]$ # ポリシードキュメントの作成
[cloudshell-user@ip-10-130-40-130 ~]$ POLICY_DOCUMENT_JSON=$(cat << EOF
> {
>     "Version": "2012-10-17",
>     "Statement": [
>         {
>             "Effect": "Allow",
>             "Action": "logs:CreateLogGroup",
>             "Resource": "arn:aws:logs:ap-northeast-1:999999999999:*"
>         },
>         {
>             "Effect": "Allow",
>             "Action": [
>                 "logs:CreateLogStream",
>                 "logs:PutLogEvents"
>             ],
>             "Resource": [
>                 "arn:aws:logs:ap-northeast-1:999999999999:log-group:/aws/lambda/translate-function:*"
>             ]
>         }
>     ]
> }
> EOF
> ) \
> && echo ${POLICY_DOCUMENT_JSON}
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "arn:aws:logs:ap-northeast-1:999999999999:*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:ap-northeast-1:999999999999:log-group:/aws/lambda/translate-function:*" ] } ] }
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ # ポリシーの作成
[cloudshell-user@ip-10-130-40-130 ~]$ aws iam create-policy \
>     --policy-name ${POLICY_NAME} \
>     --policy-document "${POLICY_DOCUMENT_JSON}"
{
    "Policy": {
        "PolicyName": "translate-function-policy",
        "PolicyId": "ANPAWFKRCMKOZKEIZQSVJ",
        "Arn": "arn:aws:iam::999999999999:policy/translate-function-policy",
        "Path": "/",
        "DefaultVersionId": "v1",
        "AttachmentCount": 0,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2024-07-13T05:52:57+00:00",
        "UpdateDate": "2024-07-13T05:52:57+00:00"
    }
}
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ # ARN取得
[cloudshell-user@ip-10-130-40-130 ~]$ POLICY_ARN=$(
>     aws iam list-policies \
>         --query "Policies[?PolicyName=='${POLICY_NAME}'].Arn" \
>         --output text
> ) \
> && echo ${POLICY_ARN}
arn:aws:iam::999999999999:policy/translate-function-policy
```
## IAMロール作成
```bash:コマンド
# 信頼関係ポリシードキュメントの作成
ASSUME_ROLE_POLICY_DOCUMENT=$(cat << EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF
) \
&& echo ${ASSUME_ROLE_POLICY_DOCUMENT}

# IAMロールの作成
aws iam create-role \
    --role-name ${ROLE_NAME} \
    --assume-role-policy-document "${ASSUME_ROLE_POLICY_DOCUMENT}"

# IAMロールにポリシーをアタッチ
aws iam attach-role-policy \
  --role-name ${ROLE_NAME} \
  --policy-arn ${POLICY_ARN}

# ARN取得
ROLE_ARN=$(
    aws iam get-role \
        --role-name ${ROLE_NAME} \
        --query 'Role.Arn' --output text
) \
&& echo ${ROLE_ARN}
```
```bash:出力
[cloudshell-user@ip-10-130-40-130 ~]$ # 信頼関係ポリシードキュメントの作成
[cloudshell-user@ip-10-130-40-130 ~]$ ASSUME_ROLE_POLICY_DOCUMENT=$(cat << EOF
> {
>     "Version": "2012-10-17",
>     "Statement": [
>         {
>             "Effect": "Allow",
>             "Principal": {
>                 "Service": "lambda.amazonaws.com"
>             },
>             "Action": "sts:AssumeRole"
>         }
>     ]
> }
> EOF
> ) \
> && echo ${ASSUME_ROLE_POLICY_DOCUMENT}
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ # IAMロールの作成
[cloudshell-user@ip-10-130-40-130 ~]$ aws iam create-role \
>     --role-name ${ROLE_NAME} \
>     --assume-role-policy-document "${ASSUME_ROLE_POLICY_DOCUMENT}"
{
    "Role": {
        "Path": "/",
        "RoleName": "translate-function-role",
        "RoleId": "AROAWFKRCMKOVFJD5Y2OU",
        "Arn": "arn:aws:iam::999999999999:role/translate-function-role",
        "CreateDate": "2024-07-13T05:54:33+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ # IAMロールにポリシーをアタッチ
[cloudshell-user@ip-10-130-40-130 ~]$ aws iam attach-role-policy \
>   --role-name ${ROLE_NAME} \
>   --policy-arn ${POLICY_ARN}
[cloudshell-user@ip-10-130-40-130 ~]$ 
[cloudshell-user@ip-10-130-40-130 ~]$ # ARN取得
[cloudshell-user@ip-10-130-40-130 ~]$ ROLE_ARN=$(
>     aws iam get-role \
>         --role-name ${ROLE_NAME} \
>         --query 'Role.Arn' --output text
> ) \
> && echo ${ROLE_ARN}
arn:aws:iam::999999999999:role/translate-function-role
```
## Lambda用コードソースの作成
```bash:コマンド
cat << EOF > lambda_function.py
import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
EOF
```
```bash:出力
[cloudshell-user@ip-10-130-40-130 ~]$ cat << EOF > lambda_function.py
> import json
> 
> def lambda_handler(event, context):
>     # TODO implement
>     return {
>         'statusCode': 200,
>         'body': json.dumps('Hello from Lambda!')
>     }
> EOF
```
## Lambda用デプロイパッケージの作成
```bash:コマンド
zip function.zip lambda_function.py
```
```bash:出力
[cloudshell-user@ip-10-130-40-130 ~]$ zip function.zip lambda_function.py
  adding: lambda_function.py (deflated 19%)
```
## Lambda関数の作成
:::note info
--handlerはPythonファイル名.関数名で指定
:::
```bash:コマンド
# Lambda関数の作成
aws lambda create-function \
    --function-name ${FUNCTION_NAME} \
    --runtime ${RUNTIME} \
    --role $ROLE_ARN \
    --handler lambda_function.lambda_handler \
    --zip-file fileb://function.zip \
    --memory-size ${MEMORY_SIZE} \
    --timeout ${TIMEOUT}

# ARN取得
FUNCTION_ARN=$(
    aws lambda list-functions \
        --query "Functions[?FunctionName=='${FUNCTION_NAME}'].FunctionArn" \
        --output text
) \
&& echo ${FUNCTION_ARN}
```
```bash:出力
[cloudshell-user@ip-10-130-40-130 ~]$ # Lambda関数の作成
[cloudshell-user@ip-10-130-40-130 ~]$ aws lambda create-function \
>     --function-name ${FUNCTION_NAME} \
>     --runtime ${RUNTIME} \
>     --role $ROLE_ARN \
>     --handler lambda_function.lambda_handler \
>     --zip-file fileb://function.zip \
>     --memory-size ${MEMORY_SIZE} \
>     --timeout ${TIMEOUT}
{
    "FunctionName": "translate-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function",
    "Runtime": "python3.8",
    "Role": "arn:aws:iam::999999999999:role/translate-function-role",
    "Handler": "lambda_function.lambda_handler",
    "CodeSize": 320,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 256,
    "LastModified": "2024-07-13T06:29:36.292+0000",
    "CodeSha256": "k2GJUJo8DYDjiUH1MeGyiT4S1t10hWI/GYWEcatHzfo=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "666ba46f-3bd0-45c2-a795-5760ad264c83",
    "State": "Pending",
    "StateReason": "The function is being created.",
    "StateReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbcd583856654db68283"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/translate-function"
    }
}
[cloudshell-user@ip-10-130-40-130 ~]$ # ARN取得
[cloudshell-user@ip-10-130-40-130 ~]$ FUNCTION_ARN=$(
>     aws lambda list-functions \
>         --query "Functions[?FunctionName=='${FUNCTION_NAME}'].FunctionArn" \
>         --output text
> ) \
> && echo ${FUNCTION_ARN}
arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function
```
## Lambda関数のテスト実行
:::note warn
--payloadはbase64形式で指定
:::
```bash:コマンド
# イベントの作成
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:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # イベントの作成
[cloudshell-user@ip-10-132-89-176 ~]$ EVENT=$(cat << EOF
> {
>   "key1": "value1",
>   "key2": "value2",
>   "key3": "value3"
> }
> EOF
> ) \
> && echo ${EVENT}
{ "key1": "value1", "key2": "value2", "key3": "value3" }
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-132-89-176 ~]$ echo ${EVENT} | python -m json.tool
{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
}
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # テスト実行
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
[cloudshell-user@ip-10-132-89-176 ~]$ cat outputfile.txt
{"statusCode": 200, "body": "\"Hello from Lambda!\""}
```
## Lambda用ソースコードの更新
```bash:コマンド
# ソースコードの更新
cat << EOF > lambda_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

# デプロイパッケージの作成
zip function.zip lambda_function.py

# Lambda関数の更新
aws lambda update-function-code \
  --function-name ${FUNCTION_NAME}  \
  --zip-file fileb://function.zip
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # ソースコードの更新
[cloudshell-user@ip-10-132-89-176 ~]$ cat << EOF > lambda_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
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # デプロイパッケージの作成
[cloudshell-user@ip-10-132-89-176 ~]$ zip function.zip lambda_function.py
updating: lambda_function.py (deflated 31%)
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # Lambda関数の更新
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda update-function-code \
>   --function-name ${FUNCTION_NAME}  \
>   --zip-file fileb://function.zip
{
    "FunctionName": "translate-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function",
    "Runtime": "python3.8",
    "Role": "arn:aws:iam::999999999999:role/translate-function-role",
    "Handler": "lambda_function.lambda_handler",
    "CodeSize": 356,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 256,
    "LastModified": "2024-07-13T10:09:30.000+0000",
    "CodeSha256": "i6TM4nP5Go39fnVJjin2PHlVi6ZCav7Xfqz1ffbmiQI=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "56db0836-ddf9-4d04-a1a9-aa382555d351",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbcd583856654db68283"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/translate-function"
    }
}
```
## Lambda関数のテスト実行
```bash:コマンド
# テスト実行
aws lambda invoke \
  --function-name ${FUNCTION_NAME} \
  --payload `echo ${EVENT} | base64 -w 0` \
  outputfile.txt

cat outputfile.txt
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ cat outputfile.txt
{"statusCode": 200, "body": "\"Hello Hands on world!\""}
```
## CloudWatchログの確認
```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} 
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # 最新のログストリームの取得
[cloudshell-user@ip-10-132-89-176 ~]$ 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/07/13/[$LATEST]20430606b82f401e94c39467f8074e5b
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # ログの確認
[cloudshell-user@ip-10-132-89-176 ~]$ aws logs get-log-events \
>     --log-group-name /aws/lambda/${FUNCTION_NAME} \
>     --log-stream-name ${LATEST_LOG_STREAM} 
{
    "events": [
        {
            "timestamp": 1720865480248,
            "message": "INIT_START Runtime Version: python:3.8.v51\tRuntime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbd583856654db68283\n",
            "ingestionTime": 1720865484129
        },
        {
            "timestamp": 1720865480373,
            "message": "START RequestId: 0b810e7a-e8aa-4a1d-88ad-8a8d1d584bdc Version: $LATEST\n",
            "ingestionTime": 1720865484129
        },
        {
            "timestamp": 1720865480374,
            "message": "[INFO]\t2024-07-13T10:11:20.373Z\t0b810e7a-e8aa-4a1d-88ad-8a8d1d584bdc\t{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}\n",
            "ingestionTime": 1720865484129
        },
        {
            "timestamp": 1720865480375,
            "message": "END RequestId: 0b810e7a-e8aa-4a1d-88ad-8a8d1d584bdc\n",
            "ingestionTime": 1720865484129
        },
        {
            "timestamp": 1720865480375,
            "message": "REPORT RequestId: 0b810e7a-e8aa-4a1d-88ad-8a8d1d584bdc\tDuration: 2.19 ms\tBilled Duration: 3 ms\tMemory Size: 256 MB\tMax Memory Used: 39 MB\tInit Duation: 124.47 ms\t\n",
            "ingestionTime": 1720865484129
        },
        {
            "timestamp": 1720865687128,
            "message": "START RequestId: a0040360-8c77-47d8-b748-9cc176dea3de Version: $LATEST\n",
            "ingestionTime": 1720865696156
        },
        {
            "timestamp": 1720865687128,
            "message": "[INFO]\t2024-07-13T10:14:47.128Z\ta0040360-8c77-47d8-b748-9cc176dea3de\t{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}\n",
            "ingestionTime": 1720865696156
        },
        {
            "timestamp": 1720865687131,
            "message": "END RequestId: a0040360-8c77-47d8-b748-9cc176dea3de\n",
            "ingestionTime": 1720865696156
        },
        {
            "timestamp": 1720865687131,
            "message": "REPORT RequestId: a0040360-8c77-47d8-b748-9cc176dea3de\tDuration: 1.24 ms\tBilled Duration: 2 ms\tMemory Size: 256 MB\tMax Memory Used: 40 MB\t\n",
            "ingestionTime": 1720865696156
        }
    ],
    "nextForwardToken": "f/38376587209602749806084872664013029979155431024260087811/s",
    "nextBackwardToken": "b/38376582595957680898473965017354384466886398518340943872/s"
}
```
# 2. AWS Lambda ハンズオン② 他のサービスを呼び出してみる
## Lambda用ソースコードの更新
```bash:コマンド
# ソースコードの更新
cat << EOF > lambda_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

# デプロイパッケージの作成
zip function.zip lambda_function.py

# Lambda関数の更新
aws lambda update-function-code \
  --function-name ${FUNCTION_NAME}  \
  --zip-file fileb://function.zip
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # ソースコードの更新
[cloudshell-user@ip-10-132-89-176 ~]$ cat << EOF > lambda_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
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # デプロイパッケージの作成
[cloudshell-user@ip-10-132-89-176 ~]$ zip function.zip lambda_function.py
updating: lambda_function.py (deflated 45%)
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # Lambda関数の更新
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda update-function-code \
>   --function-name ${FUNCTION_NAME}  \
>   --zip-file fileb://function.zip
{
    "FunctionName": "translate-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function",
    "Runtime": "python3.8",
    "Role": "arn:aws:iam::999999999999:role/translate-function-role",
    "Handler": "lambda_function.lambda_handler",
    "CodeSize": 434,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 256,
    "LastModified": "2024-07-13T11:31:33.000+0000",
    "CodeSha256": "Imcd+SL+u1nQ+JgEeEUcH6aXulz4/3qD7q6RGFMwd2I=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "58a51fb9-3851-4d06-aa68-dc6f72663229",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbcd583856654db68283"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/translate-function"
    }
}
```
## IAMロールを修正
```bash:コマンド
ATTACH_POLICY_NAME="TranslateFullAccess" \
&& echo ${ATTACH_POLICY_NAME}

# IAMロールにポリシーをアタッチ
aws iam attach-role-policy \
  --role-name ${ROLE_NAME} \
  --policy-arn arn:aws:iam::aws:policy/${ATTACH_POLICY_NAME}
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ ATTACH_POLICY_NAME="TranslateFullAccess" \
> && echo ${ATTACH_POLICY_NAME}
TranslateFullAccess
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # IAMロールにポリシーをアタッチ
[cloudshell-user@ip-10-132-89-176 ~]$ aws iam attach-role-policy \
>   --role-name ${ROLE_NAME} \
>   --policy-arn arn:aws:iam::aws:policy/${ATTACH_POLICY_NAME}
```
## Lambda関数のテスト実行
```bash:コマンド
# テスト実行
aws lambda invoke \
  --function-name ${FUNCTION_NAME} \
  --payload `echo ${EVENT} | base64 -w 0` \
  outputfile.txt

cat outputfile.txt
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # テスト実行
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ cat outputfile.txt
{"statusCode": 200, "body": "{\"output_text\": \"Good morning\"}"}
```
# Lambda用ソースコードの更新
```bash:コマンド
# ソースコードの更新
cat << EOF > lambda_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

# デプロイパッケージの作成
zip function.zip lambda_function.py

# Lambda関数の更新
aws lambda update-function-code \
  --function-name ${FUNCTION_NAME}  \
  --zip-file fileb://function.zip
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # ソースコードの更新
[cloudshell-user@ip-10-132-89-176 ~]$ cat << EOF > lambda_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
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # デプロイパッケージの作成
[cloudshell-user@ip-10-132-89-176 ~]$ zip function.zip lambda_function.py
updating: lambda_function.py (deflated 44%)
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ # Lambda関数の更新
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda update-function-code \
>   --function-name ${FUNCTION_NAME}  \
>   --zip-file fileb://function.zip
{
    "FunctionName": "translate-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function",
    "Runtime": "python3.8",
    "Role": "arn:aws:iam::999999999999:role/translate-function-role",
    "Handler": "lambda_function.lambda_handler",
    "CodeSize": 435,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 256,
    "LastModified": "2024-07-13T11:58:01.000+0000",
    "CodeSha256": "IzTNqG84eDihL3zaRBo7+Vcu77ZB/u+ZSAJWC9pdZac=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "f9500e26-d906-486f-88ee-151187e0518c",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbcd583856654db68283"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/translate-function"
    }
}
```
# Lambda関数のテスト実行
```bash:コマンド
# テスト実行
aws lambda invoke \
  --function-name ${FUNCTION_NAME} \
  --payload `echo ${EVENT} | base64 -w 0` \
  outputfile.txt

cat outputfile.txt
```
```bash:出力
[cloudshell-user@ip-10-132-89-176 ~]$ # テスト実行
[cloudshell-user@ip-10-132-89-176 ~]$ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
[cloudshell-user@ip-10-132-89-176 ~]$ 
[cloudshell-user@ip-10-132-89-176 ~]$ cat outputfile.txt
{"statusCode": 200, "body": "{\"output_text\": \"Goodnight\"}"}
```
# 3. Amazon API Gateway ハンズオン① API Gateway を単体で使ってみる
## 変数
```bash:コマンド
# API名
API_NAME="translate-api" \
&& echo ${API_NAME}

# リソース名
RESOURCE_NAME="sample" \
&& echo ${RESOURCE_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # API名
[cloudshell-user@ip-10-134-14-200 ~]$ API_NAME="translate-api" \
> && echo ${API_NAME}
translate-api
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # リソース名
[cloudshell-user@ip-10-134-14-200 ~]$ RESOURCE_NAME="sample" \
> && echo ${RESOURCE_NAME}
sample
```
## API Gateway APIの作成
```bash:コマンド
aws apigateway create-rest-api \
    --name ${API_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway create-rest-api \
>     --name ${API_NAME}
{
    "id": "jvalq6w8li",
    "name": "translate-api",
    "createdDate": "2024-07-14T02:47:29+00:00",
    "apiKeySource": "HEADER",
    "endpointConfiguration": {
        "types": [
            "EDGE"
        ]
    },
    "disableExecuteApiEndpoint": false,
    "rootResourceId": "okjuj6zkl4"
}
```
## API Gateway API IDの取得
```bash:コマンド
# API IDの取得
RESET_API_ID=$(
    aws apigateway get-rest-apis \
        --query "items[?name=='${API_NAME}'].id" \
        --output text
) \
&& echo ${RESET_API_ID}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # API IDの取得
[cloudshell-user@ip-10-134-14-200 ~]$ RESET_API_ID=$(
>     aws apigateway get-rest-apis \
>         --query "items[?name=='${API_NAME}'].id" \
>         --output text
> ) \
> && echo ${RESET_API_ID}
jvalq6w8li
```
## API GatewayルートリソースIDの取得
```bash:コマンド
# ルートリソースIDの取得
PARENT_ID=$(
    aws apigateway get-resources \
        --rest-api-id ${RESET_API_ID} \
        --query "items[?path=='/'].id" \
        --output text
) \
&& echo ${PARENT_ID}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # ルートリソースIDの取得
[cloudshell-user@ip-10-134-14-200 ~]$ PARENT_ID=$(
>     aws apigateway get-resources \
>         --rest-api-id ${RESET_API_ID} \
>         --query "items[?path=='/'].id" \
>         --output text
> ) \
> && echo ${PARENT_ID}
okjuj6zkl4
```
## API Gatewayリソースの作成
```bash:コマンド
# リソースの作成
aws apigateway create-resource \
    --rest-api-id ${RESET_API_ID} \
    --parent-id ${PARENT_ID} \
    --path-part ${RESOURCE_NAME}

# リソースIDの取得
RESOURCE_ID=$(
    aws apigateway get-resources \
        --rest-api-id ${RESET_API_ID} \
        --query "items[?path=='/${RESOURCE_NAME}'].id" \
        --output text
) \
&& echo ${RESOURCE_ID}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # リソースの作成
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway create-resource \
>     --rest-api-id ${RESET_API_ID} \
>     --parent-id ${PARENT_ID} \
>     --path-part ${RESOURCE_NAME}
{
    "id": "5wxri4",
    "parentId": "okjuj6zkl4",
    "pathPart": "sample",
    "path": "/sample"
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # リソースIDの取得
[cloudshell-user@ip-10-134-14-200 ~]$ RESOURCE_ID=$(
>     aws apigateway get-resources \
>         --rest-api-id ${RESET_API_ID} \
>         --query "items[?path=='/${RESOURCE_NAME}'].id" \
>         --output text
> ) \
> && echo ${RESOURCE_ID}
5wxri4
```
## API Gateway GETメソッドの作成
```bash:コマンド
# GETメソッドの作成
aws apigateway put-method \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --authorization-type NONE
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # GETメソッドの作成
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-method \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --authorization-type NONE
{
    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false
}
```
## API Gateway統合の作成（Mock）
```bash:コマンド
# 統合の作成（Mock）
aws apigateway put-integration \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --type MOCK \
    --request-templates '{"application/json": "{\"statusCode\": 200}"}'
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # 統合の作成（Mock）
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-integration \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --type MOCK \
>     --request-templates '{"application/json": "{\"statusCode\": 200}"}'
{
    "type": "MOCK",
    "requestTemplates": {
        "application/json": "{\"statusCode\": 200}"
    },
    "passthroughBehavior": "WHEN_NO_MATCH",
    "timeoutInMillis": 29000,
    "cacheNamespace": "5wxri4",
    "cacheKeyParameters": []
}
```
## API Gatewayレスポンスの設定
:::note warn
`--response-templates '{"application/json": '"${ESCAPED_JSON}"'}'`
${ESCAPED_JSON}のJSONは改行コード、ダブルクォートをエスケープする必要がある

下記にてJSONをエスケープ形式に変換
`ESCAPED_JSON=$(echo "${JSON_TEMPLATE}" | jq -Rs .)`
:::
```bash:コマンド
# メソッドレスポンスの設定
aws apigateway put-method-response \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --status-code 200 \
    --response-models '{"application/json": "Empty"}'

# テンプレートの作成
JSON_TEMPLATE=$(cat << EOF
{
    "statusCode": 200,
    "body": [
        {
            "report_id": 5,
            "report_title" : "Hello, world"
        },
        {
            "report_id": 7,
            "report_title" : "Good morning!"
        }
    ]
}
EOF
) \
&& echo ${JSON_TEMPLATE}

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

# JSONをエスケープ形式に変換
ESCAPED_JSON=$(echo "${JSON_TEMPLATE}" | jq -Rs .)

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

# 統合レスポンスの設定
aws apigateway put-integration-response \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --status-code 200 \
    --selection-pattern '' \
    --response-templates '{"application/json": '"${ESCAPED_JSON}"'}'
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # メソッドレスポンスの設定
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-method-response \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --status-code 200 \
>     --response-models '{"application/json": "Empty"}'
{
    "statusCode": "200",
    "responseModels": {
        "application/json": "Empty"
    }
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # テンプレートの作成
[cloudshell-user@ip-10-134-14-200 ~]$ JSON_TEMPLATE=$(cat << EOF
> {
>     "statusCode": 200,
>     "body": [
>         {
>             "report_id": 5,
>             "report_title" : "Hello, world"
>         },
>         {
>             "report_id": 7,
>             "report_title" : "Good morning!"
>         }
>     ]
> }
> EOF
> ) \
> && echo ${JSON_TEMPLATE}
{ "statusCode": 200, "body": [ { "report_id": 5, "report_title" : "Hello, world" }, { "report_id": 7, "report_title" : "Good morning!" } ] }
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-134-14-200 ~]$ echo ${JSON_TEMPLATE} | python -m json.tool
{
    "statusCode": 200,
    "body": [
        {
            "report_id": 5,
            "report_title": "Hello, world"
        },
        {
            "report_id": 7,
            "report_title": "Good morning!"
        }
    ]
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # JSONをエスケープ形式に変換
[cloudshell-user@ip-10-134-14-200 ~]$ ESCAPED_JSON=$(echo "${JSON_TEMPLATE}" | jq -Rs .)
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-134-14-200 ~]$ echo ${ESCAPED_JSON} | python -m json.tool
"{\n \"statusCode\": 200,\n \"body\": [\n {\n \"report_id\": 5,\n \"report_title\" : \"Hello, world\"\n },\n {\n \"report_id\": 7,\n \"report_title\" : \"Good morning!\"\n }\n ]\n}\n"
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # 統合レスポンスの設定
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-integration-response \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --status-code 200 \
>     --selection-pattern '' \
>     --response-templates '{"application/json": '"${ESCAPED_JSON}"'}'
{
    "statusCode": "200",
    "selectionPattern": "",
    "responseTemplates": {
        "application/json": "{\n    \"statusCode\": 200,\n    \"body\": [\n        {\n            \"report_id\": 5,\n            \"report_title\" : \"Hello, world\"\n        },\n        {\n            \"report_id\": 7,\n            \"report_title\" : \"Good morning!\"\n        }\n    ]\n}\n"
    }
}
```
## Lambda関数のテスト実行
```bash:コマンド
aws apigateway test-invoke-method \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway test-invoke-method \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET
{
    "status": 200,
    "body": "{\n    \"statusCode\": 200,\n    \"body\": [\n        {\n            \"report_id\": 5,\n            \"report_title\" : \"Hello, world\"\n        },\n        {\n            \"report_id\": 7,\n            \"report_title\" : \"Good morning!\"\n        }\n    ]\n}\n",
    "headers": {
        "Content-Type": "application/json"
    },
    "multiValueHeaders": {
        "Content-Type": [
            "application/json"
        ]
    },
    "log": "Execution log for request b93368df-81ee-4d2f-b996-2c6fefb8a6e4\nSun Jul 14 02:56:13 UTC 2024 : Starting execution for request: b93368df-81ee-4d2f-b996-2c6fefb8a6e4\nSun Jul 14 02:56:13 UTC 2024 : HTTP Method: GET, Resource Path: /sample\nSun Jul 14 02:56:13 UTC 2024 : Method request path: {}\nSun Jul 14 02:56:13 UTC 2024 : Method request query string: {}\nSun Jul 14 02:56:13 UTC 2024 : Method request headers: {}\nSun Jul 14 02:56:13 UTC 2024 : Method request body before transformations: \nSun Jul 14 02:56:13 UTC 2024 : Method response body after transformations: {\n    \"statusCode\": 200,\n    \"body\": [\n        {\n            \"report_id\": 5,\n            \"report_title\" : \"Hello, world\"\n        },\n        {\n            \"report_id\": 7,\n            \"report_title\" : \"Good morning!\"\n        }\n    ]\n}\n\nSun Jul 14 02:56:13 UTC 2024 : Method response headers: {Content-Type=application/json}\nSun Jul 14 02:56:13 UTC 2024 : Successfully completed execution\nSun Jul 14 02:56:13 UTC 2024 : Method completed with status: 200\n",
    "latency": 11
}
```
## API Gatewayのデプロイ
```bash:コマンド
# 変数
STAGE_NAME="dev" \
&& echo ${STAGE_NAME}

# デプロイ
aws apigateway create-deployment \
    --rest-api-id ${RESET_API_ID} \
    --stage-name ${STAGE_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # 変数
[cloudshell-user@ip-10-134-14-200 ~]$ STAGE_NAME="dev" \
> && echo ${STAGE_NAME}
dev
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # デプロイ
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway create-deployment \
>     --rest-api-id ${RESET_API_ID} \
>     --stage-name ${STAGE_NAME}
{
    "id": "frmb2a",
    "createdDate": "2024-07-14T03:05:44+00:00"
}
```
# 4. Amazon API Gateway ハンズオン② API Gateway と Lambda を組み合わせる
## API Gateway sampleリソースの削除
```bash:コマンド
# リソースの削除
aws apigateway delete-resource \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway delete-resource \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID}
```
## API Gateway translateリソースの作成
```bash:コマンド
# 変数
RESOURCE_NAME="translate" \
&& echo $RESOURCE_NAME{}

# リソースの作成
aws apigateway create-resource \
    --rest-api-id ${RESET_API_ID} \
    --parent-id ${PARENT_ID} \
    --path-part ${RESOURCE_NAME}

# リソースIDの取得
RESOURCE_ID=$(
    aws apigateway get-resources \
        --rest-api-id ${RESET_API_ID} \
        --query "items[?path=='/${RESOURCE_NAME}'].id" \
        --output text
) \
&& echo ${RESOURCE_ID}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # 変数
[cloudshell-user@ip-10-134-14-200 ~]$ RESOURCE_NAME="translate" \
> && echo $RESOURCE_NAME{}
translate{}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # リソースの作成
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway create-resource \
>     --rest-api-id ${RESET_API_ID} \
>     --parent-id ${PARENT_ID} \
>     --path-part ${RESOURCE_NAME}
{
    "id": "yphb9r",
    "parentId": "okjuj6zkl4",
    "pathPart": "translate",
    "path": "/translate"
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # リソースIDの取得
[cloudshell-user@ip-10-134-14-200 ~]$ RESOURCE_ID=$(
>     aws apigateway get-resources \
>         --rest-api-id ${RESET_API_ID} \
>         --query "items[?path=='/${RESOURCE_NAME}'].id" \
>         --output text
> ) \
> && echo ${RESOURCE_ID}
yphb9r
```
## API Gateway GETメソッドの作成
```bash:コマンド
# GETメソッドの作成
aws apigateway put-method \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --authorization-type NONE \
    --request-parameters "method.request.querystring.input_text=true"
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # GETメソッドの作成
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-method \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --authorization-type NONE \
>     --request-parameters "method.request.querystring.input_text=true"
{
    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false,
    "requestParameters": {
        "method.request.querystring.input_text": true
    }
}
```
## API Gateway Lambdaプロキシ統合の設定
```bash:コマンド
# Lambdaプロキシ統合の設定
aws apigateway put-integration \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --type AWS_PROXY \
    --integration-http-method POST \
    --uri "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/${FUNCTION_ARN}/invocations" \
    --passthrough-behavior WHEN_NO_MATCH \
    --content-handling CONVERT_TO_TEXT
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # Lambdaプロキシ統合の設定
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-integration \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --type AWS_PROXY \
>     --integration-http-method POST \
>     --uri "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/${FUNCTION_ARN}/invocations" \
>     --passthrough-behavior WHEN_NO_MATCH \
>     --content-handling CONVERT_TO_TEXT
{
    "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:translate-function/invocations",
    "passthroughBehavior": "WHEN_NO_MATCH",
    "contentHandling": "CONVERT_TO_TEXT",
    "timeoutInMillis": 29000,
    "cacheNamespace": "yphb9r",
    "cacheKeyParameters": []
}
```
## API Gateway レスポンスの設定
```bash:コマンド
# メソッドレスポンスの設定
aws apigateway put-method-response \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --status-code 200 \
    --response-models '{"application/json": "Empty"}' 

# 統合レスポンスの設定
aws apigateway put-integration-response \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --status-code 200 \
    --selection-pattern '' \
    --response-templates '{"application/json": ""}'
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-method-response \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --status-code 200 \
>     --response-models '{"application/json": "Empty"}' 
{
    "statusCode": "200",
    "responseModels": {
        "application/json": "Empty"
    }
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # 統合レスポンスの設定
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway put-integration-response \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID} \
>     --http-method GET \
>     --status-code 200 \
>     --selection-pattern '' \
>     --response-templates '{"application/json": ""}'
{
    "statusCode": "200",
    "selectionPattern": "",
    "responseTemplates": {
        "application/json": null
    }
}
```
## Lambda用ソースコードの更新
```bash:コマンド
# ソースコードの更新
cat << EOF > lambda_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

# デプロイパッケージの作成
zip function.zip lambda_function.py

# Lambda関数の更新
aws lambda update-function-code \
  --function-name ${FUNCTION_NAME}  \
  --zip-file fileb://function.zip
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # ソースコードの更新
[cloudshell-user@ip-10-134-14-200 ~]$ cat << EOF > lambda_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
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # デプロイパッケージの作成
[cloudshell-user@ip-10-134-14-200 ~]$ zip function.zip lambda_function.py
updating: lambda_function.py (deflated 46%)
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # Lambda関数の更新
[cloudshell-user@ip-10-134-14-200 ~]$ aws lambda update-function-code \
>   --function-name ${FUNCTION_NAME}  \
>   --zip-file fileb://function.zip
{
    "FunctionName": "translate-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function",
    "Runtime": "python3.8",
    "Role": "arn:aws:iam::999999999999:role/translate-function-role",
    "Handler": "lambda_function.lambda_handler",
    "CodeSize": 480,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 256,
    "LastModified": "2024-07-14T05:40:01.000+0000",
    "CodeSha256": "vobstg6e0kd9a1FLK3FUZd6RIolhXQ+yk9EtzgR9kNU=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "3ff59c3c-fe71-4689-b901-8060cd4ccd02",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbcd583856654db68283"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/translate-function"
    }
}
```
## Lambda関数のテスト実行
```bash:コマンド
# イベントの作成
EVENT=$(cat << EOF
{
  "body": "eyJ0ZXN0IjoiYm9keSJ9",
  "resource": "/{proxy+}",
  "path": "/path/to/resource",
  "httpMethod": "POST",
  "isBase64Encoded": true,
  "queryStringParameters": {
    "input_text": "こんにちは"
  },
  "multiValueQueryStringParameters": {
    "foo": [
      "bar"
    ]
  },
  "pathParameters": {
    "proxy": "/path/to/resource"
  },
  "stageVariables": {
    "baz": "qux"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Custom User Agent String",
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "Accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
    ],
    "Accept-Encoding": [
      "gzip, deflate, sdch"
    ],
    "Accept-Language": [
      "en-US,en;q=0.8"
    ],
    "Cache-Control": [
      "max-age=0"
    ],
    "CloudFront-Forwarded-Proto": [
      "https"
    ],
    "CloudFront-Is-Desktop-Viewer": [
      "true"
    ],
    "CloudFront-Is-Mobile-Viewer": [
      "false"
    ],
    "CloudFront-Is-SmartTV-Viewer": [
      "false"
    ],
    "CloudFront-Is-Tablet-Viewer": [
      "false"
    ],
    "CloudFront-Viewer-Country": [
      "US"
    ],
    "Host": [
      "0123456789.execute-api.us-east-1.amazonaws.com"
    ],
    "Upgrade-Insecure-Requests": [
      "1"
    ],
    "User-Agent": [
      "Custom User Agent String"
    ],
    "Via": [
      "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
    ],
    "X-Amz-Cf-Id": [
      "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
    ],
    "X-Forwarded-For": [
      "127.0.0.1, 127.0.0.2"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ]
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "123456",
    "stage": "prod",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "requestTime": "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch": 1428582896000,
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "accessKey": null,
      "sourceIp": "127.0.0.1",
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Custom User Agent String",
      "user": null
    },
    "path": "/prod/path/to/resource",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "apiId": "1234567890",
    "protocol": "HTTP/1.1"
  }
}
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:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # イベントの作成
[cloudshell-user@ip-10-134-14-200 ~]$ EVENT=$(cat << EOF
> {
>   "body": "eyJ0ZXN0IjoiYm9keSJ9",
>   "resource": "/{proxy+}",
>   "path": "/path/to/resource",
>   "httpMethod": "POST",
>   "isBase64Encoded": true,
>   "queryStringParameters": {
>     "input_text": "こんにちは"
>   },
>   "multiValueQueryStringParameters": {
>     "foo": [
>       "bar"
>     ]
>   },
>   "pathParameters": {
>     "proxy": "/path/to/resource"
>   },
>   "stageVariables": {
>     "baz": "qux"
>   },
>   "headers": {
>     "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
>     "Accept-Encoding": "gzip, deflate, sdch",
>     "Accept-Language": "en-US,en;q=0.8",
>     "Cache-Control": "max-age=0",
>     "CloudFront-Forwarded-Proto": "https",
>     "CloudFront-Is-Desktop-Viewer": "true",
>     "CloudFront-Is-Mobile-Viewer": "false",
>     "CloudFront-Is-SmartTV-Viewer": "false",
>     "CloudFront-Is-Tablet-Viewer": "false",
>     "CloudFront-Viewer-Country": "US",
>     "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
>     "Upgrade-Insecure-Requests": "1",
>     "User-Agent": "Custom User Agent String",
>     "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
>     "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
>     "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
>     "X-Forwarded-Port": "443",
>     "X-Forwarded-Proto": "https"
>   },
>   "multiValueHeaders": {
>     "Accept": [
>       "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
>     ],
>     "Accept-Encoding": [
>       "gzip, deflate, sdch"
>     ],
>     "Accept-Language": [
>       "en-US,en;q=0.8"
>     ],
>     "Cache-Control": [
>       "max-age=0"
>     ],
>     "CloudFront-Forwarded-Proto": [
>       "https"
>     ],
>     "CloudFront-Is-Desktop-Viewer": [
>       "true"
>     ],
>     "CloudFront-Is-Mobile-Viewer": [
>       "false"
>     ],
>     "CloudFront-Is-SmartTV-Viewer": [
>       "false"
>     ],
>     "CloudFront-Is-Tablet-Viewer": [
>       "false"
>     ],
>     "CloudFront-Viewer-Country": [
>       "US"
>     ],
>     "Host": [
>       "0123456789.execute-api.us-east-1.amazonaws.com"
>     ],
>     "Upgrade-Insecure-Requests": [
>       "1"
>     ],
>     "User-Agent": [
>       "Custom User Agent String"
>     ],
>     "Via": [
>       "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
>     ],
>     "X-Amz-Cf-Id": [
>       "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
>     ],
>     "X-Forwarded-For": [
>       "127.0.0.1, 127.0.0.2"
>     ],
>     "X-Forwarded-Port": [
>       "443"
>     ],
>     "X-Forwarded-Proto": [
>       "https"
>     ]
>   },
>   "requestContext": {
>     "accountId": "123456789012",
>     "resourceId": "123456",
>     "stage": "prod",
>     "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
>     "requestTime": "09/Apr/2015:12:34:56 +0000",
>     "requestTimeEpoch": 1428582896000,
>     "identity": {
>       "cognitoIdentityPoolId": null,
>       "accountId": null,
>       "cognitoIdentityId": null,
>       "caller": null,
>       "accessKey": null,
>       "sourceIp": "127.0.0.1",
>       "cognitoAuthenticationType": null,
>       "cognitoAuthenticationProvider": null,
>       "userArn": null,
>       "userAgent": "Custom User Agent String",
>       "user": null
>     },
>     "path": "/prod/path/to/resource",
>     "resourcePath": "/{proxy+}",
>     "httpMethod": "POST",
>     "apiId": "1234567890",
>     "protocol": "HTTP/1.1"
>   }
> }
> EOF
> ) \
> && echo ${EVENT}
{ "body": "eyJ0ZXN0IjoiYm9keSJ9", "resource": "/{proxy+}", "path": "/path/to/resource", "httpMethod": "POST", "isBase64Encoded": true, "queryStringParameters": { "input_text": "こんにちは" }, "multiValueQueryStringParameters": { "foo": [ "bar" ] }, "pathParameters": { "proxy": "/path/to/resource" }, "stageVariables": { "baz": "qux" }, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, sdch", "Accept-Language": "en-US,en;q=0.8", "Cache-Control": "max-age=0", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-Country": "US", "Host": "1234567890.execute-api.us-east-1.amazonaws.com", "Upgrade-Insecure-Requests": "1", "User-Agent": "Custom User Agent String", "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", "X-Forwarded-For": "127.0.0.1, 127.0.0.2", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "Accept": [ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" ], "Accept-Encoding": [ "gzip, deflate, sdch" ], "Accept-Language": [ "en-US,en;q=0.8" ], "Cache-Control": [ "max-age=0" ], "CloudFront-Forwarded-Proto": [ "https" ], "CloudFront-Is-Desktop-Viewer": [ "true" ], "CloudFront-Is-Mobile-Viewer": [ "false" ], "CloudFront-Is-SmartTV-Viewer": [ "false" ], "CloudFront-Is-Tablet-Viewer": [ "false" ], "CloudFront-Viewer-Country": [ "US" ], "Host": [ "0123456789.execute-api.us-east-1.amazonaws.com" ], "Upgrade-Insecure-Requests": [ "1" ], "User-Agent": [ "Custom User Agent String" ], "Via": [ "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)" ], "X-Amz-Cf-Id": [ "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==" ], "X-Forwarded-For": [ "127.0.0.1, 127.0.0.2" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "requestContext": { "accountId": "123456789012", "resourceId": "123456", "stage": "prod", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "requestTime": "09/Apr/2015:12:34:56 +0000", "requestTimeEpoch": 1428582896000, "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "accessKey": null, "sourceIp": "127.0.0.1", "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "Custom User Agent String", "user": null }, "path": "/prod/path/to/resource", "resourcePath": "/{proxy+}", "httpMethod": "POST", "apiId": "1234567890", "protocol": "HTTP/1.1" } }
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-134-14-200 ~]$ echo ${EVENT} | python -m json.tool
{
    "body": "eyJ0ZXN0IjoiYm9keSJ9",
    "resource": "/{proxy+}",
    "path": "/path/to/resource",
    "httpMethod": "POST",
    "isBase64Encoded": true,
    "queryStringParameters": {
        "input_text": "\u3053\u3093\u306b\u3061\u306f"
    },
    "multiValueQueryStringParameters": {
        "foo": [
            "bar"
        ]
    },
    "pathParameters": {
        "proxy": "/path/to/resource"
    },
    "stageVariables": {
        "baz": "qux"
    },
    "headers": {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, sdch",
        "Accept-Language": "en-US,en;q=0.8",
        "Cache-Control": "max-age=0",
        "CloudFront-Forwarded-Proto": "https",
        "CloudFront-Is-Desktop-Viewer": "true",
        "CloudFront-Is-Mobile-Viewer": "false",
        "CloudFront-Is-SmartTV-Viewer": "false",
        "CloudFront-Is-Tablet-Viewer": "false",
        "CloudFront-Viewer-Country": "US",
        "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Custom User Agent String",
        "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
        "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
        "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "Accept": [
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
        ],
        "Accept-Encoding": [
            "gzip, deflate, sdch"
        ],
        "Accept-Language": [
            "en-US,en;q=0.8"
        ],
        "Cache-Control": [
            "max-age=0"
        ],
        "CloudFront-Forwarded-Proto": [
            "https"
        ],
        "CloudFront-Is-Desktop-Viewer": [
            "true"
        ],
        "CloudFront-Is-Mobile-Viewer": [
            "false"
        ],
        "CloudFront-Is-SmartTV-Viewer": [
            "false"
        ],
        "CloudFront-Is-Tablet-Viewer": [
            "false"
        ],
        "CloudFront-Viewer-Country": [
            "US"
        ],
        "Host": [
            "0123456789.execute-api.us-east-1.amazonaws.com"
        ],
        "Upgrade-Insecure-Requests": [
            "1"
        ],
        "User-Agent": [
            "Custom User Agent String"
        ],
        "Via": [
            "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
        ],
        "X-Amz-Cf-Id": [
            "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
        ],
        "X-Forwarded-For": [
            "127.0.0.1, 127.0.0.2"
        ],
        "X-Forwarded-Port": [
            "443"
        ],
        "X-Forwarded-Proto": [
            "https"
        ]
    },
    "requestContext": {
        "accountId": "123456789012",
        "resourceId": "123456",
        "stage": "prod",
        "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
        "requestTime": "09/Apr/2015:12:34:56 +0000",
        "requestTimeEpoch": 1428582896000,
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "accessKey": null,
            "sourceIp": "127.0.0.1",
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "Custom User Agent String",
            "user": null
        },
        "path": "/prod/path/to/resource",
        "resourcePath": "/{proxy+}",
        "httpMethod": "POST",
        "apiId": "1234567890",
        "protocol": "HTTP/1.1"
    }
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # テスト実行
[cloudshell-user@ip-10-134-14-200 ~]$ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ cat outputfile.txt
{"statusCode": 200, "body": "{\"output_text\": \"Hi\"}", "isBase64Encoded": false, "headers": {}}
```
## API Gatewayのデプロイ
```bash:コマンド
# デプロイ
aws apigateway create-deployment \
    --rest-api-id ${RESET_API_ID} \
    --stage-name ${STAGE_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # デプロイ
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway create-deployment \
>     --rest-api-id ${RESET_API_ID} \
>     --stage-name ${STAGE_NAME}
{
    "id": "91sxdk",
    "createdDate": "2024-07-14T08:52:50+00:00"
}
```
# 5. Amazon DynamoDB ハンズオン① テーブルを作ってみる
## Amazon DynamoDBテーブル作成
```bash:コマンド
# 変数
TABLE_NAME="translate-history" \
&& echo ${TABLE_NAME}

# プライマリキー名
ATTRIBUTE_NAME="timestamp" \
&& echo ${ATTRIBUTE_NAME}

# 読み込みキャパシティーユニット：1
READCAPACITYUNITS=1

# 書き込みキャパシティーユニット：1
WRITECAPACITYUNITS=1

# テーブル作成
aws dynamodb create-table \
    --table-name ${TABLE_NAME} \
    --attribute-definitions AttributeName=timestamp,AttributeType=S \
    --key-schema AttributeName=${ATTRIBUTE_NAME},KeyType=HASH \
    --provisioned-throughput ReadCapacityUnits=${READCAPACITYUNITS},WriteCapacityUnits=${WRITECAPACITYUNITS}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # 変数
[cloudshell-user@ip-10-134-14-200 ~]$ TABLE_NAME="translate-history" \
> && echo ${TABLE_NAME}
translate-history
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # プライマリキー名
[cloudshell-user@ip-10-134-14-200 ~]$ ATTRIBUTE_NAME="timestamp" \
> && echo ${ATTRIBUTE_NAME}
timestamp
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # 読み込みキャパシティーユニット：1
[cloudshell-user@ip-10-134-14-200 ~]$ READCAPACITYUNITS=1
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # 書き込みキャパシティーユニット：1
[cloudshell-user@ip-10-134-14-200 ~]$ WRITECAPACITYUNITS=1
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # テーブル作成
[cloudshell-user@ip-10-134-14-200 ~]$ aws dynamodb create-table \
>     --table-name ${TABLE_NAME} \
>     --attribute-definitions AttributeName=timestamp,AttributeType=S \
>     --key-schema AttributeName=${ATTRIBUTE_NAME},KeyType=HASH \
>     --provisioned-throughput ReadCapacityUnits=${READCAPACITYUNITS},WriteCapacityUnits=${WRITECAPACITYUNITS}
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "timestamp",
                "AttributeType": "S"
            }
        ],
        "TableName": "translate-history",
        "KeySchema": [
            {
                "AttributeName": "timestamp",
                "KeyType": "HASH"
            }
        ],
        "TableStatus": "CREATING",
        "CreationDateTime": "2024-07-14T09:40:46.901000+00:00",
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 1,
            "WriteCapacityUnits": 1
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:ap-northeast-1:999999999999:table/translate-history",
        "TableId": "28329b22-e74a-41ee-9e57-584592a726c9",
        "DeletionProtectionEnabled": false
    }
}
```
## Amazon DynamoDBテーブル内項目の作成
```bash:コマンド
# 項目作成
aws dynamodb put-item \
    --table-name ${TABLE_NAME} \
    --item '{
        "timestamp": {"S": "20191024183000"},
        "input_text": {"S": "★こんにちは"},
        "output_text": {"S": "★Hello"}
    }'
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # 項目作成
[cloudshell-user@ip-10-134-14-200 ~]$ aws dynamodb put-item \
>     --table-name ${TABLE_NAME} \
>     --item '{
>         "timestamp": {"S": "20191024183000"},
>         "input_text": {"S": "★こんにちは"},
>         "output_text": {"S": "★Hello"}
>     }'
```
# 6. Amazon DynamoDB ハンズオン② API Gateway と Lambda と DynamoDB を組み合わせる
## Lamdba用ソースコードの更新
```bash:コマンド
# ソースコードの更新
cat << EOF > lambda_function.py
import json
import boto3
import datetime

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

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

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_text": input_text,
        "output_text": output_text
      }
    )

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

# デプロイパッケージの作成
zip function.zip lambda_function.py

# Lambda関数の更新
aws lambda update-function-code \
  --function-name ${FUNCTION_NAME}  \
  --zip-file fileb://function.zip
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ cat << EOF > lambda_function.py
> import json
> import boto3
> import datetime
> 
> translate = boto3.client(service_name='translate')
> 
> dynamodb_translate_history_tbl = boto3.resource('dynamodb').Table('translate-history')
> 
> 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_text": input_text,
>         "output_text": output_text
>       }
>     )
> 
>     return {
>         'statusCode': 200,
>         'body': json.dumps({
>             'output_text': output_text
>         }),
>         'isBase64Encoded': False,
>         'headers': {}
>     }
> EOF
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # デプロイパッケージの作成
[cloudshell-user@ip-10-134-14-200 ~]$ zip function.zip lambda_function.py
updating: lambda_function.py (deflated 53%)
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # Lambda関数の更新
[cloudshell-user@ip-10-134-14-200 ~]$ aws lambda update-function-code \
>   --function-name ${FUNCTION_NAME}  \
>   --zip-file fileb://function.zip
{
    "FunctionName": "translate-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:999999999999:function:translate-function",
    "Runtime": "python3.8",
    "Role": "arn:aws:iam::999999999999:role/translate-function-role",
    "Handler": "lambda_function.lambda_handler",
    "CodeSize": 595,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 256,
    "LastModified": "2024-07-14T10:38:17.000+0000",
    "CodeSha256": "MDk/lPJX18SMSIw5HOhSmOzZVNHkuZIjCNTsAtp5KPg=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "8d78d184-ce09-4a0f-bb65-cf47a18b41dd",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a768321bd39c88f083afe0a045064adc3615105a319dbbcd583856654db68283"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/translate-function"
    }
}
```
## IAMロールを修正
```bash:コマンド
# ポリシー名
ATTACH_POLICY_NAME="AmazonDynamoDBFullAccess" \
&& echo ${ATTACH_POLICY_NAME}

# IAMロールにポリシーをアタッチ
aws iam attach-role-policy \
  --role-name ${ROLE_NAME} \
  --policy-arn arn:aws:iam::aws:policy/${ATTACH_POLICY_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # ポリシー名
[cloudshell-user@ip-10-134-14-200 ~]$ ATTACH_POLICY_NAME="AmazonDynamoDBFullAccess" \
> && echo ${ATTACH_POLICY_NAME}
AmazonDynamoDBFullAccess
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # IAMロールにポリシーをアタッチ
[cloudshell-user@ip-10-134-14-200 ~]$ aws iam attach-role-policy \
>   --role-name ${ROLE_NAME} \
>   --policy-arn arn:aws:iam::aws:policy/${ATTACH_POLICY_NAME}
```
## Lambda関数のテスト実行
```bash:コマンド
# イベントの作成
EVENT=$(cat << EOF
{
  "body": "eyJ0ZXN0IjoiYm9keSJ9",
  "resource": "/{proxy+}",
  "path": "/path/to/resource",
  "httpMethod": "POST",
  "isBase64Encoded": true,
  "queryStringParameters": {
    "input_text": "こんばんは"
  },
  "multiValueQueryStringParameters": {
    "foo": [
      "bar"
    ]
  },
  "pathParameters": {
    "proxy": "/path/to/resource"
  },
  "stageVariables": {
    "baz": "qux"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Custom User Agent String",
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "Accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
    ],
    "Accept-Encoding": [
      "gzip, deflate, sdch"
    ],
    "Accept-Language": [
      "en-US,en;q=0.8"
    ],
    "Cache-Control": [
      "max-age=0"
    ],
    "CloudFront-Forwarded-Proto": [
      "https"
    ],
    "CloudFront-Is-Desktop-Viewer": [
      "true"
    ],
    "CloudFront-Is-Mobile-Viewer": [
      "false"
    ],
    "CloudFront-Is-SmartTV-Viewer": [
      "false"
    ],
    "CloudFront-Is-Tablet-Viewer": [
      "false"
    ],
    "CloudFront-Viewer-Country": [
      "US"
    ],
    "Host": [
      "0123456789.execute-api.us-east-1.amazonaws.com"
    ],
    "Upgrade-Insecure-Requests": [
      "1"
    ],
    "User-Agent": [
      "Custom User Agent String"
    ],
    "Via": [
      "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
    ],
    "X-Amz-Cf-Id": [
      "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
    ],
    "X-Forwarded-For": [
      "127.0.0.1, 127.0.0.2"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ]
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "123456",
    "stage": "prod",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "requestTime": "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch": 1428582896000,
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "accessKey": null,
      "sourceIp": "127.0.0.1",
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Custom User Agent String",
      "user": null
    },
    "path": "/prod/path/to/resource",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "apiId": "1234567890",
    "protocol": "HTTP/1.1"
  }
}
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:出力
[cloudshell-user@ip-10-134-14-200 ~]$ # イベントの作成
[cloudshell-user@ip-10-134-14-200 ~]$ EVENT=$(cat << EOF
> {
>   "body": "eyJ0ZXN0IjoiYm9keSJ9",
>   "resource": "/{proxy+}",
>   "path": "/path/to/resource",
>   "httpMethod": "POST",
>   "isBase64Encoded": true,
>   "queryStringParameters": {
>     "input_text": "こんばんは"
>   },
>   "multiValueQueryStringParameters": {
>     "foo": [
>       "bar"
>     ]
>   },
>   "pathParameters": {
>     "proxy": "/path/to/resource"
>   },
>   "stageVariables": {
>     "baz": "qux"
>   },
>   "headers": {
>     "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
>     "Accept-Encoding": "gzip, deflate, sdch",
>     "Accept-Language": "en-US,en;q=0.8",
>     "Cache-Control": "max-age=0",
>     "CloudFront-Forwarded-Proto": "https",
>     "CloudFront-Is-Desktop-Viewer": "true",
>     "CloudFront-Is-Mobile-Viewer": "false",
>     "CloudFront-Is-SmartTV-Viewer": "false",
>     "CloudFront-Is-Tablet-Viewer": "false",
>     "CloudFront-Viewer-Country": "US",
>     "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
>     "Upgrade-Insecure-Requests": "1",
>     "User-Agent": "Custom User Agent String",
>     "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
>     "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
>     "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
>     "X-Forwarded-Port": "443",
>     "X-Forwarded-Proto": "https"
>   },
>   "multiValueHeaders": {
>     "Accept": [
>       "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
>     ],
>     "Accept-Encoding": [
>       "gzip, deflate, sdch"
>     ],
>     "Accept-Language": [
>       "en-US,en;q=0.8"
>     ],
>     "Cache-Control": [
>       "max-age=0"
>     ],
>     "CloudFront-Forwarded-Proto": [
>       "https"
>     ],
>     "CloudFront-Is-Desktop-Viewer": [
>       "true"
>     ],
>     "CloudFront-Is-Mobile-Viewer": [
>       "false"
>     ],
>     "CloudFront-Is-SmartTV-Viewer": [
>       "false"
>     ],
>     "CloudFront-Is-Tablet-Viewer": [
>       "false"
>     ],
>     "CloudFront-Viewer-Country": [
>       "US"
>     ],
>     "Host": [
>       "0123456789.execute-api.us-east-1.amazonaws.com"
>     ],
>     "Upgrade-Insecure-Requests": [
>       "1"
>     ],
>     "User-Agent": [
>       "Custom User Agent String"
>     ],
>     "Via": [
>       "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
>     ],
>     "X-Amz-Cf-Id": [
>       "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
>     ],
>     "X-Forwarded-For": [
>       "127.0.0.1, 127.0.0.2"
>     ],
>     "X-Forwarded-Port": [
>       "443"
>     ],
>     "X-Forwarded-Proto": [
>       "https"
>     ]
>   },
>   "requestContext": {
>     "accountId": "123456789012",
>     "resourceId": "123456",
>     "stage": "prod",
>     "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
>     "requestTime": "09/Apr/2015:12:34:56 +0000",
>     "requestTimeEpoch": 1428582896000,
>     "identity": {
>       "cognitoIdentityPoolId": null,
>       "accountId": null,
>       "cognitoIdentityId": null,
>       "caller": null,
>       "accessKey": null,
>       "sourceIp": "127.0.0.1",
>       "cognitoAuthenticationType": null,
>       "cognitoAuthenticationProvider": null,
>       "userArn": null,
>       "userAgent": "Custom User Agent String",
>       "user": null
>     },
>     "path": "/prod/path/to/resource",
>     "resourcePath": "/{proxy+}",
>     "httpMethod": "POST",
>     "apiId": "1234567890",
>     "protocol": "HTTP/1.1"
>   }
> }
> EOF
> ) \
> && echo ${EVENT}
{ "body": "eyJ0ZXN0IjoiYm9keSJ9", "resource": "/{proxy+}", "path": "/path/to/resource", "httpMethod": "POST", "isBase64Encoded": true, "queryStringParameters": { "input_text": "こんばんは" }, "multiValueQueryStringParameters": { "foo": [ "bar" ] }, "pathParameters": { "proxy": "/path/to/resource" }, "stageVariables": { "baz": "qux" }, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, sdch", "Accept-Language": "en-US,en;q=0.8", "Cache-Control": "max-age=0", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-Country": "US", "Host": "1234567890.execute-api.us-east-1.amazonaws.com", "Upgrade-Insecure-Requests": "1", "User-Agent": "Custom User Agent String", "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", "X-Forwarded-For": "127.0.0.1, 127.0.0.2", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "Accept": [ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" ], "Accept-Encoding": [ "gzip, deflate, sdch" ], "Accept-Language": [ "en-US,en;q=0.8" ], "Cache-Control": [ "max-age=0" ], "CloudFront-Forwarded-Proto": [ "https" ], "CloudFront-Is-Desktop-Viewer": [ "true" ], "CloudFront-Is-Mobile-Viewer": [ "false" ], "CloudFront-Is-SmartTV-Viewer": [ "false" ], "CloudFront-Is-Tablet-Viewer": [ "false" ], "CloudFront-Viewer-Country": [ "US" ], "Host": [ "0123456789.execute-api.us-east-1.amazonaws.com" ], "Upgrade-Insecure-Requests": [ "1" ], "User-Agent": [ "Custom User Agent String" ], "Via": [ "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)" ], "X-Amz-Cf-Id": [ "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==" ], "X-Forwarded-For": [ "127.0.0.1, 127.0.0.2" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "requestContext": { "accountId": "123456789012", "resourceId": "123456", "stage": "prod", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "requestTime": "09/Apr/2015:12:34:56 +0000", "requestTimeEpoch": 1428582896000, "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "accessKey": null, "sourceIp": "127.0.0.1", "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "Custom User Agent String", "user": null }, "path": "/prod/path/to/resource", "resourcePath": "/{proxy+}", "httpMethod": "POST", "apiId": "1234567890", "protocol": "HTTP/1.1" } }
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # JSONフォーマットの確認
[cloudshell-user@ip-10-134-14-200 ~]$ echo ${EVENT} | python -m json.tool
{
    "body": "eyJ0ZXN0IjoiYm9keSJ9",
    "resource": "/{proxy+}",
    "path": "/path/to/resource",
    "httpMethod": "POST",
    "isBase64Encoded": true,
    "queryStringParameters": {
        "input_text": "\u3053\u3093\u3070\u3093\u306f"
    },
    "multiValueQueryStringParameters": {
        "foo": [
            "bar"
        ]
    },
    "pathParameters": {
        "proxy": "/path/to/resource"
    },
    "stageVariables": {
        "baz": "qux"
    },
    "headers": {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, sdch",
        "Accept-Language": "en-US,en;q=0.8",
        "Cache-Control": "max-age=0",
        "CloudFront-Forwarded-Proto": "https",
        "CloudFront-Is-Desktop-Viewer": "true",
        "CloudFront-Is-Mobile-Viewer": "false",
        "CloudFront-Is-SmartTV-Viewer": "false",
        "CloudFront-Is-Tablet-Viewer": "false",
        "CloudFront-Viewer-Country": "US",
        "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Custom User Agent String",
        "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
        "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
        "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "Accept": [
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
        ],
        "Accept-Encoding": [
            "gzip, deflate, sdch"
        ],
        "Accept-Language": [
            "en-US,en;q=0.8"
        ],
        "Cache-Control": [
            "max-age=0"
        ],
        "CloudFront-Forwarded-Proto": [
            "https"
        ],
        "CloudFront-Is-Desktop-Viewer": [
            "true"
        ],
        "CloudFront-Is-Mobile-Viewer": [
            "false"
        ],
        "CloudFront-Is-SmartTV-Viewer": [
            "false"
        ],
        "CloudFront-Is-Tablet-Viewer": [
            "false"
        ],
        "CloudFront-Viewer-Country": [
            "US"
        ],
        "Host": [
            "0123456789.execute-api.us-east-1.amazonaws.com"
        ],
        "Upgrade-Insecure-Requests": [
            "1"
        ],
        "User-Agent": [
            "Custom User Agent String"
        ],
        "Via": [
            "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
        ],
        "X-Amz-Cf-Id": [
            "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
        ],
        "X-Forwarded-For": [
            "127.0.0.1, 127.0.0.2"
        ],
        "X-Forwarded-Port": [
            "443"
        ],
        "X-Forwarded-Proto": [
            "https"
        ]
    },
    "requestContext": {
        "accountId": "123456789012",
        "resourceId": "123456",
        "stage": "prod",
        "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
        "requestTime": "09/Apr/2015:12:34:56 +0000",
        "requestTimeEpoch": 1428582896000,
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "accessKey": null,
            "sourceIp": "127.0.0.1",
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "Custom User Agent String",
            "user": null
        },
        "path": "/prod/path/to/resource",
        "resourcePath": "/{proxy+}",
        "httpMethod": "POST",
        "apiId": "1234567890",
        "protocol": "HTTP/1.1"
    }
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ # テスト実行
[cloudshell-user@ip-10-134-14-200 ~]$ aws lambda invoke \
>   --function-name ${FUNCTION_NAME} \
>   --payload `echo ${EVENT} | base64 -w 0` \
>   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
[cloudshell-user@ip-10-134-14-200 ~]$ 
[cloudshell-user@ip-10-134-14-200 ~]$ cat outputfile.txt
{"statusCode": 200, "body": "{\"output_text\": \"Good evening\"}", "isBase64Encoded": false, "headers": {}}
```
## DynamoDBテーブル内の項目の確認
```bash:コマンド
aws dynamodb scan \
    --table-name translate-history
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws dynamodb scan \
>     --table-name translate-history
{
    "Items": [
        {
            "input_text": {
                "S": "★こんにちは"
            },
            "output_text": {
                "S": "★Hello"
            },
            "timestamp": {
                "S": "20191024183000"
            }
        },
        {
            "input_text": {
                "S": "こんばんは"
            },
            "output_text": {
                "S": "Good evening"
            },
            "timestamp": {
                "S": "20240714104707"
            }
        }
    ],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null
}
```
## DynamoDBテーブル内の項目の削除
```bash:コマンド
aws dynamodb delete-item \
    --table-name ${TABLE_NAME} \
    --key '{"timestamp": {"S": "20191024183000"}}'
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws dynamodb delete-item \
>     --table-name ${TABLE_NAME} \
>     --key '{"timestamp": {"S": "20191024183000"}}'
```
## DynamoDBテーブル内の項目の確認
```bash:コマンド
aws dynamodb scan \
    --table-name translate-history
```
```bash:出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws dynamodb scan \
>     --table-name translate-history
{
    "Items": [
        {
            "input_text": {
                "S": "ハンズオン完走しました"
            },
            "output_text": {
                "S": "I finished the hands-on race"
            },
            "timestamp": {
                "S": "20240714105931"
            }
        },
        {
            "input_text": {
                "S": "こんばんは"
            },
            "output_text": {
                "S": "Good evening"
            },
            "timestamp": {
                "S": "20240714104707"
            }
        }
    ],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null
}
```
# 7. リソース削除
:::note warn
ハンズオン内ではリソースの削除は含まれていなかったが、ハンズオン後は削除を実施
:::
## DnyamoDB削除
```bash:コマンド
aws dynamodb delete-table \
    --table-name ${TABLE_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws dynamodb delete-table \
>     --table-name ${TABLE_NAME}
{
    "TableDescription": {
        "TableName": "translate-history",
        "TableStatus": "DELETING",
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 1,
            "WriteCapacityUnits": 1
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:ap-northeast-1:999999999999:table/translate-history",
        "TableId": "28329b22-e74a-41ee-9e57-584592a726c9",
        "DeletionProtectionEnabled": false
    }
}
```
## API Gateway削除
```bash:コマンド
aws apigateway delete-rest-api \
    --rest-api-id ${RESET_API_ID}
```
```bash:出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws apigateway delete-rest-api \
>     --rest-api-id ${RESET_API_ID}
```
## Lambda関数の削除
```bash:コマンド
aws lambda delete-function \
    --function-name ${FUNCTION_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws lambda delete-function \
>     --function-name ${FUNCTION_NAME}
```
## IAMロールの削除
### ポリシーのデタッチ
:::note alert
AWS CLIでロールを削除する場合、最初にすべてのポリシーをデタッチする必要がある
:::
```bash:コマンド
# ロールにアタッチされているポリシーをリスト
POLICIES=$(
    aws iam list-attached-role-policies \
        --role-name $ROLE_NAME \
        --query 'AttachedPolicies[*].PolicyArn' \
        --output text
) \
&& echo ${POLICIES}

# リスト内のポリシーをデタッチする
for POLICY in $POLICIES; do
    aws iam detach-role-policy \
        --role-name ${ROLE_NAME} \
        --policy-arn ${POLICY}
done
```
```bash:出力
[cloudshell-user@ip-10-134-26-238 ~]$ # ロールにアタッチされているポリシーをリスト
[cloudshell-user@ip-10-134-26-238 ~]$ POLICIES=$(
>     aws iam list-attached-role-policies \
>         --role-name $ROLE_NAME \
>         --query 'AttachedPolicies[*].PolicyArn' \
>         --output text
> ) \
> && echo ${POLICIES}
arn:aws:iam::999999999999:policy/translate-function-policy arn:aws:iam::aws:policy/TranslateFullAccess arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
[cloudshell-user@ip-10-134-26-238 ~]$ 
[cloudshell-user@ip-10-134-26-238 ~]$ # リスト内のポリシーをデタッチする
[cloudshell-user@ip-10-134-26-238 ~]$ for POLICY in $POLICIES; do
>     aws iam detach-role-policy \
>         --role-name ${ROLE_NAME} \
>         --policy-arn ${POLICY}
> done
```
### IAMロールの削除
```bash:コマンド
# IAMロールの削除
aws iam delete-role \
    --role-name ${ROLE_NAME}
```
```bash:出力
[cloudshell-user@ip-10-134-26-238 ~]$ # IAMロールの削除
[cloudshell-user@ip-10-134-26-238 ~]$ aws iam delete-role \
>     --role-name ${ROLE_NAME}
```
## IAMポリシーの削除
```bash:コマンド
# IAMポリシーの削除
aws iam delete-policy \
    --policy-arn ${POLICY_ARN}
```
```bash:出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws iam delete-policy \
>     --policy-arn ${POLICY_ARN}
```