

# Instruções passo a passo: criar um mecanismo de atraso com um recurso personalizado apoiado pelo Lambda
<a name="walkthrough-lambda-backed-custom-resources"></a>

Este passo a passo mostra como configurar e executar um recurso personalizado com suporte do Lambda usando um modelo de exemplo do CloudFormation. Este modelo cria um mecanismo de atraso que pausa as implantações de pilha por um período especificado. Isso pode ser útil quando você precisa introduzir atrasos deliberados durante o provisionamento de recursos, como ao esperar que os recursos se estabilizem antes que os recursos dependentes sejam criados.

**nota**  
Embora os recursos personalizados baseados em Lambda tenham sido recomendados anteriormente para recuperar IDs de AMI, recomendamos utilizar parâmetros do AWS Systems Manager. Essa abordagem torna seus modelos mais reutilizáveis e fáceis de manter. Para obter mais informações, consulte [Obter um valor em texto simples do Systems Manager Parameter Store](dynamic-references-ssm.md). 

**Topics**
+ [Visão geral](#walkthrough-lambda-backed-custom-resources-overview)
+ [Modelo de exemplo](#walkthrough-lambda-backed-custom-resources-sample-template)
+ [Modelo de passo a passo](#walkthrough-lambda-backed-custom-resources-sample-template-walkthrough)
+ [Pré-requisitos](#walkthrough-lambda-backed-custom-resources-prerequisites)
+ [Iniciar a pilha](#walkthrough-lambda-backed-custom-resources-createfunction-createstack)
+ [Limpar os recursos](#walkthrough-lambda-backed-custom-resources-createfunction-cleanup)
+ [Informações relacionadas](#w2aac11c45b9c24b9c23)

## Visão geral
<a name="walkthrough-lambda-backed-custom-resources-overview"></a>

O modelo de pilha de exemplo usado neste passo a passo cria um recurso personalizado baseado no Lambda. Esse recurso personalizado introduz um atraso configurável (60 segundos por padrão) durante a criação da pilha. O atraso ocorre durante as atualizações da pilha somente quando as propriedades do recurso personalizado são modificadas.

Este modelo fornece os seguintes recursos:
+ um recurso personalizado,
+ uma função do Lambda e
+ um perfil do IAM que permite que o Lambda grave logs no CloudWatch.

Ele também define duas saídas:
+ O tempo que a função de fato esperou.
+ Um identificador único gerado durante cada execução da função do Lambda.



**nota**  
O CloudFormation é um serviço gratuito, mas o Lambda cobra com base no número de solicitações para suas funções e no tempo durante o qual seu código é executado. Para obter mais informações sobre preços do Lambda, consulte [Preços do AWS Lambda](https://aws.amazon.com/lambda/pricing/).

## Modelo de exemplo
<a name="walkthrough-lambda-backed-custom-resources-sample-template"></a>

É possível ver o modelo de amostra de recurso personalizado apoiado pelo Lambda com o mecanismo de atraso abaixo:

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-json"></a>

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": ["lambda.amazonaws.com"] },
            "Action": ["sts:AssumeRole"]
          }]
        },
        "Path": "/",
        "Policies": [{
          "PolicyName": "AllowLogs",
          "PolicyDocument": {
            "Statement": [{
              "Effect": "Allow",
              "Action": ["logs:*"],
              "Resource": "*"
            }]
          }
        }]
      }
    },
    "CFNWaiter": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Runtime": "python3.9",
        "Timeout": 900,
        "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] },
        "Code": {
          "ZipFile": { "Fn::Join": ["\n", [
            "from time import sleep",
            "import json",
            "import cfnresponse",
            "import uuid",
            "",
            "def handler(event, context):",
            "  wait_seconds = 0",
            "  id = str(uuid.uuid1())",
            "  if event[\"RequestType\"] in [\"Create\", \"Update\"]:",
            "    wait_seconds = int(event[\"ResourceProperties\"].get(\"ServiceTimeout\", 0))",
            "    sleep(wait_seconds)",
            "  response = {",
            "    \"TimeWaited\": wait_seconds,",
            "    \"Id\": id ",
            "  }",
            "  cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)"
          ]]}
        }
      }
    },
    "CFNWaiterCustomResource": {
      "Type": "AWS::CloudFormation::CustomResource",
      "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["CFNWaiter", "Arn"] },
        "ServiceTimeout": 60
      }
    }
  },
  "Outputs": {
    "TimeWaited": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "TimeWaited"] },
      "Export": { "Name": "TimeWaited" }
    },
    "WaiterId": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "Id"] },
      "Export": { "Name": "WaiterId" }
    }
  }
}
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-yaml"></a>

