@go-to-k/cdkd 0.295.0 → 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-DuAHOXvT.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-CuI_h_Ml.js → deploy-engine-D7204v7Z.js} +986 -183
  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-CyM-wxAp.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 +37 -3
  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-BGVccWhg.js → neptune-provider-D1t9NgT-.js} +20 -14
  16. package/dist/neptune-provider-D1t9NgT-.js.map +1 -0
  17. package/dist/{program-Cbxe8TgI.js → program-B0sp_SHG.js} +6219 -5942
  18. package/dist/program-B0sp_SHG.js.map +1 -0
  19. package/dist/{provider-classes-DiWFkAOH.js → provider-classes-DMOnX5s-.js} +989 -238
  20. package/dist/provider-classes-DMOnX5s-.js.map +1 -0
  21. package/dist/{rds-provider-D6_Rcnub.js → rds-provider-DltlcDiQ.js} +22 -15
  22. package/dist/rds-provider-DltlcDiQ.js.map +1 -0
  23. package/dist/{version-CMMITJVg.js → version-DUHDmEhz.js} +2 -2
  24. package/dist/{version-CMMITJVg.js.map → version-DUHDmEhz.js.map} +1 -1
  25. package/package.json +4 -4
  26. package/dist/asg-provider-DuAHOXvT.js.map +0 -1
  27. package/dist/deploy-engine-CuI_h_Ml.js.map +0 -1
  28. package/dist/iam-policy-targets-CMSQGQnm.js.map +0 -1
  29. package/dist/iam-role-provider-CyM-wxAp.js.map +0 -1
  30. package/dist/neptune-provider-BGVccWhg.js.map +0 -1
  31. package/dist/program-Cbxe8TgI.js.map +0 -1
  32. package/dist/provider-classes-DiWFkAOH.js.map +0 -1
  33. package/dist/rds-provider-D6_Rcnub.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 isWarmThroughputDecrease, A as resolveDynamoDbDeleteBudgetClock, Ao as rewriteTemplateAssetReferences, B as getPriorAttempts, Bi as findSilentDropProperties, Bn as isTerminationProtectionPropagationError, D as beginDeleteWait, Da as hasMaskableValues, Di as refuseMalformedResourcesForDestroy, Dn as getCurrentResourceSecrets, E as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, F as waitForIndexesSettled, Fn as withIndeterminateGuard, Gs as renderAssemblyPathEscape, Hi as withoutSilentDropProperties, Hn as reDisableInstanceApiTermination, I as ECR_AUTO_DELETE_IMAGES_TAG, In as isWaitAbandonedError, It as REDACTED_DELETE_ADDRESS_SKIP_REASON, Ks as resolveAssemblyPath, L as S3_AUTO_DELETE_OBJECTS_TAG, Ln as markWaitAbandoned, Lt as isRedactedRecordedValue, M as DELETE_INDEX_WAIT_PROCEED_NOTE, Ms as canonicalizeRegion, N as INDEX_SETTLE_POLL_INTERVAL_MS, Ns as derivePartitionAndUrlSuffix, O as compensateRemovedDeletionProtection, Or as passedParameterClassesOf, P as deleteTableWithIndexBusyRetry, Pn as deleteSkipReason, Qi as coerceWarmThroughput$1, R as hasCdkAutoDeleteTag, Ra as markSameGenerationBag, Rt as redactedDeleteAddressFields, T as DynamoDBGlobalTableProvider, To as stringifyValue, Tr as parseWebACLArn, Tt as getCurrentProducerRegions, Ua as dynamicReferenceTokens, Un as reEnableInstanceApiTermination, Us as describeFileReadFailure, V as markRefusedBeforeApplying, Va as recoverMaskedOutput, Vn as observeThenDisableInstanceApiTermination, Ws as displayAssemblyPath, Zi as WARM_THROUGHPUT_MEMBERS, _a as canonicalLambdaFunctionName, _n as refuseMalformedDesiredTags, a as findNestedTemplateTreeDefect, aa as configIntegerRefusal, bn as definedAttributes, c as renderNestedTemplateTreeDefect, ca as replayWarn, ci as refuseMalformedNestedChildOutputs, cn as earliestDefined, cr as s3BucketDomainName, da as requireConfigString, dr as s3BucketWebsiteUrl, ea as toCfnInteger, fn as acquireIdempotencyToken, ft as getCurrentNestedStackContext, ga as markAuxiliaryFailure, gn as readTagList, gt as awaitInFlightSibling, ha as withRetry, hn as planTagDiff, hr as getAccountInfo, ia as configBooleanRefusal, j as resolveDynamoDbDeleteBudgetMs, jn as unchangedBehindSecretReference, k as deleteBudgetKey, kn as slowCcOperationTimeoutMs, ks as decodeListingKey, la as requireConfigArray, lr as s3BucketDualStackDomainName, lt as revertNestedChildFromJournal, mn as DBPROXY_TAG_OPTIONS, na as coerceCfnBoolean, o as isAbsoluteAssetPath, oa as configStringRefusal, or as isSettledInstanceState, ot as getNestedRevertRun, pn as DBPROXY_TAGS_WHAT, pt as withNestedStackContext, qa as carriesSecretMask, ra as coerceCfnInteger, s as listNestedTemplateRows, sa as readConfigString, sr as s3BucketArn, t as DeployEngine, ta as toFiniteNumber, ua as requireConfigObject, un as reserveStackCreateToken, ur as s3BucketRegionalDomainName, va as lambdaFunctionNameForMask, vn as tagMapAsList, vt as getStackRecords, xn as stringifyIfAssigned, ya as sameLambdaFunctionAddress, yn as tagPlanWarning, z as isTruthyCfnBoolean, za as redactSecretsForState, zn as ec2InstanceProtectionSite, zt as redactedDeleteAddressSkip } from "./deploy-engine-CuI_h_Ml.js";
6
- import { n as collectInlinePolicyNamesManagedBySiblings, t as IAMRoleProvider } from "./iam-role-provider-CyM-wxAp.js";
7
- import { a as canonicalizeIpProtocolValue, i as resolveSecretDerivedPrincipals, n as runDestroyForStack, o as resolvedResourceTimeoutMs, r as isResolvableSecretPrincipalList, t as canonicalizeIpv4Cidr } from "./program-Cbxe8TgI.js";
8
- import { RDSProvider } from "./rds-provider-D6_Rcnub.js";
9
- import { NeptuneProvider } from "./neptune-provider-BGVccWhg.js";
10
- import { ASGProvider } from "./asg-provider-DuAHOXvT.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,9 +179,59 @@ 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));
175
189
  }
176
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}`);
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("; ")}`);
234
+ }
177
235
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
178
236
  const oldPolicyName = physicalId.includes(":") ? physicalId.split(":")[0] : physicalId;
179
237
  const log = withDerivedNameMasks(this.logger, createMaskedLogSinks(this.logger, context?.maskSecrets), [[previousProperties["PolicyName"], oldPolicyName]]);
@@ -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
@@ -7771,6 +7890,7 @@ async function reportPossibleOrphans(logicalId, window, log, lookup) {
7771
7890
  const until = new Date(window.ceilingMs).toISOString();
7772
7891
  const remove = lookup.remove;
7773
7892
  const service = lookup.service ?? "API Gateway";
7893
+ const afterward = lookup.afterward ?? "Creating a new one now.";
7774
7894
  const when = remove !== void 0 ? `between ${since} and ${until}` : `at ${since}`;
7775
7895
  let found;
7776
7896
  try {
@@ -7793,10 +7913,10 @@ async function reportPossibleOrphans(logicalId, window, log, lookup) {
7793
7913
  const inspect = [...new Set(shown.map((id) => lookup.inspect(id)))].join(" ; ");
7794
7914
  if (remove !== void 0) {
7795
7915
  const deletion = [...new Set(shown.map((id) => remove(id)))].join(" ; ");
7796
- log.warn(`An earlier ${lookup.action} attempt for ${logicalId} failed without a definite answer, and ${service} may have created ${lookup.subject} then that no cdkd state records. ${found.ids.length} ${lookup.noun} were created ${when} that this deploy did not record: ${shown.join(", ")}${more}. cdkd does not adopt or delete them: nothing listed proves which deploy created one. Creating a new one now. First inspect each candidate: ${inspect}. Only after confirming one is this deploy's orphan and not another deploy's, ${lookup.removeVerb ?? "delete it"}: ${deletion}.${incomplete}`);
7916
+ log.warn(`An earlier ${lookup.action} attempt for ${logicalId} failed without a definite answer, and ${service} may have created ${lookup.subject} then that no cdkd state records. ${found.ids.length} ${lookup.noun} were created ${when} that this deploy did not record: ${shown.join(", ")}${more}. cdkd does not adopt or delete them: nothing listed proves which deploy created one. ${afterward} First inspect each candidate: ${inspect}. Only after confirming one is this deploy's orphan and not another deploy's, ${lookup.removeVerb ?? "delete it"}: ${deletion}.${incomplete}`);
7797
7917
  return;
7798
7918
  }
7799
- log.warn(`An earlier ${lookup.action} attempt for ${logicalId} failed without a definite answer (${when}), and ${service} may have created ${lookup.subject} then that no cdkd state records. ${found.ids.length} ${lookup.noun} match that this deploy did not record: ${shown.join(", ")}${more}. ${service} reports no creation time for them, so any of them may instead be this stack's own recorded one, another stack's, or older than this deploy; cdkd does not adopt or delete them. Creating a new one now. Inspect each before deleting anything: ${inspect}.${incomplete}`);
7919
+ log.warn(`An earlier ${lookup.action} attempt for ${logicalId} failed without a definite answer (${when}), and ${service} may have created ${lookup.subject} then that no cdkd state records. ${found.ids.length} ${lookup.noun} match that this deploy did not record: ${shown.join(", ")}${more}. ${service} reports no creation time for them, so any of them may instead be this stack's own recorded one, another stack's, or older than this deploy; cdkd does not adopt or delete them. ${afterward} Inspect each before deleting anything: ${inspect}.${incomplete}`);
7800
7920
  }
7801
7921
 
7802
7922
  //#endregion
@@ -8715,9 +8835,11 @@ var LambdaMicrovmImageProvider = class {
8715
8835
  ...buildCommonImageInput(properties)
8716
8836
  };
8717
8837
  if (tagList.length > 0) input.tags = Object.fromEntries(tagList.map((t) => [t.Key, t.Value]));
8838
+ let createdArn;
8718
8839
  try {
8719
8840
  const response = await this.client.send(new CreateMicrovmImageCommand(input));
8720
8841
  const imageArn = response.imageArn;
8842
+ createdArn = imageArn;
8721
8843
  if (!imageArn) throw new ProvisioningError(`CreateMicrovmImage succeeded but no imageArn returned for ${logicalId}`, resourceType, logicalId);
8722
8844
  this.logger.debug(`Created MicroVM image ${logicalId}: ${imageArn} (state=${response.state})`);
8723
8845
  const noWait = process.env["CDKD_NO_WAIT"] === "true";
@@ -8740,7 +8862,9 @@ var LambdaMicrovmImageProvider = class {
8740
8862
  })
8741
8863
  };
8742
8864
  } catch (error) {
8743
- 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;
8744
8868
  }
8745
8869
  }
8746
8870
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -9734,18 +9858,20 @@ var DynamoDBTableProvider = class {
9734
9858
  }
9735
9859
  };
9736
9860
  } catch (error) {
9861
+ let tableLeftBehind = false;
9737
9862
  if (tableCreated) {
9738
9863
  try {
9739
9864
  await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
9740
9865
  debug(`Rolled back partially-created DynamoDB table ${tableName}`);
9741
9866
  } catch (cleanupError) {
9867
+ tableLeftBehind = true;
9742
9868
  warn(`Failed to roll back partially-created DynamoDB table ${tableName}: ${describeAwsFailure(cleanupError).detail}`);
9743
9869
  }
9744
9870
  markAuxiliaryFailure(error, logicalId);
9745
9871
  }
9746
- if (error instanceof ProvisioningError) throw error;
9747
- const cause = error instanceof Error ? error : void 0;
9748
- 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;
9749
9875
  }
9750
9876
  }
9751
9877
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -10993,6 +11119,7 @@ var LogsLogGroupProvider = class {
10993
11119
  allowedPattern: /[^a-zA-Z0-9-/_]/g
10994
11120
  })}`;
10995
11121
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
11122
+ let leftBehind = false;
10996
11123
  try {
10997
11124
  const createParams = { logGroupName };
10998
11125
  if (properties["KmsKeyId"]) createParams.kmsKeyId = properties["KmsKeyId"];
@@ -11003,6 +11130,7 @@ var LogsLogGroupProvider = class {
11003
11130
  try {
11004
11131
  await this.logsClient.send(new CreateLogGroupCommand(createParams));
11005
11132
  createdNewLogGroup = true;
11133
+ leftBehind = true;
11006
11134
  } catch (createError) {
11007
11135
  if (createError instanceof ResourceAlreadyExistsException) this.logger.debug(`Log group ${logGroupName} already exists, using existing`);
11008
11136
  else throw createError;
@@ -11048,6 +11176,7 @@ var LogsLogGroupProvider = class {
11048
11176
  } catch (innerError) {
11049
11177
  if (createdNewLogGroup) try {
11050
11178
  await this.logsClient.send(new DeleteLogGroupCommand({ logGroupName }));
11179
+ leftBehind = false;
11051
11180
  this.logger.debug(`Cleaned up partially-created log group ${displaySafe(logicalId)} (${displaySafe(logGroupName)}) after wiring failure`);
11052
11181
  } catch (cleanupError) {
11053
11182
  const mask = maskerOrIdentity(context?.maskSecrets);
@@ -11056,7 +11185,7 @@ var LogsLogGroupProvider = class {
11056
11185
  identifier: logGroupName,
11057
11186
  maskSecrets: mask
11058
11187
  });
11059
- 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.";
11060
11189
  this.logger.warn(mask(`Failed to clean up partially-created log group ${displaySafe(logicalId)} (${displaySafe(mask(logGroupName))}): ${describeAwsFailure(cleanupError).detail}. ${manualStep}`));
11061
11190
  }
11062
11191
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -11069,7 +11198,9 @@ var LogsLogGroupProvider = class {
11069
11198
  } catch (error) {
11070
11199
  const cause = error instanceof Error ? error : void 0;
11071
11200
  const mask = maskerOrIdentity(context?.maskSecrets);
11072
- 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;
11073
11204
  }
11074
11205
  }
11075
11206
  async update(logicalId, physicalId, resourceType, properties, previousProperties, callerContext) {
@@ -11649,7 +11780,7 @@ var CloudWatchAnomalyDetectorProvider = class {
11649
11780
  }
11650
11781
  async delete(logicalId, physicalId, resourceType, properties, context) {
11651
11782
  this.logger.debug(`Deleting CloudWatch anomaly detector ${logicalId}: ${physicalId}`);
11652
- 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);
11653
11784
  const skip = redactedDeleteAddressSkip(this.logger, logicalId, "CloudWatch anomaly detector", redactedDeleteAddressFields({
11654
11785
  Namespace: properties["Namespace"],
11655
11786
  MetricName: properties["MetricName"],
@@ -11657,7 +11788,7 @@ var CloudWatchAnomalyDetectorProvider = class {
11657
11788
  Dimensions: properties["Dimensions"],
11658
11789
  SingleMetricAnomalyDetector: properties["SingleMetricAnomalyDetector"],
11659
11790
  MetricMathAnomalyDetector: properties["MetricMathAnomalyDetector"]
11660
- }));
11791
+ }), context);
11661
11792
  if (skip) return skip;
11662
11793
  try {
11663
11794
  await this.cloudWatchClient.send(new DeleteAnomalyDetectorCommand(buildDeleteParams(properties)));
@@ -12208,6 +12339,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12208
12339
  const type = properties["Type"] || "String";
12209
12340
  const value = properties["Value"];
12210
12341
  if (!value) throw new ProvisioningError(`Value is required for SSM parameter ${displaySafe(logicalId)}`, resourceType, logicalId);
12342
+ let leftBehind = false;
12211
12343
  try {
12212
12344
  const putParams = {
12213
12345
  Name: name,
@@ -12221,6 +12353,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12221
12353
  if (properties["Policies"]) putParams.Policies = properties["Policies"];
12222
12354
  if (properties["DataType"]) putParams.DataType = properties["DataType"];
12223
12355
  await this.ssmClient.send(new PutParameterCommand(putParams));
12356
+ leftBehind = true;
12224
12357
  try {
12225
12358
  if (ssmTags.length > 0) await this.ssmClient.send(new AddTagsToResourceCommand({
12226
12359
  ResourceType: "Parameter",
@@ -12230,6 +12363,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12230
12363
  } catch (innerError) {
12231
12364
  try {
12232
12365
  await this.ssmClient.send(new DeleteParameterCommand({ Name: name }));
12366
+ leftBehind = false;
12233
12367
  debug(`Cleaned up partially-created SSM parameter ${displaySafe(logicalId)} (${displaySafe(mask(name))}) after wiring failure`);
12234
12368
  } catch (cleanupError) {
12235
12369
  const deleteCommand = renderDisableCommand({
@@ -12237,7 +12371,7 @@ var SSMParameterProvider = class SSMParameterProvider {
12237
12371
  identifier: name,
12238
12372
  maskSecrets: mask
12239
12373
  });
12240
- 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.";
12241
12375
  warn(`Failed to clean up partially-created SSM parameter ${displaySafe(logicalId)} (${displaySafe(mask(name))}): ${describeAwsFailure(cleanupError).detail}. ${manualStep}`);
12242
12376
  }
12243
12377
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -12254,7 +12388,9 @@ var SSMParameterProvider = class SSMParameterProvider {
12254
12388
  };
12255
12389
  } catch (error) {
12256
12390
  const cause = error instanceof Error ? error : void 0;
12257
- 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;
12258
12394
  }
12259
12395
  }
12260
12396
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -12532,6 +12668,7 @@ var EventBridgeRuleProvider = class {
12532
12668
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
12533
12669
  const ruleName = properties["Name"] || generateResourceName(logicalId, { maxLength: 64 });
12534
12670
  const targets = properties["Targets"];
12671
+ let leftLiveArn;
12535
12672
  try {
12536
12673
  const heldBefore = targets && targets.length > 0 ? await nameHeldBefore(async () => {
12537
12674
  await this.eventBridgeClient.send(new DescribeRuleCommand({
@@ -12581,6 +12718,7 @@ var EventBridgeRuleProvider = class {
12581
12718
  }));
12582
12719
  this.logger.debug(`Cleaned up partially-created EventBridge rule ${logicalId} (${ruleName}) after wiring failure`);
12583
12720
  } catch (cleanupError) {
12721
+ leftLiveArn = ruleArn;
12584
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()}`)));
12585
12723
  }
12586
12724
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -12591,7 +12729,9 @@ var EventBridgeRuleProvider = class {
12591
12729
  };
12592
12730
  } catch (error) {
12593
12731
  const cause = error instanceof Error ? error : void 0;
12594
- 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;
12595
12735
  }
12596
12736
  }
12597
12737
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -13137,6 +13277,13 @@ function isEc2NotFoundCode(error) {
13137
13277
  const name = error?.name;
13138
13278
  return typeof name === "string" && name.endsWith(".NotFound");
13139
13279
  }
