

这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段，并于 2023 年 6 月 1 日终止支持。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 自定义构造库中的 AWS 构造
<a name="cfn-layer"></a>

通过 Mixins、Facades、逃生舱口、原始覆盖和自定义资源自定义 AWS 构造库中的构造。

## 使用 Mixins 向 L1 构造添加功能
<a name="develop-customize-mixins"></a>

Mixins 允许您向 L1 构造添加高级功能，而无需完整的 L2 构造。当 L2 构造不可用于服务时，或者您想使用具有通常只能通过 L2 构造才能使用的特定功能的 L1 构造时，这很有用。

例如，您可以向 L1 添加自动删除行为`CfnBucket`，这通常需要 L2 构造：`Bucket`

**Example**  

```
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';

const bucket = new s3.CfnBucket(this, 'MyBucket');
bucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

// Add auto-delete objects behavior using a Mixin
bucket.with(new s3.mixins.BucketAutoDeleteObjects());
```

```
const cdk = require('aws-cdk-lib');
const s3 = require('aws-cdk-lib/aws-s3');

const bucket = new s3.CfnBucket(this, 'MyBucket');
bucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

// Add auto-delete objects behavior using a Mixin
bucket.with(new s3.mixins.BucketAutoDeleteObjects());
```

```
import aws_cdk as cdk
import aws_cdk.aws_s3 as s3

bucket = s3.CfnBucket(self, "MyBucket")
bucket.cfn_options.deletion_policy = cdk.CfnDeletionPolicy.DELETE

# Add auto-delete objects behavior using a Mixin
bucket.with_(s3.mixins.BucketAutoDeleteObjects())
```

```
import software.amazon.awscdk.*;
import software.amazon.awscdk.services.s3.*;

CfnBucket bucket = new CfnBucket(this, "MyBucket");
bucket.getCfnOptions().setDeletionPolicy(CfnDeletionPolicy.DELETE);

// Add auto-delete objects behavior using a Mixin
bucket.with(new BucketAutoDeleteObjects());
```

```
using Amazon.CDK;
using Amazon.CDK.AWS.S3;

var bucket = new CfnBucket(this, "MyBucket");
bucket.CfnOptions.DeletionPolicy = CfnDeletionPolicy.DELETE;

// Add auto-delete objects behavior using a Mixin
bucket.With(new BucketAutoDeleteObjects());
```

```
bucket := awss3.NewCfnBucket(stack, jsii.String("MyBucket"), nil)
bucket.CfnOptions().SetDeletionPolicy(awscdk.CfnDeletionPolicy_DELETE)

bucket.With(awss3.NewBucketAutoDeleteObjects())
```

