

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

# 建立 Lambda 函數來評估 Lambda Hooks 的資源
<a name="lambda-hooks-create-lambda-function"></a>

CloudFormation Lambda Hooks 可讓您根據自己的自訂程式碼評估 CloudFormation 和 AWS 雲端控制 API 操作。您的勾點可以封鎖操作以繼續，或向發起人發出警告，並允許操作繼續。當您建立 Lambda Hook 時，您可以將其設定為攔截和評估下列 CloudFormation 操作：
+ 資源操作
+ 堆疊操作
+ 變更集操作

**Topics**
+ [開發 Lambda 勾點](#lambda-hooks-create-lambda-function-develop)
+ [使用 Lambda Hooks 評估資源操作](#lambda-hooks-create-lambda-function-resource)
+ [使用 Lambda Hooks 評估堆疊操作](#lambda-hooks-create-lambda-function-stack)
+ [使用 Lambda Hooks 評估變更集操作](#lambda-hooks-create-lambda-function-change-set)

## 開發 Lambda 勾點
<a name="lambda-hooks-create-lambda-function-develop"></a>

當 Hooks 調用 Lambda 時，Lambda 最多會等待 30 秒來評估輸入。Lambda 將傳回 JSON 回應，指出勾點是否成功或失敗。

**Topics**
+ [請求輸入](#lambda-hooks-create-lambda-function-request-input)
+ [回應輸入](#lambda-hooks-create-lambda-function-request-response)
+ [範例](#lambda-hooks-create-lambda-function-request-example)

### 請求輸入
<a name="lambda-hooks-create-lambda-function-request-input"></a>

傳遞至 Lambda 函數的輸入取決於勾點目標操作 （範例：堆疊、資源或變更集）。

### 回應輸入
<a name="lambda-hooks-create-lambda-function-request-response"></a>

為了在請求成功或失敗時與 Hooks 通訊，您的 Lambda 函數需要傳回 JSON 回應。

以下是 Hooks 預期回應的範例形狀：

```
{ 
  "hookStatus": "SUCCESS" or "FAILED" or "IN_PROGRESS", 
  "errorCode": "NonCompliant" or "InternalFailure"
  "message": String, 
  "clientRequestToken": String,
  "callbackContext": None, 
  "callbackDelaySeconds": Integer,
  "註釋": [
    {
      "annotationName": String,
      "status": "PASSED" or "FAILED" or "SKIPPED",
      "statusMessage": String,
      "remediationMessage": String,
      "remediationLink": String,
      "severityLevel": "INFORMATIONAL" or "LOW" or "MEDIUM" or "HIGH" or "CRITICAL"
    }
  ]
}
```

hookStatus  <a name="lambda-hook-response-hookstatus"></a>
勾點的狀態。此為必要欄位。  
*有效值*： (`SUCCESS` \$1 `FAILED` \$1 `IN_PROGRESS`)  
勾點可以傳回 `IN_PROGRESS` 3 次。如果未傳回任何結果，勾點將會失敗。對於 Lambda 勾點，這表示您的 Lambda 函數最多可叫用 3 次。

errorCode  <a name="lambda-hook-response-errorcode"></a>
顯示操作是否已評估並判斷為無效，或勾點內是否發生錯誤，以防止評估。如果勾點失敗，則此欄位為必要欄位。  
*有效值*：(`NonCompliant` \$1 `InternalFailure`)

message  <a name="lambda-hook-response-message"></a>
呼叫者的訊息，說明勾點成功或失敗的原因。  
評估 CloudFormation 操作時，此欄位會截斷為 4096 個字元。  
評估 Cloud Control API 操作時，此欄位會截斷為 1024 個字元。

clientRequestToken  <a name="lambda-hook-response-clientrequesttoken"></a>
做為 Hook 請求輸入而提供的請求字符。此為必要欄位。

callbackContext  <a name="lambda-hook-response-callbackcontext"></a>
如果您指出 `hookStatus` 是 `IN_PROGRESS` ，則會傳遞在叫用 Lambda 函數時作為輸入提供的額外內容。

callbackDelaySeconds  <a name="lambda-hook-response-callbackdelayseconds"></a>
Hooks 應等待多久才再次叫用此 Hook。

註釋  <a name="lambda-hook-response-annotations"></a>
註釋物件陣列，可提供進一步的詳細資訊和修補指引。    
annotationName  
註釋的識別符。  
status  
勾點調用狀態。當註釋代表具有類似 Guard 規則的通過/失敗評估的邏輯時，這很有幫助。  
*有效值*： (`PASSED` \$1 `FAILED` \$1 `SKIPPED`)  
StatusMessage  
特定狀態的說明。  
remediationMessage  
修正`FAILED`狀態的建議。例如，如果資源缺少加密，您可以陳述如何將加密新增至資源組態。  
remediationLink  
用於其他修補指導的 HTTP URL。  
severityLevel  
定義與此類型的任何違規相關的相對風險。將嚴重性等級指派給 Hook 調用結果時，您可以參考 AWS Security Hub CSPM [嚴重性架構](https://docs.aws.amazon.com/securityhub/latest/userguide/asff-required-attributes.html#Severity)做為如何建構有意義的嚴重性類別的範例。  
*有效值*： (`INFORMATIONAL` \$1 `LOW` \$1 `MEDIUM` \$1 `HIGH` \$1 `CRITICAL`)

### 範例
<a name="lambda-hooks-create-lambda-function-request-example"></a>

以下是成功回應的範例：

```
{ 
  "hookStatus": "SUCCESS",
  "message": "compliant",
  "clientRequestToken": "123avjdjk31"  
}
```

以下是失敗回應的範例：

```
{ 
  "hookStatus": "FAILED",
  "errorCode": "NonCompliant",
  "message": "S3 Bucket Versioning must be enabled.",
  "clientRequestToken": "123avjdjk31"
 }
```

## 使用 Lambda Hooks 評估資源操作
<a name="lambda-hooks-create-lambda-function-resource"></a>

每當您建立、更新或刪除資源時，即視為資源操作。例如，如果您執行更新建立新資源的 CloudFormation 堆疊，表示您已完成資源操作。當您使用 Cloud Control API 建立、更新或刪除資源時，這也被視為資源操作。您可以將 CloudFormation Lambda Hook 設定為 Hook `TargetOperations`組態中的目標`RESOURCE`和`CLOUD_CONTROL`操作。

**注意**  
只有在使用來自 Cloud Control API `delete-resource`或 CloudFormation 的操作觸發程序刪除資源時，才會叫用 `delete` Hook 處理常式`delete-stack`。

**Topics**
+ [Lambda Hook 資源輸入語法](#lambda-hooks-create-lambda-function-resource-input)
+ [Lambda Hook 資源變更輸入範例](#lambda-hooks-create-lambda-function-resource-example)
+ [資源操作的 Lambda 函數範例](#lambda-hooks-create-lambda-function-resource-example-function)

### Lambda Hook 資源輸入語法
<a name="lambda-hooks-create-lambda-function-resource-input"></a>

當您的 Lambda 被叫用進行資源操作時，您將會收到 JSON 輸入，其中包含資源屬性、提議屬性，以及勾點叫用的相關內容。

以下是 JSON 輸入的範例形狀：

```
{
    "awsAccountId": String,
    "stackId": String,
    "changeSetId": String,
    "hookTypeName": String,
    "hookTypeVersion": String,
    "hookModel": {
        "LambdaFunction": String
    },
    "actionInvocationPoint": "CREATE_PRE_PROVISION" or "UPDATE_PRE_PROVISION" or "DELETE_PRE_PROVISION"
    "requestData": {
        "targetName": String,
        "targetType": String,
        "targetLogicalId": String,
        "targetModel": {
            "resourceProperties": {...},
            "previousResourceProperties": {...}
        }
    },
    "requestContext": {
        "調用": 1,
        "callbackContext": null
    }
}
```

`awsAccountId`  <a name="lambda-hook-resource-awsaccountid"></a>
 AWS 帳戶 包含要評估之資源的 ID。

`stackId`  <a name="lambda-hook-resource-stackid"></a>
此操作所屬 CloudFormation 堆疊的堆疊 ID。如果發起人是雲端控制 API，則此欄位為空白。

`changeSetId`  <a name="lambda-hook-resource-changesetid"></a>
啟動勾點調用的變更集 ID。如果資源變更是由 Cloud Control API 或 `create-stack`、 `update-stack`或 `delete-stack`操作啟動，則此值為空白。

`hookTypeName`  <a name="lambda-hook-resource-hooktypename"></a>
正在執行的勾點名稱。

`hookTypeVersion`  <a name="lambda-hook-resource-hooktypeversion"></a>
正在執行的 Hook 版本。

`hookModel`  <a name="lambda-hook-resource-hookmodel"></a>  
`LambdaFunction`  <a name="lambda-hook-resource-hookmodel-lambdafunction"></a>
Hook 調用的目前 Lambda ARN。

`actionInvocationPoint`  <a name="lambda-hook-resource-actioninvocationpoint"></a>
Hook 執行所在佈建邏輯中的確切點。  
*有效值*： (`CREATE_PRE_PROVISION` \$1 `UPDATE_PRE_PROVISION` \$1 `DELETE_PRE_PROVISION`)

`requestData`  <a name="lambda-hook-resource-requestdata"></a>  
`targetName`  <a name="lambda-hook-resource-requestdata-targetname"></a>
正在評估的目標類型，例如 `AWS::S3::Bucket`。  
`targetType`  <a name="lambda-hook-resource-requestdata-targettype"></a>
正在評估的目標類型，例如 `AWS::S3::Bucket`。對於使用 Cloud Control API 佈建的資源，此值將為 `RESOURCE`。  
`targetLogicalId`  <a name="lambda-hook-resource-requestdata-targetlogicalid"></a>
要評估之資源的邏輯 ID。如果勾點呼叫的原始伺服器是 CloudFormation，這將是 CloudFormation 範本中定義的邏輯資源 ID。如果此勾點調用的原始伺服器是 Cloud Control API，則會是建構值。  
`targetModel`  <a name="lambda-hook-resource-requestdata-targetmodel"></a>  
`resourceProperties`  <a name="lambda-hook-resource-requestdata-targetmodel-resourceproperties"></a>
正在修改之資源的提議屬性。如果正在刪除資源，則此值將為空。  
`previousResourceProperties`  <a name="lambda-hook-resource-requestdata-targetmodel-previousresourceproperties"></a>
目前與正在修改的資源相關聯的屬性。如果正在建立資源，則此值將為空。

`requestContext`  <a name="lambda-hook-resource-requestcontext"></a>  
調用  <a name="lambda-hook-resource-requestcontext-invocation"></a>
目前執行勾點的嘗試。  
callbackContext  <a name="lambda-hook-resource-requestcontext-callbackcontext"></a>
如果 Hookwas 設定為 `IN_PROGRESS`，且`callbackContext`傳回 ，則會在叫用後出現。

### Lambda Hook 資源變更輸入範例
<a name="lambda-hooks-create-lambda-function-resource-example"></a>

下列範例輸入顯示將接收要更新之`AWS::DynamoDB::Table`資源定義的 Lambda 勾點，其中 `ReadCapacityUnits``ProvisionedThroughput`的 已從 3 變更為 10。這是可供 Lambda 評估的資料。

```
{
    "awsAccountId": "123456789012",
    "stackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/MyStack/1a2345b6-0000-00a0-a123-00abc0abc000",
    "hookTypeName": "my::lambda::resourcehookfunction",
    "hookTypeVersion": "00000008",
    "hookModel": {
        "LambdaFunction": "arn:aws:lambda:us-west-2:123456789012:function:MyFunction"
    },
    "actionInvocationPoint": "UPDATE_PRE_PROVISION",
    "requestData": {
        "targetName": "AWS::DynamoDB::Table",
        "targetType": "AWS::DynamoDB::Table",
        "targetLogicalId": "DDBTable",
        "targetModel": {
            "resourceProperties": {
                "AttributeDefinitions": [
                    {
                        "AttributeType": "S",
                        "AttributeName": "Album"
                    },
                    {
                        "AttributeType": "S",
                        "AttributeName": "Artist"
                    }
                ],
                "ProvisionedThroughput": {
                    "WriteCapacityUnits": 5,
                    "ReadCapacityUnits": 10
                },
                "KeySchema": [
                    {
                        "KeyType": "HASH",
                        "AttributeName": "Album"
                    },
                    {
                        "KeyType": "RANGE",
                        "AttributeName": "Artist"
                    }
                ]
            },
            "previousResourceProperties": {
                "AttributeDefinitions": [
                    {
                        "AttributeType": "S",
                        "AttributeName": "Album"
                    },
                    {
                        "AttributeType": "S",
                        "AttributeName": "Artist"
                    }
                ],
                "ProvisionedThroughput": {
                    "WriteCapacityUnits": 5,
                    "ReadCapacityUnits": 5
                },
                "KeySchema": [
                    {
                        "KeyType": "HASH",
                        "AttributeName": "Album"
                    },
                    {
                        "KeyType": "RANGE",
                        "AttributeName": "Artist"
                    }
                ]
            }
        }
    },
    "requestContext": {
        "invocation": 1,
        "callbackContext": null
    }    
}
```

若要查看資源類型可用的所有屬性，請參閱 [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-dynamodb-table.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-dynamodb-table.html)。

### 資源操作的 Lambda 函數範例
<a name="lambda-hooks-create-lambda-function-resource-example-function"></a>

以下是 DynamoDB 的任何資源更新失敗的簡單函數，它會嘗試將 `ReadCapacity` 的 設定為大於 10 `ProvisionedThroughput`的 。如果勾點成功，訊息「ReadCapacity 已正確設定」將顯示給發起人。如果請求驗證失敗，勾點將會失敗，狀態為「ReadCapacity 不能超過 10。」

------
#### [ Node.js ]

```
export const handler = async (event, context) => {
    var targetModel = event?.requestData?.targetModel;
    var targetName = event?.requestData?.targetName;
    var response = {
        "hookStatus": "SUCCESS",
        "message": "ReadCapacity is correctly configured.",
        "clientRequestToken": event.clientRequestToken
    };

    if (targetName == "AWS::DynamoDB::Table") {
        var readCapacity = targetModel?.resourceProperties?.ProvisionedThroughput?.ReadCapacityUnits;
        if (readCapacity > 10) {
            response.hookStatus = "FAILED";
            response.errorCode = "NonCompliant";
            response.message = "ReadCapacity must be cannot be more than 10.";
        }
    }
    return response;
};
```

------
#### [ Python ]

```
import json
                            
def lambda_handler(event, context):
    # Using dict.get() for safe access to nested dictionary values
    request_data = event.get('requestData', {})
    target_model = request_data.get('targetModel', {})
    target_name = request_data.get('targetName', '')
    
    response = {
        "hookStatus": "SUCCESS",
        "message": "ReadCapacity is correctly configured.",
        "clientRequestToken": event.get('clientRequestToken')
    }
    
    if target_name == "AWS::DynamoDB::Table":
        # Safely navigate nested dictionary
        resource_properties = target_model.get('resourceProperties', {})
        provisioned_throughput = resource_properties.get('ProvisionedThroughput', {})
        read_capacity = provisioned_throughput.get('ReadCapacityUnits')
        
        if read_capacity and read_capacity > 10:
            response['hookStatus'] = "FAILED"
            response['errorCode'] = "NonCompliant"
            response['message'] = "ReadCapacity must be cannot be more than 10."
    
    return response
```

------

## 使用 Lambda Hooks 評估堆疊操作
<a name="lambda-hooks-create-lambda-function-stack"></a>

每當您使用新範本建立、更新或刪除堆疊時，都可以透過評估新範本來設定 CloudFormation Lambda Hook，並可能封鎖堆疊操作以繼續。您可以在 Hook `TargetOperations`組態中將 CloudFormation Lambda Hook 設定為目標`STACK`操作。

**Topics**
+ [Lambda Hook 堆疊輸入語法](#lambda-hooks-create-lambda-function-stack-input)
+ [Lambda Hook 堆疊變更輸入範例](#lambda-hooks-create-lambda-function-stack-example)
+ [堆疊操作的範例 Lambda 函數](#lambda-hooks-create-lambda-function-stack-example-function)

### Lambda Hook 堆疊輸入語法
<a name="lambda-hooks-create-lambda-function-stack-input"></a>

當您的 Lambda 調用堆疊操作時，您將會收到 JSON 請求，其中包含勾點調用內容、 `actionInvocationPoint`和 請求內容。由於 CloudFormation 範本的大小，以及 Lambda 函數接受的有限輸入大小，實際範本會存放在 Amazon S3 物件中。的輸入`requestData`包含另一個物件的 Amazon S3 已簽章 URL，其中包含目前和先前的範本版本。

以下是 JSON 輸入的範例形狀：

```
{
    "clientRequesttoken": String,
    "awsAccountId": String,
    "stackID": String,
    "changeSetId": String,
    "hookTypeName": String,
    "hookTypeVersion": String,
    "hookModel": {
        "LambdaFunction":String
    },
    "actionInvocationPoint": "CREATE_PRE_PROVISION" or "UPDATE_PRE_PROVISION" or "DELETE_PRE_PROVISION"
    "requestData": {
        "targetName": "STACK",
        "targetType": "STACK",
        "targetLogicalId": String,
        "payload": String (S3 Presigned URL)
    },
    "requestContext": {
        "invocation": Integer,
        "callbackContext": String
    }
}
```

`clientRequesttoken`  <a name="lambda-hook-stack-clientrequesttoken"></a>
做為 Hook 請求輸入而提供的請求字符。此為必要欄位。

`awsAccountId`  <a name="lambda-hook-stack-awsaccountid"></a>
 AWS 帳戶 包含要評估之堆疊的 ID。

`stackID`  <a name="lambda-hook-stack-stackid"></a>
CloudFormation 堆疊的堆疊 ID。

`changeSetId`  <a name="lambda-hook-stack-changesetid"></a>
啟動勾點調用的變更集 ID。如果堆疊變更是由 Cloud Control API 或 `create-stack`、 `update-stack`或 `delete-stack`操作啟動，則此值為空白。

`hookTypeName`  <a name="lambda-hook-stack-hooktypename"></a>
正在執行的勾點名稱。

`hookTypeVersion`  <a name="lambda-hook-stack-hooktypeversion"></a>
正在執行的 Hook 版本。

`hookModel`  <a name="lambda-hook-stack-hookmodel"></a>  
`LambdaFunction`  <a name="lambda-hook-stack-hookmodel-lambdafunction"></a>
Hook 調用的目前 Lambda ARN。

`actionInvocationPoint`  <a name="lambda-hook-stack-actioninvocationpoint"></a>
佈建邏輯中 Hook 執行的確切點。  
*有效值*： (`CREATE_PRE_PROVISION` \$1 `UPDATE_PRE_PROVISION` \$1 `DELETE_PRE_PROVISION`)

`requestData`  <a name="lambda-hook-stack-requestdata"></a>  
`targetName`  <a name="lambda-hook-stack-requestdata-targetname"></a>
此值將為 `STACK`。  
`targetType`  <a name="lambda-hook-stack-requestdata-targettype"></a>
此值將為 `STACK`。  
`targetLogicalId`  <a name="lambda-hook-stack-requestdata-targetlogicalid"></a>
堆疊名稱。  
`payload`  <a name="lambda-hook-stack-requestdata-payload"></a>
Amazon S3 預先簽章的 URL，其中包含具有目前和先前範本定義的 JSON 物件。

`requestContext`  <a name="lambda-hook-stack-requestcontext"></a>
如果正在重新叫用勾點，則會設定此物件。    
`invocation`  <a name="lambda-hook-stack-requestcontext-invocation"></a>
目前執行勾點的嘗試。  
`callbackContext`  <a name="lambda-hook-stack-requestcontext-callbackcontext"></a>
如果勾點設定為 `callbackContext` `IN_PROGRESS`並傳回，則會在叫用時出現。

請求資料中的 `payload` 屬性是您程式碼需要擷取的 URL。收到 URL 後，您會取得具有下列結構描述的物件：

```
{
    "template": String,
    "previousTemplate": String
}
```

`template`  <a name="lambda-hook-stack-payload-template"></a>
提供給 `create-stack`或 的完整 CloudFormation 範本`update-stack`。根據提供給 CloudFormation 的內容，它可以是 JSON 或 YAML 字串。  
在 `delete-stack` 操作中，此值將為空。

`previousTemplate`  <a name="lambda-hook-stack-payload-previoustemplate"></a>
先前的 CloudFormation 範本。根據提供給 CloudFormation 的內容，它可以是 JSON 或 YAML 字串。  
在 `delete-stack` 操作中，此值將為空。

### Lambda Hook 堆疊變更輸入範例
<a name="lambda-hooks-create-lambda-function-stack-example"></a>

以下是堆疊變更輸入的範例。Hook 正在評估將 更新`ObjectLockEnabled`為 true 的變更，並新增 Amazon SQS 佇列：

```
{
    "clientRequestToken": "f8da6d11-b23f-48f4-814c-0fb6a667f50e",
    "awsAccountId": "123456789012",
    "stackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/MyStack/1a2345b6-0000-00a0-a123-00abc0abc000",
    "changeSetId": null,
    "hookTypeName": "my::lambda::stackhook",
    "hookTypeVersion": "00000008",
    "hookModel": {
        "LambdaFunction": "arn:aws:lambda:us-west-2:123456789012:function:MyFunction"
    },
    "actionInvocationPoint": "UPDATE_PRE_PROVISION",
    "requestData": {
        "targetName": "STACK",
        "targetType": "STACK",
        "targetLogicalId": "my-cloudformation-stack",
        "payload": "https://s3......"
    },
    "requestContext": {
        "invocation": 1,
        "callbackContext": null
    }
}
```

這是 `payload`的範例`requestData`：

```
{
    "template": "{\"Resources\":{\"S3Bucket\":{\"Type\":\"AWS::S3::Bucket\",\"Properties\":{\"ObjectLockEnabled\":true}},\"SQSQueue\":{\"Type\":\"AWS::SQS::Queue\",\"Properties\":{\"QueueName\":\"NewQueue\"}}}}",
    "previousTemplate": "{\"Resources\":{\"S3Bucket\":{\"Type\":\"AWS::S3::Bucket\",\"Properties\":{\"ObjectLockEnabled\":false}}}}"
}
```

### 堆疊操作的範例 Lambda 函數
<a name="lambda-hooks-create-lambda-function-stack-example-function"></a>

下列範例是一個簡單的 函數，可下載堆疊操作承載、剖析範本 JSON，並傳回 `SUCCESS`。

------
#### [ Node.js ]

```
export const handler = async (event, context) => {
    var targetType = event?.requestData?.targetType;
    var payloadUrl = event?.requestData?.payload;
    
    var response = {
        "hookStatus": "SUCCESS",
        "message": "Stack update is compliant",
        "clientRequestToken": event.clientRequestToken
    };
    try {
        const templateHookPayloadRequest = await fetch(payloadUrl);
        const templateHookPayload = await templateHookPayloadRequest.json()
        if (templateHookPayload.template)  {
            // Do something with the template templateHookPayload.template
            // JSON or YAML
        }
        if (templateHookPayload.previousTemplate) {
            // Do something with the template templateHookPayload.previousTemplate
            // JSON or YAML        
        }        
    } catch (error) {
        console.log(error);
        response.hookStatus = "FAILED";
        response.message = "Failed to evaluate stack operation.";
        response.errorCode = "InternalFailure";
    }
    return response;
};
```

------
#### [ Python ]

若要使用 Python，您需要匯入程式`requests`庫。若要這樣做，您需要在建立 Lambda 函數時，在部署套件中包含 程式庫。如需詳細資訊，請參閱《 *AWS Lambda 開發人員指南*》中的[建立具有相依性的 .zip 部署套件](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies)。

```
import json
import requests

def lamnbda_handler(event, context):
    # Safely access nested dictionary values
    request_data = event.get('requestData', {})
    target_type = request_data.get('targetType')
    payload_url = request_data.get('payload')
    
    response = {
        "hookStatus": "SUCCESS",
        "message": "Stack update is compliant",
        "clientRequestToken": event.get('clientRequestToken')
    }
    
    try:
        # Fetch the payload
        template_hook_payload_request = requests.get(payload_url)
        template_hook_payload_request.raise_for_status()  # Raise an exception for bad responses
        template_hook_payload = template_hook_payload_request.json()
        
        if 'template' in template_hook_payload:
            # Do something with the template template_hook_payload['template']
            # JSON or YAML
            pass
        
        if 'previousTemplate' in template_hook_payload:
            # Do something with the template template_hook_payload['previousTemplate']
            # JSON or YAML
            pass

    except Exception as error:
        print(error)
        response['hookStatus'] = "FAILED"
        response['message'] = "Failed to evaluate stack operation."
        response['errorCode'] = "InternalFailure"
    
    return response
```

------

## 使用 Lambda Hooks 評估變更集操作
<a name="lambda-hooks-create-lambda-function-change-set"></a>

每當您建立變更集時，都可以設定 CloudFormation Lambda Hook 來先評估新的變更集，並可能封鎖其執行。您可以在 Hook `TargetOperations`組態中將 CloudFormation Lambda Hook 設定為目標`CHANGE_SET`操作。

**Topics**
+ [Lambda Hook 變更集輸入語法](#lambda-hooks-create-lambda-function-change-set-input)
+ [Lambda Hook 變更集變更輸入範例](#lambda-hooks-create-lambda-function-change-set-example)
+ [變更集操作的範例 Lambda 函數](#lambda-hooks-create-lambda-function-change-set-example-function)

### Lambda Hook 變更集輸入語法
<a name="lambda-hooks-create-lambda-function-change-set-input"></a>

變更集操作的輸入類似於堆疊操作，但 的承載`requestData`也包含變更集引入的資源變更清單。

以下是 JSON 輸入的範例形狀：

```
{
    "clientRequesttoken": String,
    "awsAccountId": String,
    "stackID": String,
    "changeSetId": String,
    "hookTypeName": String,
    "hookTypeVersion": String,
    "hookModel": {
        "LambdaFunction":String
    },
    "requestData": {
        "targetName": "CHANGE_SET",
        "targetType": "CHANGE_SET",
        "targetLogicalId": String,
        "payload": String (S3 Presigned URL)
    },
    "requestContext": {
        "invocation": Integer,
        "callbackContext": String
    }
}
```

`clientRequesttoken`  <a name="lambda-hook-change-set-clientrequesttoken"></a>
做為 Hook 請求輸入而提供的請求字符。此為必要欄位。

`awsAccountId`  <a name="lambda-hook-change-set-awsaccountid"></a>
 AWS 帳戶 包含要評估之堆疊的 ID。

`stackID`  <a name="lambda-hook-change-set-stackid"></a>
CloudFormation 堆疊的堆疊 ID。

`changeSetId`  <a name="lambda-hook-change-set-changesetid"></a>
啟動勾點調用的變更集 ID。

`hookTypeName`  <a name="lambda-hook-change-set-hooktypename"></a>
正在執行的勾點名稱。

`hookTypeVersion`  <a name="lambda-hook-change-set-hooktypeversion"></a>
正在執行的 Hook 版本。

`hookModel`  <a name="lambda-hook-change-set-hookmodel"></a>  
`LambdaFunction`  <a name="lambda-hook-change-set-hookmodel-lambdafunction"></a>
Hook 調用的目前 Lambda ARN。

`requestData`  <a name="lambda-hook-change-set-requestdata"></a>  
`targetName`  <a name="lambda-hook-change-set-requestdata-targetname"></a>
此值將為 `CHANGE_SET`。  
`targetType`  <a name="lambda-hook-change-set-requestdata-targettype"></a>
此值將為 `CHANGE_SET`。  
`targetLogicalId`  <a name="lambda-hook-change-set-requestdata-targetlogicalid"></a>
變更集 ARN。  
`payload`  <a name="lambda-hook-change-set-requestdata-payload"></a>
Amazon S3 預先簽章的 URL，其中包含具有目前範本的 JSON 物件，以及此變更集引入的變更清單。

`requestContext`  <a name="lambda-hook-change-set-requestcontext"></a>
如果正在重新叫用勾點，則會設定此物件。    
`invocation`  <a name="lambda-hook-change-set-requestcontext-invocation"></a>
目前執行勾點的嘗試。  
`callbackContext`  <a name="lambda-hook-change-set-requestcontext-callbackcontext"></a>
如果勾點設定為 `callbackContext` `IN_PROGRESS`並傳回，則會在叫用時出現。

請求資料中的 `payload` 屬性是您程式碼需要擷取的 URL。收到 URL 後，您會取得具有下列結構描述的物件：

```
{
    "template": String,
    "changedResources": [
        {
            "action": String,
            "beforeContext": JSON String,
            "afterContext": JSON String,
            "lineNumber": Integer,
            "logicalResourceId": String,
            "resourceType": String
        }
    ]
}
```

`template`  <a name="lambda-hook-change-set-payload-template"></a>
提供給 `create-stack`或 的完整 CloudFormation 範本`update-stack`。根據提供給 CloudFormation 的內容，它可以是 JSON 或 YAML 字串。

`changedResources`  <a name="lambda-hook-change-set-payload-changed-resources"></a>
已變更資源的清單。    
`action`  <a name="lambda-hook-change-set-payload-changed-resources-action"></a>
套用至資源的變更類型。  
*有效值*： (`CREATE` \$1 `UPDATE` \$1 `DELETE`)  
`beforeContext`  <a name="lambda-hook-change-set-payload-changed-resources-beforecontext"></a>
變更前資源屬性的 JSON 字串。建立資源時，此值為 null。此 JSON 字串中的所有布林值和數值都是 STRINGS。  
`afterContext`  <a name="lambda-hook-change-set-payload-changed-resources-aftercontext"></a>
如果執行此變更集，資源屬性的 JSON 字串。刪除資源時，此值為 null。此 JSON 字串中的所有布林值和數值都是 STRINGS。  
`lineNumber`  <a name="lambda-hook-change-set-payload-changed-resources-linenumber"></a>
範本中造成此變更的行號。如果動作是 ，則`DELETE`此值將為 null。  
`logicalResourceId`  <a name="lambda-hook-change-set-payload-changed-resources-logicalresourceid"></a>
要變更之資源的邏輯資源 ID。  
`resourceType`  <a name="lambda-hook-change-set-payload-changed-resources-resourcetype"></a>
正在變更的資源類型。

### Lambda Hook 變更集變更輸入範例
<a name="lambda-hooks-create-lambda-function-change-set-example"></a>

以下是變更集變更輸入的範例。在下列範例中，您可以查看變更集引進的變更。第一個變更是刪除名為 的佇列`CoolQueue`。第二個變更是新增名為 的新佇列`NewCoolQueue`。上次變更是 的更新`DynamoDBTable`。

```
{
    "clientRequestToken": "f8da6d11-b23f-48f4-814c-0fb6a667f50e",
    "awsAccountId": "123456789012",
    "stackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/MyStack/1a2345b6-0000-00a0-a123-00abc0abc000",
    "changeSetId": "arn:aws:cloudformation:us-west-2:123456789012:changeSet/SampleChangeSet/1a2345b6-0000-00a0-a123-00abc0abc000",
    "hookTypeName": "my::lambda::changesethook",
    "hookTypeVersion": "00000008",
    "hookModel": {
        "LambdaFunction": "arn:aws:lambda:us-west-2:123456789012:function:MyFunction"
    },
    "actionInvocationPoint": "CREATE_PRE_PROVISION",
    "requestData": {
        "targetName": "CHANGE_SET",
        "targetType": "CHANGE_SET",
        "targetLogicalId": "arn:aws:cloudformation:us-west-2:123456789012:changeSet/SampleChangeSet/1a2345b6-0000-00a0-a123-00abc0abc000",
        "payload": "https://s3......"
    },
    "requestContext": {
        "invocation": 1,
        "callbackContext": null
    }
}
```

這是 `payload`的範例`requestData.payload`：

```
{
  template: 'Resources:\n' +
    '  DynamoDBTable:\n' +
    '    Type: AWS::DynamoDB::Table\n' +
    '    Properties:\n' +
    '      AttributeDefinitions:\n' +
    '        - AttributeName: "PK"\n' +
    '          AttributeType: "S"\n' +
    '      BillingMode: "PAY_PER_REQUEST"\n' +
    '      KeySchema:\n' +
    '        - AttributeName: "PK"\n' +
    '          KeyType: "HASH"\n' +
    '      PointInTimeRecoverySpecification:\n' +
    '        PointInTimeRecoveryEnabled: false\n' +
    '  NewSQSQueue:\n' +
    '    Type: AWS::SQS::Queue\n' +
    '    Properties:\n' +
    '      QueueName: "NewCoolQueue"',
  changedResources: [
    {
      logicalResourceId: 'SQSQueue',
      resourceType: 'AWS::SQS::Queue',
      action: 'DELETE',
      lineNumber: null,
      beforeContext: '{"Properties":{"QueueName":"CoolQueue"}}',
      afterContext: null
    },
    {
      logicalResourceId: 'NewSQSQueue',
      resourceType: 'AWS::SQS::Queue',
      action: 'CREATE',
      lineNumber: 14,
      beforeContext: null,
      afterContext: '{"Properties":{"QueueName":"NewCoolQueue"}}'
    },
    {
      logicalResourceId: 'DynamoDBTable',
      resourceType: 'AWS::DynamoDB::Table',
      action: 'UPDATE',
      lineNumber: 2,
      beforeContext: '{"Properties":{"BillingMode":"PAY_PER_REQUEST","AttributeDefinitions":[{"AttributeType":"S","AttributeName":"PK"}],"KeySchema":[{"KeyType":"HASH","AttributeName":"PK"}]}}',
      afterContext: '{"Properties":{"BillingMode":"PAY_PER_REQUEST","PointInTimeRecoverySpecification":{"PointInTimeRecoveryEnabled":"false"},"AttributeDefinitions":[{"AttributeType":"S","AttributeName":"PK"}],"KeySchema":[{"KeyType":"HASH","AttributeName":"PK"}]}}'
    }
  ]
}
```

### 變更集操作的範例 Lambda 函數
<a name="lambda-hooks-create-lambda-function-change-set-example-function"></a>

下列範例是一個簡單的 函數，可下載變更集操作承載、逐一查看每個變更，然後在傳回 之前列印屬性前後的 `SUCCESS`。

------
#### [ Node.js ]

```
export const handler = async (event, context) => {
    var payloadUrl = event?.requestData?.payload;    
    var response = {
        "hookStatus": "SUCCESS",
        "message": "Change set changes are compliant",
        "clientRequestToken": event.clientRequestToken
    };
    try {
        const changeSetHookPayloadRequest = await fetch(payloadUrl);
        const changeSetHookPayload = await changeSetHookPayloadRequest.json();
        const changes = changeSetHookPayload.changedResources || [];
        for(const change of changes) {
            var beforeContext = {};
            var afterContext = {};
            if(change.beforeContext) {
                beforeContext = JSON.parse(change.beforeContext);
            }
            if(change.afterContext) {
                afterContext = JSON.parse(change.afterContext);
            }
            console.log(beforeContext)
            console.log(afterContext)
            // Evaluate Change here
        }
    } catch (error) {
        console.log(error);
        response.hookStatus = "FAILED";
        response.message = "Failed to evaluate change set operation.";
        response.errorCode = "InternalFailure";
    }
    return response;
};
```

------
#### [ Python ]

若要使用 Python，您需要匯入程式`requests`庫。若要這樣做，您需要在建立 Lambda 函數時，在部署套件中包含 程式庫。如需詳細資訊，請參閱《 *AWS Lambda 開發人員指南*》中的[建立具有相依性的 .zip 部署套件](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies)。

```
import json
import requests

def lambda_handler(event, context):
    payload_url = event.get('requestData', {}).get('payload')
    response = {
        "hookStatus": "SUCCESS",
        "message": "Change set changes are compliant",
        "clientRequestToken": event.get('clientRequestToken')
    }

    try:
        change_set_hook_payload_request = requests.get(payload_url)
        change_set_hook_payload_request.raise_for_status()  # Raises an HTTPError for bad responses
        change_set_hook_payload = change_set_hook_payload_request.json()
        
        changes = change_set_hook_payload.get('changedResources', [])
        
        for change in changes:
            before_context = {}
            after_context = {}
            
            if change.get('beforeContext'):
                before_context = json.loads(change['beforeContext'])
            
            if change.get('afterContext'):
                after_context = json.loads(change['afterContext'])
            
            print(before_context)
            print(after_context)
            # Evaluate Change here

    except requests.RequestException as error:
        print(error)
        response['hookStatus'] = "FAILED"
        response['message'] = "Failed to evaluate change set operation."
        response['errorCode'] = "InternalFailure"
    except json.JSONDecodeError as error:
        print(error)
        response['hookStatus'] = "FAILED"
        response['message'] = "Failed to parse JSON payload."
        response['errorCode'] = "InternalFailure"

    return response
```

------