매크로 예제: 매크로 생성 및 사용 - AWS CloudFormation

매크로 예제: 매크로 생성 및 사용

다음 예제는 템플릿에서 매크로를 정의하는 것부터 매크로에 대한 Lambda 함수를 생성하고 템플릿에 있는 매크로를 사용하는 것에 이르기까지 매크로 사용 프로세스를 연습합니다.

이 예제에서 지정된 문자열을 처리된 콘텐츠의 지정된 대상 콘텐츠를 대신하여 삽입하는 단순한 매크로를 생성합니다. 또한 이를 사용하여 처리된 템플릿의 지정된 위치에 빈 WaitHandleCondition을 삽입합니다.

매크로 예제: 매크로 정의 템플릿

매크로를 사용하기 전에 2가지를 완료해야 합니다. 원하는 템플릿 처리를 수행하는 Lambda 함수를 생성하고, 매크로 정의를 생성함으로써 CloudFormation에서 Lambda 함수를 사용할 수 있도록 합니다.

참고

이 주제에서 Lambda 함수에 대한 실제 코드를 살펴보겠습니다.

다음 샘플 템플릿에는 예제 매크로에 대한 정의가 포함되어 있습니다. 특정 CloudFormation 계정에서 매크로를 사용할 수 있도록 하려면 템플릿에서 스택을 생성합니다. 매크로 정의는 매크로 이름, 간단한 설명을 지정하고, 이 매크로가 템플릿에서 사용될 때 CloudFormation에서 호출하는 Lambda 함수의 ARN을 참조합니다. (오류 로깅을 위해 LogGroupName 또는 LogRoleARN 속성을 포함하지 않았습니다.) 자세한 내용은 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으로 지정됩니다. 이는 이전 매크로 정의 예제에서 온 것입니다.

  • 매크로는 전달되는 2개의 파라미터로, 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 함수 코드

다음은 기본 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으로 반환하는 매핑입니다. requestId가 CloudFormation에서 전송된 것과 일치하는 경우 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 리포지토리에서 소스 코드 및 템플릿을 포함한 예제 매크로를 확인할 수 있습니다. 이러한 예제는 교육용으로 있는 그대로 제공됩니다.