View a markdown version of this page

自定义构造库中的 AWS 构造 - AWS 云开发套件 (AWS CDK) v2

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

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

自定义构造库中的 AWS 构造

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

使用 Mixins 向 L1 构造添加功能

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

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

例
TypeScript
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());
JavaScript
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());
Python
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())
Java
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());
C#
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());
Go
bucket := awss3.NewCfnBucket(stack, jsii.String("MyBucket"), nil) bucket.CfnOptions().SetDeletionPolicy(awscdk.CfnDeletionPolicy_DELETE) bucket.With(awss3.NewBucketAutoDeleteObjects())

Mixin 可通过每个服务模块的mixins命名空间获得。你可以将 Mixins 与 Facades 结合使用,以获得对 L1 构造的权限和其他集成。有关 Mixins 的更多信息,请参阅 Mi x ins。

使用 Facades 对 L1 结构进行集成

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

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

例
TypeScript
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);
JavaScript
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);
Python
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)
Java
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);
C#
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);
Go
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)

有关外墙的更多信息,请参见外墙。

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

使用转义孵化

AWS 构造库提供了不同抽象级别的结构。

在最高级别上,你的 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 资源和属性类型参考。

为 L1 构造开发转义孵化

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

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

例
TypeScript
new s3.CfnBucket(this, 'amzn-s3-demo-bucket', { analyticsConfigurations: [ { id: 'Config', // ... } ] });
JavaScript
new s3.CfnBucket(this, 'amzn-s3-demo-bucket', { analyticsConfigurations: [ { id: 'Config' // ... } ] });
Python
s3.CfnBucket(self, "amzn-s3-demo-bucket", analytics_configurations: [ dict(id="Config", # ... ) ] )
Java
CfnBucket.Builder.create(this, "amzn-s3-demo-bucket") .analyticsConfigurations(Arrays.asList(java.util.Map.of( // Java 9 or later "id", "Config", // ... ))).build();
C#
new CfnBucket(this, 'amzn-s3-demo-bucket', new CfnBucketProps { AnalyticsConfigurations = new Dictionary<string, string> { ["id"] = "Config", // ... } });

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

例
TypeScript
new cdk.CfnResource(this, 'amzn-s3-demo-bucket', { type: 'AWS::S3::Bucket', properties: { // Note the PascalCase here! These are CloudFormation identifiers. AnalyticsConfigurations: [ { Id: 'Config', // ... } ] } });
JavaScript
new cdk.CfnResource(this, 'amzn-s3-demo-bucket', { type: 'AWS::S3::Bucket', properties: { // Note the PascalCase here! These are CloudFormation identifiers. AnalyticsConfigurations: [ { Id: 'Config' // ... } ] } });
Python
cdk.CfnResource(self, 'amzn-s3-demo-bucket', type="AWS::S3::Bucket", properties=dict( # Note the PascalCase here! These are CloudFormation identifiers. "AnalyticsConfigurations": [ { "Id": "Config", # ... } ] ) )
Java
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();
C#
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 构造开发转义孵化

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

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

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

例
TypeScript
// Get the CloudFormation resource const cfnBucket = bucket.node.defaultChild as s3.CfnBucket; // Change its properties cfnBucket.analyticsConfiguration = [ { id: 'Config', // ... } ];
JavaScript
// Get the CloudFormation resource const cfnBucket = bucket.node.defaultChild; // Change its properties cfnBucket.analyticsConfiguration = [ { id: 'Config' // ... } ];
Python
# Get the CloudFormation resource cfn_bucket = bucket.node.default_child # Change its properties cfn_bucket.analytics_configuration = [ { "id": "Config", # ... } ]
Java
// Get the CloudFormation resource CfnBucket cfnBucket = (CfnBucket)bucket.getNode().getDefaultChild(); cfnBucket.setAnalyticsConfigurations( Arrays.asList(java.util.Map.of( // Java 9 or later "Id", "Config", // ... )); )
C#
// 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。

例
TypeScript
cfnBucket.cfnOptions.metadata = { MetadataKey: 'MetadataValue' };
JavaScript
cfnBucket.cfnOptions.metadata = { MetadataKey: 'MetadataValue' };
Python
cfn_bucket.cfn_options.metadata = { "MetadataKey": "MetadataValue" }
Java
cfnBucket.getCfnOptions().setMetadata(java.util.Map.of( // Java 9+ "MetadataKey", "Metadatavalue"));
C#
cfnBucket.CfnOptions.Metadata = new Dictionary<string, object> { ["MetadataKey"] = "Metadatavalue" };

使用取消转义孵化

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

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

提示

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

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

例
TypeScript
b1 = new s3.CfnBucket(this, "buck09", { ... }); b2 = s3.Bucket.fromCfnBucket(b1);
JavaScript
b1 = new s3.CfnBucket(this, "buck09", { ...} ); b2 = s3.Bucket.fromCfnBucket(b1);
Python
b1 = s3.CfnBucket(self, "buck09", ...) b2 = s3.from_cfn_bucket(b1)
Java
CfnBucket b1 = CfnBucket.Builder.create(this, "buck09") // .... .build(); IBucket b2 = Bucket.fromCfnBucket(b1);
C#
var b1 = new CfnBucket(this, "buck09", new CfnBucketProps { ... }); var v2 = Bucket.FromCfnBucket(b1);

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

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

使用原始覆盖

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

使用其中一种 addOverride 方法(Python:add_override),如以下示例所示。

例
TypeScript
// 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');
JavaScript
// 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');
Python
# 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")
Java
// 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");
C#
// 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");

使用自定义资源

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

构建自定义资源涉及编写 Lambda 函数来响应资源的 CREATE、UPDATE 和 DELETE 生命周期事件。如果您的自定义资源只需要进行一次 API 调用,请考虑使用AwsCustomResource。这使得在 AWS CloudFormation 部署期间可以执行任意 SDK 调用。否则,您应该编写自己的 Lambda 函数来执行需要完成的工作。

主题过于宽泛,无法在此处全面介绍,但是以下链接应该可以帮助您入门: