diff --git a/packages/@aws-cdk/toolkit-lib/README.md b/packages/@aws-cdk/toolkit-lib/README.md index 8243aef7b..38f31fbd9 100644 --- a/packages/@aws-cdk/toolkit-lib/README.md +++ b/packages/@aws-cdk/toolkit-lib/README.md @@ -165,6 +165,9 @@ Deployment is the process of provisioning or updating your infrastructure in AWS For an introduction to deploying, see [Deploy AWS CDK applications](https://docs.aws.amazon.com/cdk/v2/guide/deploy.html). With the CDK Toolkit Library, you can programmatically control deployments, including stack selection, rollback behavior, and deployment monitoring. +Deployment failure diagnosis includes CloudTrail context by default. Set `cloudTrail: false` +in the deploy options to skip CloudTrail lookups while retaining other failure diagnostics. + ```ts declare const cdk: Toolkit; declare const cx: ICloudAssemblySource; diff --git a/packages/@aws-cdk/toolkit-lib/lib/actions/deploy/index.ts b/packages/@aws-cdk/toolkit-lib/lib/actions/deploy/index.ts index ece5a6be9..51a61ab57 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/actions/deploy/index.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/actions/deploy/index.ts @@ -168,6 +168,14 @@ export interface BaseDeployOptions { */ readonly rollback?: boolean; + /** + * Include CloudTrail context when diagnosing deployment failures. + * Set to false to skip CloudTrail lookups while retaining other diagnostics. + * + * @default true + */ + readonly cloudTrail?: boolean; + /** * Automatically orphan resources that failed during rollback * diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/deployments/deployments.ts b/packages/@aws-cdk/toolkit-lib/lib/api/deployments/deployments.ts index 5c73ba961..1e349886e 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/deployments/deployments.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/deployments/deployments.ts @@ -46,6 +46,11 @@ import { DEFAULT_TOOLKIT_STACK_NAME } from '../toolkit-info'; const BOOTSTRAP_STACK_VERSION_FOR_ROLLBACK = 23; export interface DeployStackOptions { + /** + * Include CloudTrail failure context. + * @default true + */ + readonly cloudTrail?: boolean; /** * Stack to deploy */ @@ -446,6 +451,7 @@ export class Deployments { overrideTemplate: options.overrideTemplate, assetParallelism: options.assetParallelism, diagnoser: new CloudFormationStackDiagnoser({ + cloudTrail: options.cloudTrail, sdk: env.sdk, envResources: env.resources, sourceTracer: new StackArtifactSourceTracer(options.stack), diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/diagnosing/stack-diagnoser.ts b/packages/@aws-cdk/toolkit-lib/lib/api/diagnosing/stack-diagnoser.ts index 2a9165d0a..df5325235 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/diagnosing/stack-diagnoser.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/diagnosing/stack-diagnoser.ts @@ -17,6 +17,11 @@ import type { ResourceError, ResourceErrors } from '../stack-events/resource-err import { StackStatus } from '../stack-events/stack-status'; export interface CloudFormationStackDiagnoserProps { + /** + * Include CloudTrail failure context. + * @default true + */ + readonly cloudTrail?: boolean; readonly sdk: SDK; readonly envResources?: EnvironmentResources; readonly sourceTracer: ISourceTracer; @@ -188,6 +193,9 @@ export class CloudFormationStackDiagnoser { * case the output notifies the user. */ private async addCloudTrailContext(problems: TracedResourceError[], errs: readonly ResourceError[]): Promise { + if (this.props.cloudTrail === false) { + return problems; + } const sdk = await this.additionalExplorationSdk(); if (!sdk || problems.length === 0) { return problems; diff --git a/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts b/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts index 8cb119854..bb8c7b400 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts @@ -914,6 +914,7 @@ export class Toolkit extends CloudAssemblySourceBuilder { usePreviousParameters: options.parameters?.keepExistingParameters, rollback: options.rollback, notificationArns, + cloudTrail: options.cloudTrail, rollbackConfiguration, extraUserAgent: options.extraUserAgent, assetParallelism: options.assetParallelism, diff --git a/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts b/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts index 0d8539f5d..de63bf0d6 100644 --- a/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts +++ b/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts @@ -375,6 +375,14 @@ IAM Statement Changes }); describe('deployment options', () => { + test.each([true, false])('CloudTrail preference %s is passed to deployment preparation and execution', async (cloudTrail) => { + const prepare = jest.spyOn(deployments.Deployments.prototype, 'prepareStack').mockResolvedValueOnce(undefined); + const cx = await cdkOutFixture(toolkit, 'stack-with-role'); + await toolkit.deploy(cx, { cloudTrail, deploymentMethod: { method: 'change-set' } }); + expect(prepare).toHaveBeenCalledWith(expect.objectContaining({ cloudTrail })); + expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({ cloudTrail })); + successfulDeployment(); + }); test('parameters are passed in', async () => { // WHEN const cx = await cdkOutFixture(toolkit, 'stack-with-role'); diff --git a/packages/@aws-cdk/toolkit-lib/test/api/deployments/cloudformation-deployments.test.ts b/packages/@aws-cdk/toolkit-lib/test/api/deployments/cloudformation-deployments.test.ts index b109de89b..71bf75e3e 100644 --- a/packages/@aws-cdk/toolkit-lib/test/api/deployments/cloudformation-deployments.test.ts +++ b/packages/@aws-cdk/toolkit-lib/test/api/deployments/cloudformation-deployments.test.ts @@ -2,18 +2,21 @@ import { AssetManifest } from '@aws-cdk/cdk-assets-lib'; import { ContinueUpdateRollbackCommand, DescribeStackEventsCommand, + DescribeStackResourcesCommand, DescribeStacksCommand, ListStackResourcesCommand, RollbackStackCommand, type StackResourceSummary, StackStatus, } from '@aws-sdk/client-cloudformation'; +import { LookupEventsCommand } from '@aws-sdk/client-cloudtrail'; import { GetParameterCommand } from '@aws-sdk/client-ssm'; import { CloudFormationStack } from '../../../lib/api/cloudformation'; import { Deployments } from '../../../lib/api/deployments'; import * as cfnApi from '../../../lib/api/deployments/cfn-api'; import { determineAllowCrossAccountAssetPublishing } from '../../../lib/api/deployments/checks'; import { deployStack, destroyStack } from '../../../lib/api/deployments/deploy-stack'; +import { ResourceErrors } from '../../../lib/api/stack-events/resource-errors'; import { ToolkitInfo } from '../../../lib/api/toolkit-info'; import type { BootstrapError } from '../../../lib/toolkit/toolkit-error'; import { ToolkitError } from '../../../lib/toolkit/toolkit-error'; @@ -21,6 +24,7 @@ import { testStack } from '../../_helpers/assembly'; import { mockBootstrapStack, mockCloudFormationClient, + mockCloudTrailClient, MockSdk, MockSdkProvider, mockSSMClient, @@ -76,6 +80,41 @@ function mockSuccessfulBootstrapStackLookup(props?: Record) { mockToolkitInfoLookup.mockResolvedValue(ToolkitInfo.fromStack(fakeStack)); } +test.each([undefined, true, false])('deployment diagnoser respects CloudTrail preference %s', async (cloudTrail) => { + const stackArn = 'arn:aws:cloudformation:us-east-1:123456789012:stack/boop/abc'; + const timestamp = new Date('2026-06-19T12:00:00Z'); + mockCloudFormationClient.on(DescribeStackResourcesCommand).resolves({ + StackResources: [{ + LogicalResourceId: 'Function', ResourceType: 'AWS::Lambda::Function', PhysicalResourceId: 'boop-function', + }], + }); + mockCloudTrailClient.on(LookupEventsCommand).resolves({ Events: [] }); + (deployStack as jest.Mock).mockImplementation(async ({ diagnoser }) => { + const errors = new ResourceErrors(); + errors.update({ + event: { + StackId: stackArn, + StackName: 'boop', + EventId: 'failed', + LogicalResourceId: 'Function', + PhysicalResourceId: 'boop-function', + ResourceType: 'AWS::Lambda::Function', + ResourceStatus: 'CREATE_FAILED', + ResourceStatusReason: 'Handler error', + Timestamp: timestamp, + }, + parentStackLogicalIds: [], + isRootStackEvent: false, + }); + const diagnosis = await diagnoser.diagnoseFromErrorCollection(errors, { + StackName: 'boop', StackStatus: 'UPDATE_FAILED', CreationTime: timestamp, + }); + expect(diagnosis.result).toMatchObject({ type: 'problem', problems: [expect.objectContaining({ message: 'Handler error' })] }); + }); + await deployments.deployStack({ stack: testStack({ stackName: 'boop' }), cloudTrail }); + expect(mockCloudTrailClient.commandCalls(LookupEventsCommand).length).toBe(cloudTrail === false ? 0 : 1); +}); + test('passes through deploymentMethod with hotswap to deployStack()', async () => { // WHEN await deployments.deployStack({ diff --git a/packages/@aws-cdk/toolkit-lib/test/api/diagnosing/stack-diagnoser.test.ts b/packages/@aws-cdk/toolkit-lib/test/api/diagnosing/stack-diagnoser.test.ts index a2703eaf4..b01d0c595 100644 --- a/packages/@aws-cdk/toolkit-lib/test/api/diagnosing/stack-diagnoser.test.ts +++ b/packages/@aws-cdk/toolkit-lib/test/api/diagnosing/stack-diagnoser.test.ts @@ -932,8 +932,9 @@ describe('CloudFormationStackDiagnoser', () => { return errors; } - function makeExploringDiagnoser() { + function makeExploringDiagnoser(cloudTrail?: boolean) { return new CloudFormationStackDiagnoser({ + cloudTrail, sdk, sourceTracer: fakeTracer, ioHelper: ioHost.asHelper('diagnose'), @@ -942,9 +943,7 @@ describe('CloudFormationStackDiagnoser', () => { }); } - test('attaches correlated CloudTrail errors to the failed resource on any path', async () => { - // No cloudTrailEnabled gating anymore: the investigation runs for deploy-originating - // error collections too (a recent failure just yields fewer delivered events). + test.each([undefined, true])('attaches CloudTrail errors when enabled: %s', async (cloudTrail) => { fakeCfn.createStackSync({ StackName: 'MyStack', StackStatus: 'UPDATE_FAILED', StackId: STACK_ARN }); mockCloudFormationClient.on(DescribeStackResourcesCommand).resolves({ StackResources: [{ @@ -968,7 +967,7 @@ describe('CloudFormationStackDiagnoser', () => { }], }); - const result = await makeExploringDiagnoser().diagnoseFromErrorCollection(makeErrors(), { + const result = await makeExploringDiagnoser(cloudTrail).diagnoseFromErrorCollection(makeErrors(), { StackName: 'MyStack', StackStatus: 'UPDATE_FAILED', CreationTime: new Date(), @@ -980,6 +979,16 @@ describe('CloudFormationStackDiagnoser', () => { expect(context!.messages[0]).toMatch(/AccessDenied on s3\.amazonaws\.com:CreateBucket/); }); + test('disabling CloudTrail retains the original failure without querying CloudTrail', async () => { + const result = await makeExploringDiagnoser(false).diagnoseFromErrorCollection(makeErrors(), { + StackName: 'MyStack', StackStatus: 'UPDATE_FAILED', CreationTime: new Date(), + }); + const problem = assertProblem(result); + expect(problem.problems[0].message).toContain('Handler error'); + expect(mockCloudTrailClient).not.toHaveReceivedCommand(LookupEventsCommand); + expect(problem.problems[0].additionalContext?.some((context) => context.source.startsWith('CloudTrail')) ?? false).toBe(false); + }); + test('does not run the investigation without an exploration SDK', async () => { const result = await makeDiagnoser().diagnoseFromErrorCollection(makeErrors(), { StackName: 'MyStack', diff --git a/packages/aws-cdk/README.md b/packages/aws-cdk/README.md index b9337af76..fce805eda 100644 --- a/packages/aws-cdk/README.md +++ b/packages/aws-cdk/README.md @@ -306,6 +306,12 @@ In the output above: ### `cdk deploy` +Failed deployments include CloudTrail context by default. Use `cdk deploy --no-cloudtrail` +to skip CloudTrail lookups while retaining CloudFormation failure messages and other resource +diagnostics. You can also set `"cloudtrail": false` in `cdk.json`; `--cloudtrail` enables it +for an individual deployment. The programmatic `Toolkit.deploy()` API accepts the equivalent +`cloudTrail: false` option. + Deploys a stack of your CDK app to its environment. During the deployment, the toolkit will output progress indications, similar to what can be observed in the AWS CloudFormation Console. If the environment was never bootstrapped (using `cdk bootstrap`), only stacks that are not using assets and synthesize to a template that is under diff --git a/packages/aws-cdk/lib/cli/cdk-toolkit.ts b/packages/aws-cdk/lib/cli/cdk-toolkit.ts index c9a353074..9b4e65d65 100644 --- a/packages/aws-cdk/lib/cli/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cli/cdk-toolkit.ts @@ -528,6 +528,7 @@ export class CdkToolkit { forceDeployment: options.force, rollback: options.rollback, reuseAssets: options.reuseAssets, + cloudTrail: options.cloudTrail, concurrency: options.concurrency, traceLogs: options.traceLogs, notificationArns: options.notificationArns, @@ -622,6 +623,7 @@ export class CdkToolkit { usePreviousParameters: options.usePreviousParameters, rollback: options.rollback, concurrency, + cloudTrail: options.cloudTrail, requireApproval, assetParallelism: options.assetParallelism, extraUserAgent: options.extraUserAgent, @@ -1724,6 +1726,11 @@ export interface DiffOptions { } interface CfnDeployOptions { + /** + * Include CloudTrail failure context. + * @default true + */ + readonly cloudTrail?: boolean; /** * Criteria for selecting stacks to deploy */ @@ -2319,6 +2326,11 @@ export async function displayFlagsMessage(ioHost: IoHelper, toolkit: InternalToo } interface WorkGraphDeploymentActionsOptions { + /** + * Include CloudTrail failure context. + * @default true + */ + readonly cloudTrail?: boolean; readonly roleArn?: string; readonly force?: boolean; readonly stackCount?: number; @@ -2493,6 +2505,7 @@ class WorkGraphDeploymentActions implements WorkGraphActions { parameters: Object.assign({}, parameterMap['*'], parameterMap[stack.stackName]), usePreviousParameters: this.options.usePreviousParameters, rollback: this.options.rollback, + cloudTrail: this.options.cloudTrail, notificationArns, rollbackConfiguration, extraUserAgent: this.options.extraUserAgent, diff --git a/packages/aws-cdk/lib/cli/cli-config.ts b/packages/aws-cdk/lib/cli/cli-config.ts index ed80c5a48..4c650ec49 100644 --- a/packages/aws-cdk/lib/cli/cli-config.ts +++ b/packages/aws-cdk/lib/cli/cli-config.ts @@ -221,6 +221,10 @@ export async function makeConfig(): Promise { "'true' by default, use --no-logs to turn off. " + "Only in effect if specified alongside the '--watch' option", }, + 'cloudtrail': { + type: 'boolean', + desc: 'Include CloudTrail context when diagnosing deployment failures. Use --no-cloudtrail to skip the lookup', + }, 'concurrency': { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }, 'asset-parallelism': { type: 'boolean', desc: 'Whether to build/publish assets in parallel' }, 'asset-build-concurrency': { type: 'number', desc: 'Maximum number of asset builds to run in parallel', default: 1, requiresArg: true }, diff --git a/packages/aws-cdk/lib/cli/cli-type-registry.json b/packages/aws-cdk/lib/cli/cli-type-registry.json index cd1903738..86301c285 100644 --- a/packages/aws-cdk/lib/cli/cli-type-registry.json +++ b/packages/aws-cdk/lib/cli/cli-type-registry.json @@ -586,6 +586,10 @@ "default": true, "desc": "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off. Only in effect if specified alongside the '--watch' option" }, + "cloudtrail": { + "type": "boolean", + "desc": "Include CloudTrail context when diagnosing deployment failures. Use --no-cloudtrail to skip the lookup" + }, "concurrency": { "type": "number", "desc": "Maximum number of simultaneous deployments (dependency permitting) to execute.", diff --git a/packages/aws-cdk/lib/cli/cli.ts b/packages/aws-cdk/lib/cli/cli.ts index b27a7493c..e893a7f7d 100644 --- a/packages/aws-cdk/lib/cli/cli.ts +++ b/packages/aws-cdk/lib/cli/cli.ts @@ -489,6 +489,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise): any { type: 'boolean', desc: "Show CloudWatch log events from all resources in the selected Stacks in the terminal. 'true' by default, use --no-logs to turn off. Only in effect if specified alongside the '--watch' option", }) + .option('cloudtrail', { + default: undefined, + type: 'boolean', + desc: 'Include CloudTrail context when diagnosing deployment failures. Use --no-cloudtrail to skip the lookup', + }) .option('concurrency', { default: 1, type: 'number', diff --git a/packages/aws-cdk/lib/cli/user-configuration.ts b/packages/aws-cdk/lib/cli/user-configuration.ts index 3041893dc..d4c4de809 100644 --- a/packages/aws-cdk/lib/cli/user-configuration.ts +++ b/packages/aws-cdk/lib/cli/user-configuration.ts @@ -112,6 +112,7 @@ export class Configuration { versionReporting: true, assetMetadata: true, pathMetadata: true, + cloudtrail: true, output: 'cdk.out', }); @@ -354,6 +355,7 @@ export async function commandLineArgumentsToSettings(ioHelper: IoHelper, argv: A bundlingStacks, lookups: argv.lookups, rollback: argv.rollback, + cloudtrail: argv.cloudtrail, notices: argv.notices, assetParallelism: argv['asset-parallelism'], assetBuildConcurrency: argv['asset-build-concurrency'], diff --git a/packages/aws-cdk/lib/cli/user-input.ts b/packages/aws-cdk/lib/cli/user-input.ts index 33368a483..4bbe1ffb2 100644 --- a/packages/aws-cdk/lib/cli/user-input.ts +++ b/packages/aws-cdk/lib/cli/user-input.ts @@ -985,6 +985,13 @@ export interface DeployOptions { */ readonly logs?: boolean; + /** + * Include CloudTrail context when diagnosing deployment failures. Use --no-cloudtrail to skip the lookup + * + * @default - undefined + */ + readonly cloudtrail?: boolean; + /** * Maximum number of simultaneous deployments (dependency permitting) to execute. * diff --git a/packages/aws-cdk/test/cli/cli.test.ts b/packages/aws-cdk/test/cli/cli.test.ts index bcb48effe..a54b79969 100644 --- a/packages/aws-cdk/test/cli/cli.test.ts +++ b/packages/aws-cdk/test/cli/cli.test.ts @@ -70,6 +70,7 @@ jest.mock('../../lib/cli/parse-command-line-arguments', () => ({ ...result, _: ['deploy'], parameters: [], + cloudtrail: args.includes('--no-cloudtrail') ? false : args.includes('--cloudtrail') ? true : undefined, }; } else if (args.includes('publish-assets')) { result = { ...result, _: ['publish-assets'] }; @@ -694,6 +695,23 @@ describe('AI agent progress auto-default', () => { }); }); +describe('deployment CloudTrail preference', () => { + test.each([true, false])('passes the configured CloudTrail preference %s to deployment', async (cloudTrail) => { + const original = Configuration.fromArgsAndFiles; + const deploy = jest.spyOn(cdkToolkitModule.CdkToolkit.prototype, 'deploy').mockResolvedValue(); + // Use the real configuration conversion to preserve explicit false values. + Configuration.fromArgsAndFiles = jest.fn().mockImplementation((helper: any, props: any) => + actualUserConfig.Configuration.fromArgsAndFiles(helper, { ...props, readUserContext: false })); + try { + await exec(['deploy', ...(cloudTrail ? [] : ['--no-cloudtrail'])]); + expect(deploy).toHaveBeenCalledWith(expect.objectContaining({ cloudTrail })); + } finally { + Configuration.fromArgsAndFiles = original; + deploy.mockRestore(); + } + }); +}); + describe('--yes', () => { test('when --yes option is provided, CliIoHost is using autoRespond', async () => { // GIVEN diff --git a/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts b/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts index 5181d7a4d..8d958b649 100644 --- a/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts +++ b/packages/aws-cdk/test/cli/parse-command-line-arguments.test.ts @@ -1,5 +1,16 @@ import { parseCommandLineArguments } from '../../lib/cli/parse-command-line-arguments'; +test.each([ + [[], undefined], + [['--cloudtrail'], true], + [['--no-cloudtrail'], false], + [['--cloudtrail=false'], false], +])('cdk deploy CloudTrail preference %j is %s', async (options, expected) => { + const argv = await parseCommandLineArguments(['deploy', ...options as string[], 'MyStack']); + expect(argv.cloudtrail).toBe(expected); + expect(argv.STACKS).toEqual(['MyStack']); +}); + test('cdk deploy -R sets rollback to false', async () => { const argv = await parseCommandLineArguments(['deploy', '-R']); expect(argv.rollback).toBe(false); diff --git a/packages/aws-cdk/test/cli/user-config.test.ts b/packages/aws-cdk/test/cli/user-config.test.ts index 7f1ca7c5c..6c394cd0c 100644 --- a/packages/aws-cdk/test/cli/user-config.test.ts +++ b/packages/aws-cdk/test/cli/user-config.test.ts @@ -16,6 +16,19 @@ const USER_CONFIG = fs_path.join(os.homedir(), '.cdk.json'); const ioHost = new TestIoHost(); const ioHelper = ioHost.asHelper(); +test.each([ + { configured: undefined, flags: [], expected: true }, + { configured: false, flags: [], expected: false }, + { configured: false, flags: ['--cloudtrail'], expected: true }, + { configured: true, flags: ['--no-cloudtrail'], expected: false }, +])('CloudTrail configuration respects explicit CLI overrides: %j', async ({ configured, flags, expected }) => { + mockedFs.pathExists.mockImplementation(path => path === PROJECT_CONFIG); + mockedFs.readJSON.mockImplementation((async () => ({ cloudtrail: configured })) as any); + const commandLineArguments = await parseCommandLineArguments(['deploy', ...flags]); + const config = await Configuration.fromArgsAndFiles(ioHelper, { commandLineArguments, readUserContext: false }); + expect(config.settings.get(['cloudtrail'])).toBe(expected); +}); + test('correctly parses hotswap overrides', async () => { const GIVEN_CONFIG: Map = new Map([ [PROJECT_CONFIG, { diff --git a/packages/aws-cdk/test/commands/__io_snapshots__/deploy/deploy_parameters_forwarded_to_CloudFormation_CloudTrail_preference_false_is_forwarded_to_deployStack.ndjson b/packages/aws-cdk/test/commands/__io_snapshots__/deploy/deploy_parameters_forwarded_to_CloudFormation_CloudTrail_preference_false_is_forwarded_to_deployStack.ndjson new file mode 100644 index 000000000..d7765b76e --- /dev/null +++ b/packages/aws-cdk/test/commands/__io_snapshots__/deploy/deploy_parameters_forwarded_to_CloudFormation_CloudTrail_preference_false_is_forwarded_to_deployStack.ndjson @@ -0,0 +1,13 @@ +{"seq":0,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I1000","message":"Starting Synthesis ..."} +{"seq":1,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I1001","message":"\n✨ Synthesis time: \n"} +{"seq":2,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✨ Synthesis time: \n"} +{"seq":3,"type":"notify","action":"deploy","level":"debug","code":null,"message":"Checking for previously published assets"} +{"seq":4,"type":"notify","action":"deploy","level":"debug","code":null,"message":"0 total assets, 0 still need to be published"} +{"seq":5,"type":"notify","action":"deploy","level":"info","code":null,"message":"Test-Stack-A-Display-Name: deploying... [1/1]"} +{"seq":6,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I3000","message":"Starting Deploy ..."} +{"seq":7,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✅ Test-Stack-A-Display-Name"} +{"seq":8,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✨ Deployment time: \n"} +{"seq":9,"type":"notify","action":"deploy","level":"info","code":null,"message":"Stack ARN:"} +{"seq":10,"type":"notify","action":"deploy","level":"result","code":null,"message":"arn:aws:cloudformation:bermuda-triangle-1:123456789012:stack/Test-Stack-A/abcd"} +{"seq":11,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I3001","message":"\n✨ Deploy time: \n"} +{"seq":12,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✨ Total time: \n"} diff --git a/packages/aws-cdk/test/commands/__io_snapshots__/deploy/deploy_parameters_forwarded_to_CloudFormation_CloudTrail_preference_true_is_forwarded_to_deployStack.ndjson b/packages/aws-cdk/test/commands/__io_snapshots__/deploy/deploy_parameters_forwarded_to_CloudFormation_CloudTrail_preference_true_is_forwarded_to_deployStack.ndjson new file mode 100644 index 000000000..d7765b76e --- /dev/null +++ b/packages/aws-cdk/test/commands/__io_snapshots__/deploy/deploy_parameters_forwarded_to_CloudFormation_CloudTrail_preference_true_is_forwarded_to_deployStack.ndjson @@ -0,0 +1,13 @@ +{"seq":0,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I1000","message":"Starting Synthesis ..."} +{"seq":1,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I1001","message":"\n✨ Synthesis time: \n"} +{"seq":2,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✨ Synthesis time: \n"} +{"seq":3,"type":"notify","action":"deploy","level":"debug","code":null,"message":"Checking for previously published assets"} +{"seq":4,"type":"notify","action":"deploy","level":"debug","code":null,"message":"0 total assets, 0 still need to be published"} +{"seq":5,"type":"notify","action":"deploy","level":"info","code":null,"message":"Test-Stack-A-Display-Name: deploying... [1/1]"} +{"seq":6,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I3000","message":"Starting Deploy ..."} +{"seq":7,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✅ Test-Stack-A-Display-Name"} +{"seq":8,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✨ Deployment time: \n"} +{"seq":9,"type":"notify","action":"deploy","level":"info","code":null,"message":"Stack ARN:"} +{"seq":10,"type":"notify","action":"deploy","level":"result","code":null,"message":"arn:aws:cloudformation:bermuda-triangle-1:123456789012:stack/Test-Stack-A/abcd"} +{"seq":11,"type":"notify","action":"deploy","level":"trace","code":"CDK_CLI_I3001","message":"\n✨ Deploy time: \n"} +{"seq":12,"type":"notify","action":"deploy","level":"info","code":null,"message":"\n✨ Total time: \n"} diff --git a/packages/aws-cdk/test/commands/deploy.test.ts b/packages/aws-cdk/test/commands/deploy.test.ts index e587604e0..043d18f39 100644 --- a/packages/aws-cdk/test/commands/deploy.test.ts +++ b/packages/aws-cdk/test/commands/deploy.test.ts @@ -408,6 +408,7 @@ describe('deployment method', () => { selector: selectWithUpstream('Test-Stack-A-Display-Name'), deploymentMethod: { method: 'execute-change-set', changeSetName: 'MyChangeSet' }, requireApproval: RequireApproval.NEVER, + cloudTrail: false, }); expect(toolkitDeploySpy).toHaveBeenCalledWith( @@ -415,6 +416,7 @@ describe('deployment method', () => { expect.objectContaining({ deploymentMethod: { method: 'execute-change-set', changeSetName: 'MyChangeSet' }, parameters: undefined, + cloudTrail: false, }), ); expect(cloudFormation.prepareStack).not.toHaveBeenCalled(); @@ -785,6 +787,17 @@ describe('deploy parameters forwarded to CloudFormation', () => { ); }); + test.each([true, false])('CloudTrail preference %s is forwarded to deployStack', async (cloudTrail) => { + await toolkit.deploy({ + selector: selectExact('Test-Stack-A-Display-Name'), + deploymentMethod: { method: 'change-set' }, + requireApproval: RequireApproval.NEVER, + cloudTrail, + }); + expect(cloudFormation.deployStack).toHaveBeenCalledWith(expect.objectContaining({ cloudTrail })); + expect(cloudFormation.prepareStack).toHaveBeenCalledWith(expect.objectContaining({ cloudTrail })); + }); + test('--parameters are forwarded to deployStack', async () => { await toolkit.deploy({ selector: selectExact('Test-Stack-A-Display-Name'),