

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 透過範例應用程式與模式快速入門
<a name="example-apps"></a>

下列資源可用於快速建立並部署能實作部分常見 Lambda 使用案例的無伺服器應用程式。對於每個範例應用程式，我們提供使用 手動建立和設定資源的說明 AWS 管理主控台，或使用 AWS Serverless Application Model 來使用 IaC 部署資源。遵循主控台說明，進一步了解如何為每個應用程式設定個別 AWS 資源，或使用 AWS SAM 快速部署資源，就像您在生產環境中一樣。

## 檔案處理
<a name="examples-apps-file"></a>
+ **[PDF 加密應用程式](file-processing-app.md)**：建立無伺服器應用程式，能在 PDF 檔案上傳至 Amazon Simple Storage Service 儲存貯體時進行加密，並將加密後的檔案儲存至另一個儲存貯體。此類應用程式對於在上傳檔案時保護敏感文件非常有用。
+ **[映像分析應用程式](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-example-s3.html)**：建立無伺服器應用程式，能透過 Amazon Rekognition 從映像中擷取文字。此類應用程式對於文件處理、內容審核與自動化映像分析非常有用。

## 資料庫整合
<a name="examples-apps-database"></a>
+ **[佇列到資料庫應用程式](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-lambda-tutorial.html)**：建立無伺服器應用程式，能將佇列訊息寫入 Amazon RDS 資料庫。此類應用程式對於處理使用者註冊與處理訂單提交非常有用。
+ **[資料庫事件處理常式](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-example-ddb.html)**：建立無伺服器應用程式，能回應 Amazon DynamoDB 資料表變更。此類應用程式對於稽核日誌、資料複寫與工作流程自動化非常有用。