13280
+ const createVpcLatch = new AmbiguousCreateLatch("ec2:CreateVpc");
13281
+ const createSubnetLatch = new AmbiguousCreateLatch("ec2:CreateSubnet");
13282
+ const createInternetGatewayLatch = new AmbiguousCreateLatch("ec2:CreateInternetGateway");
13283
+ const allocateAddressLatch = new AmbiguousCreateLatch("ec2:AllocateAddress");
13284
+ const ec2IdsCreatedByThisProcess = new RecentIdSet();
13285
+ const isUntagged = (tags) => tags === void 0 || tags.length === 0;
13286
+ const ORPHAN_LIST_PAGE_SIZE$2 = 1e3;
13140
13287
  var EC2Provider = class {
13141
13288
  ec2Client;
13142
13289
  ec2AuthorizeClient;
@@ -13263,11 +13410,128 @@ var EC2Provider = class {
13263
13410
  this.ec2Client = awsClients.ec2;
13264
13411
  this.ec2AuthorizeClient = awsClients.ec2SingleSend ?? awsClients.ec2;
13265
13412
  }
13413
+ createClient;
13414
+ getCreateClient() {
13415
+ const shared = this.ec2Client;
13416
+ if (!(shared instanceof EC2Client)) return Promise.resolve(shared);
13417
+ this.createClient ??= shared.config.region().then((region) => withoutServerErrorRetries(new EC2Client({
13418
+ ...ambientClientDefaults(),
13419
+ region
13420
+ })), (error) => {
13421
+ this.createClient = void 0;
13422
+ throw error;
13423
+ });
13424
+ return this.createClient;
13425
+ }
13426
+ async reportPossibleEc2Orphans(logicalId, window, log, lookup) {
13427
+ const aws = pasteableAwsCommand(log.mask);
13428
+ const regionArg = await orphanCommandRegionArg(this.ec2Client, aws);
13429
+ await reportPossibleOrphans(logicalId, window, log, {
13430
+ action: lookup.action,
13431
+ service: "EC2",
13432
+ listAction: lookup.listAction,
13433
+ subject: lookup.subject,
13434
+ noun: lookup.noun,
13435
+ ...lookup.afterward !== void 0 && { afterward: lookup.afterward },
13436
+ list: lookup.list,
13437
+ inspect: (id) => lookup.inspect(aws, id, regionArg)
13438
+ });
13439
+ }
13440
+ async reportPossibleOrphanVpcs(logicalId, cidrBlock, window, context) {
13441
+ const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
13442
+ const wanted = canonicalizeIpv4Cidr(cidrBlock) ?? cidrBlock;
13443
+ await this.reportPossibleEc2Orphans(logicalId, window, log, {
13444
+ action: "CreateVpc",
13445
+ listAction: "DescribeVpcs",
13446
+ subject: `a VPC with CIDR ${log.value(cidrBlock)}`,
13447
+ noun: "untagged VPC(s)",
13448
+ list: () => collectOrphanIds(async (token) => {
13449
+ const page = await this.ec2Client.send(new DescribeVpcsCommand({
13450
+ MaxResults: ORPHAN_LIST_PAGE_SIZE$2,
13451
+ NextToken: token
13452
+ }));
13453
+ return {
13454
+ items: page.Vpcs ?? [],
13455
+ next: page.NextToken
13456
+ };
13457
+ }, (vpc) => vpc.VpcId !== void 0 && vpc.IsDefault !== true && isUntagged(vpc.Tags) && vpc.CidrBlock !== void 0 && (canonicalizeIpv4Cidr(vpc.CidrBlock) ?? vpc.CidrBlock) === wanted && !ec2IdsCreatedByThisProcess.has(vpc.VpcId) ? vpc.VpcId : void 0),
13458
+ inspect: (aws, id, regionArg) => aws`aws ec2 describe-vpcs --vpc-ids ${id}${regionArg}`.render()
13459
+ });
13460
+ }
13461
+ async reportPossibleOrphanSubnets(logicalId, vpcId, cidrBlock, window, context) {
13462
+ const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
13463
+ const wanted = canonicalizeIpv4Cidr(cidrBlock) ?? cidrBlock;
13464
+ await this.reportPossibleEc2Orphans(logicalId, window, log, {
13465
+ action: "CreateSubnet",
13466
+ listAction: "DescribeSubnets",
13467
+ subject: `a subnet with CIDR ${log.value(cidrBlock)} in VPC ${log.value(vpcId)}`,
13468
+ noun: "untagged subnet(s)",
13469
+ afterward: "Creating it again, which fails with InvalidSubnet.Conflict while that subnet exists.",
13470
+ list: () => collectOrphanIds(async (token) => {
13471
+ const page = await this.ec2Client.send(new DescribeSubnetsCommand({
13472
+ Filters: [{
13473
+ Name: "vpc-id",
13474
+ Values: [vpcId]
13475
+ }],
13476
+ MaxResults: ORPHAN_LIST_PAGE_SIZE$2,
13477
+ NextToken: token
13478
+ }));
13479
+ return {
13480
+ items: page.Subnets ?? [],
13481
+ next: page.NextToken
13482
+ };
13483
+ }, (subnet) => subnet.SubnetId !== void 0 && isUntagged(subnet.Tags) && subnet.CidrBlock !== void 0 && (canonicalizeIpv4Cidr(subnet.CidrBlock) ?? subnet.CidrBlock) === wanted && !ec2IdsCreatedByThisProcess.has(subnet.SubnetId) ? subnet.SubnetId : void 0),
13484
+ inspect: (aws, id, regionArg) => aws`aws ec2 describe-subnets --subnet-ids ${id}${regionArg}`.render()
13485
+ });
13486
+ }
13487
+ async reportPossibleOrphanInternetGateways(logicalId, window, context) {
13488
+ const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
13489
+ await this.reportPossibleEc2Orphans(logicalId, window, log, {
13490
+ action: "CreateInternetGateway",
13491
+ listAction: "DescribeInternetGateways",
13492
+ subject: "an internet gateway",
13493
+ noun: "detached, untagged internet gateway(s)",
13494
+ list: () => collectOrphanIds(async (token) => {
13495
+ const page = await this.ec2Client.send(new DescribeInternetGatewaysCommand({
13496
+ MaxResults: ORPHAN_LIST_PAGE_SIZE$2,
13497
+ NextToken: token
13498
+ }));
13499
+ return {
13500
+ items: page.InternetGateways ?? [],
13501
+ next: page.NextToken
13502
+ };
13503
+ }, (igw) => igw.InternetGatewayId !== void 0 && (igw.Attachments ?? []).length === 0 && isUntagged(igw.Tags) && !ec2IdsCreatedByThisProcess.has(igw.InternetGatewayId) ? igw.InternetGatewayId : void 0),
13504
+ inspect: (aws, id, regionArg) => aws`aws ec2 describe-internet-gateways --internet-gateway-ids ${id}${regionArg}`.render()
13505
+ });
13506
+ }
13507
+ async reportPossibleOrphanAddresses(logicalId, input, window, context) {
13508
+ const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
13509
+ await this.reportPossibleEc2Orphans(logicalId, window, log, {
13510
+ action: "AllocateAddress",
13511
+ listAction: "DescribeAddresses",
13512
+ subject: `an Elastic IP address (domain ${log.value(input.Domain ?? "vpc")})`,
13513
+ noun: "unassociated, untagged Elastic IP address(es)",
13514
+ list: async () => {
13515
+ const response = await this.ec2Client.send(new DescribeAddressesCommand({}));
13516
+ const ids = [];
13517
+ for (const address of response.Addresses ?? []) {
13518
+ const id = address.AllocationId;
13519
+ if (id === void 0 || (address.Domain ?? "vpc") !== (input.Domain ?? "vpc") || address.AssociationId !== void 0 || !isUntagged(address.Tags) || input.NetworkBorderGroup !== void 0 && address.NetworkBorderGroup !== input.NetworkBorderGroup || input.PublicIpv4Pool !== void 0 && address.PublicIpv4Pool !== input.PublicIpv4Pool || ec2IdsCreatedByThisProcess.has(id)) continue;
13520
+ ids.push(id);
13521
+ }
13522
+ return {
13523
+ ids,
13524
+ truncated: false
13525
+ };
13526
+ },
13527
+ inspect: (aws, id, regionArg) => aws`aws ec2 describe-addresses --allocation-ids ${id}${regionArg}`.render()
13528
+ });
13529
+ }
13266
13530
  async create(logicalId, resourceType, properties, context) {
13267
13531
  switch (resourceType) {
13268
- case "AWS::EC2::VPC": return this.createVpc(logicalId, resourceType, properties);
13269
- case "AWS::EC2::Subnet": return this.createSubnet(logicalId, resourceType, properties);
13270
- case "AWS::EC2::InternetGateway": return this.createInternetGateway(logicalId, resourceType, properties);
13532
+ case "AWS::EC2::VPC": return this.createVpc(logicalId, resourceType, properties, context);
13533
+ case "AWS::EC2::Subnet": return this.createSubnet(logicalId, resourceType, properties, context);
13534
+ case "AWS::EC2::InternetGateway": return this.createInternetGateway(logicalId, resourceType, properties, context);
13271
13535
  case "AWS::EC2::EIP": return this.createEip(logicalId, resourceType, properties, context);
13272
13536
  case "AWS::EC2::VPCGatewayAttachment": return this.createVpcGatewayAttachment(logicalId, resourceType, properties, context);
13273
13537
  case "AWS::EC2::NatGateway": return this.createNatGateway(logicalId, resourceType, properties);
@@ -13344,16 +13608,29 @@ var EC2Provider = class {
13344
13608
  default: return;
13345
13609
  }
13346
13610
  }
13347
- async createVpc(logicalId, resourceType, properties) {
13611
+ async createVpc(logicalId, resourceType, properties, context) {
13348
13612
  this.logger.debug(`Creating VPC ${logicalId}`);
13349
13613
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
13350
13614
  const cidrBlock = properties["CidrBlock"];
13351
13615
  if (!cidrBlock) throw new ProvisioningError(`CidrBlock is required for VPC ${logicalId}`, resourceType, logicalId);
13616
+ let leftBehindId;
13352
13617
  try {
13353
- const vpcId = (await this.ec2Client.send(new CreateVpcCommand({
13354
- CidrBlock: cidrBlock,
13355
- InstanceTenancy: properties["InstanceTenancy"] ?? void 0
13356
- }))).Vpc.VpcId;
13618
+ const createClient = await this.getCreateClient();
13619
+ const orphanWindow = createVpcLatch.take(logicalId);
13620
+ if (orphanWindow !== void 0) await this.reportPossibleOrphanVpcs(logicalId, cidrBlock, orphanWindow, context);
13621
+ const attemptStartMs = Date.now();
13622
+ let response;
13623
+ try {
13624
+ response = await createClient.send(new CreateVpcCommand({
13625
+ CidrBlock: cidrBlock,
13626
+ InstanceTenancy: properties["InstanceTenancy"] ?? void 0
13627
+ }));
13628
+ } catch (error) {
13629
+ createVpcLatch.noteFailure(logicalId, error, attemptStartMs, orphanWindow);
13630
+ throw error;
13631
+ }
13632
+ const vpcId = response.Vpc.VpcId;
13633
+ ec2IdsCreatedByThisProcess.add(vpcId);
13357
13634
  let defaultSgId;
13358
13635
  try {
13359
13636
  if (properties["EnableDnsHostnames"] === true || properties["EnableDnsHostnames"] === "true") await this.ec2Client.send(new ModifyVpcAttributeCommand({
@@ -13382,6 +13659,7 @@ var EC2Provider = class {
13382
13659
  await this.ec2Client.send(new DeleteVpcCommand({ VpcId: vpcId }));
13383
13660
  this.logger.debug(`Cleaned up partially-created VPC ${logicalId} (${vpcId}) after wiring failure`);
13384
13661
  } catch (cleanupError) {
13662
+ leftBehindId = vpcId;
13385
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()}`);
13386
13664
  }
13387
13665
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -13397,7 +13675,9 @@ var EC2Provider = class {
13397
13675
  };
13398
13676
  } catch (error) {
13399
13677
  const cause = error instanceof Error ? error : void 0;
13400
- 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;
13401
13681
  }
13402
13682
  }
13403
13683
  async updateVpc(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -13491,19 +13771,31 @@ var EC2Provider = class {
13491
13771
  return;
13492
13772
  }
13493
13773
  }
13494
- async createSubnet(logicalId, resourceType, properties) {
13774
+ async createSubnet(logicalId, resourceType, properties, context) {
13495
13775
  this.logger.debug(`Creating Subnet ${logicalId}`);
13496
13776
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
13497
13777
  const vpcId = properties["VpcId"];
13498
13778
  const cidrBlock = properties["CidrBlock"];
13499
13779
  if (!vpcId || !cidrBlock) throw new ProvisioningError(`VpcId and CidrBlock are required for Subnet ${logicalId}`, resourceType, logicalId);
13780
+ let leftBehindId;
13500
13781
  try {
13501
- const response = await this.ec2Client.send(new CreateSubnetCommand({
13502
- VpcId: vpcId,
13503
- CidrBlock: cidrBlock,
13504
- AvailabilityZone: properties["AvailabilityZone"] ?? void 0
13505
- }));
13782
+ const createClient = await this.getCreateClient();
13783
+ const orphanWindow = createSubnetLatch.take(logicalId);
13784
+ if (orphanWindow !== void 0) await this.reportPossibleOrphanSubnets(logicalId, vpcId, cidrBlock, orphanWindow, context);
13785
+ const attemptStartMs = Date.now();
13786
+ let response;
13787
+ try {
13788
+ response = await createClient.send(new CreateSubnetCommand({
13789
+ VpcId: vpcId,
13790
+ CidrBlock: cidrBlock,
13791
+ AvailabilityZone: properties["AvailabilityZone"] ?? void 0
13792
+ }));
13793
+ } catch (error) {
13794
+ createSubnetLatch.noteFailure(logicalId, error, attemptStartMs, orphanWindow);
13795
+ throw error;
13796
+ }
13506
13797
  const subnetId = response.Subnet.SubnetId;
13798
+ ec2IdsCreatedByThisProcess.add(subnetId);
13507
13799
  const availabilityZone = response.Subnet.AvailabilityZone;
13508
13800
  try {
13509
13801
  await this.applyTags(subnetId, desiredTags, logicalId);
@@ -13520,6 +13812,7 @@ var EC2Provider = class {
13520
13812
  await this.ec2Client.send(new DeleteSubnetCommand({ SubnetId: subnetId }));
13521
13813
  this.logger.debug(`Cleaned up partially-created Subnet ${logicalId} (${subnetId}) after wiring failure`);
13522
13814
  } catch (cleanupError) {
13815
+ leftBehindId = subnetId;
13523
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()}`);
13524
13817
  }
13525
13818
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -13534,7 +13827,9 @@ var EC2Provider = class {
13534
13827
  };
13535
13828
  } catch (error) {
13536
13829
  const cause = error instanceof Error ? error : void 0;
13537
- 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;
13538
13833
  }
13539
13834
  }
13540
13835
  async updateSubnet(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -13647,11 +13942,23 @@ var EC2Provider = class {
13647
13942
  return;
13648
13943
  }
13649
13944
  }
13650
- async createInternetGateway(logicalId, resourceType, properties) {
13945
+ async createInternetGateway(logicalId, resourceType, properties, context) {
13651
13946
  this.logger.debug(`Creating InternetGateway ${logicalId}`);
13652
13947
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
13653
13948
  try {
13654
- const igwId = (await this.ec2Client.send(new CreateInternetGatewayCommand({}))).InternetGateway.InternetGatewayId;
13949
+ const createClient = await this.getCreateClient();
13950
+ const orphanWindow = createInternetGatewayLatch.take(logicalId);
13951
+ if (orphanWindow !== void 0) await this.reportPossibleOrphanInternetGateways(logicalId, orphanWindow, context);
13952
+ const attemptStartMs = Date.now();
13953
+ let response;
13954
+ try {
13955
+ response = await createClient.send(new CreateInternetGatewayCommand({}));
13956
+ } catch (error) {
13957
+ createInternetGatewayLatch.noteFailure(logicalId, error, attemptStartMs, orphanWindow);
13958
+ throw error;
13959
+ }
13960
+ const igwId = response.InternetGateway.InternetGatewayId;
13961
+ ec2IdsCreatedByThisProcess.add(igwId);
13655
13962
  await this.applyTags(igwId, desiredTags, logicalId);
13656
13963
  this.logger.debug(`Successfully created InternetGateway ${logicalId}: ${igwId}`);
13657
13964
  return {
@@ -13708,15 +14015,33 @@ var EC2Provider = class {
13708
14015
  let allocationId;
13709
14016
  let publicIp;
13710
14017
  let created = false;
14018
+ let createdId;
13711
14019
  try {
13712
- const response = await this.ec2Client.send(new AllocateAddressCommand({
14020
+ const input = {
13713
14021
  Domain: requireConfigString(properties["Domain"], "vpc", "AWS::EC2::EIP Domain", replayWarn(this.logger, context)),
13714
14022
  NetworkBorderGroup: properties["NetworkBorderGroup"],
13715
14023
  PublicIpv4Pool: properties["PublicIpv4Pool"]
13716
- }));
14024
+ };
14025
+ const createClient = await this.getCreateClient();
14026
+ const orphanWindow = allocateAddressLatch.take(logicalId);
14027
+ if (orphanWindow !== void 0) await this.reportPossibleOrphanAddresses(logicalId, input, orphanWindow, context);
14028
+ const attemptStartMs = Date.now();
14029
+ let response;
14030
+ try {
14031
+ response = await createClient.send(new AllocateAddressCommand(input));
14032
+ } catch (error) {
14033
+ allocateAddressLatch.noteFailure(logicalId, error, attemptStartMs, orphanWindow);
14034
+ throw error;
14035
+ }
13717
14036
  created = true;
13718
14037
  allocationId = response.AllocationId;
13719
14038
  publicIp = response.PublicIp;
14039
+ try {
14040
+ createdId = this.eipPhysicalId(logicalId, publicIp, allocationId);
14041
+ } catch {
14042
+ createdId = allocationId;
14043
+ }
14044
+ ec2IdsCreatedByThisProcess.add(allocationId);
13720
14045
  await this.applyTags(allocationId, desiredTags, logicalId);
13721
14046
  const instanceId = properties["InstanceId"];
13722
14047
  if (instanceId) {
@@ -13733,7 +14058,9 @@ var EC2Provider = class {
13733
14058
  } catch (error) {
13734
14059
  if (created) markAuxiliaryFailure(error, logicalId);
13735
14060
  const cause = error instanceof Error ? error : void 0;
13736
- 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;
13737
14064
  }
13738
14065
  return {
13739
14066
  physicalId: this.eipPhysicalId(logicalId, publicIp, allocationId, {
@@ -13878,6 +14205,7 @@ var EC2Provider = class {
13878
14205
  scope: "CreateNatGateway",
13879
14206
  logicalId
13880
14207
  });
14208
+ let createdId;
13881
14209
  try {
13882
14210
  const natGatewayId = (await this.ec2Client.send(new CreateNatGatewayCommand({
13883
14211
  ClientToken: natGatewayToken.value,
@@ -13889,6 +14217,7 @@ var EC2Provider = class {
13889
14217
  SecondaryPrivateIpAddresses: properties["SecondaryPrivateIpAddresses"],
13890
14218
  SecondaryPrivateIpAddressCount: properties["SecondaryPrivateIpAddressCount"]
13891
14219
  }))).NatGateway.NatGatewayId;
14220
+ createdId = natGatewayId;
13892
14221
  await this.applyTags(natGatewayId, desiredTags, logicalId);
13893
14222
  if (process.env["CDKD_NO_WAIT"] !== "true") {
13894
14223
  this.logger.debug(`Waiting for NatGateway ${natGatewayId} to reach available state...`);
@@ -13908,7 +14237,9 @@ var EC2Provider = class {
13908
14237
  };
13909
14238
  } catch (error) {
13910
14239
  const cause = error instanceof Error ? error : void 0;
13911
- 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;
13912
14243
  }
13913
14244
  }
13914
14245
  updateNatGateway(logicalId, physicalId) {
@@ -14134,8 +14465,9 @@ var EC2Provider = class {
14134
14465
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
14135
14466
  const groupDescription = properties["GroupDescription"];
14136
14467
  if (!groupDescription) throw new ProvisioningError(`GroupDescription is required for SecurityGroup ${logicalId}`, resourceType, logicalId);
14468
+ let leftBehindId;
14137
14469
  try {
14138
- const groupId = (await this.ec2Client.send(new CreateSecurityGroupCommand({
14470
+ const groupId = (await (await this.getCreateClient()).send(new CreateSecurityGroupCommand({
14139
14471
  GroupName: properties["GroupName"] ?? logicalId,
14140
14472
  Description: groupDescription,
14141
14473
  VpcId: properties["VpcId"] ?? void 0
@@ -14158,6 +14490,7 @@ var EC2Provider = class {
14158
14490
  await this.ec2Client.send(new DeleteSecurityGroupCommand({ GroupId: groupId }));
14159
14491
  this.logger.debug(`Cleaned up partially-created SecurityGroup ${logicalId} (${groupId}) after wiring failure`);
14160
14492
  } catch (cleanupError) {
14493
+ leftBehindId = groupId;
14161
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()}`);
14162
14495
  }
14163
14496
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -14172,7 +14505,9 @@ var EC2Provider = class {
14172
14505
  };
14173
14506
  } catch (error) {
14174
14507
  const cause = error instanceof Error ? error : void 0;
14175
- 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;
14176
14511
  }
14177
14512
  }
14178
14513
  async authorizeInlineIngress(groupId, ingressRules) {
@@ -14560,7 +14895,7 @@ var EC2Provider = class {
14560
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`);
14561
14896
  const revoked = keptFor !== void 0 ? void 0 : await this.deleteSecurityGroupIngress(logicalId, physicalId, resourceType, previousProperties, recordedAttributes ? { recordedAttributes } : void 0);
14562
14897
  const revokeSkip = deleteSkipReason(revoked);
14563
- 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));
14564
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);
14565
14900
  return {
14566
14901
  physicalId: createResult.physicalId,
@@ -14617,7 +14952,7 @@ var EC2Provider = class {
14617
14952
  SourceSecurityGroupId: properties["SourceSecurityGroupId"],
14618
14953
  SourceSecurityGroupOwnerId: properties["SourceSecurityGroupOwnerId"],
14619
14954
  SourcePrefixListId: properties["SourcePrefixListId"]
14620
- }));
14955
+ }), context);
14621
14956
  if (redactedSkip) return redactedSkip;
14622
14957
  }
14623
14958
  const ipPermission = properties ? this.withoutDescription(this.buildIpPermission(properties)) : {
@@ -14656,6 +14991,7 @@ var EC2Provider = class {
14656
14991
  const imageId = properties["ImageId"];
14657
14992
  if (!imageId) throw new ProvisioningError(`ImageId is required for EC2 Instance ${logicalId}`, resourceType, logicalId);
14658
14993
  const instanceType = requireConfigString(properties["InstanceType"], "t3.micro", "AWS::EC2::Instance InstanceType", replayWarn(this.logger, context));
14994
+ let leftBehindId;
14659
14995
  try {
14660
14996
  const securityGroupIds = properties["SecurityGroupIds"];
14661
14997
  const securityGroups = properties["SecurityGroups"];
@@ -14723,14 +15059,15 @@ var EC2Provider = class {
14723
15059
  runInstancesToken.release();
14724
15060
  this.logger.debug(`Terminate requested for partially-created EC2 Instance ${logicalId} (${instanceId}) after wiring failure (not waiting for terminated state)`);
14725
15061
  } catch (cleanupError) {
15062
+ leftBehindId = instanceId;
14726
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()}`);
14727
15064
  }
14728
15065
  throw markAuxiliaryFailure(innerError, logicalId);
14729
15066
  }
14730
15067
  } catch (error) {
14731
- if (error instanceof ProvisioningError) throw error;
14732
- const cause = error instanceof Error ? error : void 0;
14733
- 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;
14734
15071
  }
14735
15072
  }
14736
15073
  normalizeIamInstanceProfile(raw) {
@@ -15214,7 +15551,10 @@ var EC2Provider = class {
15214
15551
  this.logger.debug(`Deleting NetworkAclEntry ${logicalId}: ${physicalId}`);
15215
15552
  const parts = physicalId.split("|");
15216
15553
  if (parts.length < 3) {
15217
- 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
+ }));
15218
15558
  return compositeIdSkipResult();
15219
15559
  }
15220
15560
  const networkAclId = parts[0];
@@ -16772,6 +17112,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
16772
17112
  const block = properties[key];
16773
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);
16774
17114
  }
17115
+ let stageLeftBehind = false;
16775
17116
  try {
16776
17117
  const rawCacheClusterSize = properties["CacheClusterSize"];
16777
17118
  await this.apiGatewayClient.send(new CreateStageCommand({
@@ -16821,6 +17162,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
16821
17162
  stageName
16822
17163
  }));
16823
17164
  } catch (cleanupError) {
17165
+ stageLeftBehind = true;
16824
17166
  log.warn(`Failed to clean up stage ${log.value(stageName)} after a post-create patch failure: ${describeAwsFailure(cleanupError).detail}`);
16825
17167
  }
16826
17168
  throw markAuxiliaryFailure(patchError, logicalId);
@@ -16832,7 +17174,9 @@ var ApiGatewayProvider = class ApiGatewayProvider {
16832
17174
  };
16833
17175
  } catch (error) {
16834
17176
  const cause = error instanceof Error ? error : void 0;
16835
- 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;
16836
17180
  }
16837
17181
  }
16838
17182
  async updateStage(logicalId, physicalId, resourceType, properties, previousProperties, log) {
@@ -17038,6 +17382,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17038
17382
  httpMethod
17039
17383
  ].map((id) => log.value(id));
17040
17384
  const methodPath = maskedIds.join("/");
17385
+ let methodLeftBehind = false;
17041
17386
  try {
17042
17387
  await this.apiGatewayClient.send(new PutMethodCommand({
17043
17388
  restApiId,
@@ -17111,6 +17456,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17111
17456
  }));
17112
17457
  log.debug(`Cleaned up partially-created API Gateway Method ${logicalId} (${methodPath}) after wiring failure`);
17113
17458
  } catch (cleanupError) {
17459
+ methodLeftBehind = true;
17114
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()}`);
17115
17461
  }
17116
17462
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -17122,7 +17468,9 @@ var ApiGatewayProvider = class ApiGatewayProvider {
17122
17468
  };
17123
17469
  } catch (error) {
17124
17470
  const cause = error instanceof Error ? error : void 0;
17125
- 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;
17126
17474
  }
17127
17475
  }
17128
17476
  async updateMethod(logicalId, physicalId, resourceType, properties, previousProperties, log) {
@@ -21094,7 +21442,9 @@ var ECSProvider = class {
21094
21442
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
21095
21443
  const client = this.getClient();
21096
21444
  const serviceName = properties["ServiceName"] || generateResourceName(logicalId, { maxLength: 255 });
21445
+ let survivorArn;
21097
21446
  try {
21447
+ const heldBefore = process.env["CDKD_FULL_WAIT"] === "true" ? await nameHeldBefore(() => this.serviceNameHeld(properties["Cluster"], serviceName), (error) => hasErrorName(error, ["ClusterNotFoundException"])) : "free";
21098
21448
  const service = (await client.send(new CreateServiceCommand({
21099
21449
  cluster: properties["Cluster"],
21100
21450
  serviceName,
@@ -21132,7 +21482,9 @@ var ECSProvider = class {
21132
21482
  const cleanupCluster = properties["Cluster"];
21133
21483
  const clusterArg = cleanupCluster ? aws` --cluster ${cleanupCluster}` : aws``;
21134
21484
  const listStopped = aws`aws ecs list-tasks${clusterArg} --desired-status STOPPED`;
21135
- 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 {
21136
21488
  await client.send(new DeleteServiceCommand({
21137
21489
  cluster: properties["Cluster"],
21138
21490
  service: service.serviceArn,
@@ -21140,7 +21492,8 @@ var ECSProvider = class {
21140
21492
  }));
21141
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()}`);
21142
21494
  } catch (cleanupError) {
21143
- 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()}`);
21144
21497
  }
21145
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 });
21146
21499
  }
@@ -21153,9 +21506,24 @@ var ECSProvider = class {
21153
21506
  };
21154
21507
  } catch (error) {
21155
21508
  const cause = error instanceof Error ? error : void 0;
21156
- 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
+ });
21157
21514
  }
21158
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
+ }
21159
21527
  async settleService(logicalId, serviceRef, cluster, log) {
21160
21528
  if (process.env["CDKD_FULL_WAIT"] === "true") {
21161
21529
  log.debug(`Waiting for ECS service ${logicalId} to reach steady state...`);
@@ -22335,6 +22703,7 @@ var ELBv2Provider = class {
22335
22703
  async createLoadBalancer(logicalId, resourceType, properties, maskSecrets) {
22336
22704
  this.logger.debug(`Creating LoadBalancer ${logicalId}`);
22337
22705
  const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
22706
+ let leftBehindArn;
22338
22707
  try {
22339
22708
  const lbName = sentElbv2Name(properties, logicalId);
22340
22709
  const lbAttributes = this.normalizeAttributes(properties["LoadBalancerAttributes"]);
@@ -22398,6 +22767,7 @@ var ELBv2Provider = class {
22398
22767
  await this.getClient().send(new DeleteLoadBalancerCommand({ LoadBalancerArn: lbArn }));
22399
22768
  this.logger.debug(`Cleaned up partially-created LoadBalancer ${logicalId} (${lbArn}) after wiring failure`);
22400
22769
  } catch (cleanupError) {
22770
+ leftBehindArn = lbArn;
22401
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()}`);
22402
22772
  }
22403
22773
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -22408,7 +22778,9 @@ var ELBv2Provider = class {
22408
22778
  };
22409
22779
  } catch (error) {
22410
22780
  const cause = error instanceof Error ? error : void 0;
22411
- 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;
22412
22784
  }
22413
22785
  }
22414
22786
  async updateLoadBalancer(logicalId, physicalId, resourceType, properties, previousProperties, callerMaskSecrets, fromReadback = false) {
@@ -22616,6 +22988,7 @@ var ELBv2Provider = class {
22616
22988
  const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
22617
22989
  const desiredTargets = readTargets(properties["Targets"], "desired");
22618
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;
22619
22992
  try {
22620
22993
  const matcher = properties["Matcher"];
22621
22994
  const tgName = sentElbv2Name(properties, logicalId);
@@ -22666,6 +23039,7 @@ var ELBv2Provider = class {
22666
23039
  await this.getClient().send(new DeleteTargetGroupCommand({ TargetGroupArn: tgArn }));
22667
23040
  this.logger.debug(`Cleaned up partially-created TargetGroup ${logicalId} (${tgArn}) after wiring failure`);
22668
23041
  } catch (cleanupError) {
23042
+ leftBehindArn = tgArn;
22669
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()}`);
22670
23044
  }
22671
23045
  throw markAuxiliaryFailure(innerError, logicalId);
@@ -22676,7 +23050,9 @@ var ELBv2Provider = class {
22676
23050
  };
22677
23051
  } catch (error) {
22678
23052
  const cause = error instanceof Error ? error : void 0;
22679
- 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;
22680
23056
  }
22681
23057
  }
22682
23058
  async updateTargetGroup(logicalId, physicalId, resourceType, properties, previousProperties, callerMaskSecrets, fromReadback = false) {
@@ -22774,14 +23150,30 @@ var ELBv2Provider = class {
22774
23150
  throw new ProvisioningError(`Failed to delete TargetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
22775
23151
  }
22776
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
+ }
22777
23166
  async createListener(logicalId, resourceType, properties, maskSecrets) {
22778
23167
  this.logger.debug(`Creating Listener ${logicalId}`);
22779
23168
  const tags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
23169
+ let leftBehindArn;
22780
23170
  try {
22781
23171
  const defaultActions = this.convertActions(properties["DefaultActions"]);
22782
23172
  const certificates = this.convertCertificates(properties["Certificates"]);
22783
23173
  const alpnPolicy = properties["AlpnPolicy"];
22784
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";
22785
23177
  const listener = (await this.getClient().send(new CreateListenerCommand({
22786
23178
  LoadBalancerArn: properties["LoadBalancerArn"],
22787
23179
  Port: properties["Port"] !== void 0 ? Number(properties["Port"]) : void 0,
@@ -22797,7 +23189,6 @@ var ELBv2Provider = class {
22797
23189
  const listenerArn = listener.ListenerArn;
22798
23190
  this.logger.debug(`Successfully created Listener ${logicalId}: ${listenerArn}`);
22799
23191
  try {
22800
- const listenerAttributes = this.normalizeAttributes(properties["ListenerAttributes"]);
22801
23192
  if (listenerAttributes.length > 0) {
22802
23193
  const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
22803
23194
  try {
@@ -22815,12 +23206,17 @@ var ELBv2Provider = class {
22815
23206
  this.logger.debug(`Applied ${listenerAttributes.length} ListenerAttribute(s) for ${logicalId}`);
22816
23207
  }
22817
23208
  } catch (innerError) {
22818
- 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()}`));
22819
23214
  try {
22820
23215
  await this.getClient().send(new DeleteListenerCommand({ ListenerArn: listenerArn }));
22821
23216
  this.logger.debug(`Cleaned up partially-created Listener ${logicalId} (${listenerArn}) after attributes-wiring failure`);
22822
23217
  } catch (cleanupError) {
22823
- 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()}`);
22824
23220
  }
22825
23221
  throw markAuxiliaryFailure(innerError, logicalId);
22826
23222
  }
@@ -22830,7 +23226,9 @@ var ELBv2Provider = class {
22830
23226
  };
22831
23227
  } catch (error) {
22832
23228
  const cause = error instanceof Error ? error : void 0;
22833
- 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;
22834
23232
  }
22835
23233
  }
22836
23234
  async updateListener(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets, fromReadback = false) {
@@ -23362,24 +23760,29 @@ var RDSDBProxyProvider = class {
23362
23760
  let vpcId;
23363
23761
  const deadline = Date.now() + POLL_TIMEOUT_MS$1;
23364
23762
  let status;
23365
- while (Date.now() < deadline) {
23366
- try {
23367
- const proxy = (await client.send(new DescribeDBProxiesCommand({ DBProxyName: dbProxyName }))).DBProxies?.[0];
23368
- status = proxy?.Status;
23369
- if (status === "available") {
23370
- endpoint = proxy?.Endpoint;
23371
- dbProxyArn = proxy?.DBProxyArn;
23372
- vpcId = proxy?.VpcId;
23373
- 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);
23374
23778
  }
23375
- if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxy ${dbProxyName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyName);
23376
- } catch (error) {
23377
- if (error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
23378
- else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyName);
23779
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS$1));
23379
23780
  }
23380
- 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;
23381
23785
  }
23382
- if (!endpoint || !dbProxyArn) throw new ProvisioningError(`Timed out waiting for DBProxy ${dbProxyName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyName);
23383
23786
  return {
23384
23787
  physicalId: dbProxyName,
23385
23788
  attributes: definedAttributes({
@@ -23660,26 +24063,31 @@ var RDSDBProxyEndpointProvider = class {
23660
24063
  let vpcId;
23661
24064
  const deadline = Date.now() + POLL_TIMEOUT_MS;
23662
24065
  let status;
23663
- while (Date.now() < deadline) {
23664
- try {
23665
- const ep = (await client.send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: dbProxyEndpointName }))).DBProxyEndpoints?.[0];
23666
- status = ep?.Status;
23667
- this.logger.debug(`DBProxyEndpoint ${dbProxyEndpointName} poll: status=${status ?? "not-yet-visible"}`);
23668
- if (status === "available") {
23669
- endpoint = ep?.Endpoint;
23670
- arn = ep?.DBProxyEndpointArn;
23671
- isDefault = ep?.IsDefault;
23672
- vpcId = ep?.VpcId;
23673
- 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);
23674
24083
  }
23675
- if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxyEndpoint ${dbProxyEndpointName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyEndpointName);
23676
- } catch (error) {
23677
- if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
23678
- else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyEndpointName);
24084
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
23679
24085
  }
23680
- 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;
23681
24090
  }
23682
- if (!endpoint || !arn) throw new ProvisioningError(`Timed out waiting for DBProxyEndpoint ${dbProxyEndpointName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyEndpointName);
23683
24091
  return {
23684
24092
  physicalId: dbProxyEndpointName,
23685
24093
  attributes: definedAttributes({
@@ -24004,7 +24412,11 @@ var RDSDBProxyTargetGroupProvider = class {
24004
24412
  Tags: tags
24005
24413
  }));
24006
24414
  } catch (error) {
24007
- 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
+ });
24008
24420
  }
24009
24421
  return {
24010
24422
  physicalId: targetGroupArn,
@@ -24036,8 +24448,10 @@ var RDSDBProxyTargetGroupProvider = class {
24036
24448
  }
24037
24449
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId, "Tags", DBPROXY_TAGS_WHAT, DBPROXY_TAG_OPTIONS);
24038
24450
  if (context?.expectedRegion) assertRegionMatch(await this.getClient().config.region(), context.expectedRegion, resourceType, logicalId, physicalId, "pre-update");
24039
- if (exemptedBySecretReference.length > 0) {
24040
- 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";
24041
24455
  const base = context?.maskSecrets ?? ((t) => t);
24042
24456
  const maskNames = (text) => {
24043
24457
  let out = text;
@@ -24060,11 +24474,11 @@ var RDSDBProxyTargetGroupProvider = class {
24060
24474
  }
24061
24475
  if (lookupFailure !== void 0) {
24062
24476
  const failureClass = lookupFailure instanceof Error && lookupFailure.name !== "" ? lookupFailure.name : "an unreadable failure";
24063
- 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);
24064
24478
  markRedactedCause(refusal);
24065
24479
  throw refusal;
24066
24480
  }
24067
- 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));
24068
24482
  }
24069
24483
  const next = readTargetLists({
24070
24484
  DBClusterIdentifiers: properties["DBClusterIdentifiers"],
@@ -24298,7 +24712,7 @@ var RDSDBProxyTargetGroupProvider = class {
24298
24712
  }
24299
24713
  return result;
24300
24714
  }
24301
- async retireRegistrationAfterFailedCreate(original, dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, maskSecrets) {
24715
+ async retireRegistrationAfterFailedCreate(original, dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, maskSecrets, leftBehind) {
24302
24716
  const clusters = dbClusterIdentifiers?.length ? dbClusterIdentifiers : void 0;
24303
24717
  const instances = dbInstanceIdentifiers?.length ? dbInstanceIdentifiers : void 0;
24304
24718
  if (!clusters && !instances) return original;
@@ -24322,6 +24736,7 @@ var RDSDBProxyTargetGroupProvider = class {
24322
24736
  for (const id of instances) targets = aws`${targets} ${id}`;
24323
24737
  }
24324
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);
24325
24740
  return original;
24326
24741
  }
24327
24742
  }
@@ -24508,8 +24923,9 @@ var DocDBProvider = class {
24508
24923
  maxLength: 63,
24509
24924
  lowercase: true
24510
24925
  });
24926
+ let clusterCreated = false;
24511
24927
  try {
24512
- if (!(await this.getClient().send(new CreateDBClusterCommand$1({
24928
+ const response = await this.getClient().send(new CreateDBClusterCommand$1({
24513
24929
  DBClusterIdentifier: dbClusterIdentifier,
24514
24930
  Engine: "docdb",
24515
24931
  EngineVersion: properties["EngineVersion"],
@@ -24526,7 +24942,9 @@ var DocDBProvider = class {
24526
24942
  DBClusterParameterGroupName: properties["DBClusterParameterGroupName"],
24527
24943
  DeletionProtection: properties["DeletionProtection"],
24528
24944
  ...tags.length > 0 && { Tags: tags }
24529
- }))).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");
24530
24948
  this.logger.debug(`Successfully created DocDB DBCluster ${logicalId}: ${dbClusterIdentifier}`);
24531
24949
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(dbClusterIdentifier);
24532
24950
  return {
@@ -24534,9 +24952,9 @@ var DocDBProvider = class {
24534
24952
  attributes: clusterAttributes(await this.describeDBCluster(dbClusterIdentifier))
24535
24953
  };
24536
24954
  } catch (error) {
24537
- if (error instanceof ProvisioningError) throw error;
24538
- const cause = error instanceof Error ? error : void 0;
24539
- 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;
24540
24958
  }
24541
24959
  }
24542
24960
  async updateDBCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -24651,8 +25069,9 @@ var DocDBProvider = class {
24651
25069
  maxLength: 63,
24652
25070
  lowercase: true
24653
25071
  });
25072
+ let instanceCreated = false;
24654
25073
  try {
24655
- if (!(await this.getClient().send(new CreateDBInstanceCommand$1({
25074
+ const response = await this.getClient().send(new CreateDBInstanceCommand$1({
24656
25075
  DBInstanceIdentifier: dbInstanceIdentifier,
24657
25076
  DBInstanceClass: properties["DBInstanceClass"],
24658
25077
  Engine: "docdb",
@@ -24661,7 +25080,9 @@ var DocDBProvider = class {
24661
25080
  PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
24662
25081
  AutoMinorVersionUpgrade: properties["AutoMinorVersionUpgrade"],
24663
25082
  ...tags.length > 0 && { Tags: tags }
24664
- }))).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");
24665
25086
  this.logger.debug(`Successfully created DocDB DBInstance ${logicalId}: ${dbInstanceIdentifier}`);
24666
25087
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForInstanceAvailable(dbInstanceIdentifier);
24667
25088
  return {
@@ -24669,9 +25090,9 @@ var DocDBProvider = class {
24669
25090
  attributes: instanceAttributes(await this.describeDBInstance(dbInstanceIdentifier))
24670
25091
  };
24671
25092
  } catch (error) {
24672
- if (error instanceof ProvisioningError) throw error;
24673
- const cause = error instanceof Error ? error : void 0;
24674
- 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;
24675
25096
  }
24676
25097
  }
24677
25098
  async updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -25143,6 +25564,8 @@ var Route53Provider = class {
25143
25564
  const name = properties["Name"];
25144
25565
  if (!name) throw new ProvisioningError(`Name is required for hosted zone ${logicalId}`, resourceType, logicalId);
25145
25566
  let zoneCreated = false;
25567
+ let createdZoneId;
25568
+ let zoneRolledBack = false;
25146
25569
  try {
25147
25570
  const hostedZoneConfig = properties["HostedZoneConfig"];
25148
25571
  const createComment = readConfigString(hostedZoneConfig, "Comment", "", "AWS::Route53::HostedZone HostedZoneConfig");
@@ -25169,6 +25592,7 @@ var Route53Provider = class {
25169
25592
  const hostedZone = response.HostedZone;
25170
25593
  if (!hostedZone?.Id) throw new Error("CreateHostedZone did not return HostedZone.Id");
25171
25594
  const zoneId = hostedZone.Id.replace("/hostedzone/", "");
25595
+ createdZoneId = zoneId;
25172
25596
  if (vpcs && vpcs.length > 1) for (let i = 1; i < vpcs.length; i++) {
25173
25597
  const additionalVpc = vpcs[i];
25174
25598
  this.logger.debug(`Associating additional VPC ${String(additionalVpc["VPCId"])} with hosted zone ${zoneId}`);
@@ -25192,6 +25616,7 @@ var Route53Provider = class {
25192
25616
  try {
25193
25617
  await this.deleteQueryLoggingConfigForZone(zoneId, logicalId);
25194
25618
  await this.getClient().send(new DeleteHostedZoneCommand({ Id: zoneId }));
25619
+ zoneRolledBack = true;
25195
25620
  callerReferenceToken.release();
25196
25621
  } catch (deleteError) {
25197
25622
  this.logger.warn(`Best-effort rollback DeleteHostedZone for ${zoneId} also failed: ${describeAwsFailure(deleteError).detail} — operator may need to clean up the orphan zone`);
@@ -25211,9 +25636,9 @@ var Route53Provider = class {
25211
25636
  };
25212
25637
  } catch (error) {
25213
25638
  if (zoneCreated) markAuxiliaryFailure(error, logicalId);
25214
- if (error instanceof ProvisioningError) throw error;
25215
- const cause = error instanceof Error ? error : void 0;
25216
- 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;
25217
25642
  }
25218
25643
  }
25219
25644
  async createOrAdoptHostedZone(input) {
@@ -26853,17 +27278,21 @@ var CognitoUserPoolProvider = class {
26853
27278
  ...effectiveProperties !== properties ? { effectiveProperties } : {}
26854
27279
  };
26855
27280
  } catch (error) {
27281
+ let poolLeftBehind = false;
26856
27282
  if (createdUserPoolId) {
26857
27283
  markAuxiliaryFailure(error, logicalId);
26858
27284
  try {
26859
27285
  await this.getClient().send(new DeleteUserPoolCommand({ UserPoolId: createdUserPoolId }));
26860
27286
  this.logger.debug(`Rolled back partially-created Cognito User Pool ${createdUserPoolId}`);
26861
27287
  } catch (rollbackError) {
27288
+ poolLeftBehind = true;
26862
27289
  this.logger.warn(`Failed to roll back partially-created Cognito User Pool ${createdUserPoolId}: ${describeAwsFailure(rollbackError).detail}`);
26863
27290
  }
26864
27291
  }
26865
27292
  const cause = error instanceof Error ? error : void 0;
26866
- 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;
26867
27296
  }
26868
27297
  }
26869
27298
  async regionArg(aws) {
@@ -27584,6 +28013,7 @@ var ElastiCacheProvider = class {
27584
28013
  maxLength: 40,
27585
28014
  lowercase: true
27586
28015
  });
28016
+ let clusterCreated = false;
27587
28017
  try {
27588
28018
  await this.getClient().send(new CreateCacheClusterCommand({
27589
28019
  CacheClusterId: cacheClusterId,
@@ -27610,6 +28040,7 @@ var ElastiCacheProvider = class {
27610
28040
  TransitEncryptionEnabled: properties["TransitEncryptionEnabled"],
27611
28041
  ...tags.length > 0 && { Tags: tags }
27612
28042
  }));
28043
+ clusterCreated = true;
27613
28044
  this.logger.debug(`Successfully created CacheCluster ${logicalId}: ${cacheClusterId}`);
27614
28045
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(cacheClusterId);
27615
28046
  const described = await this.describeCacheCluster(cacheClusterId);
@@ -27629,9 +28060,9 @@ var ElastiCacheProvider = class {
27629
28060
  attributes
27630
28061
  };
27631
28062
  } catch (error) {
27632
- if (error instanceof ProvisioningError) throw error;
27633
- const cause = error instanceof Error ? error : void 0;
27634
- 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;
27635
28066
  }
27636
28067
  }
27637
28068
  async updateCacheCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -28036,6 +28467,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28036
28467
  if (!name) throw new ProvisioningError(`Name is required for PrivateDnsNamespace ${logicalId}`, resourceType, logicalId);
28037
28468
  if (!vpc) throw new ProvisioningError(`Vpc is required for PrivateDnsNamespace ${logicalId}`, resourceType, logicalId);
28038
28469
  const inputProperties = this.extractSoaTtlProperties(properties);
28470
+ let createdNamespaceId;
28039
28471
  try {
28040
28472
  const operationId = (await client.send(new CreatePrivateDnsNamespaceCommand({
28041
28473
  Name: name,
@@ -28046,6 +28478,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28046
28478
  }))).OperationId;
28047
28479
  if (!operationId) throw new Error("CreatePrivateDnsNamespace did not return OperationId");
28048
28480
  const namespaceId = await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
28481
+ if (namespaceId !== operationId) createdNamespaceId = namespaceId;
28049
28482
  const arn = await this.buildNamespaceArn(namespaceId);
28050
28483
  this.logger.debug(`Successfully created private DNS namespace ${logicalId}: ${namespaceId}`);
28051
28484
  return {
@@ -28056,9 +28489,10 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28056
28489
  }
28057
28490
  };
28058
28491
  } catch (error) {
28059
- if (error instanceof ProvisioningError) throw error;
28060
28492
  const cause = error instanceof Error ? error : void 0;
28061
- 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;
28062
28496
  }
28063
28497
  }
28064
28498
  async updateNamespace(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -28131,6 +28565,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28131
28565
  const name = properties["Name"];
28132
28566
  const description = properties["Description"];
28133
28567
  if (!name) throw new ProvisioningError(`Name is required for HttpNamespace ${logicalId}`, resourceType, logicalId);
28568
+ let createdNamespaceId;
28134
28569
  try {
28135
28570
  const operationId = (await client.send(new CreateHttpNamespaceCommand({
28136
28571
  Name: name,
@@ -28139,6 +28574,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28139
28574
  }))).OperationId;
28140
28575
  if (!operationId) throw new Error("CreateHttpNamespace did not return OperationId");
28141
28576
  const namespaceId = await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
28577
+ if (namespaceId !== operationId) createdNamespaceId = namespaceId;
28142
28578
  const arn = await this.resolveNamespaceArn(namespaceId);
28143
28579
  this.logger.debug(`Successfully created HTTP namespace ${logicalId}: ${namespaceId}`);
28144
28580
  return {
@@ -28149,9 +28585,10 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28149
28585
  }
28150
28586
  };
28151
28587
  } catch (error) {
28152
- if (error instanceof ProvisioningError) throw error;
28153
28588
  const cause = error instanceof Error ? error : void 0;
28154
- 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;
28155
28592
  }
28156
28593
  }
28157
28594
  async updateHttpNamespace(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -28188,6 +28625,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28188
28625
  const description = properties["Description"];
28189
28626
  if (!name) throw new ProvisioningError(`Name is required for PublicDnsNamespace ${logicalId}`, resourceType, logicalId);
28190
28627
  const inputProperties = this.extractSoaTtlProperties(properties);
28628
+ let createdNamespaceId;
28191
28629
  try {
28192
28630
  const operationId = (await client.send(new CreatePublicDnsNamespaceCommand({
28193
28631
  Name: name,
@@ -28197,6 +28635,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28197
28635
  }))).OperationId;
28198
28636
  if (!operationId) throw new Error("CreatePublicDnsNamespace did not return OperationId");
28199
28637
  const namespaceId = await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
28638
+ if (namespaceId !== operationId) createdNamespaceId = namespaceId;
28200
28639
  let arn;
28201
28640
  let hostedZoneId;
28202
28641
  try {
@@ -28217,9 +28656,10 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28217
28656
  }
28218
28657
  };
28219
28658
  } catch (error) {
28220
- if (error instanceof ProvisioningError) throw error;
28221
28659
  const cause = error instanceof Error ? error : void 0;
28222
- 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;
28223
28663
  }
28224
28664
  }
28225
28665
  async updatePublicDnsNamespace(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -28263,6 +28703,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28263
28703
  const healthCheckConfig = properties["HealthCheckConfig"];
28264
28704
  const type = properties["Type"];
28265
28705
  if (!name) throw new ProvisioningError(`Name is required for ServiceDiscovery Service ${logicalId}`, resourceType, logicalId);
28706
+ let leftBehindServiceId;
28266
28707
  try {
28267
28708
  const service = (await client.send(new CreateServiceCommand$1({
28268
28709
  Name: name,
@@ -28276,6 +28717,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28276
28717
  }))).Service;
28277
28718
  if (!service || !service.Id) throw new Error("CreateService did not return Service ID");
28278
28719
  const serviceId = service.Id;
28720
+ leftBehindServiceId = serviceId;
28279
28721
  this.logger.debug(`Successfully created service discovery service ${logicalId}: ${serviceId}`);
28280
28722
  try {
28281
28723
  const attrs = this.normalizeServiceAttributes(properties["ServiceAttributes"]);
@@ -28296,12 +28738,13 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28296
28738
  this.logger.debug(`Applied ${Object.keys(attrs).length} ServiceAttribute(s) for ${logicalId}`);
28297
28739
  }
28298
28740
  } catch (innerError) {
28299
- 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()}`));
28300
28742
  try {
28301
28743
  await client.send(new DeleteServiceCommand$1({ Id: serviceId }));
28744
+ leftBehindServiceId = void 0;
28302
28745
  this.logger.debug(`Cleaned up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure`);
28303
28746
  } catch (cleanupError) {
28304
- 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()}`);
28305
28748
  }
28306
28749
  throw markAuxiliaryFailure(innerError, logicalId);
28307
28750
  }
@@ -28315,7 +28758,9 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
28315
28758
  };
28316
28759
  } catch (error) {
28317
28760
  const cause = error instanceof Error ? error : void 0;
28318
- 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;
28319
28764
  }
28320
28765
  }
28321
28766
  async updateService(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
@@ -29625,17 +30070,21 @@ var AppSyncProvider = class {
29625
30070
  ...envRecord && { effectiveProperties: this.withEnvironmentVariablesRecord(properties, envRecord) }
29626
30071
  };
29627
30072
  } catch (error) {
30073
+ let apiLeftBehind = false;
29628
30074
  if (createdApiId) {
29629
30075
  markAuxiliaryFailure(error, logicalId);
29630
30076
  try {
29631
30077
  await this.getClient().send(new DeleteGraphqlApiCommand({ apiId: createdApiId }));
29632
30078
  this.logger.debug(`Rolled back partially-created GraphQL API ${createdApiId}`);
29633
30079
  } catch (rollbackError) {
30080
+ apiLeftBehind = true;
29634
30081
  this.logger.warn(`Failed to roll back partially-created GraphQL API ${createdApiId}: ${describeAwsFailure(rollbackError).detail}`);
29635
30082
  }
29636
30083
  }
29637
30084
  const cause = error instanceof Error ? error : void 0;
29638
- 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;
29639
30088
  }
29640
30089
  }
29641
30090
  async regionArg(aws) {
@@ -32558,10 +33007,12 @@ var KMSProvider = class {
32558
33007
  markAuxiliaryFailure(error, logicalId);
32559
33008
  const aws = pasteableAwsCommand();
32560
33009
  const region = await this.regionArg(aws);
32561
- 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()}`);
32562
33011
  }
32563
33012
  const cause = error instanceof Error ? error : void 0;
32564
- 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;
32565
33016
  }
32566
33017
  }
32567
33018
  async regionArg(aws) {
@@ -33123,6 +33574,7 @@ var BudgetsBudgetProvider = class {
33123
33574
  const accountId = await this.resolveAccountId();
33124
33575
  const notifications = this.toSdkNotificationsWithSubscribers(lists.NotificationsWithSubscribers);
33125
33576
  const resourceTags = this.toSdkResourceTags(lists.ResourceTags);
33577
+ const arn = await this.budgetArn(accountId, name);
33126
33578
  await this.getClient().send(new CreateBudgetCommand({
33127
33579
  AccountId: accountId,
33128
33580
  Budget: this.toSdkBudget(properties["Budget"], name, logicalId, resourceType, mask),
@@ -33132,7 +33584,7 @@ var BudgetsBudgetProvider = class {
33132
33584
  this.logger.debug(`Successfully created budget ${logicalId}: ${name}`);
33133
33585
  return {
33134
33586
  physicalId: name,
33135
- attributes: { Arn: await this.budgetArn(accountId, name) }
33587
+ attributes: { Arn: arn }
33136
33588
  };
33137
33589
  } catch (error) {
33138
33590
  if (error instanceof ProvisioningError) throw error;
@@ -33498,6 +33950,30 @@ function readMaxRecordSize(value, mask) {
33498
33950
  reason: `AWS::Kinesis::Stream MaxRecordSizeInKiB must be a number, got ${JSON.stringify(maskDeep(value, mask))}`
33499
33951
  };
33500
33952
  }
33953
+ function readRetentionHours(value, mask) {
33954
+ if (value == null) return { kind: "absent" };
33955
+ if (typeof value === "number" && Number.isFinite(value)) return {
33956
+ kind: "usable",
33957
+ hours: value
33958
+ };
33959
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return {
33960
+ kind: "usable",
33961
+ hours: Number(value)
33962
+ };
33963
+ return {
33964
+ kind: "unusable",
33965
+ reason: `AWS::Kinesis::Stream RetentionPeriodHours must be a number, got ${JSON.stringify(maskDeep(value, mask))}`
33966
+ };
33967
+ }
33968
+ function readEncryptionKeyId(value, mask) {
33969
+ if (!isKmsEncryption(value)) return { kind: "absent" };
33970
+ const keyId = value?.["KeyId"];
33971
+ if (typeof keyId === "string" && keyId.trim() !== "") return { kind: "usable" };
33972
+ return {
33973
+ kind: "unusable",
33974
+ reason: `AWS::Kinesis::Stream StreamEncryption selects KMS but KeyId is not a non-empty string, got ${JSON.stringify(maskDeep(keyId, mask)) ?? "undefined"}`
33975
+ };
33976
+ }
33501
33977
  function comparableRecordSize(value, mask) {
33502
33978
  const read = readMaxRecordSize(value, mask);
33503
33979
  return read.kind === "usable" ? read.size : void 0;
@@ -33541,6 +34017,13 @@ var KinesisStreamProvider = class {
33541
34017
  if (context?.replayingState === true) warn(`${maxRecordRead.reason}. Replaying a state record, so ${streamName} is created with AWS's default maximum record size rather than refusing the restore`);
33542
34018
  else throw new ProvisioningError(maxRecordRead.reason, resourceType, logicalId, streamName);
33543
34019
  }
34020
+ const retentionRead = readRetentionHours(properties["RetentionPeriodHours"], mask);
34021
+ if (retentionRead.kind === "unusable") {
34022
+ if (context?.replayingState !== true) throw new ProvisioningError(retentionRead.reason, resourceType, logicalId, streamName);
34023
+ warn(`${retentionRead.reason}. Replaying a state record, so the retention period is left at AWS's default on ${streamName} rather than refusing the restore`);
34024
+ }
34025
+ const encryptionRead = readEncryptionKeyId(properties["StreamEncryption"], mask);
34026
+ if (encryptionRead.kind === "unusable") throw new ProvisioningError(encryptionRead.reason, resourceType, logicalId, streamName);
33544
34027
  let streamCreated = false;
33545
34028
  try {
33546
34029
  const streamModeDetails = properties["StreamModeDetails"];
@@ -33562,7 +34045,7 @@ var KinesisStreamProvider = class {
33562
34045
  StreamName: streamName,
33563
34046
  Tags: tags
33564
34047
  }));
33565
- const retentionPeriodHours = properties["RetentionPeriodHours"];
34048
+ const retentionPeriodHours = retentionRead.kind === "usable" ? retentionRead.hours : void 0;
33566
34049
  if (retentionPeriodHours !== void 0 && retentionPeriodHours !== 24) {
33567
34050
  this.logger.debug(`Setting retention period to ${retentionPeriodHours} hours for ${streamName}`);
33568
34051
  if (retentionPeriodHours > 24) await this.getClient().send(new IncreaseStreamRetentionPeriodCommand({
@@ -33604,9 +34087,9 @@ var KinesisStreamProvider = class {
33604
34087
  };
33605
34088
  } catch (error) {
33606
34089
  if (streamCreated) markAuxiliaryFailure(error, logicalId);
33607
- if (error instanceof ProvisioningError) throw error;
33608
- const cause = error instanceof Error ? error : void 0;
33609
- throw new ProvisioningError(`Failed to create Kinesis stream ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, streamName, cause);
34090
+ const thrown = error instanceof ProvisioningError ? error : new ProvisioningError(`Failed to create Kinesis stream ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, streamName, error instanceof Error ? error : void 0);
34091
+ if (streamCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, streamName);
34092
+ throw thrown;
33610
34093
  }
33611
34094
  }
33612
34095
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -33614,6 +34097,10 @@ var KinesisStreamProvider = class {
33614
34097
  const warn = (message) => this.logger.warn(mask(message));
33615
34098
  this.logger.debug(`Updating Kinesis stream ${logicalId}: ${physicalId}`);
33616
34099
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
34100
+ const desiredRetention = readRetentionHours(properties["RetentionPeriodHours"], mask);
34101
+ if (desiredRetention.kind === "unusable" && context?.replayingState !== true) throw new ProvisioningError(desiredRetention.reason, resourceType, logicalId, physicalId);
34102
+ if (desiredRetention.kind === "unusable") warn(`${desiredRetention.reason}. Replaying a state record, so the retention period of ${physicalId} is left as it is rather than refusing the restore`);
34103
+ const previousRetention = readRetentionHours(previousProperties["RetentionPeriodHours"], mask);
33617
34104
  try {
33618
34105
  const streamModeDetails = properties["StreamModeDetails"];
33619
34106
  const streamMode = readConfigString(streamModeDetails, "StreamMode", "PROVISIONED", "AWS::Kinesis::Stream StreamModeDetails");
@@ -33641,11 +34128,9 @@ var KinesisStreamProvider = class {
33641
34128
  await this.waitForStreamActive(physicalId);
33642
34129
  }
33643
34130
  }
33644
- const newRetention = properties["RetentionPeriodHours"];
33645
- const oldRetention = previousProperties["RetentionPeriodHours"];
33646
- const effectiveNewRetention = newRetention ?? 24;
33647
- const effectiveOldRetention = oldRetention ?? 24;
33648
- if (effectiveNewRetention !== effectiveOldRetention) {
34131
+ const effectiveNewRetention = desiredRetention.kind === "usable" ? desiredRetention.hours ?? 24 : 24;
34132
+ const effectiveOldRetention = previousRetention.kind === "usable" ? previousRetention.hours ?? 24 : 24;
34133
+ if (desiredRetention.kind !== "unusable" && effectiveNewRetention !== effectiveOldRetention) {
33649
34134
  this.logger.debug(`Updating retention period for ${physicalId}: from ${effectiveOldRetention} to ${effectiveNewRetention}`);
33650
34135
  if (effectiveNewRetention > effectiveOldRetention) await this.getClient().send(new IncreaseStreamRetentionPeriodCommand({
33651
34136
  StreamName: physicalId,
@@ -33920,6 +34405,7 @@ var KinesisStreamConsumerProvider = class {
33920
34405
  if (!streamArn) throw new ProvisioningError("AWS::Kinesis::StreamConsumer requires StreamARN", resourceType, logicalId);
33921
34406
  this.logger.debug(`Registering Kinesis stream consumer ${logicalId}: ${consumerName}`);
33922
34407
  const tagMap = tagListToMap(tagList);
34408
+ let createdArn;
33923
34409
  try {
33924
34410
  const consumer = (await this.getClient().send(new RegisterStreamConsumerCommand({
33925
34411
  StreamARN: streamArn,
@@ -33928,6 +34414,7 @@ var KinesisStreamConsumerProvider = class {
33928
34414
  }))).Consumer;
33929
34415
  if (!consumer?.ConsumerARN || !consumer.ConsumerName) throw new ProvisioningError("RegisterStreamConsumer did not return ConsumerARN/ConsumerName", resourceType, logicalId);
33930
34416
  const consumerArn = consumer.ConsumerARN;
34417
+ createdArn = consumerArn;
33931
34418
  await this.waitForConsumerActive(consumerArn);
33932
34419
  this.logger.debug(`Successfully registered Kinesis stream consumer ${logicalId}`);
33933
34420
  return {
@@ -33942,9 +34429,9 @@ var KinesisStreamConsumerProvider = class {
33942
34429
  }
33943
34430
  };
33944
34431
  } catch (error) {
33945
- if (error instanceof ProvisioningError) throw error;
33946
- const cause = error instanceof Error ? error : void 0;
33947
- 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;
33948
34435
  }
33949
34436
  }
33950
34437
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -34088,6 +34575,33 @@ function tagListToMap(tags) {
34088
34575
 
34089
34576
  //#endregion
34090
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";
34091
34605
  function groupShown(group) {
34092
34606
  return group === "***" ? group : plainOrDescribed(group, "group name");
34093
34607
  }
@@ -34200,20 +34714,97 @@ var SchedulerScheduleProvider = class {
34200
34714
  ...groupName && { GroupName: groupName },
34201
34715
  ...this.toSdkFields(properties)
34202
34716
  }));
34717
+ const creationDate = await this.creationDateBestEffort(logicalId, name, groupName);
34203
34718
  return {
34204
34719
  physicalId: name,
34205
- attributes: response.ScheduleArn ? { Arn: response.ScheduleArn } : {}
34720
+ attributes: {
34721
+ ...response.ScheduleArn && { Arn: response.ScheduleArn },
34722
+ ...creationDate !== void 0 && { ["cdkd:CreationDate"]: creationDate }
34723
+ }
34206
34724
  };
34207
34725
  } catch (error) {
34208
34726
  const cause = error instanceof Error ? error : void 0;
34209
34727
  throw new ProvisioningError(`Failed to create Schedule ${logicalId}: ${cause?.message ?? String(error)}`, resourceType, logicalId, void 0, cause);
34210
34728
  }
34211
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
+ }
34212
34788
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
34213
34789
  const groupName = this.groupNameOf(properties);
34214
34790
  const previousGroupName = this.groupNameOf(previousProperties);
34215
34791
  const mask = maskerOrIdentity(context?.maskSecrets);
34216
- 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)`);
34217
34808
  this.logger.debug(mask(`Updating Schedule ${logicalId}: ${mask(physicalId)}${groupName ? ` (group: ${mask(groupName)})` : ""}`));
34218
34809
  try {
34219
34810
  const input = {
@@ -34222,10 +34813,14 @@ var SchedulerScheduleProvider = class {
34222
34813
  ...this.toSdkFields(properties)
34223
34814
  };
34224
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;
34225
34817
  return {
34226
34818
  physicalId,
34227
34819
  wasReplaced: false,
34228
- attributes: response.ScheduleArn ? { Arn: response.ScheduleArn } : {}
34820
+ attributes: {
34821
+ ...response.ScheduleArn && { Arn: response.ScheduleArn },
34822
+ ...creationDate !== void 0 && { ["cdkd:CreationDate"]: creationDate }
34823
+ }
34229
34824
  };
34230
34825
  } catch (error) {
34231
34826
  if (error instanceof ResourceUpdateNotSupportedError) throw error;
@@ -34233,8 +34828,89 @@ var SchedulerScheduleProvider = class {
34233
34828
  throw new ProvisioningError(`Failed to update Schedule ${logicalId}: ${cause?.message ?? String(error)}`, resourceType, logicalId, physicalId, cause);
34234
34829
  }
34235
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
+ }
34236
34906
  async delete(logicalId, physicalId, resourceType, properties, context) {
34237
- 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);
34238
34914
  if (skip) return skip;
34239
34915
  const groupName = this.groupNameOf(properties);
34240
34916
  if (properties === void 0) {
@@ -34310,7 +34986,10 @@ var SchedulerScheduleProvider = class {
34310
34986
  }));
34311
34987
  return {
34312
34988
  physicalId: explicit,
34313
- 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
+ }
34314
34993
  };
34315
34994
  } catch (error) {
34316
34995
  if (error instanceof ResourceNotFoundException$11) return null;
@@ -34553,7 +35232,7 @@ var PipesPipeProvider = class {
34553
35232
  try {
34554
35233
  settled = await this.waitForSettled(name, logicalId, resourceType, "create", mask);
34555
35234
  } catch (error) {
34556
- throw await this.retireFailedCreate(name, logicalId, error, mask);
35235
+ throw await this.retireFailedCreate(name, logicalId, resourceType, error, mask);
34557
35236
  }
34558
35237
  return {
34559
35238
  physicalId: name,
@@ -34773,13 +35452,14 @@ var PipesPipeProvider = class {
34773
35452
  watch.dispose();
34774
35453
  }
34775
35454
  }
34776
- async retireFailedCreate(name, logicalId, error, mask) {
35455
+ async retireFailedCreate(name, logicalId, resourceType, error, mask) {
34777
35456
  const original = error instanceof Error ? error : new Error(String(error));
35457
+ const leftBehind = () => markCreatedBeforeFailure(original, logicalId, resourceType, name);
34778
35458
  const orphaned = async (why) => {
34779
35459
  const aws = pasteableAwsCommand(mask);
34780
35460
  const command = aws`aws pipes delete-pipe --name ${name}${await orphanCommandRegionArg(this.getClient(), aws)}`;
34781
- 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.)`);
34782
- 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();
34783
35463
  };
34784
35464
  try {
34785
35465
  await this.getClient().send(new DeletePipeCommand({ Name: name }));
@@ -34822,7 +35502,7 @@ var PipesPipeProvider = class {
34822
35502
  const describe = aws`aws pipes describe-pipe --name ${name}${region}`;
34823
35503
  const remove = aws`aws pipes delete-pipe --name ${name}${region}`;
34824
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.)");
34825
- return original;
35505
+ return leftBehind();
34826
35506
  }
34827
35507
  };
34828
35508
 
@@ -35055,6 +35735,7 @@ var EFSProvider = class {
35055
35735
  if (reservation.ledgerStartedThisDeploy) await this.warnOfPreLedgerHolder(logicalId, reservation.base, log);
35056
35736
  const tags = properties["FileSystemTags"];
35057
35737
  let fileSystemId;
35738
+ let fileSystemLeftBehind = false;
35058
35739
  try {
35059
35740
  const created = await this.sendCreateFileSystem(logicalId, resourceType, reservation, {
35060
35741
  CreationToken: creationToken,
@@ -35093,12 +35774,14 @@ var EFSProvider = class {
35093
35774
  this.unconfirmedCreationTokens.delete(creationToken);
35094
35775
  log.debug(`Rolled back partially-created EFS FileSystem ${fileSystemId}`);
35095
35776
  } catch (cleanupError) {
35777
+ fileSystemLeftBehind = true;
35096
35778
  log.warn(`Failed to roll back partially-created EFS FileSystem ${fileSystemId}: ${describeAwsFailure(cleanupError).detail}`);
35097
35779
  }
35098
35780
  }
35099
- if (error instanceof ProvisioningError) throw error;
35100
35781
  const cause = error instanceof Error ? error : void 0;
35101
- 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;
35102
35785
  }
35103
35786
  }
35104
35787
  async sendCreateFileSystem(logicalId, resourceType, reservation, input) {
@@ -35243,12 +35926,14 @@ var EFSProvider = class {
35243
35926
  const subnetId = properties["SubnetId"];
35244
35927
  if (!subnetId) throw new ProvisioningError(`SubnetId is required for EFS MountTarget ${logicalId}`, resourceType, logicalId);
35245
35928
  const securityGroups = properties["SecurityGroups"];
35929
+ let createdMountTargetId;
35246
35930
  try {
35247
35931
  const mountTargetId = (await this.getClient().send(new CreateMountTargetCommand({
35248
35932
  FileSystemId: fileSystemId,
35249
35933
  SubnetId: subnetId,
35250
35934
  SecurityGroups: securityGroups
35251
35935
  }))).MountTargetId;
35936
+ createdMountTargetId = mountTargetId;
35252
35937
  log.debug(`Created EFS MountTarget ${logicalId}: ${mountTargetId}, waiting for available state`);
35253
35938
  await this.waitForMountTargetAvailable(mountTargetId, logicalId, resourceType, log);
35254
35939
  log.debug(`Successfully created EFS MountTarget ${logicalId}: ${mountTargetId}`);
@@ -35257,9 +35942,10 @@ var EFSProvider = class {
35257
35942
  attributes: {}
35258
35943
  };
35259
35944
  } catch (error) {
35260
- if (error instanceof ProvisioningError) throw error;
35261
35945
  const cause = error instanceof Error ? error : void 0;
35262
- 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;
35263
35949
  }
35264
35950
  }
35265
35951
  async waitForMountTargetAvailable(mountTargetId, logicalId, resourceType, log) {
@@ -35799,16 +36485,27 @@ var FSxFileSystemProvider = class {
35799
36485
  attributes: this.buildAttributes(fs)
35800
36486
  };
35801
36487
  } catch (error) {
36488
+ let survivorId;
35802
36489
  if (fileSystemId !== void 0) try {
35803
36490
  await this.getClient().send(new DeleteFileSystemCommand$1({ FileSystemId: fileSystemId }));
35804
36491
  this.createTokenFirstSentAt.delete(clientRequestToken);
35805
36492
  this.logger.warn(`Rolled back partially-created FSx FileSystem ${fileSystemId}`);
35806
36493
  } catch (cleanupError) {
35807
- 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;
35808
36500
  }
35809
- if (error instanceof ProvisioningError) throw error;
35810
36501
  const cause = error instanceof Error ? error : void 0;
35811
- 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);
35812
36509
  }
35813
36510
  }
35814
36511
  toCreateLustreConfiguration(config) {
@@ -36786,6 +37483,7 @@ var EMRClusterProvider = class {
36786
37483
  attributes: this.buildAttributes(cluster)
36787
37484
  };
36788
37485
  } catch (error) {
37486
+ let survivorId;
36789
37487
  if (clusterId !== void 0) try {
36790
37488
  await this.getClient().send(new SetTerminationProtectionCommand({
36791
37489
  JobFlowIds: [clusterId],
@@ -36794,11 +37492,21 @@ var EMRClusterProvider = class {
36794
37492
  await this.getClient().send(new TerminateJobFlowsCommand({ JobFlowIds: [clusterId] }));
36795
37493
  this.logger.warn(`Rolled back partially-created EMR Cluster ${clusterId}`);
36796
37494
  } catch (cleanupError) {
37495
+ survivorId = clusterId;
36797
37496
  this.logger.warn(`Failed to roll back partially-created EMR Cluster ${clusterId}: ${describeAwsFailure(cleanupError).detail} — terminate it manually to stop billing`);
36798
37497
  }
36799
- 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
+ }
36800
37502
  const cause = error instanceof Error ? error : void 0;
36801
- 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);
36802
37510
  }
36803
37511
  }
36804
37512
  toJobFlowInstancesConfig(config) {
@@ -37382,6 +38090,7 @@ var EMRInstanceGroupConfigProvider = class {
37382
38090
  const jobFlowId = properties["JobFlowId"];
37383
38091
  if (!jobFlowId) throw new ProvisioningError(`EMR InstanceGroupConfig ${logicalId} is missing JobFlowId (the parent cluster id)`, resourceType, logicalId);
37384
38092
  this.logger.debug(`Adding EMR instance group ${logicalId} to cluster ${jobFlowId}`);
38093
+ let createdGroupId;
37385
38094
  try {
37386
38095
  const group = this.toInstanceGroupConfig(properties);
37387
38096
  const orphanWindow = addInstanceGroupsLatch.take(logicalId);
@@ -37424,6 +38133,7 @@ var EMRInstanceGroupConfigProvider = class {
37424
38133
  }
37425
38134
  const groupId = response.InstanceGroupIds?.[0];
37426
38135
  if (groupId) groupsCreatedByThisProcess.add(groupId);
38136
+ createdGroupId = groupId;
37427
38137
  if (!groupId) throw new ProvisioningError(`EMR AddInstanceGroups for ${logicalId} returned no instance group id`, resourceType, logicalId);
37428
38138
  await this.waitForGroupReady(jobFlowId, groupId, logicalId, resourceType, toNumber$1(properties["InstanceCount"]) ?? 0);
37429
38139
  this.logger.debug(`Successfully added EMR instance group ${logicalId}: ${groupId}`);
@@ -37435,9 +38145,9 @@ var EMRInstanceGroupConfigProvider = class {
37435
38145
  }
37436
38146
  };
37437
38147
  } catch (error) {
37438
- if (error instanceof ProvisioningError) throw error;
37439
- const cause = error instanceof Error ? error : void 0;
37440
- 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;
37441
38151
  }
37442
38152
  }
37443
38153
  toInstanceGroupConfig(properties) {
@@ -37676,6 +38386,7 @@ var EMRInstanceFleetConfigProvider = class {
37676
38386
  const clusterId = properties["ClusterId"];
37677
38387
  if (!clusterId) throw new ProvisioningError(`EMR InstanceFleetConfig ${logicalId} is missing ClusterId (the parent cluster id)`, resourceType, logicalId);
37678
38388
  this.logger.debug(`Adding EMR instance fleet ${logicalId} to cluster ${clusterId}`);
38389
+ let createdFleetId;
37679
38390
  try {
37680
38391
  const fleet = this.toInstanceFleetConfig(properties);
37681
38392
  const orphanWindow = addInstanceFleetLatch.take(logicalId);
@@ -37718,6 +38429,7 @@ var EMRInstanceFleetConfigProvider = class {
37718
38429
  }
37719
38430
  const fleetId = response.InstanceFleetId;
37720
38431
  if (fleetId) fleetsCreatedByThisProcess.add(fleetId);
38432
+ createdFleetId = fleetId;
37721
38433
  if (!fleetId) throw new ProvisioningError(`EMR AddInstanceFleet for ${logicalId} returned no instance fleet id`, resourceType, logicalId);
37722
38434
  await this.waitForFleetReady(clusterId, fleetId, logicalId, resourceType, this.targetCapacities(properties), {
37723
38435
  onDemand: 0,
@@ -37729,9 +38441,9 @@ var EMRInstanceFleetConfigProvider = class {
37729
38441
  attributes: { Id: fleetId }
37730
38442
  };
37731
38443
  } catch (error) {
37732
- if (error instanceof ProvisioningError) throw error;
37733
- const cause = error instanceof Error ? error : void 0;
37734
- 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;
37735
38447
  }
37736
38448
  }
37737
38449
  toInstanceFleetConfig(properties) {
@@ -37954,6 +38666,7 @@ var FirehoseProvider = class {
37954
38666
  const deliveryStreamName = properties["DeliveryStreamName"];
37955
38667
  const deliveryStreamType = properties["DeliveryStreamType"] || "DirectPut";
37956
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;
37957
38670
  try {
37958
38671
  const input = {
37959
38672
  DeliveryStreamName: deliveryStreamName || logicalId,
@@ -38082,6 +38795,7 @@ var FirehoseProvider = class {
38082
38795
  }));
38083
38796
  const response = await this.getClient().send(new CreateDeliveryStreamCommand(input));
38084
38797
  const physicalId = deliveryStreamName || input.DeliveryStreamName || response.DeliveryStreamARN?.split("/").pop() || "";
38798
+ createdStreamName = physicalId;
38085
38799
  const arn = response.DeliveryStreamARN;
38086
38800
  this.logger.debug(`Successfully created Firehose delivery stream ${logicalId}: ${physicalId}`);
38087
38801
  await this.waitForActive(physicalId, logicalId);
@@ -38090,9 +38804,9 @@ var FirehoseProvider = class {
38090
38804
  attributes: { Arn: arn }
38091
38805
  };
38092
38806
  } catch (error) {
38093
- if (error instanceof ProvisioningError) throw error;
38094
- const cause = error instanceof Error ? error : void 0;
38095
- 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;
38096
38810
  }
38097
38811
  }
38098
38812
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -38836,6 +39550,7 @@ var CloudTrailProvider = class {
38836
39550
  const eventSelectors = properties["EventSelectors"];
38837
39551
  const insightSelectors = properties["InsightSelectors"];
38838
39552
  let trailCreated = false;
39553
+ let createdTrailArn;
38839
39554
  try {
38840
39555
  const result = await this.getClient().send(new CreateTrailCommand({
38841
39556
  Name: trailName ?? logicalId,
@@ -38856,6 +39571,7 @@ var CloudTrailProvider = class {
38856
39571
  }));
38857
39572
  trailCreated = true;
38858
39573
  const trailArn = result.TrailARN;
39574
+ createdTrailArn = result.TrailARN;
38859
39575
  if (eventSelectors && eventSelectors.length > 0) {
38860
39576
  this.logger.debug(`Setting event selectors for CloudTrail Trail ${logicalId}`);
38861
39577
  await this.getClient().send(new PutEventSelectorsCommand({
@@ -38882,7 +39598,9 @@ var CloudTrailProvider = class {
38882
39598
  } catch (error) {
38883
39599
  if (trailCreated) markAuxiliaryFailure(error, logicalId);
38884
39600
  const cause = error instanceof Error ? error : void 0;
38885
- 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;
38886
39604
  }
38887
39605
  }
38888
39606
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -39668,6 +40386,7 @@ var CodeCommitRepositoryProvider = class {
39668
40386
  Triggers: properties["Triggers"],
39669
40387
  Tags: properties["Tags"]
39670
40388
  }, void 0);
40389
+ let leftLiveName;
39671
40390
  try {
39672
40391
  const tags = toSdkTagMap(lists.Tags);
39673
40392
  const description = properties["RepositoryDescription"];
@@ -39680,6 +40399,7 @@ var CodeCommitRepositoryProvider = class {
39680
40399
  }))).repositoryMetadata;
39681
40400
  if (!metadata?.repositoryName) throw new Error("CreateRepository did not return repository metadata");
39682
40401
  const createdName = metadata.repositoryName;
40402
+ leftLiveName = createdName;
39683
40403
  try {
39684
40404
  const code = properties["Code"];
39685
40405
  if (code) await this.seedInitialCommit(createdName, code);
@@ -39693,7 +40413,7 @@ var CodeCommitRepositoryProvider = class {
39693
40413
  }
39694
40414
  } catch (postCreateError) {
39695
40415
  this.logger.warn(`Post-create step failed for CodeCommit Repository ${logicalId}; deleting the just-created repository ${createdName} to avoid an orphan`);
39696
- await this.bestEffortDelete(createdName);
40416
+ if (await this.bestEffortDelete(createdName)) leftLiveName = void 0;
39697
40417
  throw markAuxiliaryFailure(postCreateError, logicalId);
39698
40418
  }
39699
40419
  this.logger.debug(`Successfully created CodeCommit Repository ${logicalId}: ${createdName}`);
@@ -39703,7 +40423,9 @@ var CodeCommitRepositoryProvider = class {
39703
40423
  };
39704
40424
  } catch (error) {
39705
40425
  const cause = error instanceof Error ? error : void 0;
39706
- 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;
39707
40429
  }
39708
40430
  }
39709
40431
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
@@ -40031,8 +40753,10 @@ var CodeCommitRepositoryProvider = class {
40031
40753
  async bestEffortDelete(repositoryName) {
40032
40754
  try {
40033
40755
  await this.getClient().send(new DeleteRepositoryCommand$1({ repositoryName }));
40756
+ return true;
40034
40757
  } catch (cleanupError) {
40035
40758
  this.logger.warn(`Failed to clean up CodeCommit Repository ${repositoryName} after a post-create failure: ${describeAwsFailure(cleanupError).detail}`);
40759
+ return false;
40036
40760
  }
40037
40761
  }
40038
40762
  async getRepositoryMetadata(repositoryName) {
@@ -40600,6 +41324,7 @@ var S3DirectoryBucketProvider = class {
40600
41324
  const tags = desiredTags;
40601
41325
  const includeTags = tags.length > 0;
40602
41326
  try {
41327
+ const attributes = await this.buildAttributes(bucketName);
40603
41328
  await this.s3Client.send(new CreateBucketCommand({
40604
41329
  Bucket: bucketName,
40605
41330
  CreateBucketConfiguration: {
@@ -40615,7 +41340,6 @@ var S3DirectoryBucketProvider = class {
40615
41340
  }
40616
41341
  }));
40617
41342
  this.logger.debug(`Created S3 Express Directory Bucket: ${bucketName}`);
40618
- const attributes = await this.buildAttributes(bucketName);
40619
41343
  return {
40620
41344
  physicalId: bucketName,
40621
41345
  attributes
@@ -41036,6 +41760,7 @@ var S3TablesProvider = class {
41036
41760
  maskSecrets: context?.maskSecrets,
41037
41761
  ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
41038
41762
  });
41763
+ let tableCreated = false;
41039
41764
  try {
41040
41765
  const response = await this.getClient().send(new CreateTableCommand$2({
41041
41766
  tableBucketARN,
@@ -41044,6 +41769,7 @@ var S3TablesProvider = class {
41044
41769
  format,
41045
41770
  ...tags !== void 0 && { tags }
41046
41771
  }));
41772
+ tableCreated = true;
41047
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);
41048
41774
  const tableARN = response.tableARN;
41049
41775
  this.logger.debug(`Successfully created S3 Tables Table ${logicalId}: ${physicalId}`);
@@ -41053,7 +41779,9 @@ var S3TablesProvider = class {
41053
41779
  };
41054
41780
  } catch (error) {
41055
41781
  const cause = error instanceof Error ? error : void 0;
41056
- 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;
41057
41785
  }
41058
41786
  }
41059
41787
  async readCurrentState(physicalId, logicalId, resourceType) {
@@ -41537,7 +42265,9 @@ var ECRProvider = class {
41537
42265
  } catch (error) {
41538
42266
  if (repositoryCreated) markAuxiliaryFailure(error, logicalId);
41539
42267
  const cause = error instanceof Error ? error : void 0;
41540
- 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;
41541
42271
  }
41542
42272
  }
41543
42273
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
@@ -41751,7 +42481,7 @@ var NestedStackProvider = class {
41751
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)"],
41752
42482
  ["TimeoutInMinutes", "CFn-only stack-create deadline — cdkd uses per-resource --resource-timeout instead (issue #459 design §9)"]
41753
42483
  ])]]);
41754
- async create(logicalId, _resourceType, properties, context) {
42484
+ async create(logicalId, resourceType, properties, context) {
41755
42485
  const ctx = this.requireContext();
41756
42486
  this.requireDeployContext(ctx, "create");
41757
42487
  const childTemplatePath = ctx.nestedTemplates[logicalId];
@@ -41763,15 +42493,27 @@ var NestedStackProvider = class {
41763
42493
  const childParameters = this.extractParameters(properties);
41764
42494
  const resourceCount = Object.keys(childTemplate.Resources ?? {}).length;
41765
42495
  this.logger.info(`Deploying nested stack ${displaySafe(childStackName)} (logicalId=${displaySafe(logicalId)}, ${resourceCount} resource(s))`);
41766
- const pendingChildDeletes = await this.runChildDeploy(ctx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates);
41767
- const childOutputs = await this.readChildOutputsAsAttributes(ctx, childStackName, childRegion);
41768
- const pendingRecord = context?.replayingState === true ? void 0 : recordWithPendingChildDeletes(properties, pendingChildDeletes);
41769
- return {
41770
- physicalId: this.synthesizeArn(ctx.accountId, childRegion, ctx.parentStackName, logicalId),
41771
- attributes: childOutputs.attributes,
41772
- ...childOutputs.noEchoAttributeNames.length > 0 && { noEchoAttributeNames: childOutputs.noEchoAttributeNames },
41773
- ...pendingRecord && { effectiveProperties: pendingRecord }
41774
- };
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
+ }
41775
42517
  }
41776
42518
  async update(logicalId, physicalId, _resourceType, properties, _previousProperties, context) {
41777
42519
  const ctx = this.requireContext();
@@ -41858,6 +42600,7 @@ var NestedStackProvider = class {
41858
42600
  ...ctx.destroyOptions?.statePrefix !== void 0 && { statePrefix: ctx.destroyOptions.statePrefix },
41859
42601
  ...deleteContext?.removeProtection === true && { removeProtection: true },
41860
42602
  ...deleteContext?.resolveSecretDerivedPrincipals !== void 0 && { resolveSecretDerivedPrincipals: { inheritedProducerRegions: deleteContext.resolveSecretDerivedPrincipals.importedProducerRegions } },
42603
+ ...deleteContext?.stackDestroy === true && { stackDestroy: true },
41861
42604
  ...(ctx.destroyOptions?.skipFinalSnapshot === true || ctx.options?.skipFinalSnapshot === true) && { skipFinalSnapshot: true },
41862
42605
  ...ctx.destroyOptions?.resourceWarnAfterMs !== void 0 && { resourceWarnAfterMs: ctx.destroyOptions.resourceWarnAfterMs },
41863
42606
  ...ctx.destroyOptions?.resourceTimeoutMs !== void 0 && { resourceTimeoutMs: ctx.destroyOptions.resourceTimeoutMs },
@@ -42141,11 +42884,19 @@ var ACMCertificateProvider = class {
42141
42884
  const survivorNote = requestedArn ? await this.cleanupRequestedCertificate(requestedArn, logicalId, idempotencyToken) : void 0;
42142
42885
  if (error instanceof CdkdError) {
42143
42886
  if (survivorNote === void 0) throw error;
42144
- 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;
42145
42890
  }
42146
42891
  const cause = error instanceof Error ? error : void 0;
42147
42892
  const detail = error instanceof Error ? error.message : String(error);
42148
- 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);
42149
42900
  }
42150
42901
  }
42151
42902
  regionOfArn(arn) {
@@ -42385,4 +43136,4 @@ var ACMCertificateProvider = class {
42385
43136
 
42386
43137
  //#endregion
42387
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 };
42388
- //# sourceMappingURL=provider-classes-DiWFkAOH.js.map
43139
+ //# sourceMappingURL=provider-classes-DMOnX5s-.js.map