@go-to-k/cdkd 0.275.4 → 0.275.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as WorkGraph, A as slowCcOperationTimeoutMs, At as warnDeprecatedNoPrefixCliFlag, B as applyRoleArnIfSet, Bt as resolveBucketRegion, C as yellow, Ct as resolveAutoAssetStorage, D as ProviderRegistry, Dt as resolveStateBucketWithDefaultAndSource, E as clearOnUpdateRemoval, Et as resolveStateBucketWithDefault, F as refStateLookupFromResource, Ft as uploadCfnTemplate, G as DagBuilder, H as describeTypeWithThrottleRetry, Ht as getAwsClients, I as WAFv2WebACLProvider, It as expectedOwnerParam, J as S3StateBackend, K as TemplateParser, Kt as CdkdError, L as normalizeAwsTagsToCfn, Lt as AssemblyReader, M as isTerminationProtectionPropagationError, Mt as CFN_TEMPLATE_URL_LIMIT, N as IntrinsicFunctionResolver, Nt as MIGRATE_TMP_PREFIX, O as findActionableSilentDrops, Ot as resolveUseCdkBootstrapAssets, P as cfnRefValueFromPhysicalId, Pt as findLargeInlineResources, Q as stringifyValue, Qt as LocalStartServiceError, R as resolveExplicitPhysicalId, Rt as processStackMessages, S as red, St as resolveApp, T as collectInlinePolicyNamesManagedBySiblings, Tt as resolveSkipPrefix, U as withRetry, Ut as resetAwsClients, V as DiffCalculator, Vt as AwsClients, W as isRetryableTransientError, Wt as setAwsClients, X as shouldRetainResource, Xt as LocalInvokeBuildError$1, Y as rebuildClientForBucketRegion, Yt as DeployCancelledError, Z as AssetPublisher, Zt as LocalMigrateError, _ as formatResourceLine, _t as getDockerImageBySourceHash, a as DeploymentEventsStore, an as ResourceUpdateNotSupportedError, at as BOOTSTRAP_MARKER_PREFIX, b as gray, bt as getDefaultStateBucketName, c as replayFailedOperations, ct as parseBootstrapMarker, d as IMPLICIT_DELETE_DEPENDENCIES, dt as buildDockerImage, en as MissingCdkCliError, et as buildAssetRedirectMap, f as computeImplicitDeleteEdges, fn as normalizeAwsError, ft as formatDockerLoginError, g as renderStatefulReason, gt as AssetManifestLoader, h as isStatefulRecreateTargetSync, ht as runDockerStreaming, i as DeploymentEventsReader, in as ResourceTimeoutError, it as AssetModeResolver, j as disableInstanceApiTermination, jt as CFN_TEMPLATE_BODY_LIMIT, k as CloudControlProvider, kt as stateBucketExistenceConfirmed, l as replayRollback, lt as validateAssetBucketName, m as MULTI_REGION_RECREATE_BLOCKED_TYPES, mt as runDockerForeground, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as PartialFailureError, nt as loadPublishableAssetManifest, o as planFailedOps, on as StackHasActiveImportsError, ot as ensureAssetStorage, p as extractDeploymentEventError, pn as withErrorHandling, pt as getDockerCmd, q as LockManager, r as DeployEngine, rn as ProvisioningError, rt as rewriteTemplateAssetReferences, s as planRollback, sn as StackTerminationProtectionError, st as getBootstrapMarkerKey, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as NestedStackChildDirectDestroyError, tt as createAssetRedirectResolver, u as withResourceDeadline, ut as validateContainerRepoName, v as bold, vt as Synthesizer, w as IAMRoleProvider, wt as resolveCaptureObservedState, x as green, xt as getLegacyStateBucketName, y as cyan, yt as synthesisStatusMessage, z as assertRegionMatch } from "./deploy-engine-CHL0hb1m.js";
2
+ import { $ as TemplateParser, $t as CdkdError, A as yellow, At as resolveAutoAssetStorage, B as IntrinsicFunctionResolver, Bt as MIGRATE_TMP_PREFIX, C as renderStatefulReason, Ct as AssetManifestLoader, D as gray, Dt as getDefaultStateBucketName, E as cyan, Et as synthesisStatusMessage, F as findActionableSilentDrops, Ft as resolveUseCdkBootstrapAssets, G as resolveExplicitPhysicalId, Gt as processStackMessages, H as refStateLookupFromResource, Ht as uploadCfnTemplate, I as CloudControlProvider, It as stateBucketExistenceConfirmed, J as DiffCalculator, Jt as AwsClients, K as assertRegionMatch, L as slowCcOperationTimeoutMs, Lt as warnDeprecatedNoPrefixCliFlag, M as collectInlinePolicyNamesManagedBySiblings, Mt as resolveSkipPrefix, N as clearOnUpdateRemoval, Nt as resolveStateBucketWithDefault, O as green, Ot as getLegacyStateBucketName, P as ProviderRegistry, Pt as resolveStateBucketWithDefaultAndSource, Q as DagBuilder, R as disableInstanceApiTermination, Rt as CFN_TEMPLATE_BODY_LIMIT, S as isStatefulRecreateTargetSync, St as runDockerStreaming, T as bold, Tt as Synthesizer, U as WAFv2WebACLProvider, Ut as expectedOwnerParam, V as cfnRefValueFromPhysicalId, Vt as findLargeInlineResources, W as normalizeAwsTagsToCfn, Wt as AssemblyReader, X as withRetry, Xt as resetAwsClients, Y as describeTypeWithThrottleRetry, Yt as getAwsClients, Z as isRetryableTransientError, Zt as setAwsClients, _ as createEbsFinalSnapshot, _t as validateContainerRepoName, a as DeploymentEventsStore, an as LocalStartServiceError, at as stringifyValue, b as extractDeploymentEventError, bn as withErrorHandling, bt as getDockerCmd, c as replayFailedOperations, cn as NestedStackChildDirectDestroyError, ct as createAssetRedirectResolver, d as IMPLICIT_DELETE_DEPENDENCIES, dn as ResourceTimeoutError, dt as AssetModeResolver, et as LockManager, f as computeImplicitDeleteEdges, fn as ResourceUpdateNotSupportedError, ft as BOOTSTRAP_MARKER_PREFIX, g as ccRoutedFinalSnapshotError, gt as validateAssetBucketName, h as buildFinalSnapshotIdentifier, ht as parseBootstrapMarker, i as DeploymentEventsReader, in as LocalMigrateError, it as AssetPublisher, j as IAMRoleProvider, jt as resolveCaptureObservedState, k as red, kt as resolveApp, l as replayRollback, ln as PartialFailureError, lt as loadPublishableAssetManifest, m as PRE_DELETE_SNAPSHOT_TYPES, mn as StackTerminationProtectionError, mt as getBootstrapMarkerKey, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as DeployCancelledError, nt as rebuildClientForBucketRegion, o as planFailedOps, ot as WorkGraph, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as StackHasActiveImportsError, pt as ensureAssetStorage, q as applyRoleArnIfSet, qt as resolveBucketRegion, r as DeployEngine, rn as LocalInvokeBuildError$1, rt as shouldRetainResource, s as planRollback, sn as MissingCdkCliError, st as buildAssetRedirectMap, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as S3StateBackend, u as withResourceDeadline, un as ProvisioningError, ut as rewriteTemplateAssetReferences, v as isFinalSnapshotError, vt as buildDockerImage, w as formatResourceLine, wt as getDockerImageBySourceHash, x as MULTI_REGION_RECREATE_BLOCKED_TYPES, xt as runDockerForeground, y as unsupportedFinalSnapshotError, yn as normalizeAwsError, yt as formatDockerLoginError, z as isTerminationProtectionPropagationError, zt as CFN_TEMPLATE_URL_LIMIT } from "./deploy-engine-D5j6_ZH6.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
4
- import { t as ASGProvider } from "./asg-provider-BxpwCQGo.js";
4
+ import { t as ASGProvider } from "./asg-provider-zDD6EIRo.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -707,6 +707,19 @@ const destroyOptions = [
707
707
  new Option("--remove-protection", "Bypass deletion protection on protected resources by flipping the per-resource protection flag off in-place before delete. Covers stack-level terminationProtection (CDK property) and resource-level protection on AWS::Logs::LogGroup, AWS::RDS::DBInstance, AWS::RDS::DBCluster, AWS::DocDB::DBCluster, AWS::Neptune::DBCluster, AWS::Neptune::DBInstance, AWS::DynamoDB::Table, AWS::EC2::Instance, AWS::Cognito::UserPool, AWS::AutoScaling::AutoScalingGroup, AWS::ElasticLoadBalancingV2::LoadBalancer, AWS::DSQL::Cluster, AWS::NeptuneGraph::Graph, AWS::SMSVOICE::ProtectConfiguration, AWS::VerifiedPermissions::PolicyStore, AWS::EKS::Cluster, AWS::RDS::GlobalCluster, and AWS::DocDB::GlobalCluster.").default(false),
