

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Lambda 함수를 생성하여 Lambda 후크에 대한 리소스 평가
<a name="lambda-hooks-create-lambda-function"></a>

CloudFormation Lambda 후크를 사용하면 자체 사용자 지정 코드를 기준으로 CloudFormation 및 AWS Cloud Control API 작업을 평가할 수 있습니다. 후크는 작업이 진행되지 않도록 차단하거나 호출자에게 경고를 발행하고 작업이 진행되도록 허용할 수 있습니다. Lambda 후크를 생성할 때 다음 CloudFormation 작업을 가로채고 평가하도록 구성할 수 있습니다.
+ 리소스 작업
+ 스택 작업
+ 변경 세트 작업

**Topics**
+ [Lambda 후크 개발](#lambda-hooks-create-lambda-function-develop)
+ [Lambda 후크를 사용하여 리소스 작업 평가](#lambda-hooks-create-lambda-function-resource)
+ [Lambda 후크를 사용하여 스택 작업 평가](#lambda-hooks-create-lambda-function-stack)
+ [Lambda 후크를 사용하여 변경 세트 작업 평가](#lambda-hooks-create-lambda-function-change-set)

## Lambda 후크 개발
<a name="lambda-hooks-create-lambda-function-develop"></a>

후크가 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>

요청이 성공하거나 실패한 경우 후크와 통신하려면 Lambda 함수가 JSON 응답을 반환해야 합니다.

다음은 후크가 기대하는 응답의 셰이프 예제입니다.

```
{ 
  "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>
후크 요청에 대한 입력으로 제공된 요청 토큰입니다. 필수 필드입니다.

callbackContext  <a name="lambda-hook-response-callbackcontext"></a>
`hookStatus`가 인 `IN_PROGRESS` 경우 Lambda 함수가 다시 호출될 때 입력으로 제공되는 추가 컨텍스트를 전달합니다.

callbackDelaySeconds  <a name="lambda-hook-response-callbackdelayseconds"></a>
후크가이 후크를 다시 호출하기 위해 대기해야 하는 시간입니다.

주석  <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 후크를 사용하여 리소스 작업 평가
<a name="lambda-hooks-create-lambda-function-resource"></a>

리소스를 생성, 업데이트 또는 삭제할 때마다 리소스 작업으로 간주됩니다. 예를 들어 새 리소스를 생성하는 CloudFormation 스택 업데이트를 실행하면 리소스 작업이 완료됩니다. Cloud Control API를 사용하여 리소스를 생성, 업데이트 또는 삭제할 때 리소스 작업으로도 간주됩니다. 후크 구성에서 대상 `RESOURCE` 및 `CLOUD_CONTROL` 작업을 지정하도록 CloudFormation Lambda 후크를 구성할 수 있습니다`TargetOperations`.

**참고**  
`delete` 후크 핸들러는 Cloud Control API `delete-resource` 또는 CloudFormation의 작업 트리거를 사용하여 리소스가 삭제된 경우에만 호출됩니다`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입니다. 호출자가 Cloud Control 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>
실행 중인 후크의 버전입니다.

`hookModel`  <a name="lambda-hook-resource-hookmodel"></a>  
`LambdaFunction`  <a name="lambda-hook-resource-hookmodel-lambdafunction"></a>
후크에서 호출한 현재 Lambda ARN입니다.

`actionInvocationPoint`  <a name="lambda-hook-resource-actioninvocationpoint"></a>
후크가 실행되는 프로비저닝 로직의 정확한 지점입니다.  
*유효한 값*: (`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>

다음은의를 10보다 큰 값으로 설정하려고 시도하는 DynamoDB에 `ReadCapacity` 대한 리소스 업데이트`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 후크를 사용하여 스택 작업 평가
<a name="lambda-hooks-create-lambda-function-stack"></a>

새 템플릿으로 스택을 생성, 업데이트 또는 삭제할 때마다 새 템플릿을 평가하여 시작하여 스택 작업이 진행되지 않도록 CloudFormation Lambda 후크를 구성할 수 있습니다. 후크 구성에서 `STACK` 작업을 대상으로 하도록 CloudFormation Lambda 후크를 구성할 수 있습니다`TargetOperations`.

**Topics**
+ [Lambda 후크 스택 입력 구문](#lambda-hooks-create-lambda-function-stack-input)
+ [Lambda 후크 스택 변경 입력 예](#lambda-hooks-create-lambda-function-stack-example)
+ [스택 작업에 대한 Lambda 함수 예제](#lambda-hooks-create-lambda-function-stack-example-function)

### Lambda 후크 스택 입력 구문
<a name="lambda-hooks-create-lambda-function-stack-input"></a>

스택 작업에 대해 Lambda가 호출되면 후크 호출 컨텍스트, `actionInvocationPoint`및 요청 컨텍스트가 포함된 JSON 요청을 받게 됩니다. 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>
후크 요청에 대한 입력으로 제공된 요청 토큰입니다. 필수 필드입니다.

`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>
실행 중인 후크의 버전입니다.

`hookModel`  <a name="lambda-hook-stack-hookmodel"></a>  
`LambdaFunction`  <a name="lambda-hook-stack-hookmodel-lambdafunction"></a>
후크에서 호출한 현재 Lambda ARN입니다.

`actionInvocationPoint`  <a name="lambda-hook-stack-actioninvocationpoint"></a>
후크가 실행되는 프로비저닝 로직의 정확한 지점입니다.  
*유효한 값*: (`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>
현재 및 이전 템플릿 정의가 있는 JSON 객체가 포함된 Amazon S3 미리 서명된 URL입니다.

`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>
후크가 로 설정`IN_PROGRESS`되고 반환`callbackContext`된 경우 재호출 시 여기에 표시됩니다.

요청 데이터의 `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 후크 스택 변경 입력 예
<a name="lambda-hooks-create-lambda-function-stack-example"></a>

다음은 스택 변경 입력의 예입니다. 후크는를 true`ObjectLockEnabled`로 업데이트하고 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 후크를 사용하여 변경 세트 작업 평가
<a name="lambda-hooks-create-lambda-function-change-set"></a>

변경 세트를 생성할 때마다 먼저 새 변경 세트를 평가하고 잠재적으로 실행을 차단하도록 CloudFormation Lambda 후크를 구성할 수 있습니다. 후크 구성에서 `CHANGE_SET` 작업을 대상으로 CloudFormation Lambda 후크를 구성할 수 있습니다`TargetOperations`.

**Topics**
+ [Lambda 후크 변경 세트 입력 구문](#lambda-hooks-create-lambda-function-change-set-input)
+ [Lambda 후크 변경 세트 변경 입력 예](#lambda-hooks-create-lambda-function-change-set-example)
+ [변경 세트 작업에 대한 Lambda 함수 예제](#lambda-hooks-create-lambda-function-change-set-example-function)

### Lambda 후크 변경 세트 입력 구문
<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>
후크 요청에 대한 입력으로 제공된 요청 토큰입니다. 필수 필드입니다.

`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>
실행 중인 후크의 버전입니다.

`hookModel`  <a name="lambda-hook-change-set-hookmodel"></a>  
`LambdaFunction`  <a name="lambda-hook-change-set-hookmodel-lambdafunction"></a>
후크에서 호출한 현재 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>
현재 템플릿이 있는 JSON 객체와이 변경 세트에서 도입한 변경 목록이 포함된 Amazon S3 미리 서명된 URL입니다.

`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>
후크가 로 설정`IN_PROGRESS`되고 반환`callbackContext`된 경우 재호출 시 여기에 표시됩니다.

요청 데이터의 `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 후크 변경 세트 변경 입력 예
<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
```

------