为 CDK 工具包配置插件 - AWS Cloud Development Kit (AWS CDK) v2

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

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

为 CDK 工具包配置插件

AWS CDK 工具包支持为您的 CDK 工作流添加新功能的插件。插件主要是为与 CDK 命令行界面 (CDK CLI) 配合使用而设计的,但也可通过编程方式与 CDK 工具包库一起使用。它们提供了一种标准化的方式来扩展功能,例如备用凭证源。

注意

虽然插件系统可以与 CDK CLI 和 CDK 工具包库配合使用,但它主要面向 CLI 使用情况。当以编程方式使用 CDK 工具包库时,通常有更简单、更直接的方法可以完成相同的任务,而无需使用插件,尤其是在凭证管理方面。

目前,CDK 工具包支持一种插件功能:

  • 自定义 AWS 凭证提供程序 - 创建除内置机制之外的获取 AWS 凭证的替代方法。

如何创建插件

要创建 CDK 工具包插件,请首先创建一个用 TypeScript 或 JavaScript 编写的 Node.js 模块,该模块可以由 CDK 工具包加载。此模块会导出一个具有 CDK 工具包可识别的特定结构的对象。导出的对象至少必须包含版本标识符和接收 IPluginHost 实例的初始化函数,插件使用该实例来注册其功能。