```
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: "AllowLogs"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:*"
                Resource: "*"
  CFNWaiter:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9 
      Timeout: 900
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile:
          !Sub |
          from time import sleep
          import json
          import cfnresponse
          import uuid
​
          def handler(event, context):
            wait_seconds = 0
            id = str(uuid.uuid1())
            if event["RequestType"] in ["Create", "Update"]:
              wait_seconds = int(event["ResourceProperties"].get("ServiceTimeout", 0))
              sleep(wait_seconds)
            response = {
              "TimeWaited": wait_seconds,
              "Id": id 
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id)
  CFNWaiterCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt CFNWaiter.Arn
      ServiceTimeout: 60
Outputs:
  TimeWaited:
    Value: !GetAtt CFNWaiterCustomResource.TimeWaited
    Export:
      Name: TimeWaited
  WaiterId:
    Value: !GetAtt CFNWaiterCustomResource.Id
    Export:
      Name: WaiterId
```

## Modelo de passo a passo
<a name="walkthrough-lambda-backed-custom-resources-sample-template-walkthrough"></a>

Os trechos a seguir explicam partes relevantes do modelo de amostra para ajudá-lo a entender como a função do Lambda está associada a um recurso personalizado e a entender a saída.

`CFNWaiter` do resource [AWS::Lambda::Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-lambda-function.html)  
O recurso `AWS::Lambda::Function` especifica o código-fonte, o nome do handler, o ambiente de runtime e o nome do recurso da Amazon (ARN) do perfil de execução da função.  
A propriedade `Handler` é definida como `index.handler`, pois usa um código-fonte do Python. Para mais informações sobre identificadores de manipuladores aceitos ao usar códigos-fonte de funções embutidas, consulte [ AWS::Lambda::Function Code](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-zipfile).  
O `Runtime` é especificado como `python3.9` porque o arquivo de origem é um código do Python.  
O `Timeout` é definido como 900 segundos.  
A propriedade `Role` usa a função `Fn::GetAtt` para obter o ARN do perfil de execução `LambdaExecutionRole` declarado no recurso `AWS::IAM::Role` do modelo.  
A propriedade `Code` define o código da função inline com uma função do Python. A função do Python no modelo de amostra faz o seguinte:  
+ Criar um ID exclusivo utilizando o UUID
+ Verificar se a solicitação é de criação ou atualização
+ Suspender pela duração especificada por `ServiceTimeout` durante solicitações `Create` ou `Update`
+ Retorna o tempo de espera e o ID exclusivo

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-lambda-resource-json"></a>

```
...
    "CFNWaiter": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Runtime": "python3.9",
        "Timeout": 900,
        "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] },
        "Code": {
          "ZipFile": { "Fn::Join": ["\n", [
            "from time import sleep",
            "import json",
            "import cfnresponse",
            "import uuid",
            "",
            "def handler(event, context):",
            "  wait_seconds = 0",
            "  id = str(uuid.uuid1())",
            "  if event[\"RequestType\"] in [\"Create\", \"Update\"]:",
            "    wait_seconds = int(event[\"ResourceProperties\"].get(\"ServiceTimeout\", 0))",
            "    sleep(wait_seconds)",
            "  response = {",
            "    \"TimeWaited\": wait_seconds,",
            "    \"Id\": id ",
            "  }",
            "  cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)"
          ]]}
        }
      }
    },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-lambda-resource-yaml"></a>

```
...
  CFNWaiter:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9 
      Timeout: 900
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile:
          !Sub |
          from time import sleep
          import json
          import cfnresponse
          import uuid
​
          def handler(event, context):
            wait_seconds = 0
            id = str(uuid.uuid1())
            if event["RequestType"] in ["Create", "Update"]:
              wait_seconds = int(event["ResourceProperties"].get("ServiceTimeout", 0))
              sleep(wait_seconds)
            response = {
              "TimeWaited": wait_seconds,
              "Id": id 
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id)
...
```

`LambdaExecutionRole` do recurso [AWS::IAM::Role](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-iam-role.html)  
O recurso `AWS::IAM:Role` cria um perfil de execução para a função do Lambda, que inclui uma política de função de suposição que permite que o Lambda a use. Ele também contém uma política que permite o acesso aos logs do CloudWatch.

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-iam-role-json"></a>

```
...
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": ["lambda.amazonaws.com"] },
            "Action": ["sts:AssumeRole"]
          }]
        },
        "Path": "/",
        "Policies": [{
          "PolicyName": "AllowLogs",
          "PolicyDocument": {
            "Statement": [{
              "Effect": "Allow",
              "Action": ["logs:*"],
              "Resource": "*"
            }]
          }
        }]
      }
    },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-iam-role-yaml"></a>

```
...
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: "AllowLogs"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:*"
                Resource: "*"
...
```

Recurso [AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html) `CFNWaiterCustomResource`  
O recurso personalizado se vincula à função do Lambda com seu ARN usando `!GetAtt CFNWaiter.Arn`. Ela implementará um tempo de espera de 60 segundos para operações de criação e atualização, conforme definido em `ServiceTimeout`. O recurso somente será chamado para uma operação de atualização se as propriedades forem modificadas.

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-custom-resource-json"></a>

