@go-to-k/cdkd 0.295.1 → 0.296.0

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.
Files changed (33) hide show
  1. package/dist/{asg-provider-CbwOK2TQ.js → asg-provider-AVCcoXnQ.js} +8 -6
  2. package/dist/asg-provider-AVCcoXnQ.js.map +1 -0
  3. package/dist/cli.js +2 -2
  4. package/dist/{deploy-engine-DG-oC9YK.js → deploy-engine-D7204v7Z.js} +775 -216
  5. package/dist/deploy-engine-D7204v7Z.js.map +1 -0
  6. package/dist/{iam-policy-targets-CMSQGQnm.js → iam-policy-targets-DSDSdxXz.js} +145 -4
  7. package/dist/iam-policy-targets-DSDSdxXz.js.map +1 -0
  8. package/dist/{iam-role-provider-DUZtt9lH.js → iam-role-provider-CCPaZ3U4.js} +11 -5
  9. package/dist/iam-role-provider-CCPaZ3U4.js.map +1 -0
  10. package/dist/index.d.ts +28 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -4
  13. package/dist/{logger-B46YMG0h.js → logger-Dvc7ddKe.js} +19 -3
  14. package/dist/{logger-B46YMG0h.js.map → logger-Dvc7ddKe.js.map} +1 -1
  15. package/dist/{neptune-provider-TnGNCeLX.js → neptune-provider-D1t9NgT-.js} +20 -14
  16. package/dist/neptune-provider-D1t9NgT-.js.map +1 -0
  17. package/dist/{program-DfJFegS1.js → program-B0sp_SHG.js} +6187 -5931
  18. package/dist/program-B0sp_SHG.js.map +1 -0
  19. package/dist/{provider-classes-DUw5QAWI.js → provider-classes-DMOnX5s-.js} +754 -208
  20. package/dist/provider-classes-DMOnX5s-.js.map +1 -0
  21. package/dist/{rds-provider-COCDd7bS.js → rds-provider-DltlcDiQ.js} +22 -15
  22. package/dist/rds-provider-DltlcDiQ.js.map +1 -0
  23. package/dist/{version-Bqzsng8Y.js → version-DUHDmEhz.js} +2 -2
  24. package/dist/{version-Bqzsng8Y.js.map → version-DUHDmEhz.js.map} +1 -1
  25. package/package.json +1 -1
  26. package/dist/asg-provider-CbwOK2TQ.js.map +0 -1
  27. package/dist/deploy-engine-DG-oC9YK.js.map +0 -1
  28. package/dist/iam-policy-targets-CMSQGQnm.js.map +0 -1
  29. package/dist/iam-role-provider-DUZtt9lH.js.map +0 -1
  30. package/dist/neptune-provider-TnGNCeLX.js.map +0 -1
  31. package/dist/program-DfJFegS1.js.map +0 -1
  32. package/dist/provider-classes-DUw5QAWI.js.map +0 -1
  33. package/dist/rds-provider-COCDd7bS.js.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import { r as getAwsClients } from "./aws-clients-CnmtbrxD.js";
2
- import { A as displaySafe, F as safeMsg, M as isPasteableIdent, P as plainIdentOr, g as generateResourceNameWithFallback, h as generateResourceName, j as displayStackName, k as displayIdent, n as getLogger, v as getCurrentStackName, w as SECRET_REF_MAX_CODE_POINTS } from "./logger-B46YMG0h.js";
3
- import { $ as compositeIdFormatMessage, A as isTerminalDeleteFailure, B as WITHHELD_AWS_COMMAND, C as refusalTypeShown, Ct as injectiveKeyPrefix, D as ProtectionFlipRegistry, Dn as markNameCollision, Dt as withPasteableAwsProfile, H as pasteableAwsCommand, Hn as markNonRetryable, In as describeAwsFailure, J as isInterruptedWaitError, Jt as ProvisioningError, L as userPoolProtectionSite, Ln as isAwsAuthoredFailure, M as observeThenDisableProtection, Mt as AccountIdUnavailableError, N as protectionFlipKey, Nn as isRetryableTransientError, On as markReplayMayCollide, Ot as ambientClientDefaults, P as rdsFamilyProtectionSite, Pn as isTransientServerError, Pt as CdkdError, Q as canonicalizeRoute53QueryName, Rn as safeStringify, S as quotedRemedyLogicalId, St as injectiveKey, T as resolveExplicitPhysicalId, U as protectedReplacementAdvice, Un as markRedactedCause, V as isAwsCliLiteral, Vn as isThrottlingError, W as renderDisableCommand, Wn as wrapMaskedAwsError, X as ElapsedBudgetRegistry, Xt as ResourceUpdateNotSupportedError, Y as startInterruptWatch, _ as VALUE_NOT_SHOWN, _n as shellQuote, a as onlySecretDerived, at as segmentAfterAnchor, b as isQuotableImportValue, c as recordedPrincipalsRepair, ct as isSecretDerivedValue, dn as isInertUnquoted, dt as withDerivedNameMasks, et as compositeIdSeparatorRefusal, fn as pasteableCommand, g as assertRegionMatch, h as RESOURCE_NOT_FOUND, i as holdsSecretDerivedEntry, j as logGroupProtectionSite, jn as isAmbiguousOutcomeError, jt as credentialFingerprint, k as deleteWithProtectionCompensation, kt as ambientCredentialConfig, ln as commandHole, lt as maskDeep, mn as plainOrDescribed, nt as logicalIdShown, o as readPrincipalLists, ot as createMaskedLogSinks, p as withRemovalDefaults, r as SECRET_DERIVED_READ_LIVE, rt as packCompositeId, st as createMaskedRetryLogger, tt as compositeIdSkipResult, u as clearOnUpdateRemoval, un as hasClauseBreak, ut as maskerOrIdentity, v as isPlainImportJson, vn as withheldTargetClause, w as remedyLogicalId, x as normalizeAwsTagsToCfn, y as isPlainImportValue, z as UNNAMEABLE_ID_CLAUSE, zn as THROTTLING_ERROR_NAMES } from "./iam-policy-targets-CMSQGQnm.js";
2
+ import { A as displaySafe, F as safeMsg, M as isPasteableIdent, P as plainIdentOr, g as generateResourceNameWithFallback, h as generateResourceName, j as displayStackName, k as displayIdent, n as getLogger, v as getCurrentStackName, w as SECRET_REF_MAX_CODE_POINTS } from "./logger-Dvc7ddKe.js";
3
+ import { $ as compositeIdFormatMessage, $n as markRedactedCause, A as isTerminalDeleteFailure, B as WITHHELD_AWS_COMMAND, C as refusalTypeShown, D as ProtectionFlipRegistry, Dn as shellQuote, H as pasteableAwsCommand, Ht as AccountIdUnavailableError, J as isInterruptedWaitError, Jn as safeStringify, Kn as describeAwsFailure, L as userPoolProtectionSite, Ln as markNameCollision, Lt as withPasteableAwsProfile, M as observeThenDisableProtection, Mt as injectiveKey, N as protectionFlipKey, Nt as injectiveKeyPrefix, On as withheldTargetClause, P as rdsFamilyProtectionSite, Q as canonicalizeRoute53QueryName, Qn as markNonRetryable, Rn as markReplayMayCollide, Rt as ambientClientDefaults, S as quotedRemedyLogicalId, Sn as pasteableCommand, T as resolveExplicitPhysicalId, U as protectedReplacementAdvice, Un as isRetryableTransientError, V as isAwsCliLiteral, Vn as isAmbiguousOutcomeError, Vt as credentialFingerprint, W as renderDisableCommand, Wn as isTransientServerError, Wt as CdkdError, X as ElapsedBudgetRegistry, Y as startInterruptWatch, Yn as THROTTLING_ERROR_NAMES, Zn as isThrottlingError, _ as VALUE_NOT_SHOWN, a as onlySecretDerived, at as segmentAfterAnchor, b as isQuotableImportValue, bn as hasClauseBreak, bt as markAuxiliaryFailure, c as recordedPrincipalsRepair, ct as createMaskedRetryLogger, dt as maskerOrIdentity, er as wrapMaskedAwsError, et as compositeIdSeparatorRefusal, ft as withDerivedNameMasks, g as assertRegionMatch, h as RESOURCE_NOT_FOUND, ht as clearCreatedBeforeFailure, i as holdsSecretDerivedEntry, in as ProvisioningError, j as logGroupProtectionSite, k as deleteWithProtectionCompensation, lt as isSecretDerivedValue, nt as logicalIdShown, o as readPrincipalLists, on as ResourceUpdateNotSupportedError, ot as stateOrphanRecordRemedy, p as withRemovalDefaults, qn as isAwsAuthoredFailure, r as SECRET_DERIVED_READ_LIVE, rt as packCompositeId, st as createMaskedLogSinks, tt as compositeIdSkipResult, u as clearOnUpdateRemoval, ut as maskDeep, v as isPlainImportJson, w as remedyLogicalId, wn as plainOrDescribed, x as normalizeAwsTagsToCfn, xn as isInertUnquoted, xt as markCreatedBeforeFailure, y as isPlainImportValue, yn as commandHole, z as UNNAMEABLE_ID_CLAUSE, zt as ambientCredentialConfig } from "./iam-policy-targets-DSDSdxXz.js";
4
4
  import { s as ambientRegion } from "./aws-client-defaults-D0q0z4dG.js";
5
- import { $i as WARM_THROUGHPUT_MEMBERS, A as resolveDynamoDbDeleteBudgetClock, Aa as hasMaskableValues, An as slowCcOperationTimeoutMs, B as getPriorAttempts, Bn as ec2InstanceProtectionSite, Bt as redactedDeleteAddressSkip, D as beginDeleteWait, E as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, Er as parseWebACLArn, Et as getCurrentProducerRegions, F as waitForIndexesSettled, Fn as deleteSkipReason, Fs as canonicalizeRegion, Ha as redactSecretsForState, Hi as findSilentDropProperties, Hn as observeThenDisableInstanceApiTermination, I as ECR_AUTO_DELETE_IMAGES_TAG, In as withIndeterminateGuard, Is as derivePartitionAndUrlSuffix, Js as renderAssemblyPathEscape, Ka as dynamicReferenceTokens, Ks as describeFileReadFailure, L as S3_AUTO_DELETE_OBJECTS_TAG, Ln as isWaitAbandonedError, Lt as REDACTED_DELETE_ADDRESS_SKIP_REASON, M as DELETE_INDEX_WAIT_PROCEED_NOTE, Mn as unchangedBehindSecretReference, Ms as decodeListingKey, N as INDEX_SETTLE_POLL_INTERVAL_MS, No as rewriteTemplateAssetReferences, O as compensateRemovedDeletionProtection, On as getCurrentResourceSecrets, Oo as stringifyValue, P as deleteTableWithIndexBusyRetry, R as hasCdkAutoDeleteTag, Rn as markWaitAbandoned, Rt as isRedactedRecordedValue, Sa as sameLambdaFunctionAddress, Sn as stringifyIfAssigned, T as DynamoDBGlobalTableProvider, Un as reDisableInstanceApiTermination, V as markRefusedBeforeApplying, Va as markSameGenerationBag, Vn as isTerminationProtectionPropagationError, Wa as recoverMaskedOutput, Wi as withoutSilentDropProperties, Wn as reEnableInstanceApiTermination, Xa as carriesSecretMask, Ys as resolveAssemblyPath, _a as withRetry, _n as readTagList, a as findNestedTemplateTreeDefect, aa as coerceCfnInteger, ba as canonicalLambdaFunctionName, bn as tagPlanWarning, c as renderNestedTemplateTreeDefect, ca as configStringRefusal, cr as s3BucketArn, da as requireConfigArray, dn as reserveStackCreateToken, dr as s3BucketRegionalDomainName, ea as coerceWarmThroughput$1, fa as requireConfigObject, fr as s3BucketWebsiteUrl, ft as getCurrentNestedStackContext, gn as planTagDiff, gr as getAccountInfo, gt as awaitInFlightSibling, hn as DBPROXY_TAG_OPTIONS, ia as coerceCfnBoolean, j as resolveDynamoDbDeleteBudgetMs, k as deleteBudgetKey, ki as refuseMalformedResourcesForDestroy, kr as passedParameterClassesOf, la as readConfigString, li as refuseMalformedNestedChildOutputs, ln as earliestDefined, lr as s3BucketDomainName, lt as revertNestedChildFromJournal, mn as DBPROXY_TAGS_WHAT, na as toCfnInteger, o as isAbsoluteAssetPath, oa as configBooleanRefusal, ot as getNestedRevertRun, pa as requireConfigString, pn as acquireIdempotencyToken, pt as withNestedStackContext, qs as displayAssemblyPath, ra as toFiniteNumber, s as listNestedTemplateRows, sa as configIntegerRefusal, sr as isSettledInstanceState, t as DeployEngine, ta as isWarmThroughputDecrease, ua as replayWarn, ur as s3BucketDualStackDomainName, va as markAuxiliaryFailure, vn as refuseMalformedDesiredTags, vt as getStackRecords, xa as lambdaFunctionNameForMask, xn as definedAttributes, ya as markCreatedBeforeFailure, yn as tagMapAsList, z as isTruthyCfnBoolean, zt as redactedDeleteAddressFields } from "./deploy-engine-DG-oC9YK.js";
6
- import { n as collectInlinePolicyNamesManagedBySiblings, t as IAMRoleProvider } from "./iam-role-provider-DUZtt9lH.js";
7
- import { a as canonicalizeIpProtocolValue, i as resolveSecretDerivedPrincipals, n as runDestroyForStack, o as resolvedResourceTimeoutMs, r as isResolvableSecretPrincipalList, t as canonicalizeIpv4Cidr } from "./program-DfJFegS1.js";
8
- import { RDSProvider } from "./rds-provider-COCDd7bS.js";
9
- import { NeptuneProvider } from "./neptune-provider-TnGNCeLX.js";
10
- import { ASGProvider } from "./asg-provider-CbwOK2TQ.js";
5
+ import { $a as dynamicReferenceTokens, $i as withoutSilentDropProperties, $n as reEnableInstanceApiTermination, A as resolveDynamoDbDeleteBudgetClock, An as definedAttributes, B as getPriorAttempts, Bo as rewriteTemplateAssetReferences, Bs as decodeListingKey, Cn as DBPROXY_TAGS_WHAT, Ct as getStackRecords, D as beginDeleteWait, Da as canonicalLambdaFunctionName, Dn as refuseMalformedDesiredTags, E as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, Ea as withRetry, En as readTagList, F as waitForIndexesSettled, Fo as stringifyValue, Fr as parseWebACLArn, Gn as withIndeterminateGuard, Gt as redactedDeleteAddressFields, I as ECR_AUTO_DELETE_IMAGES_TAG, Ja as markSameGenerationBag, Kn as isWaitAbandonedError, Kt as redactedDeleteAddressSkip, L as S3_AUTO_DELETE_OBJECTS_TAG, La as hasMaskableValues, Ln as getCurrentResourceSecrets, M as DELETE_INDEX_WAIT_PROCEED_NOTE, Mt as getCurrentProducerRegions, N as INDEX_SETTLE_POLL_INTERVAL_MS, O as compensateRemovedDeletionProtection, Oa as lambdaFunctionNameForMask, On as tagMapAsList, P as deleteTableWithIndexBusyRetry, Qn as reDisableInstanceApiTermination, R as hasCdkAutoDeleteTag, Ri as refuseMalformedResourcesForDestroy, Rr as passedParameterClassesOf, Sa as requireConfigString, Sn as acquireIdempotencyToken, T as DynamoDBGlobalTableProvider, Tn as planTagDiff, Tr as getAccountInfo, Us as canonicalizeRegion, Ut as REDACTED_DELETE_ADDRESS_SKIP_REASON, V as markRefusedBeforeApplying, Vn as unchangedBehindSecretReference, Wn as deleteSkipReason, Ws as derivePartitionAndUrlSuffix, Wt as isRedactedRecordedValue, Xn as isTerminationProtectionPropagationError, Ya as redactSecretsForState, Yn as ec2InstanceProtectionSite, Za as recoverMaskedOutput, Zi as findSilentDropProperties, Zn as observeThenDisableInstanceApiTermination, _a as configStringRefusal, _r as s3BucketArn, _t as getCurrentNestedStackContext, a as findNestedTemplateTreeDefect, ba as requireConfigArray, bn as reserveStackCreateToken, br as s3BucketRegionalDomainName, c as renderNestedTemplateTreeDefect, ca as WARM_THROUGHPUT_MEMBERS, da as toCfnInteger, dt as getNestedRevertRun, ec as describeFileReadFailure, fa as toFiniteNumber, ga as configIntegerRefusal, gr as isSettledInstanceState, ha as configBooleanRefusal, j as resolveDynamoDbDeleteBudgetMs, jn as stringifyIfAssigned, k as deleteBudgetKey, ka as sameLambdaFunctionAddress, kn as tagPlanWarning, la as coerceWarmThroughput$1, ma as coerceCfnInteger, mt as revertNestedChildFromJournal, nc as renderAssemblyPathEscape, o as isAbsoluteAssetPath, pa as coerceCfnBoolean, qn as markWaitAbandoned, rc as resolveAssemblyPath, ro as carriesSecretMask, s as listNestedTemplateRows, t as DeployEngine, tc as displayAssemblyPath, ua as isWarmThroughputDecrease, va as readConfigString, vi as refuseMalformedNestedChildOutputs, vn as earliestDefined, vr as s3BucketDomainName, vt as withNestedStackContext, wn as DBPROXY_TAG_OPTIONS, xa as requireConfigObject, xr as s3BucketWebsiteUrl, xt as awaitInFlightSibling, ya as replayWarn, yr as s3BucketDualStackDomainName, z as isTruthyCfnBoolean, zn as slowCcOperationTimeoutMs } from "./deploy-engine-D7204v7Z.js";
6
+ import { n as collectInlinePolicyNamesManagedBySiblings, t as IAMRoleProvider } from "./iam-role-provider-CCPaZ3U4.js";
7
+ import { a as canonicalizeIpProtocolValue, i as resolveSecretDerivedPrincipals, n as runDestroyForStack, o as resolvedResourceTimeoutMs, r as isResolvableSecretPrincipalList, t as canonicalizeIpv4Cidr } from "./program-B0sp_SHG.js";
8
+ import { RDSProvider } from "./rds-provider-DltlcDiQ.js";
9
+ import { NeptuneProvider } from "./neptune-provider-D1t9NgT-.js";
10
+ import { ASGProvider } from "./asg-provider-AVCcoXnQ.js";
11
11
  import { createHash, randomUUID } from "node:crypto";
12
12
  import { CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, 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, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client } from "@aws-sdk/client-s3";
13
13
  import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachRolePolicyCommand, AttachUserPolicyCommand, CreateAccessKeyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreatePolicyCommand, CreatePolicyVersionCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeletePolicyCommand, DeletePolicyVersionCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachRolePolicyCommand, DetachUserPolicyCommand, GetAccessKeyLastUsedCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetPolicyCommand, GetPolicyVersionCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListEntitiesForPolicyCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListPoliciesCommand, ListPolicyTagsCommand, ListPolicyVersionsCommand, ListUserPoliciesCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagPolicyCommand, TagUserCommand, UntagPolicyCommand, UntagUserCommand, UpdateAccessKeyCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
@@ -48,7 +48,7 @@ import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGrap
48
48
  import { ConcurrentModificationException, CrawlerRunningException, CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetJobCommand, GetSecurityConfigurationCommand, GetTableCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, TagResourceCommand as TagResourceCommand$18, UntagResourceCommand as UntagResourceCommand$18, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
49
49
  import { BudgetsClient, CreateBudgetCommand, CreateNotificationCommand, CreateSubscriberCommand, DeleteBudgetCommand, DeleteNotificationCommand, DeleteSubscriberCommand, DescribeBudgetCommand, DuplicateRecordException, NotFoundException as NotFoundException$5, TagResourceCommand as TagResourceCommand$19, UntagResourceCommand as UntagResourceCommand$19, UpdateBudgetCommand } from "@aws-sdk/client-budgets";
50
50
  import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, DescribeStreamSummaryCommand, DisableEnhancedMonitoringCommand, EnableEnhancedMonitoringCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListTagsForResourceCommand as ListTagsForResourceCommand$15, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$10, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$20, UntagResourceCommand as UntagResourceCommand$20, UpdateMaxRecordSizeCommand, UpdateShardCountCommand, UpdateStreamModeCommand } from "@aws-sdk/client-kinesis";
51
- import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, ResourceNotFoundException as ResourceNotFoundException$11, SchedulerClient, UpdateScheduleCommand } from "@aws-sdk/client-scheduler";
51
+ import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, ListSchedulesCommand, ResourceNotFoundException as ResourceNotFoundException$11, SchedulerClient, UpdateScheduleCommand } from "@aws-sdk/client-scheduler";
52
52
  import { CreatePipeCommand, DeletePipeCommand, DescribePipeCommand, NotFoundException as NotFoundException$6, PipesClient, TagResourceCommand as TagResourceCommand$21, UntagResourceCommand as UntagResourceCommand$21, UpdatePipeCommand } from "@aws-sdk/client-pipes";
53
53
  import { AccessPointAlreadyExists, AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemAlreadyExists, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, PutBackupPolicyCommand, PutFileSystemPolicyCommand, PutLifecycleConfigurationCommand, UpdateFileSystemCommand, UpdateFileSystemProtectionCommand } from "@aws-sdk/client-efs";
54
54
  import { CreateFileSystemCommand as CreateFileSystemCommand$1, CreateFileSystemFromBackupCommand, DeleteFileSystemCommand as DeleteFileSystemCommand$1, DescribeFileSystemsCommand as DescribeFileSystemsCommand$1, FSxClient, FileSystemNotFound as FileSystemNotFound$1, TagResourceCommand as TagResourceCommand$22, UntagResourceCommand as UntagResourceCommand$22, UpdateFileSystemCommand as UpdateFileSystemCommand$1 } from "@aws-sdk/client-fsx";
@@ -138,6 +138,11 @@ var IAMPolicyProvider = class {
138
138
  const { roles, groups, users } = targets;
139
139
  if (!policyDocument) throw new ProvisioningError(`PolicyDocument is required for IAM policy ${logicalId}`, resourceType, logicalId);
140
140
  if (!(roles && roles.length > 0 || groups && groups.length > 0 || users && users.length > 0)) throw new ProvisioningError(`At least one of Roles, Groups, or Users is required for IAM policy ${logicalId}`, resourceType, logicalId);
141
+ const attached = {
142
+ roles: [],
143
+ groups: [],
144
+ users: []
145
+ };
141
146
  try {
142
147
  const policyDoc = typeof policyDocument === "string" ? policyDocument : JSON.stringify(policyDocument);
143
148
  if (roles) for (const roleName of roles) {
@@ -146,6 +151,7 @@ var IAMPolicyProvider = class {
146
151
  PolicyName: policyName,
147
152
  PolicyDocument: policyDoc
148
153
  }));
154
+ attached.roles.push(roleName);
149
155
  log.debug(`Attached inline policy ${v(policyName)} to role ${v(roleName)}`);
150
156
  }
151
157
  if (groups) for (const groupName of groups) {
@@ -154,6 +160,7 @@ var IAMPolicyProvider = class {
154
160
  PolicyName: policyName,
155
161
  PolicyDocument: policyDoc
156
162
  }));
163
+ attached.groups.push(groupName);
157
164
  log.debug(`Attached inline policy ${v(policyName)} to group ${v(groupName)}`);
158
165
  }
159
166
  if (users) for (const userName of users) {
@@ -162,6 +169,7 @@ var IAMPolicyProvider = class {
162
169
  PolicyName: policyName,
163
170
  PolicyDocument: policyDoc
164
171
  }));
172
+ attached.users.push(userName);
165
173
  log.debug(`Attached inline policy ${v(policyName)} to user ${v(userName)}`);
166
174
  }
167
175
  log.debug(`Successfully created IAM policy ${logicalId}: ${v(policyName)}`);
@@ -171,8 +179,58 @@ var IAMPolicyProvider = class {
171
179
  };
