

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

# 建立或延伸建構模組
<a name="constructs-best-practices"></a>

## 什麼是建構模組
<a name="construct-definition"></a>

建構是 AWS CDK 應用程式的基本建置區塊。建構可以代表單一 AWS 資源，例如 Amazon Simple Storage Service (Amazon S3) 儲存貯體，也可以是由多個 AWS 相關資源組成的更高層級抽象。建構模組的元件可以包括具有關聯運算容量的工作者佇列，或具有監控資源和儀表板的排定作業。 AWS CDK 包含稱為 AWS 建構程式庫的建構集合。程式庫包含每個 的建構 AWS 服務。您可以使用 [Construct Hub](https://constructs.dev/search?q=&cdk=aws-cdk&cdkver=2&sort=downloadsDesc&offset=0) 來探索來自 AWS第三方和開放原始碼 AWS CDK 社群的其他建構。

## 不同類型的建構模組是什麼
<a name="construct-types"></a>

有三種不同類型的建構 AWS CDK：
+ **L1 建構模組** – 第 1 層即 L1 建構模組正是 CloudFormation 定義的資源 – 不多也不少。您必須自行提供組態所需的資源。這些 L1 建構非常基本，必須手動設定。 L1 建構具有`Cfn`字首並直接對應至 CloudFormation 規格。 AWS CDK 只要 CloudFormation 支援這些服務， AWS 服務 就會支援新的 。[CfnBucket](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.CfnBucket.html) 是 L1 建構的良好範例。此類別代表 S3 儲存貯體，您必須在其中明確設定所有屬性。如果您找不到 L2 或 L3 建構，建議您只使用 L1 建構。
+ **L2 建構模組** – 第 2 層即 L2 建構模組具有通用樣板程式碼和 glue 邏輯。這些建構模組隨附方便的預設值，並減少您需要知道的知識量。L2 建構會使用意圖型 APIs 來建構您的資源，並通常會封裝其對應的 L1 模組。L2 建構模組的一個良好範例是[儲存貯體](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html)。此類別建立具有預設屬性和方法的 S3 儲存貯體，例如 [bucket.addLifeCycleRule()](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.Bucket.html#add-wbr-lifecycle-wbr-rulerule)，它會為儲存貯體新增生命週期規則。
+ **L3 建構模組** – 第 3 層即 L3 建構模組稱為*模式*。L3 建構模組旨在協助您完成 中的常見任務 AWS，通常涉及多種資源。這些比 L2 建構模組更加具體、更固定，並且為特定使用案例提供服務。例如， [aws-ecs-patterns.ApplicationLoadBalancedFargateService](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html) 建構函數代表包含 AWS Fargate 使用 Application Load Balancer 之容器叢集的架構。另一個範例是 [aws-apigateway.LambdaRestApi](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.LambdaRestApi.html) 建構模組。此建構模組代表 Lambda 函數支援的 Amazon API Gateway API。

隨著建構模組層級變得更高，對如何使用這些建構模組進行了更多假設。這可讓您為高度特定的使用案例提供具有更有效預設值的介面。

## 如何建立自己的建構模組
<a name="create-construct"></a>

若要定義自己的建構模組，您必須遵循特定的方法。這是因為所有建構模組都延伸 `Construct` 類別。`Construct` 類別是建構模組樹狀目錄的建構區塊。建構模組在延伸 `Construct` 基本類別的類別中實作。所有建構模組在初始化時都採用以下三個參數：
+ **範圍** – 建構的父系或擁有者，可以是堆疊或其他建構，決定其在建構樹中的位置。通常必須為範圍傳遞 `this` (或 Python 中的 `self`)，它表示目前物件。
+ **id** – 在此範圍內必須是唯一的識別符。識別符可做為目前建構中定義之所有項目的命名空間，並用於配置唯一身分，例如資源名稱和 CloudFormation 邏輯 IDs。
+ **Props** – 定義建構結構初始組態的一組屬性。

下列範例顯示如何設定建構模組。

```
import { Construct } from 'constructs';

export interface CustomProps {
  // List all the properties 
  Name: string;
}
export class MyConstruct extends Construct {
  constructor(scope: Construct, id: string, props: CustomProps) {
    super(scope, id);

    // TODO
  }
}
```

## 建立或延伸 L2 建構模組
<a name="create-l2-construct"></a>

L2 建構模組代表「雲端元件」，並封裝了 CloudFormation 建立此元件必須具有的所有內容。L2 建構可以包含一或多個 AWS 資源，您可以自行自訂建構。建立或延伸 L2 建構模組的優點是您可以重複使用 CloudFormation 堆疊中的元件，而無需重新定義程式碼。您可以簡單地將建構模組匯入為類別。

當與現有建構有「是」關係時，您可以擴展現有建構，以新增額外的預設功能。最佳實務是重複使用現有 L2 建構的屬性。您可以直接在建構函數中修改屬性來覆寫屬性。

下列範例顯示如何與最佳實務保持一致並延伸稱為 `s3.Bucket` 的現有 L2 建構模組。此延伸建立預設屬性 (例如 `versioned`、`publicReadAccess`、`blockPublicAccess`)，以確保從此新的建構模組建立的所有物件 (在本範例中為 S3 儲存貯體) 永遠設定這些預設值。

```
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
export class MySecureBucket extends s3.Bucket {
  constructor(scope: Construct, id: string, props?: s3.BucketProps) {

    super(scope, id, { 
      ...props, 
      versioned: true,
      publicReadAccess: false,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL
    });
  }
}
```

## 建立 L3 建構模組
<a name="create-l3-construct"></a>

當與現有建構合成有「有」關係時，合成是更好的選擇。組合意味著您在其他現有建構模組之上建置自己的建構模組。您可以建立自己的模式，將所有資源及其預設值封裝在單一可共用的更高層級的 L3 建構模組內。建立自己的 L3 建構模組 (模式) 的好處是您可以重複使用堆疊中的元件，而無需重新定義程式碼。您可以簡單地將建構模組匯入為類別。這些模式旨在協助取用者以簡潔的方式利用有限的知識，並基於通用模式提供多種資源。

下列程式碼範例會建立名為 的 AWS CDK 建構`ExampleConstruct`。您可以使用此建構模組作為範本定義雲端元件。

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export interface ExampleConstructProps {
  //insert properties you wish to expose
}

export class ExampleConstruct extends Construct {
  constructor(scope: Construct, id: string, props: ExampleConstructProps) {
    super(scope, id);
    //Insert the AWS components you wish to integrate
  }
}
```

下列範例示範如何在 AWS CDK 應用程式或堆疊中匯入新建立的建構。

```
import { ExampleConstruct } from './lib/construct-name';
```

下列範例顯示如何執行個體化從基本類別延伸的建構模組的執行個體。

```
import { ExampleConstruct } from './lib/construct-name';

new ExampleConstruct(this, 'newConstruct', {
  //insert props which you exposed in the interface `ExampleConstructProps`
});
```

如需詳細資訊，請參閱[AWS CDK 研討會](https://cdkworkshop.com/)文件中的 AWS CDK 研討會。

## 逃生艙
<a name="escape-hatch"></a>

您可以在 中使用逃生艙 AWS CDK 來提升抽象層級，以便存取較低層級的建構。逃生艙用於延伸 建構，以使用目前版本的 未公開 AWS 但可在 CloudFormation 中使用的功能。

我們建議您在下列案例中使用逃生艙：
+  AWS 服務 此功能可透過 CloudFormation `Construct` 使用，但沒有其建構。
+  AWS 服務 此功能可透過 CloudFormation `Construct` 使用，並且有服務的建構，但這些建構尚未公開此功能。由於 `Construct` 建構模組是「手動」開發的，因此其有時可能落後於 CloudFormation 資源建構模組。

下列範例程式碼顯示使用逃生艙的常見使用案例。在此範例中，較高層級建構模組中尚未實作的功能是新增 `httpPutResponseHopLimit` 以自動擴展 `LaunchConfiguration`。

```
const launchConfig = autoscaling.onDemandASG.node.findChild("LaunchConfig") as CfnLaunchConfiguration; 
            launchConfig.metadataOptions = {
                   httpPutResponseHopLimit: autoscalingConfig.httpPutResponseHopLimit|| 2
            }
```

上述程式碼範例顯示下列工作流程：

1. 您可以使用 L2 建構模組來定義 `AutoScalingGroup`。L2 建構不支援更新 `httpPutResponseHopLimit`，因此您必須使用逃生艙。

1. 您可以使用 `node.findChild()`方法來尋找 L2 `AutoScalingGroup` 建構的特定`LaunchConfig`子系，並將其轉換為`CfnLaunchConfiguration`資源。

1. 現在您可以在 L1 `CfnLaunchConfiguration` 上設定 `launchConfig.metadataOptions` 屬性。

## 自訂資源
<a name="custom-resource"></a>

您可以使用自訂資源在範本中撰寫自訂佈建邏輯，讓 CloudFormation 在您建立、更新 (如果您變更自訂資源) 或刪除堆疊時執行。例如，如果您想要包含 中無法使用的資源，您可以使用自訂資源 AWS CDK。以此方式，您仍然可以在單一堆疊中管理所有相關資源。

建置自訂資源涉及編寫可回應資源的 CREATE、UPDATE 和 DELETE 生命週期事件的 Lambda 函數。如果您的自訂資源必須僅進行一次 API 呼叫，請考慮使用 [AwsCustomResource](https://github.com/awslabs/aws-cdk/tree/master/packages/%40aws-cdk/custom-resources) 建構模組。這使得在 CloudFormation 部署期間執行任意 SDK 呼叫成為可能。否則，我們建議您編寫自己的 Lambda 函數來執行您必須完成的工作。

如需有關自訂資源的詳細資訊，請參閱 CloudFormation 文件中的[自訂資源](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html)。如需如何使用自訂資源的範例，請參閱 GitHub 上的[自訂資源](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/custom-resource/)儲存庫。

下列範例顯示如何建立自訂資源類別來啟動 Lambda 函數並向 CloudFormation 傳送成功或失敗訊號。

```
import cdk = require('aws-cdk-lib');
import customResources = require('aws-cdk-lib/custom-resources');
import lambda = require('aws-cdk-lib/aws-lambda');
import { Construct } from 'constructs';

import fs = require('fs');

export interface MyCustomResourceProps {
  /**
   * Message to echo
   */
  message: string;
}

export class MyCustomResource extends Construct {
  public readonly response: string;

  constructor(scope: Construct, id: string, props: MyCustomResourceProps) {
    super(scope, id);

    const fn = new lambda.SingletonFunction(this, 'Singleton', {
      uuid: 'f7d4f730-4ee1-11e8-9c2d-fa7ae01bbebc',
      code: new lambda.InlineCode(fs.readFileSync('custom-resource-handler.py', { encoding: 'utf-8' })),
      handler: 'index.main',
      timeout: cdk.Duration.seconds(300),
      runtime: lambda.Runtime.PYTHON_3_6,
    });

    const provider = new customResources.Provider(this, 'Provider', {
      onEventHandler: fn,
    });

    const resource = new cdk.CustomResource(this, 'Resource', {
      serviceToken: provider.serviceToken,
      properties: props,
    });

    this.response = resource.getAtt('Response').toString();
    }
}
```

下列範例顯示自訂資源的主要邏輯。

```
def main(event, context):
    import logging as log
    import cfnresponse
    log.getLogger().setLevel(log.INFO)

    # This needs to change if there are to be multiple resources in the same stack
    physical_id = 'TheOnlyCustomResource'

    try:
        log.info('Input event: %s', event)

        # Check if this is a Create and we're failing Creates
        if event['RequestType'] == 'Create' and event['ResourceProperties'].get('FailCreate', False):
            raise RuntimeError('Create failure requested')

        # Do the thing
        message = event['ResourceProperties']['Message']
        attributes = {
            'Response': 'You said "%s"' % message
        }

        cfnresponse.send(event, context, cfnresponse.SUCCESS, attributes, physical_id)
    except Exception as e:
        log.exception(e)
        # cfnresponse's error message is always "see CloudWatch"
        cfnresponse.send(event, context, cfnresponse.FAILED, {}, physical_id)
```

下列範例顯示 AWS CDK 堆疊如何呼叫自訂資源。

```
import cdk = require('aws-cdk-lib');
import { MyCustomResource } from './my-custom-resource';

/**
 * A stack that sets up MyCustomResource and shows how to get an attribute from it
 */
class MyStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const resource = new MyCustomResource(this, 'DemoResource', {
      message: 'CustomResource says hello',
    });

    // Publish the custom resource output
    new cdk.CfnOutput(this, 'ResponseMessage', {
      description: 'The message that came back from the Custom Resource',
      value: resource.response
    });
  }
}

const app = new cdk.App();
new MyStack(app, 'CustomResourceDemoStack');
app.synth();
```