AWS SAM を使ったサーバレスアプリケーション初体験

## 概要
10月にDVAを取得し、11月にSAPを取得したのですが、知識を詰め込んだだけでサービス自体にはあまり触れていなかったので、この機会に試験で出題されたAWS SAMを触ってみました。この記事では、AWS SAM を使って簡単なサーバレスアプリケーションを構築する方法を解説します。このアプリケーションでは DynamoDB を利用してタスクを管理し、Lambda 関数を通じてタスクの作成、取得、完了といった操作を行います。構築からデプロイ、ローカルテスト、リソース削除までの手順をカバーします。

---

## 1. 開発環境の準備

AWS SAM を使ったサーバレスアプリケーションを構築するには、以下が必要です。

### 必要なツール
- **AWS CLI**: AWS サービスとやり取りするために必要です。
- **SAM CLI**: サーバレスアプリケーションの管理ツール。
- **Docker**: SAM CLI のローカルテストに必要。
- **Postman** または `curl` などの API テストツール。

### インストール確認
以下のコマンドを実行してインストールされているか確認してください。

```bash
aws --version
sam --version
docker --version
```

### Docker 起動
Docker をインストールした後、サービスを起動しておきます。

---

## 2. プロジェクトの作成

### SAM CLI を使ったプロジェクト作成
以下のコマンドでプロジェクトを作成します。

```bash
sam init
```

プロンプトに従って以下の設定を選択します。
今回は`AWS Quick Start Templates`から進めていきます。また、本格的な開発というわけではないのでX-rayやCloudWatch Application Insightsは無効で設定をしています。

1. **テンプレートの選択**: `Hello World Example`
2. **ランタイム**: `Python 3.11`
(存在しなければ後述するtemplate.yamlで指定できるのであまり気にしないでください。)
3. **プロジェクト名**: `task-app`

作成後のディレクトリ構造は以下のようになります。

```
task-app/
│  .gitignore
│  README.md
│  samconfig.toml
│  template.yaml
│  __init__.py
│
├─events
│      event.json
│
├─hello_world
│      app.py
│      requirements.txt
│      __init__.py
│
└─tests
    │  requirements.txt
    │  __init__.py
    │
    ├─integration
    │      test_api_gateway.py
    │      __init__.py
    │
    └─unit
            test_handler.py
            __init__.py
```


### **3. DynamoDB テーブルと Lambda 関数の定義 (解説)**

`template.yaml` は AWS SAM において、アプリケーションの全体構成を記述するファイルです。リソース（DynamoDB テーブル、Lambda 関数など）の作成や API Gateway の設定を定義します。

---

#### **全体構成の概要**
- **DynamoDB テーブル (`TaskTable`)**
  - タスクを管理するためのデータベース。
- **Lambda 関数**
  - タスク作成、取得、完了の 3 つの関数を定義。
- **API Gateway**
  - Lambda 関数をトリガーするエンドポイントを提供。
- **Outputs セクション**
  - デプロイ後に生成されるリソースの情報を出力。

---

### **DynamoDB テーブルの定義**

以下の部分が DynamoDB テーブルの定義です。

```yaml
TaskTable:
  Type: AWS::DynamoDB::Table
  Properties:
    TableName: TaskTable
    AttributeDefinitions:
      - AttributeName: id
        AttributeType: S
    KeySchema:
      - AttributeName: id
        KeyType: HASH
    BillingMode: PAY_PER_REQUEST
```

#### **解説**
- **`Type: AWS::DynamoDB::Table`**
  - このリソースは DynamoDB のテーブルを作成します。

- **`TableName`**
  - テーブル名を `TaskTable` に指定しています。この名前は DynamoDB 内で一意である必要があります。

- **`AttributeDefinitions`**
  - `id` という属性を定義し、その型を文字列 (`S`) としています。

- **`KeySchema`**
  - 主キーとして `id` 属性を指定しています。
  - `KeyType: HASH` は DynamoDB のパーティションキー (プライマリキー) です。

- **`BillingMode: PAY_PER_REQUEST`**
  - 使用量に応じた料金を課金する設定です。リソースを効率的に利用できます。

---

### **Lambda 関数の定義**

この YAML ファイルでは 3 つの Lambda 関数を定義しています。それぞれの役割を解説します。

