0
0

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

Last updated at Posted at 2024-07-14

上記、「AWS Hands-on for Beginners Amazon EC2 Auto Scaling スケーリング基礎編」 をAWS CLIでやってみる
image.png
ハンズオンから引用

ハンズオンではPython3.7を使用していたが、サポート修了のためPython3.8を使用

1. AWS Lambda ハンズオン① Lambda を単体で使ってみる

関数設定

コマンド
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}
出力
[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ポリシー作成

コマンド
# ポリシードキュメントの作成
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}
出力
[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ロール作成

コマンド
# 信頼関係ポリシードキュメントの作成
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}
出力
[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用コードソースの作成

コマンド
cat << EOF > lambda_function.py
import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
EOF
出力
[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用デプロイパッケージの作成

コマンド
zip function.zip lambda_function.py
出力
[cloudshell-user@ip-10-130-40-130 ~]$ zip function.zip lambda_function.py
  adding: lambda_function.py (deflated 19%)

Lambda関数の作成

--handlerはPythonファイル名.関数名で指定

コマンド
# 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}
出力
[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関数のテスト実行

--payloadはbase64形式で指定

コマンド
# イベントの作成
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
出力
[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用ソースコードの更新

コマンド
# ソースコードの更新
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
出力
[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関数のテスト実行

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

cat outputfile.txt
出力
[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ログの確認

コマンド
# 最新のログストリームの取得
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} 
出力
[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用ソースコードの更新

コマンド
# ソースコードの更新
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
出力
[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ロールを修正

コマンド
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}
出力
[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関数のテスト実行

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

cat outputfile.txt
出力
[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用ソースコードの更新

コマンド
# ソースコードの更新
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
出力
[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関数のテスト実行

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

cat outputfile.txt
出力
[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 を単体で使ってみる

変数

コマンド
# API名
API_NAME="translate-api" \
&& echo ${API_NAME}

# リソース名
RESOURCE_NAME="sample" \
&& echo ${RESOURCE_NAME}
出力
[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の作成

コマンド
aws apigateway create-rest-api \
    --name ${API_NAME}
出力
[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の取得

コマンド
# API IDの取得
RESET_API_ID=$(
    aws apigateway get-rest-apis \
        --query "items[?name=='${API_NAME}'].id" \
        --output text
) \
&& echo ${RESET_API_ID}
出力
[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の取得

コマンド
# ルートリソースIDの取得
PARENT_ID=$(
    aws apigateway get-resources \
        --rest-api-id ${RESET_API_ID} \
        --query "items[?path=='/'].id" \
        --output text
) \
&& echo ${PARENT_ID}
出力
[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リソースの作成

コマンド
# リソースの作成
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}
出力
[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メソッドの作成

コマンド
# GETメソッドの作成
aws apigateway put-method \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET \
    --authorization-type NONE
出力
[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)

コマンド
# 統合の作成(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}"}'
出力
[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レスポンスの設定

--response-templates '{"application/json": '"${ESCAPED_JSON}"'}'
${ESCAPED_JSON}のJSONは改行コード、ダブルクォートをエスケープする必要がある

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

コマンド
# メソッドレスポンスの設定
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}"'}'
出力
[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関数のテスト実行

コマンド
aws apigateway test-invoke-method \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID} \
    --http-method GET
出力
[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のデプロイ

コマンド
# 変数
STAGE_NAME="dev" \
&& echo ${STAGE_NAME}

# デプロイ
aws apigateway create-deployment \
    --rest-api-id ${RESET_API_ID} \
    --stage-name ${STAGE_NAME}
出力
[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リソースの削除

コマンド
# リソースの削除
aws apigateway delete-resource \
    --rest-api-id ${RESET_API_ID} \
    --resource-id ${RESOURCE_ID}
出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws apigateway delete-resource \
>     --rest-api-id ${RESET_API_ID} \
>     --resource-id ${RESOURCE_ID}

API Gateway translateリソースの作成

コマンド
# 変数
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}
出力
[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メソッドの作成

コマンド
# 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"
出力
[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プロキシ統合の設定

コマンド
# 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
出力
[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 レスポンスの設定

コマンド
# メソッドレスポンスの設定
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": ""}'
出力
[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用ソースコードの更新

コマンド
# ソースコードの更新
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
出力
[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関数のテスト実行

コマンド
# イベントの作成
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
出力
[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のデプロイ

コマンド
# デプロイ
aws apigateway create-deployment \
    --rest-api-id ${RESET_API_ID} \
    --stage-name ${STAGE_NAME}
出力
[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テーブル作成

コマンド
# 変数
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}
出力
[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テーブル内項目の作成

コマンド
# 項目作成
aws dynamodb put-item \
    --table-name ${TABLE_NAME} \
    --item '{
        "timestamp": {"S": "20191024183000"},
        "input_text": {"S": "★こんにちは"},
        "output_text": {"S": "★Hello"}
    }'
出力
[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用ソースコードの更新

コマンド
# ソースコードの更新
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
出力
[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ロールを修正

コマンド
# ポリシー名
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}
出力
[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関数のテスト実行

コマンド
# イベントの作成
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
出力
[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テーブル内の項目の確認

コマンド
aws dynamodb scan \
    --table-name translate-history
出力
[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テーブル内の項目の削除

コマンド
aws dynamodb delete-item \
    --table-name ${TABLE_NAME} \
    --key '{"timestamp": {"S": "20191024183000"}}'
出力
[cloudshell-user@ip-10-134-14-200 ~]$ aws dynamodb delete-item \
>     --table-name ${TABLE_NAME} \
>     --key '{"timestamp": {"S": "20191024183000"}}'

DynamoDBテーブル内の項目の確認

コマンド
aws dynamodb scan \
    --table-name translate-history
出力
[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. リソース削除

ハンズオン内ではリソースの削除は含まれていなかったが、ハンズオン後は削除を実施

DnyamoDB削除

コマンド
aws dynamodb delete-table \
    --table-name ${TABLE_NAME}
出力
[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削除

コマンド
aws apigateway delete-rest-api \
    --rest-api-id ${RESET_API_ID}
出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws apigateway delete-rest-api \
>     --rest-api-id ${RESET_API_ID}

Lambda関数の削除

コマンド
aws lambda delete-function \
    --function-name ${FUNCTION_NAME}
出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws lambda delete-function \
>     --function-name ${FUNCTION_NAME}

IAMロールの削除

ポリシーのデタッチ

AWS CLIでロールを削除する場合、最初にすべてのポリシーをデタッチする必要がある

コマンド
# ロールにアタッチされているポリシーをリスト
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
出力
[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ロールの削除

コマンド
# IAMロールの削除
aws iam delete-role \
    --role-name ${ROLE_NAME}
出力
[cloudshell-user@ip-10-134-26-238 ~]$ # IAMロールの削除
[cloudshell-user@ip-10-134-26-238 ~]$ aws iam delete-role \
>     --role-name ${ROLE_NAME}

IAMポリシーの削除

コマンド
# IAMポリシーの削除
aws iam delete-policy \
    --policy-arn ${POLICY_ARN}
出力
[cloudshell-user@ip-10-134-26-238 ~]$ aws iam delete-policy \
>     --policy-arn ${POLICY_ARN}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0