TypeScript
CommonJS
// Example plugin structure import type { IPluginHost, Plugin } from '@aws-cdk/cli-plugin-contract'; const plugin: Plugin = { // Version of the plugin infrastructure (currently always '1') version: '1', // Initialization function called when the plugin is loaded init(host: IPluginHost): void { // Register your plugin functionality with the host // For example, register a custom credential provider } }; export = plugin;
ESM
// Example plugin structure import type { IPluginHost, Plugin } from '@aws-cdk/cli-plugin-contract'; const plugin: Plugin = { // Version of the plugin infrastructure (currently always '1') version: '1', // Initialization function called when the plugin is loaded init(host: IPluginHost): void { // Register your plugin functionality with the host // For example, register a custom credential provider } }; export { plugin as 'module.exports' };
JavaScript
CommonJS
const plugin = { // Version of the plugin infrastructure (currently always '1') version: '1', // Initialization function called when the plugin is loaded init(host) { // Register your plugin functionality with the host // For example, register a custom credential provider } }; module.exports = plugin;
ESM
const plugin = { // Version of the plugin infrastructure (currently always '1') version: '1', // Initialization function called when the plugin is loaded init(host) { // Register your plugin functionality with the host // For example, register a custom credential provider } }; export { plugin as 'module.exports' };
注意

CDK 工具包插件作为 CommonJS 模块加载。您可以将插件构建为 ECMAScript 模块 (ESM),但必须遵守一些限制

  • 该模块不能包含顶层 await,也不能导入任何包含顶层 await 的模块。

  • 该模块必须通过在 module.exports 导出名称 export { plugin as 'module.exports' } 下导出插件对象来确保兼容性。

如何使用 CDK CLI 加载插件

您可以通过两种方式指定插件:

方式 1:使用 --plugin 命令行选项
# Load a single plugin $ cdk list --plugin=my-custom-plugin # Load multiple plugins $ cdk deploy --plugin=custom-plugin-1 --plugin=custom-plugin-2

--plugin 参数的值应该是一个 JavaScript 文件,当通过 require() 函数导入时,该文件会返回一个实现 Plugin 接口的对象。

方式 2:向配置文件添加条目

您可以将插件规范添加到项目特定的 cdk.json 文件或 ~/.cdk.json 全局配置文件中:

{ "plugin": [ "custom-plugin-1", "custom-plugin-2" ] }

无论采用哪种方法,CDK CLI 都会在运行命令之前加载指定的插件。

使用 CDK 工具包库在代码中加载插件

以编程方式使用 CDK 工具包库时,可以使用 @aws-cdk/toolkit-lib 包中的 PluginHost 实例直接在代码中加载插件。PluginHost 提供了一个 load() 方法,用于按模块名称或路径加载插件。

TypeScript
import { Toolkit } from '@aws-cdk/toolkit-lib'; // Create a Toolkit instance const toolkit = new Toolkit(); // Load a plugin by module name or path // The module must export an object matching the Plugin interface await toolkit.pluginHost.load('my-custom-plugin'); // You can load multiple plugins if needed await toolkit.pluginHost.load('./path/to/another-plugin'); // Now proceed with other CDK Toolkit Library operations // The plugin's functionality will be available to the toolkit
JavaScript
const { Toolkit } = require('@aws-cdk/toolkit-lib'); // Create a Toolkit instance const toolkit = new Toolkit(); // Load a plugin by module name or path // The module must export an object matching the Plugin interface await toolkit.pluginHost.load('my-custom-plugin'); // You can load multiple plugins if needed await toolkit.pluginHost.load('./path/to/another-plugin'); // Now proceed with other CDK Toolkit Library operations // The plugin's functionality will be available to the toolkit

load() 方法采用单个参数 moduleSpec,即要加载的插件模块的名称或路径。这可以是:

  • node_modules 目录中安装的 Node.js 模块名称。

  • JavaScript 或 TypeScript 模块的相对或绝对文件路径。

实施凭证提供程序插件

插件当前的主要使用案例是创建自定义 AWS 凭证提供程序。与 AWS CLI 类似,CDK CLI 也需要 AWS 凭证进行身份验证和授权。但是,在某些情况下,标准凭证解析可能会失败:

  • 无法获取初始凭证集。

  • 无法获取初始凭证所属的账户。

  • 与凭证关联的账户与 CLI 尝试操作的账户不同。

为了解决这些情况,CDK 工具包支持凭证提供程序插件。这些插件实现 @aws-cdk/cli-plugin-contract 包中的 CredentialProviderSource 接口,并使用 registerCredentialProviderSource 方法注册到工具包。这使得 CDK 工具包能够从非标准来源(例如,专用身份验证系统或自定义凭证存储)获取 AWS 凭证。

要实现自定义凭证提供程序,请创建一个实现所需接口的类:

TypeScript
CommonJS
import type { CredentialProviderSource, ForReading, ForWriting, IPluginHost, Plugin, PluginProviderResult, SDKv3CompatibleCredentials } from '@aws-cdk/cli-plugin-contract'; class CustomCredentialProviderSource implements CredentialProviderSource { // Friendly name for the provider, used in error messages public readonly name: string = 'custom-credential-provider'; // Check if this provider is available on the current system public async isAvailable(): Promise<boolean> { // Return false if the plugin cannot be used // For example, if it depends on files not present on the host return true; } // Check if this provider can provide credentials for a specific account public async canProvideCredentials(accountId: string): Promise<boolean> { // Return false if the plugin cannot provide credentials for this account // For example, if the account is not managed by this credential system return true; // You can use patterns to filter specific accounts // return accountId.startsWith('123456'); } // Get credentials for the specified account and access mode // Returns PluginProviderResult which can be one of: // - SDKv2CompatibleCredentials (AWS SDK v2 entered maintenance on Sept 8, 2024 and will reach end-of-life on Sept 8, 2025) // - SDKv3CompatibleCredentialProvider // - SDKv3CompatibleCredentials public async getProvider(accountId: string, mode: ForReading | ForWriting): Promise<PluginProviderResult> { // The access mode can be used to provide different credential sets const readOnly = mode === 0 satisfies ForReading; // Create appropriate credentials based on your authentication mechanism const credentials: SDKv3CompatibleCredentials = { accessKeyId: 'AKIAIOSFODNN7EXAMPLE', secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', // Add sessionToken if using temporary credentials // sessionToken: 'AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4Olgk', // expireTime: new Date(Date.now() + 3600 * 1000), // 1 hour from now }; return credentials; } } const plugin: Plugin = { version: '1', init(host: IPluginHost): void { // Register the credential provider to the PluginHost. host.registerCredentialProviderSource(new CustomCredentialProviderSource()); } }; export = plugin;
ESM
import type { CredentialProviderSource, ForReading, ForWriting, IPluginHost, Plugin, PluginProviderResult, SDKv3CompatibleCredentials } from '@aws-cdk/cli-plugin-contract'; class CustomCredentialProviderSource implements CredentialProviderSource { // Friendly name for the provider, used in error messages public readonly name: string = 'custom-credential-provider'; // Check if this provider is available on the current system public async isAvailable(): Promise<boolean> { // Return false if the plugin cannot be used // For example, if it depends on files not present on the host return true; } // Check if this provider can provide credentials for a specific account public async canProvideCredentials(accountId: string): Promise<boolean> { // Return false if the plugin cannot provide credentials for this account // For example, if the account is not managed by this credential system return true; // You can use patterns to filter specific accounts // return accountId.startsWith('123456'); } // Get credentials for the specified account and access mode // Returns PluginProviderResult which can be one of: // - SDKv2CompatibleCredentials (AWS SDK v2 entered maintenance on Sept 8, 2024 and will reach end-of-life on Sept 8, 2025) // - SDKv3CompatibleCredentialProvider // - SDKv3CompatibleCredentials public async getProvider(accountId: string, mode: ForReading | ForWriting): Promise<PluginProviderResult> { // The access mode can be used to provide different credential sets const readOnly = mode === 0 satisfies ForReading; // Create appropriate credentials based on your authentication mechanism const credentials: SDKv3CompatibleCredentials = { accessKeyId: 'AKIAIOSFODNN7EXAMPLE', secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', // Add sessionToken if using temporary credentials // sessionToken: 'AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4Olgk', // expireTime: new Date(Date.now() + 3600 * 1000), // 1 hour from now }; return credentials; } } const plugin: Plugin = { version: '1', init(host: IPluginHost): void { // Register the credential provider to the PluginHost. host.registerCredentialProviderSource(new CustomCredentialProviderSource()); } }; export { plugin as 'module.exports' };
JavaScript
CommonJS
// Implement the CredentialProviderSource interface class CustomCredentialProviderSource { constructor() { // Friendly name for the provider, used in error messages this.name = 'custom-credential-provider'; } // Check if this provider is available on the current system async isAvailable() { // Return false if the plugin cannot be used // For example, if it depends on files not present on the host return true; } // Check if this provider can provide credentials for a specific account async canProvideCredentials(accountId) { // Return false if the plugin cannot provide credentials for this account // For example, if the account is not managed by this credential system return true; // You can use patterns to filter specific accounts // return accountId.startsWith('123456'); } // Get credentials for the specified account and access mode // Returns PluginProviderResult which can be one of: // - SDKv2CompatibleCredentials (AWS SDK v2 entered maintenance on Sept 8, 2024 and will reach end-of-life on Sept 8, 2025) // - SDKv3CompatibleCredentialProvider // - SDKv3CompatibleCredentials async getProvider(accountId, mode) { // The access mode can be used to provide different credential sets const readOnly = mode === 0; // 0 indicates ForReading; 1 indicates ForWriting // Create appropriate credentials based on your authentication mechanism const credentials = { accessKeyId: 'ASIAIOSFODNN7EXAMPLE', secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', // Add sessionToken if using temporary credentials // sessionToken: 'AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4Olgk', // expireTime: new Date(Date.now() + 3600 * 1000), // 1 hour from now }; return credentials; } } const plugin = { version: '1', init(host) { // Register the credential provider to the PluginHost. host.registerCredentialProviderSource(new CustomCredentialProviderSource()); } }; module.exports = plugin;
ESM
// Implement the CredentialProviderSource interface class CustomCredentialProviderSource { constructor() { // Friendly name for the provider, used in error messages this.name = 'custom-credential-provider'; } // Check if this provider is available on the current system async isAvailable() { // Return false if the plugin cannot be used // For example, if it depends on files not present on the host return true; } // Check if this provider can provide credentials for a specific account async canProvideCredentials(accountId) { // Return false if the plugin cannot provide credentials for this account // For example, if the account is not managed by this credential system return true; // You can use patterns to filter specific accounts // return accountId.startsWith('123456'); } // Get credentials for the specified account and access mode // Returns PluginProviderResult which can be one of: // - SDKv2CompatibleCredentials (AWS SDK v2 entered maintenance on Sept 8, 2024 and will reach end-of-life on Sept 8, 2025) // - SDKv3CompatibleCredentialProvider // - SDKv3CompatibleCredentials async getProvider(accountId, mode) { // The access mode can be used to provide different credential sets const readOnly = mode === 0; // 0 indicates ForReading; 1 indicates ForWriting // Create appropriate credentials based on your authentication mechanism const credentials = { accessKeyId: 'ASIAIOSFODNN7EXAMPLE', secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', // Add sessionToken if using temporary credentials // sessionToken: 'AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4Olgk', // expireTime: new Date(Date.now() + 3600 * 1000), // 1 hour from now }; return credentials; } } const plugin = { version: '1', init(host) { // Register the credential provider to the PluginHost. host.registerCredentialProviderSource(new CustomCredentialProviderSource()); } }; export { plugin as 'module.exports' };
重要

CDK 工具包会缓存从提供程序处获得的凭证。强烈建议您的提供程序返回的凭证对象会自刷新,以防止长时间运行的操作期间出现过期问题。有关更多信息,请参阅 AWS SDK for JavaScript v3 文档中的 Credentials

了解更多

要了解有关 CDK 工具包插件的更多信息,请参阅 aws-cdk-cli GitHub 存储库中的 AWS CDK Toolkit Plugin Contract