708
708
  allowUnsupportedTypesOption
709
709
  ];
710
+ /**
711
+ * `--skip-final-snapshot` (issue #1352) — the explicit data-loss opt-out for
712
+ * `DeletionPolicy: Snapshot` resources. By default the destroy paths honor
713
+ * the policy (CloudFormation parity): an atomic final-snapshot delete
714
+ * parameter for RDS DBInstance / DBCluster, Neptune / DocDB clusters and
715
+ * ElastiCache CacheCluster, a pre-delete EBS `CreateSnapshot`+wait for
716
+ * `AWS::EC2::Volume`, and a refusal for Snapshot-tagged types cdkd cannot
717
+ * snapshot yet (issue #1353). Attached to `cdkd deploy` (template-removal
718
+ * deletes), `cdkd destroy`, and `cdkd state destroy` — NOT to the shared
719
+ * `destroyOptions` array, which `cdkd orphan` also consumes (orphan never
720
+ * deletes AWS resources, so the flag would be inert noise there).
721
+ */
722
+ const skipFinalSnapshotOption = new Option("--skip-final-snapshot", "Delete resources whose DeletionPolicy is 'Snapshot' WITHOUT creating the final snapshot the policy promises (DATA LOSS — explicit opt-out of CloudFormation parity). By default cdkd creates the final snapshot (RDS / Neptune / DocDB / ElastiCache CacheCluster / EC2 Volume) and refuses to delete Snapshot-tagged types it cannot snapshot yet.").default(false);
710
723
 