---

#### **1. タスク作成関数 (`CreateTaskFunction`)**

```yaml
CreateTaskFunction:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: hello_world/
    Handler: app.create_task
    Runtime: python3.11
    Architectures:
      - x86_64
    Environment:
      Variables:
        TABLE_NAME: TaskTable
    Policies:
      - DynamoDBCrudPolicy:
          TableName: TaskTable
    Events:
      CreateTaskApi:
        Type: Api
        Properties:
          Path: /tasks
          Method: post
```

- **`CodeUri`**:
  - 関数コードが格納されているフォルダを指定します (`hello_world/`)。

- **`Handler`**:
  - `app.py` 内の `create_task` 関数がエントリポイントとして実行されます。

- **`Runtime`**:
  - 使用する言語のランタイムを指定します (`python3.11`)。

- **`Environment`**:
  - Lambda 関数で使用する環境変数を定義します。
  - `TABLE_NAME` 変数に DynamoDB テーブル名 `TaskTable` を指定しています。

- **`Policies`**:
  - DynamoDB の操作権限を指定します。
  - `DynamoDBCrudPolicy` により、`TaskTable` に対して読み取り、書き込み、更新、削除の操作が可能になります。

- **`Events`**:
  - API Gateway をトリガーとして設定。
  - `/tasks` に対する POST リクエストを処理します。

---

#### **2. タスク取得関数 (`GetTasksFunction`)**

```yaml
GetTasksFunction:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: hello_world/
    Handler: app.get_tasks
    Runtime: python3.11
    Architectures:
      - x86_64
    Environment:
      Variables:
        TABLE_NAME: TaskTable
    Policies:
      - DynamoDBReadPolicy:
          TableName: TaskTable
    Events:
      GetTasksApi:
        Type: Api
        Properties:
          Path: /tasks
          Method: get
```

- **`Policies`**:
  - `DynamoDBReadPolicy` により、DynamoDB テーブルからのデータ読み取り操作が可能です。

- **`Events`**:
  - `/tasks` に対する GET リクエストを処理します。

---

#### **3. タスク完了関数 (`CompleteTaskFunction`)**

```yaml
CompleteTaskFunction:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: hello_world/
    Handler: app.complete_task
    Runtime: python3.11
    Architectures:
      - x86_64
    Environment:
      Variables:
        TABLE_NAME: TaskTable
    Policies:
      - DynamoDBCrudPolicy:
          TableName: TaskTable
    Events:
      CompleteTaskApi:
        Type: Api
        Properties:
          Path: /tasks/{id}/complete
          Method: put
```

- **`Events`**:
  - `/tasks/{id}/complete` に対する PUT リクエストを処理します。
  - `{id}` は URL パスパラメータとして指定されます。

---

### **Outputs セクション**

Outputs セクションでは、デプロイ後に生成されるリソースの情報を出力します。これにより、API Gateway エンドポイント URL や Lambda 関数 ARN などが簡単に参照できます。

```yaml
Outputs:
  CreateTaskApi:
    Description: "API Gateway endpoint URL for creating tasks"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/tasks"
  GetTasksApi:
    Description: "API Gateway endpoint URL for retrieving tasks"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/tasks"
  CompleteTaskApi:
    Description: "API Gateway endpoint URL for completing tasks"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/tasks/{id}/complete"
```

#### **解説**
- **`CreateTaskApi`**:
  - タスク作成用 API のエンドポイントを出力します。
- **`GetTasksApi`**:
  - タスク取得用 API のエンドポイントを出力します。
- **`CompleteTaskApi`**:
  - タスク完了用 API のエンドポイントを出力します。
- **`!Sub`**:
  - 文字列中の `${}` 部分を CloudFormation が解釈して動的に値を埋め込みます。


## **4. Lambda 関数の実装**

以下は、`app.py` ファイルに記述された 3 つの Lambda 関数についての解説です。

---

### **全体構成**

```python
import json
import boto3
import uuid
import os

# DynamoDB リソースの設定
# 本番環境とローカル環境で切り替え可能
# dynamodb = boto3.resource('dynamodb', endpoint_url="http://host.docker.internal:8000")
dynamodb = boto3.resource('dynamodb')

# 環境変数から DynamoDB テーブル名を取得
table_name = os.environ['TABLE_NAME']
table = dynamodb.Table(table_name)
```