172
180
  } catch (error) {
173
181
  const cause = error instanceof Error ? error : void 0;
174
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM policy ${logicalId}: ${text}`, resourceType, logicalId, policyName, cause));
182
+ await this.detachPartialPolicy(logicalId, policyName, attached, log, context?.maskSecrets);
183
+ const errorSinks = withDerivedNameMasks(this.logger, log, [
184
+ ...roles ?? [],
185
+ ...groups ?? [],
186
+ ...users ?? []
187
+ ].map((n) => [n, n]));
188
+ throw this.wrapMaskedError(errorSinks.mask, error, (text) => new ProvisioningError(`Failed to create IAM policy ${logicalId}: ${text}`, resourceType, logicalId, policyName, cause));
189
+ }
190
+ }
191
+ async detachPartialPolicy(logicalId, policyName, attached, sinks, maskSecrets) {
192
+ const aws = pasteableAwsCommand(maskSecrets);
193
+ const log = withDerivedNameMasks(this.logger, sinks, [
194
+ ...attached.roles,
195
+ ...attached.groups,
196
+ ...attached.users
197
+ ].map((n) => [n, n]));
198
+ const { value: v } = log;
199
+ const steps = [
200
+ ...attached.roles.map((name) => ({
201
+ what: `role ${v(name)}`,
202
+ send: () => this.iamClient.send(new DeleteRolePolicyCommand({
203
+ RoleName: name,
204
+ PolicyName: policyName
205
+ })),
206
+ command: aws`aws iam delete-role-policy --role-name ${name} --policy-name ${policyName}`.render()
207
+ })),
208
+ ...attached.groups.map((name) => ({
209
+ what: `group ${v(name)}`,
210
+ send: () => this.iamClient.send(new DeleteGroupPolicyCommand({
211
+ GroupName: name,
212
+ PolicyName: policyName
213
+ })),
214
+ command: aws`aws iam delete-group-policy --group-name ${name} --policy-name ${policyName}`.render()
215
+ })),
216
+ ...attached.users.map((name) => ({
217
+ what: `user ${v(name)}`,
218
+ send: () => this.iamClient.send(new DeleteUserPolicyCommand({
219
+ UserName: name,
220
+ PolicyName: policyName
221
+ })),
222
+ command: aws`aws iam delete-user-policy --user-name ${name} --policy-name ${policyName}`.render()
223
+ }))
224
+ ];
225
+ const failed = [];
226
+ for (const step of steps) try {
227
+ await step.send();
228
+ log.debug(`Detached inline policy ${v(policyName)} from ${step.what} after the create failed`);
229
+ } catch (error) {
230
+ if (error instanceof NoSuchEntityException) continue;
231
+ failed.push(`${step.what} (${log.mask(describeAwsFailure(error).detail)}): ${step.command}`);
175
232
  }
233
+ if (failed.length > 0) log.warn(`Failed to detach the partially-created IAM policy ${logicalId} (${v(policyName)}) after its create failed; cdkd has no record of it, so it stays attached and granting until it is removed by hand: ${failed.join("; ")}`);
176
234
  }
177
235
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
178
236
  const oldPolicyName = physicalId.includes(":") ? physicalId.split(":")[0] : physicalId;
@@ -300,11 +358,11 @@ var IAMPolicyProvider = class {
300
358
  let resolvedFromSecret = false;
301
359
  let legacyRoleFromPhysicalId = !roles && !groups && !users && physicalId.includes(":") ? physicalId.split(":")[1] : void 0;
302
360
  if (!policyName && policyNameRedacted) {
303
- const skip = redactedDeleteAddressSkip(this.logger, logicalId, "IAM policy", ["PolicyName"]);
361
+ const skip = redactedDeleteAddressSkip(this.logger, logicalId, "IAM policy", ["PolicyName"], context);
304
362
  if (skip) return skip;
305
363
  }
306
364
  if (!policyName) {
307
- this.logger.warn(`Invalid physical ID format: ${physicalId}, and no PolicyName in the state record's properties — skipping deletion. No AWS call is issued, so the inline policy is LEFT ATTACHED to its roles / groups / users, UNLESS the role / group / user it is attached to is itself part of this stack (deleting that principal removes its inline policies, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). Otherwise repair the physicalId in state.json and re-run, or delete the inline policy by hand. ${DEPLOY_SKIP_CAVEAT$3}`);
365
+ this.logger.warn(`Invalid physical ID format: ${physicalId}, and no PolicyName in the state record's properties — skipping deletion. No AWS call is issued, so the inline policy is LEFT ATTACHED to its roles / groups / users, UNLESS the role / group / user it is attached to is itself part of this stack (deleting that principal removes its inline policies, and then only the cdkd record is stale — clear it with ${stateOrphanRecordRemedy(context, logicalId)}). Otherwise repair the physicalId in state.json and re-run, or delete the inline policy by hand. ${DEPLOY_SKIP_CAVEAT$3}`);
308
366
  return {
309
367
  outcome: "skipped",
310
368
  reason: POLICY_NAME_SKIP_REASON
@@ -338,7 +396,7 @@ var IAMPolicyProvider = class {
338
396
  }
339
397
  if (malformedKinds.length > 0 && !resolvedFromSecret) {
340
398
  const plainKinds = malformedKinds.filter((k) => !secretDerivedKinds.includes(k));
341
- const repair = secretDerivedKinds.length === 0 ? `Repair ${plainKinds.join(" / ")} in state.json to a list of role / group / user names and re-run, or delete the inline policy by hand.` : `The recorded ${secretDerivedKinds.join(" / ")} is secret-derived (cdkd keeps the dynamic reference or its mask in state), so do not write the name into state.json` + (resolutionAttempted ? "; cdkd could not resolve the reference (its region, access to it, or its value), so fix that and re-run, or" : `:`) + ` cdkd will keep skipping this record` + (plainKinds.length > 0 ? `, whatever is repaired in the recorded ${plainKinds.join(" / ")}` : "") + ". Remove the inline policy from its principals by hand (a principal this stack also deletes takes its inline policies with it); on cdkd destroy every other resource is still deleted, so once this is the stack's last record 'cdkd state orphan <stack> --stack-region <region>' clears it.";
399
+ const repair = secretDerivedKinds.length === 0 ? `Repair ${plainKinds.join(" / ")} in state.json to a list of role / group / user names and re-run, or delete the inline policy by hand.` : `The recorded ${secretDerivedKinds.join(" / ")} is secret-derived (cdkd keeps the dynamic reference or its mask in state), so do not write the name into state.json` + (resolutionAttempted ? "; cdkd could not resolve the reference (its region, access to it, or its value), so fix that and re-run, or" : `:`) + ` cdkd will keep skipping this record` + (plainKinds.length > 0 ? `, whatever is repaired in the recorded ${plainKinds.join(" / ")}` : "") + `. Remove the inline policy from its principals by hand (a principal this stack also deletes takes its inline policies with it), then drop this record with ${stateOrphanRecordRemedy(context, logicalId)}.`;
342
400
  this.logger.warn(`The state record for IAM policy ${logicalId} holds ${malformedKinds.join(" / ")} that is not a list of IAM names — skipping deletion rather than guessing which principals it names. No AWS call is issued, so the inline policy is LEFT ATTACHED wherever it is. ${repair} ${DEPLOY_SKIP_CAVEAT$3}`);
343
401
  return {
344
402
  outcome: "skipped",
@@ -346,7 +404,7 @@ var IAMPolicyProvider = class {
346
404
  };
347
405
  }
348
406
  if (!roles && !groups && !users && !legacyRoleFromPhysicalId) {
349
- this.logger.warn(`No Roles, Groups or Users in the state record for IAM policy ${logicalId} (physicalId "${physicalId}"), skipping deletion — an inline policy exists only as an attachment, so with no principal named there is no delete to issue and the policy is LEFT ATTACHED wherever it is, UNLESS the role / group / user it is attached to is itself part of this stack (deleting that principal removes its inline policies, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). Otherwise restore Roles / Groups / Users in state.json and re-run, or delete the inline policy by hand. ${DEPLOY_SKIP_CAVEAT$3}`);
407
+ this.logger.warn(`No Roles, Groups or Users in the state record for IAM policy ${logicalId} (physicalId "${physicalId}"), skipping deletion — an inline policy exists only as an attachment, so with no principal named there is no delete to issue and the policy is LEFT ATTACHED wherever it is, UNLESS the role / group / user it is attached to is itself part of this stack (deleting that principal removes its inline policies, and then only the cdkd record is stale — clear it with ${stateOrphanRecordRemedy(context, logicalId)}). Otherwise restore Roles / Groups / Users in state.json and re-run, or delete the inline policy by hand. ${DEPLOY_SKIP_CAVEAT$3}`);
350
408
  return {
351
409
  outcome: "skipped",
352
410
  reason: POLICY_NO_TARGET_SKIP_REASON
@@ -433,7 +491,7 @@ var IAMPolicyProvider = class {
433
491
  }
434
492
  }
435
493
  if (secretPrincipalLacksGrant) {
436
- this.logger.warn(safeMsg`IAM policy ${logicalId}: a principal the secret's CURRENT value names does not hold this inline policy. The value may have rotated since the policy was attached, so a principal only the OLD value named may still hold it; or an earlier cdkd run already removed it there, or that principal was deleted first. The record is KEPT rather than read as deleted: delete the inline policy from any old principal by hand (the principals the current value names are done), then drop this record with 'cdkd orphan <constructPath>', or, with no CDK app, 'cdkd state orphan <stack> --stack-region <region>' once it is the stack's last record.`);
494
+ this.logger.warn(safeMsg`IAM policy ${logicalId}: a principal the secret's CURRENT value names does not hold this inline policy. The value may have rotated since the policy was attached, so a principal only the OLD value named may still hold it; or an earlier cdkd run already removed it there, or that principal was deleted first. The record is KEPT rather than read as deleted: delete the inline policy from any old principal by hand (the principals the current value names are done), then drop this record with 'cdkd orphan <constructPath>', or, with no CDK app, ${stateOrphanRecordRemedy(context, logicalId)}.`);
437
495
  return {
438
496
  outcome: "skipped",
439
497
  reason: POLICY_SECRET_PRINCIPAL_LACKS_GRANT_SKIP_REASON
@@ -544,6 +602,7 @@ var IAMManagedPolicyProvider = class {
544
602
  const principals = principalsOf(properties);
545
603
  if ("malformed" in principals) throw new ProvisioningError(`${principals.malformed.join(" / ")} of IAM managed policy ${logicalId} is not a list of IAM names — no managed policy was created or attached`, resourceType, logicalId);
546
604
  const policyDoc = typeof policyDocument === "string" ? policyDocument : JSON.stringify(policyDocument);
605
+ let leftBehindArn;
547
606
  try {
548
607
  const createParams = {
549
608
  PolicyName: policyName,
@@ -554,6 +613,7 @@ var IAMManagedPolicyProvider = class {
554
613
  if (tags.length > 0) createParams.Tags = tags;
555
614
  const policyArn = (await this.iamClient.send(new CreatePolicyCommand(createParams))).Policy?.Arn;
556
615
  if (!policyArn) throw new ProvisioningError(`CreatePolicy succeeded but no Arn returned for ${logicalId}`, resourceType, logicalId, policyName);
616
+ leftBehindArn = policyArn;
557
617
  log.debug(`Created IAM managed policy: ${v(policyArn)}`);
558
618
  try {
559
619
  await this.attachToPrincipals(policyArn, principals.lists.Groups, principals.lists.Roles, principals.lists.Users, log);
@@ -562,6 +622,7 @@ var IAMManagedPolicyProvider = class {
562
622
  await this.detachAllPrincipals(policyArn);
563
623
  await this.deleteAllNonDefaultVersions(policyArn);
564
624
  await this.iamClient.send(new DeletePolicyCommand({ PolicyArn: policyArn }));
625
+ leftBehindArn = void 0;
565
626
  log.debug(`Cleaned up partially-created managed policy ${logicalId} (${v(policyArn)}) after attachment failure`);
566
627
  } catch (cleanupError) {
567
628
  const aws = pasteableAwsCommand(log.mask);
@@ -574,9 +635,13 @@ var IAMManagedPolicyProvider = class {
574
635
  attributes: { PolicyArn: policyArn }
575
636
  };
576
637
  } catch (error) {
577
- if (error instanceof CdkdError) throw error;
638
+ const markLeftBehind = (e) => leftBehindArn !== void 0 ? markCreatedBeforeFailure(e, logicalId, resourceType, leftBehindArn) : e;
639
+ if (error instanceof CdkdError) {
640
+ markLeftBehind(error);
641
+ throw error;
642
+ }
578
643
  const cause = error instanceof Error ? error : void 0;
579
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM managed policy ${logicalId}: ${text}`, resourceType, logicalId, policyName, cause));
644
+ throw this.wrapMaskedError(log.mask, error, (text) => markLeftBehind(new ProvisioningError(`Failed to create IAM managed policy ${logicalId}: ${text}`, resourceType, logicalId, policyName, cause)));
580
645
  }
581
646
  }
582
647
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -1070,11 +1135,13 @@ var IAMInstanceProfileProvider = class {
1070
1135
  const principals = readPrincipalLists({ Roles: properties["Roles"] });
1071
1136
  if ("malformed" in principals) throw new ProvisioningError(`Roles of IAM instance profile ${logicalId} is not a list of IAM role names — no instance profile was created`, resourceType, logicalId);
1072
1137
  const roles = principals.lists.Roles;
1138
+ let leftBehind = false;
1073
1139
  try {
1074
1140
  const response = await this.iamClient.send(new CreateInstanceProfileCommand({
1075
1141
  InstanceProfileName: instanceProfileName,
1076
1142
  Path: path
1077
1143
  }));
1144
+ leftBehind = true;
1078
1145
  log.debug(`Created IAM instance profile: ${v(instanceProfileName)}`);
1079
1146
  const attachedRoles = [];
1080
1147
  try {
@@ -1097,6 +1164,7 @@ var IAMInstanceProfileProvider = class {
1097
1164
  if (!(err instanceof NoSuchEntityException)) throw err;
1098
1165
  }
1099
1166
  await this.iamClient.send(new DeleteInstanceProfileCommand({ InstanceProfileName: instanceProfileName }));
1167
+ leftBehind = false;
1100
1168
  log.debug(`Cleaned up partially-created IAM instance profile ${logicalId} (${v(instanceProfileName)}) after wiring failure`);
1101
1169
  } catch (cleanupError) {
1102
1170
  const aws = pasteableAwsCommand(log.mask);
@@ -1111,7 +1179,9 @@ var IAMInstanceProfileProvider = class {
1111
1179
  };
1112
1180
  } catch (error) {
1113
1181
  const cause = error instanceof Error ? error : void 0;
1114
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM instance profile ${logicalId}: ${text}`, resourceType, logicalId, instanceProfileName, cause));
1182
+ const thrown = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM instance profile ${logicalId}: ${text}`, resourceType, logicalId, instanceProfileName, cause));
1183
+ if (leftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, instanceProfileName);
1184
+ throw thrown;
1115
1185
  }
1116
1186
  }
1117
1187
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -1277,16 +1347,19 @@ var IAMAccessKeyProvider = class {
1277
1347
  const aws = pasteableAwsCommand(log.mask);
1278
1348
  const attemptStartMs = Date.now() - CREATE_DATE_SKEW_MARGIN_MS;
1279
1349
  const baseline = await this.tryListAccessKeyIds(userName, logicalId, log);
1350
+ let leftBehindKeyId;
1280
1351
  try {
1281
1352
  const response = await this.iamClient.send(new CreateAccessKeyCommand({ UserName: userName }));
1282
1353
  const accessKeyId = response.AccessKey?.AccessKeyId;
1283
1354
  const secretAccessKey = response.AccessKey?.SecretAccessKey;
1355
+ leftBehindKeyId = accessKeyId;
1284
1356
  if (!accessKeyId || !secretAccessKey) {
1285
1357
  if (accessKeyId) try {
1286
1358
  await this.iamClient.send(new DeleteAccessKeyCommand({
1287
1359
  UserName: userName,
1288
1360
  AccessKeyId: accessKeyId
1289
1361
  }));
1362
+ leftBehindKeyId = void 0;
1290
1363
  } catch (cleanupError) {
1291
1364
  log.warn(`Failed to clean up IAM access key ${logicalId} (${v(accessKeyId)}) minted by a partial CreateAccessKey response: ${v(describeAwsFailure(cleanupError).detail)}. Manual deletion may be required: ${aws`aws iam delete-access-key --user-name ${userName} --access-key-id ${accessKeyId}`.render()}`);
1292
1365
  }
@@ -1304,6 +1377,7 @@ var IAMAccessKeyProvider = class {
1304
1377
  UserName: userName,
1305
1378
  AccessKeyId: accessKeyId
1306
1379
  }));
1380
+ leftBehindKeyId = void 0;
1307
1381
  log.debug(`Cleaned up partially-created IAM access key ${logicalId} (${v(accessKeyId)}) after status wiring failure`);
1308
1382
  } catch (cleanupError) {
1309
1383
  log.warn(`Failed to clean up partially-created IAM access key ${logicalId} (${v(accessKeyId)}): ${v(describeAwsFailure(cleanupError).detail)}. Manual deletion may be required before the next deploy: ${aws`aws iam delete-access-key --user-name ${userName} --access-key-id ${accessKeyId}`.render()}`);
@@ -1320,9 +1394,12 @@ var IAMAccessKeyProvider = class {
1320
1394
  }
1321
1395
  };
1322
1396
  } catch (error) {
1323
- await this.deleteOrphanFromFailedAttempt(userName, logicalId, baseline, attemptStartMs, log);
1397
+ const reconciled = await this.deleteOrphanFromFailedAttempt(userName, logicalId, baseline, attemptStartMs, log);
1398
+ if (leftBehindKeyId !== void 0 && reconciled.has(leftBehindKeyId)) leftBehindKeyId = void 0;
1324
1399
  const cause = error instanceof Error ? error : void 0;
1325
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM access key ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
1400
+ const thrown = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM access key ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
1401
+ if (leftBehindKeyId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindKeyId);
1402
+ throw thrown;
1326
1403
  }
1327
1404
  }
1328
1405
  async withUserCreateLock(userName, fn) {
@@ -1342,9 +1419,10 @@ var IAMAccessKeyProvider = class {
1342
1419
  async deleteOrphanFromFailedAttempt(userName, logicalId, baseline, attemptStartMs, log) {
1343
1420
  const { value: v } = log;
1344
1421
  const aws = pasteableAwsCommand(log.mask);
1345
- if (baseline === void 0) return;
1422
+ const deleted = /* @__PURE__ */ new Set();
1423
+ if (baseline === void 0) return deleted;
1346
1424
  const current = await this.tryListAccessKeyMetadata(userName, logicalId, log);
1347
- if (!current) return;
1425
+ if (!current) return deleted;
1348
1426
  for (const key of current) {
1349
1427
  const accessKeyId = key.accessKeyId;
1350
1428
  if (baseline.has(accessKeyId) || this.accessKeyIdsCreatedByThisProcess.has(accessKeyId)) continue;
@@ -1358,10 +1436,12 @@ var IAMAccessKeyProvider = class {
1358
1436
  UserName: userName,
1359
1437
  AccessKeyId: accessKeyId
1360
1438
  }));
1439
+ deleted.add(accessKeyId);
1361
1440
  } catch (error) {
1362
1441
  log.warn(`Failed to delete the orphaned IAM access key ${v(accessKeyId)} for user ${v(userName)}: ${v(describeAwsFailure(error).detail)}. Manual deletion may be required: ${aws`aws iam delete-access-key --user-name ${userName} --access-key-id ${accessKeyId}`.render()}`);
1363
1442
  }
1364
1443
  }
1444
+ return deleted;
1365
1445
  }
1366
1446
  async tryListAccessKeyIds(userName, logicalId, log) {
1367
1447
  const metadata = await this.tryListAccessKeyMetadata(userName, logicalId, log);
@@ -1615,11 +1695,13 @@ var IAMUserGroupProvider = class {
1615
1695
  const userName = generateResourceNameWithFallback(properties["UserName"], logicalId, { maxLength: USER_NAME_MAX_LENGTH });
1616
1696
  const groups = readPrincipalLists({ Groups: properties["Groups"] });
1617
1697
  if ("malformed" in groups) throw new ProvisioningError(`Groups of IAM user ${logicalId} is not a list of IAM group names — no user was created`, resourceType, logicalId);
1698
+ let leftBehind = false;
1618
1699
  try {
1619
1700
  const createParams = { UserName: userName };
1620
1701
  if (properties["Path"]) createParams.Path = properties["Path"];
1621
1702
  if (properties["Tags"] !== void 0 && properties["Tags"] !== null) createParams.Tags = desiredTags;
1622
1703
  const response = await this.iamClient.send(new CreateUserCommand(createParams));
1704
+ leftBehind = true;
1623
1705
  try {
1624
1706
  const permissionsBoundary = properties["PermissionsBoundary"];
1625
1707
  if (permissionsBoundary) {
@@ -1680,6 +1762,7 @@ var IAMUserGroupProvider = class {
1680
1762
  if (!(err instanceof NoSuchEntityException)) throw err;
1681
1763
  }
1682
1764
  await this.iamClient.send(new DeleteUserCommand({ UserName: userName }));
1765
+ leftBehind = false;
1683
1766
  log.debug(`Cleaned up partially-created IAM user ${logicalId} (${v(userName)}) after wiring failure`);
1684
1767
  } catch (cleanupError) {
1685
1768
  const aws = pasteableAwsCommand(log.mask);
@@ -1694,7 +1777,9 @@ var IAMUserGroupProvider = class {
1694
1777
  };
1695
1778
  } catch (error) {
1696
1779
  const cause = error instanceof Error ? error : void 0;
1697
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM user ${logicalId}: ${text}`, resourceType, logicalId, userName, cause));
1780
+ const thrown = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM user ${logicalId}: ${text}`, resourceType, logicalId, userName, cause));
1781
+ if (leftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, userName);
1782
+ throw thrown;
1698
1783
  }
1699
1784
  }
1700
1785
  async updateUser(logicalId, physicalId, resourceType, properties, previousProperties, log, claimed) {
@@ -1989,10 +2074,12 @@ var IAMUserGroupProvider = class {
1989
2074
  const { value: v } = log;
1990
2075
  log.debug(`Creating IAM group ${logicalId}`);
1991
2076
  const groupName = generateResourceNameWithFallback(properties["GroupName"], logicalId, { maxLength: GROUP_NAME_MAX_LENGTH });
2077
+ let leftBehind = false;
1992
2078
  try {
1993
2079
  const createParams = { GroupName: groupName };
1994
2080
  if (properties["Path"]) createParams.Path = properties["Path"];
1995
2081
  const response = await this.iamClient.send(new CreateGroupCommand(createParams));
2082
+ leftBehind = true;
1996
2083
  try {
1997
2084
  const managedPolicyArns = properties["ManagedPolicyArns"];
1998
2085
  if (managedPolicyArns && Array.isArray(managedPolicyArns)) for (const policyArn of managedPolicyArns) {
@@ -2017,6 +2104,7 @@ var IAMUserGroupProvider = class {
2017
2104
  await this.detachAllGroupPolicies(groupName, log);
2018
2105
  await this.deleteAllGroupInlinePolicies(groupName, log);
2019
2106
  await this.iamClient.send(new DeleteGroupCommand({ GroupName: groupName }));
2107
+ leftBehind = false;
2020
2108
  log.debug(`Cleaned up partially-created IAM group ${logicalId} (${v(groupName)}) after wiring failure`);
2021
2109
  } catch (cleanupError) {
2022
2110
  const aws = pasteableAwsCommand(log.mask);
@@ -2031,7 +2119,9 @@ var IAMUserGroupProvider = class {
2031
2119
  };
2032
2120
  } catch (error) {
2033
2121
  const cause = error instanceof Error ? error : void 0;
2034
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM group ${logicalId}: ${text}`, resourceType, logicalId, groupName, cause));
2122
+ const thrown = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create IAM group ${logicalId}: ${text}`, resourceType, logicalId, groupName, cause));
2123
+ if (leftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, groupName);
2124
+ throw thrown;
2035
2125
  }
2036
2126
  }
2037
2127
  async updateGroup(logicalId, physicalId, resourceType, properties, previousProperties, log, claimed) {
@@ -2258,7 +2348,7 @@ var IAMUserGroupProvider = class {
2258
2348
  async deleteUserToGroupAddition(logicalId, physicalId, resourceType, properties, context) {
2259
2349
  this.logger.debug(`Deleting IAM UserToGroupAddition ${logicalId}`);
2260
2350
  if (!properties) {
2261
- this.logger.warn(`No properties for UserToGroupAddition ${logicalId}, skipping deletion — GroupName and Users are both required to call RemoveUserFromGroup, so no AWS call is issued and the group memberships are LEFT IN PLACE, UNLESS the group or the users are themselves part of this stack (their own deletes remove exactly these memberships, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). Otherwise restore the record's properties in state.json and re-run, or remove the users from the group by hand. ${DEPLOY_SKIP_CAVEAT$2}`);
2351
+ this.logger.warn(`No properties for UserToGroupAddition ${logicalId}, skipping deletion — GroupName and Users are both required to call RemoveUserFromGroup, so no AWS call is issued and the group memberships are LEFT IN PLACE, UNLESS the group or the users are themselves part of this stack (their own deletes remove exactly these memberships, and then only the cdkd record is stale — clear it with ${stateOrphanRecordRemedy(context, logicalId)}). Otherwise restore the record's properties in state.json and re-run, or remove the users from the group by hand. ${DEPLOY_SKIP_CAVEAT$2}`);
2262
2352
  return {
2263
2353
  outcome: "skipped",
2264
2354
  reason: MEMBERSHIP_NO_PROPERTIES_SKIP_REASON
@@ -2267,13 +2357,13 @@ var IAMUserGroupProvider = class {
2267
2357
  const groupName = properties["GroupName"];
2268
2358
  const recordedUsers = properties["Users"];
2269
2359
  if (!groupName || !recordedUsers) {
2270
- this.logger.warn(`Missing GroupName or Users for ${logicalId}, skipping deletion — both are required to call RemoveUserFromGroup, so no AWS call is issued and the group memberships are LEFT IN PLACE, UNLESS the group or the users are themselves part of this stack (their own deletes remove exactly these memberships, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). Otherwise restore them in state.json and re-run, or remove the users from the group by hand. ${DEPLOY_SKIP_CAVEAT$2}`);
2360
+ this.logger.warn(`Missing GroupName or Users for ${logicalId}, skipping deletion — both are required to call RemoveUserFromGroup, so no AWS call is issued and the group memberships are LEFT IN PLACE, UNLESS the group or the users are themselves part of this stack (their own deletes remove exactly these memberships, and then only the cdkd record is stale — clear it with ${stateOrphanRecordRemedy(context, logicalId)}). Otherwise restore them in state.json and re-run, or remove the users from the group by hand. ${DEPLOY_SKIP_CAVEAT$2}`);
2271
2361
  return {
2272
2362
  outcome: "skipped",
2273
2363
  reason: MEMBERSHIP_MISSING_FIELDS_SKIP_REASON
2274
2364
  };
2275
2365
  }
2276
- const redactedGroup = redactedDeleteAddressSkip(this.logger, logicalId, "UserToGroupAddition", redactedDeleteAddressFields({ GroupName: properties["GroupName"] }));
2366
+ const redactedGroup = redactedDeleteAddressSkip(this.logger, logicalId, "UserToGroupAddition", redactedDeleteAddressFields({ GroupName: properties["GroupName"] }), context);
2277
2367
  if (redactedGroup) return redactedGroup;
2278
2368
  const read = readPrincipalLists({ Users: properties["Users"] });
2279
2369
  let users = "malformed" in read ? void 0 : read.lists.Users;
@@ -2296,8 +2386,8 @@ var IAMUserGroupProvider = class {
2296
2386
  }
2297
2387
  }
2298
2388
  if ("malformed" in read && users === void 0) {
2299
- const repair = read.secretDerived.length > 0 ? "The recorded Users is secret-derived (cdkd keeps the dynamic reference or its mask in state), so do not write the name into state.json" + (resolutionAttempted ? "; cdkd could not resolve the reference (its region, access to it, or its value), so fix that and re-run, or" : ":") + " cdkd will keep skipping this record. Remove the users from the group by hand; on cdkd destroy every other resource is still deleted, so once this is the stack's last record 'cdkd state orphan <stack> --stack-region <region>' clears it." : "Repair the recorded Users in state.json to a list of user names and re-run, or remove the users from the group by hand.";
2300
- this.logger.warn(safeMsg`The state record for UserToGroupAddition ${logicalId} holds a Users that is not a list of IAM user names — skipping deletion rather than guessing which users it names. No AWS call is issued, so the group memberships are LEFT IN PLACE, UNLESS the group or the users are themselves part of this stack (their own deletes remove exactly these memberships, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). ${repair} ${DEPLOY_SKIP_CAVEAT$2}`);
2389
+ const repair = read.secretDerived.length > 0 ? "The recorded Users is secret-derived (cdkd keeps the dynamic reference or its mask in state), so do not write the name into state.json" + (resolutionAttempted ? "; cdkd could not resolve the reference (its region, access to it, or its value), so fix that and re-run, or" : ":") + ` cdkd will keep skipping this record. Remove the users from the group by hand, then drop this record with ${stateOrphanRecordRemedy(context, logicalId)}.` : "Repair the recorded Users in state.json to a list of user names and re-run, or remove the users from the group by hand.";
2390
+ this.logger.warn(safeMsg`The state record for UserToGroupAddition ${logicalId} holds a Users that is not a list of IAM user names — skipping deletion rather than guessing which users it names. No AWS call is issued, so the group memberships are LEFT IN PLACE, UNLESS the group or the users are themselves part of this stack (their own deletes remove exactly these memberships, and then only the cdkd record is stale — clear it with ${stateOrphanRecordRemedy(context, logicalId)}). ${repair} ${DEPLOY_SKIP_CAVEAT$2}`);
2301
2391
  return {
2302
2392
  outcome: "skipped",
2303
2393
  reason: MEMBERSHIP_MALFORMED_USERS_SKIP_REASON
@@ -2333,7 +2423,7 @@ var IAMUserGroupProvider = class {
2333
2423
  }
2334
2424
  }
2335
2425
  if (secretUserNotMember) {
2336
- this.logger.warn(safeMsg`UserToGroupAddition ${logicalId}: a user the secret's CURRENT value names is not in the group. The value may have rotated since the membership was added, so a user only the OLD value named may still be a member; or an earlier cdkd run already removed it, or that user was deleted first. The record is KEPT rather than read as deleted: remove any old user from the group by hand (the users the current value names are done), then drop this record with 'cdkd orphan <constructPath>', or, with no CDK app, 'cdkd state orphan <stack> --stack-region <region>' once it is the stack's last record.`);
2426
+ this.logger.warn(safeMsg`UserToGroupAddition ${logicalId}: a user the secret's CURRENT value names is not in the group. The value may have rotated since the membership was added, so a user only the OLD value named may still be a member; or an earlier cdkd run already removed it, or that user was deleted first. The record is KEPT rather than read as deleted: remove any old user from the group by hand (the users the current value names are done), then drop this record with 'cdkd orphan <constructPath>', or, with no CDK app, ${stateOrphanRecordRemedy(context, logicalId)}.`);
2337
2427
  return {
2338
2428
  outcome: "skipped",
2339
2429
  reason: MEMBERSHIP_SECRET_USER_NOT_MEMBER_SKIP_REASON
@@ -2510,13 +2600,14 @@ var IAMUserGroupProvider = class {
2510
2600
 
2511
2601
  //#endregion
2512
2602
  //#region src/provisioning/providers/s3-bucket-provider.ts
2513
- function manualBucketDeletionClause(bucketName, maskSecrets) {
2603
+ function manualBucketDeletionClause(bucketName, maskSecrets, journaled) {
2514
2604
  const command = renderDisableCommand({
2515
2605
  before: "aws s3api delete-bucket --bucket",
2516
2606
  identifier: bucketName,
2517
2607
  maskSecrets
2518
2608
  });
2519
- return command ? `Manual deletion may be required before the next deploy: ${command}` : "Manual deletion may be required before the next deploy, via the console: the bucket name cannot be reproduced safely on a command line, so any command shown here could act on a different bucket or run part of the name as shell.";
2609
+ const lead = journaled ? "On a first-time create the failed deploy's rollback journal records the bucket for `cdkd rollback --revert-failed`; otherwise delete it yourself before the next deploy" : "Manual deletion may be required before the next deploy";
2610
+ return command ? `${lead}: ${command}` : `${lead}, via the console: the bucket name cannot be reproduced safely on a command line, so any command shown here could act on a different bucket or run part of the name as shell.`;
2520
2611
  }
2521
2612
  function configItemId(item) {
2522
2613
  if (!isPlainObject$2(item)) return void 0;
@@ -4114,7 +4205,7 @@ var S3BucketProvider = class S3BucketProvider {
4114
4205
  };
4115
4206
  }
4116
4207
  }
4117
- async assertStateBucketRegion(operation, logicalId, resourceType, physicalId, expectedRegion) {
4208
+ async assertStateBucketRegion(operation, logicalId, resourceType, physicalId, expectedRegion, context) {
4118
4209
  const wantRegion = canonicalizeRegion(expectedRegion);
4119
4210
  const probe = await this.probeBucketRegion(physicalId);
4120
4211
  if (probe.kind === "absent") return;
@@ -4125,7 +4216,7 @@ var S3BucketProvider = class S3BucketProvider {
4125
4216
  }
4126
4217
  if (probe.region === wantRegion) return;
4127
4218
  const consequence = operation === "update" ? `Applying this stack's bucket configuration would reconfigure a bucket in ${probe.region} that this stack does not own the definition of.` : `Deleting it would destroy a bucket in ${probe.region} that this stack's region does not contain, and that deletion cannot be undone.`;
4128
- const remedy = operation === "update" ? `Confirm which bucket you mean, then either rerun this stack against ${probe.region}, or — if the recorded id is stale — drop this bucket's record with 'cdkd orphan <StackPath>/<Path/To/Bucket>', which removes only that record (and rewrites the references to it) without touching any AWS resource, then deploy again to create the bucket in this stack's region — first giving it a BucketName unique to that region if the template fixes this one, since S3 names are global.` : `Confirm which bucket you mean. If ${displaySafe(physicalId)} is genuinely yours to delete, delete it deliberately in ${probe.region}; if this record is simply stale, drop it with 'cdkd state orphan <stack> --stack-region <region>' (this stack's region, not the bucket's), which removes EVERY record the stack has in that region without touching any AWS resource, so run it once this is the stack's last record.`;
4219
+ const remedy = operation === "update" ? `Confirm which bucket you mean, then either rerun this stack against ${probe.region}, or — if the recorded id is stale — drop this bucket's record with 'cdkd orphan <StackPath>/<Path/To/Bucket>', which removes only that record (and rewrites the references to it) without touching any AWS resource, then deploy again to create the bucket in this stack's region — first giving it a BucketName unique to that region if the template fixes this one, since S3 names are global.` : `Confirm which bucket you mean. If ${displaySafe(physicalId)} is genuinely yours to delete, delete it deliberately in ${probe.region}; if this record is simply stale, drop it without touching any AWS resource (<region> is this stack's region, not the bucket's) with ${stateOrphanRecordRemedy(context, logicalId)}.`;
4129
4220
  throw markNonRetryable(new ProvisioningError(`Refusing to ${operation} S3 bucket ${this.shown(physicalId)} for ${displaySafe(logicalId)} (${resourceType}): the bucket lives in ${probe.region}, while this stack's state is for ${wantRegion}. S3 bucket names are globally unique, so a state record can name a bucket in another region — records written before the create-path region guard can carry one. ${consequence} ${remedy}`, resourceType, logicalId, physicalId));
4130
4221
  }
4131
4222
  announceUnverifiedBucketIdentity(operation, logicalId, physicalId, cause) {
@@ -4145,6 +4236,7 @@ var S3BucketProvider = class S3BucketProvider {
4145
4236
  lowercase: true,
4146
4237
  allowedPattern: /[^a-z0-9.-]/g
4147
4238
  });
4239
+ let bucketLeftBehind = false;
4148
4240
  try {
4149
4241
  const createParams = { Bucket: bucketName };
4150
4242
  const region = await this.getRegion();
@@ -4156,6 +4248,7 @@ var S3BucketProvider = class S3BucketProvider {
4156
4248
  try {
4157
4249
  await this.s3Client.send(new CreateBucketCommand(createParams));
4158
4250
  createdNewBucket = preflight.kind === "absent";
4251
+ bucketLeftBehind = createdNewBucket;
4159
4252
  if (preflight.kind === "region" && preflight.region !== "us-east-1") this.refuseForeignRegionAdopt(logicalId, resourceType, bucketName, "us-east-1", preflight.region);
4160
4253
  if (preflight.kind === "region") this.logger.warn(`S3 bucket ${this.shown(bucketName)} for ${displaySafe(logicalId)} (${resourceType}) already existed in ${preflight.region} and was ADOPTED, not created. In us-east-1 S3 answers a re-create of a bucket you already own with 200 OK and RESETS that bucket's access control lists, so any ACL previously set on ${this.shown(bucketName)} is now the default. cdkd will not delete this bucket if the rest of this create fails.`);
4161
4254
  this.logger.debug(`Created S3 bucket: ${this.shown(bucketName)}`);
@@ -4176,14 +4269,15 @@ var S3BucketProvider = class S3BucketProvider {
4176
4269
  } catch (innerError) {
4177
4270
  if (createdNewBucket) try {
4178
4271
  await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
4272
+ bucketLeftBehind = false;
4179
4273
  this.logger.debug(`Cleaned up partially-created S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}) after wiring failure`);
4180
4274
  } catch (cleanupError) {
4181
4275
  this.logger.debug(`DeleteBucket cleanup failed for S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}): ${describeAwsFailure(cleanupError).detail}`);
4182
- this.logger.warn(safeMsg`Failed to clean up partially-created S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}) ` + safeMsg`(${cleanupError instanceof Error ? cleanupError.name : typeof cleanupError}). ` + `Re-run with --verbose for AWS's own message. ` + safeMsg`${manualBucketDeletionClause(bucketName, this.shownMask)}`);
4276
+ this.logger.warn(safeMsg`Failed to clean up partially-created S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}) ` + safeMsg`(${cleanupError instanceof Error ? cleanupError.name : typeof cleanupError}). ` + `Re-run with --verbose for AWS's own message. ` + safeMsg`${manualBucketDeletionClause(bucketName, this.shownMask, true)}`);
4183
4277
  }
4184
4278
  else if (preflight.kind === "indeterminate") {
4185
4279
  this.logger.debug(`GetBucketLocation failed for S3 bucket ${this.shown(bucketName)} (${displaySafe(logicalId)}) during create: ${preflight.reason}`);
4186
- this.logger.warn(safeMsg`Not cleaning up S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}) after a wiring failure: ` + `this deploy could not confirm whether it created the bucket (region probe failed: ` + safeMsg`${preflight.errorName}), and in us-east-1 a successful CreateBucket does not ` + "prove it. Re-run with --verbose for AWS's own message. If cdkd created it, the bucket is an orphan. " + safeMsg`${manualBucketDeletionClause(bucketName, this.shownMask)}`);
4280
+ this.logger.warn(safeMsg`Not cleaning up S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}) after a wiring failure: ` + `this deploy could not confirm whether it created the bucket (region probe failed: ` + safeMsg`${preflight.errorName}), and in us-east-1 a successful CreateBucket does not ` + "prove it. Re-run with --verbose for AWS's own message. If cdkd created it, the bucket is an orphan. " + safeMsg`${manualBucketDeletionClause(bucketName, this.shownMask, false)}`);
4187
4281
  }
4188
4282
  throw markAuxiliaryFailure(innerError, logicalId);
4189
4283
  }
@@ -4196,7 +4290,9 @@ var S3BucketProvider = class S3BucketProvider {
4196
4290
  ...effectiveProperties ? { effectiveProperties } : {}
4197
4291
  };
4198
4292
  } catch (error) {
4199
- throw this.wrapOperationError("create", logicalId, resourceType, bucketName, error);
4293
+ const thrown = this.wrapOperationError("create", logicalId, resourceType, bucketName, error);
4294
+ if (bucketLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, bucketName);
4295
+ throw thrown;
4200
4296
  }
4201
4297
  }
4202
4298
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -4248,7 +4344,7 @@ var S3BucketProvider = class S3BucketProvider {
4248
4344
  async delete(logicalId, physicalId, resourceType, properties, context) {
4249
4345
  this.logger.debug(`Deleting S3 bucket ${displaySafe(logicalId)}: ${displaySafe(physicalId)}`);
4250
4346
  const allowAutoEmpty = context?.forceDataDelete === true || hasCdkAutoDeleteTag(properties, "aws-cdk:auto-delete-objects");
4251
- if (context?.expectedRegion) await this.assertStateBucketRegion("delete", logicalId, resourceType, physicalId, context.expectedRegion);
4347
+ if (context?.expectedRegion) await this.assertStateBucketRegion("delete", logicalId, resourceType, physicalId, context.expectedRegion, context);
4252
4348
  else this.announceUnverifiedBucketIdentity("delete", logicalId, physicalId, noRecordedRegionCause());
4253
4349
  try {
4254
4350
  await this.deleteBucketWithEmptyRetry(logicalId, physicalId, allowAutoEmpty);
@@ -5408,6 +5504,7 @@ var SQSQueuePolicyProvider = class {
5408
5504
  const policyDocument = properties["PolicyDocument"];
5409
5505
  if (!queues || queues.length === 0) throw new ProvisioningError(`Queues is required for SQS queue policy ${logicalId}`, resourceType, logicalId);
5410
5506
  if (!policyDocument) throw new ProvisioningError(`PolicyDocument is required for SQS queue policy ${logicalId}`, resourceType, logicalId);
5507
+ const applied = [];
5411
5508
  try {
5412
5509
  const policyDoc = typeof policyDocument === "string" ? policyDocument : JSON.stringify(policyDocument);
5413
5510
  for (const queueUrl of queues) {
@@ -5416,6 +5513,7 @@ var SQSQueuePolicyProvider = class {
5416
5513
  QueueUrl: queueUrl,
5417
5514
  Attributes: { Policy: policyDoc }
5418
5515
  }));
5516
+ applied.push(queueUrl);
5419
5517
  }
5420
5518
  this.logger.debug(`Successfully created SQS queue policy ${logicalId}`);
5421
5519
  return {
@@ -5424,7 +5522,9 @@ var SQSQueuePolicyProvider = class {
5424
5522
  };
5425
5523
  } catch (error) {
5426
5524
  const cause = error instanceof Error ? error : void 0;
5427
- throw new ProvisioningError(`Failed to create SQS queue policy ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, queues[0], cause);
5525
+ const thrown = new ProvisioningError(`Failed to create SQS queue policy ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, queues[0], cause);
5526
+ if (applied.length > 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, applied.join(","));
5527
+ throw thrown;
5428
5528
  }
5429
5529
  }
5430
5530
  async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
@@ -5455,22 +5555,23 @@ var SQSQueuePolicyProvider = class {
5455
5555
  }
5456
5556
  async delete(logicalId, physicalId, resourceType, _properties, context) {
5457
5557
  this.logger.debug(`Deleting SQS queue policy ${logicalId}: ${physicalId}`);
5458
- try {
5558
+ const queueUrls = physicalId.split(",");
5559
+ for (const queueUrl of queueUrls) try {
5459
5560
  await this.sqsClient.send(new SetQueueAttributesCommand({
5460
- QueueUrl: physicalId,
5561
+ QueueUrl: queueUrl,
5461
5562
  Attributes: { Policy: "" }
5462
5563
  }));
5463
- this.logger.debug(`Successfully deleted SQS queue policy ${logicalId}`);
5464
5564
  } catch (error) {
5465
5565
  if (error instanceof Error && (error.name === "QueueDoesNotExist" || error.message.includes("does not exist"))) {
5466
5566
  const clientRegion = await this.sqsClient.config.region();
5467
- assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, physicalId);
5468
- this.logger.debug(`Queue ${physicalId} does not exist, skipping policy deletion`);
5469
- return;
5567
+ assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, queueUrl);
5568
+ this.logger.debug(`Queue ${queueUrl} does not exist, skipping policy deletion`);
5569
+ continue;
5470
5570
  }
5471
5571
  const cause = error instanceof Error ? error : void 0;
5472
5572
  throw new ProvisioningError(`Failed to delete SQS queue policy ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
5473
5573
  }
5574
+ this.logger.debug(`Successfully deleted SQS queue policy ${logicalId}`);
5474
5575
  }
5475
5576
  async readCurrentState(physicalId, _logicalId, _resourceType) {
5476
5577
  let policyAttr;
@@ -5592,6 +5693,7 @@ var SNSTopicProvider = class {
5592
5693
  this.logger.debug(`Creating SNS topic ${logicalId}`);
5593
5694
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
5594
5695
  const topicName = properties["TopicName"] || generateResourceName(logicalId, { maxLength: 256 });
5696
+ let leftBehindArn;
5595
5697
  try {
5596
5698
  const topicAttributes = {};
5597
5699
  if (properties["FifoTopic"]) topicAttributes["FifoTopic"] = stringifyValue(properties["FifoTopic"]);
@@ -5645,7 +5747,8 @@ var SNSTopicProvider = class {
5645
5747
  await this.snsClient.send(new DeleteTopicCommand({ TopicArn: topicArn }));
5646
5748
  this.logger.debug(`Cleaned up partially-created SNS topic ${logicalId} (${topicArn}) after wiring failure`);
5647
5749
  } catch (cleanupError) {
5648
- warn(`Failed to clean up partially-created SNS topic ${logicalId} (${topicArn}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws sns delete-topic --topic-arn ${topicArn}`.render()}`);
5750
+ leftBehindArn = topicArn;
5751
+ warn(`Failed to clean up partially-created SNS topic ${logicalId} (${topicArn}): ${describeAwsFailure(cleanupError).detail}. On a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise delete it yourself before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws sns delete-topic --topic-arn ${topicArn}`.render()}`);
5649
5752
  }
5650
5753
  throw markAuxiliaryFailure(innerError, logicalId);
5651
5754
  }
@@ -5659,7 +5762,9 @@ var SNSTopicProvider = class {
5659
5762
  };
5660
5763
  } catch (error) {
5661
5764
  const cause = error instanceof Error ? error : void 0;
5662
- throw new ProvisioningError(`Failed to create SNS topic ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, topicName, cause);
5765
+ const thrown = new ProvisioningError(`Failed to create SNS topic ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, topicName, cause);
5766
+ if (leftBehindArn) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindArn);
5767
+ throw thrown;
5663
5768
  }
5664
5769
  }
5665
5770
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -6241,16 +6346,21 @@ var SNSTopicPolicyProvider = class {
6241
6346
  if (!topics || topics.length === 0) throw new ProvisioningError(`Topics is required for SNS topic policy ${logicalId}`, resourceType, logicalId);
6242
6347
  if (!policyDocument) throw new ProvisioningError(`PolicyDocument is required for SNS topic policy ${logicalId}`, resourceType, logicalId);
6243
6348
  const policyDoc = typeof policyDocument === "string" ? policyDocument : JSON.stringify(policyDocument);
6349
+ const applied = [];
6244
6350
  try {
6245
- for (const topicArn of topics) await this.setTopicPolicy(topicArn, policyDoc);
6351
+ for (const topicArn of topics) {
6352
+ await this.setTopicPolicy(topicArn, policyDoc);
6353
+ applied.push(topicArn);
6354
+ }
6246
6355
  this.logger.debug(`Successfully created SNS topic policy ${logicalId}`);
6247
6356
  return {
6248
6357
  physicalId: topics.join(","),
6249
6358
  attributes: {}
6250
6359
  };
6251
6360
  } catch (error) {
6252
- const cause = error instanceof Error ? error : void 0;
6253
- throw new ProvisioningError(`Failed to create SNS topic policy ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
6361
+ const thrown = new ProvisioningError(`Failed to create SNS topic policy ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
6362
+ if (applied.length > 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, applied.join(","));
6363
+ throw thrown;
6254
6364
  }
6255
6365
  }
6256
6366
  async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
@@ -6579,6 +6689,8 @@ var LambdaFunctionProvider = class {
6579
6689
  if (!role) throw new ProvisioningError(`Role is required for Lambda function ${logicalId}`, resourceType, logicalId);
6580
6690
  refuseImageFunctionUnsupported(logicalId, resourceType, properties);
6581
6691
  let functionCreated = false;
6692
+ let createdFunctionName;
6693
+ const postCreateCleanup = { deleted: false };
6582
6694
  try {
6583
6695
  const tags = properties["Tags"] ? Object.fromEntries(desiredTags.map((tag) => [tag.Key, tag.Value])) : void 0;
6584
6696
  const createParams = {
@@ -6610,6 +6722,7 @@ var LambdaFunctionProvider = class {
6610
6722
  };
6611
6723
  const response = await this.lambdaClient.send(new CreateFunctionCommand(createParams));
6612
6724
  functionCreated = true;
6725
+ createdFunctionName = response.FunctionName || functionName;
6613
6726
  const recursiveLoop = properties["RecursiveLoop"];
6614
6727
  if (recursiveLoop !== void 0) await this.applyPostCreateConfig(() => this.lambdaClient.send(new PutFunctionRecursionConfigCommand({
6615
6728
  FunctionName: functionName,
@@ -6620,7 +6733,8 @@ var LambdaFunctionProvider = class {
6620
6733
  logicalId,
6621
6734
  resourceType,
6622
6735
  functionName,
6623
- log
6736
+ log,
6737
+ cleanup: postCreateCleanup
6624
6738
  });
6625
6739
  const reservedConcurrentExecutions = properties["ReservedConcurrentExecutions"];
6626
6740
  if (reservedConcurrentExecutions !== void 0) await this.applyPostCreateConfig(() => this.lambdaClient.send(new PutFunctionConcurrencyCommand({
@@ -6632,7 +6746,8 @@ var LambdaFunctionProvider = class {
6632
6746
  logicalId,
6633
6747
  resourceType,
6634
6748
  functionName,
6635
- log
6749
+ log,
6750
+ cleanup: postCreateCleanup
6636
6751
  });
6637
6752
  const runtimeManagementConfig = properties["RuntimeManagementConfig"];
6638
6753
  if (runtimeManagementConfig !== void 0) await this.applyPostCreateConfig(async () => {
@@ -6649,7 +6764,8 @@ var LambdaFunctionProvider = class {
6649
6764
  logicalId,
6650
6765
  resourceType,
6651
6766
  functionName,
6652
- log
6767
+ log,
6768
+ cleanup: postCreateCleanup
6653
6769
  });
6654
6770
  log.debug(`Successfully created Lambda function ${logicalId}: ${log.value(functionName)}`);
6655
6771
  return {
@@ -6662,7 +6778,9 @@ var LambdaFunctionProvider = class {
6662
6778
  } catch (error) {
6663
6779
  if (functionCreated) markAuxiliaryFailure(error, logicalId);
6664
6780
  const cause = error instanceof Error ? error : void 0;
6665
- throw this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to create Lambda function ${logicalId}: ${text}`, resourceType, logicalId, functionName, cause ?? new Error(String(error))));
6781
+ const thrown = this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to create Lambda function ${logicalId}: ${text}`, resourceType, logicalId, functionName, cause ?? new Error(String(error))));
6782
+ if (createdFunctionName !== void 0 && !postCreateCleanup.deleted) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdFunctionName);
6783
+ throw thrown;
6666
6784
  }
6667
6785
  }
6668
6786
  wrapMaskedError(log, error, build) {
@@ -6691,6 +6809,7 @@ var LambdaFunctionProvider = class {
6691
6809
  if (!/currently in the following state|ResourceConflict/i.test(deleteMessage)) break;
6692
6810
  if (attempt < 2) await new Promise((resolve) => setTimeout(resolve, this.cleanupDeleteRetryDelayMs * (attempt + 1)));
6693
6811
  }
6812
+ if (cleanupFailure === void 0) ctx.cleanup.deleted = true;
6694
6813
  if (cleanupFailure !== void 0) this.logger.error(log.mask(`Cleanup DeleteFunction failed for ${ctx.logicalId} after ${ctx.apiName} failure — function may be orphaned: ${cleanupFailure}`));
6695
6814
  const cause = error instanceof Error ? error : void 0;
6696
6815
  const refusal = new ProvisioningError(`Failed to set ${ctx.propertyName} on Lambda function ${ctx.logicalId} (function was deleted to maintain atomicity): ${message}`, ctx.resourceType, ctx.logicalId, ctx.functionName, cause ?? new Error(rawMessage));
@@ -7358,7 +7477,7 @@ var LambdaPermissionProvider = class {
7358
7477
  }
7359
7478
  }
7360
7479
  if (statementId === "") {
7361
- this.logger.warn(safeMsg`Lambda permission ${logicalId} has no StatementId in its physicalId ` + safeMsg`("${physicalId}"), skipping deletion — no AWS call is issued, so the permission ` + `statement is LEFT IN PLACE on the function's resource policy, UNLESS the function itself is part of this stack (deleting it removes its whole resource policy, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). Otherwise repair the physicalId in state.json and re-run, or remove the statement by hand ('aws lambda remove-permission'). ${DEPLOY_SKIP_CAVEAT$1}`);
7480
+ this.logger.warn(safeMsg`Lambda permission ${logicalId} has no StatementId in its physicalId ` + safeMsg`("${physicalId}"), skipping deletion — no AWS call is issued, so the permission ` + "statement is LEFT IN PLACE on the function's resource policy, UNLESS the function itself is part of this stack (deleting it removes its whole resource policy, and then only the cdkd record is stale — clear it with " + safeMsg`${stateOrphanRecordRemedy(context, logicalId)}). ` + `Otherwise repair the physicalId in state.json and re-run, or remove the statement by hand ('aws lambda remove-permission'). ${DEPLOY_SKIP_CAVEAT$1}`);
7362
7481
  return {
7363
7482
  outcome: "skipped",
7364
7483
  reason: PERMISSION_STATEMENT_ID_SKIP_REASON
@@ -7366,11 +7485,11 @@ var LambdaPermissionProvider = class {
7366
7485
  }
7367
7486
  const functionName = functionNameFromProperties || functionNameFromPhysicalId;
7368
7487
  if (!functionName && functionNameRedacted) {
7369
- const skip = redactedDeleteAddressSkip(this.logger, logicalId, "Lambda permission", ["FunctionName"]);
7488
+ const skip = redactedDeleteAddressSkip(this.logger, logicalId, "Lambda permission", ["FunctionName"], context);
7370
7489
  if (skip) return skip;
7371
7490
  }
7372
7491
  if (!functionName) {
7373
- this.logger.warn(safeMsg`FunctionName not available for Lambda permission ${logicalId} (neither the state ` + `record's FunctionName nor a function ARN in the physicalId), skipping deletion — no AWS call is issued, so the permission statement is LEFT IN PLACE on the function's resource policy, UNLESS the function itself is part of this stack (deleting it removes its whole resource policy, and then only the cdkd record is stale — clear it with 'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack has in that region). Otherwise repair the record's FunctionName in state.json and re-run, or remove the statement by hand ('aws lambda remove-permission'). ${DEPLOY_SKIP_CAVEAT$1}`);
7492
+ this.logger.warn(safeMsg`FunctionName not available for Lambda permission ${logicalId} (neither the state ` + "record's FunctionName nor a function ARN in the physicalId), skipping deletion — no AWS call is issued, so the permission statement is LEFT IN PLACE on the function's resource policy, UNLESS the function itself is part of this stack (deleting it removes its whole resource policy, and then only the cdkd record is stale — clear " + safeMsg`it with ${stateOrphanRecordRemedy(context, logicalId)}). ` + `Otherwise repair the record's FunctionName in state.json and re-run, or remove the statement by hand ('aws lambda remove-permission'). ${DEPLOY_SKIP_CAVEAT$1}`);
7374
7493
  return {
7375
7494
  outcome: "skipped",
7376
7495
  reason: PERMISSION_FUNCTION_NAME_SKIP_REASON
@@ -8716,9 +8835,11 @@ var LambdaMicrovmImageProvider = class {
8716
8835
  ...buildCommonImageInput(properties)
8717
8836
  };
8718
8837
  if (tagList.length > 0) input.tags = Object.fromEntries(tagList.map((t) => [t.Key, t.Value]));
8838
+ let createdArn;
8719
8839
  try {
8720
8840
  const response = await this.client.send(new CreateMicrovmImageCommand(input));
8721
8841
  const imageArn = response.imageArn;
8842
+ createdArn = imageArn;
8722
8843
  if (!imageArn) throw new ProvisioningError(`CreateMicrovmImage succeeded but no imageArn returned for ${logicalId}`, resourceType, logicalId);
8723
8844
  this.logger.debug(`Created MicroVM image ${logicalId}: ${imageArn} (state=${response.state})`);
8724
8845
  const noWait = process.env["CDKD_NO_WAIT"] === "true";
@@ -8741,7 +8862,9 @@ var LambdaMicrovmImageProvider = class {
8741
8862
  })
8742
8863
  };
8743
8864
  } catch (error) {
8744
- throw this.wrapError("create", logicalId, resourceType, void 0, error);
8865
+ const thrown = this.wrapError("create", logicalId, resourceType, void 0, error);
8866
+ if (createdArn) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdArn);
8867
+ throw thrown;
8745
8868
  }
8746
8869
  }
8747
8870
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -9735,18 +9858,20 @@ var DynamoDBTableProvider = class {
9735
9858
  }
9736
9859
  };
9737
9860
  } catch (error) {
9861
+ let tableLeftBehind = false;
9738
9862
  if (tableCreated) {
9739
9863
  try {
9740
9864
  await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
9741
9865
  debug(`Rolled back partially-created DynamoDB table ${tableName}`);
9742
9866
  } catch (cleanupError) {
9867
+ tableLeftBehind = true;
9743
9868
  warn(`Failed to roll back partially-created DynamoDB table ${tableName}: ${describeAwsFailure(cleanupError).detail}`);
9744
9869
  }
9745
9870
  markAuxiliaryFailure(error, logicalId);
9746
9871
  }
9747
- if (error instanceof ProvisioningError) throw error;
9748
- const cause = error instanceof Error ? error : void 0;
9749
- throw new ProvisioningError(`Failed to create DynamoDB table ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, tableName, cause);
9872
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create DynamoDB table ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, tableName, error instanceof Error ? error : void 0);
9873
+ if (tableLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, tableName);
9874
+ throw thrown;
9750
9875
  }
9751
9876
  }
9752
9877
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -10994,6 +11119,7 @@ var LogsLogGroupProvider = class {
10994
11119
  allowedPattern: /[^a-zA-Z0-9-/_]/g
10995
11120
  })}`;
10996
11121
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
11122
+ let leftBehind = false;
10997
11123
  try {
10998
11124
  const createParams = { logGroupName };
10999
11125
  if (properties["KmsKeyId"]) createParams.kmsKeyId = properties["KmsKeyId"];
@@ -11004,6 +11130,7 @@ var LogsLogGroupProvider = class {
11004
11130
  try {
11005
11131
  await this.logsClient.send(new CreateLogGroupCommand(createParams));
11006
11132
  createdNewLogGroup = true;
11133
+ leftBehind = true;
11007
11134
  } catch (createError) {
11008
11135
  if (createError instanceof ResourceAlreadyExistsException) this.logger.debug(`Log group ${logGroupName} already exists, using existing`);
11009
11136
  else throw createError;
@@ -11049,6 +11176,7 @@ var LogsLogGroupProvider = class {
11049
11176
  } catch (innerError) {
11050
11177
  if (createdNewLogGroup) try {
11051
11178
  await this.logsClient.send(new DeleteLogGroupCommand({ logGroupName }));
11179
+ leftBehind = false;
11052
11180
  this.logger.debug(`Cleaned up partially-created log group ${displaySafe(logicalId)} (${displaySafe(logGroupName)}) after wiring failure`);
11053
11181
  } catch (cleanupError) {
11054
11182
  const mask = maskerOrIdentity(context?.maskSecrets);
@@ -11057,7 +11185,7 @@ var LogsLogGroupProvider = class {
11057
11185
  identifier: logGroupName,
11058
11186
  maskSecrets: mask
11059
11187
  });
11060
- const manualStep = deleteCommand ? `Manual deletion may be required before the next deploy: ${deleteCommand}` : "Manual deletion may be required before the next deploy, via the console: the log group name cannot be reproduced safely on a command line.";
11188
+ const manualStep = deleteCommand ? `On a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise delete it yourself before the next deploy: ${deleteCommand}` : "On a first-time create the failed deploy's rollback journal records it for `cdkd rollback --revert-failed`; otherwise delete it yourself before the next deploy, via the console: the log group name cannot be reproduced safely on a command line.";
11061
11189
  this.logger.warn(mask(`Failed to clean up partially-created log group ${displaySafe(logicalId)} (${displaySafe(mask(logGroupName))}): ${describeAwsFailure(cleanupError).detail}. ${manualStep}`));
11062
11190
  }
11063
11191
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -11070,7 +11198,9 @@ var LogsLogGroupProvider = class {
11070
11198
  } catch (error) {
11071
11199
  const cause = error instanceof Error ? error : void 0;
11072
11200
  const mask = maskerOrIdentity(context?.maskSecrets);
11073
- throw this.wrapMaskedError(mask, error, (text) => new ProvisioningError(`Failed to create log group ${logicalId}: ${text}`, resourceType, logicalId, logGroupName, cause));
11201
+ const thrown = this.wrapMaskedError(mask, error, (text) => new ProvisioningError(`Failed to create log group ${logicalId}: ${text}`, resourceType, logicalId, logGroupName, cause));
11202
+ if (leftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, logGroupName);
11203
+ throw thrown;
11074
11204
  }
11075
11205
  }
11076
11206
  async update(logicalId, physicalId, resourceType, properties, previousProperties, callerContext) {
@@ -11650,7 +11780,7 @@ var CloudWatchAnomalyDetectorProvider = class {
11650
11780
  }
11651
11781
  async delete(logicalId, physicalId, resourceType, properties, context) {
11652
11782
  this.logger.debug(`Deleting CloudWatch anomaly detector ${logicalId}: ${physicalId}`);
11653
- if (!properties) throw new ProvisioningError(`Cannot delete AnomalyDetector ${logicalId}: the state record carries no properties, and DeleteAnomalyDetector addresses the model by its metric descriptor. Use 'cdkd state orphan <stack> --stack-region <region>' to drop the record (that command drops every record the stack has in that region) and delete the detector manually.`, resourceType, logicalId, physicalId);
11783
+ if (!properties) throw new ProvisioningError(`Cannot delete AnomalyDetector ${logicalId}: the state record carries no properties, and DeleteAnomalyDetector addresses the model by its metric descriptor. Delete the detector manually and drop the record with ${stateOrphanRecordRemedy(context, logicalId)}.`, resourceType, logicalId, physicalId);
11654
11784
  const skip = redactedDeleteAddressSkip(this.logger, logicalId, "CloudWatch anomaly detector", redactedDeleteAddressFields({
11655
11785
  Namespace: properties["Namespace"],
11656
11786
  MetricName: properties["MetricName"],
@@ -11658,7 +11788,7 @@ var CloudWatchAnomalyDetectorProvider = class {
11658
11788
  Dimensions: properties["Dimensions"],
11659
11789
  SingleMetricAnomalyDetector: properties["SingleMetricAnomalyDetector"],
11660
11790
  MetricMathAnomalyDetector: properties["MetricMathAnomalyDetector"]
11661
- }));
11791
+ }), context);
11662
11792
  if (skip) return skip;
11663
11793
  try {
11664
11794
  await this.cloudWatchClient.send(new DeleteAnomalyDetectorCommand(buildDeleteParams(properties)));
@@ -12209,6 +12339,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12209
12339
  const type = properties["Type"] || "String";
12210
12340
  const value = properties["Value"];
12211
12341
  if (!value) throw new ProvisioningError(`Value is required for SSM parameter ${displaySafe(logicalId)}`, resourceType, logicalId);
12342
+ let leftBehind = false;
12212
12343
  try {
12213
12344
  const putParams = {
12214
12345
  Name: name,
@@ -12222,6 +12353,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12222
12353
  if (properties["Policies"]) putParams.Policies = properties["Policies"];
12223
12354
  if (properties["DataType"]) putParams.DataType = properties["DataType"];
12224
12355
  await this.ssmClient.send(new PutParameterCommand(putParams));
12356
+ leftBehind = true;
12225
12357
  try {
12226
12358
  if (ssmTags.length > 0) await this.ssmClient.send(new AddTagsToResourceCommand({
12227
12359
  ResourceType: "Parameter",
@@ -12231,6 +12363,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12231
12363
  } catch (innerError) {
12232
12364
  try {
12233
12365
  await this.ssmClient.send(new DeleteParameterCommand({ Name: name }));
12366
+ leftBehind = false;
12234
12367
  debug(`Cleaned up partially-created SSM parameter ${displaySafe(logicalId)} (${displaySafe(mask(name))}) after wiring failure`);
12235
12368
  } catch (cleanupError) {
12236
12369
  const deleteCommand = renderDisableCommand({
@@ -12238,7 +12371,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12238
12371
  identifier: name,
12239
12372
  maskSecrets: mask
12240
12373
  });
12241
- const manualStep = deleteCommand ? `Manual deletion may be required before the next deploy: ${deleteCommand}` : "Manual deletion may be required before the next deploy, via the console: the parameter name cannot be reproduced safely on a command line: a command naming the sanitized form would delete a DIFFERENT parameter, and one naming a name that is not inert unquoted could run part of it as shell.";
12374
+ const manualStep = deleteCommand ? `On a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise delete it yourself before the next deploy: ${deleteCommand}` : "On a first-time create the failed deploy's rollback journal records it for `cdkd rollback --revert-failed`; otherwise delete it yourself before the next deploy, via the console: the parameter name cannot be reproduced safely on a command line: a command naming the sanitized form would delete a DIFFERENT parameter, and one naming a name that is not inert unquoted could run part of it as shell.";
12242
12375
  warn(`Failed to clean up partially-created SSM parameter ${displaySafe(logicalId)} (${displaySafe(mask(name))}): ${describeAwsFailure(cleanupError).detail}. ${manualStep}`);
12243
12376
  }
12244
12377
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -12255,7 +12388,9 @@ var SSMParameterProvider = class SSMParameterProvider {
12255
12388
  };
12256
12389
  } catch (error) {
12257
12390
  const cause = error instanceof Error ? error : void 0;
12258
- throw this.wrapMaskedError(mask, error, (text) => new ProvisioningError(`Failed to create SSM parameter ${displaySafe(logicalId)}: ${text}`, resourceType, logicalId, void 0, cause));
12391
+ const thrown = this.wrapMaskedError(mask, error, (text) => new ProvisioningError(`Failed to create SSM parameter ${displaySafe(logicalId)}: ${text}`, resourceType, logicalId, void 0, cause));
12392
+ if (leftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, name);
12393
+ throw thrown;
12259
12394
  }
12260
12395
  }
12261
12396
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -12533,6 +12668,7 @@ var EventBridgeRuleProvider = class {
12533
12668
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
12534
12669
  const ruleName = properties["Name"] || generateResourceName(logicalId, { maxLength: 64 });
12535
12670
  const targets = properties["Targets"];
12671
+ let leftLiveArn;
12536
12672
  try {
12537
12673
  const heldBefore = targets && targets.length > 0 ? await nameHeldBefore(async () => {
12538
12674
  await this.eventBridgeClient.send(new DescribeRuleCommand({
@@ -12582,6 +12718,7 @@ var EventBridgeRuleProvider = class {
12582
12718
  }));
12583
12719
  this.logger.debug(`Cleaned up partially-created EventBridge rule ${logicalId} (${ruleName}) after wiring failure`);
12584
12720
  } catch (cleanupError) {
12721
+ leftLiveArn = ruleArn;
12585
12722
  this.logger.warn(mask(`Failed to clean up partially-created EventBridge rule ${logicalId} (${ruleName}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy` + (command.text === void 0 ? `: ${command.render()}` : `. If the rule has no targets left, the remove-targets step reports an empty id list; that error is harmless and the delete-rule step still runs: ${command.render()}`)));
12586
12723
  }
12587
12724
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -12592,7 +12729,9 @@ var EventBridgeRuleProvider = class {
12592
12729
  };
12593
12730
  } catch (error) {
12594
12731
  const cause = error instanceof Error ? error : void 0;
12595
- throw new ProvisioningError(`Failed to create EventBridge rule ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, ruleName, cause);
12732
+ const thrown = new ProvisioningError(`Failed to create EventBridge rule ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, ruleName, cause);
12733
+ if (leftLiveArn) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftLiveArn);
12734
+ throw thrown;
12596
12735
  }
12597
12736
  }
12598
12737
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -13474,6 +13613,7 @@ var EC2Provider = class {
13474
13613
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
13475
13614
  const cidrBlock = properties["CidrBlock"];
13476
13615
  if (!cidrBlock) throw new ProvisioningError(`CidrBlock is required for VPC ${logicalId}`, resourceType, logicalId);
13616
+ let leftBehindId;
13477
13617
  try {
13478
13618
  const createClient = await this.getCreateClient();
13479
13619
  const orphanWindow = createVpcLatch.take(logicalId);
@@ -13519,6 +13659,7 @@ var EC2Provider = class {
13519
13659
  await this.ec2Client.send(new DeleteVpcCommand({ VpcId: vpcId }));
13520
13660
  this.logger.debug(`Cleaned up partially-created VPC ${logicalId} (${vpcId}) after wiring failure`);
13521
13661
  } catch (cleanupError) {
13662
+ leftBehindId = vpcId;
13522
13663
  this.logger.warn(`Failed to clean up partially-created VPC ${logicalId} (${vpcId}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand()`aws ec2 delete-vpc --vpc-id ${vpcId}`.render()}`);
13523
13664
  }
13524
13665
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -13534,7 +13675,9 @@ var EC2Provider = class {
13534
13675
  };
13535
13676
  } catch (error) {
13536
13677
  const cause = error instanceof Error ? error : void 0;
13537
- throw new ProvisioningError(`Failed to create VPC ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
13678
+ const thrown = new ProvisioningError(`Failed to create VPC ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
13679
+ if (leftBehindId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindId);
13680
+ throw thrown;
13538
13681
  }
13539
13682
  }
13540
13683
  async updateVpc(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -13634,6 +13777,7 @@ var EC2Provider = class {
13634
13777
  const vpcId = properties["VpcId"];
13635
13778
  const cidrBlock = properties["CidrBlock"];
13636
13779
  if (!vpcId || !cidrBlock) throw new ProvisioningError(`VpcId and CidrBlock are required for Subnet ${logicalId}`, resourceType, logicalId);
13780
+ let leftBehindId;
13637
13781
  try {
13638
13782
  const createClient = await this.getCreateClient();
13639
13783
  const orphanWindow = createSubnetLatch.take(logicalId);
@@ -13668,6 +13812,7 @@ var EC2Provider = class {
13668
13812
  await this.ec2Client.send(new DeleteSubnetCommand({ SubnetId: subnetId }));
13669
13813
  this.logger.debug(`Cleaned up partially-created Subnet ${logicalId} (${subnetId}) after wiring failure`);
13670
13814
  } catch (cleanupError) {
13815
+ leftBehindId = subnetId;
13671
13816
  this.logger.warn(`Failed to clean up partially-created Subnet ${logicalId} (${subnetId}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand()`aws ec2 delete-subnet --subnet-id ${subnetId}`.render()}`);
13672
13817
  }
13673
13818
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -13682,7 +13827,9 @@ var EC2Provider = class {
13682
13827
  };
13683
13828
  } catch (error) {
13684
13829
  const cause = error instanceof Error ? error : void 0;
13685
- throw new ProvisioningError(`Failed to create Subnet ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
13830
+ const thrown = new ProvisioningError(`Failed to create Subnet ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
13831
+ if (leftBehindId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindId);
13832
+ throw thrown;
13686
13833
  }
13687
13834
  }
13688
13835
  async updateSubnet(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -13868,6 +14015,7 @@ var EC2Provider = class {
13868
14015
  let allocationId;
13869
14016
  let publicIp;
13870
14017
  let created = false;
14018
+ let createdId;
13871
14019
  try {
13872
14020
  const input = {
13873
14021
  Domain: requireConfigString(properties["Domain"], "vpc", "AWS::EC2::EIP Domain", replayWarn(this.logger, context)),
@@ -13888,6 +14036,11 @@ var EC2Provider = class {
13888
14036
  created = true;
13889
14037
  allocationId = response.AllocationId;
13890
14038
  publicIp = response.PublicIp;
14039
+ try {
14040
+ createdId = this.eipPhysicalId(logicalId, publicIp, allocationId);
14041
+ } catch {
14042
+ createdId = allocationId;
14043
+ }
13891
14044
  ec2IdsCreatedByThisProcess.add(allocationId);
13892
14045
  await this.applyTags(allocationId, desiredTags, logicalId);
13893
14046
  const instanceId = properties["InstanceId"];
@@ -13905,7 +14058,9 @@ var EC2Provider = class {
13905
14058
  } catch (error) {
13906
14059
  if (created) markAuxiliaryFailure(error, logicalId);
13907
14060
  const cause = error instanceof Error ? error : void 0;
13908
- throw new ProvisioningError(`Failed to create EIP ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
14061
+ const thrown = new ProvisioningError(`Failed to create EIP ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
14062
+ if (createdId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdId);
14063
+ throw thrown;
13909
14064
  }
13910
14065
  return {
13911
14066
  physicalId: this.eipPhysicalId(logicalId, publicIp, allocationId, {
@@ -14050,6 +14205,7 @@ var EC2Provider = class {
14050
14205
  scope: "CreateNatGateway",
14051
14206
  logicalId
14052
14207
  });
14208
+ let createdId;
14053
14209
  try {
14054
14210
  const natGatewayId = (await this.ec2Client.send(new CreateNatGatewayCommand({
14055
14211
  ClientToken: natGatewayToken.value,
@@ -14061,6 +14217,7 @@ var EC2Provider = class {
14061
14217
  SecondaryPrivateIpAddresses: properties["SecondaryPrivateIpAddresses"],
14062
14218
  SecondaryPrivateIpAddressCount: properties["SecondaryPrivateIpAddressCount"]
14063
14219
  }))).NatGateway.NatGatewayId;
14220
+ createdId = natGatewayId;
14064
14221
  await this.applyTags(natGatewayId, desiredTags, logicalId);
14065
14222
  if (process.env["CDKD_NO_WAIT"] !== "true") {
14066
14223
  this.logger.debug(`Waiting for NatGateway ${natGatewayId} to reach available state...`);
@@ -14080,7 +14237,9 @@ var EC2Provider = class {
14080
14237
  };
14081
14238
  } catch (error) {
14082
14239
  const cause = error instanceof Error ? error : void 0;
14083
- throw new ProvisioningError(`Failed to create NatGateway ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
14240
+ const thrown = new ProvisioningError(`Failed to create NatGateway ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
14241
+ if (createdId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdId);
14242
+ throw thrown;
14084
14243
  }
14085
14244
  }
14086
14245
  updateNatGateway(logicalId, physicalId) {
@@ -14306,6 +14465,7 @@ var EC2Provider = class {
14306
14465
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
14307
14466
  const groupDescription = properties["GroupDescription"];
14308
14467
  if (!groupDescription) throw new ProvisioningError(`GroupDescription is required for SecurityGroup ${logicalId}`, resourceType, logicalId);
14468
+ let leftBehindId;
14309
14469
  try {
14310
14470
  const groupId = (await (await this.getCreateClient()).send(new CreateSecurityGroupCommand({
14311
14471
  GroupName: properties["GroupName"] ?? logicalId,
@@ -14330,6 +14490,7 @@ var EC2Provider = class {
14330
14490
  await this.ec2Client.send(new DeleteSecurityGroupCommand({ GroupId: groupId }));
14331
14491
  this.logger.debug(`Cleaned up partially-created SecurityGroup ${logicalId} (${groupId}) after wiring failure`);
14332
14492
  } catch (cleanupError) {
14493
+ leftBehindId = groupId;
14333
14494
  this.logger.warn(`Failed to clean up partially-created SecurityGroup ${logicalId} (${groupId}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand()`aws ec2 delete-security-group --group-id ${groupId}`.render()}`);
14334
14495
  }
14335
14496
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -14344,7 +14505,9 @@ var EC2Provider = class {
14344
14505
  };
14345
14506
  } catch (error) {
14346
14507
  const cause = error instanceof Error ? error : void 0;
14347
- throw new ProvisioningError(`Failed to create SecurityGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
14508
+ const thrown = new ProvisioningError(`Failed to create SecurityGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
14509
+ if (leftBehindId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindId);
14510
+ throw thrown;
14348
14511
  }
14349
14512
  }
14350
14513
  async authorizeInlineIngress(groupId, ingressRules) {
@@ -14732,7 +14895,7 @@ var EC2Provider = class {
14732
14895
  if (keptFor !== void 0) this.logger.info(safeMsg`SecurityGroupIngress ${logicalId}: its previous rule is also held by ${keptFor} of this stack, so the update left it in place`);
14733
14896
  const revoked = keptFor !== void 0 ? void 0 : await this.deleteSecurityGroupIngress(logicalId, physicalId, resourceType, previousProperties, recordedAttributes ? { recordedAttributes } : void 0);
14734
14897
  const revokeSkip = deleteSkipReason(revoked);
14735
- if (revokeSkip !== void 0) throw markNonRetryable(new ProvisioningError(`Cannot update SecurityGroupIngress ${logicalId}: the previous rule could not be revoked because ${revokeSkip === "redacted address property in state — no delete issued" ? "its recorded permission is redacted in state" : "its delete was skipped"}, so the new rule was NOT authorized. Revoke the old rule by hand, then clear the stack's records with 'cdkd state orphan <stack>' — that command drops EVERY record for the stack, not just this one — and re-deploy.`, resourceType, logicalId, physicalId));
14898
+ if (revokeSkip !== void 0) throw markNonRetryable(new ProvisioningError(`Cannot update SecurityGroupIngress ${logicalId}: the previous rule could not be revoked because ${revokeSkip === "redacted address property in state — no delete issued" ? "its recorded permission is redacted in state" : "its delete was skipped"}, so the new rule was NOT authorized. Revoke the old rule by hand, then drop its record with ${stateOrphanRecordRemedy(void 0, logicalId)}; then re-deploy.`, resourceType, logicalId, physicalId));
14736
14899
  const createResult = await this.createSecurityGroupIngress(logicalId, resourceType, properties, (message) => this.logger.warn(message), maskSecrets, replayingState, keptFor !== void 0 ? void 0 : typeof recordedAttributes?.["Id"] === "string" ? recordedAttributes["Id"] : physicalId);
14737
14900
  return {
14738
14901
  physicalId: createResult.physicalId,
@@ -14789,7 +14952,7 @@ var EC2Provider = class {
14789
14952
  SourceSecurityGroupId: properties["SourceSecurityGroupId"],
14790
14953
  SourceSecurityGroupOwnerId: properties["SourceSecurityGroupOwnerId"],
14791
14954
  SourcePrefixListId: properties["SourcePrefixListId"]
14792
- }));
14955
+ }), context);
14793
14956
  if (redactedSkip) return redactedSkip;
14794
14957
  }
14795
14958
  const ipPermission = properties ? this.withoutDescription(this.buildIpPermission(properties)) : {
@@ -14828,6 +14991,7 @@ var EC2Provider = class {
14828
14991
  const imageId = properties["ImageId"];
14829
14992
  if (!imageId) throw new ProvisioningError(`ImageId is required for EC2 Instance ${logicalId}`, resourceType, logicalId);
14830
14993
  const instanceType = requireConfigString(properties["InstanceType"], "t3.micro", "AWS::EC2::Instance InstanceType", replayWarn(this.logger, context));
14994
+ let leftBehindId;
14831
14995
  try {
14832
14996
  const securityGroupIds = properties["SecurityGroupIds"];
14833
14997
  const securityGroups = properties["SecurityGroups"];
@@ -14895,14 +15059,15 @@ var EC2Provider = class {
14895
15059
  runInstancesToken.release();
14896
15060
  this.logger.debug(`Terminate requested for partially-created EC2 Instance ${logicalId} (${instanceId}) after wiring failure (not waiting for terminated state)`);
14897
15061
  } catch (cleanupError) {
15062
+ leftBehindId = instanceId;
14898
15063
  this.logger.warn(`Failed to terminate partially-created EC2 Instance ${logicalId} (${instanceId}): ${describeAwsFailure(cleanupError).detail}. THE INSTANCE IS STILL RUNNING AND BILLING. Manual termination required: ${pasteableAwsCommand(context?.maskSecrets)`aws ec2 terminate-instances --instance-ids ${instanceId}`.render()}`);
14899
15064
  }
14900
15065
  throw markAuxiliaryFailure(innerError, logicalId);
14901
15066
  }
14902
15067
  } catch (error) {
14903
- if (error instanceof ProvisioningError) throw error;
14904
- const cause = error instanceof Error ? error : void 0;
14905
- throw new ProvisioningError(`Failed to create EC2 Instance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
15068
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create EC2 Instance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
15069
+ if (leftBehindId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindId);
15070
+ throw thrown;
14906
15071
  }
14907
15072
  }
14908
15073
  normalizeIamInstanceProfile(raw) {
@@ -15386,7 +15551,10 @@ var EC2Provider = class {
15386
15551
  this.logger.debug(`Deleting NetworkAclEntry ${logicalId}: ${physicalId}`);
15387
15552
  const parts = physicalId.split("|");
15388
15553
  if (parts.length < 3) {
15389
- this.logger.warn(compositeIdFormatMessage(EC2_NETWORK_ACL_ENTRY_ID_FORMAT, logicalId, physicalId, { skipping: true }));
15554
+ this.logger.warn(compositeIdFormatMessage(EC2_NETWORK_ACL_ENTRY_ID_FORMAT, logicalId, physicalId, {
15555
+ skipping: true,
15556
+ context
15557
+ }));
15390
15558
  return compositeIdSkipResult();
15391
15559
  }
15392
15560
  const networkAclId = parts[0];
@@ -16944,6 +17112,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
16944
17112
  const block = properties[key];
16945
17113
  if (block != null && !isPlainObjectBlock(block)) throw new ProvisioningError(`AWS::ApiGateway::Stage ${key} for ${logicalId} must be an object block; got ${Array.isArray(block) ? "an array" : typeof block}. Fix the template value instead of letting cdkd drop the config.`, resourceType, logicalId);
16946
17114
  }
17115
+ let stageLeftBehind = false;
16947
17116
  try {
16948
17117
  const rawCacheClusterSize = properties["CacheClusterSize"];
16949
17118
  await this.apiGatewayClient.send(new CreateStageCommand({
@@ -16993,6 +17162,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
16993
17162
  stageName
16994
17163
  }));
16995
17164
  } catch (cleanupError) {
17165
+ stageLeftBehind = true;
16996
17166
  log.warn(`Failed to clean up stage ${log.value(stageName)} after a post-create patch failure: ${describeAwsFailure(cleanupError).detail}`);
16997
17167
  }
16998
17168
  throw markAuxiliaryFailure(patchError, logicalId);
@@ -17004,7 +17174,9 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17004
17174
  };
17005
17175
  } catch (error) {
17006
17176
  const cause = error instanceof Error ? error : void 0;
17007
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create API Gateway Stage ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
17177
+ const thrown = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create API Gateway Stage ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
17178
+ if (stageLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, stageName);
17179
+ throw thrown;
17008
17180
  }
17009
17181
  }
17010
17182
  async updateStage(logicalId, physicalId, resourceType, properties, previousProperties, log) {
@@ -17210,6 +17382,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17210
17382
  httpMethod
17211
17383
  ].map((id) => log.value(id));
17212
17384
  const methodPath = maskedIds.join("/");
17385
+ let methodLeftBehind = false;
17213
17386
  try {
17214
17387
  await this.apiGatewayClient.send(new PutMethodCommand({
17215
17388
  restApiId,
@@ -17283,6 +17456,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17283
17456
  }));
17284
17457
  log.debug(`Cleaned up partially-created API Gateway Method ${logicalId} (${methodPath}) after wiring failure`);
17285
17458
  } catch (cleanupError) {
17459
+ methodLeftBehind = true;
17286
17460
  log.warn(`Failed to clean up partially-created API Gateway Method ${logicalId} (${methodPath}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand(log.mask)`aws apigateway delete-method --rest-api-id ${restApiId} --resource-id ${resourceId} --http-method ${httpMethod}`.render()}`);
17287
17461
  }
17288
17462
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -17294,7 +17468,9 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17294
17468
  };
17295
17469
  } catch (error) {
17296
17470
  const cause = error instanceof Error ? error : void 0;
17297
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create API Gateway Method ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
17471
+ const thrown = this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create API Gateway Method ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
17472
+ if (methodLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, physicalId);
17473
+ throw thrown;
17298
17474
  }
17299
17475
  }
17300
17476
  async updateMethod(logicalId, physicalId, resourceType, properties, previousProperties, log) {
@@ -21266,7 +21442,9 @@ var ECSProvider = class {
21266
21442
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
21267
21443
  const client = this.getClient();
21268
21444
  const serviceName = properties["ServiceName"] || generateResourceName(logicalId, { maxLength: 255 });
21445
+ let survivorArn;
21269
21446
  try {
21447
+ const heldBefore = process.env["CDKD_FULL_WAIT"] === "true" ? await nameHeldBefore(() => this.serviceNameHeld(properties["Cluster"], serviceName), (error) => hasErrorName(error, ["ClusterNotFoundException"])) : "free";
21270
21448
  const service = (await client.send(new CreateServiceCommand({
21271
21449
  cluster: properties["Cluster"],
21272
21450
  serviceName,
@@ -21304,7 +21482,9 @@ var ECSProvider = class {
21304
21482
  const cleanupCluster = properties["Cluster"];
21305
21483
  const clusterArg = cleanupCluster ? aws` --cluster ${cleanupCluster}` : aws``;
21306
21484
  const listStopped = aws`aws ecs list-tasks${clusterArg} --desired-status STOPPED`;
21307
- try {
21485
+ const deleteCommand = aws`aws ecs delete-service${clusterArg} --service ${service.serviceArn} --force`;
21486
+ if (heldBefore !== "free") log.warn(skippedCleanupText(heldBefore, `ECS service ${logicalIdShown(logicalId)} ${isPlainImportValue(service.serviceArn) ? `(${service.serviceArn})` : VALUE_NOT_SHOWN}`, deleteCommand.render()));
21487
+ else try {
21308
21488
  await client.send(new DeleteServiceCommand({
21309
21489
  cluster: properties["Cluster"],
21310
21490
  service: service.serviceArn,
@@ -21312,7 +21492,8 @@ var ECSProvider = class {
21312
21492
  }));
21313
21493
  log.warn(`Deleted partially-created ECS service ${logicalId} (${service.serviceArn}) after the steady-state wait failed, so the next deploy's CreateService does not collide on the name. Its stopped tasks remain inspectable for about an hour: ${listStopped.render()}`);
21314
21494
  } catch (cleanupError) {
21315
- log.warn(`Failed to clean up partially-created ECS service ${logicalIdShown(logicalId)} ${isPlainImportValue(service.serviceArn) ? `(${service.serviceArn})` : VALUE_NOT_SHOWN}: ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${aws`aws ecs delete-service${clusterArg} --service ${service.serviceArn} --force`.render()}`);
21495
+ survivorArn = service.serviceArn;
21496
+ log.warn(`Failed to clean up partially-created ECS service ${logicalIdShown(logicalId)} ${isPlainImportValue(service.serviceArn) ? `(${service.serviceArn})` : VALUE_NOT_SHOWN}: ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: ${deleteCommand.render()}`);
21316
21497
  }
21317
21498
  throw new Error(`ECS service ${logicalIdShown(logicalId)} did not reach steady state under --full-wait: ${waitError instanceof Error ? waitError.message : String(waitError)}. Inspect why its tasks stopped (stopped tasks stay visible for about an hour): ${listStopped.render()}, then ${aws`aws ecs describe-tasks${clusterArg} --tasks '<task-arn>' --query 'tasks[].[stoppedReason,containers[].reason]'`.render()}`, { cause: waitError });
21318
21499
  }
@@ -21325,9 +21506,24 @@ var ECSProvider = class {
21325
21506
  };
21326
21507
  } catch (error) {
21327
21508
  const cause = error instanceof Error ? error : void 0;
21328
- throw this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to create ECS service ${logicalIdShown(logicalId)}: ${text}`, resourceType, logicalId, serviceName, cause));
21509
+ throw this.wrapMaskedError(log, error, (text) => {
21510
+ const wrapped = new ProvisioningError(`Failed to create ECS service ${logicalIdShown(logicalId)}: ${text}`, resourceType, logicalId, serviceName, cause);
21511
+ if (survivorArn !== void 0) markCreatedBeforeFailure(wrapped, logicalId, resourceType, survivorArn);
21512
+ return wrapped;
21513
+ });
21329
21514
  }
21330
21515
  }
21516
+ async serviceNameHeld(cluster, serviceName) {
21517
+ const response = await this.getClient().send(new DescribeServicesCommand({
21518
+ cluster,
21519
+ services: [serviceName]
21520
+ }));
21521
+ const services = response.services ?? [];
21522
+ const failures = response.failures ?? [];
21523
+ if (services.length === 0 && failures.length === 1 && failures[0]?.reason === "MISSING") return false;
21524
+ if (failures.length > 0 || services.length !== 1) throw new Error("DescribeServices gave no readable answer for the service name");
21525
+ return services[0].status !== "INACTIVE";
21526
+ }
21331
21527
  async settleService(logicalId, serviceRef, cluster, log) {
21332
21528
  if (process.env["CDKD_FULL_WAIT"] === "true") {
21333
21529
  log.debug(`Waiting for ECS service ${logicalId} to reach steady state...`);
@@ -22507,6 +22703,7 @@ var ELBv2Provider = class {
22507
22703
  async createLoadBalancer(logicalId, resourceType, properties, maskSecrets) {
22508
22704
  this.logger.debug(`Creating LoadBalancer ${logicalId}`);
22509
22705
  const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
22706
+ let leftBehindArn;
22510
22707
  try {
22511
22708
  const lbName = sentElbv2Name(properties, logicalId);
22512
22709
  const lbAttributes = this.normalizeAttributes(properties["LoadBalancerAttributes"]);
@@ -22570,6 +22767,7 @@ var ELBv2Provider = class {
22570
22767
  await this.getClient().send(new DeleteLoadBalancerCommand({ LoadBalancerArn: lbArn }));
22571
22768
  this.logger.debug(`Cleaned up partially-created LoadBalancer ${logicalId} (${lbArn}) after wiring failure`);
22572
22769
  } catch (cleanupError) {
22770
+ leftBehindArn = lbArn;
22573
22771
  this.logger.warn(`Failed to clean up partially-created LoadBalancer ${logicalId} (${lbArn}): ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-load-balancer --load-balancer-arn ${lbArn}`.render()}`);
22574
22772
  }
22575
22773
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -22580,7 +22778,9 @@ var ELBv2Provider = class {
22580
22778
  };
22581
22779
  } catch (error) {
22582
22780
  const cause = error instanceof Error ? error : void 0;
22583
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create LoadBalancer ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
22781
+ const thrown = this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create LoadBalancer ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
22782
+ if (leftBehindArn !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindArn);
22783
+ throw thrown;
22584
22784
  }
22585
22785
  }
22586
22786
  async updateLoadBalancer(logicalId, physicalId, resourceType, properties, previousProperties, callerMaskSecrets, fromReadback = false) {
@@ -22788,6 +22988,7 @@ var ELBv2Provider = class {
22788
22988
  const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
22789
22989
  const desiredTargets = readTargets(properties["Targets"], "desired");
22790
22990
  if (desiredTargets.kind === "malformed") throw markNonRetryable(new ProvisioningError(`Targets of TargetGroup ${logicalId} is not a list of ${TARGETS_WHAT}${targetsSecretCause(desiredTargets)} — the target group was not created`, resourceType, logicalId));
22991
+ let leftBehindArn;
22791
22992
  try {
22792
22993
  const matcher = properties["Matcher"];
22793
22994
  const tgName = sentElbv2Name(properties, logicalId);
@@ -22838,6 +23039,7 @@ var ELBv2Provider = class {
22838
23039
  await this.getClient().send(new DeleteTargetGroupCommand({ TargetGroupArn: tgArn }));
22839
23040
  this.logger.debug(`Cleaned up partially-created TargetGroup ${logicalId} (${tgArn}) after wiring failure`);
22840
23041
  } catch (cleanupError) {
23042
+ leftBehindArn = tgArn;
22841
23043
  this.logger.warn(`Failed to clean up partially-created TargetGroup ${logicalId} (${tgArn}): ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-target-group --target-group-arn ${tgArn}`.render()}`);
22842
23044
  }
22843
23045
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -22848,7 +23050,9 @@ var ELBv2Provider = class {
22848
23050
  };
22849
23051
  } catch (error) {
22850
23052
  const cause = error instanceof Error ? error : void 0;
22851
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create TargetGroup ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
23053
+ const thrown = this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create TargetGroup ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
23054
+ if (leftBehindArn !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindArn);
23055
+ throw thrown;
22852
23056
  }
22853
23057
  }
22854
23058
  async updateTargetGroup(logicalId, physicalId, resourceType, properties, previousProperties, callerMaskSecrets, fromReadback = false) {
@@ -22946,14 +23150,30 @@ var ELBv2Provider = class {
22946
23150
  throw new ProvisioningError(`Failed to delete TargetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
22947
23151
  }
22948
23152
  }
23153
+ async listenerPortHeld(loadBalancerArn, port) {
23154
+ let marker;
23155
+ for (let page = 0; page < 100; page++) {
23156
+ const response = await this.getClient().send(new DescribeListenersCommand({
23157
+ LoadBalancerArn: loadBalancerArn,
23158
+ ...marker !== void 0 && { Marker: marker }
23159
+ }));
23160
+ if ((response.Listeners ?? []).some((l) => l.Port === port)) return true;
23161
+ if (!response.NextMarker) return false;
23162
+ marker = response.NextMarker;
23163
+ }
23164
+ throw new Error("DescribeListeners did not finish paging");
23165
+ }
22949
23166
  async createListener(logicalId, resourceType, properties, maskSecrets) {
22950
23167
  this.logger.debug(`Creating Listener ${logicalId}`);
22951
23168
  const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
23169
+ let leftBehindArn;
22952
23170
  try {
22953
23171
  const defaultActions = this.convertActions(properties["DefaultActions"]);
22954
23172
  const certificates = this.convertCertificates(properties["Certificates"]);
22955
23173
  const alpnPolicy = properties["AlpnPolicy"];
22956
23174
  const mutualAuth = properties["MutualAuthentication"];
23175
+ const listenerAttributes = this.normalizeAttributes(properties["ListenerAttributes"]);
23176
+ const heldBefore = listenerAttributes.length > 0 ? await nameHeldBefore(() => this.listenerPortHeld(properties["LoadBalancerArn"], properties["Port"] !== void 0 ? Number(properties["Port"]) : void 0), (error) => hasErrorName(error, ["LoadBalancerNotFoundException"])) : "free";
22957
23177
  const listener = (await this.getClient().send(new CreateListenerCommand({
22958
23178
  LoadBalancerArn: properties["LoadBalancerArn"],
22959
23179
  Port: properties["Port"] !== void 0 ? Number(properties["Port"]) : void 0,
@@ -22969,7 +23189,6 @@ var ELBv2Provider = class {
22969
23189
  const listenerArn = listener.ListenerArn;
22970
23190
  this.logger.debug(`Successfully created Listener ${logicalId}: ${listenerArn}`);
22971
23191
  try {
22972
- const listenerAttributes = this.normalizeAttributes(properties["ListenerAttributes"]);
22973
23192
  if (listenerAttributes.length > 0) {
22974
23193
  const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
22975
23194
  try {
@@ -22987,12 +23206,17 @@ var ELBv2Provider = class {
22987
23206
  this.logger.debug(`Applied ${listenerAttributes.length} ListenerAttribute(s) for ${logicalId}`);
22988
23207
  }
22989
23208
  } catch (innerError) {
22990
- if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating Listener ${logicalId} (${listenerArn}) but before its attributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy with DuplicateListener and neither rollback nor destroy could reach it. If that delete does not complete, remove it manually: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-listener --listener-arn ${listenerArn}`.render()}`));
23209
+ if (heldBefore !== "free") {
23210
+ this.logger.warn(maskerOrIdentity(maskSecrets)(skippedCleanupText(heldBefore, `Listener ${logicalId} (${listenerArn})`, pasteableAwsCommand(maskSecrets)`aws elbv2 delete-listener --listener-arn ${listenerArn}`.render())));
23211
+ throw markAuxiliaryFailure(innerError, logicalId);
23212
+ }
23213
+ if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating Listener ${logicalId} (${listenerArn}) but before its attributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy with DuplicateListener and destroy could not reach it. If that delete fails, on a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise remove it yourself: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-listener --listener-arn ${listenerArn}`.render()}`));
22991
23214
  try {
22992
23215
  await this.getClient().send(new DeleteListenerCommand({ ListenerArn: listenerArn }));
22993
23216
  this.logger.debug(`Cleaned up partially-created Listener ${logicalId} (${listenerArn}) after attributes-wiring failure`);
22994
23217
  } catch (cleanupError) {
22995
- this.logger.warn(`Failed to clean up partially-created Listener ${logicalId} (${listenerArn}): ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-listener --listener-arn ${listenerArn}`.render()}`);
23218
+ leftBehindArn = listenerArn;
23219
+ this.logger.warn(`Failed to clean up partially-created Listener ${logicalId} (${listenerArn}): ${this.maskErrorMessage(cleanupError, maskSecrets)}. On a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise delete it yourself before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-listener --listener-arn ${listenerArn}`.render()}`);
22996
23220
  }
22997
23221
  throw markAuxiliaryFailure(innerError, logicalId);
22998
23222
  }
@@ -23002,7 +23226,9 @@ var ELBv2Provider = class {
23002
23226
  };
23003
23227
  } catch (error) {
23004
23228
  const cause = error instanceof Error ? error : void 0;
23005
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create Listener ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
23229
+ const thrown = this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create Listener ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
23230
+ if (leftBehindArn !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindArn);
23231
+ throw thrown;
23006
23232
  }
23007
23233
  }
23008
23234
  async updateListener(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets, fromReadback = false) {
@@ -23534,24 +23760,29 @@ var RDSDBProxyProvider = class {
23534
23760
  let vpcId;
23535
23761
  const deadline = Date.now() + POLL_TIMEOUT_MS$1;
23536
23762
  let status;
23537
- while (Date.now() < deadline) {
23538
- try {
23539
- const proxy = (await client.send(new DescribeDBProxiesCommand({ DBProxyName: dbProxyName }))).DBProxies?.[0];
23540
- status = proxy?.Status;
23541
- if (status === "available") {
23542
- endpoint = proxy?.Endpoint;
23543
- dbProxyArn = proxy?.DBProxyArn;
23544
- vpcId = proxy?.VpcId;
23545
- break;
23763
+ try {
23764
+ while (Date.now() < deadline) {
23765
+ try {
23766
+ const proxy = (await client.send(new DescribeDBProxiesCommand({ DBProxyName: dbProxyName }))).DBProxies?.[0];
23767
+ status = proxy?.Status;
23768
+ if (status === "available") {
23769
+ endpoint = proxy?.Endpoint;
23770
+ dbProxyArn = proxy?.DBProxyArn;
23771
+ vpcId = proxy?.VpcId;
23772
+ break;
23773
+ }
23774
+ if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxy ${dbProxyName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyName);
23775
+ } catch (error) {
23776
+ if (error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
23777
+ else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyName);
23546
23778
  }
23547
- if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxy ${dbProxyName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyName);
23548
- } catch (error) {
23549
- if (error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
23550
- else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyName);
23779
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS$1));
23551
23780
  }
23552
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS$1));
23781
+ if (!endpoint || !dbProxyArn) throw new ProvisioningError(`Timed out waiting for DBProxy ${dbProxyName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyName);
23782
+ } catch (error) {
23783
+ markCreatedBeforeFailure(error, logicalId, resourceType, dbProxyName);
23784
+ throw error;
23553
23785
  }
23554
- if (!endpoint || !dbProxyArn) throw new ProvisioningError(`Timed out waiting for DBProxy ${dbProxyName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyName);
23555
23786
  return {
23556
23787
  physicalId: dbProxyName,
23557
23788
  attributes: definedAttributes({
@@ -23832,26 +24063,31 @@ var RDSDBProxyEndpointProvider = class {
23832
24063
  let vpcId;
23833
24064
  const deadline = Date.now() + POLL_TIMEOUT_MS;
23834
24065
  let status;
23835
- while (Date.now() < deadline) {
23836
- try {
23837
- const ep = (await client.send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: dbProxyEndpointName }))).DBProxyEndpoints?.[0];
23838
- status = ep?.Status;
23839
- this.logger.debug(`DBProxyEndpoint ${dbProxyEndpointName} poll: status=${status ?? "not-yet-visible"}`);
23840
- if (status === "available") {
23841
- endpoint = ep?.Endpoint;
23842
- arn = ep?.DBProxyEndpointArn;
23843
- isDefault = ep?.IsDefault;
23844
- vpcId = ep?.VpcId;
23845
- break;
24066
+ try {
24067
+ while (Date.now() < deadline) {
24068
+ try {
24069
+ const ep = (await client.send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: dbProxyEndpointName }))).DBProxyEndpoints?.[0];
24070
+ status = ep?.Status;
24071
+ this.logger.debug(`DBProxyEndpoint ${dbProxyEndpointName} poll: status=${status ?? "not-yet-visible"}`);
24072
+ if (status === "available") {
24073
+ endpoint = ep?.Endpoint;
24074
+ arn = ep?.DBProxyEndpointArn;
24075
+ isDefault = ep?.IsDefault;
24076
+ vpcId = ep?.VpcId;
24077
+ break;
24078
+ }
24079
+ if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxyEndpoint ${dbProxyEndpointName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyEndpointName);
24080
+ } catch (error) {
24081
+ if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
24082
+ else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyEndpointName);
23846
24083
  }
23847
- if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxyEndpoint ${dbProxyEndpointName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyEndpointName);
23848
- } catch (error) {
23849
- if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
23850
- else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyEndpointName);
24084
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
23851
24085
  }
23852
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
24086
+ if (!endpoint || !arn) throw new ProvisioningError(`Timed out waiting for DBProxyEndpoint ${dbProxyEndpointName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyEndpointName);
24087
+ } catch (error) {
24088
+ markCreatedBeforeFailure(error, logicalId, resourceType, dbProxyEndpointName);
24089
+ throw error;
23853
24090
  }
23854
- if (!endpoint || !arn) throw new ProvisioningError(`Timed out waiting for DBProxyEndpoint ${dbProxyEndpointName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyEndpointName);
23855
24091
  return {
23856
24092
  physicalId: dbProxyEndpointName,
23857
24093
  attributes: definedAttributes({
@@ -24176,7 +24412,11 @@ var RDSDBProxyTargetGroupProvider = class {
24176
24412
  Tags: tags
24177
24413
  }));
24178
24414
  } catch (error) {
24179
- throw await this.retireRegistrationAfterFailedCreate(this.wrapError(error, "CREATE (add tags)", resourceType, logicalId, void 0), dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, context?.maskSecrets);
24415
+ throw await this.retireRegistrationAfterFailedCreate(this.wrapError(error, "CREATE (add tags)", resourceType, logicalId, void 0), dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, context?.maskSecrets, {
24416
+ logicalId,
24417
+ resourceType,
24418
+ physicalId: targetGroupArn
24419
+ });
24180
24420
  }
24181
24421
  return {
24182
24422
  physicalId: targetGroupArn,
@@ -24208,8 +24448,10 @@ var RDSDBProxyTargetGroupProvider = class {
24208
24448
  }
24209
24449
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId, "Tags", DBPROXY_TAGS_WHAT, DBPROXY_TAG_OPTIONS);
24210
24450
  if (context?.expectedRegion) assertRegionMatch(await this.getClient().config.region(), context.expectedRegion, resourceType, logicalId, physicalId, "pre-update");
24211
- if (exemptedBySecretReference.length > 0) {
24212
- const fields = exemptedBySecretReference.join(" / ");
24451
+ const replayCheck = exemptedBySecretReference.length === 0 && context?.replayingState === true;
24452
+ if (exemptedBySecretReference.length > 0 || replayCheck) {
24453
+ const fields = replayCheck ? "DBProxyName / TargetGroupName" : exemptedBySecretReference.join(" / ");
24454
+ const provenance = replayCheck ? "replayed by a rollback, which resolves any secret they come from again" : "secret-derived";
24213
24455
  const base = context?.maskSecrets ?? ((t) => t);
24214
24456
  const maskNames = (text) => {
24215
24457
  let out = text;
@@ -24232,11 +24474,11 @@ var RDSDBProxyTargetGroupProvider = class {
24232
24474
  }
24233
24475
  if (lookupFailure !== void 0) {
24234
24476
  const failureClass = lookupFailure instanceof Error && lookupFailure.name !== "" ? lookupFailure.name : "an unreadable failure";
24235
- const refusal = new ProvisioningError(`${fields} of AWS::RDS::DBProxyTargetGroup ${logicalId} is secret-derived, and whether the value its secret resolves to still addresses the recorded target group could not be confirmed (${maskNames(failureClass)}) — re-run once the lookup can succeed`, resourceType, logicalId, physicalId, lookupFailure instanceof Error ? lookupFailure : void 0);
24477
+ const refusal = new ProvisioningError(`${fields} of AWS::RDS::DBProxyTargetGroup ${logicalId} is ${provenance}, and whether ${replayCheck ? "the names still address" : "the value its secret resolves to still addresses"} the recorded target group could not be confirmed (${maskNames(failureClass)}) — re-run once the lookup can succeed`, resourceType, logicalId, physicalId, lookupFailure instanceof Error ? lookupFailure : void 0);
24236
24478
  markRedactedCause(refusal);
24237
24479
  throw refusal;
24238
24480
  }
24239
- if (liveArn !== physicalId) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `${fields} is immutable on AWS::RDS::DBProxyTargetGroup, and the value its secret now resolves to ${liveArn === void 0 ? "addresses no target group" : "addresses a different target group"} (the secret may have been rotated) — destroy + redeploy to change it`);
24481
+ if (liveArn !== physicalId) throw markNonRetryable(new ProvisioningError(`${fields} of AWS::RDS::DBProxyTargetGroup ${logicalId} is ${provenance}, and ${replayCheck ? "the names now address" : "the value its secret now resolves to addresses"} ${liveArn === void 0 ? "no target group" : "a different target group"} (${replayCheck ? "the proxy, or a secret its name comes from, may have changed" : "the secret may have been rotated"}). cdkd does not move registered targets to another proxy, with or without --replace: ${replayCheck ? "make the names address the proxy that holds this target group again" : "restore the secret's value to the proxy that holds this target group"}`, resourceType, logicalId, physicalId));
24240
24482
  }
24241
24483
  const next = readTargetLists({
24242
24484
  DBClusterIdentifiers: properties["DBClusterIdentifiers"],
@@ -24470,7 +24712,7 @@ var RDSDBProxyTargetGroupProvider = class {
24470
24712
  }
24471
24713
  return result;
24472
24714
  }
24473
- async retireRegistrationAfterFailedCreate(original, dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, maskSecrets) {
24715
+ async retireRegistrationAfterFailedCreate(original, dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, maskSecrets, leftBehind) {
24474
24716
  const clusters = dbClusterIdentifiers?.length ? dbClusterIdentifiers : void 0;
24475
24717
  const instances = dbInstanceIdentifiers?.length ? dbInstanceIdentifiers : void 0;
24476
24718
  if (!clusters && !instances) return original;
@@ -24494,6 +24736,7 @@ var RDSDBProxyTargetGroupProvider = class {
24494
24736
  for (const id of instances) targets = aws`${targets} ${id}`;
24495
24737
  }
24496
24738
  original.message += ` The targets this create registered are still registered and could not be deregistered (${describeAwsFailure(cleanupError).detail}). Manual cleanup: ` + aws`aws rds deregister-db-proxy-targets --db-proxy-name ${dbProxyName} --target-group-name ${targetGroupName}${targets}`.render();
24739
+ if (leftBehind) markCreatedBeforeFailure(original, leftBehind.logicalId, leftBehind.resourceType, leftBehind.physicalId);
24497
24740
  return original;
24498
24741
  }
24499
24742
  }
@@ -24680,8 +24923,9 @@ var DocDBProvider = class {
24680
24923
  maxLength: 63,
24681
24924
  lowercase: true
24682
24925
  });
24926
+ let clusterCreated = false;
24683
24927
  try {
24684
- if (!(await this.getClient().send(new CreateDBClusterCommand$1({
24928
+ const response = await this.getClient().send(new CreateDBClusterCommand$1({
24685
24929
  DBClusterIdentifier: dbClusterIdentifier,
24686
24930
  Engine: "docdb",
24687
24931
  EngineVersion: properties["EngineVersion"],
@@ -24698,7 +24942,9 @@ var DocDBProvider = class {
24698
24942
  DBClusterParameterGroupName: properties["DBClusterParameterGroupName"],
24699
24943
  DeletionProtection: properties["DeletionProtection"],
24700
24944
  ...tags.length > 0 && { Tags: tags }
24701
- }))).DBCluster) throw new Error("CreateDBCluster did not return DBCluster");
24945
+ }));
24946
+ clusterCreated = true;
24947
+ if (!response.DBCluster) throw new Error("CreateDBCluster did not return DBCluster");
24702
24948
  this.logger.debug(`Successfully created DocDB DBCluster ${logicalId}: ${dbClusterIdentifier}`);
24703
24949
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(dbClusterIdentifier);
24704
24950
  return {
@@ -24706,9 +24952,9 @@ var DocDBProvider = class {
24706
24952
  attributes: clusterAttributes(await this.describeDBCluster(dbClusterIdentifier))
24707
24953
  };
24708
24954
  } catch (error) {
24709
- if (error instanceof ProvisioningError) throw error;
24710
- const cause = error instanceof Error ? error : void 0;
24711
- throw new ProvisioningError(`Failed to create DocDB DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, dbClusterIdentifier, cause);
24955
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create DocDB DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, dbClusterIdentifier, error instanceof Error ? error : void 0);
24956
+ if (clusterCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, dbClusterIdentifier);
24957
+ throw thrown;
24712
24958
  }
24713
24959
  }
24714
24960
  async updateDBCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -24823,8 +25069,9 @@ var DocDBProvider = class {
24823
25069
  maxLength: 63,
24824
25070
  lowercase: true
24825
25071
  });
25072
+ let instanceCreated = false;
24826
25073
  try {
24827
- if (!(await this.getClient().send(new CreateDBInstanceCommand$1({
25074
+ const response = await this.getClient().send(new CreateDBInstanceCommand$1({
24828
25075
  DBInstanceIdentifier: dbInstanceIdentifier,
24829
25076
  DBInstanceClass: properties["DBInstanceClass"],
24830
25077
  Engine: "docdb",
@@ -24833,7 +25080,9 @@ var DocDBProvider = class {
24833
25080
  PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
24834
25081
  AutoMinorVersionUpgrade: properties["AutoMinorVersionUpgrade"],
24835
25082
  ...tags.length > 0 && { Tags: tags }
24836
- }))).DBInstance) throw new Error("CreateDBInstance did not return DBInstance");
25083
+ }));
25084
+ instanceCreated = true;
25085
+ if (!response.DBInstance) throw new Error("CreateDBInstance did not return DBInstance");
24837
25086
  this.logger.debug(`Successfully created DocDB DBInstance ${logicalId}: ${dbInstanceIdentifier}`);
24838
25087
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForInstanceAvailable(dbInstanceIdentifier);
24839
25088
  return {
@@ -24841,9 +25090,9 @@ var DocDBProvider = class {
24841
25090
  attributes: instanceAttributes(await this.describeDBInstance(dbInstanceIdentifier))
24842
25091
  };
24843
25092
  } catch (error) {
24844
- if (error instanceof ProvisioningError) throw error;
24845
- const cause = error instanceof Error ? error : void 0;
24846
- throw new ProvisioningError(`Failed to create DocDB DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, dbInstanceIdentifier, cause);
25093
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create DocDB DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, dbInstanceIdentifier, error instanceof Error ? error : void 0);
25094
+ if (instanceCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, dbInstanceIdentifier);
25095
+ throw thrown;
24847
25096
  }
24848
25097
  }
24849
25098
  async updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -25315,6 +25564,8 @@ var Route53Provider = class {
25315
25564
  const name = properties["Name"];
25316
25565
  if (!name) throw new ProvisioningError(`Name is required for hosted zone ${logicalId}`, resourceType, logicalId);
25317
25566
  let zoneCreated = false;
25567
+ let createdZoneId;
25568
+ let zoneRolledBack = false;
25318
25569
  try {
25319
25570
  const hostedZoneConfig = properties["HostedZoneConfig"];
25320
25571
  const createComment = readConfigString(hostedZoneConfig, "Comment", "", "AWS::Route53::HostedZone HostedZoneConfig");
@@ -25341,6 +25592,7 @@ var Route53Provider = class {
25341
25592
  const hostedZone = response.HostedZone;
25342
25593
  if (!hostedZone?.Id) throw new Error("CreateHostedZone did not return HostedZone.Id");
25343
25594
  const zoneId = hostedZone.Id.replace("/hostedzone/", "");
25595
+ createdZoneId = zoneId;
25344
25596
  if (vpcs && vpcs.length > 1) for (let i = 1; i < vpcs.length; i++) {
25345
25597
  const additionalVpc = vpcs[i];
25346
25598
  this.logger.debug(`Associating additional VPC ${String(additionalVpc["VPCId"])} with hosted zone ${zoneId}`);
@@ -25364,6 +25616,7 @@ var Route53Provider = class {
25364
25616
  try {
25365
25617
  await this.deleteQueryLoggingConfigForZone(zoneId, logicalId);
25366
25618
  await this.getClient().send(new DeleteHostedZoneCommand({ Id: zoneId }));
25619
+ zoneRolledBack = true;
25367
25620
  callerReferenceToken.release();
25368
25621
  } catch (deleteError) {
25369
25622
  this.logger.warn(`Best-effort rollback DeleteHostedZone for ${zoneId} also failed: ${describeAwsFailure(deleteError).detail} — operator may need to clean up the orphan zone`);
@@ -25383,9 +25636,9 @@ var Route53Provider = class {
25383
25636
  };
25384
25637
  } catch (error) {
25385
25638
  if (zoneCreated) markAuxiliaryFailure(error, logicalId);
25386
- if (error instanceof ProvisioningError) throw error;
25387
- const cause = error instanceof Error ? error : void 0;
25388
- throw new ProvisioningError(`Failed to create hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
25639
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
25640
+ if (createdZoneId !== void 0 && !zoneRolledBack) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdZoneId);
25641
+ throw thrown;
25389
25642
  }
25390
25643
  }
25391
25644
  async createOrAdoptHostedZone(input) {
@@ -27025,17 +27278,21 @@ var CognitoUserPoolProvider = class {
27025
27278
  ...effectiveProperties !== properties ? { effectiveProperties } : {}
27026
27279
  };
27027
27280
  } catch (error) {
27281
+ let poolLeftBehind = false;
27028
27282
  if (createdUserPoolId) {
27029
27283
  markAuxiliaryFailure(error, logicalId);
27030
27284
  try {
27031
27285
  await this.getClient().send(new DeleteUserPoolCommand({ UserPoolId: createdUserPoolId }));
27032
27286
  this.logger.debug(`Rolled back partially-created Cognito User Pool ${createdUserPoolId}`);
27033
27287
  } catch (rollbackError) {
27288
+ poolLeftBehind = true;
27034
27289
  this.logger.warn(`Failed to roll back partially-created Cognito User Pool ${createdUserPoolId}: ${describeAwsFailure(rollbackError).detail}`);
27035
27290
  }
27036
27291
  }
27037
27292
  const cause = error instanceof Error ? error : void 0;
27038
- throw new ProvisioningError(`Failed to create Cognito User Pool ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, poolName, cause);
27293
+ const thrown = new ProvisioningError(`Failed to create Cognito User Pool ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, poolName, cause);
27294
+ if (poolLeftBehind && createdUserPoolId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdUserPoolId);
27295
+ throw thrown;
27039
27296
  }
27040
27297
  }
27041
27298
  async regionArg(aws) {
@@ -27756,6 +28013,7 @@ var ElastiCacheProvider = class {
27756
28013
  maxLength: 40,
27757
28014
  lowercase: true
27758
28015
  });
28016
+ let clusterCreated = false;
27759
28017
  try {
27760
28018
  await this.getClient().send(new CreateCacheClusterCommand({
27761
28019
  CacheClusterId: cacheClusterId,
@@ -27782,6 +28040,7 @@ var ElastiCacheProvider = class {
27782
28040
  TransitEncryptionEnabled: properties["TransitEncryptionEnabled"],
27783
28041
  ...tags.length > 0 && { Tags: tags }
27784
28042
  }));
28043
+ clusterCreated = true;
27785
28044
  this.logger.debug(`Successfully created CacheCluster ${logicalId}: ${cacheClusterId}`);
27786
28045
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(cacheClusterId);
27787
28046
  const described = await this.describeCacheCluster(cacheClusterId);
@@ -27801,9 +28060,9 @@ var ElastiCacheProvider = class {
27801
28060
  attributes
27802
28061
  };
27803
28062
  } catch (error) {
27804
- if (error instanceof ProvisioningError) throw error;
27805
- const cause = error instanceof Error ? error : void 0;
27806
- throw new ProvisioningError(`Failed to create CacheCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, cacheClusterId, cause);
28063
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create CacheCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, cacheClusterId, error instanceof Error ? error : void 0);
28064
+ if (clusterCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, cacheClusterId);
28065
+ throw thrown;
27807
28066
  }
27808
28067
  }
27809
28068
  async updateCacheCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -28208,6 +28467,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28208
28467
  if (!name) throw new ProvisioningError(`Name is required for PrivateDnsNamespace ${logicalId}`, resourceType, logicalId);
28209
28468
  if (!vpc) throw new ProvisioningError(`Vpc is required for PrivateDnsNamespace ${logicalId}`, resourceType, logicalId);
28210
28469
  const inputProperties = this.extractSoaTtlProperties(properties);
28470
+ let createdNamespaceId;
28211
28471
  try {
28212
28472
  const operationId = (await client.send(new CreatePrivateDnsNamespaceCommand({
28213
28473
  Name: name,
@@ -28218,6 +28478,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28218
28478
  }))).OperationId;
28219
28479
  if (!operationId) throw new Error("CreatePrivateDnsNamespace did not return OperationId");
28220
28480
  const namespaceId = await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
28481
+ if (namespaceId !== operationId) createdNamespaceId = namespaceId;
28221
28482
  const arn = await this.buildNamespaceArn(namespaceId);
28222
28483
  this.logger.debug(`Successfully created private DNS namespace ${logicalId}: ${namespaceId}`);
28223
28484
  return {
@@ -28228,9 +28489,10 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28228
28489
  }
28229
28490
  };
28230
28491
  } catch (error) {
28231
- if (error instanceof ProvisioningError) throw error;
28232
28492
  const cause = error instanceof Error ? error : void 0;
28233
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create private DNS namespace ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28493
+ const thrown = error instanceof ProvisioningError ? error : this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create private DNS namespace ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28494
+ if (createdNamespaceId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdNamespaceId);
28495
+ throw thrown;
28234
28496
  }
28235
28497
  }
28236
28498
  async updateNamespace(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -28303,6 +28565,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28303
28565
  const name = properties["Name"];
28304
28566
  const description = properties["Description"];
28305
28567
  if (!name) throw new ProvisioningError(`Name is required for HttpNamespace ${logicalId}`, resourceType, logicalId);
28568
+ let createdNamespaceId;
28306
28569
  try {
28307
28570
  const operationId = (await client.send(new CreateHttpNamespaceCommand({
28308
28571
  Name: name,
@@ -28311,6 +28574,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28311
28574
  }))).OperationId;
28312
28575
  if (!operationId) throw new Error("CreateHttpNamespace did not return OperationId");
28313
28576
  const namespaceId = await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
28577
+ if (namespaceId !== operationId) createdNamespaceId = namespaceId;
28314
28578
  const arn = await this.resolveNamespaceArn(namespaceId);
28315
28579
  this.logger.debug(`Successfully created HTTP namespace ${logicalId}: ${namespaceId}`);
28316
28580
  return {
@@ -28321,9 +28585,10 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28321
28585
  }
28322
28586
  };
28323
28587
  } catch (error) {
28324
- if (error instanceof ProvisioningError) throw error;
28325
28588
  const cause = error instanceof Error ? error : void 0;
28326
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create HTTP namespace ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28589
+ const thrown = error instanceof ProvisioningError ? error : this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create HTTP namespace ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28590
+ if (createdNamespaceId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdNamespaceId);
28591
+ throw thrown;
28327
28592
  }
28328
28593
  }
28329
28594
  async updateHttpNamespace(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -28360,6 +28625,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28360
28625
  const description = properties["Description"];
28361
28626
  if (!name) throw new ProvisioningError(`Name is required for PublicDnsNamespace ${logicalId}`, resourceType, logicalId);
28362
28627
  const inputProperties = this.extractSoaTtlProperties(properties);
28628
+ let createdNamespaceId;
28363
28629
  try {
28364
28630
  const operationId = (await client.send(new CreatePublicDnsNamespaceCommand({
28365
28631
  Name: name,
@@ -28369,6 +28635,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28369
28635
  }))).OperationId;
28370
28636
  if (!operationId) throw new Error("CreatePublicDnsNamespace did not return OperationId");
28371
28637
  const namespaceId = await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
28638
+ if (namespaceId !== operationId) createdNamespaceId = namespaceId;
28372
28639
  let arn;
28373
28640
  let hostedZoneId;
28374
28641
  try {
@@ -28389,9 +28656,10 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28389
28656
  }
28390
28657
  };
28391
28658
  } catch (error) {
28392
- if (error instanceof ProvisioningError) throw error;
28393
28659
  const cause = error instanceof Error ? error : void 0;
28394
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create public DNS namespace ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28660
+ const thrown = error instanceof ProvisioningError ? error : this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create public DNS namespace ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28661
+ if (createdNamespaceId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdNamespaceId);
28662
+ throw thrown;
28395
28663
  }
28396
28664
  }
28397
28665
  async updatePublicDnsNamespace(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -28435,6 +28703,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28435
28703
  const healthCheckConfig = properties["HealthCheckConfig"];
28436
28704
  const type = properties["Type"];
28437
28705
  if (!name) throw new ProvisioningError(`Name is required for ServiceDiscovery Service ${logicalId}`, resourceType, logicalId);
28706
+ let leftBehindServiceId;
28438
28707
  try {
28439
28708
  const service = (await client.send(new CreateServiceCommand$1({
28440
28709
  Name: name,
@@ -28448,6 +28717,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28448
28717
  }))).Service;
28449
28718
  if (!service || !service.Id) throw new Error("CreateService did not return Service ID");
28450
28719
  const serviceId = service.Id;
28720
+ leftBehindServiceId = serviceId;
28451
28721
  this.logger.debug(`Successfully created service discovery service ${logicalId}: ${serviceId}`);
28452
28722
  try {
28453
28723
  const attrs = this.normalizeServiceAttributes(properties["ServiceAttributes"]);
@@ -28468,12 +28738,13 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28468
28738
  this.logger.debug(`Applied ${Object.keys(attrs).length} ServiceAttribute(s) for ${logicalId}`);
28469
28739
  }
28470
28740
  } catch (innerError) {
28471
- if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating ServiceDiscovery Service ${logicalId} (${serviceId}) but before its ServiceAttributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy on a name collision AND block deletion of its namespace with ResourceInUse. If that delete does not complete, remove it manually: ${pasteableAwsCommand(maskSecrets)`aws servicediscovery delete-service --id ${serviceId}`.render()}`));
28741
+ if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating ServiceDiscovery Service ${logicalId} (${serviceId}) but before its ServiceAttributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy on a name collision AND block deletion of its namespace with ResourceInUse. If that delete fails, on a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise remove it yourself: ${pasteableAwsCommand(maskSecrets)`aws servicediscovery delete-service --id ${serviceId}`.render()}`));
28472
28742
  try {
28473
28743
  await client.send(new DeleteServiceCommand$1({ Id: serviceId }));
28744
+ leftBehindServiceId = void 0;
28474
28745
  this.logger.debug(`Cleaned up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure`);
28475
28746
  } catch (cleanupError) {
28476
- this.logger.warn(`Failed to clean up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure: ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws servicediscovery delete-service --id ${serviceId}`.render()}`);
28747
+ this.logger.warn(`Failed to clean up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure: ${this.maskErrorMessage(cleanupError, maskSecrets)}. On a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise delete it yourself before the next deploy: ${pasteableAwsCommand(maskSecrets)`aws servicediscovery delete-service --id ${serviceId}`.render()}`);
28477
28748
  }
28478
28749
  throw markAuxiliaryFailure(innerError, logicalId);
28479
28750
  }
@@ -28487,7 +28758,9 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28487
28758
  };
28488
28759
  } catch (error) {
28489
28760
  const cause = error instanceof Error ? error : void 0;
28490
- throw this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create service discovery service ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28761
+ const thrown = this.wrapMaskedError(maskerOrIdentity(maskSecrets), error, (text) => new ProvisioningError(`Failed to create service discovery service ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
28762
+ if (leftBehindServiceId) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftBehindServiceId);
28763
+ throw thrown;
28491
28764
  }
28492
28765
  }
28493
28766
  async updateService(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -29797,17 +30070,21 @@ var AppSyncProvider = class {
29797
30070
  ...envRecord && { effectiveProperties: this.withEnvironmentVariablesRecord(properties, envRecord) }
29798
30071
  };
29799
30072
  } catch (error) {
30073
+ let apiLeftBehind = false;
29800
30074
  if (createdApiId) {
29801
30075
  markAuxiliaryFailure(error, logicalId);
29802
30076
  try {
29803
30077
  await this.getClient().send(new DeleteGraphqlApiCommand({ apiId: createdApiId }));
29804
30078
  this.logger.debug(`Rolled back partially-created GraphQL API ${createdApiId}`);
29805
30079
  } catch (rollbackError) {
30080
+ apiLeftBehind = true;
29806
30081
  this.logger.warn(`Failed to roll back partially-created GraphQL API ${createdApiId}: ${describeAwsFailure(rollbackError).detail}`);
29807
30082
  }
29808
30083
  }
29809
30084
  const cause = error instanceof Error ? error : void 0;
29810
- throw new ProvisioningError(`Failed to create GraphQL API ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
30085
+ const thrown = new ProvisioningError(`Failed to create GraphQL API ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
30086
+ if (apiLeftBehind && createdApiId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdApiId);
30087
+ throw thrown;
29811
30088
  }
29812
30089
  }
29813
30090
  async regionArg(aws) {
@@ -32730,10 +33007,12 @@ var KMSProvider = class {
32730
33007
  markAuxiliaryFailure(error, logicalId);
32731
33008
  const aws = pasteableAwsCommand();
32732
33009
  const region = await this.regionArg(aws);
32733
- this.logger.warn(safeMsg`KMS key ${createdKeyId} was created for ${logicalId}, but a follow-up call failed. A retry of this create reuses that key instead of creating another, so do not delete it while the deploy is still retrying. Only if the deploy then FAILS is the key left unrecorded in cdkd state; delete it then with: ${aws`aws kms schedule-key-deletion --key-id ${createdKeyId}${region} --pending-window-in-days 7`.render()}`);
33010
+ this.logger.warn(safeMsg`KMS key ${createdKeyId} was created for ${logicalId}, but a follow-up call failed. A retry of this create reuses that key instead of creating another, so do not delete it while the deploy is still retrying. If the deploy then FAILS on a first-time create, its rollback journal records the key for \`cdkd rollback --revert-failed\` to schedule its deletion; otherwise schedule it yourself with: ${aws`aws kms schedule-key-deletion --key-id ${createdKeyId}${region} --pending-window-in-days 7`.render()}`);
32734
33011
  }
32735
33012
  const cause = error instanceof Error ? error : void 0;
32736
- throw new ProvisioningError(`Failed to create KMS Key ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
33013
+ const thrown = new ProvisioningError(`Failed to create KMS Key ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
33014
+ if (createdKeyId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdKeyId);
33015
+ throw thrown;
32737
33016
  }
32738
33017
  }
32739
33018
  async regionArg(aws) {
@@ -33295,6 +33574,7 @@ var BudgetsBudgetProvider = class {
33295
33574
  const accountId = await this.resolveAccountId();
33296
33575
  const notifications = this.toSdkNotificationsWithSubscribers(lists.NotificationsWithSubscribers);
33297
33576
  const resourceTags = this.toSdkResourceTags(lists.ResourceTags);
33577
+ const arn = await this.budgetArn(accountId, name);
33298
33578
  await this.getClient().send(new CreateBudgetCommand({
33299
33579
  AccountId: accountId,
33300
33580
  Budget: this.toSdkBudget(properties["Budget"], name, logicalId, resourceType, mask),
@@ -33304,7 +33584,7 @@ var BudgetsBudgetProvider = class {
33304
33584
  this.logger.debug(`Successfully created budget ${logicalId}: ${name}`);
33305
33585
  return {
33306
33586
  physicalId: name,
33307
- attributes: { Arn: await this.budgetArn(accountId, name) }
33587
+ attributes: { Arn: arn }
33308
33588
  };
33309
33589
  } catch (error) {
33310
33590
  if (error instanceof ProvisioningError) throw error;
@@ -34125,6 +34405,7 @@ var KinesisStreamConsumerProvider = class {
34125
34405
  if (!streamArn) throw new ProvisioningError("AWS::Kinesis::StreamConsumer requires StreamARN", resourceType, logicalId);
34126
34406
  this.logger.debug(`Registering Kinesis stream consumer ${logicalId}: ${consumerName}`);
34127
34407
  const tagMap = tagListToMap(tagList);
34408
+ let createdArn;
34128
34409
  try {
34129
34410
  const consumer = (await this.getClient().send(new RegisterStreamConsumerCommand({
34130
34411
  StreamARN: streamArn,
@@ -34133,6 +34414,7 @@ var KinesisStreamConsumerProvider = class {
34133
34414
  }))).Consumer;
34134
34415
  if (!consumer?.ConsumerARN || !consumer.ConsumerName) throw new ProvisioningError("RegisterStreamConsumer did not return ConsumerARN/ConsumerName", resourceType, logicalId);
34135
34416
  const consumerArn = consumer.ConsumerARN;
34417
+ createdArn = consumerArn;
34136
34418
  await this.waitForConsumerActive(consumerArn);
34137
34419
  this.logger.debug(`Successfully registered Kinesis stream consumer ${logicalId}`);
34138
34420
  return {
@@ -34147,9 +34429,9 @@ var KinesisStreamConsumerProvider = class {
34147
34429
  }
34148
34430
  };
34149
34431
  } catch (error) {
34150
- if (error instanceof ProvisioningError) throw error;
34151
- const cause = error instanceof Error ? error : void 0;
34152
- throw new ProvisioningError(`Failed to register Kinesis stream consumer ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, consumerName, cause);
34432
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to register Kinesis stream consumer ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, consumerName, error instanceof Error ? error : void 0);
34433
+ if (createdArn) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdArn);
34434
+ throw thrown;
34153
34435
  }
34154
34436
  }
34155
34437
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -34293,6 +34575,33 @@ function tagListToMap(tags) {
34293
34575
 
34294
34576
  //#endregion
34295
34577
  //#region src/provisioning/providers/scheduler-schedule-provider.ts
34578
+ const RECORDED_CREATION_DATE_KEY = "cdkd:CreationDate";
34579
+ function recordedCreationDate(attributes) {
34580
+ const value = attributes?.[RECORDED_CREATION_DATE_KEY];
34581
+ if (typeof value !== "string" || value === "") return void 0;
34582
+ const parsed = new Date(value);
34583
+ return !Number.isNaN(parsed.getTime()) && parsed.toISOString() === value ? value : void 0;
34584
+ }
34585
+ const RECORDED_ONLY = maskerOrIdentity(void 0);
34586
+ function usableRecorded(value) {
34587
+ return typeof value === "string" && value !== "" && !isSecretDerivedValue(value, RECORDED_ONLY) ? value : void 0;
34588
+ }
34589
+ function recordedTarget(properties) {
34590
+ const target = properties?.["Target"];
34591
+ const fields = typeof target === "object" && target !== null && !Array.isArray(target) ? target : {};
34592
+ return {
34593
+ targetArn: usableRecorded(fields["Arn"]),
34594
+ roleArn: usableRecorded(fields["RoleArn"])
34595
+ };
34596
+ }
34597
+ function sameRecordedDate(live, recorded) {
34598
+ return live.creation !== void 0 && live.creation === recorded.creation;
34599
+ }
34600
+ function sameRecordedIdentity(live, recorded) {
34601
+ return sameRecordedDate(live, recorded) && (recorded.targetArn === void 0 || live.targetArn === recorded.targetArn) && (recorded.roleArn === void 0 || live.roleArn === recorded.roleArn);
34602
+ }
34603
+ const NO_REGION_FOR_SCHEDULE_SEARCH_SKIP_REASON = "secret-derived group and no recorded region — no delete issued";
34604
+ const AMBIGUOUS_SCHEDULE_SKIP_REASON = "schedule matches the recorded date but not its target — no delete issued";
34296
34605
  function groupShown(group) {
34297
34606
  return group === "***" ? group : plainOrDescribed(group, "group name");
34298
34607
  }
@@ -34405,20 +34714,97 @@ var SchedulerScheduleProvider = class {
34405
34714
  ...groupName && { GroupName: groupName },
34406
34715
  ...this.toSdkFields(properties)
34407
34716
  }));
34717
+ const creationDate = await this.creationDateBestEffort(logicalId, name, groupName);
34408
34718
  return {
34409
34719
  physicalId: name,
34410
- attributes: response.ScheduleArn ? { Arn: response.ScheduleArn } : {}
34720
+ attributes: {
34721
+ ...response.ScheduleArn && { Arn: response.ScheduleArn },
34722
+ ...creationDate !== void 0 && { ["cdkd:CreationDate"]: creationDate }
34723
+ }
34411
34724
  };
34412
34725
  } catch (error) {
34413
34726
  const cause = error instanceof Error ? error : void 0;
34414
34727
  throw new ProvisioningError(`Failed to create Schedule ${logicalId}: ${cause?.message ?? String(error)}`, resourceType, logicalId, void 0, cause);
34415
34728
  }
34416
34729
  }
34730
+ async readIdentity(name, groupName) {
34731
+ const response = await this.getClient().send(new GetScheduleCommand({
34732
+ Name: name,
34733
+ ...groupName && { GroupName: groupName }
34734
+ }));
34735
+ const date = response.CreationDate;
34736
+ const text = (value) => typeof value === "string" && value !== "" ? value : void 0;
34737
+ return {
34738
+ creation: date instanceof Date && !Number.isNaN(date.getTime()) ? date.toISOString() : void 0,
34739
+ targetArn: text(response.Target?.Arn),
34740
+ roleArn: text(response.Target?.RoleArn)
34741
+ };
34742
+ }
34743
+ sleep(ms) {
34744
+ return new Promise((resolve) => setTimeout(resolve, ms));
34745
+ }
34746
+ readBackDelaysMs = [500, 1e3];
34747
+ async creationDateBestEffort(logicalId, name, groupName) {
34748
+ for (let attempt = 0;; attempt++) try {
34749
+ return (await this.readIdentity(name, groupName)).creation;
34750
+ } catch (error) {
34751
+ const transient = error instanceof ResourceNotFoundException$11 || isThrottlingError(error);
34752
+ const delay = this.readBackDelaysMs[attempt];
34753
+ if (transient && delay !== void 0) {
34754
+ await this.sleep(delay);
34755
+ continue;
34756
+ }
34757
+ const failureClass = error instanceof Error && error.name !== "" ? error.name : "an unreadable failure";
34758
+ this.logger.debug(safeMsg`Could not read the creation date of Schedule ${logicalId} (${failureClass}); none is recorded`);
34759
+ return;
34760
+ }
34761
+ }
34762
+ wrapUnconfirmedGroupError(logicalId, physicalId, resourceType, error, mask) {
34763
+ const failureClass = error instanceof Error && error.name !== "" ? error.name : "an unreadable failure";
34764
+ return markRedactedCause(new ProvisioningError(`Whether the group the GroupName of Schedule ${logicalId} resolves to still holds this schedule could not be confirmed (${mask(failureClass)}) — re-run once the lookup can succeed`, resourceType, logicalId, physicalId, error instanceof Error ? error : void 0));
34765
+ }
34766
+ refuseUnconfirmed(logicalId, physicalId, resourceType, finding, secretDerived, remedy) {
34767
+ throw markNonRetryable(new ProvisioningError(`${secretDerived ? `GroupName of Schedule ${logicalId} is secret-derived, and the group its secret now resolves to` : `The group of Schedule ${logicalId}`} ${finding}${secretDerived ? " (the secret may have been rotated)" : ""}, so nothing is written to it, with or without --replace: ${remedy}`, resourceType, logicalId, physicalId));
34768
+ }
34769
+ async confirmRecordedSchedule(logicalId, physicalId, resourceType, groupName, recorded, mask) {
34770
+ if (recorded.creation === void 0) return "undated";
34771
+ let live;
34772
+ try {
34773
+ live = await this.readIdentity(physicalId, groupName);
34774
+ } catch (error) {
34775
+ if (error instanceof ResourceNotFoundException$11) return "absent";
34776
+ throw this.wrapUnconfirmedGroupError(logicalId, physicalId, resourceType, error, mask);
34777
+ }
34778
+ if (sameRecordedDate(live, recorded) && !sameRecordedIdentity(live, recorded)) this.refuseUnconfirmed(logicalId, physicalId, resourceType, "holds a schedule of this name that carries the recorded creation date but not the recorded target or role (edited outside cdkd?)", false, "restore them, or drop the record with `cdkd orphan <construct path>` and deploy again");
34779
+ if (!sameRecordedIdentity(live, recorded)) this.refuseUnconfirmed(logicalId, physicalId, resourceType, "holds a different schedule of this name (its creation date is not the one cdkd recorded)", true, "restore the secret's value to the schedule's group");
34780
+ return "confirmed";
34781
+ }
34782
+ referenceRepointed(desiredGroup, recordedGroup) {
34783
+ const bag = getCurrentResourceSecrets();
34784
+ if (bag === void 0 || typeof desiredGroup !== "string") return false;
34785
+ if (typeof recordedGroup !== "string" || !recordedGroup.includes("{{resolve:")) return false;
34786
+ return redactSecretsForState(desiredGroup, bag) !== recordedGroup;
34787
+ }
34417
34788
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
34418
34789
  const groupName = this.groupNameOf(properties);
34419
34790
  const previousGroupName = this.groupNameOf(previousProperties);
34420
34791
  const mask = maskerOrIdentity(context?.maskSecrets);
34421
- if (groupName !== previousGroupName) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `GroupName addresses the schedule (from ${groupShown(mask(previousGroupName ?? "default"))} to ${groupShown(mask(groupName ?? "default"))}); re-run with \`cdkd deploy --replace\` to recreate it in the new group (--replace is a boolean flag and takes no resource id; it applies to every resource in the run whose in-place update is refused)`);
34792
+ const recorded = {
34793
+ creation: recordedCreationDate(context?.recordedAttributes),
34794
+ ...recordedTarget(previousProperties)
34795
+ };
34796
+ const replaying = context?.replayingState === true;
34797
+ const desiredSecretDerived = isSecretDerivedValue(properties["GroupName"], mask);
34798
+ let confirmedMove = false;
34799
+ if (groupName !== previousGroupName && isSecretDerivedValue(previousProperties["GroupName"], RECORDED_ONLY) && desiredSecretDerived) {
34800
+ const verdict = await this.confirmRecordedSchedule(logicalId, physicalId, resourceType, groupName, recorded, mask);
34801
+ if (verdict === "undated") this.refuseUnconfirmed(logicalId, physicalId, resourceType, "cannot be confirmed to hold this schedule: its state record predates the creation date cdkd now records", true, "delete the schedule by hand (`aws scheduler delete-schedule`), drop its record with `cdkd orphan <construct path>`, and deploy again, which records the date");
34802
+ if (verdict === "absent" && !this.referenceRepointed(groupName, previousProperties["GroupName"])) this.refuseUnconfirmed(logicalId, physicalId, resourceType, "holds no schedule of this name", true, "restore the secret's value to the schedule's group; if the schedule was deleted outside cdkd, drop its record with `cdkd orphan <construct path>` and deploy again");
34803
+ confirmedMove = verdict === "confirmed";
34804
+ } else if (groupName === previousGroupName && replaying && recorded.creation !== void 0 && desiredSecretDerived) {
34805
+ if (await this.confirmRecordedSchedule(logicalId, physicalId, resourceType, groupName, recorded, mask) !== "confirmed") this.refuseUnconfirmed(logicalId, physicalId, resourceType, "holds no schedule of this name", true, "restore the secret's value to the schedule's group, then re-run the rollback");
34806
+ }
34807
+ if (groupName !== previousGroupName && !confirmedMove) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `GroupName addresses the schedule (from ${groupShown(mask(previousGroupName ?? "default"))} to ${groupShown(mask(groupName ?? "default"))}); re-run with \`cdkd deploy --replace\` to recreate it in the new group (--replace is a boolean flag and takes no resource id; it applies to every resource in the run whose in-place update is refused)`);
34422
34808
  this.logger.debug(mask(`Updating Schedule ${logicalId}: ${mask(physicalId)}${groupName ? ` (group: ${mask(groupName)})` : ""}`));
34423
34809
  try {
34424
34810
  const input = {
@@ -34427,10 +34813,14 @@ var SchedulerScheduleProvider = class {
34427
34813
  ...this.toSdkFields(properties)
34428
34814
  };
34429
34815
  const response = await this.getClient().send(new UpdateScheduleCommand(input));
34816
+ const creationDate = replaying || desiredSecretDerived ? recorded.creation : await this.creationDateBestEffort(logicalId, physicalId, groupName) ?? recorded.creation;
34430
34817
  return {
34431
34818
  physicalId,
34432
34819
  wasReplaced: false,
34433
- attributes: response.ScheduleArn ? { Arn: response.ScheduleArn } : {}
34820
+ attributes: {
34821
+ ...response.ScheduleArn && { Arn: response.ScheduleArn },
34822
+ ...creationDate !== void 0 && { ["cdkd:CreationDate"]: creationDate }
34823
+ }
34434
34824
  };
34435
34825
  } catch (error) {
34436
34826
  if (error instanceof ResourceUpdateNotSupportedError) throw error;
@@ -34438,8 +34828,89 @@ var SchedulerScheduleProvider = class {
34438
34828
  throw new ProvisioningError(`Failed to update Schedule ${logicalId}: ${cause?.message ?? String(error)}`, resourceType, logicalId, physicalId, cause);
34439
34829
  }
34440
34830
  }
34831
+ async deleteByRecordedIdentity(logicalId, physicalId, resourceType, recorded, context) {
34832
+ if (!context?.expectedRegion) {
34833
+ this.logger.warn(safeMsg`Schedule ${logicalId} is recorded with a secret-derived GroupName and no stack region, so cdkd cannot scope the search for it; skipping deletion and keeping its state record. Delete the schedule by hand, then drop the record with 'cdkd orphan'.`);
34834
+ return {
34835
+ outcome: "skipped",
34836
+ reason: NO_REGION_FOR_SCHEDULE_SEARCH_SKIP_REASON
34837
+ };
34838
+ }
34839
+ assertRegionMatch(await this.getClient().config.region(), context.expectedRegion, resourceType, logicalId, physicalId);
34840
+ const wrapDeleteError = (step, error) => {
34841
+ const failureClass = error instanceof Error && error.name !== "" ? error.name : "an unreadable failure";
34842
+ return markRedactedCause(new ProvisioningError(`Failed to delete Schedule ${logicalId}: its recorded GroupName is secret-derived, and ${step} failed (${failureClass})`, resourceType, logicalId, physicalId, error instanceof Error ? error : void 0));
34843
+ };
34844
+ const findMatches = async () => {
34845
+ const groups = [];
34846
+ try {
34847
+ let nextToken;
34848
+ do {
34849
+ const page = await this.getClient().send(new ListSchedulesCommand({
34850
+ NamePrefix: physicalId,
34851
+ ...nextToken !== void 0 && { NextToken: nextToken }
34852
+ }));
34853
+ for (const schedule of page.Schedules ?? []) if (schedule.Name === physicalId && typeof schedule.GroupName === "string") groups.push(schedule.GroupName);
34854
+ nextToken = page.NextToken;
34855
+ } while (nextToken !== void 0 && nextToken !== "");
34856
+ } catch (error) {
34857
+ throw wrapDeleteError("listing the schedules of its name", error);
34858
+ }
34859
+ const matches = [];
34860
+ let datedOnly = 0;
34861
+ for (const group of groups) {
34862
+ let live;
34863
+ try {
34864
+ live = await this.readIdentity(physicalId, group);
34865
+ } catch (error) {
34866
+ if (error instanceof ResourceNotFoundException$11) continue;
34867
+ throw wrapDeleteError("reading a schedule of its name", error);
34868
+ }
34869
+ if (sameRecordedIdentity(live, recorded)) matches.push(group);
34870
+ else if (sameRecordedDate(live, recorded)) datedOnly++;
34871
+ }
34872
+ return {
34873
+ matches,
34874
+ datedOnly
34875
+ };
34876
+ };
34877
+ let found = await findMatches();
34878
+ if (found.matches.length === 0 && found.datedOnly === 0) {
34879
+ await this.sleep(this.readBackDelaysMs[0] ?? 0);
34880
+ found = await findMatches();
34881
+ }
34882
+ const { matches } = found;
34883
+ if (matches.length === 0 && found.datedOnly > 0) {
34884
+ this.logger.warn(safeMsg`Schedule ${logicalId}: a schedule of its name carries the recorded creation date but not the recorded target or role (edited outside cdkd?), so cdkd cannot tell whether it is this one; skipping deletion and keeping its state record. Delete it by hand if it is, then drop the record with 'cdkd orphan'.`);
34885
+ return {
34886
+ outcome: "skipped",
34887
+ reason: AMBIGUOUS_SCHEDULE_SKIP_REASON
34888
+ };
34889
+ }
34890
+ if (matches.length === 0) {
34891
+ this.logger.warn(safeMsg`Schedule ${logicalId}: no schedule of its name carries the creation date cdkd recorded, so it is treated as already deleted and its state record is removed`);
34892
+ return;
34893
+ }
34894
+ if (matches.length > 1) throw markNonRetryable(new ProvisioningError(`Failed to delete Schedule ${logicalId}: its recorded GroupName is secret-derived, and ${matches.length} schedules of its name match the one cdkd recorded, so none is deleted`, resourceType, logicalId, physicalId));
34895
+ try {
34896
+ await this.getClient().send(new DeleteScheduleCommand({
34897
+ Name: physicalId,
34898
+ GroupName: matches[0]
34899
+ }));
34900
+ this.logger.debug(safeMsg`Deleted Schedule ${logicalId} (found by its recorded creation date)`);
34901
+ } catch (error) {
34902
+ if (error instanceof ResourceNotFoundException$11) return;
34903
+ throw wrapDeleteError("the delete", error);
34904
+ }
34905
+ }
34441
34906
  async delete(logicalId, physicalId, resourceType, properties, context) {
34442
- const skip = redactedDeleteAddressSkip(this.logger, logicalId, "Schedule", redactedDeleteAddressFields({ GroupName: properties?.["GroupName"] }));
34907
+ const redactedFields = redactedDeleteAddressFields({ GroupName: properties?.["GroupName"] });
34908
+ const recordedCreation = recordedCreationDate(context?.recordedAttributes);
34909
+ if (redactedFields.length > 0 && recordedCreation !== void 0) return this.deleteByRecordedIdentity(logicalId, physicalId, resourceType, {
34910
+ creation: recordedCreation,
34911
+ ...recordedTarget(properties)
34912
+ }, context);
34913
+ const skip = redactedDeleteAddressSkip(this.logger, logicalId, "Schedule", redactedFields);
34443
34914
  if (skip) return skip;
34444
34915
  const groupName = this.groupNameOf(properties);
34445
34916
  if (properties === void 0) {
@@ -34515,7 +34986,10 @@ var SchedulerScheduleProvider = class {
34515
34986
  }));
34516
34987
  return {
34517
34988
  physicalId: explicit,
34518
- attributes: response.Arn ? { Arn: response.Arn } : {}
34989
+ attributes: {
34990
+ ...response.Arn && { Arn: response.Arn },
34991
+ ...response.CreationDate instanceof Date && !Number.isNaN(response.CreationDate.getTime()) && { ["cdkd:CreationDate"]: response.CreationDate.toISOString() }
34992
+ }
34519
34993
  };
34520
34994
  } catch (error) {
34521
34995
  if (error instanceof ResourceNotFoundException$11) return null;
@@ -34758,7 +35232,7 @@ var PipesPipeProvider = class {
34758
35232
  try {
34759
35233
  settled = await this.waitForSettled(name, logicalId, resourceType, "create", mask);
34760
35234
  } catch (error) {
34761
- throw await this.retireFailedCreate(name, logicalId, error, mask);
35235
+ throw await this.retireFailedCreate(name, logicalId, resourceType, error, mask);
34762
35236
  }
34763
35237
  return {
34764
35238
  physicalId: name,
@@ -34978,13 +35452,14 @@ var PipesPipeProvider = class {
34978
35452
  watch.dispose();
34979
35453
  }
34980
35454
  }
34981
- async retireFailedCreate(name, logicalId, error, mask) {
35455
+ async retireFailedCreate(name, logicalId, resourceType, error, mask) {
34982
35456
  const original = error instanceof Error ? error : new Error(String(error));
35457
+ const leftBehind = () => markCreatedBeforeFailure(original, logicalId, resourceType, name);
34983
35458
  const orphaned = async (why) => {
34984
35459
  const aws = pasteableAwsCommand(mask);
34985
35460
  const command = aws`aws pipes delete-pipe --name ${name}${await orphanCommandRegionArg(this.getClient(), aws)}`;
34986
- original.message += ` (cdkd could not delete the pipe it had created: ${why}; it is not recorded in state. ` + (command.text !== void 0 ? `Delete it with: ${command.text})` : `Delete it from the EventBridge Pipes console.)`);
34987
- return original;
35461
+ original.message += ` (cdkd could not delete the pipe it had created: ${why}; on a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`. ` + (command.text !== void 0 ? `Otherwise delete it yourself with: ${command.text})` : `Otherwise delete it yourself from the EventBridge Pipes console.)`);
35462
+ return leftBehind();
34988
35463
  };
34989
35464
  try {
34990
35465
  await this.getClient().send(new DeletePipeCommand({ Name: name }));
@@ -35027,7 +35502,7 @@ var PipesPipeProvider = class {
35027
35502
  const describe = aws`aws pipes describe-pipe --name ${name}${region}`;
35028
35503
  const remove = aws`aws pipes delete-pipe --name ${name}${region}`;
35029
35504
  original.message += " (cdkd asked AWS to delete the pipe it had created but could not confirm the delete progressed; " + (describe.text !== void 0 && remove.text !== void 0 ? `if ${describe.text} still shows it, delete it with: ${remove.text})` : "if the EventBridge Pipes console still shows it, delete it there.)");
35030
- return original;
35505
+ return leftBehind();
35031
35506
  }
35032
35507
  };
35033
35508
 
@@ -35260,6 +35735,7 @@ var EFSProvider = class {
35260
35735
  if (reservation.ledgerStartedThisDeploy) await this.warnOfPreLedgerHolder(logicalId, reservation.base, log);
35261
35736
  const tags = properties["FileSystemTags"];
35262
35737
  let fileSystemId;
35738
+ let fileSystemLeftBehind = false;
35263
35739
  try {
35264
35740
  const created = await this.sendCreateFileSystem(logicalId, resourceType, reservation, {
35265
35741
  CreationToken: creationToken,
@@ -35298,12 +35774,14 @@ var EFSProvider = class {
35298
35774
  this.unconfirmedCreationTokens.delete(creationToken);
35299
35775
  log.debug(`Rolled back partially-created EFS FileSystem ${fileSystemId}`);
35300
35776
  } catch (cleanupError) {
35777
+ fileSystemLeftBehind = true;
35301
35778
  log.warn(`Failed to roll back partially-created EFS FileSystem ${fileSystemId}: ${describeAwsFailure(cleanupError).detail}`);
35302
35779
  }
35303
35780
  }
35304
- if (error instanceof ProvisioningError) throw error;
35305
35781
  const cause = error instanceof Error ? error : void 0;
35306
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create EFS FileSystem ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
35782
+ const thrown = error instanceof ProvisioningError ? error : this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create EFS FileSystem ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
35783
+ if (fileSystemLeftBehind && fileSystemId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, fileSystemId);
35784
+ throw thrown;
35307
35785
  }
35308
35786
  }
35309
35787
  async sendCreateFileSystem(logicalId, resourceType, reservation, input) {
@@ -35448,12 +35926,14 @@ var EFSProvider = class {
35448
35926
  const subnetId = properties["SubnetId"];
35449
35927
  if (!subnetId) throw new ProvisioningError(`SubnetId is required for EFS MountTarget ${logicalId}`, resourceType, logicalId);
35450
35928
  const securityGroups = properties["SecurityGroups"];
35929
+ let createdMountTargetId;
35451
35930
  try {
35452
35931
  const mountTargetId = (await this.getClient().send(new CreateMountTargetCommand({
35453
35932
  FileSystemId: fileSystemId,
35454
35933
  SubnetId: subnetId,
35455
35934
  SecurityGroups: securityGroups
35456
35935
  }))).MountTargetId;
35936
+ createdMountTargetId = mountTargetId;
35457
35937
  log.debug(`Created EFS MountTarget ${logicalId}: ${mountTargetId}, waiting for available state`);
35458
35938
  await this.waitForMountTargetAvailable(mountTargetId, logicalId, resourceType, log);
35459
35939
  log.debug(`Successfully created EFS MountTarget ${logicalId}: ${mountTargetId}`);
@@ -35462,9 +35942,10 @@ var EFSProvider = class {
35462
35942
  attributes: {}
35463
35943
  };
35464
35944
  } catch (error) {
35465
- if (error instanceof ProvisioningError) throw error;
35466
35945
  const cause = error instanceof Error ? error : void 0;
35467
- throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create EFS MountTarget ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
35946
+ const thrown = error instanceof ProvisioningError ? error : this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to create EFS MountTarget ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
35947
+ if (createdMountTargetId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdMountTargetId);
35948
+ throw thrown;
35468
35949
  }
35469
35950
  }
35470
35951
  async waitForMountTargetAvailable(mountTargetId, logicalId, resourceType, log) {
@@ -36004,16 +36485,27 @@ var FSxFileSystemProvider = class {
36004
36485
  attributes: this.buildAttributes(fs)
36005
36486
  };
36006
36487
  } catch (error) {
36488
+ let survivorId;
36007
36489
  if (fileSystemId !== void 0) try {
36008
36490
  await this.getClient().send(new DeleteFileSystemCommand$1({ FileSystemId: fileSystemId }));
36009
36491
  this.createTokenFirstSentAt.delete(clientRequestToken);
36010
36492
  this.logger.warn(`Rolled back partially-created FSx FileSystem ${fileSystemId}`);
36011
36493
  } catch (cleanupError) {
36012
- this.logger.warn(`Failed to roll back partially-created FSx FileSystem ${fileSystemId}: ${describeAwsFailure(cleanupError).detail} — delete it manually to stop billing`);
36494
+ survivorId = fileSystemId;
36495
+ this.logger.warn(`Failed to roll back partially-created FSx FileSystem ${fileSystemId}: ${describeAwsFailure(cleanupError).detail} — on a first-time create the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise delete it yourself to stop billing`);
36496
+ }
36497
+ if (error instanceof ProvisioningError) {
36498
+ if (survivorId !== void 0) markCreatedBeforeFailure(error, logicalId, resourceType, survivorId);
36499
+ throw error;
36013
36500
  }
36014
- if (error instanceof ProvisioningError) throw error;
36015
36501
  const cause = error instanceof Error ? error : void 0;
36016
- throw new ProvisioningError(`Failed to create FSx FileSystem ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
36502
+ const message = `Failed to create FSx FileSystem ${logicalId}: ${error instanceof Error ? error.message : String(error)}`;
36503
+ if (survivorId !== void 0) {
36504
+ const thrown = new ProvisioningError(message, resourceType, logicalId, void 0, cause);
36505
+ markCreatedBeforeFailure(thrown, logicalId, resourceType, survivorId);
36506
+ throw thrown;
36507
+ }
36508
+ throw new ProvisioningError(message, resourceType, logicalId, void 0, cause);
36017
36509
  }
36018
36510
  }
36019
36511
  toCreateLustreConfiguration(config) {
@@ -36991,6 +37483,7 @@ var EMRClusterProvider = class {
36991
37483
  attributes: this.buildAttributes(cluster)
36992
37484
  };
36993
37485
  } catch (error) {
37486
+ let survivorId;
36994
37487
  if (clusterId !== void 0) try {
36995
37488
  await this.getClient().send(new SetTerminationProtectionCommand({
36996
37489
  JobFlowIds: [clusterId],
@@ -36999,11 +37492,21 @@ var EMRClusterProvider = class {
36999
37492
  await this.getClient().send(new TerminateJobFlowsCommand({ JobFlowIds: [clusterId] }));
37000
37493
  this.logger.warn(`Rolled back partially-created EMR Cluster ${clusterId}`);
37001
37494
  } catch (cleanupError) {
37495
+ survivorId = clusterId;
37002
37496
  this.logger.warn(`Failed to roll back partially-created EMR Cluster ${clusterId}: ${describeAwsFailure(cleanupError).detail} — terminate it manually to stop billing`);
37003
37497
  }
37004
- if (error instanceof ProvisioningError) throw error;
37498
+ if (error instanceof ProvisioningError) {
37499
+ if (survivorId !== void 0) markCreatedBeforeFailure(error, logicalId, resourceType, survivorId);
37500
+ throw error;
37501
+ }
37005
37502
  const cause = error instanceof Error ? error : void 0;
37006
- throw new ProvisioningError(`Failed to create EMR Cluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
37503
+ const message = `Failed to create EMR Cluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`;
37504
+ if (survivorId !== void 0) {
37505
+ const thrown = new ProvisioningError(message, resourceType, logicalId, void 0, cause);
37506
+ markCreatedBeforeFailure(thrown, logicalId, resourceType, survivorId);
37507
+ throw thrown;
37508
+ }
37509
+ throw new ProvisioningError(message, resourceType, logicalId, void 0, cause);
37007
37510
  }
37008
37511
  }
37009
37512
  toJobFlowInstancesConfig(config) {
@@ -37587,6 +38090,7 @@ var EMRInstanceGroupConfigProvider = class {
37587
38090
  const jobFlowId = properties["JobFlowId"];
37588
38091
  if (!jobFlowId) throw new ProvisioningError(`EMR InstanceGroupConfig ${logicalId} is missing JobFlowId (the parent cluster id)`, resourceType, logicalId);
37589
38092
  this.logger.debug(`Adding EMR instance group ${logicalId} to cluster ${jobFlowId}`);
38093
+ let createdGroupId;
37590
38094
  try {
37591
38095
  const group = this.toInstanceGroupConfig(properties);
37592
38096
  const orphanWindow = addInstanceGroupsLatch.take(logicalId);
@@ -37629,6 +38133,7 @@ var EMRInstanceGroupConfigProvider = class {
37629
38133
  }
37630
38134
  const groupId = response.InstanceGroupIds?.[0];
37631
38135
  if (groupId) groupsCreatedByThisProcess.add(groupId);
38136
+ createdGroupId = groupId;
37632
38137
  if (!groupId) throw new ProvisioningError(`EMR AddInstanceGroups for ${logicalId} returned no instance group id`, resourceType, logicalId);
37633
38138
  await this.waitForGroupReady(jobFlowId, groupId, logicalId, resourceType, toNumber$1(properties["InstanceCount"]) ?? 0);
37634
38139
  this.logger.debug(`Successfully added EMR instance group ${logicalId}: ${groupId}`);
@@ -37640,9 +38145,9 @@ var EMRInstanceGroupConfigProvider = class {
37640
38145
  }
37641
38146
  };
37642
38147
  } catch (error) {
37643
- if (error instanceof ProvisioningError) throw error;
37644
- const cause = error instanceof Error ? error : void 0;
37645
- throw new ProvisioningError(`Failed to add EMR instance group ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
38148
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to add EMR instance group ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
38149
+ if (createdGroupId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdGroupId);
38150
+ throw thrown;
37646
38151
  }
37647
38152
  }
37648
38153
  toInstanceGroupConfig(properties) {
@@ -37881,6 +38386,7 @@ var EMRInstanceFleetConfigProvider = class {
37881
38386
  const clusterId = properties["ClusterId"];
37882
38387
  if (!clusterId) throw new ProvisioningError(`EMR InstanceFleetConfig ${logicalId} is missing ClusterId (the parent cluster id)`, resourceType, logicalId);
37883
38388
  this.logger.debug(`Adding EMR instance fleet ${logicalId} to cluster ${clusterId}`);
38389
+ let createdFleetId;
37884
38390
  try {
37885
38391
  const fleet = this.toInstanceFleetConfig(properties);
37886
38392
  const orphanWindow = addInstanceFleetLatch.take(logicalId);
@@ -37923,6 +38429,7 @@ var EMRInstanceFleetConfigProvider = class {
37923
38429
  }
37924
38430
  const fleetId = response.InstanceFleetId;
37925
38431
  if (fleetId) fleetsCreatedByThisProcess.add(fleetId);
38432
+ createdFleetId = fleetId;
37926
38433
  if (!fleetId) throw new ProvisioningError(`EMR AddInstanceFleet for ${logicalId} returned no instance fleet id`, resourceType, logicalId);
37927
38434
  await this.waitForFleetReady(clusterId, fleetId, logicalId, resourceType, this.targetCapacities(properties), {
37928
38435
  onDemand: 0,
@@ -37934,9 +38441,9 @@ var EMRInstanceFleetConfigProvider = class {
37934
38441
  attributes: { Id: fleetId }
37935
38442
  };
37936
38443
  } catch (error) {
37937
- if (error instanceof ProvisioningError) throw error;
37938
- const cause = error instanceof Error ? error : void 0;
37939
- throw new ProvisioningError(`Failed to add EMR instance fleet ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
38444
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to add EMR instance fleet ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
38445
+ if (createdFleetId) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdFleetId);
38446
+ throw thrown;
37940
38447
  }
37941
38448
  }
37942
38449
  toInstanceFleetConfig(properties) {
@@ -38159,6 +38666,7 @@ var FirehoseProvider = class {
38159
38666
  const deliveryStreamName = properties["DeliveryStreamName"];
38160
38667
  const deliveryStreamType = properties["DeliveryStreamType"] || "DirectPut";
38161
38668
  if (!isReadableTagList(properties["Tags"])) throw markNonRetryable(new ProvisioningError(`Tags of Firehose delivery stream ${logicalId} is not a list of tags with a Key — the delivery stream was not created`, resourceType, logicalId));
38669
+ let createdStreamName;
38162
38670
  try {
38163
38671
  const input = {
38164
38672
  DeliveryStreamName: deliveryStreamName || logicalId,
@@ -38287,6 +38795,7 @@ var FirehoseProvider = class {
38287
38795
  }));
38288
38796
  const response = await this.getClient().send(new CreateDeliveryStreamCommand(input));
38289
38797
  const physicalId = deliveryStreamName || input.DeliveryStreamName || response.DeliveryStreamARN?.split("/").pop() || "";
38798
+ createdStreamName = physicalId;
38290
38799
  const arn = response.DeliveryStreamARN;
38291
38800
  this.logger.debug(`Successfully created Firehose delivery stream ${logicalId}: ${physicalId}`);
38292
38801
  await this.waitForActive(physicalId, logicalId);
@@ -38295,9 +38804,9 @@ var FirehoseProvider = class {
38295
38804
  attributes: { Arn: arn }
38296
38805
  };
38297
38806
  } catch (error) {
38298
- if (error instanceof ProvisioningError) throw error;
38299
- const cause = error instanceof Error ? error : void 0;
38300
- throw new ProvisioningError(`Failed to create Firehose delivery stream ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
38807
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create Firehose delivery stream ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
38808
+ if (createdStreamName !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdStreamName);
38809
+ throw thrown;
38301
38810
  }
38302
38811
  }
38303
38812
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -39041,6 +39550,7 @@ var CloudTrailProvider = class {
39041
39550
  const eventSelectors = properties["EventSelectors"];
39042
39551
  const insightSelectors = properties["InsightSelectors"];
39043
39552
  let trailCreated = false;
39553
+ let createdTrailArn;
39044
39554
  try {
39045
39555
  const result = await this.getClient().send(new CreateTrailCommand({
39046
39556
  Name: trailName ?? logicalId,
@@ -39061,6 +39571,7 @@ var CloudTrailProvider = class {
39061
39571
  }));
39062
39572
  trailCreated = true;
39063
39573
  const trailArn = result.TrailARN;
39574
+ createdTrailArn = result.TrailARN;
39064
39575
  if (eventSelectors && eventSelectors.length > 0) {
39065
39576
  this.logger.debug(`Setting event selectors for CloudTrail Trail ${logicalId}`);
39066
39577
  await this.getClient().send(new PutEventSelectorsCommand({
@@ -39087,7 +39598,9 @@ var CloudTrailProvider = class {
39087
39598
  } catch (error) {
39088
39599
  if (trailCreated) markAuxiliaryFailure(error, logicalId);
39089
39600
  const cause = error instanceof Error ? error : void 0;
39090
- throw new ProvisioningError(`Failed to create CloudTrail Trail ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
39601
+ const thrown = new ProvisioningError(`Failed to create CloudTrail Trail ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
39602
+ if (createdTrailArn) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdTrailArn);
39603
+ throw thrown;
39091
39604
  }
39092
39605
  }
39093
39606
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -39873,6 +40386,7 @@ var CodeCommitRepositoryProvider = class {
39873
40386
  Triggers: properties["Triggers"],
39874
40387
  Tags: properties["Tags"]
39875
40388
  }, void 0);
40389
+ let leftLiveName;
39876
40390
  try {
39877
40391
  const tags = toSdkTagMap(lists.Tags);
39878
40392
  const description = properties["RepositoryDescription"];
@@ -39885,6 +40399,7 @@ var CodeCommitRepositoryProvider = class {
39885
40399
  }))).repositoryMetadata;
39886
40400
  if (!metadata?.repositoryName) throw new Error("CreateRepository did not return repository metadata");
39887
40401
  const createdName = metadata.repositoryName;
40402
+ leftLiveName = createdName;
39888
40403
  try {
39889
40404
  const code = properties["Code"];
39890
40405
  if (code) await this.seedInitialCommit(createdName, code);
@@ -39898,7 +40413,7 @@ var CodeCommitRepositoryProvider = class {
39898
40413
  }
39899
40414
  } catch (postCreateError) {
39900
40415
  this.logger.warn(`Post-create step failed for CodeCommit Repository ${logicalId}; deleting the just-created repository ${createdName} to avoid an orphan`);
39901
- await this.bestEffortDelete(createdName);
40416
+ if (await this.bestEffortDelete(createdName)) leftLiveName = void 0;
39902
40417
  throw markAuxiliaryFailure(postCreateError, logicalId);
39903
40418
  }
39904
40419
  this.logger.debug(`Successfully created CodeCommit Repository ${logicalId}: ${createdName}`);
@@ -39908,7 +40423,9 @@ var CodeCommitRepositoryProvider = class {
39908
40423
  };
39909
40424
  } catch (error) {
39910
40425
  const cause = error instanceof Error ? error : void 0;
39911
- throw new ProvisioningError(`Failed to create CodeCommit Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, repositoryName, cause);
40426
+ const thrown = new ProvisioningError(`Failed to create CodeCommit Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, repositoryName, cause);
40427
+ if (leftLiveName !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, leftLiveName);
40428
+ throw thrown;
39912
40429
  }
39913
40430
  }
39914
40431
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -40236,8 +40753,10 @@ var CodeCommitRepositoryProvider = class {
40236
40753
  async bestEffortDelete(repositoryName) {
40237
40754
  try {
40238
40755
  await this.getClient().send(new DeleteRepositoryCommand$1({ repositoryName }));
40756
+ return true;
40239
40757
  } catch (cleanupError) {
40240
40758
  this.logger.warn(`Failed to clean up CodeCommit Repository ${repositoryName} after a post-create failure: ${describeAwsFailure(cleanupError).detail}`);
40759
+ return false;
40241
40760
  }
40242
40761
  }
40243
40762
  async getRepositoryMetadata(repositoryName) {
@@ -40805,6 +41324,7 @@ var S3DirectoryBucketProvider = class {
40805
41324
  const tags = desiredTags;
40806
41325
  const includeTags = tags.length > 0;
40807
41326
  try {
41327
+ const attributes = await this.buildAttributes(bucketName);
40808
41328
  await this.s3Client.send(new CreateBucketCommand({
40809
41329
  Bucket: bucketName,
40810
41330
  CreateBucketConfiguration: {
@@ -40820,7 +41340,6 @@ var S3DirectoryBucketProvider = class {
40820
41340
  }
40821
41341
  }));
40822
41342
  this.logger.debug(`Created S3 Express Directory Bucket: ${bucketName}`);
40823
- const attributes = await this.buildAttributes(bucketName);
40824
41343
  return {
40825
41344
  physicalId: bucketName,
40826
41345
  attributes
@@ -41241,6 +41760,7 @@ var S3TablesProvider = class {
41241
41760
  maskSecrets: context?.maskSecrets,
41242
41761
  ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
41243
41762
  });
41763
+ let tableCreated = false;
41244
41764
  try {
41245
41765
  const response = await this.getClient().send(new CreateTableCommand$2({
41246
41766
  tableBucketARN,
@@ -41249,6 +41769,7 @@ var S3TablesProvider = class {
41249
41769
  format,
41250
41770
  ...tags !== void 0 && { tags }
41251
41771
  }));
41772
+ tableCreated = true;
41252
41773
  if (!response.tableARN) throw new ProvisioningError(`CreateTable did not return a tableARN for ${logicalId} (${physicalId}) — refusing to record an empty TableARN attribute`, resourceType, logicalId, physicalId);
41253
41774
  const tableARN = response.tableARN;
41254
41775
  this.logger.debug(`Successfully created S3 Tables Table ${logicalId}: ${physicalId}`);
@@ -41258,7 +41779,9 @@ var S3TablesProvider = class {
41258
41779
  };
41259
41780
  } catch (error) {
41260
41781
  const cause = error instanceof Error ? error : void 0;
41261
- throw new ProvisioningError(`Failed to create S3 Tables Table ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
41782
+ const thrown = new ProvisioningError(`Failed to create S3 Tables Table ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
41783
+ if (tableCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, physicalId);
41784
+ throw thrown;
41262
41785
  }
41263
41786
  }
41264
41787
  async readCurrentState(physicalId, logicalId, resourceType) {
@@ -41742,7 +42265,9 @@ var ECRProvider = class {
41742
42265
  } catch (error) {
41743
42266
  if (repositoryCreated) markAuxiliaryFailure(error, logicalId);
41744
42267
  const cause = error instanceof Error ? error : void 0;
41745
- throw new ProvisioningError(`Failed to create ECR Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, repositoryName, cause);
42268
+ const thrown = new ProvisioningError(`Failed to create ECR Repository ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, repositoryName, cause);
42269
+ if (repositoryCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, repositoryName);
42270
+ throw thrown;
41746
42271
  }
41747
42272
  }
41748
42273
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -41956,7 +42481,7 @@ var NestedStackProvider = class {
41956
42481
  ["Tags", "CFn-only — cdkd does not tag the synthesized \"stack\" (the parent's synthesized ARN is a cdkd-local placeholder, not a real AWS resource)"],
41957
42482
  ["TimeoutInMinutes", "CFn-only stack-create deadline — cdkd uses per-resource --resource-timeout instead (issue #459 design §9)"]
41958
42483
  ])]]);
41959
- async create(logicalId, _resourceType, properties, context) {
42484
+ async create(logicalId, resourceType, properties, context) {
41960
42485
  const ctx = this.requireContext();
41961
42486
  this.requireDeployContext(ctx, "create");
41962
42487
  const childTemplatePath = ctx.nestedTemplates[logicalId];
@@ -41968,15 +42493,27 @@ var NestedStackProvider = class {
41968
42493
  const childParameters = this.extractParameters(properties);
41969
42494
  const resourceCount = Object.keys(childTemplate.Resources ?? {}).length;
41970
42495
  this.logger.info(`Deploying nested stack ${displaySafe(childStackName)} (logicalId=${displaySafe(logicalId)}, ${resourceCount} resource(s))`);
41971
- const pendingChildDeletes = await this.runChildDeploy(ctx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates);
41972
- const childOutputs = await this.readChildOutputsAsAttributes(ctx, childStackName, childRegion);
41973
- const pendingRecord = context?.replayingState === true ? void 0 : recordWithPendingChildDeletes(properties, pendingChildDeletes);
41974
- return {
41975
- physicalId: this.synthesizeArn(ctx.accountId, childRegion, ctx.parentStackName, logicalId),
41976
- attributes: childOutputs.attributes,
41977
- ...childOutputs.noEchoAttributeNames.length > 0 && { noEchoAttributeNames: childOutputs.noEchoAttributeNames },
41978
- ...pendingRecord && { effectiveProperties: pendingRecord }
41979
- };
42496
+ let pendingChildDeletes;
42497
+ try {
42498
+ pendingChildDeletes = await this.runChildDeploy(ctx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates);
42499
+ } catch (error) {
42500
+ clearCreatedBeforeFailure(error);
42501
+ throw error;
42502
+ }
42503
+ const physicalId = this.synthesizeArn(ctx.accountId, childRegion, ctx.parentStackName, logicalId);
42504
+ try {
42505
+ const childOutputs = await this.readChildOutputsAsAttributes(ctx, childStackName, childRegion);
42506
+ const pendingRecord = context?.replayingState === true ? void 0 : recordWithPendingChildDeletes(properties, pendingChildDeletes);
42507
+ return {
42508
+ physicalId,
42509
+ attributes: childOutputs.attributes,
42510
+ ...childOutputs.noEchoAttributeNames.length > 0 && { noEchoAttributeNames: childOutputs.noEchoAttributeNames },
42511
+ ...pendingRecord && { effectiveProperties: pendingRecord }
42512
+ };
42513
+ } catch (error) {
42514
+ markCreatedBeforeFailure(error, logicalId, resourceType, physicalId);
42515
+ throw error;
42516
+ }
41980
42517
  }
41981
42518
  async update(logicalId, physicalId, _resourceType, properties, _previousProperties, context) {
41982
42519
  const ctx = this.requireContext();
@@ -42063,6 +42600,7 @@ var NestedStackProvider = class {
42063
42600
  ...ctx.destroyOptions?.statePrefix !== void 0 && { statePrefix: ctx.destroyOptions.statePrefix },
42064
42601
  ...deleteContext?.removeProtection === true && { removeProtection: true },
42065
42602
  ...deleteContext?.resolveSecretDerivedPrincipals !== void 0 && { resolveSecretDerivedPrincipals: { inheritedProducerRegions: deleteContext.resolveSecretDerivedPrincipals.importedProducerRegions } },
42603
+ ...deleteContext?.stackDestroy === true && { stackDestroy: true },
42066
42604
  ...(ctx.destroyOptions?.skipFinalSnapshot === true || ctx.options?.skipFinalSnapshot === true) && { skipFinalSnapshot: true },
42067
42605
  ...ctx.destroyOptions?.resourceWarnAfterMs !== void 0 && { resourceWarnAfterMs: ctx.destroyOptions.resourceWarnAfterMs },
42068
42606
  ...ctx.destroyOptions?.resourceTimeoutMs !== void 0 && { resourceTimeoutMs: ctx.destroyOptions.resourceTimeoutMs },
@@ -42346,11 +42884,19 @@ var ACMCertificateProvider = class {
42346
42884
  const survivorNote = requestedArn ? await this.cleanupRequestedCertificate(requestedArn, logicalId, idempotencyToken) : void 0;
42347
42885
  if (error instanceof CdkdError) {
42348
42886
  if (survivorNote === void 0) throw error;
42349
- throw new ProvisioningError(`${error.message} ${survivorNote}`, resourceType, logicalId, requestedArn, error);
42887
+ const thrown = new ProvisioningError(`${error.message} ${survivorNote}`, resourceType, logicalId, requestedArn, error);
42888
+ if (requestedArn) markCreatedBeforeFailure(thrown, logicalId, resourceType, requestedArn);
42889
+ throw thrown;
42350
42890
  }
42351
42891
  const cause = error instanceof Error ? error : void 0;
42352
42892
  const detail = error instanceof Error ? error.message : String(error);
42353
- throw new ProvisioningError(`Failed to create ACM certificate ${logicalIdShown(logicalId)}: ${detail}` + (survivorNote === void 0 ? "" : ` ${survivorNote}`), resourceType, logicalId, requestedArn, cause);
42893
+ const message = `Failed to create ACM certificate ${logicalIdShown(logicalId)}: ${detail}`;
42894
+ if (requestedArn && survivorNote !== void 0) {
42895
+ const thrown = new ProvisioningError(`${message} ${survivorNote}`, resourceType, logicalId, requestedArn, cause);
42896
+ markCreatedBeforeFailure(thrown, logicalId, resourceType, requestedArn);
42897
+ throw thrown;
42898
+ }
42899
+ throw new ProvisioningError(message, resourceType, logicalId, requestedArn, cause);
42354
42900
  }
42355
42901
  }
42356
42902
  regionOfArn(arn) {
@@ -42590,4 +43136,4 @@ var ACMCertificateProvider = class {
42590
43136
 
42591
43137
  //#endregion
42592
43138
  export { ACMCertificateProvider, ASGProvider, AgentCoreBrowserProvider, AgentCoreCodeInterpreterProvider, AgentCoreEvaluatorProvider, AgentCoreRuntimeProvider, ApiGatewayProvider, ApiGatewayV2Provider, AppSyncProvider, BudgetsBudgetProvider, CloudFrontDistributionProvider, CloudFrontOACProvider, CloudFrontOAIProvider, CloudTrailProvider, CloudWatchAlarmProvider, CloudWatchAnomalyDetectorProvider, CodeBuildProvider, CodeCommitRepositoryProvider, CognitoUserPoolProvider, DLMLifecyclePolicyProvider, DocDBProvider, DocDBSubnetGroupProvider, DynamoDBGlobalTableProvider, DynamoDBTableProvider, EC2Provider, ECRProvider, ECSProvider, EFSProvider, ELBv2Provider, EMRClusterProvider, EMRInstanceFleetConfigProvider, EMRInstanceGroupConfigProvider, ElastiCacheProvider, EventBridgeBusProvider, EventBridgeRuleProvider, FSxFileSystemProvider, FirehoseProvider, GlueConnectionProvider, GlueCrawlerProvider, GlueJobProvider, GlueProvider, GlueSecurityConfigurationProvider, GlueTriggerProvider, GlueWorkflowProvider, IAMAccessKeyProvider, IAMInstanceProfileProvider, IAMManagedPolicyProvider, IAMPolicyProvider, IAMRoleProvider, IAMUserGroupProvider, KMSProvider, KinesisStreamConsumerProvider, KinesisStreamProvider, LambdaEventInvokeConfigProvider, LambdaEventSourceMappingProvider, LambdaFunctionProvider, LambdaLayerVersionProvider, LambdaMicrovmImageProvider, LambdaPermissionProvider, LambdaUrlProvider, LogsLogGroupProvider, NeptuneProvider, NestedStackProvider, PipesPipeProvider, RDSDBProxyEndpointProvider, RDSDBProxyProvider, RDSDBProxyTargetGroupProvider, RDSProvider, Route53Provider, S3BucketPolicyProvider, S3BucketProvider, S3DirectoryBucketProvider, S3TablesProvider, S3VectorsProvider, SNSSubscriptionProvider, SNSTopicPolicyProvider, SNSTopicProvider, SQSQueuePolicyProvider, SQSQueueProvider, SSMParameterProvider, SchedulerScheduleProvider, SecretsManagerSecretProvider, ServiceDiscoveryProvider, StepFunctionsProvider, WAFv2WebACLProvider, WaitConditionHandleProvider };
42593
- //# sourceMappingURL=provider-classes-DUw5QAWI.js.map
43139
+ //# sourceMappingURL=provider-classes-DMOnX5s-.js.map