使用模板宏对 CloudFormation 模板执行自定义处理 - AWS CloudFormation

使用模板宏对 CloudFormation 模板执行自定义处理

通过使用宏,您可以对模板执行自定义处理,包括查找并替换操作等简单操作以及整个模板的大型转换。

要了解可能性的广度,请考虑 AWS::IncludeAWS::Serverless 转换,这些转换是由 CloudFormation 托管的宏:

  • AWS::Include 转换 可让您将样板文件模板代码段插入您的模板中。

  • AWS::Serverless 转换 获取用 AWS Serverless Application Model(AWS SAM)语法编写的完整模板,将其转换并扩展为符合规范的 CloudFormation 模板。(有关无服务器应用程序和 AWS SAM 的更多信息,请参阅《AWS Lambda 开发人员指南》中的部署基于 Lambda 的应用程序。)

CloudFormation 宏的工作原理

使用宏处理模板有两个主要步骤:创建宏本身,然后使用宏来对模板执行处理。

要创建宏定义,您需要创建以下内容:

  • 一个执行模板处理的 AWS Lambda 函数。此 Lambda 函数接受一个代码段或整个模板,以及您定义的任何其他参数。它返回已处理的模板代码段或整个模板作为响应。

  • AWS::CloudFormation::Macro 类型的资源,使用户能够从 CloudFormation 模板内调用 Lambda 函数。此资源指定要为此宏调用的 Lambda函数的 ARN,以及辅助调试的其他可选属性。要在账户中创建此资源,请创建一个包含 AWS::CloudFormation::Macro 资源的模板,然后利用该模板创建具有自行管理权限的堆栈或堆栈集。AWSCloudFormation StackSets 目前不支持通过引用宏的模板创建或更新具有服务托管权限的堆栈集。

要使用宏,请在您的模板中引用宏:

  • 要处理模板的一个部分或代码段,请在 函数中引用宏(位置是相对于要转换的模板内容而言的)。当使用 Fn::Transform 时,您还可以传递它需要的任何指定参数。

  • 要处理整个模板,请在模板的 Transform 部分中引用宏。

接下来,您通常会创建一个更改集,然后执行它。(处理宏可能会添加您可能不知道的多个资源。为确保您了解宏所引入的所有更改,我们强烈建议您使用更改集。) CloudFormation 将指定的模板内容以及任何其他指定参数一并传递给在宏资源中指定的 Lambda 函数。Lambda 函数返回已处理的模板内容,无论是代码段还是整个模板。

在调用模板中的所有宏之后,CloudFormation 会生成包含已处理模板内容的更改集。检查更改集后,执行它来应用更改。

重要

如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用 CreateStackSetUpdateStackSet 操作并指定 CAPABILITY_AUTO_EXPAND 功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。

使用 Fn::Transform 内部函数或模板的 Transform 部分,将模板内容和关联参数传递给宏的底层 Lambda 函数,该函数返回处理的模板内容。
注意

如果您喜欢直接从处理的模板来创建或更新堆栈,则无需先查看更改集中提议的更改,而是可以在 CreateStackUpdateStack 请求期间指定 CAPABILITY_AUTO_EXPAND 功能来直接实现。仅当您知道宏执行的处理操作时,才应直接通过引用宏的模板创建堆栈。

有关更多信息,请参阅《AWS CloudFormation API 参考》中的 CreateStackUpdateStack

创建 CloudFormation 宏定义

创建宏定义时,该宏定义会使底层 Lambda 函数在指定的账户中可用,以便 CloudFormation 可以调用该函数来处理模板。

CloudFormation 宏函数接口

对于宏,CloudFormation 使用以下事件映射调用底层 Lambda 函数。CloudFormation 以 JSON 格式发送请求,并且要求函数响应也采用 JSON 格式。

{ "region" : "us-east-1", "accountId" : "$ACCOUNT_ID", "fragment" : { ... }, "transformId" : "$TRANSFORM_ID", "params" : { ... }, "requestId" : "$REQUEST_ID", "templateParameterValues" : { ... } }
  • 区域

    宏所在的区域。

  • accountId

    宏从中调用 Lambda 函数的账户的账户 ID。

  • fragment

    可用于自定义处理的模板内容,采用 JSON 格式。

    • 对于 Transform 模板部分中包含的宏,这是整个模板,除 Transform 部分外。

    • 对于在 Fn::Transform 内部函数调用中包含的宏,这包括基于内部函数在模板中的位置的所有同级节点(及其子节点),除 Fn::Transform 函数外。有关更多信息,请参阅 CloudFormation 宏范围

  • transformId

    调用此函数的宏的名称。

  • params

    对于 Fn::Transform 函数调用,是函数的任何指定参数。CloudFormation 在将这些参数传递给函数之前不会进行评估。

    对于在 Transform 模板部分中包含的宏,此部分为空。

  • requestId

    调用此函数的请求的 ID。

  • templateParameterValues

    在模板的 Parameters 部分中指定的任何参数。CloudFormation 在将这些参数传递给函数之前会进行评估。

