Exemplo de macro: criar e usar uma macro - AWS CloudFormation

Exemplo de macro: criar e usar uma macro

O exemplo a seguir apresenta o processo do uso de macros, desde definir a macro em um modelo até criar uma função Lambda para essa macro e usar a macro em um modelo.

Neste exemplo, criamos uma macro simples que insere a sequência especificada no lugar do conteúdo de destino especificado no modelo processado. E, em seguida, vamos usá-la para inserir um espaço em branco WaitHandleCondition no local especificado do modelo processado.

Exemplo de macro: modelo de definição de macro

Antes de usar uma macro, é necessário concluir duas tarefas: criar a função do Lambda que executa o processamento de modelo desejado e, em seguida, disponibilizar essa função do Lambda ao CloudFormation, criando uma definição de macro.

nota

Vamos examinar o código real para a função do Lambda mais adiante neste tópico.

O modelo de exemplo a seguir contém a definição da nossa macro de exemplo. Para tornar a macro disponível em uma conta específica do CloudFormation, criamos uma pilha a partir do modelo. A definição da macro especifica o nome da macro, uma breve descrição e faz referência ao ARN da função Lambda que o CloudFormation invoca quando essa macro é usada em um modelo. (Não incluímos uma propriedade LogGroupName ou LogRoleARN para registro em log de erros.) Para obter mais informações, consulte AWS::CloudFormation::Macro.

Neste exemplo, suponha que a pilha criada a partir desse modelo se chame JavaMacroFunc. Como a propriedade Name da macro está definida como o nome da pilha, a macro resultante se chama JavaMacroFunc também.

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"

Exemplo de macro: modelo de uso de macro

Para usar nossa macro neste caso, vamos incluí-la em um modelo usando a função intrínseca Fn::Transform.

Quando você cria uma pilha usando o modelo a seguir, o CloudFormation chama nossa macro de exemplo. A função Lambda subjacente substitui uma sequência especificada por outra sequência especificada. Nesse caso, o resultado é um AWS::CloudFormation::WaitConditionHandle em branco inserido no modelo processado.

Observe o seguinte:

  • A macro a ser invocada é especificada como JavaMacroFunc, extraída do exemplo de definição de macro anterior.

  • A macro recebe dois parâmetros, target e replacement, que representam a sequência de destino e seu valor de substituição desejado.

  • A macro pode operar no conteúdo do nó Type, pois Type é um irmão da função Fn::Transform que referencia a macro.

  • O AWS::CloudFormation::WaitConditionHandle resultante é chamado 2a.

  • O modelo também contém um parâmetro de modelo, ExampleParameter, ao qual a macro também tem acesso (mas não usa neste caso).

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

Exemplo de macro: mapeamento de eventos de solicitação

Quando o CloudFormation processa nosso modelo de exemplo durante a criação da pilha, ele transmite o mapeamento de evento a seguir à função Lambda referenciada na definição de macro JavaMacroFunc.

fragment contém um JSON que representa o fragmento de modelo que a macro pode processar. Esse fragmento consiste nos irmãos da chamada da função Fn::Transform, mas não a chamada de função propriamente dita. Além disso, params contém um JSON que representa os parâmetros da macro. Nesse caso, a substituição e o destino. Da mesma forma, templateParameterValues contém um JSON que representa os parâmetros especificados para o modelo como um todo.

  • 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" }

Example de macro: código da função Lambda

Veja a seguir o código real para a função Lambda subjacente ao exemplo de macro JavaMacroFunc. Ele itera sobre o fragmento de modelo incluído na resposta (seja em formato de sequência, lista ou mapa), procurando a sequência de destino especificada. Se ele encontrar a sequência de destino especificada, a função Lambda substituirá a sequência de destino pela sequência de substituição especificada. Caso contrário, a função deixará o fragmento de modelo inalterado. Em seguida, a função retorna um mapa das propriedades esperadas, discutido em detalhes abaixo, para o 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; } }

Example de macro: resposta da função Lambda

Veja a seguir o mapeamento que a função do Lambda retorna ao AWS CloudFormation para processamento. As correspondências de requestId enviadas do CloudFormation e um valor status de SUCCESS indica que a função Lambda processou com êxito o fragmento de modelo incluído na solicitação. Nessa resposta, fragment contém um JSON que representa o conteúdo a ser inserido no modelo processado no lugar do trecho de modelo original.

  • requestId

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

  • status

    SUCCESS

  • fragment

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

Exemplo de macro: modelo processado resultante

Depois que o CloudFormation recebe uma resposta bem-sucedida da função do Lambda, ele insere o fragmento de modelo retornado no modelo processado.

Veja a seguir o modelo processado resultante para o nosso exemplo. A chamada de função intrínseca Fn::Transform que referenciava a macro JavaMacroFunc não está mais incluída. O fragmento de modelo retornado pela função do Lambda está incluído no local apropriado, com o resultado de que o conteúdo "Type": "$$REPLACEMENT$$" foi substituído por "Type": "AWS::CloudFormation::WaitConditionHandle".

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

Exemplos de macro adicionais

Além da orientação passo a passo apresentada neste guia, é possível encontrar macros de exemplo, incluindo código-fonte e modelos, em nosso repositório do GitHub. Esses exemplos são fornecidos "como estão" para fins de instrução.