711
724
  //#endregion
712
725
  //#region src/cli/commands/state-file-keys.ts
@@ -25232,10 +25245,10 @@ var RDSProvider = class {
25232
25245
  "EnableIAMDatabaseAuthentication"
25233
25246
  ])]
25234
25247
  ]);
25235
- unhandledByDesign = /* @__PURE__ */ new Map([["AWS::RDS::DBCluster", /* @__PURE__ */ new Map([["DeleteAutomatedBackups", "cdkd hardcodes SkipFinalSnapshot=true on destroy; this CFn lifecycle flag has no equivalent on the runtime path"]])], ["AWS::RDS::DBInstance", /* @__PURE__ */ new Map([
25248
+ unhandledByDesign = /* @__PURE__ */ new Map([["AWS::RDS::DBCluster", /* @__PURE__ */ new Map([["DeleteAutomatedBackups", "delete-time lifecycle flag not threaded through cdkd destroy; final snapshots are governed by DeletionPolicy: Snapshot (issue #1352), which this flag does not control"]])], ["AWS::RDS::DBInstance", /* @__PURE__ */ new Map([
25236
25249
  ["DBSecurityGroups", "EC2-Classic-only feature retired by AWS (2022-08-15); new accounts cannot use this — use VPCSecurityGroups instead"],
25237
25250
  ["ApplyImmediately", "cdkd always applies modifications immediately (rds:ModifyDBInstance.ApplyImmediately=true is hardcoded); users wanting maintenance-window deferral should run aws rds modify-db-instance directly"],
25238
- ["DeleteAutomatedBackups", "cdkd hardcodes SkipFinalSnapshot=true on destroy; this CFn lifecycle flag has no equivalent on the runtime path"]
25251
+ ["DeleteAutomatedBackups", "delete-time lifecycle flag not threaded through cdkd destroy; final snapshots are governed by DeletionPolicy: Snapshot (issue #1352), which this flag does not control"]
25239
25252
  ])]]);
25240
25253
  getClient() {
25241
25254
  if (!this.rdsClient) this.rdsClient = new RDSClient(this.providerRegion ? { region: this.providerRegion } : {});
@@ -25257,11 +25270,11 @@ var RDSProvider = class {
25257
25270
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
25258
25271
  }
25259
25272
  }
25260
- async delete(logicalId, physicalId, resourceType, _properties, context) {
25273
+ async delete(logicalId, physicalId, resourceType, properties, context) {
25261
25274
  switch (resourceType) {
25262
25275
  case "AWS::RDS::DBSubnetGroup": return this.deleteDBSubnetGroup(logicalId, physicalId, resourceType, context);
25263
25276
  case "AWS::RDS::DBCluster": return this.deleteDBCluster(logicalId, physicalId, resourceType, context);
25264
- case "AWS::RDS::DBInstance": return this.deleteDBInstance(logicalId, physicalId, resourceType, context);
25277
+ case "AWS::RDS::DBInstance": return this.deleteDBInstance(logicalId, physicalId, resourceType, properties, context);
25265
25278
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
25266
25279
  }
25267
25280
  }
@@ -25465,10 +25478,15 @@ var RDSProvider = class {
25465
25478
  } catch (disableError) {
25466
25479
  if (!this.isNotFoundError(disableError, "DBClusterNotFoundFault")) this.logger.debug(`Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`);
25467
25480
  }
25481
+ const finalSnapshotId = context?.finalSnapshotIdentifier;
25468
25482
  await this.getClient().send(new DeleteDBClusterCommand({
25469
25483
  DBClusterIdentifier: physicalId,
25470
- SkipFinalSnapshot: true
25484
+ ...finalSnapshotId ? {
25485
+ SkipFinalSnapshot: false,
25486
+ FinalDBSnapshotIdentifier: finalSnapshotId
25487
+ } : { SkipFinalSnapshot: true }
25471
25488
  }));
25489
+ if (finalSnapshotId) this.logger.info(`Deleting DBCluster ${logicalId} with final snapshot ${finalSnapshotId} (DeletionPolicy: Snapshot)`);
25472
25490
  this.logger.debug(`Successfully initiated deletion of DBCluster ${logicalId}`);
25473
25491
  await this.waitForClusterDeleted(physicalId);
25474
25492
  } catch (error) {
@@ -25574,7 +25592,7 @@ var RDSProvider = class {
25574
25592
  throw new ProvisioningError(`Failed to update DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
25575
25593
  }
25576
25594
  }
25577
- async deleteDBInstance(logicalId, physicalId, resourceType, context) {
25595
+ async deleteDBInstance(logicalId, physicalId, resourceType, properties, context) {
25578
25596
  this.logger.debug(`Deleting DBInstance ${logicalId}: ${physicalId}`);
25579
25597
  try {
25580
25598
  if (context?.removeProtection === true) try {
@@ -25587,10 +25605,15 @@ var RDSProvider = class {
25587
25605
  } catch (disableError) {
25588
25606
  if (!this.isNotFoundError(disableError, "DBInstanceNotFoundFault")) this.logger.debug(`Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`);
25589
25607
  }
25608
+ const finalSnapshotId = properties?.["DBClusterIdentifier"] === void 0 ? context?.finalSnapshotIdentifier : void 0;
25590
25609
  await this.getClient().send(new DeleteDBInstanceCommand({
25591
25610
  DBInstanceIdentifier: physicalId,
25592
- SkipFinalSnapshot: true
25611
+ ...finalSnapshotId ? {
25612
+ SkipFinalSnapshot: false,
25613
+ FinalDBSnapshotIdentifier: finalSnapshotId
25614
+ } : { SkipFinalSnapshot: true }
25593
25615
  }));
25616
+ if (finalSnapshotId) this.logger.info(`Deleting DBInstance ${logicalId} with final snapshot ${finalSnapshotId} (DeletionPolicy: Snapshot)`);
25594
25617
  this.logger.debug(`Successfully initiated deletion of DBInstance ${logicalId}`);
25595
25618
  await this.waitForInstanceDeleted(physicalId);
25596
25619
  } catch (error) {
@@ -27106,10 +27129,15 @@ var DocDBProvider = class {
27106
27129
  } catch (disableError) {
27107
27130
  if (!this.isNotFoundError(disableError, "DBClusterNotFoundFault")) this.logger.debug(`Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`);
27108
27131
  }
27132
+ const finalSnapshotId = context?.finalSnapshotIdentifier;
27109
27133
  await this.getClient().send(new DeleteDBClusterCommand$1({
27110
27134
  DBClusterIdentifier: physicalId,
27111
- SkipFinalSnapshot: true
27135
+ ...finalSnapshotId ? {
27136
+ SkipFinalSnapshot: false,
27137
+ FinalDBSnapshotIdentifier: finalSnapshotId
27138
+ } : { SkipFinalSnapshot: true }
27112
27139
  }));
27140
+ if (finalSnapshotId) this.logger.info(`Deleting DocDB DBCluster ${logicalId} with final snapshot ${finalSnapshotId} (DeletionPolicy: Snapshot)`);
27113
27141
  this.logger.debug(`Successfully initiated deletion of DocDB DBCluster ${logicalId}`);
27114
27142
  await this.waitForClusterDeleted(physicalId);
27115
27143
  } catch (error) {
@@ -27731,10 +27759,15 @@ var NeptuneProvider = class {
27731
27759
  } catch (disableError) {
27732
27760
  if (!this.isNotFoundError(disableError, "DBClusterNotFoundFault")) this.logger.debug(`Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`);
27733
27761
  }
27762
+ const finalSnapshotId = context?.finalSnapshotIdentifier;
27734
27763
  await this.getClient().send(new DeleteDBClusterCommand$2({
27735
27764
  DBClusterIdentifier: physicalId,
27736
- SkipFinalSnapshot: true
27765
+ ...finalSnapshotId ? {
27766
+ SkipFinalSnapshot: false,
27767
+ FinalDBSnapshotIdentifier: finalSnapshotId
27768
+ } : { SkipFinalSnapshot: true }
27737
27769
  }));
27770
+ if (finalSnapshotId) this.logger.info(`Deleting Neptune DBCluster ${logicalId} with final snapshot ${finalSnapshotId} (DeletionPolicy: Snapshot)`);
27738
27771
  this.logger.debug(`Successfully initiated deletion of Neptune DBCluster ${logicalId}`);
27739
27772
  await this.waitForClusterDeleted(physicalId);
27740
27773
  } catch (error) {
@@ -29646,7 +29679,12 @@ var ElastiCacheProvider = class {
29646
29679
  async deleteCacheCluster(logicalId, physicalId, resourceType, context) {
29647
29680
  this.logger.debug(`Deleting CacheCluster ${logicalId}: ${physicalId}`);
29648
29681
  try {
29649
- await this.getClient().send(new DeleteCacheClusterCommand({ CacheClusterId: physicalId }));
29682
+ const finalSnapshotId = context?.finalSnapshotIdentifier;
29683
+ await this.getClient().send(new DeleteCacheClusterCommand({
29684
+ CacheClusterId: physicalId,
29685
+ ...finalSnapshotId ? { FinalSnapshotIdentifier: finalSnapshotId } : {}
29686
+ }));
29687
+ if (finalSnapshotId) this.logger.info(`Deleting CacheCluster ${logicalId} with final snapshot ${finalSnapshotId} (DeletionPolicy: Snapshot)`);
29650
29688
  this.logger.debug(`Successfully initiated deletion of CacheCluster ${logicalId}`);
29651
29689
  await this.waitForClusterDeleted(physicalId);
29652
29690
  } catch (error) {
@@ -42623,16 +42661,50 @@ async function runDestroyForStack(stackName, state, ctx) {
42623
42661
  destroyProviderRegistry.setCustomResourceResponseBucket(ctx.stateBucket);
42624
42662
  if (ctx.allowUnsupportedTypes?.length) destroyProviderRegistry.allowUnsupportedTypes(ctx.allowUnsupportedTypes);
42625
42663
  }
42664
+ const renderer = getLiveRenderer();
42665
+ let draining = false;
42666
+ let lockHeld = false;
42667
+ const sigintHandler = () => {
42668
+ if (draining) {
42669
+ if (lockHeld) {
42670
+ ctx.lockManager.releaseLock(stackName, regionForState).catch(() => {});
42671
+ process.stderr.write(`\nForce-quit: stack lock may not be released. If the next run reports a lock, run: cdkd force-unlock ${stackName}\n`);
42672
+ }
42673
+ process.exit(130);
42674
+ }
42675
+ draining = true;
42676
+ renderer.printAbove(() => {
42677
+ process.stderr.write("\nInterrupted — finishing in-flight deletes, then flushing state and releasing the lock (press Ctrl-C again to force-quit)...\n");
42678
+ });
42679
+ };
42680
+ process.setMaxListeners(Math.max(process.getMaxListeners(), 100));
42681
+ process.on("SIGINT", sigintHandler);
42626
42682
  logger.info(`\nAcquiring lock for stack ${stackName}...`);
42627
- await ctx.lockManager.acquireLock(stackName, regionForState, void 0, "destroy");
42683
+ try {
42684
+ await ctx.lockManager.acquireLock(stackName, regionForState, void 0, "destroy");
42685
+ } catch (error) {
42686
+ process.removeListener("SIGINT", sigintHandler);
42687
+ throw error;
42688
+ }
42689
+ lockHeld = true;
42628
42690
  if (needsStrongRefCheck) {
42629
- const consumers = await scanActiveConsumers(stackName, regionForState, ctx);
42630
- if (consumers.length > 0) {
42691
+ const releaseThenUnregister = async () => {
42631
42692
  try {
42632
42693
  await ctx.lockManager.releaseLock(stackName, regionForState);
42633
42694
  } catch (releaseErr) {
42634
- logger.warn(`Failed to release lock after strong-ref refusal: ${releaseErr instanceof Error ? releaseErr.message : String(releaseErr)}`);
42695
+ logger.warn(`Failed to release lock after strong-ref refusal/failure: ${releaseErr instanceof Error ? releaseErr.message : String(releaseErr)}`);
42635
42696
  }
42697
+ process.removeListener("SIGINT", sigintHandler);
42698
+ };
42699
+ let consumers;
42700
+ try {
42701
+ consumers = await scanActiveConsumers(stackName, regionForState, ctx);
42702
+ } catch (error) {
42703
+ await releaseThenUnregister();
42704
+ throw error;
42705
+ }
42706
+ if (consumers.length > 0) {
42707
+ await releaseThenUnregister();
42636
42708
  throw new StackHasActiveImportsError(stackName, regionForState, consumers);
42637
42709
  }
42638
42710
  }
@@ -42657,22 +42729,7 @@ async function runDestroyForStack(stackName, state, ctx) {
42657
42729
  }
42658
42730
  });
42659
42731
  };
42660
- const renderer = getLiveRenderer();
42661
42732
  renderer.start();
42662
- let draining = false;
42663
- const sigintHandler = () => {
42664
- if (draining) {
42665
- ctx.lockManager.releaseLock(stackName, regionForState).catch(() => {});
42666
- process.stderr.write(`\nForce-quit: stack lock may not be released. If the next run reports a lock, run: cdkd force-unlock ${stackName}\n`);
42667
- process.exit(130);
42668
- }
42669
- draining = true;
42670
- renderer.printAbove(() => {
42671
- process.stderr.write("\nInterrupted — finishing in-flight deletes, then flushing state and releasing the lock (press Ctrl-C again to force-quit)...\n");
42672
- });
42673
- };
42674
- process.setMaxListeners(Math.max(process.getMaxListeners(), 100));
42675
- process.on("SIGINT", sigintHandler);
42676
42733
  try {
42677
42734
  logger.info("Building dependency graph...");
42678
42735
  const template = {
@@ -42765,6 +42822,11 @@ async function runDestroyForStack(stackName, state, ctx) {
42765
42822
  ...resource.provisionedBy && { provisionedBy: resource.provisionedBy }
42766
42823
  });
42767
42824
  try {
42825
+ let finalSnapshotIdentifier;
42826
+ if (resource.deletionPolicy === "Snapshot" && ctx.skipFinalSnapshot !== true) if (ATOMIC_FINAL_SNAPSHOT_TYPES.has(resource.resourceType) && resource.provisionedBy !== "cc-api") finalSnapshotIdentifier = buildFinalSnapshotIdentifier(resource.physicalId, resource.resourceType);
42827
+ else if (ATOMIC_FINAL_SNAPSHOT_TYPES.has(resource.resourceType)) throw ccRoutedFinalSnapshotError(logicalId, resource.resourceType, "--skip-final-snapshot");
42828
+ else if (PRE_DELETE_SNAPSHOT_TYPES.has(resource.resourceType)) await createEbsFinalSnapshot((destroyAwsClients ?? ctx.baseAwsClients).ec2, resource.physicalId, logicalId, logger);
42829
+ else throw unsupportedFinalSnapshotError(logicalId, resource.resourceType, "--skip-final-snapshot");
42768
42830
  const provider = destroyProviderRegistry.getProviderFor({
42769
42831
  resourceType: resource.resourceType,
42770
42832
  provisionedBy: resource.provisionedBy
@@ -42780,7 +42842,8 @@ async function runDestroyForStack(stackName, state, ctx) {
42780
42842
  for (let attempt = 0; attempt <= maxAttempts; attempt++) try {
42781
42843
  await provider.delete(logicalId, resource.physicalId, resource.resourceType, resource.properties, {
42782
42844
  ...state.region !== void 0 && { expectedRegion: state.region },
42783
- ...ctx.removeProtection === true && { removeProtection: true }
42845
+ ...ctx.removeProtection === true && { removeProtection: true },
42846
+ ...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier }
42784
42847
  });
42785
42848
  lastDeleteError = null;
42786
42849
  break;
@@ -42823,7 +42886,7 @@ async function runDestroyForStack(stackName, state, ctx) {
42823
42886
  } catch (error) {
42824
42887
  renderer.removeTask(logicalId);
42825
42888
  const msg = error instanceof Error ? error.message : String(error);
42826
- if (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException")) {
42889
+ if (!isFinalSnapshotError(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
42827
42890
  logger.debug(` ${logicalId} already deleted, removing from state`);
42828
42891
  result.deletedCount++;
42829
42892
  ctx.eventRecorder?.record({
@@ -43126,6 +43189,7 @@ var NestedStackProvider = class {
43126
43189
  ...ctx.exportIndexStore && { exportIndexStore: ctx.exportIndexStore },
43127
43190
  ...ctx.destroyOptions?.profile && { profile: ctx.destroyOptions.profile },
43128
43191
  ...deleteContext?.removeProtection === true && { removeProtection: true },
43192
+ ...(ctx.destroyOptions?.skipFinalSnapshot === true || ctx.options?.skipFinalSnapshot === true) && { skipFinalSnapshot: true },
43129
43193
  ...ctx.destroyOptions?.resourceWarnAfterMs !== void 0 && { resourceWarnAfterMs: ctx.destroyOptions.resourceWarnAfterMs },
43130
43194
  ...ctx.destroyOptions?.resourceTimeoutMs !== void 0 && { resourceTimeoutMs: ctx.destroyOptions.resourceTimeoutMs },
43131
43195
  ...ctx.destroyOptions?.resourceWarnAfterByType && { resourceWarnAfterByType: ctx.destroyOptions.resourceWarnAfterByType },
@@ -44239,6 +44303,8 @@ async function deployCommand(stacks, options) {
44239
44303
  ...recreateViaSdkProviderTargets && recreateViaSdkProviderTargets.size > 0 && { recreateViaSdkProviderTargets },
44240
44304
  ...options.replace && { replace: true },
44241
44305
  ...options.forceStatefulRecreation && { forceStatefulRecreation: true },
44306
+ ...options.skipFinalSnapshot && { skipFinalSnapshot: true },
44307
+ finalSnapshotEc2: stackAwsClients.ec2,
44242
44308
  ...options.strictGetatt && { strictGetAtt: true },
44243
44309
  captureObservedState: resolveCaptureObservedState(options.captureObservedState),
44244
44310
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
@@ -44329,6 +44395,7 @@ function createDeployCommand() {
44329
44395
  ...stateOptions,
44330
44396
  ...stackOptions,
44331
44397
  ...deployOptions,
44398
+ skipFinalSnapshotOption,
44332
44399
  ...contextOptions,
44333
44400
  ...annotationMessageOptions
44334
44401
  ].forEach((opt) => cmd.addOption(opt));
@@ -47114,6 +47181,7 @@ async function destroyCommand(stackArgs, options) {
47114
47181
  destroyOptions: {
47115
47182
  ...options.profile && { profile: options.profile },
47116
47183
  ...options.removeProtection === true && { removeProtection: true },
47184
+ ...options.skipFinalSnapshot === true && { skipFinalSnapshot: true },
47117
47185
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
47118
47186
  ...options.resourceTimeout?.globalMs !== void 0 && { resourceTimeoutMs: options.resourceTimeout.globalMs },
47119
47187
  ...options.resourceWarnAfter?.perTypeMs && { resourceWarnAfterByType: options.resourceWarnAfter.perTypeMs },
@@ -47129,6 +47197,7 @@ async function destroyCommand(stackArgs, options) {
47129
47197
  stateBucket,
47130
47198
  skipConfirmation: options.yes || options.force,
47131
47199
  removeProtection: options.removeProtection === true,
47200
+ skipFinalSnapshot: options.skipFinalSnapshot === true,
47132
47201
  exportIndexStore,
47133
47202
  ...options.allowUnsupportedTypes?.length && { allowUnsupportedTypes: options.allowUnsupportedTypes },
47134
47203
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
@@ -47184,6 +47253,7 @@ function createDestroyCommand() {
47184
47253
  ...stackOptions,
47185
47254
  ...destroyOptions,
47186
47255
  ...resourceTimeoutOptions,
47256
+ skipFinalSnapshotOption,
47187
47257
  ...contextOptions
47188
47258
  ].forEach((opt) => cmd.addOption(opt));
47189
47259
  cmd.addOption(deprecatedRegionOption);
@@ -50596,6 +50666,7 @@ async function stateDestroyCommand(stackArgs, options) {
50596
50666
  destroyOptions: {
50597
50667
  ...options.profile && { profile: options.profile },
50598
50668
  ...options.removeProtection === true && { removeProtection: true },
50669
+ ...options.skipFinalSnapshot === true && { skipFinalSnapshot: true },
50599
50670
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
50600
50671
  ...options.resourceTimeout?.globalMs !== void 0 && { resourceTimeoutMs: options.resourceTimeout.globalMs },
50601
50672
  ...options.resourceWarnAfter?.perTypeMs && { resourceWarnAfterByType: options.resourceWarnAfter.perTypeMs },
@@ -50611,6 +50682,7 @@ async function stateDestroyCommand(stackArgs, options) {
50611
50682
  stateBucket: setup.bucket,
50612
50683
  skipConfirmation: options.yes || options.all === true,
50613
50684
  removeProtection: options.removeProtection === true,
50685
+ skipFinalSnapshot: options.skipFinalSnapshot === true,
50614
50686
  exportIndexStore: setup.exportIndexStore,
50615
50687
  ...options.allowUnsupportedTypes?.length && { allowUnsupportedTypes: options.allowUnsupportedTypes },
50616
50688
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
@@ -50650,7 +50722,8 @@ function createStateDestroyCommand() {
50650
50722
  ...commonOptions,
50651
50723
  ...stateOptions,
50652
50724
  ...resourceTimeoutOptions,
50653
- allowUnsupportedTypesOption
50725
+ allowUnsupportedTypesOption,
50726
+ skipFinalSnapshotOption
50654
50727
  ].forEach((opt) => cmd.addOption(opt));
50655
50728
  cmd.addOption(deprecatedRegionOption);
50656
50729
  return cmd;
@@ -51238,7 +51311,6 @@ async function rollbackCommand(stackArg, options) {
51238
51311
  const providerRegistry = new ProviderRegistry();
51239
51312
  registerAllProviders(providerRegistry);
51240
51313
  providerRegistry.setCustomResourceResponseBucket(setup.bucket);
51241
- await setup.lockManager.acquireLockWithRetry(stackName, region, void 0, "rollback");
51242
51314
  let interrupted = false;
51243
51315
  const sigintHandler = () => {
51244
51316
  process.stderr.write("\nInterrupted — stopping rollback after the current operation...\n");
@@ -51246,6 +51318,13 @@ async function rollbackCommand(stackArg, options) {
51246
51318
  };
51247
51319
  process.on("SIGINT", sigintHandler);
51248
51320
  const unforwardSigterm = forwardSigtermToSigint();
51321
+ try {
51322
+ await setup.lockManager.acquireLockWithRetry(stackName, region, void 0, "rollback");
51323
+ } catch (error) {
51324
+ unforwardSigterm();
51325
+ process.removeListener("SIGINT", sigintHandler);
51326
+ throw error;
51327
+ }
51249
51328
  try {
51250
51329
  const stateData = await setup.stateBackend.getState(stackName, region);
51251
51330
  const journal = await setup.stateBackend.loadRollbackJournal(stackName, region);
@@ -63624,7 +63703,7 @@ function createMigrateCommand() {
63624
63703
  */
63625
63704
  function buildProgram() {
63626
63705
  const program = new Command();
63627
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.275.4");
63706
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.275.6");
63628
63707
  program.hook("preAction", (_thisCommand, actionCommand) => {
63629
63708
  const { profile } = actionCommand.optsWithGlobals();
63630
63709
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;