Mixin 可通过每个服务模块的`mixins`命名空间获得。你可以将 Mixins 与 [Facades](#develop-customize-facades) 结合使用，以获得对 L1 构造的权限和其他集成。有关 Mixins 的更多信息，请参阅 Mi [x](mixins.md) ins。

## 使用 Facades 对 L1 结构进行集成
<a name="develop-customize-facades"></a>

Facades 是特定于资源的类，可为 L1 和 L2 结构提供集成（例如权限、指标和反射）。最常见的 Facade 是 Grants 类，它提供基于意图的权限方法。

你可以直接在 L1 构造上使用 Facades，而不必将它们包装在 L2 中：

**Example**  

```
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as iam from 'aws-cdk-lib/aws-iam';

const bucket = new s3.CfnBucket(this, 'MyBucket');
const role = new iam.Role(this, 'MyRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});

// Use the Grants Facade directly on the L1 construct
s3.BucketGrants.fromBucket(bucket).read(role);
```

```
const s3 = require('aws-cdk-lib/aws-s3');
const iam = require('aws-cdk-lib/aws-iam');

const bucket = new s3.CfnBucket(this, 'MyBucket');
const role = new iam.Role(this, 'MyRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});

// Use the Grants Facade directly on the L1 construct
s3.BucketGrants.fromBucket(bucket).read(role);
```

```
import aws_cdk.aws_s3 as s3
import aws_cdk.aws_iam as iam

bucket = s3.CfnBucket(self, "MyBucket")
role = iam.Role(self, "MyRole",
    assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"),
)

# Use the Grants Facade directly on the L1 construct
s3.BucketGrants.from_bucket(bucket).read(role)
```

```
import software.amazon.awscdk.services.s3.*;
import software.amazon.awscdk.services.iam.*;

CfnBucket bucket = new CfnBucket(this, "MyBucket");
Role role = Role.Builder.create(this, "MyRole")
        .assumedBy(new ServicePrincipal("lambda.amazonaws.com"))
        .build();

// Use the Grants Facade directly on the L1 construct
BucketGrants.fromBucket(bucket).read(role);
```

```
using Amazon.CDK.AWS.S3;
using Amazon.CDK.AWS.IAM;

var bucket = new CfnBucket(this, "MyBucket");
var role = new Role(this, "MyRole", new RoleProps
{
    AssumedBy = new ServicePrincipal("lambda.amazonaws.com")
});

// Use the Grants Facade directly on the L1 construct
BucketGrants.FromBucket(bucket).Read(role);
```

```
bucket := awss3.NewCfnBucket(stack, jsii.String("MyBucket"), nil)
role := awsiam.NewRole(stack, jsii.String("MyRole"), &awsiam.RoleProps{
    AssumedBy: awsiam.NewServicePrincipal(jsii.String("lambda.amazonaws.com"), nil),
})

awss3.BucketGrants_FromBucket(bucket).Read(role, nil)
```

有关外墙的更多信息，请参见[外](facades.md)墙。

当 Mixins 和 Facades 无法覆盖你的用例时，你可以使用逃生舱口和原始覆盖来自定义较低级别的构造。

## 使用转义孵化
<a name="develop-customize-escape"></a>

 AWS 构造库提供了不同抽象级别的[结构](constructs.md)。

在最高级别上，你的 AWS CDK 应用程序及其中的堆栈本身就是整个云基础架构或其中的很大一部分的抽象。可以对它们进行参数化以将其部署在不同的环境中或满足不同的需求。

抽象是用于设计和实现云应用程序的强大工具。 AWS CDK 使您不仅可以利用其抽象进行构建，还可以创建新的抽象。以现有的开源 L2 和 L3 构造为指导，您可以构建自己的 L2 和 L3 构造，以反映自己组织的最佳实践和观点。

没有哪个抽象是完美的，即使是良好的抽象也无法涵盖所有可能的使用案例。在开发过程中，您可能会发现一种几乎符合您的需求的构造，需要进行小规模或大规模的自定义。

因此， AWS CDK 提供了*突破*构造模型的方法。这包括改为使用较低级别的抽象或完全不同的模型。逃生舱口可以让你*逃脱* AWS CDK 范式，并以适合你需求的方式对其进行自定义。然后，您可以将更改封装在一个新的构造中，以抽象出底层的复杂性，并为其他开发人员提供一个干净的 API。

以下是您可以使用转义孵化的情况示例：
+  AWS 服务功能可通过获得 AWS CloudFormation，但没有 L2 结构适用于该功能。
+  AWS 服务功能可通过获得 AWS CloudFormation，并且该服务有 L2 结构，但这些结构尚未公开该功能。由于 L2 构造由 CDK 团队策管，因此它们可能无法立即用于新功能。
+ 该功能还未完全 AWS CloudFormation 可用。

  要确定某项功能是否可通过提供 AWS CloudFormation，请参阅[AWS 资源和属性类型参考](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)。

### 为 L1 构造开发转义孵化
<a name="develop-customize-escape-l1"></a>

如果 L2 构造不可用于该服务，则可以使用自动生成的 L1 构造。这些资源可以按其名称（以 `Cfn` 开头）进行识别，例如 `CfnBucket` 或 `CfnRole`。您可以完全按照使用等效 AWS CloudFormation 资源的方式来实例化它们。

例如，要实例化启用了分析功能的低级别 Amazon S3 存储桶 L1，您需要编写如下内容。

**Example**  

```
new s3.CfnBucket(this, 'amzn-s3-demo-bucket', {
  analyticsConfigurations: [
    {
      id: 'Config',
      // ...
    }
  ]
});
```

```
new s3.CfnBucket(this, 'amzn-s3-demo-bucket', {
  analyticsConfigurations: [
    {
      id: 'Config'
      // ...
    }
  ]
});
```

```
s3.CfnBucket(self, "amzn-s3-demo-bucket",
    analytics_configurations: [
        dict(id="Config",
             # ...
             )
    ]
)
```

```
CfnBucket.Builder.create(this, "amzn-s3-demo-bucket")
    .analyticsConfigurations(Arrays.asList(java.util.Map.of(    // Java 9 or later
        "id", "Config", // ...
    ))).build();
```

```
new CfnBucket(this, 'amzn-s3-demo-bucket', new CfnBucketProps {
    AnalyticsConfigurations = new Dictionary<string, string>
    {
        ["id"] = "Config",
        // ...
    }
});
```

在极少数情况下，您可能希望定义一个没有对应 `CfnXxx` 类的资源。这可能是尚未在资源规范中发布的新 AWS CloudFormation 资源类型。在这种情况下，您可以直接实例化 `cdk.CfnResource` 并指定资源类型和属性。如以下示例所示。

**Example**  

```
new cdk.CfnResource(this, 'amzn-s3-demo-bucket', {
  type: 'AWS::S3::Bucket',
  properties: {
    // Note the PascalCase here! These are CloudFormation identifiers.
    AnalyticsConfigurations: [
      {
        Id: 'Config',
        // ...
      }
    ]
  }
});
```

```
new cdk.CfnResource(this, 'amzn-s3-demo-bucket', {
  type: 'AWS::S3::Bucket',
  properties: {
    // Note the PascalCase here! These are CloudFormation identifiers.
    AnalyticsConfigurations: [
      {
        Id: 'Config'
        // ...
      }
    ]
  }
});
```

```
cdk.CfnResource(self, 'amzn-s3-demo-bucket',
  type="AWS::S3::Bucket",
  properties=dict(
    # Note the PascalCase here! These are CloudFormation identifiers.
    "AnalyticsConfigurations": [
      {
        "Id": "Config",
        # ...
      }
    ]
  )
)
```

```
CfnResource.Builder.create(this, "amzn-s3-demo-bucket")
        .type("AWS::S3::Bucket")
        .properties(java.util.Map.of(    // Map.of requires Java 9 or later
            // Note the PascalCase here! These are CloudFormation identifiers
            "AnalyticsConfigurations", Arrays.asList(
                    java.util.Map.of("Id", "Config", // ...
                    ))))
        .build();
```

```
new CfnResource(this, "amzn-s3-demo-bucket", new CfnResourceProps
{
    Type = "AWS::S3::Bucket",
    Properties = new Dictionary<string, object>
    {   // Note the PascalCase here! These are CloudFormation identifiers
        ["AnalyticsConfigurations"] = new Dictionary<string, string>[]
        {
            new Dictionary<string, string> {
                ["Id"] = "Config"
            }
        }
    }
});
```

### 为 L2 构造开发转义孵化
<a name="develop-customize-escape-l2"></a>

如果 L2 构造缺少功能或您正在尝试解决问题，则可以修改由 L2 构造封装的 L1 构造。

所有 L2 构造都包含在对应的 L1 构造内。例如，高级别 `Bucket` 构造封装低级别 `CfnBucket` 构造。由于直接对`CfnBucket`应于 AWS CloudFormation 资源，因此它公开了可通过 AWS CloudFormation使用的所有功能。

访问 L1 构造的基本方法是使用 `construct.node.defaultChild`（Python：`default_child`），将其转换为正确的类型（如有必要），然后修改其属性。我们再次以 `Bucket` 为例。

**Example**  

```
// Get the CloudFormation resource
const cfnBucket = bucket.node.defaultChild as s3.CfnBucket;

// Change its properties
cfnBucket.analyticsConfiguration = [
  {
    id: 'Config',
    // ...
  }
];
```

```
// Get the CloudFormation resource
const cfnBucket = bucket.node.defaultChild;

// Change its properties
cfnBucket.analyticsConfiguration = [
  {
    id: 'Config'
    // ...
  }
];
```

```
# Get the CloudFormation resource
cfn_bucket = bucket.node.default_child

# Change its properties
cfn_bucket.analytics_configuration = [
    {
        "id": "Config",
        # ...
    }
]
```

```
// Get the CloudFormation resource
CfnBucket cfnBucket = (CfnBucket)bucket.getNode().getDefaultChild();

cfnBucket.setAnalyticsConfigurations(
        Arrays.asList(java.util.Map.of(    // Java 9 or later
            "Id", "Config", // ...
        ));
)
```

```
// Get the CloudFormation resource
var cfnBucket = (CfnBucket)bucket.Node.DefaultChild;

cfnBucket.AnalyticsConfigurations = new List<object> {
    new Dictionary<string, string>
    {
        ["Id"] = "Config",
        // ...
    }
};
```

您也可以使用此对象来更改诸如`Metadata`和之类的 AWS CloudFormation 选项`UpdatePolicy`。

**Example**  

```
cfnBucket.cfnOptions.metadata = {
  MetadataKey: 'MetadataValue'
};
```

```
cfnBucket.cfnOptions.metadata = {
  MetadataKey: 'MetadataValue'
};
```

```
cfn_bucket.cfn_options.metadata = {
    "MetadataKey": "MetadataValue"
}
```

```
cfnBucket.getCfnOptions().setMetadata(java.util.Map.of(    // Java 9+
    "MetadataKey", "Metadatavalue"));
```

```
cfnBucket.CfnOptions.Metadata = new Dictionary<string, object>
{
    ["MetadataKey"] = "Metadatavalue"
};
```

## 使用取消转义孵化
<a name="develop-customize-unescape"></a>

 AWS *CDK 还提供了提升抽象级别的功能，我们可以将其称为 “无法逃脱” 的舱口。*如果您有 L1 构造（例如 `CfnBucket`），则可以创建一个新的 L2 构造（在本例中为 `Bucket`）来封装 L1 构造。

当您创建 L1 资源但又想将其与需要 L2 资源的构造结合使用时，这会很方便。

**提示**  
如果您只需要对 L1 构造进行权限或其他集成，则无需将其封装在 L2 中。[改](facades.md)为直接在L1上使用像Grants职业这样的外观，例如，。`BucketGrants.fromBucket(cfnBucket).read(role)`

您可以在调用 `.fromCfnXxxxx()`（例如，对于 Amazon S3 存储桶为 `Bucket.fromCfnBucket()`）的 L2 类上使用静态方法移至更高抽象级别。L1 资源是唯一的参数。

**Example**  

```
b1 = new s3.CfnBucket(this, "buck09", { ... });
b2 = s3.Bucket.fromCfnBucket(b1);
```

```
b1 = new s3.CfnBucket(this, "buck09", { ...} );
b2 = s3.Bucket.fromCfnBucket(b1);
```

```
b1 = s3.CfnBucket(self, "buck09", ...)
 b2 = s3.from_cfn_bucket(b1)
```

```
CfnBucket b1 = CfnBucket.Builder.create(this, "buck09")
								// ....
		                        .build();
IBucket b2 = Bucket.fromCfnBucket(b1);
```

```
var b1 = new CfnBucket(this, "buck09", new CfnBucketProps { ... });
var v2 = Bucket.FromCfnBucket(b1);
```

从 L1 构造创建的 L2 构造是引用 L1 资源的代理对象，类似于根据资源名称或查找创建的代理对象。 ARNs对这些构造的修改不会影响最终合成的 AWS CloudFormation 模板（但是，由于您拥有 L1 资源，因此可以改为对其进行修改）。有关代理对象的更多信息，请参阅[引用 AWS 账户中的资源](resources.md#resources-external)。

为避免混淆，请勿创建引用同一 L1 构造的多个 L2 构造。例如，如果您使用[上一节](#develop-customize-escape-l2)中的方法从 `Bucket` 中提取 `CfnBucket`，则不应使用该 `CfnBucket` 调用 `Bucket.fromCfnBucket()` 来创建第二个 `Bucket` 实例。它实际上可以按预期工作（只有一个 ` AWS::S3::Bucket` 是合成的），但它会使代码更难维护。

## 使用原始覆盖
<a name="develop-customize-override"></a>

如果 L1 构造中缺少某些属性，则可以使用原始覆盖来绕过所有输入。这也可以删除合成的属性。

使用其中一种 `addOverride` 方法（Python：`add_override`），如以下示例所示。

**Example**  

```
// Get the CloudFormation resource
const cfnBucket = bucket.node.defaultChild as s3.CfnBucket;

// Use dot notation to address inside the resource template fragment
cfnBucket.addOverride('Properties.VersioningConfiguration.Status', 'NewStatus');
cfnBucket.addDeletionOverride('Properties.VersioningConfiguration.Status');

// use index (0 here) to address an element of a list
cfnBucket.addOverride('Properties.Tags.0.Value', 'NewValue');
cfnBucket.addDeletionOverride('Properties.Tags.0');

// addPropertyOverride is a convenience function for paths starting with "Properties."
cfnBucket.addPropertyOverride('VersioningConfiguration.Status', 'NewStatus');
cfnBucket.addPropertyDeletionOverride('VersioningConfiguration.Status');
cfnBucket.addPropertyOverride('Tags.0.Value', 'NewValue');
cfnBucket.addPropertyDeletionOverride('Tags.0');
```

```
// Get the CloudFormation resource
const cfnBucket = bucket.node.defaultChild ;

// Use dot notation to address inside the resource template fragment
cfnBucket.addOverride('Properties.VersioningConfiguration.Status', 'NewStatus');
cfnBucket.addDeletionOverride('Properties.VersioningConfiguration.Status');

// use index (0 here) to address an element of a list
cfnBucket.addOverride('Properties.Tags.0.Value', 'NewValue');
cfnBucket.addDeletionOverride('Properties.Tags.0');

// addPropertyOverride is a convenience function for paths starting with "Properties."
cfnBucket.addPropertyOverride('VersioningConfiguration.Status', 'NewStatus');
cfnBucket.addPropertyDeletionOverride('VersioningConfiguration.Status');
cfnBucket.addPropertyOverride('Tags.0.Value', 'NewValue');
cfnBucket.addPropertyDeletionOverride('Tags.0');
```

```
# Get the CloudFormation resource
cfn_bucket = bucket.node.default_child

# Use dot notation to address inside the resource template fragment
cfn_bucket.add_override("Properties.VersioningConfiguration.Status", "NewStatus")
cfn_bucket.add_deletion_override("Properties.VersioningConfiguration.Status")

# use index (0 here) to address an element of a list
cfn_bucket.add_override("Properties.Tags.0.Value", "NewValue")
cfn_bucket.add_deletion_override("Properties.Tags.0")

# addPropertyOverride is a convenience function for paths starting with "Properties."
cfn_bucket.add_property_override("VersioningConfiguration.Status", "NewStatus")
cfn_bucket.add_property_deletion_override("VersioningConfiguration.Status")
cfn_bucket.add_property_override("Tags.0.Value", "NewValue")
cfn_bucket.add_property_deletion_override("Tags.0")
```

```
// Get the CloudFormation resource
CfnBucket cfnBucket = (CfnBucket)bucket.getNode().getDefaultChild();

// Use dot notation to address inside the resource template fragment
cfnBucket.addOverride("Properties.VersioningConfiguration.Status", "NewStatus");
cfnBucket.addDeletionOverride("Properties.VersioningConfiguration.Status");

// use index (0 here) to address an element of a list
cfnBucket.addOverride("Properties.Tags.0.Value", "NewValue");
cfnBucket.addDeletionOverride("Properties.Tags.0");

// addPropertyOverride is a convenience function for paths starting with "Properties."
cfnBucket.addPropertyOverride("VersioningConfiguration.Status", "NewStatus");
cfnBucket.addPropertyDeletionOverride("VersioningConfiguration.Status");
cfnBucket.addPropertyOverride("Tags.0.Value", "NewValue");
cfnBucket.addPropertyDeletionOverride("Tags.0");
```

```
// Get the CloudFormation resource
var cfnBucket = (CfnBucket)bucket.node.defaultChild;

// Use dot notation to address inside the resource template fragment
cfnBucket.AddOverride("Properties.VersioningConfiguration.Status", "NewStatus");
cfnBucket.AddDeletionOverride("Properties.VersioningConfiguration.Status");

// use index (0 here) to address an element of a list
cfnBucket.AddOverride("Properties.Tags.0.Value", "NewValue");
cfnBucket.AddDeletionOverride("Properties.Tags.0");

// addPropertyOverride is a convenience function for paths starting with "Properties."
cfnBucket.AddPropertyOverride("VersioningConfiguration.Status", "NewStatus");
cfnBucket.AddPropertyDeletionOverride("VersioningConfiguration.Status");
cfnBucket.AddPropertyOverride("Tags.0.Value", "NewValue");
cfnBucket.AddPropertyDeletionOverride("Tags.0");
```

## 使用自定义资源
<a name="develop-customize-custom"></a>

如果该功能无法通过直接 API 调用 AWS CloudFormation，而只能通过直接 API 调用，则必须编写 AWS CloudFormation 自定义资源来进行所需的 API 调用。您可以使用 AWS CDK 编写自定义资源并将其封装到常规构造接口中。从构造使用者的角度来看，体验会让人感觉很原生。

构建自定义资源涉及编写 Lambda 函数来响应资源的 `CREATE`、`UPDATE` 和 `DELETE` 生命周期事件。如果您的自定义资源只需要进行一次 API 调用，请考虑使用[AwsCustomResource](https://github.com/awslabs/aws-cdk/tree/master/packages/%40aws-cdk/custom-resources)。这使得在 AWS CloudFormation 部署期间可以执行任意 SDK 调用。否则，您应该编写自己的 Lambda 函数来执行需要完成的工作。

主题过于宽泛，无法在此处全面介绍，但是以下链接应该可以帮助您入门：
+  [自定义资源](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) 
+  [自定义资源示例](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/custom-resource/) 
+ 有关更完整的示例，请参阅 CDK 标准库中的[DnsValidatedCertificate](https://github.com/awslabs/aws-cdk/blob/master/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts)类。这是作为自定义资源实现的。