## 排程任務
<a name="examples-apps-scheduled"></a>
+ **[資料庫維護應用程式](scheduled-task-app.md)**：建立無伺服器應用程式，能使用 Cron 排程自動刪除 Amazon DynamoDB 資料表中超過 12 個月的項目。此類應用程式對於自動化資料庫維護與資料生命週期管理非常有用。
+ **[為 Lambda 函式建立 EventBridge 排程規則](https://docs.aws.amazon.com/eventbridge/latest/userguide/run-lambda-schedule.html)**：在 EventBridge 中為規則套用排程表達式，依據定時排程觸發 Lambda 函式。此格式使用 cron 語法，並且可以設定一分鐘精細度。

## 長時間執行的工作流程
<a name="examples-apps-workflows"></a>
+ **[訂單處理應用程式](order-processing-app.md)**：使用處理複雜訂單履行的耐用函數建立無伺服器應用程式，包括付款處理、庫存檢查和運送協調。此範例示範如何建置可在維護狀態的同時長時間執行的工作流程。

## 其他資源
<a name="examples-apps-additional-resources"></a>

使用下列資源進一步探索 Lambda 與無伺服器應用程式部署：
+ **[Serverless Land](https://serverlessland.com/)**：用於建構無伺服器應用程式的現成模式庫。它可協助開發人員使用 Lambda、API Gateway 和 EventBridge 等 AWS 服務更快地建立應用程式。該網站提供預先建置的解決方案與最佳實務，讓開發無伺服器系統變得更加輕鬆。
+ **[Lambda 範例應用程式](https://docs.aws.amazon.com/lambda/latest/dg/lambda-samples.html)**：本指南 GitHub 儲存庫中提供的應用程式。這些範例示範使用各種語言 AWS 和服務。每個範例應用程式都包含可用於輕鬆部署和清理的指令碼和支援資源。
+ **[Lambda AWS SDKs的程式碼範例](https://docs.aws.amazon.com/lambda/latest/dg/service_code_examples.html)**：示範如何搭配 AWS 軟體開發套件 (SDKs) 使用 Lambda 的範例。這些範例包括基本概念、動作、案例和 AWS 社群貢獻。範例涵蓋基本操作、個別服務函數，以及使用多個函數 AWS 或服務的特定任務。

# 建立無伺服器檔案處理應用程式
<a name="file-processing-app"></a>

Lambda 最常見的使用案例之一是執行檔案處理任務。例如，您可以使用 Lambda 函數從 HTML 檔案或影像自動建立 PDF 檔案，或在使用者上傳影像時建立縮圖。

在此範例中，您會建立這樣的應用程式：當 PDF 檔案上傳至 Amazon Simple Storage Service (Amazon S3) 儲存貯體時，該應用程式會自動加密 PDF 檔案。若要實作此應用程式，您需要建立下列資源：
+ 供使用者上傳 PDF 檔案 S3 儲存貯體
+ 使用 Python 編寫的 Lambda 函數，用於讀取上傳的檔案，並建立其受密碼保護的加密版本
+ 供 Lambda 儲存加密檔案的第二個 S3 儲存貯體

您也可以建立 AWS Identity and Access Management (IAM) 政策，授予 Lambda 函數在 S3 儲存貯體上執行讀取和寫入操作的許可。

![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/ExampleApps/file_process_resources.png)


**提示**  
如果您是 Lambda 新手，建議在建立此範例應用程式之前，先學習「[建立第一個 Lambda 函數](getting-started.md)」教學課程。

您可以使用 AWS 管理主控台 或 AWS Command Line Interface () 建立和設定資源，以手動部署您的應用程式AWS CLI。您也可以使用 AWS Serverless Application Model (AWS SAM) 來部署應用程式。 AWS SAM 是基礎設施即程式碼 (IaC) 工具。藉助 IaC，您無需手動建立資源，而是在程式碼中定義資源，然後便可自動部署資源。

如果您想要在部署此範例應用程式之前，進一步了解如何將 Lambda 與 IaC 搭配使用，請參閱[將 Lambda 搭配基礎設施即程式碼 (IaC)](foundation-iac.md)。

## 建立 Lambda 函式原始程式碼檔案
<a name="file-processing-app-download"></a>

在專案目錄中建立下列檔案：
+ `lambda_function.py`：執行檔案加密之 Lambda 函數的 Python 函數程式碼
+ `requirements.txt`：資訊清單檔案，用於定義 Python 函數程式碼所需的相依項

展開下列各區段即可檢視程式碼，並進一步了解每個檔案的作用。若要在本機電腦上建立檔案，請複製並貼上下面的程式碼，或從 [aws-lambda-developer-guide GitHub 儲存庫](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/file-processing-python)下載檔案。

### Python 函數程式碼
<a name="file-processing-app-function-code"></a>

複製以下程式碼並貼到名稱為 `lambda_function.py` 的檔案中。

```
from pypdf import PdfReader, PdfWriter
import uuid
import os
from urllib.parse import unquote_plus
import boto3

# Create the S3 client to download and upload objects from S3
s3_client = boto3.client('s3')

def lambda_handler(event, context):
    # Iterate over the S3 event object and get the key for all uploaded files
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key']) # Decode the S3 object key to remove any URL-encoded characters
        download_path = f'/tmp/{uuid.uuid4()}.pdf' # Create a path in the Lambda tmp directory to save the file to 
        upload_path = f'/tmp/converted-{uuid.uuid4()}.pdf' # Create another path to save the encrypted file to
        
        # If the file is a PDF, encrypt it and upload it to the destination S3 bucket
        if key.lower().endswith('.pdf'):
            s3_client.download_file(bucket, key, download_path)
            encrypt_pdf(download_path, upload_path)
            encrypted_key = add_encrypted_suffix(key)
            s3_client.upload_file(upload_path, f'{bucket}-encrypted', encrypted_key)

# Define the function to encrypt the PDF file with a password
def encrypt_pdf(file_path, encrypted_file_path):
    reader = PdfReader(file_path)
    writer = PdfWriter()
    
    for page in reader.pages:
        writer.add_page(page)

    # Add a password to the new PDF
    writer.encrypt("my-secret-password")

    # Save the new PDF to a file
    with open(encrypted_file_path, "wb") as file:
        writer.write(file)

# Define a function to add a suffix to the original filename after encryption
def add_encrypted_suffix(original_key):
    filename, extension = original_key.rsplit('.', 1)
    return f'{filename}_encrypted.{extension}'
```

**注意**  
在此範例程式碼中，用於加密檔案的密碼 (`my-secret-password`) 寫死到函數程式碼中。但在生產應用程式中，請勿在函數程式碼中包含密碼等敏感資訊。反之，[請建立 AWS Secrets Manager 秘密](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html)，[然後使用 AWS 參數和秘密 Lambda 延伸](with-secrets-manager.md)來擷取 Lambda 函數中的登入資料。

Python 函數程式碼包含三個函數：調用 Lambda 函數時執行的[處理常式函數](python-handler.md)，以及該處理常式調用以執行 PDF 加密的其他兩個函數，分別為 `encrypt_pdf` 和 `add_encrypted_suffix`。

當該函數被 Amazon S3 調用時，Lambda 會將 JSON 格式的*事件*引數傳遞給該函數，其中包含導致調用的事件詳細資訊。在此例中，這些資訊包括 S3 儲存貯體的名稱和上傳檔案的物件索引鍵。若要進一步了解 Amazon S3 的事件物件格式，請參閱[使用 Lambda 處理 Amazon S3 事件通知](with-s3.md)。

然後，您的函數會使用 適用於 Python (Boto3) 的 AWS SDK 將事件物件中指定的 PDF 檔案下載至其本機暫時儲存目錄，再使用 [https://pypi.org/project/pypdf/](https://pypi.org/project/pypdf/)程式庫進行加密。

最後，該函數會使用 Boto3 SDK 將經過加密的檔案存放在 S3 目的地儲存貯體中。

### `requirements.txt` 資訊清單檔案
<a name="file-processing-app-dependencies"></a>

複製以下程式碼並貼到名稱為 `requirements.txt` 的檔案中。

```
boto3
pypdf
```

在此範例中，函數程式碼只包含兩個不屬於標準 Python 程式庫的相依項，即適用於 Python 的 SDK (Boto3) 和函數用來執行 PDF 加密的 `pypdf` 套件。

**注意**  
Lambda 執行時期包含適用於 Python 的 SDK (Boto3) 版本，因此程式碼無需將 Boto3 新增至函數的部署套件即可執行。不過，為了維持對函數相依項的完整控制，並避免版本不一致可能造成的問題，Python 的最佳實務是在函數的部署套件中包含所有函數相依項。如需進一步了解，請參閱[Python 中的執行期相依項](python-package.md#python-package-dependencies)。

## 部署應用程式
<a name="file-processing-app-deploy"></a>

您可以手動或使用 來建立和部署此範例應用程式的資源 AWS SAM。在生產環境中，我們建議您使用類似 的 IaC 工具 AWS SAM ，快速且重複地部署整個無伺服器應用程式，而無需使用手動程序。

### 手動部署資源
<a name="file-processing-app-deploy-manual"></a>

若要手動部署應用程式：
+ 建立來源和目的地 Amazon S3 儲存貯體
+ 建立一個 Lambda 函數來加密 PDF 檔案，並將經過加密的版本儲存至 S3 儲存貯體
+ 設定一個 Lambda 觸發條件，在物件上傳至來源儲存貯體時調用函數

在開始之前，先確保建置機器上已安裝 [Python](https://www.python.org/downloads/)。

#### 建立兩個 S3 儲存貯體
<a name="file-processing-app-deploy-manual-create-buckets"></a>

首先建立兩個 S3 儲存貯體。第一個儲存貯體是將接收 PDF 檔案上傳的來源儲存貯體。當您調用函數時，Lambda 會使用第二個儲存貯體來儲存經過加密的檔案。

------
#### [ Console ]

**若要建立 S3 儲存貯體 (主控台)**

1. 開啟 Amazon S3 主控台的[一般用途儲存貯體](https://console.aws.amazon.com/s3/buckets)頁面。

1. 選取最 AWS 區域 接近您地理位置的 。可使用螢幕頂端的下拉式清單來變更區域。  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/console_region_select.png)

1. 選擇**建立儲存貯體**。

1. 在 **General configuration (一般組態)** 下，執行下列動作：

   1. 在**儲存貯體類型**欄位中，選取**一般用途**。

   1. 在**儲存貯體名稱**欄位中，輸入符合 Amazon S3 [儲存貯體命名規則](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)的全域唯一名稱。儲存貯體名稱只能包含小寫字母、數字、句點 (.) 和連字號 (-)。

1. 其他所有選項維持設為預設值，然後選擇**建立儲存貯體**。

1. 重複步驟 1 到 4 以建立目的地儲存貯體。對於**儲存貯體名稱**，輸入 `amzn-s3-demo-bucket-encrypted`，其中 `amzn-s3-demo-bucket` 是您剛才建立的來源儲存貯體名稱。

------
#### [ AWS CLI ]

在開始之前，先確保建置機器上[已安裝AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。

**建立 Amazon S3 儲存貯體 (AWS CLI)**

1. 執行下列 CLI 命令以建立來源儲存貯體。您為儲存貯體選擇的名稱必須具有全域唯一性，且遵循 Amazon S3 [儲存貯體命名規則](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)。名稱只能包含小寫字母、數字、句點 (.) 和連字號 (-)。對於 `region` 和 `LocationConstraint`，請選擇最接近您地理位置的 [AWS 區域](https://docs.aws.amazon.com/general/latest/gr/lambda-service.html)。

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-bucket --region us-east-2 \
   --create-bucket-configuration LocationConstraint=us-east-2
   ```

   稍後在教學課程中，您必須在與來源儲存貯體 AWS 區域 相同的 中建立 Lambda 函數，因此請記下您選擇的區域。

1. 執行下列命令以建立目的地儲存貯體。對於儲存貯體名稱，必須使用 `amzn-s3-demo-bucket-encrypted`，其中 `amzn-s3-demo-bucket` 是您在步驟 1 中建立的來源儲存貯體名稱。針對 `region`和 `LocationConstraint`，選擇 AWS 區域 您用來建立來源儲存貯體的相同 。

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-bucket-encrypted --region us-east-2 \
   --create-bucket-configuration LocationConstraint=us-east-2
   ```

------

#### 建立執行角色
<a name="file-processing-app-deploy-manual-create-execution-role"></a>

執行角色是授予 Lambda 函數存取 AWS 服務 和資源許可的 IAM 角色。若要向函數提供 Amazon S3 的讀取和寫入權限，請連接 [AWS 受管政策](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) `AmazonS3FullAccess`。

------
#### [ Console ]

**若要建立執行角色並連接 `AmazonS3FullAccess` 受管政策 (主控台)**

1. 在 IAM 主控台中開啟 [角色頁面](https://console.aws.amazon.com/iam/home/roles)。

1. 選擇建**立角色**。

1. 在**信任的實體類型**欄位中，選取 **AWS 服務**；在**使用案例**欄位中，選取 **Lambda**。

1. 選擇**下一步**。

1. 執行下列操作，新增 `AmazonS3FullAccess` 受管政策：

   1. 在**許可政策**區段，於搜尋列輸入 **AmazonS3FullAccess**。

   1. 勾選政策旁的核取方塊。

   1. 選擇**下一步**。

1. 在**角色詳細資訊**區段，於**角色名稱**欄位輸入 **LambdaS3Role**。

1. 選擇**建立角色**。

------
#### [ AWS CLI ]

**若要建立執行角色並連接 `AmazonS3FullAccess` 受管政策 (AWS CLI)**

1. 將下面的 JSON 儲存在名為 `trust-policy.json` 的檔案中。此信任政策允許 Lambda 透過授予服務主體呼叫 AWS Security Token Service (AWS STS) `AssumeRole`動作的許可來使用角色的`lambda.amazonaws.com`許可。  
****  

   ```
   {
     "Version":"2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. 在儲存 JSON 信任政策文件的目錄中，執行下列 CLI 命令，建立執行角色。

   ```
   aws iam create-role --role-name LambdaS3Role --assume-role-policy-document file://trust-policy.json
   ```

1. 若要執行下列 CLI 命令，以連接 `AmazonS3FullAccess` 受管政策。

   ```
   aws iam attach-role-policy --role-name LambdaS3Role --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
   ```

------

#### 建立函數部署套件
<a name="file-processing-app-deploy-manual-create-function-package"></a>

要建立函數，需建立包含函數程式碼和其相依項的*部署套件*。對於此應用程式，函數程式碼使用單獨的程式庫進行 PDF 加密。

**建立部署套件**

1. 導覽至專案目錄，其中包含您先前從 GitHub 建立或下載的檔案 `lambda_function.py` 和 `requirements.txt`，然後建立新的目錄，並命名為 `package`。

1. 執行以下命令，在 `package` 目錄中安裝 `requirements.txt` 檔案中指定的相依項。

   ```
   pip install -r requirements.txt --target ./package/
   ```

1. 建立包含應用程式程式碼及其相依項的 .zip 檔案。在 Linux 或 MacOS 中，使用命令列界面執行下列命令。

   ```
   cd package
   zip -r ../lambda_function.zip .
   cd ..
   zip lambda_function.zip lambda_function.py
   ```

    在 Windows 中，使用您偏好的 zip 工具建立 `lambda_function.zip` 檔案。確保包含相依項的 `lambda_function.py` 檔案和資料夾全部都在 .zip 檔案的根目錄中。

您也可以使用 Python 虛擬環境建立部署套件。請參閱 [使用 .zip 封存檔部署 Python Lambda 函數](python-package.md)

#### 建立 Lambda 函式
<a name="file-processing-app-deploy-manual-createfunction"></a>

現在使用上一個步驟建立的部署套件來部署 Lambda 函數。

------
#### [ Console ]

**建立函數的方式 (主控台)**

要使用主控台建立 Lambda 函數，首先建立包含一些 ‘Hello world’ 程式碼的基本函數。然後，透過上傳您在上一個步驟中建立的 .zip 檔案，將此程式碼取代為您自己的函數程式碼。

若要確保函數在加密大型 PDF 檔案時不會逾時，您需要設定函數的記憶體和逾時設定。另外，您還需要將函數的日誌格式設定為 JSON。使用提供的測試指令碼時，必須設定 JSON 格式日誌，以便從 CloudWatch Logs 讀取函數的調用狀態，以確認調用成功。

1. 開啟 Lambda 主控台中的[函數頁面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 請確定您在 AWS 區域 建立 S3 儲存貯體的相同 中作業。可使用螢幕頂端的下拉式清單來變更區域。  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/console_region_select.png)

1. 選擇 **Create function (建立函數)**。

1. 選擇 **Author from scratch** (從頭開始撰寫)。

1. 在**基本資訊**下，請執行下列動作：

   1. 針對**函數名稱**，請輸入 `EncryptPDF`。

   1. 針對**執行時期**，選擇 **Python 3.12**。

   1. 對於 **Architecture** (架構)，選擇 **x86\$164**。

1. 執行下列步驟，連接在上一個步驟建立的執行角色：

   1. 展開 **Change default execution role** (變更預設執行角色) 區段。

   1. 選取**使用現有角色**。

   1. 在**現有角色**欄位中，選取您的角色 (`LambdaS3Role`)。

1. 選擇**建立函數**。

**上傳函數程式碼 (主控台)**

1. 在**程式碼來源**窗格中選擇**上傳來源**。

1. 選擇 **.zip 檔案**。

1. 選擇**上傳**。

1. 在檔案選擇器中，選取 .zip 檔案，並選擇**開啟**。

1. 選擇**儲存**。

**若要設定函數記憶體和逾時 (主控台)**

1. 選取函數的**組態**索引標籤。

1. 在**一般組態**窗格中，選擇**編輯**。

1. 將**記憶體**設定為 256 MB，並將**逾時**設定為 15 秒。

1. 選擇**儲存**。

**若要設定日誌格式 (主控台)**

1. 選取函數的**組態**索引標籤。

1. 選取**監控和操作工具**。

1. 在**日誌組態**窗格中，選擇**編輯**。

1. 針對**記錄組態**，選取 **JSON**。

1. 選擇**儲存**。

------
#### [ AWS CLI ]

**建立函數 (AWS CLI)**
+ 從包含 `lambda_function.zip` 檔案的目錄執行以下命令。針對 `region` 參數，請將 `us-east-2` 取代為您建立 S3 儲存貯體的區域。

  ```
  aws lambda create-function --function-name EncryptPDF \
  --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \
  --runtime python3.12 --timeout 15 --memory-size 256 \
  --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-2 \
  --logging-config LogFormat=JSON
  ```

------

#### 設定 Amazon S3 觸發條件以調用函數
<a name="file-processing-app-deploy-manual-configure-s3-trigger"></a>

若要在將檔案上傳至來源儲存貯體時執行 Lambda 函數，您需要設定函數的觸發條件。可以使用主控台或 AWS CLI來設定 Amazon S3 觸發條件。

**重要**  
此程序會將 S3 儲存貯體設定為每次在儲存貯體中建立物件時即會調用您的函數。請務必僅在來源儲存貯體上進行設定。如果 Lambda 函數在進行調用的同一個儲存貯體中建立物件，則可以[在一個迴圈中連續調用](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)函數。這可能會導致未預期的費用向您的 收費 AWS 帳戶。

------
#### [ Console ]

**設定 Amazon S3 觸發條件 (主控台)**

1. 開啟 Lambda 主控台的[函數頁面](https://console.aws.amazon.com/lambda/home#/functions)，然後選擇您的函數 (`EncryptPDF`)。

1. 選擇 **Add trigger (新增觸發條件)**。

1. 選取 **S3**。

1. 在**儲存貯體**下，選取您的來源儲存貯體。

1. 在**事件類型**下，選取**所有物件建立事件**。

1. 在**遞迴調用**下，選取核取方塊，確認您了解不建議使用相同的 S3 儲存貯體進行輸入和輸出作業。您可以閱讀無伺服器園地中 [導致 Lambda 函數失控的遞迴模式](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)，進一步了解 Lambda 中的遞迴調用模式。

1. 選擇**新增**。

   當您使用 Lambda 主控台建立觸發條件時，Lambda 會自動建立[資源型政策](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html)，為您選取的服務授予調用函數的許可。

------
#### [ AWS CLI ]

**設定 Amazon S3 觸發條件 (AWS CLI)**

1. 為函式新增[資源型政策](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html)，允許 Amazon S3 來源儲存貯體在您新增檔案時調用函式。資源型政策陳述式提供叫用 函數的其他 AWS 服務 許可。若要授予 Amazon S3 調用函數的許可，請執行下列 CLI 命令。請務必以您自己的 AWS 帳戶 ID 取代 `source-account` 參數，並使用您自己的來源儲存貯體名稱。

   ```
   aws lambda add-permission --function-name EncryptPDF \
   --principal s3.amazonaws.com --statement-id s3invoke --action "lambda:InvokeFunction" \
   --source-arn arn:aws:s3:::amzn-s3-demo-bucket \
   --source-account 123456789012
   ```

   使用此命令定義的政策允許 Amazon S3 僅在來源儲存貯體上發生動作時調用函數。
**注意**  
雖然 S3 儲存貯體名稱全域唯一，但是在使用資源型政策時，最佳實務是指定儲存貯體必須屬於您的帳戶。這是因為如果您刪除儲存貯體，另一個 可以使用相同的 Amazon Resource Name (ARN) AWS 帳戶 建立儲存貯體。

1. 將下面的 JSON 儲存在名為 `notification.json` 的檔案中。套用至來源儲存貯體時，此 JSON 會設定儲存貯體，以便在每次新增新物件時傳送通知至 Lambda 函數。將 AWS 帳戶 Lambda 函數 ARN AWS 區域 中的數字 和 取代為您自己的帳號和區域。

   ```
   {
   "LambdaFunctionConfigurations": [
       {
         "Id": "EncryptPDFEventConfiguration",
         "LambdaFunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:EncryptPDF",
         "Events": [ "s3:ObjectCreated:Put" ]
       }
     ]
   }
   ```

1. 執行下列 CLI 命令，將您建立的 JSON 檔案中的通知設定套用至來源儲存貯體。用您自己的來源儲存貯體名稱取代 `amzn-s3-demo-bucket`。

   ```
   aws s3api put-bucket-notification-configuration --bucket amzn-s3-demo-bucket \
   --notification-configuration file://notification.json
   ```

   若要深入了解 `put-bucket-notification-configuration` 命令和 `notification-configuration` 選項，請參閱 *AWS CLI 命令參考*中的 [put-bucket-notification-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-notification-configuration.html)。

------

### 使用 部署資源 AWS SAM
<a name="file-processing-app-deploy-sam"></a>

在開始之前，先確保建置機器上已安裝 [Docker](https://docs.docker.com/get-docker/) 與[最新版本的 AWS SAMCLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)。

1. 在專案目錄中，將下列程式碼複製並貼到名為 `template.yaml` 的檔案中。取代預留位置儲存貯體名稱：
   + 對於來源儲存貯體，請以符合 [S3 儲存貯體命名規則](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)的任何名稱取代 `amzn-s3-demo-bucket`。
   + 對於目的地儲存貯體，請將取代 `amzn-s3-demo-bucket-encrypted` 為 `<source-bucket-name>-encrypted`，其中 `<source-bucket>` 是您為來源儲存貯體選擇的名稱。

   ```
   AWSTemplateFormatVersion: '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   
   Resources:
     EncryptPDFFunction:
       Type: AWS::Serverless::Function
       Properties:
         FunctionName: EncryptPDF
         Architectures: [x86_64]
         CodeUri: ./
         Handler: lambda_function.lambda_handler
         Runtime: python3.12
         Timeout: 15
         MemorySize: 256
         LoggingConfig:
           LogFormat: JSON
         Policies:
           - AmazonS3FullAccess
         Events:
           S3Event:
             Type: S3
             Properties:
               Bucket: !Ref PDFSourceBucket
               Events: s3:ObjectCreated:*
   
     PDFSourceBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: amzn-s3-demo-bucket
   
     EncryptedPDFBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: amzn-s3-demo-bucket-encrypted
   ```

    AWS SAM 範本會定義您為應用程式建立的資源。在此範例中，範本使用 `AWS::Serverless::Function` 類型定義 Lambda 函數，並使用 `AWS::S3::Bucket` 類型定義兩個 S3 儲存貯體。範本中指定的儲存貯體名稱是預留位置。使用 部署應用程式之前 AWS SAM，您需要編輯範本，以使用符合 [S3 儲存貯體命名規則的全域唯一名稱來重新命名儲存貯](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)體。如需進一步了解，請參閱[使用 部署資源 AWS SAM](#file-processing-app-deploy-sam)。

   該 Lambda 函數資源的定義使用 `S3Event` 事件屬性設定函數的觸發條件。此觸發條件會在來源儲存貯體中有物件建立時調用該函數。

   函數定義也會指定要連接到函數[執行角色](lambda-intro-execution-role.md)的 AWS Identity and Access Management (IAM) 政策。[AWS 受管政策](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) `AmazonS3FullAccess` 為該函數提供讀取和寫入物件至 Amazon S3 所需的許可。

1. 在您儲存 `template.yaml`、`lambda_function.py` 和 `requirements.txt` 檔案的目錄中執行以下命令。

   ```
   sam build --use-container
   ```

   此命令會收集應用程式的建置成品，並將它們放置在適當的格式和位置以進行部署。指定 `--use-container` 選項可在類似 Lambda 的 Docker 容器中建置函數。之所以要使用它，是為了讓您不必在本機機器上安裝 Python 3.12，即可進行建置。

   在建置過程中， 會在您以 範本中的 `CodeUri` 屬性指定的位置 AWS SAM 尋找 Lambda 函數程式碼。在此例中，我們將該位置設定為目前的目錄 (`./`)。

   如果`requirements.txt`檔案存在， AWS SAM 會使用它來收集指定的相依性。根據預設， 會使用函數程式碼和相依性 AWS SAM 建立 .zip 部署套件。您也可以選擇使用 [PackageType](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-packagetype) 屬性，將函數部署為容器映像。

1. 若要部署應用程式並建立 AWS SAM 範本中指定的 Lambda 和 Amazon S3 資源，請執行下列命令。

   ```
   sam deploy --guided
   ```

   使用 `--guided`旗標表示 AWS SAM 會顯示提示，引導您完成部署程序。對於此部署，請按 Enter 接受預設選項。

在部署過程中， 會在您的 中 AWS SAM 建立下列資源 AWS 帳戶：
+ 名為 的 An CloudFormation [stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-concepts.html#cfn-concepts-stacks) `sam-app`
+ 名稱為 `EncryptPDF` 的 Lambda 函數
+ 兩個 S3 儲存貯體，其中包含您在編輯`template.yaml` AWS SAM 範本檔案時選擇的名稱
+ 具有名稱格式為 `sam-app-EncryptPDFFunctionRole-2qGaapHFWOQ8` 的函數 IAM 執行角色

 AWS SAM 完成建立資源後，您應該會看到下列訊息：

```
Successfully created/updated stack - sam-app in us-east-2
```

## 測試應用程式
<a name="file-processing-app-test"></a>

若要測試應用程式，可將 PDF 檔案上傳至來源儲存貯體，並確認 Lambda 在目的地儲存貯體中建立加密的檔案版本。在此範例中，您可以使用 主控台或 手動測試， AWS CLI或使用提供的測試指令碼。

對於生產應用程式，您可以使用單元測試等傳統測試方法和技術，確認 Lambda 函數程式碼是否正常運作。最佳實務也是執行提供的測試指令碼中執行的測試，即執行使用真實雲端資源的整合測試。雲端的整合測試可確認基礎設施已正確部署，且事件會如預期在不同服務之間流動。如需詳細資訊，請參閱 [如何測試無伺服器函數和應用程式](testing-guide.md)。

### 手動測試應用程式
<a name="file-processing-app-test-manual"></a>

您可以將 PDF 檔案新增至 Amazon S3 來源儲存貯體，藉此以手動方式測試該函數。在將檔案新增至來源儲存貯體後，Lambda 函數應該能夠被自動調用，並且會產生經過加密的檔案版本並存放在目標儲存貯體中。

------
#### [ Console ]

**若要透過上傳檔案來測試應用程式 (主控台)**

1. 若要將 PDF 檔案上傳到 S3 儲存貯體，請執行以下操作：

   1. 開啟 Amazon S3 主控台的[儲存貯體](https://console.aws.amazon.com/s3/buckets)頁面，並選擇來源儲存貯體。

   1. 選擇**上傳**。

   1. 選擇**新增檔案**，然後使用檔案選擇器選擇您要上傳的 PDF 檔案。

   1. 選擇**開啟**，然後選擇**上傳**。

1. 透過執行下列操作，確認 Lambda 已在目標儲存貯體中儲存了經過加密的 PDF 檔案版本：

   1. 導覽回 Amazon S3 主控台的[儲存貯體](https://console.aws.amazon.com/s3/buckets)頁面，然後選擇目的地儲存貯體。

   1. 在**物件**窗格中，您現在應該會看到名稱格式為 `filename_encrypted.pdf` 的檔案 (其中 `filename.pdf` 是您上傳到來源儲存貯體的檔案名稱)。若要下載經過加密的 PDF 檔案，請選取檔案，然後選擇**下載**。

   1. 確認您可以使用 Lambda 函數用於保護下載之檔案的密碼 (`my-secret-password`) 開啟文檔。

------
#### [ AWS CLI ]

**若要透過上傳檔案 (AWS CLI) 測試應用程式**

1. 從包含要上傳之 PDF 檔案的目錄中，執行下列 CLI 命令。將 `--bucket` 參數取代為來源儲存貯體名稱。對於 `--key` 和 `--body` 參數，請使用測試檔案的檔案名稱。

   ```
   aws s3api put-object --bucket amzn-s3-demo-bucket --key test.pdf --body ./test.pdf
   ```

1. 確認函數已建立檔案的加密版本，並將其儲存到目標 S3 儲存貯體。執行以下 CLI 命令，將 `amzn-s3-demo-bucket-encrypted` 取代為您自己的目的地儲存貯體的名稱。

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-bucket-encrypted
   ```

   如果函數成功運作，則您會看到類似以下內容的輸出。您的目標儲存貯體應包含名稱格式為 `<your_test_file>_encrypted.pdf` 的檔案，其中 `<your_test_file>` 是您上傳的檔案名稱。

   ```
   {
       "Contents": [
           {
               "Key": "test_encrypted.pdf",
               "LastModified": "2023-06-07T00:15:50+00:00",
               "ETag": "\"7781a43e765a8301713f533d70968a1e\"",
               "Size": 2763,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

1. 若要下載 Lambda 儲存在目的地儲存貯體中的檔案，請執行下面的 CLI 命令。用目的地儲存貯體名稱取代 `--bucket`。針對 `--key` 參數，請使用檔案名稱 `<your_test_file>_encrypted.pdf`，其中 `<your_test_file>` 是上傳的測試檔案名稱。

   ```
   aws s3api get-object --bucket amzn-s3-demo-bucket-encrypted --key test_encrypted.pdf my_encrypted_file.pdf
   ```

   此命令會將檔案下載到您目前的目錄，並將其儲存為 `my_encrypted_file.pdf`。

1. 確認您可以使用 Lambda 函數用於保護下載之檔案的密碼 (`my-secret-password`) 開啟文檔。

------

### 使用自動化指令碼測試應用程式
<a name="file-processing-app-test-auto"></a>

在專案目錄中建立下列檔案：
+ `test_pdf_encrypt.py`：用於自動化測試應用程式的測試指令碼
+ `pytest.ini`：測試指令碼的組態檔案

展開下列各區段即可檢視程式碼，並進一步了解每個檔案的作用。

#### 自動化測試指令碼
<a name="file-processing-app-test-script"></a>

複製以下程式碼並貼到名稱為 `test_pdf_encrypt.py` 的檔案中。請務必取代預留位置儲存貯體名稱：
+ 在 `test_source_bucket_available` 函數中，將 `amzn-s3-demo-bucket` 取代為來源儲存貯體名稱。
+ 在 `test_encrypted_file_in_bucket` 函數中，以 `source-bucket-encrypted` 取代 `amzn-s3-demo-bucket-encrypted`，其中 `source-bucket>` 是來源儲存貯體的名稱。
+ 在 `cleanup` 函式中，以來源儲存貯體的名稱取代 `amzn-s3-demo-bucket`，並以目的地儲存貯體取代 `amzn-s3-demo-bucket-encrypted`。

```
import boto3
import json
import pytest
import time
import os

@pytest.fixture
def lambda_client():
    return boto3.client('lambda')
    
@pytest.fixture
def s3_client():
    return boto3.client('s3')

@pytest.fixture
def logs_client():
    return boto3.client('logs')

@pytest.fixture(scope='session')
def cleanup():
    # Create a new S3 client for cleanup
    s3_client = boto3.client('s3')

    yield
    # Cleanup code will be executed after all tests have finished

    # Delete test.pdf from the source bucket
    source_bucket = 'amzn-s3-demo-bucket'
    source_file_key = 'test.pdf'
    s3_client.delete_object(Bucket=source_bucket, Key=source_file_key)
    print(f"\nDeleted {source_file_key} from {source_bucket}")

    # Delete test_encrypted.pdf from the destination bucket
    destination_bucket = 'amzn-s3-demo-bucket-encrypted'
    destination_file_key = 'test_encrypted.pdf'
    s3_client.delete_object(Bucket=destination_bucket, Key=destination_file_key)
    print(f"Deleted {destination_file_key} from {destination_bucket}")
        

@pytest.mark.order(1)
def test_source_bucket_available(s3_client):
    s3_bucket_name = 'amzn-s3-demo-bucket'
    file_name = 'test.pdf'
    file_path = os.path.join(os.path.dirname(__file__), file_name)

    file_uploaded = False
    try:
        s3_client.upload_file(file_path, s3_bucket_name, file_name)
        file_uploaded = True
    except:
        print("Error: couldn't upload file")

    assert file_uploaded, "Could not upload file to S3 bucket"

    

@pytest.mark.order(2)
def test_lambda_invoked(logs_client):

    # Wait for a few seconds to make sure the logs are available
    time.sleep(5)

    # Get the latest log stream for the specified log group
    log_streams = logs_client.describe_log_streams(
        logGroupName='/aws/lambda/EncryptPDF',
        orderBy='LastEventTime',
        descending=True,
        limit=1
    )

    latest_log_stream_name = log_streams['logStreams'][0]['logStreamName']

    # Retrieve the log events from the latest log stream
    log_events = logs_client.get_log_events(
        logGroupName='/aws/lambda/EncryptPDF',
        logStreamName=latest_log_stream_name
    )

    success_found = False
    for event in log_events['events']:
        message = json.loads(event['message'])
        status = message.get('record', {}).get('status')
        if status == 'success':
            success_found = True
            break

    assert success_found, "Lambda function execution did not report 'success' status in logs."

@pytest.mark.order(3)
def test_encrypted_file_in_bucket(s3_client):
    # Specify the destination S3 bucket and the expected converted file key
    destination_bucket = 'amzn-s3-demo-bucket-encrypted'
    converted_file_key = 'test_encrypted.pdf'

    try:
        # Attempt to retrieve the metadata of the converted file from the destination S3 bucket
        s3_client.head_object(Bucket=destination_bucket, Key=converted_file_key)
    except s3_client.exceptions.ClientError as e:
        # If the file is not found, the test will fail
        pytest.fail(f"Converted file '{converted_file_key}' not found in the destination bucket: {str(e)}")

def test_cleanup(cleanup):
    # This test uses the cleanup fixture and will be executed last
    pass
```

自動化測試指令碼會執行三個測試函數，以確認應用程式的正確運作：
+ `test_source_bucket_available` 測試透過將測試 PDF 檔案上傳至儲存貯體，確認來源儲存貯體已成功建立。
+ 測試 `test_lambda_invoked` 會查詢函數的最新 CloudWatch Logs 日誌串流，以確認在上傳測試檔案後，該 Lambda 函數執行並報告成功。
+ 測試 `test_encrypted_file_in_bucket` 會確認目的地儲存貯體包含經過加密的檔案 `test_encrypted.pdf`。

執行所有這些測試後，指令碼會執行額外的清除步驟，以刪除來源和目的地儲存貯體中的 `test.pdf` 和 `test_encrypted.pdf` 檔案。

如同 AWS SAM 範本，此檔案中指定的儲存貯體名稱是預留位置。在執行測試之前，您需要編輯此檔案，修改為應用程式的真實儲存貯體名稱。如需進一步了解，請參閱[使用自動化指令碼測試應用程式](#file-processing-app-test-auto)

#### 測試指令碼組態檔案
<a name="file-processing-app-test-config"></a>

複製以下程式碼並貼到名稱為 `pytest.ini` 的檔案中。

```
[pytest]
markers =
    order: specify test execution order
```

這用於指定 `test_pdf_encrypt.py` 指令碼中測試的執行順序。

若要執行測試，請執行下列動作：

1. 確保本機環境中已安裝 `pytest` 模組。您可以透過執行以下命令安裝 `pytest`：

   ```
   pip install pytest
   ```

1. 在包含檔案 `test.pdf` 和 `test_pdf_encrypt.py` 的目錄中儲存名為 `pytest.ini` 的 PDF 檔案。

1. 開啟終端機或 Shell 程式，並從包含測試檔案的目錄執行以下命令。

   ```
   pytest -s -v
   ```

   測試完成後，輸出應如以下所示：

   ```
   ============================================================== test session starts =========================================================
   platform linux -- Python 3.12.2, pytest-7.2.2, pluggy-1.0.0 -- /usr/bin/python3
   cachedir: .pytest_cache
   hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/pdf_encrypt_app/.hypothesis/examples')
   Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
   rootdir: /home/pdf_encrypt_app, configfile: pytest.ini
   plugins: anyio-3.7.1, hypothesis-6.70.0, localserver-0.7.1, random-order-1.1.0
   collected 4 items
   
   test_pdf_encrypt.py::test_source_bucket_available PASSED
   test_pdf_encrypt.py::test_lambda_invoked PASSED
   test_pdf_encrypt.py::test_encrypted_file_in_bucket PASSED
   test_pdf_encrypt.py::test_cleanup PASSED
   Deleted test.pdf from amzn-s3-demo-bucket
   Deleted test_encrypted.pdf from amzn-s3-demo-bucket-encrypted
   
   
   =============================================================== 4 passed in 7.32s ==========================================================
   ```

## 後續步驟
<a name="file-processing-app-next-steps"></a>

現在您已完成此範例應用程式的建立。您可以使用提供的程式碼做為基礎，建立其他類型的檔案處理應用程式。您可以根據自己使用案例的檔案處理邏輯，修改 `lambda_function.py` 檔案中的程式碼。

許多典型的檔案處理使用案例都涉及影像處理。使用 Python 時，最常用的影像處理程式庫 (例如 [pillow](https://pypi.org/project/pillow/))，通常包含 C 或 C\$1\$1 元件。為了確保函數的部署套件與 Lambda 執行環境相容，請務必使用正確的來源分佈二進位檔。

使用 部署資源時 AWS SAM，您需要採取一些額外的步驟，在部署套件中包含正確的來源分佈。由於 AWS SAM 不會為與建置機器不同的平台安裝相依性，因此如果您的建置機器使用與 Lambda 執行環境不同的作業系統或架構，在 `requirements.txt` 檔案中指定正確的來源分佈 (`.whl` 檔案） 將無法運作。您應該執行下列其中一項操作：
+ 執行 `sam build` 時使用 `--use-container` 選項。當您指定此選項時， AWS SAM 會下載與 Lambda 執行環境相容的容器基礎映像，並使用該映像在 Docker 容器中建置函數的部署套件。若要進一步了解，請參閱 [Building a Lambda function inside of a provided container](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-build.html#using-sam-cli-build-options-container) 一節。
+ 使用正確的來源分佈二進位檔自行建置函數的 .zip 部署套件，並將 .zip 檔案儲存在範本中指定為 `CodeUri`的目錄中 AWS SAM 。若要進一步了解如何使用二進位分佈為 Python 建置 .zip 部署套件，請參閱[建立含相依項的 .zip 部署套件](python-package.md#python-package-create-dependencies)和[建立含原生程式庫的 .zip 部署套件](python-package.md#python-package-native-libraries)。

# 建立應用程式以執行排定的資料庫維護
<a name="scheduled-task-app"></a>

您可以使用 AWS Lambda 取代排程程序，例如自動化系統備份、檔案轉換和維護任務。在此範例中，您會建立一個無伺服器應用程式，用於透過刪除舊項目，對 DynamoDB 資料表執行定期的排定維護。該應用程式使用 EventBridge 排程器，對 Cron 排程調用 Lambda 函數。調用時，函數會查詢資料表中是否有超過一年的項目，並刪除它們。函數會在 CloudWatch Logs 中記錄刪除的每個項目。

若要實作此範例，首先需要建立 DynamoDB 資料表，並將其填入一些測試資料，以供函數查詢。然後，建立具有 EventBridge 排程器觸發條件的 Python Lambda 函數，並為該函數建立 IAM 執行角色，讓其能夠從資料表讀取和刪除項目。

![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/ExampleApps/cron_app.png)


**提示**  
如果您是 Lambda 的新手，建議您在建立此範例應用程式之前完成教學課程「[建立第一個 Lambda 函數](getting-started.md)」。

您可以使用 建立和設定資源，以手動部署您的應用程式 AWS 管理主控台。您也可以使用 AWS Serverless Application Model (AWS SAM) 來部署應用程式。 AWS SAM 是基礎設施即程式碼 (IaC) 工具。藉助 IaC，您無需手動建立資源，而是在程式碼中定義資源，然後便可自動部署資源。

如果您想要在部署此範例應用程式之前，進一步了解如何將 Lambda 與 IaC 搭配使用，請參閱[將 Lambda 搭配基礎設施即程式碼 (IaC)](foundation-iac.md)。

## 先決條件
<a name="scheduled-task-app-prereqs"></a>

在建立範例應用程式之前，請確保已安裝必要的命令列工具和程式。
+ **Python**

  為向用於測試應用程式的 DynamoDB 資料表填入資料，此範例使用 Python 指令碼和 CSV 檔案。請確定您的機器已安裝 Python 3.8 版或更新版本。
+ **AWS SAM CLI**

  如果您想要使用 建立 DynamoDB 資料表並部署範例應用程式 AWS SAM，則需要安裝 AWS SAM CLI。如需了解如何安裝，請參閱《AWS SAM 使用者指南》**中的[安裝指示](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)。
+ **AWS CLI**

  若要使用提供的 Python 指令碼填入測試資料表，則需要安裝並設定 AWS CLI。這是因為指令碼使用 適用於 Python (Boto3) 的 AWS SDK，需要存取您的 AWS Identity and Access Management (IAM) 登入資料。您也需要 AWS CLI 安裝 ，才能使用 來部署資源 AWS SAM。若要安裝 CLI，請參閱《AWS Command Line Interface 使用者指南》**中的[安裝指示](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
+ **Docker**

  若要使用 部署應用程式 AWS SAM，您的建置機器也必須安裝 Docker。請遵循 Docker 文件網站上 [Install Docker Engine](https://docs.docker.com/engine/install/) 一節的指示。

## 下載範例應用程式檔案
<a name="scheduled-task-app-download"></a>

若要建立範例資料庫和排定維護應用程式，您需要在專案目錄中建立下列檔案：

**範例資料庫檔案**
+ `template.yaml` - 可用來建立 DynamoDB 資料表的 AWS SAM 範本
+ `sample_data.csv`：CSV 檔案，其中包含要載入資料表的範例資料
+ `load_sample_data.py`：Python 指令碼，用於將 CSV 檔案中的資料寫入資料表

**排定維護應用程式檔案**
+ `lambda_function.py`：執行資料庫維護之 Lambda 函數的 Python 函數程式碼
+ `requirements.txt`：資訊清單檔案，用於定義 Python 函數程式碼所需的相依項
+ `template.yaml` - 可用來部署應用程式的 AWS SAM 範本

**測試檔案**
+ `test_app.py`：Python 指令碼，用於掃描資料表，以確認函數的成功運作，並輸出已存在超過一年的所有記錄

展開以下區段以檢視程式碼，並進一步了解每個檔案在建立和測試應用程式過程中的作用。若要在本機機器上建立這些檔案，請複製並貼上下面的程式碼。

### AWS SAM 範本 （範例 DynamoDB 資料表）
<a name="scheduled-task-app-table-yaml"></a>

複製以下程式碼並貼到名稱為 `template.yaml` 的檔案中。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for DynamoDB Table with Order_number as Partition Key and Date as Sort Key

Resources:
  MyDynamoDBTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      TableName: MyOrderTable
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: Order_number
          AttributeType: S
        - AttributeName: Date
          AttributeType: S
      KeySchema:
        - AttributeName: Order_number
          KeyType: HASH
        - AttributeName: Date
          KeyType: RANGE
      SSESpecification:
        SSEEnabled: true
      GlobalSecondaryIndexes:
        - IndexName: Date-index
          KeySchema:
            - AttributeName: Date
              KeyType: HASH
          Projection:
            ProjectionType: ALL
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true

Outputs:
  TableName:
    Description: DynamoDB Table Name
    Value: !Ref MyDynamoDBTable
  TableArn:
    Description: DynamoDB Table ARN
    Value: !GetAtt MyDynamoDBTable.Arn
```

**注意**  
AWS SAM 範本使用 的標準命名慣例`template.yaml`。此範例有兩個範本檔案：一個用於建立範例資料庫，另一個用於建立應用程式本身。將它們儲存在專案資料夾的單獨子目錄中。

此 AWS SAM 範本會定義您為測試應用程式所建立的 DynamoDB 資料表資源。該資料表的主索引鍵為 `Order_number`，排序索引鍵為 `Date`。為了讓該 Lambda 函數直接依日期尋找項目，我們還定義了名為 `Date-index` 的[全域次要索引](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html)。

若要進一步了解如何使用 `AWS::DynamoDB::Table` 資源建立和設定 DynamoDB 資料表，請參閱《AWS CloudFormation 使用者指南》**中的 [AWS::DynamoDB::Table](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html) 一節。

### 範例資料庫資料檔案
<a name="scheduled-task-app-csv-file"></a>

複製以下程式碼並貼到名稱為 `sample_data.csv` 的檔案中。

```
Date,Order_number,CustomerName,ProductID,Quantity,TotalAmount
2023-09-01,ORD001,Alejandro Rosalez,PROD123,2,199.98
2023-09-01,ORD002,Akua Mansa,PROD456,1,49.99
2023-09-02,ORD003,Ana Carolina Silva,PROD789,3,149.97
2023-09-03,ORD004,Arnav Desai,PROD123,1,99.99
2023-10-01,ORD005,Carlos Salazar,PROD456,2,99.98
2023-10-02,ORD006,Diego Ramirez,PROD789,1,49.99
2023-10-03,ORD007,Efua Owusu,PROD123,4,399.96
2023-10-04,ORD008,John Stiles,PROD456,2,99.98
2023-10-05,ORD009,Jorge Souza,PROD789,3,149.97
2023-10-06,ORD010,Kwaku Mensah,PROD123,1,99.99
2023-11-01,ORD011,Li Juan,PROD456,5,249.95
2023-11-02,ORD012,Marcia Oliveria,PROD789,2,99.98
2023-11-03,ORD013,Maria Garcia,PROD123,3,299.97
2023-11-04,ORD014,Martha Rivera,PROD456,1,49.99
2023-11-05,ORD015,Mary Major,PROD789,4,199.96
2023-12-01,ORD016,Mateo Jackson,PROD123,2,199.99
2023-12-02,ORD017,Nikki Wolf,PROD456,3,149.97
2023-12-03,ORD018,Pat Candella,PROD789,1,49.99
2023-12-04,ORD019,Paulo Santos,PROD123,5,499.95
2023-12-05,ORD020,Richard Roe,PROD456,2,99.98
2024-01-01,ORD021,Saanvi Sarkar,PROD789,3,149.97
2024-01-02,ORD022,Shirley Rodriguez,PROD123,1,99.99
2024-01-03,ORD023,Sofia Martinez,PROD456,4,199.96
2024-01-04,ORD024,Terry Whitlock,PROD789,2,99.98
2024-01-05,ORD025,Wang Xiulan,PROD123,3,299.97
```

此檔案包含一些範例測試資料，為標準逗號分隔值 (CSV) 格式，用於填入 DynamoDB 資料表。

### 用於載入範例資料的 Python 指令碼
<a name="scheduled-task-app-load-script"></a>

複製以下程式碼並貼到名稱為 `load_sample_data.py` 的檔案中。

```
import boto3
import csv
from decimal import Decimal

# Initialize the DynamoDB client
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('MyOrderTable') 
print("DDB client initialized.")

def load_data_from_csv(filename):
    with open(filename, 'r') as file:
        csv_reader = csv.DictReader(file)
        for row in csv_reader:
            item = {
                'Order_number': row['Order_number'],
                'Date': row['Date'],
                'CustomerName': row['CustomerName'],
                'ProductID': row['ProductID'],
                'Quantity': int(row['Quantity']),
                'TotalAmount': Decimal(str(row['TotalAmount']))
            }
            table.put_item(Item=item)
            print(f"Added item: {item['Order_number']} - {item['Date']}")

if __name__ == "__main__":
    load_data_from_csv('sample_data.csv')
    print("Data loading completed.")
```

此 Python 指令碼會先使用 適用於 Python (Boto3) 的 AWS SDK 來建立 DynamoDB 資料表的連線。然後，它會逐一查看 CSV 檔案 example-data 中的每一列，從該列建立項目，並使用 boto3 SDK 將項目寫入 DynamoDB 資料表。

### Python 函數程式碼
<a name="scheduled-task-app-function-code"></a>

複製以下程式碼並貼到名稱為 `lambda_function.py` 的檔案中。

```
import boto3
from datetime import datetime, timedelta
from boto3.dynamodb.conditions import Key, Attr
import logging

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

def lambda_handler(event, context):
    # Initialize the DynamoDB client
    dynamodb = boto3.resource('dynamodb')
    
    # Specify the table name
    table_name = 'MyOrderTable'
    table = dynamodb.Table(table_name)
    
    # Get today's date
    today = datetime.now()
    
    # Calculate the date one year ago
    one_year_ago = (today - timedelta(days=365)).strftime('%Y-%m-%d')
    
    # Scan the table using a global secondary index
    response = table.scan(
        IndexName='Date-index',
        FilterExpression='#date < :one_year_ago',
        ExpressionAttributeNames={
            '#date': 'Date'
        },
        ExpressionAttributeValues={
            ':one_year_ago': one_year_ago
        }
    )
    
     # Delete old items
    with table.batch_writer() as batch:
        for item in response['Items']:
            Order_number = item['Order_number']
            batch.delete_item(
                Key={
                    'Order_number': Order_number,
                    'Date': item['Date']
                }
            )
            logger.info(f'deleted order number {Order_number}')
    
    # Check if there are more items to scan
    while 'LastEvaluatedKey' in response:
        response = table.scan(
            IndexName='DateIndex',
            FilterExpression='#date < :one_year_ago',
            ExpressionAttributeNames={
                '#date': 'Date'
            },
            ExpressionAttributeValues={
                ':one_year_ago': one_year_ago
            },
            ExclusiveStartKey=response['LastEvaluatedKey']
        )
        
        # Delete old items
        with table.batch_writer() as batch:
            for item in response['Items']:
                batch.delete_item(
                    Key={
                        'Order_number': item['Order_number'],
                        'Date': item['Date']
                    }
                )
    
    return {
        'statusCode': 200,
        'body': 'Cleanup completed successfully'
    }
```

該 Python 函數的程式碼包含 Lambda 在調用該函數時執行的[處理常式](python-handler.md)函數 (`lambda_handler`)。

當 EventBridge 排程器調用函數時，它會使用 適用於 Python (Boto3) 的 AWS SDK 來建立 DynamoDB 資料表的連線，以執行排定的維護任務。然後，它會使用 Python 程式庫 `datetime` 來計算距離當日一年的日期，然後再掃描資料表中是否有早於此日期的項目並刪除它們。

請注意，DynamoDB 查詢和掃描操作的回應大小上限為 1 MB。如果回應大於 1 MB，DynamoDB 會對資料進行分頁，並在回應中傳回 `LastEvaluatedKey` 元素。為了確保函數處理資料表中的所有記錄，我們會檢查此索引鍵是否存在，然後繼續從上次評估的位置執行資料表掃描，直到掃描完整個資料表為止。

### `requirements.txt` 資訊清單檔案
<a name="scheduled-task-app-dependencies"></a>

複製以下程式碼並貼到名稱為 `requirements.txt` 的檔案中。

```
boto3
```

在此範例中，函數程式碼只包含一個不屬於標準 Python 程式庫的相依項，即函數用於掃描 DynamoDB 資料表和刪除項目的適用於 Python 的 SDK (Boto3)。

**注意**  
Lambda 執行時期包含適用於 Python 的 SDK (Boto3) 版本，因此程式碼無需將 Boto3 新增至函數的部署套件即可執行。不過，為了維持對函數相依項的完整控制，並避免版本不一致可能造成的問題，Python 的最佳實務是在函數的部署套件中包含所有函數相依項。如需進一步了解，請參閱[Python 中的執行期相依項](python-package.md#python-package-dependencies)。

### AWS SAM 範本 （排程維護應用程式）
<a name="scheduled-task-app-table-yaml"></a>

複製以下程式碼並貼到名稱為 `template.yaml` 的檔案中。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for Lambda function and EventBridge Scheduler rule

Resources:
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ScheduledDBMaintenance
      CodeUri: ./
      Handler: lambda_function.lambda_handler
      Runtime: python3.11
      Architectures:
        - x86_64
      Events:
        ScheduleEvent:
          Type: ScheduleV2
          Properties:
            ScheduleExpression: cron(0 3 1 * ? *)
            Description: Run on the first day of every month at 03:00 AM
      Policies:
        - CloudWatchLogsFullAccess
        - Statement:
            - Effect: Allow
              Action:
                - dynamodb:Scan
                - dynamodb:BatchWriteItem
              Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/MyOrderTable'

  LambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${MyLambdaFunction}
      RetentionInDays: 30

Outputs:
  LambdaFunctionName:
    Description: Lambda Function Name
    Value: !Ref MyLambdaFunction
  LambdaFunctionArn:
    Description: Lambda Function ARN
    Value: !GetAtt MyLambdaFunction.Arn
```

**注意**  
AWS SAM 範本使用 的標準命名慣例`template.yaml`。此範例有兩個範本檔案：一個用於建立範例資料庫，另一個用於建立應用程式本身。將它們儲存在專案資料夾的單獨子目錄中。

此 AWS SAM 範本會定義您應用程式的資源。我們使用 `AWS::Serverless::Function` 資源定義 Lambda 函數。EventBridge 排程器排程和調用 Lambda 函數的觸發條件乃使用類型為 `ScheduleV2` 之此資源的 `Events` 屬性建立。若要進一步了解如何在 AWS SAM 範本中定義 EventBridge 排程器排程，請參閱《 *AWS Serverless Application Model 開發人員指南*》中的 [ScheduleV2](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedulev2.html)。

除了 Lambda 函數和 EventBridge 排程器排程外，我們還需要為該函數定義一個 CloudWatch 日誌群組，以將已刪除項目的記錄傳送至其中。

### 測試指令碼
<a name="scheduled-task-app-test-script"></a>

複製以下程式碼並貼到名稱為 `test_app.py` 的檔案中。

```
import boto3
from datetime import datetime, timedelta
import json

# Initialize the DynamoDB client
dynamodb = boto3.resource('dynamodb')

# Specify your table name
table_name = 'YourTableName'
table = dynamodb.Table(table_name)

# Get the current date
current_date = datetime.now()

# Calculate the date one year ago
one_year_ago = current_date - timedelta(days=365)

# Convert the date to string format (assuming the date in DynamoDB is stored as a string)
one_year_ago_str = one_year_ago.strftime('%Y-%m-%d')

# Scan the table
response = table.scan(
    FilterExpression='#date < :one_year_ago',
    ExpressionAttributeNames={
        '#date': 'Date'
    },
    ExpressionAttributeValues={
        ':one_year_ago': one_year_ago_str
    }
)

# Process the results
old_records = response['Items']

# Continue scanning if we have more items (pagination)
while 'LastEvaluatedKey' in response:
    response = table.scan(
        FilterExpression='#date < :one_year_ago',
        ExpressionAttributeNames={
            '#date': 'Date'
        },
        ExpressionAttributeValues={
            ':one_year_ago': one_year_ago_str
        },
        ExclusiveStartKey=response['LastEvaluatedKey']
    )
    old_records.extend(response['Items'])

for record in old_records:
    print(json.dumps(record))

# The total number of old records should be zero.
print(f"Total number of old records: {len(old_records)}")
```

此測試指令碼使用 適用於 Python (Boto3) 的 AWS SDK 建立 DynamoDB 資料表的連線，並掃描超過一年的項目。為了讓您能夠確認該 Lambda 函數是否已成功執行，在測試結束時，該函數會列印資料表中存在時間超過一年之記錄的數目。如果 Lambda 函數成功執行，則資料表中的舊記錄數目應為零。

## 建立範例 DynamoDB 資料表並填入資料
<a name="scheduled-task-app-create-table"></a>

若要測試該排定維護應用程式，請先建立 DynamoDB 資料表，並填入一些範例資料。您可以使用 AWS 管理主控台 手動或使用 AWS SAM自動建立資料表。建議您使用 AWS SAM ，使用幾個 AWS CLI 命令快速建立和設定資料表。

------
#### [ Console ]

**若要建立 DynamoDB 資料表**

1. 開啟 DynamoDB 主控台的[資料表](https://console.aws.amazon.com/dynamodbv2/home#tables)頁面。

1. 選擇**建立資料表**。

1. 執行下列動作來建立資料表：

   1. 在**資料表詳細資訊**下，對於**資料表名稱**，輸入 **MyOrderTable**。

   1. 對於**分割區索引鍵**，輸入 **Order\$1number** 並保持類型設定為**字串**。

   1. 對於**排序索引鍵**，輸入 **Date** 並保持類型設定為**字串**。

   1. 將**資料表設定**保持為**預設設定**，然後選擇**建立資料表**。

1. 當資料表完成建立且其**狀態**顯示為**作用中**時，請執行下列動作來建立全域次要索引 (GSI)。該應用程式將使用此 GSI 直接依日期搜尋項目，以決定要刪除的項目。

   1. 從資料表清單中選擇 **MyOrderTable**。

   1. 選擇**索引**索引標籤。

   1. 在**全域次要索引**下，選擇**建立索引**。

   1. 在**索引詳細資訊**下，輸入 **Date** 作為**分割區索引鍵**，並保持**資料類型**設定為**字串**。

   1. 對於 **Index name** (索引名稱)，輸入 **Date-index**。

   1. 保持所有其他參數的預設設定，捲動至頁面底部，然後選擇**建立索引**。

------
#### [ AWS SAM ]

**若要建立 DynamoDB 資料表**

1. 導覽至儲存 DynamoDB 資料表 `template.yaml` 檔案的資料夾。請注意，此範例使用兩個 `template.yaml` 檔案。請確定它們儲存在不同的子資料夾中，而且您位於包含範本的資料夾中，這樣才能建立 DynamoDB 資料表。

1. 執行下列命令。

   ```
   sam build
   ```

   此命令會收集要部署之資源的建置成品，並將它們放置在適當的格式和位置以進行部署。

1. 執行以下命令，以建立 `template.yaml` 檔案中指定的 DynamoDB 資源。

   ```
   sam deploy --guided
   ```

   使用 `--guided`旗標表示 AWS SAM 會顯示提示，引導您完成部署程序。在此部署中，輸入 **cron-app-test-db** 的 `Stack name`，並使用 Enter 鍵接受所有其他選項的預設值。

   當 AWS SAM 完成建立 DynamoDB 資源時，您應該會看到下列訊息。

   ```
   Successfully created/updated stack - cron-app-test-db in us-west-2
   ```

1. 此外，您可以透過開啟 DynamoDB 主控台的[資料表](https://console.aws.amazon.com/dynamodbv2/home#tables)頁面，進一步確認該 DynamoDB 資料表已成功建立。您應該會看到名為 `MyOrderTable` 的資料表。

------

建立資料表之後，您接下來需要新增一些範例資料來測試應用程式。您先前下載的 CSV 檔案 `sample_data.csv` 包含一些範例項目，其中包括訂單編號、日期，以及客戶和訂單資訊。使用提供的 python 指令碼 `load_sample_data.py`，將這些資料新增至資料表。

**若要將範例資料新增至資料表**

1. 導覽至包含檔案 `sample_data.csv` 和 `load_sample_data.py` 的目錄。如果這兩個檔案位於不同的目錄中，請將它們移動至同一位置。

1. 透過執行以下命令，建立用於執行該指令碼的 Python 虛擬環境。建議您使用虛擬環境，因為在後續步驟中，您需要安裝 適用於 Python (Boto3) 的 AWS SDK。

   ```
   python -m venv venv
   ```

1. 執行以下命令以啟用該虛擬環境：

   ```
   source venv/bin/activate
   ```

1. 執行以下命令，在虛擬環境中安裝適用於 Python 的 SDK (Boto3)。該指令碼使用此程式庫連線到 DynamoDB 資料表和新增項目。

   ```
   pip install boto3
   ```

1. 執行以下命令，以執行指令碼來填入資料表。

   ```
   python load_sample_data.py
   ```

   如果指令碼執行成功，則它會在載入每個項目時將其列印到主控台並報告 `Data loading completed`。

1. 執行以下命令以停用該虛擬環境：

   ```
   deactivate
   ```

1. 您可以透過進行以下操作，以確認資料是否已載入 DynamoDB 資料表：

   1. 開啟 DynamoDB 主控台的[探索項目](https://console.aws.amazon.com/dynamodbv2/home#item-explorer)頁面，然後選擇相應資料表 (`MyOrderTable`)。

   1. 在**傳回的項目**窗格中，您應該會看到指令碼新增至資料表的 CSV 檔案中的 25 個項目。

## 建立排定維護應用程式
<a name="scheduled-task-app-create-app"></a>

您可以使用 AWS 管理主控台 或使用 逐步建立和部署此範例應用程式的資源 AWS SAM。在生產環境中，我們建議您使用 Infrustracture-as-Code (IaC) 工具 AWS SAM ，例如 ，在不使用手動程序的情況下重複部署無伺服器應用程式。

在此範例中，請依照主控台指示了解如何分別設定每個 AWS 資源，或依照 AWS SAM 指示使用 AWS CLI 命令快速部署應用程式。

------
#### [ Console ]

**使用 建立 函數 AWS 管理主控台**

首先，建立包含基本入門程式碼的函數。然後，您可以直接在 Lambda 程式碼編輯器中複製和貼上的程式碼，或以 `.zip` 套件的形式上傳程式碼，從而以自己的函數程式碼取代此程式碼。針對此任務，建議複製並貼上程式碼。

1. 開啟 Lambda 主控台中的 [函數頁面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 選擇**建立函數**。

1. 選擇 **Author from scratch** (從頭開始撰寫)。

1. 在**基本資訊**下，請執行下列動作：

   1. 針對**函數名稱**，請輸入 `ScheduledDBMaintenance`。

   1. 針對**執行時期**，選擇最新的 Python 版本。

   1. 對於 **Architecture** (架構)，選擇 **x86\$164**。

1. 選擇**建立函數**。

1. 建立函數後，您可以使用提供的函數程式碼來設定函數。

   1. 在**程式碼來源**窗格中，以您先前儲存的 `lambda_function.py` 檔案中的 Python 函數程式碼取代 Lambda 建立的 Hello world 程式碼。

   1. 在 **DEPLOY** 區段中，選擇**部署**以更新函數的程式碼：  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

**若要設定函數記憶體和逾時 (主控台)**

1. 選取函數的**組態**索引標籤。

1. 在**一般組態**窗格中，選擇**編輯**。

1. 將**記憶體**設定為 256 MB，並將**逾時**設定為 15 秒。如果您處理的是具有許多記錄的大型資料表，例如生產環境中的資料表，那麼可以考慮將**逾時**設定為較大的數字。這可讓函數有更多時間掃描和清理資料庫。

1. 選擇**儲存**。

**若要設定日誌格式 (主控台)**

您可以設定 Lambda 函數，以非結構化文字或 JSON 格式輸出日誌。建議您使用 JSON 格式的日誌，這樣搜尋和篩選日誌資料可以更輕鬆。若要進一步了解 Lambda 日誌組態選項，請參閱[設定 Lambda 函數的進階日誌記錄控制項](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)。

1. 選取函數的**組態**索引標籤。

1. 選取**監控和操作工具**。

1. 在**日誌組態**窗格中，選擇**編輯**。

1. 針對**記錄組態**，選取 **JSON**。

1. 選擇**儲存**。

**若要設定 IAM 許可**

若要向該函數提供讀取和刪除 DynamoDB 項目所需的許可，您需要向該函數的[執行角色](lambda-intro-execution-role.md)新增政策，以定義必要的許可。

1. 開啟**組態**索引標籤，然後從左側導覽列中選擇**許可**。

1. 在**執行角色**下面，選擇角色名稱。

1. 在 IAM 主控台中，選擇**新增許可**，然後選擇**建立內嵌政策**。

1. 使用 JSON 編輯器並輸入以下政策：  
****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "dynamodb:Scan",
                   "dynamodb:DeleteItem",
                   "dynamodb:BatchWriteItem"
               ],
               "Resource": "arn:aws:dynamodb:*:*:table/MyOrderTable"
           }
       ]
   }
   ```

1. 將政策命名為 **DynamoDBCleanupPolicy**，然後建立它。

**若要將 EventBridge 排程器設定為觸發條件 (主控台)**

1. 開啟 [EventBridge 主控台](https://console.aws.amazon.com/events/home)。

1. 在左側導覽窗格中，選擇**排程器**區段下的**排程器**。

1. 選擇**建立排程**。

1. 執行下列操作以配置排程：

   1. 在**排程名稱**下，輸入排程的名稱 (例如 **DynamoDBCleanupSchedule**)。

   1. 在**排程模式**下，選擇**週期性排程**。

   1. 對於**排程類型**，保留預設設定**以 Cron 為基礎的排程**，然後輸入以下排程詳細資訊：
      + **分鐘**：**0**
      + **小時**：**3**
      + **月曆日**：**1**
      + **月**：**\$1**
      + **一週當中的天**：**?**
      + **年**：**\$1**

      評估時，此 Cron 表達式會在每個月第一天的上午 03:00 執行。

   1. 對於**彈性時段**，選擇**關閉**。

1. 選擇**下一步**。

1. 執行以下操作以設定 Lambda 函數的觸發條件：

   1. 在**目標詳細資訊**窗格中，將**目標 API** 設定為**範本目標**，然後選取 **AWS Lambda 調用**。

   1. 在**調用**下，從下拉式清單中選取 Lambda 函數 (`ScheduledDBMaintenance`)。

   1. 讓**承載**保留空白，然後選擇**下一步**。

   1. 向下捲動至**許可**，然後選取**為此排程建立新角色**。當您使用主控台建立新的 EventBridge 排程器排程時，EventBridge 排程器會建立新的政策，其中包含該排程調用函數所需的必要許可。如需有關管理排程許可的詳細資訊，請參閱《EventBridge 排程器使用者指南》**中的 [Cron-based schedules](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html#cron-based) 一節。

   1. 選擇**下一步**。

1. 檢閱設定，然後選擇**建立排程**，以完成排程和 Lambda 觸發條件的建立。

------
#### [ AWS SAM ]

**使用 部署應用程式 AWS SAM**

1. 導覽至儲存應用程式 `template.yaml` 檔案的資料夾。請注意，此範例使用兩個 `template.yaml` 檔案。請確定它們儲存在不同的子資料夾中，而且您位於包含範本的資料夾中，這樣才能建立應用程式。

1. 將您先前下載的檔案 `lambda_function.py` 和 `requirements.txt` 複製到相同的資料夾。 AWS SAM 範本中指定的程式碼位置為 `./`，表示目前位置。當您嘗試部署應用程式時， AWS SAM 將在此資料夾中搜尋 Lambda 函數程式碼。

1. 執行下列命令。

   ```
   sam build --use-container
   ```

   此命令會收集要部署之資源的建置成品，並將它們放置在適當的格式和位置以進行部署。指定 `--use-container` 選項可在類似 Lambda 的 Docker 容器中建置函數。之所以要使用它，是為了讓您不必在本機機器上安裝 Python 3.12，即可進行建置。

1. 執行以下命令，以建立 `template.yaml` 檔案中指定的 Lambda 和 EventBridge 排程器資源。

   ```
   sam deploy --guided
   ```

   使用 `--guided`旗標表示 AWS SAM 會顯示提示，引導您完成部署程序。在此部署中，輸入 **cron-maintenance-app** 的 `Stack name`，並使用 Enter 鍵接受所有其他選項的預設值。

   當 AWS SAM 完成建立 Lambda 和 EventBridge 排程器資源時，您應該會看到下列訊息。

   ```
   Successfully created/updated stack - cron-maintenance-app in us-west-2
   ```

1. 此外，您可以透過開啟 Lambda 主控台的[函數](https://console.aws.amazon.com/lambda/home#/functions)頁面，進一步確認該 Lambda 函數已成功建立。您應該會看到名為 `ScheduledDBMaintenance` 的函數。

------

## 測試應用程式
<a name="scheduled-task-app-test-app"></a>

 若要測試排程是否能夠正確觸發函數，以及函數是否能夠正確清除資料庫中的記錄，您可以暫時修改排程，以在特定時間執行一次。然後，您可以再次執行 `sam deploy`，將週期性排程重設為每月執行一次。

**使用 執行應用程式 AWS 管理主控台**

1. 導覽回 EventBridge 排程器主控台頁面。

1. 選擇相應排程，然後選擇**編輯**。

1. 在**排程模式**區段的**週期**下，選擇**一次性排程**。

1.  將調用時間設定為幾分鐘後並檢閱設定，然後選擇**儲存**。

 在排程執行並調用其目標之後，您可以執行 `test_app.py` 指令碼，以確認函數已成功從 DynamoDB 資料表移除所有舊記錄。

**若要使用 Python 指令碼驗證舊記錄是否已被刪除**

1.  在命令列中，導覽至儲存 `test_app.py` 的資料夾。

1. 執行指令碼。

   ```
   python test_app.py
   ```

    如果成功，您就會看到以下輸出。

   ```
   Total number of old records: 0
   ```

## 後續步驟
<a name="scheduled-task-app-next-steps"></a>

 您現在可以根據自己特定的應用程式需求，修改 EventBridge 排程器排程。EventBridge Sch排程器支援以下排程表達式：cron、rate 和 one-time schedules。

 如需有關 EventBridge 排程器排程表達式的詳細資訊，請參閱 *EventBridge Scheduler User Guide* 中的 [Schedule types](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)。*IAM User Guide* 中的 [Access Management](https://docs.aws.amazon.com/IAM/latest/UserGuide/access.html) 

# 使用 Lambda 耐久函數建立訂單處理系統
<a name="order-processing-app"></a>

**注意**  
NEED：新增顯示 API Gateway、耐用函數工作流程和支援服務的架構圖 (DynamoDB、EventBridge)

## 先決條件
<a name="order-processing-prerequisites"></a>
+ AWS CLI 已安裝和設定
+ 需求：特定耐用函數需求

## 建立來源碼檔案
<a name="order-processing-source"></a>

在專案目錄中建立下列檔案：
+ `lambda_function.py` - 函數程式碼
+ `requirements.txt` - 相依性資訊清單

### 函式程式碼
<a name="order-processing-function-code"></a>

```
# NEED: Verify correct imports
import boto3
import json

def lambda_handler(event, context):
    # NEED: Verify DurableContext syntax
    durable = context.durable
    
    try:
        # Validate and store order
        order = await durable.step('validate', async () => {
            return validate_order(event['order'])
        })
        
        # Process payment
        # NEED: Verify wait syntax
        await durable.wait(/* wait configuration */)
        
        # Additional steps
        # NEED: Complete implementation
        
    except Exception as e:
        # NEED: Error handling patterns
        raise e

def validate_order(order_data):
    # NEED: Implementation
    pass
```

### 要求檔案
<a name="order-processing-requirements"></a>

```
# NEED: List of required packages
```

## 部署應用程式
<a name="order-processing-deploy"></a>

### 為訂單建立 DynamoDB 資料表
<a name="order-processing-dynamodb"></a>

1. 請在 [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) 開啟 DynamoDB 主控台。

1. 選擇**建立資料表**

1. 針對**資料表名稱**，輸入 **Orders**

1. 針對**分割區索引鍵**，輸入 **orderId**

1. 將其他設定保留為預設值

1. 選擇**建立資料表**

### 建立 Lambda 函數
<a name="order-processing-lambda"></a>

1. 在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 Lambda 主控台

1. 選擇 **Create function (建立函數)**

1. **從頭選取作者**

1. 在**函數名稱**輸入 **ProcessOrder**

1. 針對**執行期**，選擇您偏好的執行期

1. NEED：新增耐用函數特定的組態

1. 選擇 **Create function (建立函數)**

### 建立 API Gateway 端點
<a name="order-processing-apigateway"></a>

1. 在 https：//[https://console.aws.amazon.com/apigateway/](https://console.aws.amazon.com/apigateway/) 開啟 API Gateway 主控台

1. 選擇**建立 API**

1. 選取 **HTTP API**

1. 選擇**建置**

1. 新增與您的 Lambda 函數的整合

1. 設定訂單處理的路由

1. 部署 API

## 測試應用程式
<a name="order-processing-test"></a>

提交測試順序：

```
{
    "orderId": "12345",
    "items": [
        {
            "productId": "ABC123",
            "quantity": 1
        }
    ]
}
```

NEED：新增持久性函數的特定監控指示

## 後續步驟
<a name="order-processing-next-steps"></a>

### 新增商業邏輯
<a name="order-processing-business-logic"></a>

實作庫存管理：

```
async def check_inventory(order):
    # Add inventory check logic
    pass
```

新增價格計算：

```
async def calculate_total(order):
    # Add pricing logic
    pass
```

### 改善錯誤處理
<a name="order-processing-error-handling"></a>

新增補償邏輯：

```
async def reverse_payment(order):
    # Add payment reversal logic
    pass
```

處理訂單取消：

```
async def cancel_order(order):
    # Add cancellation logic
    pass
```

### 整合外部系統
<a name="order-processing-integrations"></a>

```
async def notify_shipping_provider(order):
    # Add shipping integration
    pass

async def send_customer_notification(order):
    # Add notification logic
    pass
```

### 增強監控
<a name="order-processing-monitoring"></a>
+ 建立 CloudWatch 儀表板
+ 設定訂單處理時間的指標
+ 設定延遲訂單的提醒