

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Constructos da camada 3
<a name="layer-3"></a>

[Se as construções L1 realizam uma tradução literal de CloudFormation recursos em código programático e as construções L2 substituem grande parte da CloudFormation sintaxe detalhada por métodos auxiliares e lógica personalizada, o que as construções L3 fazem?](https://docs.aws.amazon.com/cdk/v2/guide/constructs.html#constructs_lib) A resposta para isso é limitada apenas pela sua imaginação. Você pode criar a camada 3 para se adequar a qualquer caso de uso específico. Se seu projeto precisar de um recurso que tenha um subconjunto específico de propriedades, você poderá criar um constructo L3 reutilizável para atender a essa necessidade.

Os constructos L3 são chamados de *padrões* no AWS CDK. Um padrão é qualquer objeto que estende a `Construct` classe no AWS CDK (ou estende uma classe que estende a `Construct ` classe) para realizar qualquer lógica abstrata além da camada 2. Ao usar a AWS CDK CLI para executar **cdk init** para iniciar um novo AWS CDK projeto, você deve escolher entre três tipos de AWS CDK aplicativos:`app`, e. `lib` `sample-app`



![AWS CDK tipos de aplicativos](http://docs.aws.amazon.com/pt_br/prescriptive-guidance/latest/aws-cdk-layers/images/cdk-app-types.png)


`app`e `sample-app` ambos representam AWS CDK aplicativos clássicos nos quais você cria e implanta CloudFormation pilhas em ambientes da AWS. Ao escolher`lib`, você está optando por construir uma nova construção L3. `app`e `sample-app` permitem que você escolha qualquer idioma AWS CDK compatível, mas você só pode escolher TypeScript com`lib`. Isso ocorre porque o AWS CDK é escrito de forma nativa TypeScript e usa um sistema de código aberto chamado [JSii](https://github.com/aws/jsii) para traduzir o código original para os outros idiomas suportados. Quando você escolhe `lib` para iniciar seu projeto, você está optando por criar uma extensão para o AWS CDK.

Qualquer classe que estenda a classe `Construct` pode ser um constructo L3, mas os casos de uso mais comuns da camada 3 são interações de recursos, extensões de recursos e recursos personalizados. A maioria dos constructos L3 usa um ou mais desses três casos para estender a funcionalidade do AWS CDK .

## Interações de recursos
<a name="l3-resource-interactions"></a>

Uma solução normalmente emprega vários serviços da AWS que funcionam juntos. Por exemplo, uma CloudFront distribuição da Amazon geralmente usa um bucket S3 como origem e AWS WAF como proteção contra explorações comuns. AWS AppSync e o Amazon API Gateway costumam usar tabelas do Amazon DynamoDB como fontes de dados para suas APIs. Um pipeline AWS CodePipeline geralmente usa o Amazon S3 como fonte e AWS CodeBuild para seus estágios de construção. Nesses casos, geralmente é útil criar um único constructo L3 que manipule o provisionamento de dois ou mais constructos L2 interconectados.

Aqui está um exemplo de uma construção L3 que provisiona uma CloudFront distribuição junto com sua origem S3, um AWS WAF registro do Amazon Route 53 e um certificado Gerenciador de certificados da AWS (ACM) para adicionar um endpoint personalizado com criptografia em trânsito, tudo em uma construção reutilizável:

```
// Define the properties passed to the L3 construct
export interface CloudFrontWebsiteProps {
     distributionProps: DistributionProps
     bucketProps: BucketProps
     wafProps: CfnWebAclProps
     zone: IHostedZone
}

// Define the L3 construct
export class CloudFrontWebsite extends Construct {
     public distribution: Distribution

     constructor(
        scope: Construct,
        id: string,
        props: CloudFrontWebsiteProps
     ) {
         super(scope, id);
                 
         const certificate = new Certificate(this, "Certificate", {
            domainName: props.zone.zoneName,
            validation: CertificateValidation.fromDns(props.zone)
         });
         const defaultBehavior = {
            origin: new S3Origin(new Bucket(this, "bucket", props.bucketProps))
         }
         const waf = new CfnWebACL(this, "waf", props.wafProps); 
         this.distribution = new Distribution(this, id, {
            ...props.distributionProps,
            defaultBehavior,
            certificate,
            domainNames: [this.domainName],
            webAclId: waf.attrArn,
          });
     }
}
```

Observe que o Amazon S3 CloudFront, o Route 53 e o ACM usam construções L2, mas a ACL da web (que define regras para lidar com solicitações da web) usa uma construção L1. Isso ocorre porque AWS CDK é um pacote de código aberto em evolução que não está totalmente completo e ainda não existe uma construção L2. `WebAcl` No entanto, qualquer pessoa pode contribuir com a criação AWS CDK de novas construções L2. Então, até que AWS CDK ofereça uma construção L2 para`WebAcl`, você precisa usar uma construção L1. Para criar um novo site usando o constructo L3 `CloudFrontWebsite`, você usa o seguinte código:

```
const siteADotCom = new CloudFrontWebsite(stack, "siteA", siteAProps);
const siteBDotCom = new CloudFrontWebsite(stack, "siteB", siteBProps);
const siteCDotCom = new CloudFrontWebsite(stack, "siteC", siteCProps);
```

Neste exemplo, a construção CloudFront `Distribution` L2 é exposta como uma propriedade pública da construção L3. Ainda haverá casos em que você precisará expor propriedades do L3 como esta, conforme necessário. Na verdade, veremos `Distribution` novamente mais tarde, na seção [Recursos personalizados](#l3-custom-resources).

 AWS CDK Isso inclui alguns exemplos de padrões de interação de recursos, como este. Além do pacote `aws-ecs` que contém os constructos L2 para o Amazon Elastic Container Service (Amazon ECS), o AWS CDK tem um pacote chamado [aws-ecs-patterns.](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns-readme.html) Esse pacote contém vários constructos L3 que combinam o Amazon ECS com Application Load Balancers, Network Load Balancers e grupos de destino, ao mesmo tempo em que oferecem versões diferentes que são predefinidas para o Amazon Elastic Compute Cloud (Amazon EC2) e o AWS Fargate. Como muitas aplicações sem servidor usam o Amazon ECS somente com o Fargate, esses constructos L3 oferecem uma conveniência que pode economizar o tempo dos desenvolvedores e o dinheiro dos clientes.

## Extensões de recursos
<a name="l3-resource-extensions"></a>

Alguns casos de uso exigem que os recursos tenham configurações padrão específicas que não sejam nativas do constructo L2. No nível da pilha, isso pode ser tratado usando [aspects](https://docs.aws.amazon.com/cdk/v2/guide/aspects.html), mas outra maneira conveniente de dar novos padrões a um constructo L2 é estendendo a camada 2. Como um constructo é qualquer classe que herda a classe `Construct`, e os constructos L2 estendem essa classe, você também pode criar um constructo L3 estendendo diretamente um constructo L2.

Isso pode ser especialmente útil para uma lógica de negócios personalizada que ofereça suporte às necessidades singulares de um cliente. Suponhamos que uma empresa tenha um repositório que armazene todo o seu código de AWS Lambda função em um único diretório chamado `src/lambda` e que a maioria das funções do Lambda reutilize sempre o mesmo tempo de execução e nome de manipulador. Em vez de configurar o caminho do código toda vez que você configura uma nova função do Lambda, você pode criar um novo constructo L3:

```
export class MyCompanyLambdaFunction extends Function {
      constructor(
        scope: Construct,
        id: string,
        props: Partial<FunctionProps> = {}
     ) {
         super(scope, id, {
            handler: 'index.handler',
            runtime: Runtime.NODEJS_LATEST,
            code: Code.fromAsset(`src/lambda/${props.functionName || id}`),
            ...props
     });
}
```

Você pode então substituir o constructo L2 `Function` em todos os lugares do repositório da seguinte forma:

```
new MyCompanyLambdaFunction(this, "MyFunction");
new MyCompanyLambdaFunction(this, "MyOtherFunction");
new MyCompanyLambdaFunction(this, "MyThirdFunction", {
   runtime: Runtime.PYTHON_3_11
});
```

Os padrões permitem que você crie novas funções do Lambda em uma única linha, e o constructo L3 é configurado para que você ainda possa substituir as propriedades padrão, se necessário.

Estender constructos L2 diretamente funciona melhor quando você deseja apenas adicionar novos padrões aos constructos L2 existentes. Se você também precisar de outra lógica personalizada, é melhor estender a classe `Construct`. A razão para isso decorre do método `super`, que é chamado dentro do construtor. Em classes que estendem outras classes, o método `super` é usado para chamar o construtor da classe primária, e isso **deve** ser a primeira coisa que acontece em seu construtor. Isso significa que qualquer manipulação dos argumentos passados ou de outra lógica personalizada só pode acontecer **após** a criação do constructo L2 original. Se você precisar executar alguma dessas lógicas personalizadas antes de instanciar seu constructo L2, é melhor seguir o padrão descrito anteriormente na seção [Interações de recursos](#l3-resource-interactions).

## Recursos personalizados
<a name="l3-custom-resources"></a>

Os [recursos personalizados](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) são um recurso poderoso CloudFormation que permite executar a lógica personalizada a partir de uma função Lambda que é ativada durante a implantação da pilha. Sempre que precisar de algum processo durante a implantação que não seja diretamente suportado CloudFormation, você pode usar um recurso personalizado para que isso aconteça. O AWS CDK oferece classes que também permitem criar recursos personalizados de forma programática. Ao usar recursos personalizados em um construtor L3, você poderá criar um constructo de quase tudo.

Uma das vantagens de usar a Amazon CloudFront são seus fortes recursos globais de armazenamento em cache. Se você quiser redefinir manualmente esse cache para que seu site reflita imediatamente as novas alterações feitas em sua origem, você pode usar uma [CloudFront invalidação](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html). No entanto, as invalidações são processos executados em**** uma CloudFront distribuição em vez de serem propriedades de uma CloudFront distribuição. Elas podem ser criadas e aplicadas a uma distribuição existente a qualquer momento, portanto, não são parte nativa do processo de provisionamento e implantação.

Nesse cenário, talvez você queira criar e executar uma invalidação após cada atualização na origem de uma distribuição. Por causa dos recursos personalizados, você pode criar um constructo L3 que seja parecido com:

```
export interface CloudFrontInvalidationProps {
     distribution: Distribution
     region?: string
     paths?: string[]
}

export class CloudFrontInvalidation extends Construct {
      constructor(
        scope: Construct,
        id: string,
        props: CloudFrontInvalidationProps
     ) {
          super(scope, id);
          const policy = AwsCustomResourcePolicy.fromSdkCalls({
               resources:AwsCustomResourcePolicy.ANY_RESOURCE
          });
          new AwsCustomResource(scope, `${id}Invalidation`, {
             policy,
             onUpdate: {
                  service: 'CloudFront',
                  action: 'createInvalidation',
                  region: props.region || 'us-east-1',
                  physicalResourceId: PhysicalResourceId.fromResponse('Invalidation.Id'),
                  parameters: {
                     DistributionId: props.distribution.distributionId,
                     InvalidationBatch: {
                         Paths: {
                            Quantity: props.paths?.length || 1,
                            Items: props.paths || ['/*']
                         },
                         CallerReference: crypto.randomBytes(5).toString('hex')
                     }
                  }
             }
          }
     }
}
```

Usando a distribuição que criamos anteriormente no constructo L3 `CloudFrontWebsite`, você pode fazer isso com muita facilidade:

```
new CloudFrontInvalidation(this, 'MyInvalidation', { 
       distribution: siteADotCom.distribution
});
```

Essa construção L3 usa uma construção AWS CDK L3 chamada [AwsCustomResource](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources.AwsCustomResource.html)para criar um recurso personalizado que executa a lógica personalizada. `AwsCustomResource`é muito conveniente quando você precisa fazer exatamente uma chamada do AWS SDK, pois permite que você faça isso sem precisar escrever nenhum código Lambda. Se você tiver requisitos mais complexos e quiser implementar sua própria lógica, poderá usar a [CustomResource](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CustomResource.html)classe básica diretamente.

Outro bom exemplo do AWS CDK uso de uma construção L3 de recurso personalizada é a implantação do [bucket do S3](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeployment.html). A função Lambda criada pelo recurso personalizado dentro do construtor dessa construção L3 adiciona funcionalidades que não CloudFormation seriam capazes de lidar de outra forma: ela adiciona e atualiza objetos em um bucket do S3. Sem a implantação do bucket do S3, você não conseguiria colocar conteúdo no bucket do S3 que acabou de criar como parte da sua pilha, o que seria muito inconveniente.

O melhor exemplo de como AWS CDK eliminar a necessidade de escrever uma grande quantidade de CloudFormation sintaxe é esse básico: `S3BucketDeployment`

```
new BucketDeployment(this, 'BucketObjects', {
  sources: [Source.asset('./path/to/amzn-s3-demo-bucket')],
  destinationBucket: amzn-s3-demo-bucket
});
```

Compare isso com o CloudFormation código que você precisaria escrever para realizar a mesma coisa:

```
"lambdapolicyA5E98E09": {
   "Type": "AWS::IAM::Policy",
   "Properties": {
    "PolicyDocument": {
     "Statement": [
      {
       "Action": "lambda:UpdateFunctionCode",
       "Effect": "Allow",
       "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
      }
     ],
     "Version": "2012-10-17"		 	 	 
    },
    "PolicyName": "lambdaPolicy",
    "Roles": [
     {
      "Ref": "myiamroleF09C7974"
     }
    ]
   },
   "Metadata": {
    "aws:cdk:path": "CdkScratchStack/lambda-policy/Resource"
   }
  },
  "BucketObjectsAwsCliLayer8C081206": {
   "Type": "AWS::Lambda::LayerVersion",
   "Properties": {
    "Content": {
     "S3Bucket": {
      "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
     },
     "S3Key": "e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip"
    },
    "Description": "/opt/awscli/aws"
   },
   "Metadata": {
    "aws:cdk:path": "CdkScratchStack/BucketObjects/AwsCliLayer/Resource",
    "aws:asset:path": "asset.e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip",
    "aws:asset:is-bundled": false,
    "aws:asset:property": "Content"
   }
  },
  "BucketObjectsCustomResourceB12E6837": {
   "Type": "Custom::CDKBucketDeployment",
   "Properties": {
    "ServiceToken": {
     "Fn::GetAtt": [
      "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536",
      "Arn"
     ]
    },
    "SourceBucketNames": [
     {
      "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
     }
    ],
    "SourceObjectKeys": [
     "f888a9d977f0b5bdbc04a1f8f07520ede6e00d4051b9a6a250860a1700924f26.zip"
    ],
    "DestinationBucketName": {
     "Ref": "amzn-s3-demo-bucket77F80CC0"
    },
    "Prune": true
   },
   "UpdateReplacePolicy": "Delete",
   "DeletionPolicy": "Delete",
   "Metadata": {
    "aws:cdk:path": "CdkScratchStack/BucketObjects/CustomResource/Default"
   }
  },
  "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": {
   "Type": "AWS::IAM::Role",
   "Properties": {
    "AssumeRolePolicyDocument": {
     "Statement": [
      {
       "Action": "sts:AssumeRole",
       "Effect": "Allow",
       "Principal": {
        "Service": "lambda.amazonaws.com"
       }
      }
     ],
     "Version": "2012-10-17"		 	 	 
    },
    "ManagedPolicyArns": [
     {
      "Fn::Join": [
       "",
       [
        "arn:",
        {
         "Ref": "AWS::Partition"
        },
        ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
       ]
      ]
     }
    ]
   },
   "Metadata": {
    "aws:cdk:path": "CdkScratchStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource"
   }
  },
  "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF": {
   "Type": "AWS::IAM::Policy",
   "Properties": {
    "PolicyDocument": {
     "Statement": [
      {
       "Action": [
        "s3:GetBucket*",
        "s3:GetObject*",
        "s3:List*"
       ],
       "Effect": "Allow",
       "Resource": [
        {
         "Fn::Join": [
          "",
          [
           "arn:",
           {
            "Ref": "AWS::Partition"
           },
           ":s3:::",
           {
            "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
           },
           "/*"
          ]
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           "arn:",
           {
            "Ref": "AWS::Partition"
           },
           ":s3:::",
           {
            "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
           }
          ]
         ]
        }
       ]
      },
      {
       "Action": [
        "s3:Abort*",
        "s3:DeleteObject*",
        "s3:GetBucket*",
        "s3:GetObject*",
        "s3:List*",
        "s3:PutObject",
        "s3:PutObjectLegalHold",
        "s3:PutObjectRetention",
        "s3:PutObjectTagging",
        "s3:PutObjectVersionTagging"
       ],
       "Effect": "Allow",
       "Resource": [
        {
         "Fn::GetAtt": [
          "amzns3demobucket77F80CC0",
          "Arn"
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           {
            "Fn::GetAtt": [
             "amzns3demobucket77F80CC0",
             "Arn"
            ]
           },
           "/*"
          ]
         ]
        }
       ]
      }
     ],
     "Version": "2012-10-17"		 	 	 
    },
    "PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF",
    "Roles": [
     {
      "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265"
     }
    ]
   },
   "Metadata": {
    "aws:cdk:path": "CdkScratchStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource"
   }
  },
  "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536": {
   "Type": "AWS::Lambda::Function",
   "Properties": {
    "Code": {
     "S3Bucket": {
      "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
     },
     "S3Key": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip"
    },
    "Role": {
     "Fn::GetAtt": [
      "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265",
      "Arn"
     ]
    },
    "Environment": {
     "Variables": {
      "AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"
     }
    },
    "Handler": "index.handler",
    "Layers": [
     {
      "Ref": "BucketObjectsAwsCliLayer8C081206"
     }
    ],
    "Runtime": "python3.9",
    "Timeout": 900
   },
   "DependsOn": [
  "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF",
    "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265"
   ],
   "Metadata": {
    "aws:cdk:path": "CdkScratchStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource",
    "aws:asset:path": "asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd",
    "aws:asset:is-bundled": false,
    "aws:asset:property": "Code"
   }
  }
```

4 linhas versus 241 linhas é uma **grande** diferença\! E este é apenas um exemplo do que é possível quando você aproveita a camada 3 para personalizar suas pilhas.