CloudFormation 要求底层函数以如下 JSON 格式返回响应:

{ "requestId" : "$REQUEST_ID", "status" : "$STATUS", "fragment" : { ... } "errorMessage": "optional error message for failures" }
  • requestId

    调用此函数的请求的 ID。这必须在调用函数时与 CloudFormation 提供的请求 ID 匹配。

  • 状态

    请求的状态(不区分大小写)。应设置为 success。CloudFormation 会将任何其他响应都视为失败。

  • fragment

    CloudFormation 要包含在已处理模板中的已处理模板内容,包括同级内容。CloudFormation 会将传递给 Lambda 函数的模板内容替换为在 Lambda 响应中收到的模板片段。

    已处理模板内容必须是有效的 JSON,并且其在已处理模板中的包含必须生成有效的模板。

    如果函数实际上没有更改 CloudFormation 传递的模板内容,但您仍需要将该内容包含在已处理模板中,则函数需要将该模板内容在其响应中返回到 CloudFormation。

  • errorMessage

    解释转换失败原因的错误消息。CloudFormation 将在堆栈的 Stack details(堆栈详细信息)页面的 Events(事件)窗格中显示此错误消息。

    例如,“Error creating change set: Transform AWS 账户 account number::macro name failed with: error message string”。

有关在创建宏时的其他注意事项的信息,请参阅创建 CloudFormation 宏定义时的注意事项

CloudFormation 宏账户范围和权限

您只能在创建宏的账户中将宏用作资源。宏的名称在给定账户中必须是唯一的。但是,您可以通过在底层 Lambda 函数上启用跨账户访问,然后创建宏定义以在多个账户中引用该函数,从而在多个账户中提供相同的功能。在下面的示例中,三个账户包含宏定义,每个宏定义都指向相同的 Lambda 函数。

通过允许对 Lambda 函数进行跨账户存取,AWS 能让您在引用该函数的多个账户中创建宏。

有关更多信息,请参阅《AWS Lambda 开发人员指南》中的管理您的 AWS Lambda 资源的访问权限概述

要创建宏定义,用户必须具有在指定账户中创建堆栈或堆栈集的权限。

要使 CloudFormation 成功运行模板中包含的宏,用户必须对底层 Lambda 函数具有 Invoke 权限。为防止可能的权限升级,CloudFormation 会在运行宏时模拟用户。有关更多信息,请参阅《AWS Lambda 开发人员指南》中的 Lambda 权限模型和《IAM 用户指南》中的 AWS Lambda 的操作和条件上下文键

AWS::Serverless 转换AWS::Include 转换 转换是由 CloudFormation 托管的宏。使用这些转换不需要特殊权限,并且可以从任何账户在 CloudFormation 中使用。

调试 CloudFormation 宏

为了帮助调试,您还可以在为宏创建 AWS::CloudFormation::Macro 资源类型时指定 LogGroupNameLogRoleArn 属性。借助这些属性,您可以指定 CloudFormation 在调用宏的底层 AWS Lambda 函数时向其发送错误日志信息的 CloudWatch 日志组,以及 CloudFormation 在将日志条目发送到这些日志时应代入的角色。

Billing

当宏运行时,将对 Lambda 函数的所有者收取与执行该函数相关的任何费用。

AWS::Serverless 转换AWS::Include 转换 转换是由 CloudFormation 托管的宏。使用它们不产生任何费用。

创建 CloudFormation 宏定义时的注意事项

