![Olawale Olaleye Olawale Olaleye](https://d1.awsstatic.com/xuefezha-jennie/%e7%ba%a7%e5%88%ab_level%201.01c35fea97656a6beac50b0c8ae81e8afc1eedef.png)
![](https://d1.awsstatic.com/xuefezha-jennie/Group%20281.67a8494bd80a4bd979e37efcb490ada486dd72ae.png)
AWS CloudFormation 是一项云资源模板服务,你可以通过代码来定义、管理和预配 AWS 云基础设施。为了加强云基础设施管理,确保你的基础设施符合你的企业标准,AWS 提供了 CloudFormation Hooks 功能。你可以利用 Hook 作为扩展点,在 CloudFormation 堆栈运行过程中的特定节点调用自定义逻辑,执行验证、修改或触发其他流程的操作。在这些扩展点中,Lambda Hook 是 AWS 提供的一个强大工具。通过该托管 Hook,你可以使用 Lambda 函数在部署前验证 CloudFormation 模板。借助 Lambda Hook,你可以在创建、更新或删除 CloudFormation 资源、堆栈以及更改集时,调用自定义逻辑来检查基础设施的配置,也可以在调用 AWS Cloud Control API (CCAPI) 创建或更新资源操作时进行配置检查。这项功能让你能够强化实施基础设施即代码 (IaC) 的预定义策略,防止部署不合规的资源,并针对潜在问题发出警告。本文将介绍在部署前,如何使用 Lambda Hook 验证你的 CloudFormation 模板,从而确保你的基础设施从一开始就符合合规性和安全性要求。
Lambda Hook 简介
Lambda Hook 是 AWS 提供的一个托管 Hook,其类型表示为 AWS::Hooks::LambdaHook。它简化了将自定义逻辑集成到 CloudFormation 堆栈的流程。使用 Lambda Hook,你不必从头开始创建复杂的 Hook,从而你可以将精力集中在构建和测试用于实现自定义逻辑的 Lambda 函数。
使用 Lambda Hook 时,你可以激活预构建的 Hook,然后使用 AWS CLI、AWS Serverless Application Model (SAM) 或 AWS Cloud Development Kit (CDK) 等你熟悉的工具,将自定义逻辑部署为 Lambda 函数。这种方法减少了工作流中需要管理的组件数量,使操作更加精简。Lambda Hook 还提供了灵活的评估功能,从而让你能够根据需要调整特定的模板属性或配置。
Lambda Hook 的一大优势是它提供的增强控制能力。你可以将 VPC 集成、本地日志记录和细粒度资源管理等功能与 AWS Lambda 函数的强大功能结合使用。开始使用 Lambda Hook 之前,你需要先在你的 AWS 账户中将其激活。通过这种方式激活 Lambda Hook 可以省去使用 AWS CloudFormation 命令行界面 (CFN-CLI) 来编写、测试、打包和部署自定义 Hook 的步骤,大大简化了你的工作流。
示例使用场景:S3 存储桶版本控制配置验证
本示例将演示:部署前,如何使用 Lambda Hook 验证 S3 存储桶版本控制功能是否启用。尽管这里以 S3 存储桶为例,但这种方法同样适用于其他资源类型、属性、堆栈以及更改集操作。
通过使用 Lambda Hook,你可以将自定义逻辑轻松集成到你的 CloudFormation 堆栈中。具体步骤如下:
- 在你的账户中激活类型为 AWS::Hooks::LambdaHook 的 Lambda Hook
- 编写用于实现验证的 Lambda 函数
- 将该 Lambda 函数的 ARN 输入 Hook
该示例展示了如何强化基础设施即代码实践,确保从源头做好部署的合规性和安全性。
架构
本节将介绍 Lambda Hook 和 Lambda 函数如何协同工作,优化 CloudFormation 模板部署。
Lambda Hook 与 Lambda 函数
首先,你需要创建一个 Lambda 函数,并编写用于响应 Hook 的业务逻辑。接着,你需要创建一个 IAM 执行角色,并授予该角色调用 Lambda 函数所需的权限。准备好 Lambda 函数和 IAM 执行角色后,即可激活 AWS Lambda Hook。关于在 AWS 管理控制台激活 Lambda Hook 的操作说明,请参阅在 AWS 管理控制台中激活 Lambda Hook 文档。你也可以使用 AWS Command Line Interface (AWS CLI),通过运行 activate-type 和 set-type-configuration 命令来激活 Hook。此外,你还可以在 CloudFormation 模板中,将 AWS::CloudFormation::LambdaHook 作为一种资源来激活和配置 Lambda Hook。你可以参考这篇有关部署 CloudFormation Hook 的博文,了解如何使用 AWS CloudFormation StackSets 实现跨账户和跨区域 Hook 资源共享。
Lambda Hook 的运行机制
下方的图文展示了如何将 Lambda Hook 与 CloudFormation 操作集成的详细步骤,直观地呈现了从模板创建到资源部署或更新的完整流程。
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lambda-Hooks-Arch.ddeb428425d3402c41ab3b7e551be6edf80782cb.jpg)
图 1:Lambda Hook 运行机制
该架构图展示了在 CloudFormation 堆栈中使用 Lambda Hook 的操作步骤。
- 创建模板:编写 CloudFormation 模板,定义所需配置的资源。
- 创建堆栈:CloudFormation 堆栈创建过程已启动,但尚未开始创建模板中定义的资源。
- CloudFormation 服务接收请求:当发起资源创建、更新或删除请求时,CloudFormation 服务会接收该请求。
- 调用 Hook:接收到请求后,CloudFormation 服务会调用 Lambda Hook。
- Hook 调用 Lambda 函数:Lambda Hook 随后触发你在激活 Hook 时指定的 Lambda 函数。
- Lambda 函数处理请求并将结果返回给 Hook:Lambda 函数处理请求,执行对应的验证或其他任务。任务处理完成后,Lambda 函数将处理结果返回给 Lambda Hook。
- 根据是否出现警告,堆栈工作流进入下一步(创建/更新/删除资源)或操作失败:基于 Lambda 函数的返回结果,Lambda Hook 会决定是继续执行堆栈的资源操作(如创建资源),还是拒绝资源操作并回滚堆栈。
这一工作流展示了如何将 Lambda Hook 无缝集成到 CloudFormation 堆栈部署流程中,从而通过 Lambda 函数来实现自定义验证、执行策略,并扩展基础设施即代码的部署能力。借助 Lambda Hook 和自定义 Lambda 函数,你可以扩展 CloudFormation 的部署能力,实现资源验证等高级用例,或执行其他任务。
部署示例
本节将介绍如何启用类型为 AWS::Hooks::LambdaHook 的 Lambda Hook,并在 Lambda 函数中添加验证 S3 存储桶版本控制配置的业务逻辑。本文中的示例解决方案将展示如何为 AWS::S3::Bucket 类型资源设置 Hook 触发。如果你希望为所有资源类型设置 Hook 触发,可以在 Hook 过滤器配置中使用带有通配符的资源定义,如 "AWS::*::*",作为资源过滤条件,或指定多个目标资源类型,如 "AWS::S3::Bucket"、"AWS::DynamoDB::Table"。另外,请确保 Lambda 函数中定义了能够处理这些类型资源的逻辑。你也可以添加更多 Hook 目标,例如验证 STACK 或 CHANGE_SET。
在本文的示例中,我们将展示如何配置 Hook,并将 Hook 的触发操作配置为资源创建和更新操作。关于如何配置目标过滤条件的详细信息,请参阅 Hook 配置模式;要了解更多关于 Lambda Hook 的信息,请参阅 Lambda Hooks。在进行这些修改时,请注意以下两点:首先,你需要在 Lambda 函数代码中实现处理不同类型资源的业务逻辑。其次,可能会根据你的资源使用情况产生额外费用。相关详情请参阅 Lambda 定价页面。
创建 Lambda 函数
你可以通过多种方式创建 Lambda 函数:使用 AWS 管理控制台、使用 CloudFormation、使用 AWS CLI,或通过 SDK 直接调用相应的 API。本节将介绍如何在 AWS 管理控制台上快速创建 Lambda 函数。要了解如何使用 SAM CLI、CDK 或 CloudFormation 部署 Lambda 函数,请参阅使用基础设施即代码 (IaC) 部署 Lambda。
请按照使用控制台创建 Lambda 函数的说明,在 AWS 管理控制台上使用以下示例 Python 代码创建 Lambda 函数。
"""Example Lambda function called by AWS::Hooks::LambdaHook."""
import logging
HOOK_INVOCATION_POINTS = [
"CREATE_PRE_PROVISION",
"UPDATE_PRE_PROVISION",
"DELETE_PRE_PROVISION",
]
TARGET_NAMES = [
"AWS::S3::Bucket",
]
LOGGER = logging.getLogger()
LOGGER.setLevel("INFO")
def lambda_handler(event, context):
"""Define the entry point of the function."""
try:
request = event
LOGGER.info(f"Request: {request}")
invocation_point = request["actionInvocationPoint"]
LOGGER.info(f"Invocation point: {invocation_point}")
target_name = request["requestData"]["targetName"]
LOGGER.info(f"Target name: {target_name}")
clientRequestToken = request["clientRequestToken"]
if (
invocation_point not in HOOK_INVOCATION_POINTS
or target_name not in TARGET_NAMES
):
message = (
f"Skipping {target_name} evaluation for {invocation_point}."
)
LOGGER.info(message)
payload = {
"clientRequestToken": clientRequestToken,
"hookStatus": "SUCCESS",
"errorCode": None,
"message": message,
"callbackContext": None,
"callbackDelaySeconds": 0,
}
LOGGER.debug(payload)
return payload
target_model = request["requestData"]["targetModel"]
resource_properties = (
target_model.get("resourceProperties")
if target_model and target_model.get("resourceProperties")
else None
)
LOGGER.debug(f"Resource properties: {resource_properties}")
versioning_configuration = (
resource_properties.get("VersioningConfiguration")
if resource_properties
and resource_properties.get("VersioningConfiguration")
else None
)
versioning_configuration_status = (
versioning_configuration.get("Status")
if versioning_configuration
and versioning_configuration.get("Status")
else None
)
if (
not resource_properties
or not versioning_configuration
or not versioning_configuration_status
or not versioning_configuration_status == "Enabled"
):
message = "Versioning not set or not enabled for the S3 bucket."
LOGGER.error(message)
payload = {
"clientRequestToken": clientRequestToken,
"hookStatus": "FAILED",
"errorCode": "NonCompliant",
"message": message,
}
else:
message = "Versioning is enabled for the S3 bucket."
LOGGER.info(message)
payload = {
"clientRequestToken": clientRequestToken,
"hookStatus": "SUCCESS",
"errorCode": None,
"message": message,
}
LOGGER.debug(payload)
return payload
except Exception as exception:
message = str(exception)
payload = {
"clientRequestToken": event["clientRequestToken"],
"hookStatus": "FAILED",
"errorCode": "InternalFailure",
"message": message,
"callbackContext": None,
"callbackDelaySeconds": 0,
}
LOGGER.error(message)
return payload
Hook 发送给 Lambda 函数的事件数据示例
{
"clientRequestToken": "XXXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"awsAccountId": "111111111111",
"stackId": "arn:aws:cloudformation:<AWS-Region>:111111111111:stack/example-stack/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"changeSetId": "None",
"hookTypeName": "AWS::Hooks::LambdaHook",
"hookTypeVersion": "00000001",
"hookModel":
{
"LambdaFunction": "example-hook-function-name",
},
"actionInvocationPoint": "CREATE_PRE_PROVISION",
"requestData":
{
"targetName": "AWS::S3::Bucket",
"targetType": "AWS::S3::Bucket",
"targetLogicalId": "Bucket",
"callerCredentials": "None",
"providerCredentials": "None",
"providerLogGroupName": "None",
"hookEncryptionKeyArn": "None",
"hookEncryptionKeyRole": "None",
"targetModel":
{
"resourceProperties":
{
"PublicAccessBlockConfiguration":
{ "RestrictPublicBuckets": true, "IgnorePublicAcls": true },
"BucketName": "XXXXXXXXXXXXXXXXXXXXXXXXXXX",
"VersioningConfiguration": { "Status": "Enabled" },
},
"previousResourceProperties": "None",
},
},
"requestContext": { "invocation": 1, "callbackContext": "None" },
}
Lambda 函数代码说明
这段 Lambda 函数代码用于处理来自 Lambda Hook 的事件,并验证目标 S3 存储桶资源是否启用了版本控制功能。下面是详细的代码说明:
- 该函数首先从事件数据中提取相关信息,包括调用点和目标资源类型。
- 接着检查当前调用点是否包含在配置的 HOOK_INVOCATION_POINTS 列表中,并验证目标资源类型是否为 AWS::S3::Bucket。如果不满足条件,该函数将返回成功响应,并跳过验证。
注意:这段跳过验证的代码是一个回退逻辑,用于处理不满足目标过滤条件的情况。由于这是一个通配符 Hook,如果不使用目标过滤条件,模板中包含的所有 AWS 资源类型都会触发该 Hook。此外,由于 Hook 默认的触发目标为 preCreate、preUpdate 和 preDelete,系统会在这些调用点默认触发该 Hook。如要缩小触发范围并降低成本,请设置目标过滤条件来避免为所有 AWS 资源类型目标和调用点触发该 Hook。
- 然后,该函数从事件数据中获取资源属性,特别是 VersioningConfiguration 属性及其 Status 参数。
- 如果未找到 VersioningConfiguration 属性,或其 Status 未设置为 Enabled,函数将返回失败响应,表明该 S3 存储桶未启用版本控制。
- 如果已启用版本控制,该函数将返回成功响应。
- 此外,该函数还包含一个回退机制,用于在其他任何异常情况下返回失败响应。通过分析这段示例代码,你可以了解如何基于基础设施即代码策略,在 CloudFormation 堆栈的创建和更新过程中,验证 S3 存储桶的版本控制配置。
在你的 AWS 账户和区域中启用 Lambda Hook
- 登录 AWS 管理控制台,进入 AWS CloudFormation 控制台,然后在 Hooks 页面选择 Create Hook(创建 Hook)→with Lambda(使用 Lambda):
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lambda-Create-Hooks.ae8cbc13c7a8fff69096daeb5e48dc6d54e09161.png)
图 2:Lambda Hook 创建页面
该页面展示了如何将 Lambda 函数作为 Hook 使用。
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lambda-hooks-Provide-Lambda.77170f008d91dc948ca1a6d7589012416d3445aa.png)
图 3:在控制台上为 Hook 配置 Lambda 函数
- 设置 Hook 的详细信息:名称、要使用的 Lambda 函数、类型和模式。你还可以直接在 Hook 配置页面中,选择 “New execution role”(新建执行角色)来创建执行角色。
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lambda-Hooks-Hooks-Details-Config.094657b770b9b19e073b9b2e9f57286a79f238bd.png)
图 4:在控制台上为 Hook 配置 Lambda 函数
你可以在下一页检查 Lambda Hook 的配置并激活它。
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lambda-Hooks-Review-And-Activate%20(1).1c5378b35ed6317e8b2d53c0dd46c98f80457b88.png)
图 5:Lambda Hook 检查页面
测试 Hook
本节将展示如何测试你为 S3 存储桶资源激活的 Hook 和 Lambda 函数。
创建一个未启用版本控制的 S3 存储桶
AWSTemplateFormatVersion: "2010-09-09"
Description: This CloudFormation template provisions an S3 Bucket without versioning enabled
Resources:
S3Bucket:
DeletionPolicy: Delete
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub test-bucket-versioning-1-${AWS::Region}-${AWS::AccountId}
由于未启用版本控制,当 Hook 调用 Lambda 函数后,函数将返回失败消息。
当你使用上述模板创建或更新堆栈时,系统会调用 Lambda Hook,而由于未启用存储桶版本控制,Lambda 函数会返回失败消息。Lambda 函数会从事件数据中提取 resourceProperties,检查 VersioningConfiguration 属性,发现 Status 未设置为 Enabled。因此,如果使用上述未启用版本控制的 S3 存储桶模板,Lambda 函数会向 Hook 返回失败响应,导致 CloudFormation 堆栈操作失败,如下图所示。
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lambda-HooksFailure.98fad40f689d0e917485bd3dfa4b0a7f0dba6ee2.png)
图 6:Lambda Hook 失败的堆栈示例
创建启用版本控制的 S3 存储桶:接下来,我们来创建一个启用版本控制的 S3 存储桶,然后观察 Hook 验证的成功过程。
AWSTemplateFormatVersion: "2010-09-09"
Description: This CloudFormation template provisions an S3 Bucket with Versioning enabled
Resources:
S3Bucket:
DeletionPolicy: Delete
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub test-bucket-versioning-2-${AWS::Region}-${AWS::AccountId}
VersioningConfiguration:
Status: Enabled
在这个示例中,由于已启用版本控制,Hook 调用 Lambda 函数后,将收到成功消息
当你使用此 CloudFormation 模板创建堆栈时,系统会调用 Lambda Hook,而由于已启用版本控制,Lambda 函数会返回成功消息。Lambda 函数会从事件数据中提取 resourceProperties,检查 VersioningConfiguration 属性,发现 Status 已设为 Enabled。这样,Lambda 函数就会向 Hook 返回成功响应,允许 CloudFormation 堆栈操作继续执行,如下图所示。
![](https://d1.awsstatic.com/meixuadu/cloudformation-templates-with-aws-lambda/Lamnbda-Hooks-Success.3ac2b162a19e85769a0a1df8e5f340f20e9d7d2a.png)
图 6:Lambda Hook 响应成功的堆栈示例
通过测试这两种场景,你可以确认 Lambda Hook 和关联的 Lambda 函数能够按预期运行,在 CloudFormation 堆栈操作过程中正确执行 S3 存储桶版本控制策略。
清理资源
要清理资源,请参阅以下文档了解如何删除 CloudFormation 堆栈:在 AWS CloudFormation 控制台中删除堆栈或使用 AWS CLI 删除堆栈。请参阅相关文档了解如何停用 Hook。
总结
在本文中,我们探讨了 CloudFormation Hooks 的功能,以及如何利用 Hook 来扩展基础设施即代码 (IaC) 的部署能力。特别是,我们介绍了 Lambda Hook,这一预构建的 Hook 可以简化将自定义逻辑整合到 CloudFormation 堆栈的过程。
通过激活 Lambda Hook 并部署自定义 Lambda 函数,你可以在 CloudFormation 堆栈的创建和更新过程中验证 S3 存储桶的版本控制配置。这种方法让你能够在部署时就强制执行基础设施即代码 (IaC) 策略并确保合规性,无需依赖部署后的检查或间接的管理机制。借助熟悉的工具和工作流(如 AWS CLI、AWS SAM、CI/CD 管道或 AWS CDK),你可以更轻松地将自定义逻辑整合到 CloudFormation 部署中。这降低了传统 Hook 编排和打包带来的运维成本和复杂性,帮助你简化基础设施即代码 (IaC) 实践。
在继续构建和部署云基础设施的过程中,建议你探索更多可用的 CloudFormation Hook,具体可参阅 aws-cloudformation/aws-cloudformation-samples 和 aws-cloudformation/community-registry-extensions GitHub 仓库。本文展示的方法同样适用于 CloudFormation 支持的其他资源类型,你可以用相同的方法来验证和执行各类基础设施组件的策略,涵盖从 EC2 实例和 VPC 到数据库和应用程序服务在内的多种资源。
更多教程
快速搭建容量高达 35GB 的免费个人网盘
本教程将介绍如何搭建一个没有使用限制的免费私人网盘。
构建企业专属智能客服机器人
本文将演示如何结合多种服务,打造企业专属的智能客服。
使用生成式 AI 构建多语言问答知识库
使用多种服务,构建可汇总搜索结果的多语言知识库。
免费套餐
AWS 海外区域
拓展海外业务或个人体验
免费使用 100 余种云产品或服务, 长达 12 个月
AWS 中国区域
发展中国业务
免费使用 40 余种核心云服务产品,长达 12 个月