宏示例:创建和使用宏 - Amazon CloudFormation
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

宏示例:创建和使用宏

下面的示例演示了使用宏的过程:从在模板中定义宏,到为宏创建 Lambda 函数,再到在模板中使用宏。

在此示例中,我们创建一个简单的宏,它插入指定的字符串来替换已处理模板中的指定目标内容。然后我们将使用它在已处理模板的指定位置插入空白 WaitHandleCondition

宏示例:宏定义模板

在使用宏之前,我们首先要完成两件事:创建执行所需模板处理的 Lambda 函数,然后通过创建宏定义使该 Lambda 函数可用于 CloudFormation。

注意

我们将在本主题后面的内容中检查 Lambda 函数的实际代码。

以下示例模板包含示例宏的定义。为了使宏在特定 CloudFormation 账户中可用,我们从模板创建一个堆栈。宏定义指定宏名称、简要描述,并引用在模板中使用此宏时 CloudFormation 调用的 Lambda 函数的 ARN。(我们没有包含 LogGroupNameLogRoleARN 属性来用于记录错误日志。) 有关更多信息,请参阅 AWS::CloudFormation::Macro

在本示例中,假定从此模板创建的堆栈名为 JavaMacroFunc。由于宏 Name 属性设置为堆栈名称,因此生成的宏也名为 JavaMacroFunc

AWSTemplateFormatVersion: 2010-09-09 Resources: Macro: Type: AWS::CloudFormation::Macro Properties: Name: !Sub '${AWS::StackName}' Description: Adds a blank WaitConditionHandle named 'WaitHandle' FunctionName: "arn:aws:lambda:us-east-1:012345678910:function:JavaMacroFunc"

宏示例:宏使用情况模板

为了在这种情况下使用我们的宏,我们将使用 Fn::Transform 内部函数将其包含在模板中。

当我们使用下面的模板创建堆栈时,CloudFormation 会调用我们的示例宏。底层 Lambda 函数将一个指定的字符串替换为另一个指定的字符串。在此情况下,结果是空的 AWS::CloudFormation::WaitConditionHandle,并且已插入到处理的模板中。

请注意以下几点:

  • 要调用的宏被指定为 JavaMacroFunc,它来自上一个宏定义示例。

  • 将会向该宏传递两个参数,targetreplacement,它们代表目标字符串及其所需的替换值。

  • 该宏可以对 Type 节点的内容进行操作,因为 Type 是引用该宏的 Fn::Transform 函数的同级节点。

  • 生成的 AWS::CloudFormation::WaitConditionHandle 名为 2a

  • 该模板还包含一个该宏也可以访问的模板参数 ExampleParameter(但在这种情况下不使用)。

Parameters: ExampleParameter: Type: String Default: 'SampleMacro' Resources: 2a: Fn::Transform: Name: "JavaMacroFunc" Parameters: replacement: 'AWS::CloudFormation::WaitConditionHandle' target: '$$REPLACEMENT$$' Type: '$$REPLACEMENT$$'

宏示例:请求事件映射

当 CloudFormation 在堆栈创建期间处理我们的示例模板时,它将以下事件映射传递给在 JavaMacroFunc 宏定义中引用的 Lambda 函数。

fragment 包含 JSON,表示该宏可以处理的模板片段。此片段由 Fn::Transform 函数调用的同级函数调用组成,但不包括函数调用本身。此外,params 包含 JSON,表示宏参数。在这种情况下,是替换和目标。同样,templateParameterValues 包含 JSON,表示为整个模板指定的参数。

  • region

    us-east-1

  • accountId

    012345678910

  • fragment

    { "Type": "$$REPLACEMENT$$" }
  • transformId

    012345678910::JavaMacroFunc

  • params

    { "replacement": "AWS::CloudFormation::WaitConditionHandle", "target": "$$REPLACEMENT$$" }
  • requestId

    5dba79b5-f117-4de0-9ce4-d40363bfb6ab

  • templateParameterValues

    { "ExampleParameter": "SampleMacro" }

宏示例:Lambda 函数代码

以下是 Lambda 函数的实际代码,它是 JavaMacroFunc 示例宏的底层。它迭代响应中包含的模板片段(无论是字符串、列表还是映射格式),从而查找指定的目标字符串。如果它找到指定的目标字符串,则 Lambda 函数会使用指定的替换字符串替换目标字符串。否则,该函数会保持模板片段不变。然后,该函数将下面详细讨论的预期属性的映射返回到 CloudFormation。