#### **ポイント**
- **`boto3.resource('dynamodb')`**:
  - AWS DynamoDB にアクセスするための設定。
  - 本番環境では `endpoint_url` を設定せずにデフォルトで動作します。
- **環境変数 `TABLE_NAME`**:
  - DynamoDB テーブル名を環境変数から取得し、動的に設定します。

---

### **1. タスク作成関数 (`create_task`)**

```python
def create_task(event, context):
    """
    タスクを作成するLambda関数
    """
    body = event.get('body')
    if not body:
        return {
            "statusCode": 400,
            "body": json.dumps({"message": "Invalid request, 'body' is missing"})
        }

    try:
        data = json.loads(body)
    except json.JSONDecodeError:
        return {
            "statusCode": 400,
            "body": json.dumps({"message": "Invalid JSON in request body"})
        }

    # タスクを作成
    task_id = str(uuid.uuid4())
    task = {
        "id": task_id,
        "title": data.get("title", "Untitled Task"),
        "description": data.get("description", ""),
        "status": "pending"  # デフォルトで未完了状態
    }
    table.put_item(Item=task)

    return {
        "statusCode": 201,
        "body": json.dumps({"message": "Task created", "task": task})
    }
```

#### **動作の流れ**
1. **リクエストボディの検証**
   - `event['body']` が存在するか確認。
   - JSON 形式で解析し、不正なリクエストはエラーを返します。

2. **タスクデータの作成**
   - UUID で一意の ID を生成。
   - タスクの `title`, `description`, `status` を設定。

3. **DynamoDB に保存**
   - `table.put_item(Item=task)` を使用してデータを挿入。

4. **レスポンス**
   - 作成されたタスクのデータを含むレスポンスを返します。

---

### **2. タスク取得関数 (`get_tasks`)**

```python
def get_tasks(event, context):
    """
    すべてのタスクを取得するLambda関数
    """
    response = table.scan()
    tasks = response.get("Items", [])
    return {
        "statusCode": 200,
        "body": json.dumps({"tasks": tasks})
    }
```

#### **動作の流れ**
1. **データ取得**
   - `table.scan()` で DynamoDB テーブル内のすべてのタスクを取得します。

2. **レスポンス**
   - 取得したタスクデータを JSON 形式で返します。

---

### **3. タスク完了関数 (`complete_task`)**

```python
def complete_task(event, context):
    """
    指定されたタスクを完了にするLambda関数
    """
    # タスクIDを取得
    path_parameters = event.get("pathParameters", {})
    task_id = path_parameters.get("id")

    if not task_id:
        return {
            "statusCode": 400,
            "body": json.dumps({"message": "Task ID is required in the path"})
        }

    # タスクの状態を更新
    try:
        response = table.update_item(
            Key={"id": task_id},
            UpdateExpression="SET #status = :status",
            ExpressionAttributeNames={
                "#status": "status"
            },
            ExpressionAttributeValues={
                ":status": "completed"
            },
            ReturnValues="UPDATED_NEW"
        )
        return {
            "statusCode": 200,
            "body": json.dumps({"message": "Task completed", "updatedAttributes": response.get("Attributes", {})})
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "body": json.dumps({"message": "Failed to complete task", "error": str(e)})
        }
```

#### **動作の流れ**
1. **タスク ID の取得**
   - `event['pathParameters']['id']` から ID を取得。
   - ID が指定されていない場合はエラーを返します。

2. **DynamoDB 更新**
   - 指定された ID のタスクを検索し、`status` を `completed` に変更。
   - 更新された内容をレスポンスとして返します。

3. **エラーハンドリング**
   - 更新処理中の例外はキャッチし、エラーメッセージを含むレスポンスを返します。

---

### **ローカル環境での注意点**
- ローカル環境で動作確認する場合、`endpoint_url` を設定して DynamoDB Local を使用します。
- 本番環境ではコメントアウトして、標準の AWS DynamoDB に接続します。

```python
# dynamodb = boto3.resource('dynamodb', endpoint_url="http://host.docker.internal:8000")
dynamodb = boto3.resource('dynamodb')
```

このように、ローカルテスト用と本番環境用を簡単に切り替えることができます。

