diff --git a/docs/src/tools.rst b/docs/src/tools.rst index 1d9c5aca3dced..5610c9a6144d7 100644 --- a/docs/src/tools.rst +++ b/docs/src/tools.rst @@ -64,21 +64,31 @@ Below are the actions you can take on your CDK app: init [TEMPLATE] Create a new, empty CDK project from a template. Invoked without TEMPLATE, the app template will be used. + doctor Check your set-up for potential problems Options: - --help Show help [boolean] - --app, -a REQUIRED: Command-line of cloud executable (e.g. "node - bin/my-app.js") [string] - --context, -c Add contextual string parameter. [array] - --plugin, -p Name or path of a node package that extend the CDK features. - Can be specified multiple times [array] - --rename Rename stack name if different then the one defined in the - cloud executable [string] - --trace Print trace for stack warnings [boolean] - --strict Do not construct stacks with warnings [boolean] - --json, -j Use JSON output instead of YAML [boolean] - --verbose, -v Show debug logs [boolean] - --version Show version number [boolean] + --app, -a REQUIRED: Command-line for executing your CDK app (e.g. + "node bin/my-app.js") [string] + --context, -c Add contextual string parameter. [array] + --plugin, -p Name or path of a node package that extend the CDK + features. Can be specified multiple times [array] + --rename Rename stack name if different then the one defined in + the cloud executable [string] + --trace Print trace for stack warnings [boolean] + --strict Do not construct stacks with warnings [boolean] + --ignore-errors Ignores synthesis errors, which will likely produce an + invalid output [boolean] [default: false] + --json, -j Use JSON output instead of YAML [boolean] + --verbose, -v Show debug logs [boolean] + --profile Use the indicated AWS profile as the default environment + [string] + --proxy Use the indicated proxy. Will read from HTTPS_PROXY + environment variable if not specified. [string] + --version-reporting Disable insersion of the CDKMetadata resource in + synthesized templates [boolean] + --version Show version number [boolean] + --help Show help [boolean] + If your app has a single stack, there is no need to specify the stack name diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index 2fd6f28632b1e..9b83f1b2b1dbd 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -47,6 +47,7 @@ async function parseCommandLineArguments() { .option('json', { type: 'boolean', alias: 'j', desc: 'Use JSON output instead of YAML' }) .option('verbose', { type: 'boolean', alias: 'v', desc: 'Show debug logs' }) .option('profile', { type: 'string', desc: 'Use the indicated AWS profile as the default environment' }) + .option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified.' }) // tslint:disable-next-line:max-line-length .option('version-reporting', { type: 'boolean', desc: 'Disable insersion of the CDKMetadata resource in synthesized templates', default: undefined }) .command([ 'list', 'ls' ], 'Lists all stacks in the app', yargs => yargs @@ -108,7 +109,7 @@ async function initCommandLine() { debug('Command line arguments:', argv); - const aws = new SDK(argv.profile); + const aws = new SDK(argv.profile, argv.proxy); const availableContextProviders: contextplugins.ProviderMap = { 'availability-zones': new contextplugins.AZContextProviderPlugin(aws), diff --git a/packages/aws-cdk/lib/api/util/sdk.ts b/packages/aws-cdk/lib/api/util/sdk.ts index 84f4046e5c0b4..9da3f1adb1d19 100644 --- a/packages/aws-cdk/lib/api/util/sdk.ts +++ b/packages/aws-cdk/lib/api/util/sdk.ts @@ -19,26 +19,37 @@ import { SharedIniFile } from './sdk_ini_file'; * to the requested account. */ export class SDK { - private readonly userAgent: string; private readonly defaultAwsAccount: DefaultAWSAccount; private readonly credentialsCache: CredentialsCache; + private readonly defaultClientArgs: any = {}; - constructor(private readonly profile: string | undefined) { - // Find the package.json from the main toolkit - const pkg = (require.main as any).require('../package.json'); - this.userAgent = `${pkg.name}/${pkg.version}`; - + constructor(private readonly profile: string | undefined, proxyAddress: string | undefined) { const defaultCredentialProvider = makeCLICompatibleCredentialProvider(profile); this.defaultAwsAccount = new DefaultAWSAccount(defaultCredentialProvider); this.credentialsCache = new CredentialsCache(this.defaultAwsAccount, defaultCredentialProvider); + + // Find the package.json from the main toolkit + const pkg = (require.main as any).require('../package.json'); + this.defaultClientArgs.userAgent = `${pkg.name}/${pkg.version}`; + + // https://aws.amazon.com/blogs/developer/using-the-aws-sdk-for-javascript-from-behind-a-proxy/ + if (proxyAddress === undefined) { + proxyAddress = httpsProxyFromEnvironment(); + } + if (proxyAddress) { // Ignore empty string on purpose + debug('Using proxy server: %s', proxyAddress); + this.defaultClientArgs.httpOptions = { + agent: require('proxy-agent')(proxyAddress) + }; + } } public async cloudFormation(environment: Environment, mode: Mode): Promise { return new AWS.CloudFormation({ region: environment.region, credentials: await this.credentialsCache.get(environment.account, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -46,7 +57,7 @@ export class SDK { return new AWS.EC2({ region, credentials: await this.credentialsCache.get(awsAccountId, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -54,7 +65,7 @@ export class SDK { return new AWS.SSM({ region, credentials: await this.credentialsCache.get(awsAccountId, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -62,7 +73,7 @@ export class SDK { return new AWS.S3({ region: environment.region, credentials: await this.credentialsCache.get(environment.account, mode), - customUserAgent: this.userAgent + ...this.defaultClientArgs }); } @@ -109,7 +120,11 @@ class CredentialsCache { const defaultAccount = await this.defaultAwsAccount.get(); if (!awsAccountId || awsAccountId === defaultAccount) { debug(`Using default AWS SDK credentials for account ${awsAccountId}`); - return this.defaultCredentialProvider; + + // CredentialProviderChain extends Credentials, but that is a lie. + // https://github.com/aws/aws-sdk-js/issues/2235 + // Call resolve() instead. + return (await this.defaultCredentialProvider).resolvePromise(); } const triedSources: CredentialProviderSource[] = []; @@ -122,7 +137,14 @@ class CredentialsCache { triedSources.push(source); if (!(await source.canProvideCredentials(awsAccountId))) { continue; } debug(`Using ${source.name} credentials for account ${awsAccountId}`); - return await source.getProvider(awsAccountId, mode); + const providerOrCreds = await source.getProvider(awsAccountId, mode); + + // Backwards compatibility: if the plugin returns a ProviderChain, resolve that chain. + // Otherwise it must have returned credentials. + if ((providerOrCreds as any).resolvePromise) { + return await (providerOrCreds as any).resolvePromise(); + } + return providerOrCreds; } const sourceNames = ['default credentials'].concat(triedSources.map(s => s.name)).join(', '); throw new Error(`Need to perform AWS calls for account ${awsAccountId}, but no credentials found. Tried: ${sourceNames}.`); @@ -256,3 +278,16 @@ async function getCLICompatibleDefaultRegion(profile: string | undefined): Promi return region; } + +/** + * Find and return the configured HTTPS proxy address + */ +function httpsProxyFromEnvironment(): string | undefined { + if (process.env.https_proxy) { + return process.env.https_proxy; + } + if (process.env.HTTPS_PROXY) { + return process.env.HTTPS_PROXY; + } + return undefined; +} \ No newline at end of file diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 9eed25f940f32..fbc129b655ae1 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -56,6 +56,7 @@ "json-diff": "^0.3.1", "minimatch": ">=3.0", "promptly": "^0.2.0", + "proxy-agent": "^3.0.1", "request": "^2.83.0", "source-map-support": "^0.5.6", "yamljs": "^0.2.0",