在创建宏定义时,请记住以下几点:

  • 只有在提供 AWS Lambda 的 AWS 区域 才支持宏。有关提供 Lambda 的区域的列表,请参阅 AWS Lambda 端点和配额

  • 任何已处理模板代码段都必须是有效的 JSON。

  • 任何已处理的模板代码段都必须通过创建堆栈、更新堆栈、创建堆栈集或更新堆栈集等操作的验证检查。

  • CloudFormation 会先解析宏,然后再处理模板。生成的模板必须是有效的 JSON,并且不得超出模板大小限制。

  • 由于 CloudFormation 处理模板中元素的顺序,宏无法在其返回给 CloudFormation 的已处理模板内容中包含模块。有关模块的更多信息,请参阅《CloudFormationCLI 用户指南》中的开发模块

  • 使用更新回滚功能时,CloudFormation 会使用原始模板的副本。即使包含的代码段发生更改,它也会回滚到原始模板。

  • 宏中不能嵌套宏,因为我们不会迭代处理宏。

  • 目前在宏中不支持 Fn::ImportValue 内部函数。

  • 模板中包含的内部函数会在任何宏之后被评估。因此,宏返回的已处理模板内容可以包括对内部函数的调用,并且像往常一样对其进行评估。

  • StackSets 目前不支持通过引用 CloudFormation 宏的模板,使用服务托管权限创建或更新堆栈集。

  • 如果堆栈集模板引用了一个或多个宏,则您必须直接通过处理过的模板创建或更新堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,请使用 CreateStackSetUpdateStackSet 操作并指定 CAPABILITY_AUTO_EXPAND 功能。处理宏可添加您可能不知道的多个资源。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。

  • 目前,更改集不支持嵌套堆栈。如果要使用引用宏包含嵌套堆栈的模板创建或更新堆栈,您必须直接创建或更新堆栈。要执行此操作,请使用 CreateStackUpdateStack 操作并指定 CAPABILITY_AUTO_EXPAND 功能。

创建 CloudFormation 宏定义:
  1. 构建一个 AWS Lambda 函数,用于处理 CloudFormation 模板。

    您构建的 Lambda 函数执行模板内容的处理。您的函数可以处理模板的任何部分,甚至是整个模板。有关函数必须遵循的事件映射的信息,请参阅CloudFormation 宏函数接口。有关在创建宏时的其他注意事项的信息,请参阅创建 CloudFormation 宏定义时的注意事项

  2. 创建一个模板,其中包含 AWS::CloudFormation::Macro 资源类型。

    • 您必须指定 NameFunctionName 属性。FunctionName 属性指定在 CloudFormation 运行宏时调用的 Lambda 函数的 ARN。

    • 为了帮助调试,您还可以指定 LogGroupNameLogRoleArn 属性。

  3. 通过在所需账户中包含宏的模板创建堆栈,或通过在管理员账户中引用宏的模板创建具有自行管理权限的堆栈集,然后在所需目标账户中创建堆栈实例。

    在 CloudFormation 成功创建包含宏定义的堆栈之后,该宏便可这些账户中使用。

在模板中使用 CloudFormation 宏

在 CloudFormation 成功创建包含宏定义的堆栈之后,该宏便可在这些账户中使用。您可以通过在模板中,在与要处理的模板内容相关的适当位置引用宏来使用宏。

CloudFormation 宏评估顺序

您可以在给定模板中引用多个宏,包括由 CloudFormation 托管的转换,例如 AWS::Include 转换AWS::Serverless 转换

将会根据宏在模板中的位置,从嵌套最深的宏向外直到最一般的宏,按顺序评估宏。对于模板中相同位置的宏,将会根据列出的顺序按顺序进行评估。

诸如 AWS::IncludeAWS::Transform 之类的转换在操作顺序和范围方面被视为与任何其他宏相同。

例如,在下面的模板示例中,CloudFormation 会首先评估 PolicyAdder 宏,因为这是模板中最深的嵌套宏。然后,CloudFormation 会首先评估 MyMacro,然后再评估 AWS::Serverless,因为在 Transform 部分中它列于 AWS::Serverless 之前。

AWSTemplateFormatVersion: 2010-09-09 Transform: [MyMacro, AWS::Serverless] Resources: WaitCondition: Type: AWS::CloudFormation::WaitCondition MyBucket: Type: 'AWS::S3::Bucket' Properties: BucketName: amzn-s3-demo-bucket Tags: [{"key":"value"}] 'Fn::Transform': - Name: PolicyAdder CorsConfiguration: [] MyEc2Instance: Type: 'AWS::EC2::Instance' Properties: ImageID: ami-1234567890abcdef0

CloudFormation 宏范围

在模板的 Transform 部分中引用的宏可以处理该模板的全部内容。

Fn::Transform 函数中引用的宏可以处理模板中该 Fn::Transform 函数的任何同级元素(包括子元素)的内容。

例如,在下面的模板示例中,AWS::Include 可以根据包含它的 Fn::Transform 函数的位置处理 MyBucket 属性。MyMacro 可以处理整个模板的内容,因为它包含在 Transform 部分中。

# Start of processable content for MyMacro AWSTemplateFormatVersion: 2010-09-09 Transform: [MyMacro] Resources: WaitCondition: Type: AWS::CloudFormation::WaitCondition MyBucket: Type: 'AWS::S3::Bucket' # Start of processable content for AWS::Include Properties: BucketName: amzn-s3-demo-bucket1 Tags: [{"key":"value"}] 'Fn::Transform': - Name: 'AWS::Include' Parameters: Location: s3://amzn-s3-demo-bucket2/MyFileName.yaml CorsConfiguration: [] # End of processable content for AWS::Include MyEc2Instance: Type: 'AWS::EC2::Instance' Properties: ImageID: ami-1234567890abcdef0 # End of processable content for MyMacro

