巨集範例:建立和使用巨集 - AWS CloudFormation

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

巨集範例:建立和使用巨集

以下範例逐步介紹如何使用巨集,包括在範本中定義巨集、為巨集建立 Lambda 函數,然後在範本中使用巨集。

在這個範例中,我們會建立簡單巨集,在處理的範本中插入指定的字串,以取代指定的目標內容。然後,我們會使用它在處理的範本中的指定位置插入空白 WaitHandleCondition

巨集範例:巨集定義範本

在使用巨集之前,我們必須先完成兩件事:建立執行所需範本處理的 Lambda 函數,然後 CloudFormation 透過建立巨集定義讓該 Lambda 函數可供使用。

注意

在本主題稍後,我們會檢查 Lambda 函數的實際程式碼。

以下範例範本包含範例巨集的定義。為了讓巨集在特定 CloudFormation 帳戶中可用,我們會從範本建立堆疊。巨集定義會指定巨集名稱、簡短描述,並參考在ARN範本中使用此巨集時 CloudFormation叫用的 Lambda 函數。(我們沒有包含錯誤日誌記錄的LogGroupNameLogRoleARN屬性。) 如需詳細資訊,請參閱AWS::CloudFormation: 巨集

在此範例中,假設從此範本建立的堆疊名稱為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表示巨集參數。在此案例中是 replacement 和 target。同樣地,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 函數程式碼

以下是基於JavaMacroFunc示例宏的 Lambda 函數的實際代碼。它會逐一查看回應 (不論是字串、清單或映射格式) 包含的範本片段,尋找指定的目標字串。如果找到指定的目標字串,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 函數返回到 CloudFormation 進行處理的映射。傳送來源 CloudFormation的requestId相符項目和statusSUCCESS表示 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 儲存庫中找到範例巨集,包括原始程式碼和範本。這些範例「按現狀」提供作為教學用途。