### **5. ローカルテスト**

ローカルテストでは、AWSリソースを直接触らずに環境を模擬して、Lambda関数やDynamoDBテーブルの動作確認を行います。このセクションでは、DynamoDB Local を使用したローカルテストの手順を詳しく説明します。

---

### **前提条件**
1. AWS SAM CLI がインストールされていること。
2. Docker がインストールされ、起動していること。
3. DynamoDB Local が起動していること（[この記事](https://qiita.com/to-fmak/items/3a6df367196ed216b1a4)を参考）。

---

### **手順**

#### **1. DynamoDB Local の起動**

以下のコマンドで DynamoDB Local を Docker コンテナとして起動します。

```bash
docker run -d -p 8000:8000 amazon/dynamodb-local
```

- **オプションの確認**
  - `-d` : バックグラウンドで実行。
  - `-p 8000:8000` : ホストとコンテナのポートを 8000 にバインド。

#### **2. DynamoDB テーブルの作成**

以下のコマンドで DynamoDB Local にテーブルを作成します。

```bash
aws dynamodb create-table \
    --table-name TaskTable \
    --attribute-definitions AttributeName=id,AttributeType=S \
    --key-schema AttributeName=id,KeyType=HASH \
    --billing-mode PAY_PER_REQUEST \
    --endpoint-url http://localhost:8000
```

- **各パラメータの意味**
  - `--table-name TaskTable`: 作成するテーブル名。
  - `--attribute-definitions`: テーブルの属性定義。ここでは `id` を文字列型で定義。
  - `--key-schema`: プライマリキーを指定。
  - `--billing-mode PAY_PER_REQUEST`: 従量課金モードを指定。
  - `--endpoint-url http://localhost:8000`: ローカル DynamoDB に接続。

#### **3. DynamoDB の状態確認**

テーブルが作成されたかを確認します。

```bash
aws dynamodb list-tables --endpoint-url http://localhost:8000
```

出力例:

```json
{
    "TableNames": [
        "TaskTable"
    ]
}
```

次に、テーブルの詳細情報を確認します。

```bash
aws dynamodb describe-table --table-name TaskTable --endpoint-url http://localhost:8000
```

---

#### **4. イベントファイルの準備**

Lambda 関数のテストに使用するイベントファイルを用意します。

**① タスク作成 (`create_task.json`):**
```json
{
    "resource": "/tasks",
    "path": "/tasks",
    "httpMethod": "POST",
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": "application/json"
    },
    "body": "{\"title\": \"Test Task\", \"description\": \"This is a test task.\"}"
}
```

**② タスク取得 (`get_tasks.json`):**
```json
{
    "resource": "/tasks",
    "path": "/tasks",
    "httpMethod": "GET",
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": "application/json"
    }
}
```

**③ タスク完了 (`complete_task.json`):**
`path`と`pathParameters.id`は作成したタスクを確認して適宜変えてください。
```json
{
    "resource": "/tasks/{id}/complete",
    "path": "/tasks/a044c959-0139-401d-8131-9781a991ffe0/complete",
    "httpMethod": "PUT",
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": "application/json"
    },
    "pathParameters": {
        "id": "a044c959-0139-401d-8131-9781a991ffe0"
    }
}
```

---

#### **5. Lambda 関数のローカルテスト**

AWS SAM CLI を使用して Lambda 関数をローカルでテストします。

1. **SAM ビルド**

以下のコマンドで Lambda 関数をビルドします。

```bash
sam build
```

成功例:

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

2. **Lambda 関数のテスト**

- **タスク作成**
  ```bash
  sam local invoke CreateTaskFunction --event events/create_task.json
  ```

- **タスク取得**
  ```bash
  sam local invoke GetTasksFunction --event events/get_tasks.json
  ```

- **タスク完了**
  ```bash
  sam local invoke CompleteTaskFunction --event events/complete_task.json
  ```

出力例:
- **タスク作成の成功例**
  ```json
  {
      "statusCode": 201,
      "body": "{\"message\": \"Task created\", \"task\": {\"id\": \"01e86c41-a58b-46b0-9495-c8390a0bb420\", \"title\": \"Test Task\", \"description\": \"This is a test task.\", \"status\": \"pending\"}}"
  }
  ```

---

#### **6. DynamoDB Local でデータ確認**

DynamoDB Local に保存されたデータを確認します。

```bash
aws dynamodb scan --table-name TaskTable --endpoint-url http://localhost:8000
```

出力例:

```json
{
    "Items": [
        {
            "title": {
                "S": "Test Task"
            },
            "description": {
                "S": "This is a test task."
            },
            "id": {
                "S": "01e86c41-a58b-46b0-9495-c8390a0bb420"
            },
            "status": {
                "S": "pending"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}
```

---
:::note
### **補足: DynamoDB Local GUI (dynamodb-admin)**

より視覚的に DynamoDB Local を操作したい場合は、`dynamodb-admin` を使用します。
https://github.com/aaronshaf/dynamodb-admin

1. **インストール**
   ```bash
   npm install -g dynamodb-admin
   ```

2. **起動**
   ```bash
   dynamodb-admin --dynamo-endpoint=http://localhost:8000 --skip-default-credentials
   ```

3. **アクセス**
   - ブラウザで [http://localhost:8001](http://localhost:8001) にアクセスします。
   - テーブルデータを GUI で確認できます。
:::

### **6. 本番環境へのデプロイ**

ローカルでの動作確認が終わったら、次は本番環境へのデプロイを行います。AWS SAM CLI を使用して、作成したリソースを AWS にデプロイする方法を以下に詳しく説明します。

---

### **デプロイ手順**

#### **1. `sam deploy --guided` コマンドの実行**

以下のコマンドを実行して、AWS にデプロイします。

```bash
sam deploy --guided
```

#### **2. 対話形式の設定**

コマンド実行後に対話形式で設定を進めます。以下は設定の例です。

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

        Looking for config file [samconfig.toml] :  Not found
        Setting default arguments for 'sam deploy'

Stack Name [task-app]:  # デプロイする CloudFormation スタック名を入力します (例: task-app)
AWS Region [us-east-1]: ap-northeast-1  # デプロイするリージョンを指定します (例: ap-northeast-1)

# Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: Y  # デプロイ前にリソースの変更を確認したい場合は "Y" を選択
# SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: Y  # 必要な IAM ロールを作成する場合は "Y"

# Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: N  # エラー時にロールバックを無効にしたい場合は "Y" を選択

# 今回は簡単な検証目的なので認証不要
CreateTaskFunction has no authentication. Is this okay? [y/N]: Y
GetTasksFunction has no authentication. Is this okay? [y/N]: Y
CompleteTaskFunction has no authentication. Is this okay? [y/N]: Y

Save arguments to configuration file [Y/n]: Y  # 設定を `samconfig.toml` に保存する場合は "Y"
SAM configuration file [samconfig.toml]:  # デフォルトで "samconfig.toml" を指定
SAM configuration environment [default]:  # 環境名を指定 (通常は "default")
```

---

#### **3. デプロイの進行**

設定を入力すると、以下のような進捗が表示されます。

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

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

Waiting for changeset to be created..

CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------     
Operation                                               LogicalResourceId                                       ResourceType                                            Replacement
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------     
+ Add                                                   CompleteTaskFunctionCompleteTaskApiPermissionProd       AWS::Lambda::Permission                                 N/A
+ Add                                                   CompleteTaskFunctionRole                                AWS::IAM::Role                                          N/A
+ Add                                                   CompleteTaskFunction                                    AWS::Lambda::Function                                   N/A
+ Add                                                   CreateTaskFunctionCreateTaskApiPermissionProd           AWS::Lambda::Permission                                 N/A
+ Add                                                   CreateTaskFunctionRole                                  AWS::IAM::Role                                          N/A
+ Add                                                   CreateTaskFunction                                      AWS::Lambda::Function                                   N/A
+ Add                                                   GetTasksFunctionGetTasksApiPermissionProd               AWS::Lambda::Permission                                 N/A
+ Add                                                   GetTasksFunctionRole                                    AWS::IAM::Role                                          N/A
+ Add                                                   GetTasksFunction                                        AWS::Lambda::Function                                   N/A
+ Add                                                   ServerlessRestApiDeployment26a16bfa26                   AWS::ApiGateway::Deployment                             N/A
+ Add                                                   ServerlessRestApiProdStage                              AWS::ApiGateway::Stage                                  N/A
+ Add                                                   ServerlessRestApi                                       AWS::ApiGateway::RestApi                                N/A
+ Add                                                   TaskTable                                               AWS::DynamoDB::Table                                    N/A
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

Deploy this changeset? [y/N]: y
```

`y` を入力してデプロイを進めます。

---

#### **4. デプロイ完了後の確認**

デプロイが成功すると以下のような結果が表示されます。

```
Successfully created/updated stack - task-app in ap-northeast-1
Outputs
----------------------------------------------------------------------------------------------------------------------------------
Key                 TaskTableName
Description         DynamoDB table name
Value               TaskTable

Key                 CompleteTaskApi
Description         API Gateway endpoint URL for completing tasks
Value               https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/tasks/{id}/complete

Key                 GetTasksApi
Description         API Gateway endpoint URL for retrieving tasks
Value               https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/tasks

Key                 CreateTaskApi
Description         API Gateway endpoint URL for creating tasks
Value               https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/tasks
----------------------------------------------------------------------------------------------------------------------------------
```

---

### **API エンドポイントの確認**

1. **タスク作成 API**
   - **URL**: `https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/tasks`
   - **HTTP メソッド**: POST
   - **リクエストボディ例**:
     ```json
     {
       "title": "New Task",
       "description": "This is a new task."
     }
     ```

2. **タスク取得 API**
   - **URL**: `https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/tasks`
   - **HTTP メソッド**: GET

3. **タスク完了 API**
   - **URL**: `https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/tasks/{id}/complete`
   - **HTTP メソッド**: PUT

---

:::note warn
### **補足: デプロイの問題解決**
#### **template.yamlに問題がある場合**
- デプロイ時にエラーが発生した場合、以下のコマンドで template.yamlの構成に問題がないかを確認できます。
  ```bash
  sam validate
  ```

#### **エラーが発生した場合**
- デプロイ時にエラーが発生した場合、以下のコマンドで CloudFormation のスタック状況を確認できます。
  ```bash
  aws cloudformation describe-stacks --stack-name task-app
  ```
- マネジメントコンソールにアクセスしてCloudFormationのイベントタブからも原因を確認可能です。
(キャプチャは正常にデプロイが完了しているものです)
![スクリーンショット 2024-12-21 124135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884367/bfbd872c-b3f9-37ef-96f5-ed5995966d79.png)

:::

---

### **7. 本番環境での動作確認**

#### **1. デプロイ後の API Gateway 確認**

AWS マネジメントコンソールから API Gateway の設定を確認します。

**API Gateway コンソールに移動**:
   - AWS マネジメントコンソールで「API Gateway」に移動します。
   - デプロイされた API (例: `task-app`) がリストに表示されていることを確認します。

![スクリーンショット 2024-12-21 123231.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884367/e47115fe-fe8b-aaf1-9561-3af827996b25.png)

---

#### **2. Postman を使った API テスト**

Postman を使用して、本番環境の API をテストしました。以下の順序で API を実行しています。

---

**① タスクの作成 (POST)**


![スクリーンショット 2024-12-21 124655.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884367/6d342dc6-56d9-b823-0fdf-2d9509ddd42e.png)


---

**② タスクの取得 (GET)**

![スクリーンショット 2024-12-21 125007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884367/b70f7822-20e8-3a4e-6b99-d7dc5d320ad6.png)


---

**③ タスクの完了 (PUT)**

![スクリーンショット 2024-12-21 125337.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884367/0595a1f1-c433-4c6b-4413-5b0162d0a502.png)


---

**④ 完了後のタスクの取得 (GET)**

![スクリーンショット 2024-12-21 125447.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884367/99d2efe9-553e-ee4b-048f-6bebcca992a7.png)


---

### **結果のまとめ**

- 全ての操作 (タスクの作成、取得、完了) が正常に動作することを確認しました。
- DynamoDB に保存されたデータがリアルタイムで更新されていることも確認済みです。


---



### **8. 最後に**
:::note
#### **リソースの削除**
- 不要になったリソースを削除するには以下のコマンドを使用します。
  ```bash
  sam delete
  ```
:::

これで、簡単にですがSAMを試すことができました。結構簡単にいろいろできそうなので、まだまだ触れていない部分も引き続き勉強を続けていきます！