更改集和 CloudFormation 宏

要使用引用宏的模板创建或更新堆栈,通常需要创建一个更改集,然后执行它。更改集描述 CloudFormation 将基于处理后的模板执行的操作。处理宏可添加您可能不知道的多个资源。为确保您已了解宏所引入的所有更改,我们强烈建议您使用更改集。在查看更改集后,您可以执行它来实际应用更改。

宏可以将 IAM 资源添加到您的模板中。对于这些资源,CloudFormation 要求您确认其功能。由于 CloudFormation 无法在处理模板前了解所添加的资源,因此您可能需要在创建更改集时确认 IAM 功能,具体取决于引用的宏是否包含 IAM 资源。这样,当您运行该更改集时,CloudFormation 会具有创建 IAM 资源所需的功能。

重要

如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用 CreateStackSetUpdateStackSet 操作并指定 CAPABILITY_AUTO_EXPAND 功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。

注意

如果您喜欢直接从处理的模板来创建或更新堆栈,则无需先查看更改集中提议的更改,而是可以在 CreateStackUpdateStack 请求期间指定 CAPABILITY_AUTO_EXPAND 功能来直接实现。仅当您知道宏在处理什么时,才应直接从包含宏的堆栈模板创建堆栈。您不能将更改集与堆栈集宏一起使用,必须直接更新堆栈集。

有关更多信息,请参阅《AWS CloudFormation API 参考》中的 CreateStackUpdateStack

如果您使用 AWS CLI,则可使用 packagedeploy 命令减少从引用宏的模板启动堆栈的步骤数。有关更多信息,请参阅《AWS Lambda 开发人员指南》中的部署基于 Lambda 的应用程序

模板阶段和 CloudFormation 宏

模板的阶段指示模板是原始用户提交的模板还是 CloudFormation 已在其中处理宏的模板。

  • Original:用户最初提交的用于创建或更新堆栈或堆栈集的模板。

  • Processed:CloudFormation 在处理任何引用的宏之后,用于创建或更新堆栈或堆栈集的模板。即使原始模板格式化为 YAML,已处理模板也会格式化为 JSON。

使用处理后的模板可解决堆栈问题。如果模板不引用宏,则原始模板和已处理模板是相同的。

您可以使用 CloudFormation 控制台AWS CLI 查看堆栈模板的阶段。

注意

当处理的堆栈模板直接传递到 CreateStackUpdateStackValidateTemplate 请求时,其最大大小为 51200 字节;或者当使用 Amazon S3 模板 URL 作为 S3 对象传递时,为 1MB。但是,在处理期间,CloudFormation 会更新模板的临时状态,因为它连续处理模板中包含的宏。因此,在处理期间,模板的大小可能会临时超出完全处理的模板所允许的大小。CloudFormation 允许为这些处理中的模板留出一些缓冲。但是,在设计模板和宏时,要始终考虑处理的堆栈模板所允许的最大大小。

如果在处理模板期间 CloudFormation 返回一个 Transformation data limit exceeded 错误,则说明您的模板超出了 CloudFormation 所允许的处理期间模板的最大大小。

要解决这一问题,可执行下列操作:

  • 将您的模板分成多个模板,以避免超出为处理中的模板规定的最大大小。例如:

  • 减小给定宏返回的模板片段大小。CloudFormation 不会篡改宏返回的片段内容。

在模板中使用 CloudFormation 宏
注意

要使 CloudFormation 成功运行模板中引用的宏,用户必须对底层 Lambda 函数具有 Invoke 权限。有关更多信息,请参阅《AWS Lambda 开发人员指南》中的管理您的 AWS Lambda 资源的访问权限概述

  1. 在模板中包含对宏的引用。

    • 要处理模板代码段,请在 Fn::Transform 函数中引用宏(位置是相对于要处理的模板内容而言的)。

    • 要处理整个模板,请在模板的 Transform 部分中引用宏。

  2. 使用模板创建更改集

    重要

    如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用 CreateStackSetUpdateStackSet 操作并指定 CAPABILITY_AUTO_EXPAND 功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。

  3. 审核并运行更改集

    重要

    如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用 CreateStackSetUpdateStackSet 操作并指定 CAPABILITY_AUTO_EXPAND 功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。

宏示例

除了本指南中的 宏示例:创建和使用宏 演练之外,您还可以在 GitHub 存储库中找到示例宏,包括源代码和模板。这些示例按“原样”提供,用于教学目的。

另请参阅

AWS::CloudFormation::Macro

Transform

Fn::Transform

AWS::Serverless 转换

AWS::Include 转换