使用 AWS CloudFormation 宏对模板执行自定义处理
通过使用宏,您可以对模板执行自定义处理,包括查找并替换操作等简单操作以及整个模板的大型转换。
要了解可能性的广度,请考虑 AWS::Include
和 AWS::Serverless
转换,它们是由 AWS CloudFormation 托管的宏:
-
AWS::Include 转换
可让您将样板文件模板代码段插入您的模板中。 -
AWS::Serverless 转换
获取用 AWS Serverless Application Model(AWS SAM)语法编写的整个模板,并将其转换并扩展为符合 AWS CloudFormation 的模板。(有关无服务器应用程序和 AWS SAM 的更多信息,请参阅《AWS Lambda 开发人员指南》中的部署基于 Lambda 的应用程序。)
AWS CloudFormation 宏的工作原理
使用宏处理模板有两个主要步骤:创建宏本身,然后使用宏来对模板执行处理。
要创建宏定义,您需要创建以下内容:
-
一个执行模板处理的 AWS Lambda 函数。此 Lambda 函数接受一个代码段或整个模板,以及您定义的任何其他参数。它返回已处理的模板代码段或整个模板作为响应。
-
类型为
AWS::CloudFormation::Macro
的资源,它使用户能够从 AWS CloudFormation 模板内调用 Lambda 函数。此资源指定要为此宏调用的 Lambda函数的 ARN,以及辅助调试的其他可选属性。要在账户中创建此资源,请创建一个模板(其中包含AWS::CloudFormation::Macro
资源),然后从该模板创建具有自行管理权限的堆栈或堆栈集。AWSCloudFormation StackSets 目前不支持通过引用宏的模板创建或更新具有服务托管权限的堆栈集。
要使用宏,请在您的模板中引用宏:
-
要处理模板的一个部分或代码段,请在
Fn::Transform
函数中引用宏(位置是相对于要转换的模板内容而言的)。当使用Fn::Transform
时,您还可以传递它需要的任何指定参数。 -
要处理整个模板,请在模板的 转换 部分中引用宏。
接下来,您通常会创建一个更改集,然后执行它。(处理宏可添加您可能不知道的多个资源。为确保您了解宏所引入的所有更改,我们强烈建议您使用更改集。)AWS CloudFormation 将指定的模板内容以及任何其他指定的参数一并传递给在宏资源中指定的 Lambda 函数。Lambda 函数返回已处理的模板内容,无论是代码段还是整个模板。
在调用模板中的所有宏之后,AWS CloudFormation 会生成包含已处理模板内容的更改集。检查更改集后,执行它来应用更改。
重要
如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用 CreateStackSet
或 UpdateStackSet
操作并指定 CAPABILITY_AUTO_EXPAND
功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。
![使用 Fn::Transform 内部函数或模板的 Transform 部分,将模板内容和关联参数传递给宏的底层 Lambda 函数,该函数返回处理的模板内容。](images/template-macro-use.png)
注意
如果您喜欢直接从处理的模板来创建或更新堆栈,则无需先查看更改集中提议的更改,而是可以在 CreateStack
或 UpdateStack
请求期间指定 CAPABILITY_AUTO_EXPAND
功能来直接实现。仅当您知道宏执行的处理操作时,才应直接通过引用宏的模板创建堆栈。
有关更多信息,请参阅《AWS CloudFormation API 参考》中的 CreateStack 或 UpdateStack。
创建 AWS CloudFormation 宏定义
当您创建宏定义时,该宏定义使底层 Lambda 函数在指定的账户中可用,以便 AWS CloudFormation 可以调用它来处理模板。
AWS CloudFormation 宏函数接口
对于宏,AWS CloudFormation 使用以下事件映射调用底层 Lambda 函数。AWS 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
函数外。有关更多信息,请参阅 AWS CloudFormation 宏范围。
-
-
transformId
调用此函数的宏的名称。
-
params
对于
Fn::Transform
函数调用,是函数的任何指定参数。AWS CloudFormation 在将这些参数传递给函数之前不会对它们进行评估。对于在
Transform
模板部分中包含的宏,此部分为空。 -
requestId
调用此函数的请求的 ID。
-
templateParameterValues
在模板的 参数 部分中指定的任何参数。AWS CloudFormation 在将这些参数传递给函数之前对其进行评估。
AWS CloudFormation 预计底层函数以以下 JSON 格式返回响应:
{ "requestId" : "
$REQUEST_ID
", "status" : "$STATUS
", "fragment" : {...
} "errorMessage": "optional error message for failures" }
-
requestId
调用此函数的请求的 ID。这必须在调用函数时与 AWS CloudFormation 提供的请求 ID 匹配。
-
状态
请求的状态(不区分大小写)。应设置为
success
。AWS CloudFormation 会将任何其他响应都视为失败。 -
fragment
要在已处理模板中包含的已处理 AWS CloudFormation 模板内容,包括同级内容。AWS CloudFormation 会将传递给 Lambda 函数的模板内容替换为它在 Lambda 响应中接收的模板片段。
已处理模板内容必须是有效的 JSON,并且其在已处理模板中的包含必须生成有效的模板。
如果您的函数实际上没有更改 AWS CloudFormation 传递给它的模板内容,但您仍需要将该内容包含在已处理模板中,则您的函数需要将该模板内容返回到其响应中的 AWS CloudFormation。
-
errorMessage
解释转换失败原因的错误消息。CloudFormation 将在堆栈的 Stack details(堆栈详细信息)页面的 Events(事件)窗格中显示此错误消息。
例如,“Error creating change set: Transform
AWS 账户 account number
::macro name
failed with:error message string
”。
有关在创建宏时的其他注意事项的信息,请参阅创建 AWS CloudFormation 宏定义时的注意事项。
AWS CloudFormation 宏账户范围和权限
您只能在创建宏的账户中将宏用作资源。宏的名称在给定账户中必须是唯一的。但是,您可以通过在底层 Lambda 函数上启用跨账户访问,然后创建宏定义以在多个账户中引用该函数,从而在多个账户中提供相同的功能。在下面的示例中,三个账户包含宏定义,每个宏定义都指向相同的 Lambda 函数。
![通过允许对 Lambda 函数进行跨账户存取,AWS 能让您在引用该函数的多个账户中创建宏。](images/template-macro-accounts.png)
有关更多信息,请参阅《AWS Lambda 开发人员指南》中的管理您的 AWS Lambda 资源的访问权限概述。
要创建宏定义,用户必须具有在指定账户中创建堆栈或堆栈集的权限。
为了使 AWS CloudFormation 成功运行模板中包含的宏,用户必须对底层 Lambda 函数具有 Invoke
权限。为了防止可能的权限升级,AWS CloudFormation 会在运行宏时模拟用户。有关更多信息,请参阅《AWS Lambda 开发人员指南》中的 Lambda 权限模型和《IAM 用户指南》中的 AWS Lambda 的操作和条件上下文键。
AWS::Serverless 转换 和 AWS::Include 转换 转换是由 AWS CloudFormation 托管的宏。使用它们不需要特殊权限,并且可以从 AWS CloudFormation 内的任何账户中获得它们。
调试 AWS CloudFormation 宏
为了帮助调试,您还可以在为宏创建 AWS::CloudFormation::Macro 资源类型时指定 LogGroupName
和 LogRoleArn
属性。通过这些属性,您可以指定 AWS CloudFormation 在调用宏的底层 AWS Lambda 函数时向其发送错误日志记录信息的 CloudWatch 日志组,以及 AWS CloudFormation 在将日志条目发送到这些日志时应该担任的角色。
Billing
当宏运行时,将对 Lambda 函数的所有者收取与执行该函数相关的任何费用。
AWS::Serverless 转换 和 AWS::Include 转换 转换是由 AWS CloudFormation 托管的宏。使用它们不产生任何费用。
创建 AWS CloudFormation 宏定义时的注意事项
在创建宏定义时,请记住以下几点:
-
只有在提供 AWS Lambda 的 AWS 区域 才支持宏。有关提供 Lambda 的区域的列表,请参阅 AWS Lambda 端点和配额。
-
任何已处理模板代码段都必须是有效的 JSON。
-
任何已处理的模板代码段都必须通过创建堆栈、更新堆栈、创建堆栈集或更新堆栈集等操作的验证检查。
-
AWS CloudFormation 先解析宏,然后再处理模板。生成的模板必须是有效的 JSON,并且不得超出模板大小限制。
-
由于 CloudFormation 处理模板中元素的顺序,宏无法在其返回给 CloudFormation 的已处理模板内容中包含模块。有关模块的更多信息,请参阅《CloudFormationCLI 用户指南》中的开发模块。
-
使用更新回滚功能时,AWS CloudFormation 会使用原始模板的副本。即使包含的代码段发生更改,它也会回滚到原始模板。
-
在宏中包含宏是行不通的,因为我们不迭代处理宏。
-
目前在宏中不支持
Fn::ImportValue
内部函数。 -
模板中包含的内部函数会在任何宏之后被评估。因此,宏返回的已处理模板内容可以包括对内部函数的调用,并且像往常一样对其进行评估。
-
StackSets 目前不支持通过引用 AWS CloudFormation 宏的模板创建或更新具有服务托管权限的堆栈集。
-
如果堆栈集模板引用了一个或多个宏,则您必须直接通过处理过的模板创建或更新堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,请使用
CreateStackSet
或UpdateStackSet
操作并指定CAPABILITY_AUTO_EXPAND
功能。处理宏可添加您可能不知道的多个资源。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。 -
目前,更改集不支持嵌套堆栈。如果要使用引用宏和包含嵌套堆栈的模板创建或更新堆栈,您必须直接创建或更新堆栈。要执行此操作,请使用
CreateStack
或UpdateStack
操作并指定CAPABILITY_AUTO_EXPAND
功能。
创建 AWS CloudFormation 宏定义:
-
构建一个 AWS Lambda 函数,用于处理 AWS CloudFormation 模板。
您构建的 Lambda 函数执行模板内容的处理。您的函数可以处理模板的任何部分,甚至是整个模板。有关函数必须遵循的事件映射的信息,请参阅AWS CloudFormation 宏函数接口。有关在创建宏时的其他注意事项的信息,请参阅创建 AWS CloudFormation 宏定义时的注意事项。
-
创建模板,在其中包含 AWS::CloudFormation::Macro 资源类型。
-
您必须指定
Name
和FunctionName
属性。FunctionName
属性指定在 AWS CloudFormation 运行宏时调用的 Lambda 函数的 ARN。 -
为了帮助调试,您还可以指定
LogGroupName
和LogRoleArn
属性。
-
-
通过在所需账户中包含宏的模板创建堆栈,或通过在管理员账户中引用宏的模板创建具有自行管理权限的堆栈集,然后在所需目标账户中创建堆栈实例。
在 AWS CloudFormation 成功创建包含宏定义的堆栈之后,该宏便可这些账户中使用。
在模板中使用 AWS CloudFormation 宏
在 AWS CloudFormation 成功创建包含宏定义的堆栈之后,该宏便可在这些账户中使用。您可以通过在模板中,在与要处理的模板内容相关的适当位置引用宏来使用宏。
AWS CloudFormation 宏评估顺序
您可以在给定模板中引用多个宏,包括由 AWS CloudFormation 托管的转换,例如 AWS::Include 转换 和 AWS::Serverless 转换。
将会根据宏在模板中的位置,从嵌套最深的宏向外直到最一般的宏,按顺序评估宏。对于模板中相同位置的宏,将会根据列出的顺序按顺序进行评估。
诸如 AWS::Include
和 AWS::Transform
之类的转换在操作顺序和范围方面被视为与任何其他宏相同。
例如,在下面的模板示例中,AWS CloudFormation 首先评估 PolicyAdder
宏,因为它是模板中嵌套最深的宏。然后,AWS CloudFormation 在评估 AWS::Serverless
之前评估 MyMacro
,因为它在 Transform
部分中在 AWS::Serverless
之前列出。
AWSTemplateFormatVersion: 2010-09-09 Transform: [MyMacro, AWS::Serverless] Resources: WaitCondition: Type: AWS::CloudFormation::WaitCondition MyBucket: Type: 'AWS::S3::Bucket' Properties: BucketName: MyBucket Tags: [{"key":"value"}] 'Fn::Transform': - Name: PolicyAdder CorsConfiguration:[] MyEc2Instance: Type: 'AWS::EC2::Instance' Properties: ImageID: "ami-123"
AWS 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: MyBucket Tags: [{"key":"value"}] 'Fn::Transform': - Name: 'AWS::Include' Parameters: Location: s3://DOC-EXAMPLE-BUCKET/MyFileName.yaml CorsConfiguration:[] //End of processable content for AWS::Include MyEc2Instance: Type: 'AWS::EC2::Instance' Properties: ImageID: "ami-123" // End of processable content for MyMacro
更改集和 AWS CloudFormation 宏
要使用引用宏的模板创建或更新堆栈,通常需要创建一个更改集,然后执行它。更改集描述 CloudFormation 将基于处理后的模板执行的操作。处理宏可添加您可能不知道的多个资源。为确保您已了解宏所引入的所有更改,我们强烈建议您使用更改集。在查看更改集后,您可以执行它来实际应用更改。
宏可以将 IAM 资源添加到您的模板中。对于这些资源,AWS CloudFormation 要求您确认其功能。由于 AWS CloudFormation 无法在处理模板前了解所添加的资源,因此您可能需要在创建更改集时确认 IAM 功能,具体取决于引用的宏是否包含 IAM 资源。这样一来,当您运行更改集时,AWS CloudFormation 会具有创建 IAM 资源所需的功能。
重要
如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用 CreateStackSet
或 UpdateStackSet
操作并指定 CAPABILITY_AUTO_EXPAND
功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。
注意
如果您喜欢直接从处理的模板来创建或更新堆栈,则无需先查看更改集中提议的更改,而是可以在 CreateStack
或 UpdateStack
请求期间指定 CAPABILITY_AUTO_EXPAND
功能来直接实现。仅当您知道宏在处理什么时,才应直接从包含宏的堆栈模板创建堆栈。您不能将更改集与堆栈集宏一起使用,必须直接更新堆栈集。
有关更多信息,请参阅《AWS CloudFormation API 参考》 中的 CreateStack
或 UpdateStack
。
如果您使用 AWS CLI,则可使用 package
和 deploy
命令减少从引用宏的模板启动堆栈的步骤数。有关更多信息,请参阅《AWS Lambda 开发人员指南》中的部署基于 Lambda 的应用程序。
模板阶段和 CloudFormation 宏
模板的阶段指示模板是原始用户提交的模板还是 AWS CloudFormation 已在其中处理宏的模板。
-
Original
:用户最初提交的用于创建或更新堆栈或堆栈集的模板。 -
Processed
:模板 AWS CloudFormation 用于在处理任何引用的宏之后创建或更新堆栈或堆栈集。即使原始模板格式化为 YAML,已处理模板也会格式化为 JSON。
使用处理后的模板可解决堆栈问题。如果模板不引用宏,则原始模板和已处理模板是相同的。
您可以使用 AWS CloudFormation 控制台或 AWS CLI 查看堆栈模板的阶段。
注意
当处理的堆栈模板直接传递到 CreateStack
、UpdateStack
或 ValidateTemplate
请求时,其最大大小为 51200 字节;或者当使用 Amazon S3 模板 URL 作为 S3 对象传递时,为 1MB。但是,在处理期间,CloudFormation 会更新模板的临时状态,因为它连续处理模板中包含的宏。因此,在处理期间,模板的大小可能会临时超出完全处理的模板所允许的大小。CloudFormation 允许为这些处理中的模板留出一些缓冲。但是,在设计模板和宏时,要始终考虑处理的堆栈模板所允许的最大大小。
如果在处理模板期间 CloudFormation 返回一个 Transformation data limit exceeded
错误,则说明您的模板超出了 CloudFormation 所允许的处理期间模板的最大大小。
要解决这一问题,可执行下列操作:
-
将您的模板分成多个模板,以避免超出为处理中的模板规定的最大大小。例如:
-
使用嵌套堆栈模板来封装模板的不同部分。有关更多信息,请参阅 使用嵌套堆栈。
-
创建多个堆栈,并使用跨堆栈引用来相互交换信息。有关更多信息,请参阅 演练:引用另一个 AWS CloudFormation 堆栈中的资源输出。
-
-
减小给定宏返回的模板片段大小。CloudFormation 不会篡改宏返回的片段内容。
在模板中使用 AWS CloudFormation 宏
注意
为了使 AWS CloudFormation 成功运行模板中引用的宏,用户必须对底层 Lambda 函数具有 Invoke
权限。有关更多信息,请参阅《AWS Lambda 开发人员指南》中的管理您的 AWS Lambda 资源的访问权限概述。
-
在模板中包含对宏的引用。
-
要处理模板代码段,请在
Fn::Transform
函数中引用宏(位置是相对于要处理的模板内容而言的)。 -
要处理整个模板,请在模板的 转换 部分中引用宏。
-
-
使用模板创建更改集。
重要
如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用
CreateStackSet
或UpdateStackSet
操作并指定CAPABILITY_AUTO_EXPAND
功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。 -
审核并运行更改集。
重要
如果堆栈集模板引用了一个或多个宏,则必须直接从处理过的模板创建堆栈集,而无需先查看更改集中生成的更改。要直接创建或更新堆栈集,必须使用
CreateStackSet
或UpdateStackSet
操作并指定CAPABILITY_AUTO_EXPAND
功能。在通过直接引用宏的模板创建或更新堆栈集之前,请确保您知道宏执行的处理操作。
宏示例
除了本指南中的 宏示例:创建和使用宏 演练之外,您还可以在 GitHub 存储库