```
...
    "CFNWaiterCustomResource": {
      "Type": "AWS::CloudFormation::CustomResource",
      "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["CFNWaiter", "Arn"] },
        "ServiceTimeout": 60
      }
    }
  },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-custom-resource-yaml"></a>

```
...
  CFNWaiterCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt CFNWaiter.Arn
      ServiceTimeout: 60
...
```

`Outputs`  
As `Outputs` desse modelo são `TimeWaited` e `WaiterId`. O valor `TimeWaited` usa uma função `Fn::GetAtt` para fornecer a quantidade de tempo que o recurso de espera realmente aguardou. `WaiterId` usa uma função `Fn::GetAtt` para fornecer o ID exclusivo que foi gerado e associado à execução.

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-output-json"></a>

```
...
  "Outputs": {
    "TimeWaited": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "TimeWaited"] },
      "Export": { "Name": "TimeWaited" }
    },
    "WaiterId": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "Id"] },
      "Export": { "Name": "WaiterId" }
    }
  }
}
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-output-yaml"></a>

```
...
Outputs:
  TimeWaited:
    Value: !GetAtt CFNWaiterCustomResource.TimeWaited
    Export:
      Name: TimeWaited
  WaiterId:
    Value: !GetAtt CFNWaiterCustomResource.Id
    Export:
      Name: WaiterId
...
```

## Pré-requisitos
<a name="walkthrough-lambda-backed-custom-resources-prerequisites"></a>

Você deve ter permissões do IAM para usar todos os serviços correspondentes, como o Lambda e o CloudFormation.

## Iniciar a pilha
<a name="walkthrough-lambda-backed-custom-resources-createfunction-createstack"></a>

**Para criar a pilha**

1. Encontre o modelo de sua preferência (YAML ou JSON) na seção [Modelo de exemplo](#walkthrough-lambda-backed-custom-resources-sample-template) e salve-o na máquina com o nome `samplelambdabackedcustomresource.template`.

1. Abra o console do CloudFormation em [https://console.aws.amazon.com/cloudformation/](https://console.aws.amazon.com/cloudformation/).

1. Na página **Pilhas**, escolha **Criar pilha** no canto superior direito e depois **Com novos recursos (padrão)**.

1. Em **Pré-requisito: preparar modelo**, escolha **Escolher um modelo existente**.

1. Para **Especificar modelo**, selecione **Carregar um arquivo de modelo** e depois **Escolher arquivo**.

1. Selecione o arquivo de modelo `samplelambdabackedcustomresource.template` salvou anteriormente.

1. Escolha **Próximo**.

1. Para **Nome da pilha**, digite **SampleCustomResourceStack** e escolha **Próximo**.

1. Para esta demonstração, você não precisa adicionar tags ou especificar configurações avançadas, portanto escolha **Próximo**.

1. Certifique-se de que o nome da pilha esteja correto e escolha **Criar**.

Pode levar alguns minutos para que o CloudFormation crie a pilha. Para monitorar o progresso, visualize os eventos da pilha. Para obter mais informações, consulte [Visualizar informações da pilha no console do CloudFormation](cfn-console-view-stack-data-resources.md).

Se a criação da pilha for bem-sucedida, todos os recursos da pilha, como a função do Lambda e o recurso personalizado, foram criados. Você usou com êxito uma função do Lambda e um recurso personalizado.

Se a função do Lambda retornar um erro, visualize os logs da função no [console](https://console.aws.amazon.com/cloudwatch/home#logs:) do CloudWatch Logs. O nome do stream de logs é o ID físico do recurso personalizado, que pode ser encontrado visualizando os recursos da pilha. Para obter mais informações, consulte [Visualizar dados de log](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html#ViewingLogData), no *Guia do usuário do Amazon CloudWatch*.

## Limpar os recursos
<a name="walkthrough-lambda-backed-custom-resources-createfunction-cleanup"></a>

Exclua a pilha para limpar todos os recursos de pilha criados; assim, você não será cobrado por recursos desnecessários.

**Para excluir a pilha**

1. No console do CloudFormation, escolha a pilha **SampleCustomResourceStack**.

1. Escolha **Ações** e, em seguida, **Excluir pilha**.

1. Na mensagem de confirmação, escolha **Sim, excluir**.

Todos os recursos que você criou serão excluídos.

Agora que você sabe como criar e usar o recurso personalizado apoiado pelo Lambda, pode usar o modelo de amostra e o código deste passo a passo para criar e experimentar outras pilhas e funções.

## Informações relacionadas
<a name="w2aac11c45b9c24b9c23"></a>
+ [Referência de recursos personalizados do CloudFormation](crpg-ref.md)
+ [AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html)