亚马逊AWS官方博客

使用 AWS Lambda 支持的宏扩展 AWS CloudFormation

今天,我非常高兴为大家介绍 AWS CloudFormation 的一个强大新功能,它叫做 Macros。CloudFormation Macros 允许开发人员调用 AWS Lambda 支持的转换函数,从而扩展 CloudFormation 模板的原生句法。它采用与支持流行的无服务器应用程序模型功能所用的相同技术,但转换函数 (transform) 在您自己的账户中运行,使用您自己的 lambda 函数,并且是完全可自定义的。如果您还不熟悉 AWS,请记住 CloudFormation 是一个对基础设施即代码(YAML 或 JSON)的建模和定义绝对关键的工具。它也是所有 AWS 产品的核心组件之一,我们的许多服务都依赖它而存在。

宏的使用有两个主要步骤。首先,我们需要定义宏。当然,为此我们需要使用 CloudFormation 模板。然后,为了在我们的模板中使用宏,我们需要将它添加为整个模板的转换函数,或者直接调用它。在本博文中,宏和转换函数这两个术语基本可以互换。已经准备好了解其工作原理了?

创建 CloudFormation 宏

宏的创建有两个要素:一是定义,二是实现。如要创建宏的定义,我们需要一个类型为 AWS::CloudFormation::Macro 的 CloudFormation 资源,该资源定义了需要使用的 Lamda 函数以及应当调用的宏。

Type: "AWS::CloudFormation::Macro"
Properties:
  Description: String
  FunctionName: String
  LogGroupName: String
  LogRoleARN: String
  Name: String

宏的 Name 必须在整个区域是唯一的,并且通过 FunctionName 引用的 Lambda 函数必须与要创建的宏位于同一区域。您执行宏模板时,将会使该宏对其他模板也同样可用。宏的实现由 Lambda 函数完成。宏可以位于自己的模板中,也可与其他宏组合,但无法在注册宏的模板中使用该宏。Lambda 函数会收到与以下类似的 JSON 负载:

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

负载的 fragment 部分包含了整个模板或者模板的相关区段 — 具体取决于如何从调用宏的模板中调用转换函数。区段始终在 JSON 中,即使模板在 YAML 中亦是如此。

Lambda 函数预计将会返回一个简单的 JSON 应答:

{
    "requestId": "$REQUEST_ID",
    "status": "success",
    "fragment": { ... }
}

requestId 需要输入负载中收到的相同,如果 status 包含除 success(不区分大小写)以外的任何值,则变更集的创建将会失败。然后,fragment 必须包含转换后模板的有效 CloudFormation JSON。即使您的函数未执行任何操作,仍然需要返回它的区段以包含到最终模板中。

使用 CloudFormation 宏


如要使用宏,我们只需使用要求的参数调用Fn::Transform。如果您希望利用宏来对整个模板进行句法分析,则我们可以按照与 SAM: Transform: [Echo] 相同的方式,将其列入模板中的转换函数列表中。在我们执行模板时,通过调用每个宏的制定函数并返回最终模板,转换函数将会被收集到变更集中。

假设我们有一个名为 EchoFunction 的 虚拟 Lambda 函数,它只是记录通过它的数据并返回未加任何修改的区段。我们将宏定义为一种正常的 CloudFormation 资源,与以下类似:

EchoMacro:
  Type: "AWS::CloudFormation::Macro"
  Properties:
    FunctionName: arn:aws:lambda:us-east-1:1234567:function:EchoFunction
	Name: EchoMacro

Lambda 函数的代码可能简单如下:

def lambda_handler(event, context):
    print(event)
    return {
        "requestId": event['requestId'],
        "status": "success",
        "fragment": event["fragment"]
    }

然后在部署此函数并执行宏模板后,我们可以在任何其他模板的顶层的转换函数中调用宏,与以下类似:

AWSTemplateFormatVersion: 2010-09-09 
 Transform: [EchoMacro, AWS::Serverless-2016-10-31]
 Resources:
    FancyTable:
      Type: AWS::Serverless::SimpleTable

CloudFormation 会首先调用我们定义的 Echo 宏,然后调用 AWS::Serverless 转换函数,从而创建模板的变更集。它将按照列表中的顺序执行转换函数中列举的宏。

此外,我们还可以使用内部函数 Fn::Transform 调用宏,该函数允许我们输入额外的参数。例如:

AWSTemplateFormatVersion: 2010-09-09
Resources:
  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Fn::Transform:
      Name: EchoMacro
      Parameters:
        Key: Value

内联转换函数将拥有其所有同级节点和下级节点的访问权限。转换函数的处理顺序为由深到浅,这意味着最顶层的转换函数将会最后执行。我知道大多数人都会说:不对,你不能在宏中包含宏 — 但想法不错。

在您执行 CloudFormation 模板时,它将会直接要求您创建一个变更集,并且您可以在部署前预览输出结果。

示例宏

我们正在推出多个参考宏,以帮助开发人员上手,同时我预计许多人也将发布更多的宏。以下四个宏是在此功能发布前,一个小的内部骇客马拉松的优胜作品:

名称 描述 作者
PyPlate 允许您在模板中插入内敛 Python 函数 Jay McConnel — 合作伙伴解决方案架构师
ShortHand 定义通用 cloudformation 资源的简略句法 Steve Engledow — 解决方案构建师
StackMetrics 为堆栈添加 Cloudwatch 指标 Steve Engledow 和 Jason Gregson — 全球解决方案架构师
String Functions 为您的模板添加常用的串函数 Jay McConnel — 合作伙伴解决方案架构师

以下是我认为有人可能会感兴趣实现的一些理念:

如果您有任何奇妙的想法,我非常期待您写出来!

现已推出

CloudFormation Macros 从今天开始已在所有支持 AWS Lambda 的 AWS 区域推出。使用 Macros 不会发生额外的 CloudFormation 费用,这意味着您只需承担正常的 AWS Lambda 函数费用。更多信息请参阅文档

这是我最喜爱的 CloudFormation 新功能之一,我非常期待看到客户利用它创造出神奇的东西。它的真正强大之处在于,您可以使用代码来扩展现有的基础设施即代码。这种新功能带来的可能性几乎是无限的。

本篇作者

Randall Hunt

AWS 全球高级布道师。此前供职于 NASA, SpaceX 及 MongoDB。