|
| 1 | +include::attributes.txt[] |
| 2 | + |
| 3 | +// Attributes |
| 4 | + |
| 5 | +[.topic] |
| 6 | +[#plugins] |
| 7 | += Configure plugins for the CDK Toolkit |
| 8 | +:info_titleabbrev: Configure plugins |
| 9 | +:keywords: CDK Command Line Interface, CDK CLI, {aws} CDK, {aws} Cloud Development Kit ({aws} CDK), credentials, plugins, CDK Toolkit, CDK Toolkit Library |
| 10 | + |
| 11 | +[abstract] |
| 12 | +-- |
| 13 | +The {aws} CDK Toolkit provides a plugin system that enables developers to extend its functionality. |
| 14 | +-- |
| 15 | + |
| 16 | +// Content start |
| 17 | + |
| 18 | +The {aws} CDK Toolkit supports plugins that add new capabilities to your CDK workflows. Plugins are primarily designed for use with the CDK Command Line Interface (CDK CLI), though they can also be used with the CDK Toolkit Library programmatically. They provide a standardized way to extend functionality, such as alternative credential sources. |
| 19 | + |
| 20 | +[NOTE] |
| 21 | +==== |
| 22 | +While the plugin system works with both the CDK CLI and CDK Toolkit Library, it is primarily intended for CLI usage. When using the CDK Toolkit Library programmatically, there are often simpler, more direct ways to accomplish the same tasks without plugins, particularly for credential management. |
| 23 | +==== |
| 24 | + |
| 25 | +Currently, the CDK Toolkit supports one plugin capability: |
| 26 | + |
| 27 | +* *Custom {aws} credential providers* - Create alternative methods to obtain {aws} credentials beyond the built-in mechanisms. |
| 28 | +
|
| 29 | +[#plugins-create] |
| 30 | +== How to create plugins |
| 31 | + |
| 32 | +To create a CDK Toolkit plugin, you first create a Node.js module, authored in TypeScript or JavaScript, that can be loaded by the CDK Toolkit. This module exports an object with a specific structure that the CDK Toolkit can recognize. At minimum, the exported object must include a version identifier and an initialization function that receives an `IPluginHost` instance, which the plugin uses to register its capabilities. |
| 33 | + |
| 34 | +==== |
| 35 | +[role="tablist"] |
| 36 | +TypeScript:: |
| 37 | ++ |
| 38 | +[source,typescript,subs="verbatim,attributes"] |
| 39 | +---- |
| 40 | +// Example plugin structure |
| 41 | +import type { IPluginHost } from '@aws-cdk/cli-plugin-contract'; |
| 42 | +
|
| 43 | +export = { |
| 44 | + // Version of the plugin infrastructure (currently always '1') |
| 45 | + version: '1', |
| 46 | + |
| 47 | + // Initialization function called when the plugin is loaded |
| 48 | + init(host: IPluginHost): void { |
| 49 | + // Register your plugin functionality with the host |
| 50 | + // For example, register a custom credential provider |
| 51 | + } |
| 52 | +}; |
| 53 | +---- |
| 54 | +
|
| 55 | +JavaScript:: |
| 56 | ++ |
| 57 | +[source,javascript,subs="verbatim,attributes"] |
| 58 | +---- |
| 59 | +module.exports = { |
| 60 | + // Version of the plugin infrastructure (currently always '1') |
| 61 | + version: '1', |
| 62 | + |
| 63 | + // Initialization function called when the plugin is loaded |
| 64 | + init(host) { |
| 65 | + // Register your plugin functionality with the host |
| 66 | + // For example, register a custom credential provider |
| 67 | + } |
| 68 | +}; |
| 69 | +---- |
| 70 | +==== |
| 71 | + |
| 72 | +[#plugins-load-cli] |
| 73 | +== How to load plugins with the CDK CLI |
| 74 | + |
| 75 | +You have two options to specify plugins: |
| 76 | + |
| 77 | +*Option 1: Use the `--plugin` command line option*:: |
| 78 | ++ |
| 79 | +[source,bash,subs="verbatim,attributes"] |
| 80 | +---- |
| 81 | +# Load a single plugin |
| 82 | +$ cdk list --plugin=my-custom-plugin |
| 83 | + |
| 84 | +# Load multiple plugins |
| 85 | +$ cdk deploy --plugin=custom-plugin-1 --plugin=custom-plugin-2 |
| 86 | +---- |
| 87 | ++ |
| 88 | +The value of the `--plugin` argument should be a JavaScript file that, when imported via the Node.js `require()` function, returns an object implementing the `Plugin` interface. |
| 89 | + |
| 90 | +*Option 2: Add entries to configuration files*:: |
| 91 | ++ |
| 92 | +You can add plugin specifications to the project-specific `cdk.json` file or the `~/.cdk.json` global configuration file: |
| 93 | ++ |
| 94 | +[source,json,subs="verbatim,attributes"] |
| 95 | +---- |
| 96 | +{ |
| 97 | + "plugin": [ |
| 98 | + "custom-plugin-1", |
| 99 | + "custom-plugin-2" |
| 100 | + ] |
| 101 | +} |
| 102 | +---- |
| 103 | ++ |
| 104 | +With either approach, the CDK CLI will load the specified plugins before running commands. |
| 105 | + |
| 106 | +[#plugins-load-library] |
| 107 | +=== Load plugins in code with the CDK Toolkit Library |
| 108 | + |
| 109 | +When working with the CDK Toolkit Library programmatically, you can load plugins directly in your code using the `PluginHost` instance from the `@aws-cdk/toolkit-lib` package. The `PluginHost` provides a `load()` method for loading plugins by module name or path. |
| 110 | + |
| 111 | +==== |
| 112 | +[role="tablist"] |
| 113 | +TypeScript:: |
| 114 | ++ |
| 115 | +[source,typescript,subs="verbatim,attributes"] |
| 116 | +---- |
| 117 | +import { Toolkit } from '@aws-cdk/toolkit-lib'; |
| 118 | +
|
| 119 | +// Create a Toolkit instance |
| 120 | +const toolkit = new Toolkit(); |
| 121 | +
|
| 122 | +// Load a plugin by module name or path |
| 123 | +// The module must export an object matching the Plugin interface |
| 124 | +await toolkit.pluginHost.load('my-custom-plugin'); |
| 125 | +
|
| 126 | +// You can load multiple plugins if needed |
| 127 | +await toolkit.pluginHost.load('./path/to/another-plugin'); |
| 128 | +
|
| 129 | +// Now proceed with other CDK Toolkit Library operations |
| 130 | +// The plugin's functionality will be available to the toolkit |
| 131 | +---- |
| 132 | +
|
| 133 | +JavaScript:: |
| 134 | ++ |
| 135 | +[source,javascript,subs="verbatim,attributes"] |
| 136 | +---- |
| 137 | +const { Toolkit } = require('@aws-cdk/toolkit-lib'); |
| 138 | +
|
| 139 | +// Create a Toolkit instance |
| 140 | +const toolkit = new Toolkit(); |
| 141 | +
|
| 142 | +// Load a plugin by module name or path |
| 143 | +// The module must export an object matching the Plugin interface |
| 144 | +await toolkit.pluginHost.load('my-custom-plugin'); |
| 145 | +
|
| 146 | +// You can load multiple plugins if needed |
| 147 | +await toolkit.pluginHost.load('./path/to/another-plugin'); |
| 148 | +
|
| 149 | +// Now proceed with other CDK Toolkit Library operations |
| 150 | +// The plugin's functionality will be available to the toolkit |
| 151 | +---- |
| 152 | +==== |
| 153 | + |
| 154 | +The `load()` method takes a single parameter, `moduleSpec`, which is the name or path of the plugin module to load. This can be either: |
| 155 | + |
| 156 | +* A Node.js module name installed in the `node_modules` directory. |
| 157 | +* A relative or absolute file path to a JavaScript or TypeScript module. |
| 158 | +
|
| 159 | +[#plugins-credentials] |
| 160 | +== Implementing credential provider plugins |
| 161 | + |
| 162 | +The current primary use case for plugins is to create custom {aws} credential providers. Like the {aws} CLI, the CDK CLI needs {aws} credentials for authentication and authorization. However, there are several scenarios where the standard credential resolution might fail: |
| 163 | + |
| 164 | +* The initial set of credentials cannot be obtained. |
| 165 | +* The account to which the initial credentials belong cannot be obtained. |
| 166 | +* The account associated with the credentials is different from the account on which the CLI is trying to operate. |
| 167 | +
|
| 168 | +To address these scenarios, the CDK Toolkit supports credential provider plugins. These plugins implement the `CredentialProviderSource` interface from the `@aws-cdk/cli-plugin-contract` package and are registered to the Toolkit using the `registerCredentialProviderSource` method. This enables the CDK Toolkit to obtain {aws} credentials from non-standard sources like specialized authentication systems or custom credential stores. |
| 169 | + |
| 170 | +To implement a custom credential provider, create a class that implements the required interface: |
| 171 | + |
| 172 | +==== |
| 173 | +[role="tablist"] |
| 174 | +TypeScript:: |
| 175 | ++ |
| 176 | +[source,typescript,subs="verbatim,attributes"] |
| 177 | +---- |
| 178 | +import type { IPluginHost } from '@aws-cdk/cli-plugin-contract'; |
| 179 | +import { registerCredentialProviderSource, Mode, CredentialProviderSource } from '@aws-cdk/cli-plugin-contract'; |
| 180 | +import { Credentials } from '@aws-sdk/client-sts'; |
| 181 | +
|
| 182 | +class CustomCredentialProviderSource implements CredentialProviderSource { |
| 183 | + // Friendly name for the provider, used in error messages |
| 184 | + public readonly name: string = 'custom-credential-provider'; |
| 185 | + |
| 186 | + // Check if this provider is available on the current system |
| 187 | + public async isAvailable(): Promise<boolean> { |
| 188 | + // Return false if the plugin cannot be used |
| 189 | + // For example, if it depends on files not present on the host |
| 190 | + return true; |
| 191 | + } |
| 192 | + |
| 193 | + // Check if this provider can provide credentials for a specific account |
| 194 | + public async canProvideCredentials(accountId: string): Promise<boolean> { |
| 195 | + // Return false if the plugin cannot provide credentials for this account |
| 196 | + // For example, if the account is not managed by this credential system |
| 197 | + return true; |
| 198 | + // You can use patterns to filter specific accounts |
| 199 | + // return accountId.startsWith('123456'); |
| 200 | + } |
| 201 | + |
| 202 | + // Get credentials for the specified account and access mode |
| 203 | + // Returns PluginProviderResult which can be one of: |
| 204 | + // - SDKv2CompatibleCredentials (AWS SDK v2 entered maintenance on Sept 8, 2024 and will reach end-of-life on Sept 8, 2025) |
| 205 | + // - SDKv3CompatibleCredentialProvider |
| 206 | + // - SDKv3CompatibleCredentials |
| 207 | + public async getProvider(accountId: string, mode: Mode): Promise<PluginProviderResult> { |
| 208 | + // The access mode can be used to provide different credential sets |
| 209 | + const readOnly = mode === Mode.ForReading; |
| 210 | + |
| 211 | + // Create appropriate credentials based on your authentication mechanism |
| 212 | + // In this example we're using AWS SDK v3 credentials |
| 213 | + const credentials = new Credentials({ |
| 214 | + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', |
| 215 | + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', |
| 216 | + // Add sessionToken if using temporary credentials |
| 217 | + // sessionToken: 'AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4Olgk', |
| 218 | + // expireTime: new Date(Date.now() + 3600 * 1000), // 1 hour from now |
| 219 | + }); |
| 220 | + |
| 221 | + return credentials; |
| 222 | + } |
| 223 | +} |
| 224 | +
|
| 225 | +export = { |
| 226 | + version = '1', |
| 227 | + init(host: IPluginHost): void { |
| 228 | + // Register the credential provider to the PluginHost. |
| 229 | + host.registerCredentialProviderSource(new CustomCredentialProviderSource()); |
| 230 | + } |
| 231 | +}; |
| 232 | +---- |
| 233 | +
|
| 234 | +JavaScript:: |
| 235 | ++ |
| 236 | +[source,javascript,subs="verbatim,attributes"] |
| 237 | +---- |
| 238 | +const { Mode, registerCredentialProviderSource, CredentialProviderSource } = require('@aws-cdk/cli-plugin-contract'); |
| 239 | +const { Credentials } = require('@aws-sdk/client-sts'); |
| 240 | +
|
| 241 | +// Implement the CredentialProviderSource interface |
| 242 | +class CustomCredentialProviderSource extends CredentialProviderSource { |
| 243 | + constructor() { |
| 244 | + super(); |
| 245 | + // Friendly name for the provider, used in error messages |
| 246 | + this.name = 'custom-credential-provider'; |
| 247 | + } |
| 248 | + |
| 249 | + // Check if this provider is available on the current system |
| 250 | + async isAvailable() { |
| 251 | + // Return false if the plugin cannot be used |
| 252 | + // For example, if it depends on files not present on the host |
| 253 | + return true; |
| 254 | + } |
| 255 | + |
| 256 | + // Check if this provider can provide credentials for a specific account |
| 257 | + async canProvideCredentials(accountId) { |
| 258 | + // Return false if the plugin cannot provide credentials for this account |
| 259 | + // For example, if the account is not managed by this credential system |
| 260 | + return true; |
| 261 | + // You can use patterns to filter specific accounts |
| 262 | + // return accountId.startsWith('123456'); |
| 263 | + } |
| 264 | + |
| 265 | + // Get credentials for the specified account and access mode |
| 266 | + // Returns PluginProviderResult which can be one of: |
| 267 | + // - SDKv2CompatibleCredentials (AWS SDK v2 entered maintenance on Sept 8, 2024 and will reach end-of-life on Sept 8, 2025) |
| 268 | + // - SDKv3CompatibleCredentialProvider |
| 269 | + // - SDKv3CompatibleCredentials |
| 270 | + async getProvider(accountId, mode) { |
| 271 | + // The access mode can be used to provide different credential sets |
| 272 | + const readOnly = mode === Mode.ForReading; |
| 273 | + |
| 274 | + // Create appropriate credentials based on your authentication mechanism |
| 275 | + // In this example we're using AWS SDK v3 credentials |
| 276 | + const credentials = new Credentials({ |
| 277 | + accessKeyId: 'ASIAIOSFODNN7EXAMPLE', |
| 278 | + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', |
| 279 | + // Add sessionToken if using temporary credentials |
| 280 | + // sessionToken: 'AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4Olgk', |
| 281 | + // expireTime: new Date(Date.now() + 3600 * 1000), // 1 hour from now |
| 282 | + }); |
| 283 | + |
| 284 | + return credentials; |
| 285 | + } |
| 286 | +} |
| 287 | +
|
| 288 | +module.exports = { |
| 289 | + version = '1', |
| 290 | + init(host) { |
| 291 | + // Register the credential provider to the PluginHost. |
| 292 | + host.registerCredentialProviderSource(new CustomCredentialProviderSource()); |
| 293 | + } |
| 294 | +}; |
| 295 | +---- |
| 296 | +==== |
| 297 | + |
| 298 | +[IMPORTANT] |
| 299 | +==== |
| 300 | +Credentials obtained from providers are cached by the CDK Toolkit. It's strongly recommended that credential objects returned by your provider are self-refreshing to prevent expiration issues during long-running operations. For more information, see link:https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-sts/Interface/Credentials/[Credentials] in the _{aws} SDK for JavaScript v3 documentation_. |
| 301 | +==== |
| 302 | + |
| 303 | +[#plugins-learn] |
| 304 | +== Learn more |
| 305 | + |
| 306 | +To learn more about CDK Toolkit plugins, see the link:https://github.com/aws/aws-cdk-cli/tree/main/packages/%40aws-cdk/cli-plugin-contract[{aws} CDK Toolkit Plugin Contract] in the _aws-cdk-cli GitHub repository_. |
0 commit comments