package com.macroexample.lambda.demo; import java.util.List; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; public class LambdaFunctionHandler implements RequestHandler<Map<String, Object>, Map<String, Object>> { private static final String REPLACEMENT = "replacement"; private static final String TARGET = "target"; private static final String PARAMS = "params"; private static final String FRAGMENT = "fragment"; private static final String REQUESTID = "requestId"; private static final String STATUS = "status"; private static final String SUCCESS = "SUCCESS"; private static final String FAILURE = "FAILURE"; @Override public Map<String, Object> handleRequest(Map<String, Object> event, Context context) { // TODO: implement your handler final Map<String, Object> responseMap = new HashMap<String, Object>(); responseMap.put(REQUESTID, event.get(REQUESTID)); responseMap.put(STATUS, FAILURE); try { if (!event.containsKey(PARAMS)) { throw new RuntimeException("Params are required"); } final Map<String, Object> params = (Map<String, Object>) event.get(PARAMS); if (!params.containsKey(REPLACEMENT) || !params.containsKey(TARGET)) { throw new RuntimeException("replacement or target under Params are required"); } final String replacement = (String) params.get(REPLACEMENT); final String target = (String) params.get(TARGET); final Object fragment = event.getOrDefault(FRAGMENT, new HashMap<String, Object>()); final Object retFragment; if (fragment instanceof String) { retFragment = iterateAndReplace(replacement, target, (String) fragment); } else if (fragment instanceof List) { retFragment = iterateAndReplace(replacement, target, (List<Object>) fragment); } else if (fragment instanceof Map) { retFragment = iterateAndReplace(replacement, target, (Map<String, Object>) fragment); } else { retFragment = fragment; } responseMap.put(STATUS, SUCCESS); responseMap.put(FRAGMENT, retFragment); return responseMap; } catch (Exception e) { e.printStackTrace(); context.getLogger().log(e.getMessage()); return responseMap; } } private Map<String, Object> iterateAndReplace(final String replacement, final String target, final Map<String, Object> fragment) { final Map<String, Object> retFragment = new HashMap<String, Object>(); final List<String> replacementKeys = new ArrayList<>(); fragment.forEach((k, v) -> { if (v instanceof String) { retFragment.put(k, iterateAndReplace(replacement, target, (String)v)); } else if (v instanceof List) { retFragment.put(k, iterateAndReplace(replacement, target, (List<Object>)v)); } else if (v instanceof Map ) { retFragment.put(k, iterateAndReplace(replacement, target, (Map<String, Object>) v)); } else { retFragment.put(k, v); } }); return retFragment; } private List<Object> iterateAndReplace(final String replacement, final String target, final List<Object> fragment) { final List<Object> retFragment = new ArrayList<>(); fragment.forEach(o -> { if (o instanceof String) { retFragment.add(iterateAndReplace(replacement, target, (String) o)); } else if (o instanceof List) { retFragment.add(iterateAndReplace(replacement, target, (List<Object>) o)); } else if (o instanceof Map) { retFragment.add(iterateAndReplace(replacement, target, (Map<String, Object>) o)); } else { retFragment.add(o); } }); return retFragment; } private String iterateAndReplace(final String replacement, final String target, final String fragment) { System.out.println(replacement + " == " + target + " == " + fragment ); if (fragment != null AND_AND fragment.equals(target)) return replacement; return fragment; } }

宏示例:Lambda 函数响应

以下是 Lambda 函数返回到 Amazon CloudFormation 进行处理的映射。从 CloudFormation 发送的 requestId 匹配项,以及 SUCCESSstatus 值表示 Lambda 函数成功处理了请求中包含的模板片段。在此响应中,fragment 包含 JSON,表示要插入到已处理模板中的内容,而不是原始模板代码段。

  • requestId

    5dba79b5-f117-4de0-9ce4-d40363bfb6ab

  • status

    SUCCESS

  • fragment

    { "Type": "AWS::CloudFormation::WaitConditionHandle" }

宏示例:生成的已处理模板

在 CloudFormation 从 Lambda 函数收到成功响应后,它将返回的模板片段插入到已处理模板中。

下面是我们示例的生成的已处理模板。不再包括引用 JavaMacroFunc 宏的 Fn::Transform 内部函数调用。Lambda 函数返回的模板片段包含在适当的位置,结果是内容 "Type": "$$REPLACEMENT$$" 已替换为 "Type": "AWS::CloudFormation::WaitConditionHandle"

{ "Parameters": { "ExampleParameter": { "Default": "SampleMacro", "Type": "String" } }, "Resources": { "2a": { "Type": "AWS::CloudFormation::WaitConditionHandle" } } }

其他宏示例

除了本指南中的此演练之外,您还可以在 GitHub 上的 Amazon Web Services - Labs(亚马逊云科技 - 实验室)存储库的 Macros Examples(宏示例)部分中找到示例宏,包括源代码和模板。这些示例按“原样”提供,用于教学目的。