@go-to-k/cdkd 0.308.0 → 0.309.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 (30) hide show
  1. package/dist/{asg-provider-CsRMe_zh.js → asg-provider-DQoKGfKH.js} +3 -3
  2. package/dist/{asg-provider-CsRMe_zh.js.map → asg-provider-DQoKGfKH.js.map} +1 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/{db-instance-modify-retry-DOJPkECL.js → db-instance-modify-retry-BUG7NMWS.js} +3 -3
  5. package/dist/{db-instance-modify-retry-DOJPkECL.js.map → db-instance-modify-retry-BUG7NMWS.js.map} +1 -1
  6. package/dist/{deploy-engine-DSpxEUjC.js → deploy-engine-tQ0aqZMo.js} +1418 -281
  7. package/dist/deploy-engine-tQ0aqZMo.js.map +1 -0
  8. package/dist/{iam-policy-targets-TFGunWmV.js → iam-policy-targets-D94wh05t.js} +5 -2
  9. package/dist/{iam-policy-targets-TFGunWmV.js.map → iam-policy-targets-D94wh05t.js.map} +1 -1
  10. package/dist/{iam-role-provider--eWrOXce.js → iam-role-provider-BI8Acvps.js} +3 -3
  11. package/dist/{iam-role-provider--eWrOXce.js.map → iam-role-provider-BI8Acvps.js.map} +1 -1
  12. package/dist/index.d.ts +15 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +3 -3
  15. package/dist/{neptune-provider-p59T8CFv.js → neptune-provider-DmVmx69p.js} +11 -9
  16. package/dist/neptune-provider-DmVmx69p.js.map +1 -0
  17. package/dist/{program-DOPic-aS.js → program-Bnt3Yt0k.js} +121 -260
  18. package/dist/program-Bnt3Yt0k.js.map +1 -0
  19. package/dist/{provider-classes-d9K3vBVh.js → provider-classes-DYke4HKr.js} +1210 -194
  20. package/dist/provider-classes-DYke4HKr.js.map +1 -0
  21. package/dist/{rds-provider-BPs9JSk_.js → rds-provider-Qpf09VQv.js} +49 -20
  22. package/dist/rds-provider-Qpf09VQv.js.map +1 -0
  23. package/dist/{version-DzajQDkv.js → version-9LVuCtBU.js} +2 -2
  24. package/dist/{version-DzajQDkv.js.map → version-9LVuCtBU.js.map} +1 -1
  25. package/package.json +2 -2
  26. package/dist/deploy-engine-DSpxEUjC.js.map +0 -1
  27. package/dist/neptune-provider-p59T8CFv.js.map +0 -1
  28. package/dist/program-DOPic-aS.js.map +0 -1
  29. package/dist/provider-classes-d9K3vBVh.js.map +0 -1
  30. package/dist/rds-provider-BPs9JSk_.js.map +0 -1
@@ -1,14 +1,14 @@
1
1
  import { r as getAwsClients, s as withApiGatewayPacing } from "./aws-clients-Bu9EMILF.js";
2
2
  import { B as SECRET_REF_MAX_CODE_POINTS, C as generateResourceName, D as getCurrentStackInstanceId, J as displayStackName, K as displayIdent, M as recordedGeneratedNameKept, Q as safeMsg, T as generatedNameFor, Y as isPasteableIdent, Z as plainIdentOr, a as safeStringify, i as isAwsAuthoredFailure, k as isBeforeStateRead, n as describeAwsFailure, q as displaySafe, r as describeAwsStatusReason, s as getLogger, t as VERBOSE_POINTER, w as generateResourceNameWithFallback } from "./aws-failure-text-NSBdKPYK.js";
3
- import { $ as userPoolProtectionSite, $t as injectiveKeyPrefix, A as isPlainImportJson, C as setBounded, Cr as markNonRetryable, Ct as createMaskedLogSinks, D as markNotGone, Dn as ResourceUpdateNotSupportedError, Dt as maskerOrIdentity, Et as maskDeep, F as refusalTypeShown, Ft as isAuxiliaryFailure, G as isTerminalDbStatus, H as deleteWithProtectionCompensation, Hn as hasClauseBreak, I as remedyLogicalId, J as observeThenDisableProtection, K as isTerminalDeleteFailure, Kn as plainOrDescribed, L as resolveExplicitPhysicalId, Lt as markAuxiliaryFailure, M as isQuotableImportValue, N as normalizeAwsTagsToCfn, O as assertRegionMatch, Ot as withDerivedNameMasks, P as quotedRemedyLogicalId, Qt as injectiveKey, Rt as markCreatedBeforeFailure, S as replayedSendWindow, Sr as isThrottlingError, St as stateOrphanRecordRemedy, T as RESOURCE_NOT_FOUND, Tn as ProvisioningError, Tr as wrapMaskedAwsError, Tt as isSecretDerivedValue, Un as isInertUnquoted, V as deleteAfterProtectionFlipWithFinalSnapshot, Vn as commandHole, Wn as pasteableCommand, X as rdsFamilyProtectionSite, Xn as withheldTargetClause, Y as protectionFlipKey, Yn as shellQuote, _ as RecentIdSet, _r as isRetryableTransientError, _t as compositeIdSkipResult, a as onlySecretDerived, an as ambientCredentialConfig, ar as markNameCollision, at as protectedReplacementAdvice, b as isInsideWindow, br as THROTTLING_ERROR_NAMES, c as recordedPrincipalsRepair, cn as AccountIdUnavailableError, dr as explicitDenyPolicyKind, ft as isInterruptedWaitError, g as AmbiguousCreateLatch, gt as compositeIdSeparatorRefusal, h as AMBIGUOUS_LATCH_TTL_MS, hr as isExplicitDenyError, ht as compositeIdFormatMessage, i as holdsSecretDerivedEntry, in as ambientClientDefaults, it as pasteableAwsCommand, j as isPlainImportValue, jt as clearCreatedBeforeFailure, k as VALUE_NOT_SHOWN, mr as isAmbiguousOutcomeError, mt as canonicalizeRoute53QueryName, nt as WITHHELD_AWS_COMMAND, o as readPrincipalLists, on as clientDefaultsFor, or as markReplayMayCollide, ot as renderDisableCommand, p as withRemovalDefaults, pt as startInterruptWatch, q as logGroupProtectionSite, r as SECRET_DERIVED_READ_LIVE, rn as withPasteableAwsProfile, rt as isAwsCliLiteral, sn as credentialFingerprint, sr as relayedNameCollision, st as ElapsedBudgetRegistry, tt as UNNAMEABLE_ID_CLAUSE, u as clearOnUpdateRemoval, un as CdkdError, v as ambiguousAttemptLead, vr as isTransientServerError, vt as logicalIdShown, w as withoutServerErrorRetries, wr as markRedactedCause, wt as createMaskedRetryLogger, x as replayFollowedAmbiguousAttempt, xr as isMarkedNonRetryable, xt as segmentAfterAnchor, y as createAttemptKey, yt as packCompositeId, z as ProtectionFlipRegistry, zt as markSupersedesEarlierCreate } from "./iam-policy-targets-TFGunWmV.js";
3
+ import { $ as userPoolProtectionSite, $t as injectiveKeyPrefix, A as isPlainImportJson, C as setBounded, Cr as markNonRetryable, Ct as createMaskedLogSinks, D as markNotGone, Dn as ResourceUpdateNotSupportedError, Dt as maskerOrIdentity, Et as maskDeep, F as refusalTypeShown, Ft as isAuxiliaryFailure, G as isTerminalDbWaitStatus, H as deleteWithProtectionCompensation, Hn as hasClauseBreak, I as remedyLogicalId, J as observeThenDisableProtection, K as isTerminalDeleteFailure, Kn as plainOrDescribed, L as resolveExplicitPhysicalId, Lt as markAuxiliaryFailure, M as isQuotableImportValue, N as normalizeAwsTagsToCfn, O as assertRegionMatch, Ot as withDerivedNameMasks, P as quotedRemedyLogicalId, Qt as injectiveKey, Rt as markCreatedBeforeFailure, S as replayedSendWindow, Sr as isThrottlingError, St as stateOrphanRecordRemedy, T as RESOURCE_NOT_FOUND, Tn as ProvisioningError, Tr as wrapMaskedAwsError, Tt as isSecretDerivedValue, Un as isInertUnquoted, V as deleteAfterProtectionFlipWithFinalSnapshot, Vn as commandHole, Wn as pasteableCommand, X as rdsFamilyProtectionSite, Xn as withheldTargetClause, Y as protectionFlipKey, Yn as shellQuote, _ as RecentIdSet, _r as isRetryableTransientError, _t as compositeIdSkipResult, a as onlySecretDerived, an as ambientCredentialConfig, ar as markNameCollision, at as protectedReplacementAdvice, b as isInsideWindow, br as THROTTLING_ERROR_NAMES, c as recordedPrincipalsRepair, cn as AccountIdUnavailableError, dr as explicitDenyPolicyKind, ft as isInterruptedWaitError, g as AmbiguousCreateLatch, gt as compositeIdSeparatorRefusal, h as AMBIGUOUS_LATCH_TTL_MS, hr as isExplicitDenyError, ht as compositeIdFormatMessage, i as holdsSecretDerivedEntry, in as ambientClientDefaults, it as pasteableAwsCommand, j as isPlainImportValue, jt as clearCreatedBeforeFailure, k as VALUE_NOT_SHOWN, mr as isAmbiguousOutcomeError, mt as canonicalizeRoute53QueryName, nt as WITHHELD_AWS_COMMAND, o as readPrincipalLists, on as clientDefaultsFor, or as markReplayMayCollide, ot as renderDisableCommand, p as withRemovalDefaults, pt as startInterruptWatch, q as logGroupProtectionSite, r as SECRET_DERIVED_READ_LIVE, rn as withPasteableAwsProfile, rt as isAwsCliLiteral, sn as credentialFingerprint, sr as relayedNameCollision, st as ElapsedBudgetRegistry, tt as UNNAMEABLE_ID_CLAUSE, u as clearOnUpdateRemoval, un as CdkdError, v as ambiguousAttemptLead, vr as isTransientServerError, vt as logicalIdShown, w as withoutServerErrorRetries, wr as markRedactedCause, wt as createMaskedRetryLogger, x as replayFollowedAmbiguousAttempt, xr as isMarkedNonRetryable, xt as segmentAfterAnchor, y as createAttemptKey, yt as packCompositeId, z as ProtectionFlipRegistry, zt as markSupersedesEarlierCreate } from "./iam-policy-targets-D94wh05t.js";
4
4
  import { c as ambientRegion } from "./aws-client-defaults-BX9Y_fKp.js";
5
- import { $c as carriesSecretMask, $s as canonicalLambdaFunctionName, A as beginDeleteWait, Ai as tagPlanWarning, An as redactedDeleteAddressFields, B as waitForIndexesSettled, Bl as stringifyValue, Bs as configIntegerRefusal, C as planContributorInsightsOp, Ci as markDeleteStarted, Cs as withoutSilentDropProperties, D as reverseMapTableContributorInsights, Di as readTagList, E as reverseMapIndexContributorInsights, Ei as planTagDiff, En as getCurrentProducerRegions, Er as replacedDeleteFailureVerdict, F as DELETE_INDEX_WAIT_PROCEED_NOTE, Fs as toCfnInteger, G as S3_AUTO_DELETE_OBJECTS_TAG, Ga as getAccountInfo, Gl as rewriteTemplateAssetReferences, Gs as requireConfigObject, H as planTimeToLive, Hi as getCurrentResourceSecrets, Hs as readConfigString, I as INDEX_SETTLE_POLL_INTERVAL_MS, Is as toFiniteNumber, J as getPriorAttempts, Jc as recoverMaskedOutput, Ji as slowCcOperationTimeoutMs, Ju as derivePartitionAndUrlSuffix, K as hasCdkAutoDeleteTag, Ks as requireConfigString, L as deleteTableWithIndexBusyRetry, Ll as timePhase, Ls as coerceCfnBoolean, M as deleteBudgetKey, Mi as stringifyIfAssigned, Ms as WARM_THROUGHPUT_MEMBERS, N as resolveDynamoDbDeleteBudgetClock, Ns as coerceWarmThroughput$1, O as stripIndexContributorInsights, Oi as refuseMalformedDesiredTags, On as REDACTED_DELETE_ADDRESS_SKIP_REASON, P as resolveDynamoDbDeleteBudgetMs, Ps as isWarmThroughputDecrease, Pt as revertNestedChildFromJournal, Qs as emrClusterInstancesReplaced, Qt as withApiLimit, R as maskedLogger, Ri as waitForLambdaFunctionActive, Rs as coerceCfnInteger, Rt as getCurrentNestedStackContext, S as indexDeclaresContributorInsights, Sc as snapshotPassProvenance, Si as isRefusedByName, Sr as awaitInFlightSibling, T as readContributorInsightsSpec, Ta as outputReferenceProvenanceOf, Tc as hasMaskableValues, Ti as DBPROXY_TAG_OPTIONS, U as readTtlSpec, Us as replayWarn, V as isTtlAlreadyDisabled, Vc as dynamicReferenceTokens, Vo as refuseMalformedNestedChildOutputs, Vs as configStringRefusal, W as ECR_AUTO_DELETE_IMAGES_TAG, Ws as requireConfigArray, Wu as decodeListingKey, Xc as recordAttributeIdCopy, Xi as unchangedBehindSecretReference, Xs as withRetry, Xt as isAccessDeniedError, Y as markRefusedBeforeApplying, Yc as physicalNameCopiesOf, Yi as onlySecretReferencesDiffer, Yt as chunks, Zs as ecsServiceLaunchTypeSetOrChanged, Zt as lookupEachName, _ as streamMemberRefusals, _a as s3BucketDomainName, ao as passedParameterClassesOf, at as isAbsoluteAssetPath, b as CONTRIBUTOR_INSIGHTS_KEY, ba as s3BucketWebsiteUrl, bc as redactForState, c as DynamoDBGlobalTableProvider, ca as observeThenDisableInstanceApiTermination, cd as displayAssemblyPath, d as declaredStreamPolicyMember, ec as lambdaFunctionNameForMask, f as declaredStreamTagsMember, fo as accountArgs, g as streamDeclaresTags, ga as s3BucketArn, gi as reserveStackCreateToken, h as streamDeclaresPolicy, ha as isSettledInstanceState, ia as markWaitAbandoned, it as findNestedTemplateTreeDefect, j as compensateRemovedDeletionProtection, ji as definedAttributes, jn as redactedDeleteAddressSkip, jt as getNestedRevertRun, k as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, ki as tagMapAsList, kn as isRedactedRecordedValue, l as STREAM_POLICY_KEY, la as reDisableInstanceApiTermination, ld as renderAssemblyPathEscape, m as reverseMapStreamPolicy, mi as earliestDefined, mt as enclosingDeadlineRemainingMs, na as withIndeterminateGuard, no as parseWebACLArn, oa as ec2InstanceProtectionSite, ot as listNestedTemplateRows, p as planStreamMemberOps, pc as passedNoEchoParametersOf, pt as enclosingDeadlineExpired, q as isTruthyCfnBoolean, qu as canonicalizeRegion, ra as isWaitAbandonedError, sa as isTerminationProtectionPropagationError, sd as describeFileReadFailure, ss as refuseMalformedResourcesForDestroy, st as renderNestedTemplateTreeDefect, t as DeployEngine, ta as deleteSkipReason, tc as sameLambdaFunctionAddress, ti as secretNamedAttributeNamesOf, u as STREAM_TAGS_KEY, ua as reEnableInstanceApiTermination, ud as resolveAssemblyPath, v as streamPolicyMatchesDeclared, va as s3BucketDualStackDomainName, vi as acquireIdempotencyToken, w as planIndexContributorInsightsOps, wi as DBPROXY_TAGS_WHAT, wr as getStackRecords, x as contributorInsightsRefusals, xc as recordReferenceProvenanceEntry, xs as findSilentDropProperties, y as streamTagsMatchDeclared, ya as s3BucketRegionalDomainName, yc as markSameGenerationBag, ys as withheldAccountClause, z as relayedIndexBusyDelete, zc as withoutPhysicalNameCopies, zs as configBooleanRefusal, zt as withNestedStackContext } from "./deploy-engine-DSpxEUjC.js";
6
- import { n as collectInlinePolicyNamesManagedBySiblings, r as IamCreateClientCache, t as IAMRoleProvider } from "./iam-role-provider--eWrOXce.js";
7
- import { a as resolveSecretDerivedPrincipals, i as isResolvableSecretPrincipalList, n as runDestroyForStack, o as canonicalizeIpProtocolValue, r as holdsUnprovenSecretPrincipalToken, s as resolvedResourceTimeoutMs, t as canonicalizeIpv4Cidr } from "./program-DOPic-aS.js";
8
- import { t as modifyDbInstanceWhenAvailable } from "./db-instance-modify-retry-DOJPkECL.js";
9
- import { RDSProvider } from "./rds-provider-BPs9JSk_.js";
10
- import { NeptuneProvider } from "./neptune-provider-p59T8CFv.js";
11
- import { ASGProvider } from "./asg-provider-CsRMe_zh.js";
5
+ import { $l as stringifyValue, $s as readConfigString, $t as lookupEachName, A as beginDeleteWait, Aa as s3BucketRegionalDomainName, Ai as isRefusedByName, Ar as awaitInFlightSibling, As as withheldAccountClause, B as waitForIndexesSettled, Bi as stringifyIfAssigned, Bt as getCurrentNestedStackContext, C as planContributorInsightsOp, Cd as resolveAssemblyPath, Co as accountArgs, D as reverseMapTableContributorInsights, Da as s3BucketArn, E as reverseMapIndexContributorInsights, Ea as isSettledInstanceState, Ec as passedNoEchoParametersOf, Ei as acquireIdempotencyToken, F as DELETE_INDEX_WAIT_PROCEED_NOTE, Fc as snapshotPassProvenance, Fi as readTagList, G as sendTtlWaitingOutRateLimit, Gs as isWarmThroughputDecrease, H as isTtlRateLimited, I as INDEX_SETTLE_POLL_INTERVAL_MS, Ia as outputReferenceProvenanceOf, Ii as refuseMalformedDesiredTags, In as REDACTED_DELETE_ADDRESS_SKIP_REASON, It as revertNestedChildFromJournal, J as hasCdkAutoDeleteTag, Js as coerceCfnBoolean, K as ECR_AUTO_DELETE_IMAGES_TAG, Ki as waitForLambdaFunctionActive, Ks as toCfnInteger, L as deleteTableWithIndexBusyRetry, Li as tagMapAsList, Ln as isRedactedRecordedValue, M as deleteBudgetKey, Mc as markSameGenerationBag, Mi as DBPROXY_TAGS_WHAT, Mr as getStackRecords, Ms as findSilentDropProperties, N as resolveDynamoDbDeleteBudgetClock, Nc as redactForState, Ni as DBPROXY_TAG_OPTIONS, Nt as getNestedRevertRun, O as stripIndexContributorInsights, Oa as s3BucketDomainName, P as resolveDynamoDbDeleteBudgetMs, Pc as recordReferenceProvenanceEntry, Pi as planTagDiff, Pn as getCurrentProducerRegions, Pr as replacedDeleteFailureVerdict, Ps as withoutSilentDropProperties, Qc as withoutPhysicalNameCopies, Qo as refuseMalformedNestedChildOutputs, Qs as configStringRefusal, Qt as isAccessDeniedError, R as maskedLogger, Rc as hasMaskableValues, Ri as tagPlanWarning, Rn as redactedDeleteAddressFields, S as indexDeclaresContributorInsights, Sd as renderAssemblyPathEscape, Si as earliestDefined, T as readContributorInsightsSpec, U as planTimeToLive, Us as WARM_THROUGHPUT_MEMBERS, V as isTtlAlreadyDisabled, Vt as withNestedStackContext, W as readTtlSpec, Ws as coerceWarmThroughput$1, X as getPriorAttempts, Xi as getCurrentResourceSecrets, Xl as timePhase, Xs as configBooleanRefusal, Y as isTruthyCfnBoolean, Ys as coerceCfnInteger, Z as markRefusedBeforeApplying, Zs as configIntegerRefusal, Zt as chunks, _ as streamMemberRefusals, _a as reDisableInstanceApiTermination, b as CONTRIBUTOR_INSIGHTS_KEY, bd as describeFileReadFailure, c as DynamoDBGlobalTableProvider, cc as blockDeviceMappingsDifferOnlyInDeleteOnTermination, cl as physicalNameCopiesOf, ct as listNestedTemplateRows, d as declaredStreamPolicyMember, da as isWaitAbandonedError, dc as emrClusterInstancesReplaced, ec as replayWarn, el as dynamicReferenceTokens, en as withApiLimit, f as declaredStreamTagsMember, fa as markWaitAbandoned, fc as canonicalLambdaFunctionName, fl as carriesSecretMask, g as streamDeclaresTags, ga as observeThenDisableInstanceApiTermination, go as passedParameterClassesOf, gt as enclosingDeadlineRemainingMs, h as streamDeclaresPolicy, ha as isTerminationProtectionPropagationError, ht as enclosingDeadlineExpired, ia as unchangedBehindSecretReference, iu as rewriteTemplateAssetReferences, j as compensateRemovedDeletionProtection, ja as s3BucketWebsiteUrl, ji as markDeleteStarted, k as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, ka as s3BucketDualStackDomainName, l as STREAM_POLICY_KEY, la as deleteSkipReason, li as secretNamedAttributeNamesOf, ll as recordAttributeIdCopy, lt as renderNestedTemplateTreeDefect, m as reverseMapStreamPolicy, ma as ec2InstanceProtectionSite, mc as sameLambdaFunctionAddress, na as slowCcOperationTimeoutMs, nc as requireConfigObject, no as getAccountInfo, od as canonicalizeRegion, ot as findNestedTemplateTreeDefect, p as planStreamMemberOps, pc as lambdaFunctionNameForMask, po as parseWebACLArn, q as S3_AUTO_DELETE_OBJECTS_TAG, qs as toFiniteNumber, ra as onlySecretReferencesDiffer, rc as requireConfigString, rd as decodeListingKey, sc as withRetry, sd as derivePartitionAndUrlSuffix, sl as recoverMaskedOutput, st as isAbsoluteAssetPath, t as DeployEngine, tc as requireConfigArray, u as STREAM_TAGS_KEY, ua as withIndeterminateGuard, uc as ecsServiceLaunchTypeSetOrChanged, v as streamPolicyMatchesDeclared, va as reEnableInstanceApiTermination, vs as refuseMalformedResourcesForDestroy, w as planIndexContributorInsightsOps, wi as reserveStackCreateToken, x as contributorInsightsRefusals, xd as displayAssemblyPath, y as streamTagsMatchDeclared, z as relayedIndexBusyDelete, zi as definedAttributes, zn as redactedDeleteAddressSkip } from "./deploy-engine-tQ0aqZMo.js";
6
+ import { n as collectInlinePolicyNamesManagedBySiblings, r as IamCreateClientCache, t as IAMRoleProvider } from "./iam-role-provider-BI8Acvps.js";
7
+ import { a as resolveSecretDerivedPrincipals, i as isResolvableSecretPrincipalList, n as runDestroyForStack, o as canonicalizeIpProtocolValue, r as holdsUnprovenSecretPrincipalToken, s as resolvedResourceTimeoutMs, t as canonicalizeIpv4Cidr } from "./program-Bnt3Yt0k.js";
8
+ import { t as modifyDbInstanceWhenAvailable } from "./db-instance-modify-retry-BUG7NMWS.js";
9
+ import { RDSProvider } from "./rds-provider-Qpf09VQv.js";
10
+ import { NeptuneProvider } from "./neptune-provider-DmVmx69p.js";
11
+ import { ASGProvider } from "./asg-provider-DQoKGfKH.js";
12
12
  import { createHash, randomUUID } from "node:crypto";
13
13
  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, ListBucketsCommand, 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";
14
14
  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, UpdateGroupCommand, UpdateLoginProfileCommand, UpdateUserCommand } from "@aws-sdk/client-iam";
@@ -16,7 +16,7 @@ import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, GetQ
16
16
  import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesCommand, GetTopicAttributesCommand, InvalidParameterException, ListSubscriptionsByTopicCommand, ListTagsForResourceCommand, ListTopicsCommand, NotFoundException, SNSClient, SetSubscriptionAttributesCommand, SetTopicAttributesCommand, SubscribeCommand, TagResourceCommand, UnsubscribeCommand, UntagResourceCommand } from "@aws-sdk/client-sns";
17
17
  import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCommand, CreateFunctionUrlConfigCommand, DeleteEventSourceMappingCommand, DeleteFunctionCodeSigningConfigCommand, DeleteFunctionCommand, DeleteFunctionConcurrencyCommand, DeleteFunctionEventInvokeConfigCommand, DeleteFunctionUrlConfigCommand, DeleteLayerVersionCommand, GetEventSourceMappingCommand, GetFunctionCodeSigningConfigCommand, GetFunctionCommand, GetFunctionConcurrencyCommand, GetFunctionEventInvokeConfigCommand, GetFunctionRecursionConfigCommand, GetFunctionUrlConfigCommand, GetLayerVersionByArnCommand, GetPolicyCommand as GetPolicyCommand$1, GetRuntimeManagementConfigCommand, LambdaClient, ListEventSourceMappingsCommand, ListLayerVersionsCommand, ListTagsCommand, PublishLayerVersionCommand, PutFunctionCodeSigningConfigCommand, PutFunctionConcurrencyCommand, PutFunctionEventInvokeConfigCommand, PutFunctionRecursionConfigCommand, PutRuntimeManagementConfigCommand, RemovePermissionCommand, ResourceNotFoundException, TagResourceCommand as TagResourceCommand$1, UntagResourceCommand as UntagResourceCommand$1, UpdateEventSourceMappingCommand, UpdateFunctionCodeCommand, UpdateFunctionConfigurationCommand, UpdateFunctionUrlConfigCommand, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
18
18
  import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
19
- import { AllocateAddressCommand, AssociateAddressCommand, AssociateIamInstanceProfileCommand, AssociateNatGatewayAddressCommand, AssociateRouteTableCommand, AttachInternetGatewayCommand, AttachVpnGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAddressesCommand, DescribeAvailabilityZonesCommand, DescribeIamInstanceProfileAssociationsCommand, DescribeInstanceAttributeCommand, DescribeInstanceCreditSpecificationsCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupRulesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, DetachInternetGatewayCommand, DetachVpnGatewayCommand, DisassociateAddressCommand, DisassociateNatGatewayAddressCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifyInstanceCreditSpecificationCommand, ModifyInstanceMetadataOptionsCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, MonitorInstancesCommand, ReleaseAddressCommand, ReplaceNetworkAclAssociationCommand, ReplaceNetworkAclEntryCommand, ReplaceRouteCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, TerminateInstancesCommand, UnmonitorInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
19
+ import { AllocateAddressCommand, AssociateAddressCommand, AssociateIamInstanceProfileCommand, AssociateNatGatewayAddressCommand, AssociateRouteTableCommand, AttachInternetGatewayCommand, AttachVpnGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAddressesCommand, DescribeAvailabilityZonesCommand, DescribeIamInstanceProfileAssociationsCommand, DescribeInstanceAttributeCommand, DescribeInstanceCreditSpecificationsCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupRulesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, DetachInternetGatewayCommand, DetachVpnGatewayCommand, DisassociateAddressCommand, DisassociateIamInstanceProfileCommand, DisassociateNatGatewayAddressCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifyInstanceCreditSpecificationCommand, ModifyInstanceMetadataOptionsCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, ModifyVpcTenancyCommand, MonitorInstancesCommand, ReleaseAddressCommand, ReplaceIamInstanceProfileAssociationCommand, ReplaceNetworkAclAssociationCommand, ReplaceNetworkAclEntryCommand, ReplaceRouteCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, StartInstancesCommand, StopInstancesCommand, TerminateInstancesCommand, UnmonitorInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
20
20
  import { CreateTableCommand, DeleteResourcePolicyCommand, DeleteTableCommand, DescribeContinuousBackupsCommand, DescribeContributorInsightsCommand, DescribeKinesisStreamingDestinationCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DisableKinesisStreamingDestinationCommand, DynamoDBClient, EnableKinesisStreamingDestinationCommand, GetResourcePolicyCommand, ListTagsOfResourceCommand, PolicyNotFoundException, PutResourcePolicyCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateContinuousBackupsCommand, UpdateContributorInsightsCommand, UpdateKinesisStreamingDestinationCommand, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
21
21
  import { APIGatewayClient, CreateAuthorizerCommand, CreateDeploymentCommand, CreateResourceCommand, CreateStageCommand, DeleteAuthorizerCommand, DeleteDeploymentCommand, DeleteIntegrationCommand, DeleteIntegrationResponseCommand, DeleteMethodCommand, DeleteMethodResponseCommand, DeleteResourceCommand, DeleteStageCommand, GetAccountCommand, GetAuthorizerCommand, GetAuthorizersCommand, GetDeploymentCommand, GetDeploymentsCommand, GetMethodCommand, GetResourceCommand, GetStageCommand, NotFoundException as NotFoundException$1, PutIntegrationCommand, PutIntegrationResponseCommand, PutMethodCommand, PutMethodResponseCommand, TagResourceCommand as TagResourceCommand$3, UntagResourceCommand as UntagResourceCommand$3, UpdateAccountCommand, UpdateAuthorizerCommand, UpdateDeploymentCommand, UpdateMethodCommand, UpdateStageCommand } from "@aws-sdk/client-api-gateway";
22
22
  import { CreateEventBusCommand, DeleteEventBusCommand, DeleteRuleCommand, DescribeEventBusCommand, DescribeRuleCommand, EventBridgeClient, ListRulesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$1, ListTargetsByRuleCommand, PutPermissionCommand, PutRuleCommand, PutTargetsCommand, RemoveTargetsCommand, ResourceNotFoundException as ResourceNotFoundException$2, TagResourceCommand as TagResourceCommand$4, UntagResourceCommand as UntagResourceCommand$4, UpdateEventBusCommand } from "@aws-sdk/client-eventbridge";
@@ -53,17 +53,17 @@ import { BudgetsClient, CreateBudgetCommand, CreateNotificationCommand, CreateSu
53
53
  import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, DescribeStreamSummaryCommand, DisableEnhancedMonitoringCommand, EnableEnhancedMonitoringCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListTagsForResourceCommand as ListTagsForResourceCommand$16, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$12, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$21, UntagResourceCommand as UntagResourceCommand$22, UpdateMaxRecordSizeCommand, UpdateShardCountCommand, UpdateStreamModeCommand, UpdateStreamRecordDistributionStrategyCommand } from "@aws-sdk/client-kinesis";
54
54
  import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, ListSchedulesCommand, ResourceNotFoundException as ResourceNotFoundException$13, SchedulerClient, UpdateScheduleCommand } from "@aws-sdk/client-scheduler";
55
55
  import { CreatePipeCommand, DeletePipeCommand, DescribePipeCommand, NotFoundException as NotFoundException$6, PipesClient, TagResourceCommand as TagResourceCommand$22, UntagResourceCommand as UntagResourceCommand$23, UpdatePipeCommand } from "@aws-sdk/client-pipes";
56
- 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";
57
- import { CreateFileSystemCommand as CreateFileSystemCommand$1, CreateFileSystemFromBackupCommand, DeleteFileSystemCommand as DeleteFileSystemCommand$1, DescribeFileSystemsCommand as DescribeFileSystemsCommand$1, FSxClient, FileSystemNotFound as FileSystemNotFound$1, TagResourceCommand as TagResourceCommand$23, UntagResourceCommand as UntagResourceCommand$24, UpdateFileSystemCommand as UpdateFileSystemCommand$1 } from "@aws-sdk/client-fsx";
56
+ import { AccessPointAlreadyExists, AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteFileSystemPolicyCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemAlreadyExists, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, PutBackupPolicyCommand, PutFileSystemPolicyCommand, PutLifecycleConfigurationCommand, TagResourceCommand as TagResourceCommand$23, UntagResourceCommand as UntagResourceCommand$24, UpdateFileSystemCommand, UpdateFileSystemProtectionCommand } from "@aws-sdk/client-efs";
57
+ import { AssociateFileSystemAliasesCommand, CreateFileSystemCommand as CreateFileSystemCommand$1, CreateFileSystemFromBackupCommand, DeleteFileSystemCommand as DeleteFileSystemCommand$1, DescribeFileSystemAliasesCommand, DescribeFileSystemsCommand as DescribeFileSystemsCommand$1, DisassociateFileSystemAliasesCommand, FSxClient, FileSystemNotFound as FileSystemNotFound$1, TagResourceCommand as TagResourceCommand$24, UntagResourceCommand as UntagResourceCommand$25, UpdateFileSystemCommand as UpdateFileSystemCommand$1 } from "@aws-sdk/client-fsx";
58
58
  import { AddInstanceFleetCommand, AddInstanceGroupsCommand, AddTagsCommand as AddTagsCommand$1, DescribeClusterCommand, EMRClient, InvalidRequestException, ListClustersCommand, ListInstanceFleetsCommand, ListInstanceGroupsCommand, ModifyClusterCommand, ModifyInstanceFleetCommand, ModifyInstanceGroupsCommand, PutAutoScalingPolicyCommand, PutAutoTerminationPolicyCommand, PutManagedScalingPolicyCommand, RemoveAutoScalingPolicyCommand, RemoveAutoTerminationPolicyCommand, RemoveManagedScalingPolicyCommand, RemoveTagsCommand as RemoveTagsCommand$1, RunJobFlowCommand, SetTerminationProtectionCommand, SetUnhealthyNodeReplacementCommand, SetVisibleToAllUsersCommand, TerminateJobFlowsCommand } from "@aws-sdk/client-emr";
59
59
  import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$14, StartDeliveryStreamEncryptionCommand, StopDeliveryStreamEncryptionCommand, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
60
60
  import { AddTagsCommand as AddTagsCommand$2, CloudTrailClient, CreateTrailCommand, DeleteTrailCommand, GetEventSelectorsCommand, GetInsightSelectorsCommand, GetTrailCommand, GetTrailStatusCommand, ListTagsCommand as ListTagsCommand$2, PutEventSelectorsCommand, PutInsightSelectorsCommand, RemoveTagsCommand as RemoveTagsCommand$2, StartLoggingCommand, StopLoggingCommand, TrailNotFoundException, UpdateTrailCommand } from "@aws-sdk/client-cloudtrail";
61
61
  import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ResourceNotFoundException as ResourceNotFoundException$15, UpdateProjectCommand, UpdateProjectVisibilityCommand } from "@aws-sdk/client-codebuild";
62
- import { CodeCommitClient, CreateCommitCommand, CreateRepositoryCommand as CreateRepositoryCommand$1, DeleteRepositoryCommand as DeleteRepositoryCommand$1, GetRepositoryCommand, GetRepositoryTriggersCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, PutRepositoryTriggersCommand, RepositoryDoesNotExistException, TagResourceCommand as TagResourceCommand$24, UntagResourceCommand as UntagResourceCommand$25, UpdateRepositoryDescriptionCommand, UpdateRepositoryEncryptionKeyCommand, UpdateRepositoryNameCommand } from "@aws-sdk/client-codecommit";
63
- import { CreateLifecyclePolicyCommand, DLMClient, DeleteLifecyclePolicyCommand as DeleteLifecyclePolicyCommand$1, GetLifecyclePoliciesCommand, GetLifecyclePolicyCommand as GetLifecyclePolicyCommand$1, ResourceNotFoundException as ResourceNotFoundException$16, TagResourceCommand as TagResourceCommand$25, UntagResourceCommand as UntagResourceCommand$26, UpdateLifecyclePolicyCommand } from "@aws-sdk/client-dlm";
64
- import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, S3VectorsClient, TagResourceCommand as TagResourceCommand$26, UntagResourceCommand as UntagResourceCommand$27 } from "@aws-sdk/client-s3vectors";
65
- import { ListTagsForResourceCommand as ListTagsForResourceCommand$19, S3ControlClient, TagResourceCommand as TagResourceCommand$27, UntagResourceCommand as UntagResourceCommand$28 } from "@aws-sdk/client-s3-control";
66
- import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetNamespaceCommand as GetNamespaceCommand$1, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$20, NotFoundException as NotFoundException$7, RenameTableCommand, S3TablesClient, TagResourceCommand as TagResourceCommand$28, UntagResourceCommand as UntagResourceCommand$29 } from "@aws-sdk/client-s3tables";
62
+ import { CodeCommitClient, CreateCommitCommand, CreateRepositoryCommand as CreateRepositoryCommand$1, DeleteRepositoryCommand as DeleteRepositoryCommand$1, GetRepositoryCommand, GetRepositoryTriggersCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, PutRepositoryTriggersCommand, RepositoryDoesNotExistException, TagResourceCommand as TagResourceCommand$25, UntagResourceCommand as UntagResourceCommand$26, UpdateRepositoryDescriptionCommand, UpdateRepositoryEncryptionKeyCommand, UpdateRepositoryNameCommand } from "@aws-sdk/client-codecommit";
63
+ import { CreateLifecyclePolicyCommand, DLMClient, DeleteLifecyclePolicyCommand as DeleteLifecyclePolicyCommand$1, GetLifecyclePoliciesCommand, GetLifecyclePolicyCommand as GetLifecyclePolicyCommand$1, ResourceNotFoundException as ResourceNotFoundException$16, TagResourceCommand as TagResourceCommand$26, UntagResourceCommand as UntagResourceCommand$27, UpdateLifecyclePolicyCommand } from "@aws-sdk/client-dlm";
64
+ import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, S3VectorsClient, TagResourceCommand as TagResourceCommand$27, UntagResourceCommand as UntagResourceCommand$28 } from "@aws-sdk/client-s3vectors";
65
+ import { ListTagsForResourceCommand as ListTagsForResourceCommand$19, S3ControlClient, TagResourceCommand as TagResourceCommand$28, UntagResourceCommand as UntagResourceCommand$29 } from "@aws-sdk/client-s3-control";
66
+ import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetNamespaceCommand as GetNamespaceCommand$1, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$20, NotFoundException as NotFoundException$7, RenameTableCommand, S3TablesClient, TagResourceCommand as TagResourceCommand$29, UntagResourceCommand as UntagResourceCommand$30 } from "@aws-sdk/client-s3tables";
67
67
 
68
68
  //#region src/provisioning/providers/iam-policy-provider.ts
69
69
  const POLICY_NAME_SKIP_REASON = "no policy name in state — no delete issued";
@@ -7924,6 +7924,13 @@ function inlineCodeFileNameForRuntime(runtime) {
7924
7924
  if (runtime?.startsWith("python")) return "index.py";
7925
7925
  return "index.js";
7926
7926
  }
7927
+ const SEPARATELY_APPLIED_KEYS = /* @__PURE__ */ new Set([
7928
+ "Tags",
7929
+ "ReservedConcurrentExecutions",
7930
+ "RecursiveLoop",
7931
+ "CodeSigningConfigArn",
7932
+ "RuntimeManagementConfig"
7933
+ ]);
7927
7934
  var LambdaFunctionProvider = class {
7928
7935
  lambdaClient;
7929
7936
  createClient = new LambdaCreateClientCache(() => this.lambdaClient);
@@ -8168,6 +8175,7 @@ var LambdaFunctionProvider = class {
8168
8175
  }));
8169
8176
  }
8170
8177
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
8178
+ const markUnsettled = (key) => context?.markUnsettled?.(key);
8171
8179
  properties = withRemovalDefaults(this.removalDefaults, resourceType, properties, previousProperties, context);
8172
8180
  const log = lambdaOperationSinks(this.logger, context?.maskSecrets, [[physicalId, physicalId], [previousProperties["FunctionName"], physicalId]], properties);
8173
8181
  const operationMask = log.mask;
@@ -8195,12 +8203,9 @@ var LambdaFunctionProvider = class {
8195
8203
  "LoggingConfig",
8196
8204
  "DurableConfig"
8197
8205
  ];
8198
- let hasConfigChanges = false;
8199
- for (const field of configFields) if (JSON.stringify(properties[field]) !== JSON.stringify(previousProperties[field])) {
8200
- hasConfigChanges = true;
8201
- break;
8202
- }
8203
- if (hasConfigChanges) {
8206
+ const changedConfigFields = [];
8207
+ for (const field of configFields) if (JSON.stringify(properties[field]) !== JSON.stringify(previousProperties[field])) changedConfigFields.push(field);
8208
+ if (changedConfigFields.length > 0) {
8204
8209
  const configParams = {
8205
8210
  FunctionName: physicalId,
8206
8211
  Role: properties["Role"],
@@ -8222,6 +8227,7 @@ var LambdaFunctionProvider = class {
8222
8227
  LoggingConfig: properties["LoggingConfig"],
8223
8228
  DurableConfig: properties["DurableConfig"]
8224
8229
  };
8230
+ for (const field of changedConfigFields) markUnsettled(field);
8225
8231
  await this.lambdaClient.send(new UpdateFunctionConfigurationCommand(configParams));
8226
8232
  log.debug(`Updated configuration for Lambda function ${log.value(physicalId)}`);
8227
8233
  await this.waitForFunctionUpdated(logicalId, resourceType, physicalId, log);
@@ -8229,6 +8235,7 @@ var LambdaFunctionProvider = class {
8229
8235
  const newCodeSigningConfigArn = properties["CodeSigningConfigArn"];
8230
8236
  const codeSigningChanged = newCodeSigningConfigArn !== previousProperties["CodeSigningConfigArn"];
8231
8237
  if (codeSigningChanged && newCodeSigningConfigArn === void 0) {
8238
+ markUnsettled("CodeSigningConfigArn");
8232
8239
  await this.lambdaClient.send(new DeleteFunctionCodeSigningConfigCommand({ FunctionName: physicalId }));
8233
8240
  log.debug(`Detached CodeSigningConfigArn from Lambda function ${log.value(physicalId)} (template removed the property)`);
8234
8241
  }
@@ -8249,6 +8256,8 @@ var LambdaFunctionProvider = class {
8249
8256
  ImageUri: builtCode.ImageUri,
8250
8257
  Architectures: architecturesChanged ? normalizeArchitectures(properties["Architectures"]) : void 0
8251
8258
  };
8259
+ markUnsettled("Code");
8260
+ if (architecturesChanged) markUnsettled("Architectures");
8252
8261
  await this.lambdaClient.send(new UpdateFunctionCodeCommand(codeParams));
8253
8262
  log.debug(`Updated code for Lambda function ${log.value(physicalId)}`);
8254
8263
  await this.waitForFunctionUpdated(logicalId, resourceType, physicalId, log);
@@ -8256,6 +8265,7 @@ var LambdaFunctionProvider = class {
8256
8265
  const newRecursiveLoop = properties["RecursiveLoop"];
8257
8266
  const prevRecursiveLoop = previousProperties["RecursiveLoop"];
8258
8267
  if (newRecursiveLoop !== void 0 && newRecursiveLoop !== prevRecursiveLoop) {
8268
+ markUnsettled("RecursiveLoop");
8259
8269
  await this.lambdaClient.send(new PutFunctionRecursionConfigCommand({
8260
8270
  FunctionName: physicalId,
8261
8271
  RecursiveLoop: newRecursiveLoop
@@ -8264,6 +8274,7 @@ var LambdaFunctionProvider = class {
8264
8274
  }
8265
8275
  const newReservedConcurrentExecutions = properties["ReservedConcurrentExecutions"];
8266
8276
  if (newReservedConcurrentExecutions !== previousProperties["ReservedConcurrentExecutions"]) {
8277
+ markUnsettled("ReservedConcurrentExecutions");
8267
8278
  if (newReservedConcurrentExecutions === void 0) {
8268
8279
  await this.lambdaClient.send(new DeleteFunctionConcurrencyCommand({ FunctionName: physicalId }));
8269
8280
  log.debug(`Cleared ReservedConcurrentExecutions for Lambda function ${log.value(physicalId)} (template removed the property)`);
@@ -8276,6 +8287,7 @@ var LambdaFunctionProvider = class {
8276
8287
  }
8277
8288
  }
8278
8289
  if (codeSigningChanged && newCodeSigningConfigArn !== void 0) {
8290
+ markUnsettled("CodeSigningConfigArn");
8279
8291
  await this.lambdaClient.send(new PutFunctionCodeSigningConfigCommand({
8280
8292
  FunctionName: physicalId,
8281
8293
  CodeSigningConfigArn: newCodeSigningConfigArn
@@ -8285,12 +8297,16 @@ var LambdaFunctionProvider = class {
8285
8297
  const newRuntimeManagementConfig = properties["RuntimeManagementConfig"];
8286
8298
  const prevRuntimeManagementConfig = previousProperties["RuntimeManagementConfig"];
8287
8299
  if (newRuntimeManagementConfig?.["UpdateRuntimeOn"] !== prevRuntimeManagementConfig?.["UpdateRuntimeOn"] || newRuntimeManagementConfig?.["RuntimeVersionArn"] !== prevRuntimeManagementConfig?.["RuntimeVersionArn"]) {
8300
+ markUnsettled("RuntimeManagementConfig");
8288
8301
  await this.putRuntimeManagementConfig(physicalId, newRuntimeManagementConfig ?? { UpdateRuntimeOn: "Auto" });
8289
8302
  log.debug(`Updated RuntimeManagementConfig for Lambda function ${log.value(physicalId)} to ${JSON.stringify(maskDeep(newRuntimeManagementConfig ?? { UpdateRuntimeOn: "Auto" }, operationMask))}`);
8290
8303
  }
8291
8304
  const getResponse = await this.lambdaClient.send(new GetFunctionCommand({ FunctionName: physicalId }));
8292
8305
  const functionArn = getResponse.Configuration?.FunctionArn;
8293
- await this.applyTagDiff(functionArn, physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], log);
8306
+ await this.applyTagDiff(functionArn, physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], log, {
8307
+ forceUpsert: context?.unsettledProperties?.has("Tags") === true,
8308
+ beforeWrite: () => markUnsettled("Tags")
8309
+ });
8294
8310
  return {
8295
8311
  physicalId,
8296
8312
  wasReplaced: false,
@@ -8377,13 +8393,17 @@ var LambdaFunctionProvider = class {
8377
8393
  const subnets = raw["SubnetIds"];
8378
8394
  return Array.isArray(subnets) && subnets.length > 0;
8379
8395
  }
8380
- async applyTagDiff(functionArn, functionName, resourceType, logicalId, oldTagsRaw, newTagsRaw, log) {
8396
+ async applyTagDiff(functionArn, functionName, resourceType, logicalId, oldTagsRaw, newTagsRaw, log, unsettled = {
8397
+ forceUpsert: false,
8398
+ beforeWrite: () => void 0
8399
+ }) {
8381
8400
  if (!functionArn) return;
8382
8401
  const plan = planTagDiff(oldTagsRaw, newTagsRaw);
8383
8402
  const tagWarning = tagPlanWarning(plan, resourceType, logicalId);
8384
8403
  if (tagWarning !== void 0) log.warn(tagWarning);
8385
- const tagsToAdd = Object.fromEntries(plan.set);
8404
+ const tagsToAdd = Object.fromEntries(unsettled.forceUpsert ? planTagDiff(void 0, newTagsRaw).set : plan.set);
8386
8405
  const tagsToRemove = plan.remove;
8406
+ if (tagsToRemove.length > 0 || Object.keys(tagsToAdd).length > 0) unsettled.beforeWrite();
8387
8407
  if (tagsToRemove.length > 0) {
8388
8408
  await this.lambdaClient.send(new UntagResourceCommand$1({
8389
8409
  Resource: functionArn,
@@ -8623,6 +8643,43 @@ var LambdaFunctionProvider = class {
8623
8643
  throw err;
8624
8644
  }
8625
8645
  }
8646
+ async readUnsettled(physicalId, logicalId, resourceType, names) {
8647
+ const live = await this.readCurrentState(physicalId, logicalId, resourceType);
8648
+ if (typeof live !== "object" || live === null) return live;
8649
+ const out = { ...live };
8650
+ const strict = async (read) => {
8651
+ try {
8652
+ return await read();
8653
+ } catch (error) {
8654
+ if (error instanceof ResourceNotFoundException) return void 0;
8655
+ throw error;
8656
+ }
8657
+ };
8658
+ const set = (key, value) => {
8659
+ if (value === void 0) delete out[key];
8660
+ else out[key] = value;
8661
+ };
8662
+ if (names.has("ReservedConcurrentExecutions")) set("ReservedConcurrentExecutions", (await strict(() => this.lambdaClient.send(new GetFunctionConcurrencyCommand({ FunctionName: physicalId }))))?.ReservedConcurrentExecutions);
8663
+ if (names.has("RecursiveLoop")) set("RecursiveLoop", (await strict(() => this.lambdaClient.send(new GetFunctionRecursionConfigCommand({ FunctionName: physicalId }))))?.RecursiveLoop);
8664
+ if (names.has("RuntimeManagementConfig")) {
8665
+ const r = await strict(() => this.lambdaClient.send(new GetRuntimeManagementConfigCommand({ FunctionName: physicalId })));
8666
+ set("RuntimeManagementConfig", r?.UpdateRuntimeOn === void 0 ? void 0 : {
8667
+ UpdateRuntimeOn: r.UpdateRuntimeOn,
8668
+ ...r.RuntimeVersionArn !== void 0 && { RuntimeVersionArn: r.RuntimeVersionArn }
8669
+ });
8670
+ }
8671
+ if (names.has("CodeSigningConfigArn")) set("CodeSigningConfigArn", (await strict(() => this.lambdaClient.send(new GetFunctionCodeSigningConfigCommand({ FunctionName: physicalId }))))?.CodeSigningConfigArn);
8672
+ return out;
8673
+ }
8674
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names) {
8675
+ try {
8676
+ const cfg = (await this.lambdaClient.send(new GetFunctionCommand({ FunctionName: physicalId }))).Configuration;
8677
+ if (cfg?.State !== "Pending" && cfg?.LastUpdateStatus !== "InProgress") return /* @__PURE__ */ new Set();
8678
+ return new Set([...names].filter((name) => !SEPARATELY_APPLIED_KEYS.has(name)));
8679
+ } catch {
8680
+ return names;
8681
+ }
8682
+ }
8626
8683
  async readCurrentState(physicalId, _logicalId, _resourceType) {
8627
8684
  try {
8628
8685
  const resp = await this.lambdaClient.send(new GetFunctionCommand({ FunctionName: physicalId }));
@@ -12228,7 +12285,8 @@ var DynamoDBTableProvider = class {
12228
12285
  await this.applyPointInTimeRecovery(tableName, properties["PointInTimeRecoverySpecification"], void 0, maskSecrets);
12229
12286
  await this.applyTimeToLive(tableName, properties["TimeToLiveSpecification"], void 0, "create", {
12230
12287
  logicalId,
12231
- resourceType
12288
+ resourceType,
12289
+ maskSecrets
12232
12290
  });
12233
12291
  await this.applyKinesisStreamingDestination(tableName, properties["KinesisStreamSpecification"], void 0, maskSecrets);
12234
12292
  await this.applyContributorInsights(tableName, properties["ContributorInsightsSpecification"], void 0, maskSecrets);
@@ -12266,6 +12324,7 @@ var DynamoDBTableProvider = class {
12266
12324
  }
12267
12325
  }
12268
12326
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
12327
+ const markUnsettled = (key) => context?.markUnsettled?.(key);
12269
12328
  const maskSecrets = context?.maskSecrets ?? ((text) => text);
12270
12329
  const warn = (message) => this.logger.warn(maskSecrets(message));
12271
12330
  const debug = (message) => this.logger.debug(maskSecrets(message));
@@ -12309,7 +12368,10 @@ var DynamoDBTableProvider = class {
12309
12368
  warn(`${lsiMessage}; the LocalSecondaryIndexes change was not applied`);
12310
12369
  }
12311
12370
  let latestStreamArn = table?.LatestStreamArn;
12312
- if (table?.TableArn) await this.applyTagDiff(table.TableArn, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
12371
+ if (table?.TableArn) await this.applyTagDiff(table.TableArn, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], {
12372
+ forceUpsert: context?.unsettledProperties?.has("Tags") === true,
12373
+ beforeWrite: () => markUnsettled("Tags")
12374
+ });
12313
12375
  const normalizeTableClass = (v) => typeof v === "string" && v.length > 0 ? v : "STANDARD";
12314
12376
  const tableClassChanged = normalizeTableClass(properties["TableClass"]) !== normalizeTableClass(previousProperties["TableClass"]);
12315
12377
  const liveBillingMode = table?.BillingModeSummary?.BillingMode ?? "PROVISIONED";
@@ -12368,6 +12430,7 @@ var DynamoDBTableProvider = class {
12368
12430
  warn(`AWS::DynamoDB::Table ${logicalId}: this deploy removes global secondary index(es) ${removable.map((n) => maskSecrets(n)).join(", ")} and flips BillingMode to PROVISIONED. The removal would normally be applied BEFORE the flip, but ${reasons.join(" and ")}, so AWS rejects the flip either way. Nothing was removed.`);
12369
12431
  } else if (removable.length > 0) {
12370
12432
  debug(`Deleting GSI(s) ${removable.join(", ")} on DynamoDB table ${physicalId} before the BillingMode flip to PROVISIONED`);
12433
+ markUnsettled("GlobalSecondaryIndexes");
12371
12434
  await this.runGsiOps(physicalId, removable.map((name) => ({ Delete: { IndexName: name } })), void 0, maskSecrets);
12372
12435
  for (const name of removable) preFlipDeletedIndexNames.add(name);
12373
12436
  }
@@ -12414,6 +12477,10 @@ var DynamoDBTableProvider = class {
12414
12477
  if (indexUpdates.length > 0) updateInput.GlobalSecondaryIndexUpdates = indexUpdates;
12415
12478
  if (unspecified.length > 0) warn(`AWS::DynamoDB::Table ${logicalId}: flipping BillingMode to PROVISIONED, but the template declares no usable ProvisionedThroughput for live index(es) ${unspecified.map((n) => maskSecrets(n)).join(", ")}. AWS requires per-index capacity in the same UpdateTable and will reject the flip naming them. Declare GlobalSecondaryIndexes[].ProvisionedThroughput (both ReadCapacityUnits and WriteCapacityUnits) for each. An index this deploy REMOVES is deleted BEFORE the flip automatically, so a name here is one the template still keeps, one that exists in AWS but in neither the template nor cdkd state (created out of band), or one whose pre-flip removal was skipped for the reason warned above.`);
12416
12479
  }
12480
+ if (billingMode !== prevBillingMode) markUnsettled("BillingMode");
12481
+ if (JSON.stringify(properties["ProvisionedThroughput"]) !== JSON.stringify(previousProperties["ProvisionedThroughput"])) markUnsettled("ProvisionedThroughput");
12482
+ if (tableClassChanged) markUnsettled("TableClass");
12483
+ if (updateInput.GlobalSecondaryIndexUpdates !== void 0) markUnsettled("GlobalSecondaryIndexes");
12417
12484
  await this.dynamoDBClient.send(new UpdateTableCommand(updateInput));
12418
12485
  if (billingMode !== liveBillingMode) await this.waitForTableAndIndexesActive(physicalId, BILLING_FLIP_ACTIVE_WAIT_ATTEMPTS);
12419
12486
  else await this.waitForTableActiveAfterUpdate(physicalId, 60);
@@ -12424,6 +12491,7 @@ var DynamoDBTableProvider = class {
12424
12491
  if (properties["OnDemandThroughput"] || ceilingRemovals.length > 0) {
12425
12492
  const declaredForSend = properties["OnDemandThroughput"] ? this.coerceOnDemandCeilingsForSend(`AWS::DynamoDB::Table ${logicalId}`, properties["OnDemandThroughput"], maskSecrets) : {};
12426
12493
  if (ceilingRemovals.length > 0) debug(`AWS::DynamoDB::Table ${logicalId}: removing the OnDemandThroughput ${ceilingRemovals.join(" / ")} the template no longer declares (sending ${ON_DEMAND_LIMIT_RESET}, DynamoDB's reset sentinel)`);
12494
+ markUnsettled("OnDemandThroughput");
12427
12495
  await this.dynamoDBClient.send(new UpdateTableCommand({
12428
12496
  TableName: physicalId,
12429
12497
  OnDemandThroughput: withOnDemandCeilingRemovals(declaredForSend, ceilingRemovals)
@@ -12436,6 +12504,7 @@ var DynamoDBTableProvider = class {
12436
12504
  this.warnRefusedWarmThroughput(`AWS::DynamoDB::Table ${logicalId}`, properties["WarmThroughput"], maskSecrets);
12437
12505
  const warmThroughputToSend = this.coerceWarmThroughputForSend(`AWS::DynamoDB::Table ${logicalId}`, properties["WarmThroughput"], maskSecrets);
12438
12506
  if (warmThroughputToSend !== void 0 && !this.skipWarmThroughputDecrease(logicalId, physicalId, warmThroughputToSend, table?.WarmThroughput, maskSecrets)) {
12507
+ markUnsettled("WarmThroughput");
12439
12508
  await this.dynamoDBClient.send(new UpdateTableCommand({
12440
12509
  TableName: physicalId,
12441
12510
  WarmThroughput: warmThroughputToSend
@@ -12451,6 +12520,7 @@ var DynamoDBTableProvider = class {
12451
12520
  if (liveSseStatus === "UPDATING" || liveSseStatus === "ENABLING" || liveSseStatus === "DISABLING") liveTable = await this.waitForTableSseSettled(physicalId, maskSecrets);
12452
12521
  const sseUpdate = sseSpecificationToSend(properties["SSESpecification"], previousProperties["SSESpecification"], liveTable?.SSEDescription, liveTable !== void 0);
12453
12522
  if (sseUpdate !== void 0) {
12523
+ markUnsettled("SSESpecification");
12454
12524
  await this.dynamoDBClient.send(new UpdateTableCommand({
12455
12525
  TableName: physicalId,
12456
12526
  SSESpecification: sseUpdate
@@ -12464,6 +12534,7 @@ var DynamoDBTableProvider = class {
12464
12534
  const desiredDeletionProtection = rawDeletionProtection === void 0 ? false : coerceCfnBoolean(rawDeletionProtection);
12465
12535
  if (desiredDeletionProtection === void 0) warn(`AWS::DynamoDB::Table ${logicalId}: DeletionProtectionEnabled is not a boolean, so it was not applied`);
12466
12536
  else if (table?.DeletionProtectionEnabled !== desiredDeletionProtection) {
12537
+ markUnsettled("DeletionProtectionEnabled");
12467
12538
  await this.dynamoDBClient.send(new UpdateTableCommand({
12468
12539
  TableName: physicalId,
12469
12540
  DeletionProtectionEnabled: desiredDeletionProtection
@@ -12473,6 +12544,7 @@ var DynamoDBTableProvider = class {
12473
12544
  }
12474
12545
  }
12475
12546
  if (JSON.stringify(properties["StreamSpecification"]) !== JSON.stringify(previousProperties["StreamSpecification"])) {
12547
+ markUnsettled("StreamSpecification");
12476
12548
  const newViewType = this.extractStreamViewType(properties["StreamSpecification"]);
12477
12549
  const prevViewType = this.extractStreamViewType(previousProperties["StreamSpecification"]);
12478
12550
  const liveStream = table?.StreamSpecification;
@@ -12520,22 +12592,38 @@ var DynamoDBTableProvider = class {
12520
12592
  }
12521
12593
  if (newViewType) await this.applyStreamMembers(logicalId, resourceType, physicalId, latestStreamArn, properties["StreamSpecification"], previousProperties["StreamSpecification"], mintedStream, maskSecrets, mintedStream ? preUpdateStreamArn : void 0, contentsUnknown);
12522
12594
  }
12523
- if (JSON.stringify(properties["GlobalSecondaryIndexes"]) !== JSON.stringify(previousProperties["GlobalSecondaryIndexes"])) await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip, currentLiveIndexNames, currentLiveIndexByName, liveCapacityComparable, effectiveBillingMode === "PAY_PER_REQUEST", maskSecrets);
12524
- if (JSON.stringify(properties["PointInTimeRecoverySpecification"]) !== JSON.stringify(previousProperties["PointInTimeRecoverySpecification"])) await this.applyPointInTimeRecovery(physicalId, properties["PointInTimeRecoverySpecification"], previousProperties["PointInTimeRecoverySpecification"], maskSecrets);
12595
+ if (context?.unsettledProperties?.has("GlobalSecondaryIndexes") === true && currentLiveIndexNames !== void 0) this.warnUndeclaredLiveIndexes(physicalId, currentLiveIndexNames, [previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"]], maskSecrets);
12596
+ if (JSON.stringify(properties["GlobalSecondaryIndexes"]) !== JSON.stringify(previousProperties["GlobalSecondaryIndexes"])) {
12597
+ markUnsettled("GlobalSecondaryIndexes");
12598
+ await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip, currentLiveIndexNames, currentLiveIndexByName, liveCapacityComparable, effectiveBillingMode === "PAY_PER_REQUEST", maskSecrets);
12599
+ }
12600
+ if (JSON.stringify(properties["PointInTimeRecoverySpecification"]) !== JSON.stringify(previousProperties["PointInTimeRecoverySpecification"])) {
12601
+ markUnsettled("PointInTimeRecoverySpecification");
12602
+ await this.applyPointInTimeRecovery(physicalId, properties["PointInTimeRecoverySpecification"], previousProperties["PointInTimeRecoverySpecification"], maskSecrets);
12603
+ }
12525
12604
  if (JSON.stringify(properties["TimeToLiveSpecification"]) !== JSON.stringify(previousProperties["TimeToLiveSpecification"])) {
12526
12605
  this.assertNoActiveTtlAttributeNameChange(logicalId, resourceType, physicalId, properties["TimeToLiveSpecification"], previousProperties["TimeToLiveSpecification"]);
12606
+ markUnsettled("TimeToLiveSpecification");
12527
12607
  await this.applyTimeToLive(physicalId, properties["TimeToLiveSpecification"], previousProperties["TimeToLiveSpecification"], "update", {
12528
12608
  logicalId,
12529
- resourceType
12609
+ resourceType,
12610
+ maskSecrets
12530
12611
  });
12531
12612
  }
12532
12613
  if (JSON.stringify(properties["ResourcePolicy"]) !== JSON.stringify(previousProperties["ResourcePolicy"])) {
12533
12614
  if (!table?.TableArn) throw new ProvisioningError(`Cannot apply ResourcePolicy change for DynamoDB table ${logicalId}: DescribeTable returned no TableArn`, resourceType, logicalId, physicalId);
12615
+ markUnsettled("ResourcePolicy");
12534
12616
  await this.applyResourcePolicy(table.TableArn, properties["ResourcePolicy"], previousProperties["ResourcePolicy"]);
12535
12617
  }
12536
- if (JSON.stringify(properties["KinesisStreamSpecification"]) !== JSON.stringify(previousProperties["KinesisStreamSpecification"])) await this.applyKinesisStreamingDestination(physicalId, properties["KinesisStreamSpecification"], previousProperties["KinesisStreamSpecification"], maskSecrets);
12537
- if (JSON.stringify(properties["ContributorInsightsSpecification"]) !== JSON.stringify(previousProperties["ContributorInsightsSpecification"])) await this.applyContributorInsights(physicalId, properties["ContributorInsightsSpecification"], previousProperties["ContributorInsightsSpecification"], maskSecrets);
12538
- await this.applyIndexContributorInsights(physicalId, properties["GlobalSecondaryIndexes"], previousProperties["GlobalSecondaryIndexes"], maskSecrets);
12618
+ if (JSON.stringify(properties["KinesisStreamSpecification"]) !== JSON.stringify(previousProperties["KinesisStreamSpecification"])) {
12619
+ markUnsettled("KinesisStreamSpecification");
12620
+ await this.applyKinesisStreamingDestination(physicalId, properties["KinesisStreamSpecification"], previousProperties["KinesisStreamSpecification"], maskSecrets);
12621
+ }
12622
+ if (JSON.stringify(properties["ContributorInsightsSpecification"]) !== JSON.stringify(previousProperties["ContributorInsightsSpecification"])) {
12623
+ markUnsettled("ContributorInsightsSpecification");
12624
+ await this.applyContributorInsights(physicalId, properties["ContributorInsightsSpecification"], previousProperties["ContributorInsightsSpecification"], maskSecrets);
12625
+ }
12626
+ await this.applyIndexContributorInsights(physicalId, properties["GlobalSecondaryIndexes"], previousProperties["GlobalSecondaryIndexes"], maskSecrets, () => markUnsettled("GlobalSecondaryIndexes"));
12539
12627
  return {
12540
12628
  physicalId,
12541
12629
  wasReplaced: false,
@@ -12673,15 +12761,19 @@ var DynamoDBTableProvider = class {
12673
12761
  throw error instanceof Error && error.name.includes("NotFound") ? markNotGone(thrown) : thrown;
12674
12762
  }
12675
12763
  }
12676
- async applyTagDiff(tableArn, resourceType, logicalId, oldTagsRaw, newTagsRaw) {
12764
+ async applyTagDiff(tableArn, resourceType, logicalId, oldTagsRaw, newTagsRaw, unsettled = {
12765
+ forceUpsert: false,
12766
+ beforeWrite: () => void 0
12767
+ }) {
12677
12768
  const plan = planTagDiff(oldTagsRaw, newTagsRaw);
12678
12769
  const tagWarning = tagPlanWarning(plan, resourceType, logicalId);
12679
12770
  if (tagWarning !== void 0) this.logger.warn(tagWarning);
12680
- const tagsToAdd = [...plan.set].map(([k, v]) => ({
12771
+ const tagsToAdd = [...unsettled.forceUpsert ? planTagDiff(void 0, newTagsRaw).set : plan.set].map(([k, v]) => ({
12681
12772
  Key: k,
12682
12773
  Value: v
12683
12774
  }));
12684
12775
  const tagsToRemove = plan.remove;
12776
+ if (tagsToRemove.length > 0 || tagsToAdd.length > 0) unsettled.beforeWrite();
12685
12777
  if (tagsToRemove.length > 0) {
12686
12778
  await this.dynamoDBClient.send(new UntagResourceCommand$2({
12687
12779
  ResourceArn: tableArn,
@@ -12732,6 +12824,7 @@ var DynamoDBTableProvider = class {
12732
12824
  const retryLine = `Transient error on "${label}" (attempt ${attempt}/${maxAttempts}): ${msg} — retrying in ${delayMs}ms`;
12733
12825
  this.logger.debug(options.maskSecrets ? options.maskSecrets(retryLine) : retryLine);
12734
12826
  await new Promise((resolve) => setTimeout(resolve, delayMs));
12827
+ if (options.stopAtDeadline && enclosingDeadlineRemainingMs() <= 0) throw error;
12735
12828
  delayMs = Math.min(delayMs * 2, 3e4);
12736
12829
  }
12737
12830
  }
@@ -12746,10 +12839,10 @@ var DynamoDBTableProvider = class {
12746
12839
  let attributeName;
12747
12840
  if (plan.kind === "none") return;
12748
12841
  if (plan.kind === "disable-live") {
12749
- const live = (await this.retryOnTransientControlPlane(() => this.dynamoDBClient.send(new DescribeTimeToLiveCommand({ TableName: tableName })), `read TTL on ${tableName}`)).TimeToLiveDescription;
12842
+ const live = (await this.retryOnTransientControlPlane(() => this.dynamoDBClient.send(new DescribeTimeToLiveCommand({ TableName: tableName })), `read TTL on ${tableName}`, void 0, { maskSecrets: ids.maskSecrets })).TimeToLiveDescription;
12750
12843
  const status = live?.TimeToLiveStatus;
12751
12844
  if (status !== "ENABLED" && status !== "ENABLING" || !live?.AttributeName) {
12752
- this.logger.debug(`TimeToLive already ${status ?? "DISABLED"} on DynamoDB table ${tableName}; nothing to disable`);
12845
+ this.logger.debug(ids.maskSecrets(`TimeToLive already ${status ?? "DISABLED"} on DynamoDB table ${tableName}; nothing to disable`));
12753
12846
  return;
12754
12847
  }
12755
12848
  enabled = false;
@@ -12759,22 +12852,28 @@ var DynamoDBTableProvider = class {
12759
12852
  attributeName = plan.attributeName;
12760
12853
  }
12761
12854
  try {
12762
- await this.retryOnTransientControlPlane(() => this.dynamoDBClient.send(new UpdateTimeToLiveCommand({
12855
+ await sendTtlWaitingOutRateLimit(() => this.retryOnTransientControlPlane(() => this.dynamoDBClient.send(new UpdateTimeToLiveCommand({
12763
12856
  TableName: tableName,
12764
12857
  TimeToLiveSpecification: {
12765
12858
  Enabled: enabled,
12766
12859
  AttributeName: attributeName
12767
12860
  }
12768
- })), `${enabled ? "set" : "disable"} TTL on ${tableName}`);
12861
+ })), `${enabled ? "set" : "disable"} TTL on ${tableName}`, void 0, {
12862
+ maskSecrets: ids.maskSecrets,
12863
+ stopAtDeadline: true
12864
+ }), {
12865
+ what: ids.maskSecrets(`DynamoDB TimeToLive rate-limit wait on ${tableName}`),
12866
+ announce: () => this.logger.info(ids.maskSecrets(`DynamoDB table ${ids.logicalId}: TimeToLive was changed recently, and AWS refuses another change until its rate-limit interval passes (up to about an hour). Retrying every minute, like CloudFormation, within the resource timeout.`))
12867
+ });
12769
12868
  } catch (error) {
12770
12869
  if (!enabled && isTtlAlreadyDisabled(describeAwsFailure(error).detail)) {
12771
- this.logger.debug(`TimeToLive already disabled on DynamoDB table ${tableName}`);
12870
+ this.logger.debug(ids.maskSecrets(`TimeToLive already disabled on DynamoDB table ${tableName}`));
12772
12871
  return;
12773
12872
  }
12774
- if (describeAwsFailure(error).detail.includes("Time to live has been modified multiple times")) throw new ProvisioningError(`DynamoDB table ${ids.logicalId}: AWS refused to ${enabled ? "enable" : "disable"} TimeToLive because it was already modified within DynamoDB's rate-limit interval. Wait for the interval to pass (up to about an hour), then redeploy.`, ids.resourceType, ids.logicalId, tableName, error instanceof Error ? error : void 0);
12873
+ if (isTtlRateLimited(describeAwsFailure(error).detail)) throw new ProvisioningError(`DynamoDB table ${ids.logicalId}: AWS refused to ${enabled ? "enable" : "disable"} TimeToLive because it was already modified within DynamoDB's rate-limit interval, and kept refusing until the resource timeout ran out. Redeploy once the interval has passed (up to about an hour), or raise --resource-timeout.`, ids.resourceType, ids.logicalId, tableName, error instanceof Error ? error : void 0);
12775
12874
  throw error;
12776
12875
  }
12777
- this.logger.debug(`Set TimeToLive Enabled=${enabled} AttributeName=${attributeName} on DynamoDB table ${tableName}`);
12876
+ this.logger.debug(ids.maskSecrets(`Set TimeToLive Enabled=${enabled} AttributeName=${attributeName} on DynamoDB table ${tableName}`));
12778
12877
  }
12779
12878
  extractResourcePolicyDocument(spec) {
12780
12879
  if (spec === void 0 || spec === null) return void 0;
@@ -12987,12 +13086,15 @@ var DynamoDBTableProvider = class {
12987
13086
  if (op === void 0) return;
12988
13087
  await this.sendContributorInsightsOp(tableName, op, maskSecrets);
12989
13088
  }
12990
- async applyIndexContributorInsights(tableName, desiredIndexes, previousIndexes, maskSecrets) {
13089
+ async applyIndexContributorInsights(tableName, desiredIndexes, previousIndexes, maskSecrets, beforeSend = () => void 0) {
12991
13090
  const warn = (message) => this.logger.warn(maskSecrets(message));
12992
13091
  const ops = planIndexContributorInsightsOps(desiredIndexes, previousIndexes, (indexName, reason) => warn(`${this.indexScopeAt(indexName, tableName, maskSecrets)}: ${reason}, so its Contributor Insights was left as it is. Fix the template value and re-deploy.`));
12993
13092
  if (ops.length === 0) return;
12994
13093
  await this.waitForTableAndIndexesActive(tableName);
12995
- for (const op of ops) await this.sendContributorInsightsOp(tableName, op, maskSecrets);
13094
+ for (const op of ops) {
13095
+ beforeSend();
13096
+ await this.sendContributorInsightsOp(tableName, op, maskSecrets);
13097
+ }
12996
13098
  }
12997
13099
  async sendContributorInsightsOp(tableName, op, maskSecrets) {
12998
13100
  const debug = (message) => this.logger.debug(maskSecrets(message));
@@ -13144,6 +13246,21 @@ var DynamoDBTableProvider = class {
13144
13246
  indexScope(indexName, logicalId, maskSecrets) {
13145
13247
  return `GSI ${typeof indexName === "string" ? maskSecrets(indexName) : "<unnamed>"} on AWS::DynamoDB::Table ${logicalId}`;
13146
13248
  }
13249
+ warnUndeclaredLiveIndexes(physicalId, liveIndexNames, lists, maskSecrets) {
13250
+ const named = /* @__PURE__ */ new Set();
13251
+ for (const list of lists) {
13252
+ if (!Array.isArray(list)) continue;
13253
+ for (const entry of list) {
13254
+ const name = typeof entry === "object" && entry !== null ? entry["IndexName"] : void 0;
13255
+ if (typeof name === "string") named.add(name);
13256
+ }
13257
+ }
13258
+ for (const name of liveIndexNames) {
13259
+ if (named.has(name)) continue;
13260
+ const secretDerived = maskSecrets(name) !== name || maskSecrets(physicalId) !== physicalId;
13261
+ this.logger.warn(maskSecrets(`${this.indexScopeAt(name, physicalId, maskSecrets)} is live, but neither cdkd's record nor the template declares it, so cdkd left it in place while converging the unsettled GlobalSecondaryIndexes (CloudFormation never deletes an index it did not record). It may belong to another tool, or an earlier failed cdkd update may have created it; cdkd cannot tell which. If it should not exist, delete it ` + (secretDerived ? `with an UpdateTable GlobalSecondaryIndexUpdates Delete for the index.` : `with: aws dynamodb update-table --table-name ${physicalId} --global-secondary-index-updates '[{"Delete":{"IndexName":"${name}"}}]' (in the table's region)`)));
13262
+ }
13263
+ }
13147
13264
  indexScopeAt(indexName, physicalId, maskSecrets) {
13148
13265
  return `GSI ${typeof indexName === "string" ? maskSecrets(indexName) : "<unnamed>"} on DynamoDB table ${physicalId}`;
13149
13266
  }
@@ -13430,6 +13547,62 @@ var DynamoDBTableProvider = class {
13430
13547
  aws
13431
13548
  };
13432
13549
  }
13550
+ async readUnsettled(physicalId, logicalId, resourceType, names, recordedProperties, desiredProperties) {
13551
+ const live = await this.readCurrentState(physicalId, logicalId, resourceType, recordedProperties);
13552
+ if (typeof live !== "object" || live === null) return live;
13553
+ const out = { ...live };
13554
+ if (out["BillingMode"] === "PAY_PER_REQUEST") delete out["ProvisionedThroughput"];
13555
+ if (names.has("GlobalSecondaryIndexes")) {
13556
+ out["GlobalSecondaryIndexes"] = projectIndexesOnto(out["GlobalSecondaryIndexes"], recordedProperties?.["GlobalSecondaryIndexes"], desiredProperties?.["GlobalSecondaryIndexes"]);
13557
+ if (out["GlobalSecondaryIndexes"] === void 0) delete out["GlobalSecondaryIndexes"];
13558
+ }
13559
+ const strict = async (key, read) => {
13560
+ if (!names.has(key)) return;
13561
+ const value = await read();
13562
+ if (value === void 0) delete out[key];
13563
+ else out[key] = value;
13564
+ };
13565
+ await strict("PointInTimeRecoverySpecification", () => this.readPointInTimeRecovery(physicalId, true));
13566
+ await strict("TimeToLiveSpecification", () => this.readTimeToLive(physicalId, true));
13567
+ await strict("KinesisStreamSpecification", () => this.readKinesisStreamSpecification(physicalId, true));
13568
+ await strict("ContributorInsightsSpecification", () => this.readTableContributorInsights(physicalId, true));
13569
+ if (names.has("ResourcePolicy")) {
13570
+ const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
13571
+ if (!table?.TableArn) throw new ProvisioningError(`DescribeTable returned no TableArn for the table, which reading its unsettled ResourcePolicy needs`, resourceType, logicalId, physicalId);
13572
+ const tableArn = table.TableArn;
13573
+ await strict("ResourcePolicy", () => this.readTableResourcePolicy(physicalId, tableArn, true));
13574
+ }
13575
+ return out;
13576
+ }
13577
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names) {
13578
+ const SEPARATE = /* @__PURE__ */ new Set([
13579
+ "Tags",
13580
+ "PointInTimeRecoverySpecification",
13581
+ "TimeToLiveSpecification",
13582
+ "ResourcePolicy",
13583
+ "KinesisStreamSpecification",
13584
+ "ContributorInsightsSpecification"
13585
+ ]);
13586
+ try {
13587
+ const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
13588
+ const moving = /* @__PURE__ */ new Set();
13589
+ if (table?.TableStatus === "CREATING" || table?.TableStatus === "UPDATING" || table?.TableStatus === "DELETING") {
13590
+ for (const name of names) if (!SEPARATE.has(name)) moving.add(name);
13591
+ }
13592
+ if (names.has("GlobalSecondaryIndexes") && (table?.GlobalSecondaryIndexes ?? []).some((index) => index.IndexStatus === "CREATING" || index.IndexStatus === "UPDATING" || index.IndexStatus === "DELETING")) moving.add("GlobalSecondaryIndexes");
13593
+ const sse = table?.SSEDescription?.Status;
13594
+ if (names.has("SSESpecification") && (sse === "ENABLING" || sse === "DISABLING" || sse === "UPDATING")) moving.add("SSESpecification");
13595
+ if (names.has("TimeToLiveSpecification")) {
13596
+ if ((await this.dynamoDBClient.send(new DescribeTimeToLiveCommand({ TableName: physicalId }))).TimeToLiveDescription?.TimeToLiveStatus === "ENABLING") moving.add("TimeToLiveSpecification");
13597
+ }
13598
+ if (names.has("KinesisStreamSpecification")) {
13599
+ if (((await this.dynamoDBClient.send(new DescribeKinesisStreamingDestinationCommand({ TableName: physicalId }))).KinesisDataStreamDestinations ?? []).some((d) => d.DestinationStatus === "ENABLING" || d.DestinationStatus === "DISABLING" || d.DestinationStatus === "UPDATING")) moving.add("KinesisStreamSpecification");
13600
+ }
13601
+ return moving;
13602
+ } catch {
13603
+ return names;
13604
+ }
13605
+ }
13433
13606
  async readCurrentState(physicalId, _logicalId, _resourceType, properties, context) {
13434
13607
  try {
13435
13608
  const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
@@ -13512,7 +13685,7 @@ var DynamoDBTableProvider = class {
13512
13685
  throw err;
13513
13686
  }
13514
13687
  }
13515
- async readPointInTimeRecovery(physicalId) {
13688
+ async readPointInTimeRecovery(physicalId, strict = false) {
13516
13689
  try {
13517
13690
  const pitrDesc = (await this.dynamoDBClient.send(new DescribeContinuousBackupsCommand({ TableName: physicalId }))).ContinuousBackupsDescription?.PointInTimeRecoveryDescription;
13518
13691
  const pitrStatus = pitrDesc?.PointInTimeRecoveryStatus;
@@ -13521,11 +13694,12 @@ var DynamoDBTableProvider = class {
13521
13694
  if (pitrStatus === "ENABLED" && pitrDesc?.RecoveryPeriodInDays !== void 0) pitr["RecoveryPeriodInDays"] = pitrDesc.RecoveryPeriodInDays;
13522
13695
  return pitr;
13523
13696
  } catch (err) {
13697
+ if (strict) throw err;
13524
13698
  this.logger.debug(`Could not read PointInTimeRecovery for ${physicalId}: ${describeAwsFailure(err).detail}`);
13525
13699
  return;
13526
13700
  }
13527
13701
  }
13528
- async readTimeToLive(physicalId) {
13702
+ async readTimeToLive(physicalId, strict = false) {
13529
13703
  try {
13530
13704
  const ttlDesc = (await this.dynamoDBClient.send(new DescribeTimeToLiveCommand({ TableName: physicalId }))).TimeToLiveDescription;
13531
13705
  if (ttlDesc?.TimeToLiveStatus === "ENABLED" && ttlDesc.AttributeName) return {
@@ -13534,11 +13708,12 @@ var DynamoDBTableProvider = class {
13534
13708
  };
13535
13709
  return;
13536
13710
  } catch (err) {
13711
+ if (strict) throw err;
13537
13712
  this.logger.debug(`Could not read TimeToLive for ${physicalId}: ${describeAwsFailure(err).detail}`);
13538
13713
  return;
13539
13714
  }
13540
13715
  }
13541
- async readTableResourcePolicy(physicalId, tableArn) {
13716
+ async readTableResourcePolicy(physicalId, tableArn, strict = false) {
13542
13717
  try {
13543
13718
  const rpResp = await this.dynamoDBClient.send(new GetResourcePolicyCommand({ ResourceArn: tableArn }));
13544
13719
  if (!rpResp.Policy) return void 0;
@@ -13548,11 +13723,12 @@ var DynamoDBTableProvider = class {
13548
13723
  } catch {}
13549
13724
  return { PolicyDocument: doc };
13550
13725
  } catch (err) {
13726
+ if (strict && !(err instanceof ResourceNotFoundException$1) && !(err instanceof PolicyNotFoundException)) throw err;
13551
13727
  if (!(err instanceof ResourceNotFoundException$1)) this.logger.debug(`Could not read ResourcePolicy for ${physicalId}: ${describeAwsFailure(err).detail}`);
13552
13728
  return;
13553
13729
  }
13554
13730
  }
13555
- async readKinesisStreamSpecification(physicalId) {
13731
+ async readKinesisStreamSpecification(physicalId, strict = false) {
13556
13732
  try {
13557
13733
  const active = ((await this.dynamoDBClient.send(new DescribeKinesisStreamingDestinationCommand({ TableName: physicalId }))).KinesisDataStreamDestinations ?? []).find((d) => d.DestinationStatus === "ACTIVE" || d.DestinationStatus === "ENABLING" || d.DestinationStatus === "UPDATING");
13558
13734
  if (!active?.StreamArn) return void 0;
@@ -13560,15 +13736,17 @@ var DynamoDBTableProvider = class {
13560
13736
  if (active.ApproximateCreationDateTimePrecision !== void 0) kspec["ApproximateCreationDateTimePrecision"] = active.ApproximateCreationDateTimePrecision;
13561
13737
  return kspec;
13562
13738
  } catch (err) {
13739
+ if (strict) throw err;
13563
13740
  this.logger.debug(`Could not read KinesisStreamingDestination for ${physicalId}: ${describeAwsFailure(err).detail}`);
13564
13741
  return;
13565
13742
  }
13566
13743
  }
13567
- async readTableContributorInsights(physicalId) {
13744
+ async readTableContributorInsights(physicalId, strict = false) {
13568
13745
  try {
13569
13746
  const ciResp = await this.dynamoDBClient.send(new DescribeContributorInsightsCommand({ TableName: physicalId }));
13570
13747
  return reverseMapTableContributorInsights(ciResp.ContributorInsightsStatus, ciResp.ContributorInsightsMode);
13571
13748
  } catch (err) {
13749
+ if (strict) throw err;
13572
13750
  this.logger.debug(`Could not read ContributorInsights for ${physicalId}: ${describeAwsFailure(err).detail}`);
13573
13751
  return;
13574
13752
  }
@@ -13692,6 +13870,22 @@ var DynamoDBTableProvider = class {
13692
13870
  function isTransientStreamReadFailure(err) {
13693
13871
  return isThrottlingError(err) || isTransientServerError(err) || isAmbiguousOutcomeError(err) || err instanceof ResourceNotFoundException$1;
13694
13872
  }
13873
+ function projectIndexesOnto(live, recorded, desired) {
13874
+ if (!Array.isArray(live)) return void 0;
13875
+ const named = /* @__PURE__ */ new Set();
13876
+ for (const list of [recorded, desired]) {
13877
+ if (!Array.isArray(list)) continue;
13878
+ for (const entry of list) {
13879
+ const name = typeof entry === "object" && entry !== null ? entry["IndexName"] : void 0;
13880
+ if (typeof name === "string") named.add(name);
13881
+ }
13882
+ }
13883
+ const kept = live.filter((entry) => {
13884
+ const name = typeof entry === "object" && entry !== null ? entry["IndexName"] : void 0;
13885
+ return typeof name === "string" && named.has(name);
13886
+ });
13887
+ return kept.length > 0 ? kept : void 0;
13888
+ }
13695
13889
 
13696
13890
  //#endregion
13697
13891
  //#region src/provisioning/providers/logs-loggroup-provider.ts
@@ -16131,6 +16325,46 @@ const EC2_VPC_GATEWAY_ATTACHMENT_ID_FORMAT = {
16131
16325
  segments: ["gatewayId", "vpcId"]
16132
16326
  };
16133
16327
  const VPN_GATEWAY_ATTACHMENT_WAIT_MS = 6e5;
16328
+ const INSTANCE_STATE_WAIT_CAP_MS = 9e5;
16329
+ const INSTANCE_STATE_POLL_MS = 5e3;
16330
+ const STOPPED_AFTER_START_READS = 3;
16331
+ const PROFILE_DISASSOCIATING_POLLS = 15;
16332
+ const PROFILE_DISASSOCIATING_DELAY_MS = 2e3;
16333
+ function refusedBeforeApplying(error) {
16334
+ return markRefusedBeforeApplying(markNonRetryable(error));
16335
+ }
16336
+ const STOPPABLE_INSTANCE_STATES = /* @__PURE__ */ new Set([
16337
+ "pending",
16338
+ "running",
16339
+ "stopping",
16340
+ "stopped"
16341
+ ]);
16342
+ const instancesLeftStoppedByCdkd = /* @__PURE__ */ new Set();
16343
+ const instanceStoppedAfterStart = /* @__PURE__ */ new WeakSet();
16344
+ function isRecordedStandIn$1(value) {
16345
+ if (Array.isArray(value)) return value.some(isRecordedStandIn$1);
16346
+ if (value !== null && typeof value === "object") return Object.values(value).some(isRecordedStandIn$1);
16347
+ return typeof value === "string" && (value === "***" || dynamicReferenceTokens(value).length > 0);
16348
+ }
16349
+ function instanceProfileKey(value) {
16350
+ const fromArn = (arn) => arn.slice(arn.lastIndexOf("/") + 1);
16351
+ if (typeof value === "string") {
16352
+ const v = value.trim();
16353
+ if (v === "") return void 0;
16354
+ return v.startsWith("arn:") ? fromArn(v) : v;
16355
+ }
16356
+ if (value !== null && typeof value === "object") {
16357
+ const obj = value;
16358
+ if (typeof obj["Name"] === "string" && obj["Name"] !== "") return obj["Name"];
16359
+ if (typeof obj["Arn"] === "string" && obj["Arn"] !== "") return fromArn(obj["Arn"]);
16360
+ }
16361
+ }
16362
+ function sameStringSet(a, b) {
16363
+ if (!Array.isArray(a) || !Array.isArray(b)) return a === b;
16364
+ const sa = new Set(a.map(String));
16365
+ const sb = new Set(b.map(String));
16366
+ return sa.size === sb.size && [...sa].every((x) => sb.has(x));
16367
+ }
16134
16368
  const VPN_DETACH_IN_PROGRESS_ERRORS = /* @__PURE__ */ new Set(["IncorrectState"]);
16135
16369
  const EC2_ROUTE_ID_FORMAT = {
16136
16370
  label: "Route",
@@ -16723,19 +16957,19 @@ var EC2Provider = class {
16723
16957
  switch (resourceType) {
16724
16958
  case "AWS::EC2::VPC": return this.updateVpc(logicalId, physicalId, resourceType, properties, previousProperties, context);
16725
16959
  case "AWS::EC2::Subnet": return this.updateSubnet(logicalId, physicalId, resourceType, properties, previousProperties, context);
16726
- case "AWS::EC2::InternetGateway": return this.updateInternetGateway(logicalId, physicalId);
16960
+ case "AWS::EC2::InternetGateway": return this.updateTagsOnly(logicalId, physicalId, resourceType, false, properties, previousProperties, context);
16727
16961
  case "AWS::EC2::EIP": return this.updateEip(logicalId, physicalId, resourceType, properties, previousProperties, context);
16728
16962
  case "AWS::EC2::VPCGatewayAttachment": return this.updateVpcGatewayAttachment(logicalId, physicalId, resourceType, properties, previousProperties, context);
16729
16963
  case "AWS::EC2::NatGateway": return this.updateNatGateway(logicalId, physicalId, resourceType, properties, previousProperties, context);
16730
- case "AWS::EC2::RouteTable": return this.updateRouteTable(logicalId, physicalId);
16964
+ case "AWS::EC2::RouteTable":
16965
+ case "AWS::EC2::NetworkAcl": return this.updateTagsOnly(logicalId, physicalId, resourceType, true, properties, previousProperties, context);
16731
16966
  case "AWS::EC2::Route": return this.updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, context);
16732
16967
  case "AWS::EC2::SubnetRouteTableAssociation": return this.updateSubnetRouteTableAssociation(logicalId, physicalId);
16733
16968
  case "AWS::EC2::SecurityGroup": return this.updateSecurityGroup(logicalId, physicalId, resourceType, properties, previousProperties, context);
16734
16969
  case "AWS::EC2::SecurityGroupIngress": return this.updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets, context?.replayingState === true, context?.recordedAttributes);
16735
16970
  case "AWS::EC2::Instance": return this.updateInstance(logicalId, physicalId, resourceType, properties, previousProperties, context);
16736
16971
  case "AWS::EC2::NetworkAclEntry": return this.updateNetworkAclEntry(logicalId, physicalId, resourceType, properties, previousProperties, context);
16737
- case "AWS::EC2::NetworkAcl":
16738
- case "AWS::EC2::SubnetNetworkAclAssociation": throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS provides no in-place Update API for this EC2 sub-resource type; every property change requires Delete + Create. Re-deploy with cdkd deploy --replace, or destroy + redeploy.");
16972
+ case "AWS::EC2::SubnetNetworkAclAssociation": throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS provides no in-place Update API for this EC2 sub-resource type; every property change requires Delete + Create. Re-deploy with cdkd deploy --replace, or destroy + redeploy");
16739
16973
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
16740
16974
  }
16741
16975
  }
@@ -16976,6 +17210,10 @@ var EC2Provider = class {
16976
17210
  this.logger.debug(`Updating VPC ${logicalId}: ${physicalId}`);
16977
17211
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
16978
17212
  try {
17213
+ const tenancyOf = (v) => v == null ? "default" : String(v);
17214
+ const newTenancy = tenancyOf(properties["InstanceTenancy"]);
17215
+ const oldTenancy = tenancyOf(previousProperties["InstanceTenancy"]);
17216
+ if (newTenancy !== oldTenancy && newTenancy !== "default") throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "destroy + redeploy the VPC (and the resources that depend on it). AWS changes InstanceTenancy in place only to default, and CloudFormation creates a new VPC for this change");
16979
17217
  const asBool = (v) => {
16980
17218
  if (v === void 0) return void 0;
16981
17219
  return v === true || v === "true";
@@ -16992,6 +17230,10 @@ var EC2Provider = class {
16992
17230
  VpcId: physicalId,
16993
17231
  EnableDnsSupport: { Value: newDnsSupport }
16994
17232
  }));
17233
+ if (newTenancy !== oldTenancy) await this.ec2Client.send(new ModifyVpcTenancyCommand({
17234
+ VpcId: physicalId,
17235
+ InstanceTenancy: "default"
17236
+ }));
16995
17237
  await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], maskerOrIdentity(context?.maskSecrets));
16996
17238
  this.logger.debug(`Successfully updated VPC ${logicalId}`);
16997
17239
  return {
@@ -17003,6 +17245,7 @@ var EC2Provider = class {
17003
17245
  }
17004
17246
  };
17005
17247
  } catch (error) {
17248
+ if (error instanceof CdkdError) throw error;
17006
17249
  const cause = error instanceof Error ? error : void 0;
17007
17250
  throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to update VPC ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
17008
17251
  }
@@ -17272,8 +17515,20 @@ var EC2Provider = class {
17272
17515
  throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to create InternetGateway ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
17273
17516
  }
17274
17517
  }
17275
- updateInternetGateway(logicalId, physicalId) {
17276
- return Promise.reject(new ResourceUpdateNotSupportedError("AWS::EC2::InternetGateway", logicalId, "destroy + redeploy the InternetGateway. IGW properties are immutable in AWS"));
17518
+ async updateTagsOnly(logicalId, physicalId, resourceType, vpcIdCreateOnly, properties, previousProperties, context) {
17519
+ const label = resourceType.slice(10);
17520
+ this.logger.debug(safeMsg`Updating ${label} ${logicalId}: ${physicalId}`);
17521
+ refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
17522
+ if (vpcIdCreateOnly) {
17523
+ const next = properties["VpcId"];
17524
+ const prev = previousProperties["VpcId"];
17525
+ if (next !== void 0 && prev !== void 0 && next !== prev) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `destroy + redeploy the ${label} (and the resources that depend on it). VpcId is immutable in AWS`);
17526
+ }
17527
+ await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], maskerOrIdentity(context?.maskSecrets));
17528
+ return {
17529
+ physicalId,
17530
+ wasReplaced: false
17531
+ };
17277
17532
  }
17278
17533
  async deleteInternetGateway(logicalId, physicalId, resourceType, context) {
17279
17534
  this.logger.debug(`Deleting InternetGateway ${logicalId}: ${physicalId}`);
@@ -18019,9 +18274,6 @@ var EC2Provider = class {
18019
18274
  throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to create RouteTable ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
18020
18275
  }
18021
18276
  }
18022
- updateRouteTable(logicalId, physicalId) {
18023
- return Promise.reject(new ResourceUpdateNotSupportedError("AWS::EC2::RouteTable", logicalId, "destroy + redeploy the RouteTable (and its associated Routes / SubnetRouteTableAssociations). VpcId is immutable."));
18024
- }
18025
18277
  async deleteRouteTable(logicalId, physicalId, resourceType, context) {
18026
18278
  this.logger.debug(`Deleting RouteTable ${logicalId}: ${physicalId}`);
18027
18279
  try {
@@ -18180,7 +18432,7 @@ var EC2Provider = class {
18180
18432
  }
18181
18433
  }
18182
18434
  updateSubnetRouteTableAssociation(logicalId, physicalId) {
18183
- return Promise.reject(new ResourceUpdateNotSupportedError("AWS::EC2::SubnetRouteTableAssociation", logicalId, "destroy + redeploy the association. (SubnetId, RouteTableId) is immutable."));
18435
+ return Promise.reject(new ResourceUpdateNotSupportedError("AWS::EC2::SubnetRouteTableAssociation", logicalId, "destroy + redeploy the association. (SubnetId, RouteTableId) is immutable"));
18184
18436
  }
18185
18437
  async deleteSubnetRouteTableAssociation(logicalId, physicalId, resourceType, context) {
18186
18438
  this.logger.debug(`Deleting SubnetRouteTableAssociation ${logicalId}: ${physicalId}`);
@@ -18256,7 +18508,7 @@ var EC2Provider = class {
18256
18508
  if (!Array.isArray(ingressRules) || ingressRules.length === 0) return;
18257
18509
  await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
18258
18510
  GroupId: groupId,
18259
- IpPermissions: ingressRules.map((rule) => this.buildIpPermission(rule))
18511
+ IpPermissions: ingressRules.map((rule) => this.buildIpPermission(rule, "ingress", true))
18260
18512
  }));
18261
18513
  }
18262
18514
  async applyInlineEgress(groupId, egressRules) {
@@ -18902,9 +19154,12 @@ var EC2Provider = class {
18902
19154
  this.logger.debug(`Updating EC2 Instance ${logicalId}: ${physicalId}`);
18903
19155
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
18904
19156
  this.guardTerminationProtectionRemoval(logicalId, physicalId, resourceType, properties, previousProperties, context);
19157
+ const memberPlan = this.planInstanceMemberUpdates(logicalId, physicalId, resourceType, properties, previousProperties, context);
18905
19158
  try {
19159
+ await this.prepareInstanceMemberUpdates(logicalId, physicalId, resourceType, memberPlan);
18906
19160
  await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], maskerOrIdentity(context?.maskSecrets));
18907
19161
  await this.updateInstanceSecurityProps(physicalId, properties, previousProperties);
19162
+ await this.applyInstanceMemberUpdates(logicalId, physicalId, resourceType, memberPlan);
18908
19163
  const instance = (await this.ec2Client.send(new DescribeInstancesCommand({ InstanceIds: [physicalId] }))).Reservations?.[0]?.Instances?.[0];
18909
19164
  return {
18910
19165
  physicalId,
@@ -18912,6 +19167,7 @@ var EC2Provider = class {
18912
19167
  attributes: describedInstanceAttributes(physicalId, instance)
18913
19168
  };
18914
19169
  } catch (error) {
19170
+ if (error instanceof CdkdError || isInterruptedWaitError(error)) throw error;
18915
19171
  const cause = error instanceof Error ? error : void 0;
18916
19172
  throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to update EC2 Instance ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
18917
19173
  }
@@ -18950,6 +19206,286 @@ var EC2Provider = class {
18950
19206
  CpuCredits: newCpuCredits
18951
19207
  }] }));
18952
19208
  }
19209
+ planInstanceMemberUpdates(logicalId, physicalId, resourceType, properties, previousProperties, context) {
19210
+ const plan = {};
19211
+ const removed = (key) => context?.removedProperties !== void 0 ? context.removedProperties.has(key) : previousProperties[key] !== void 0 && properties[key] === void 0;
19212
+ const changed = (key, same) => {
19213
+ const before = previousProperties[key];
19214
+ const after = properties[key];
19215
+ if (after === void 0 && !removed(key)) return false;
19216
+ if (context?.changedProperties !== void 0 && !context.changedProperties.has(key)) return false;
19217
+ if (isDeepStrictEqual(before, after)) {
19218
+ if (context?.replayingState === true && isRecordedStandIn$1(after)) this.logger.warn(safeMsg`${logicalId} (${resourceType}): both sides of the rollback record ${key} as a stand-in (an unresolved {{resolve:...}} reference or the redaction mask), so cdkd cannot tell whether it changed; the rollback leaves ${key} at its current AWS value.`);
19219
+ return false;
19220
+ }
19221
+ return !(!isRecordedStandIn$1(before) && !isRecordedStandIn$1(after) && same(before, after));
19222
+ };
19223
+ const standInSkips = (key) => {
19224
+ if (!isRecordedStandIn$1(properties[key])) return false;
19225
+ if (context?.replayingState === true) {
19226
+ this.logger.warn(safeMsg`${logicalId} (${resourceType}): the state being restored records ${key} as a stand-in (an unresolved {{resolve:...}} reference or the redaction mask), not a value cdkd can send; the rollback leaves ${key} at its current AWS value.`);
19227
+ return true;
19228
+ }
19229
+ throw refusedBeforeApplying(new ProvisioningError(`${logicalId} (${resourceType}): ${key} holds a recorded stand-in (an unresolved {{resolve:...}} reference or the redaction mask), not a value cdkd can send. The instance was not updated; deploy from the template so the value resolves.`, resourceType, logicalId, physicalId));
19230
+ };
19231
+ if (changed("InstanceType", (a, b) => a === b)) {
19232
+ if (properties["InstanceType"] === void 0) {
19233
+ if (context?.replayingState === true) this.logger.warn(safeMsg`${logicalId} (${resourceType}): the state being restored does not declare InstanceType; the rollback leaves the instance type at its current AWS value.`);
19234
+ else throw refusedBeforeApplying(new ProvisioningError(safeMsg`${logicalId} (${resourceType}): InstanceType was removed from the template, and CloudFormation fails that update for an existing instance. Keep it declared (set it to the instance's current type to leave it unchanged). The instance was not updated.`, resourceType, logicalId, physicalId));
19235
+ } else if (!standInSkips("InstanceType")) plan.instanceType = requireConfigString(properties["InstanceType"], "", "AWS::EC2::Instance InstanceType");
19236
+ }
19237
+ if (changed("UserData", (a, b) => a === b)) {
19238
+ const userData = properties["UserData"];
19239
+ if (userData === void 0) plan.userData = /* @__PURE__ */ new Uint8Array(0);
19240
+ else if (!standInSkips("UserData")) {
19241
+ if (typeof userData !== "string") throw refusedBeforeApplying(new ProvisioningError(`${logicalId} (${resourceType}): UserData must be a base64 string (Fn::Base64), got a ${Array.isArray(userData) ? "list" : typeof userData}. The instance was not updated.`, resourceType, logicalId, physicalId));
19242
+ plan.userData = new Uint8Array(Buffer.from(userData, "base64"));
19243
+ }
19244
+ }
19245
+ if (changed("IamInstanceProfile", (a, b) => instanceProfileKey(a) === instanceProfileKey(b))) {
19246
+ if (properties["IamInstanceProfile"] === void 0) plan.profile = { kind: "remove" };
19247
+ else if (!standInSkips("IamInstanceProfile")) {
19248
+ const desired = this.normalizeIamInstanceProfile(properties["IamInstanceProfile"]);
19249
+ if (desired === void 0) throw refusedBeforeApplying(new ProvisioningError(`${logicalId} (${resourceType}): IamInstanceProfile is neither a profile name, an ARN, nor an {Arn,Name} object. The instance was not updated.`, resourceType, logicalId, physicalId));
19250
+ plan.profile = {
19251
+ kind: "set",
19252
+ ...desired
19253
+ };
19254
+ }
19255
+ }
19256
+ if (changed("SecurityGroupIds", (a, b) => sameStringSet(a, b))) {
19257
+ const groups = properties["SecurityGroupIds"];
19258
+ if (groups === void 0) plan.securityGroups = { kind: "vpc-default" };
19259
+ else {
19260
+ const ids = Array.isArray(groups) ? groups : void 0;
19261
+ if (ids === void 0 || ids.length === 0 || !ids.every((g) => typeof g === "string" && g !== "")) throw refusedBeforeApplying(new ProvisioningError(`${logicalId} (${resourceType}): SecurityGroupIds must be a non-empty list of security group ids. The instance was not updated.`, resourceType, logicalId, physicalId));
19262
+ if (!standInSkips("SecurityGroupIds")) plan.securityGroups = {
19263
+ kind: "set",
19264
+ ids
19265
+ };
19266
+ }
19267
+ }
19268
+ const beforeBdm = previousProperties["BlockDeviceMappings"];
19269
+ const afterBdm = properties["BlockDeviceMappings"];
19270
+ if (afterBdm !== void 0 && (context?.changedProperties === void 0 || context.changedProperties.has("BlockDeviceMappings")) && blockDeviceMappingsDifferOnlyInDeleteOnTermination(beforeBdm, afterBdm)) {
19271
+ const flips = [];
19272
+ afterBdm.forEach((m, i) => {
19273
+ const before = beforeBdm[i];
19274
+ const read = (x) => {
19275
+ const ebs = x?.["Ebs"];
19276
+ if (ebs === void 0 || ebs === null) return void 0;
19277
+ return this.coerceBool(ebs["DeleteOnTermination"]) ?? true;
19278
+ };
19279
+ const want = read(m);
19280
+ if (want !== void 0 && want !== read(before) && typeof m["DeviceName"] === "string") flips.push({
19281
+ DeviceName: m["DeviceName"],
19282
+ DeleteOnTermination: want
19283
+ });
19284
+ });
19285
+ if (flips.length > 0) plan.deleteOnTermination = flips;
19286
+ }
19287
+ return plan;
19288
+ }
19289
+ async prepareInstanceMemberUpdates(logicalId, physicalId, resourceType, plan) {
19290
+ const needsStop = plan.instanceType !== void 0 || plan.userData !== void 0;
19291
+ const needsDefaultGroup = plan.securityGroups?.kind === "vpc-default";
19292
+ if (!needsStop && !needsDefaultGroup) return;
19293
+ const instance = (await this.ec2Client.send(new DescribeInstancesCommand({ InstanceIds: [physicalId] }))).Reservations?.[0]?.Instances?.[0];
19294
+ if (needsStop) {
19295
+ const members = [plan.instanceType !== void 0 ? "InstanceType" : "", plan.userData !== void 0 ? "UserData" : ""].filter(Boolean).join(" and ");
19296
+ if (instance?.RootDeviceType === "instance-store") throw markRefusedBeforeApplying(new ResourceUpdateNotSupportedError(resourceType, logicalId, `${members} can change only on a stopped instance, and an instance-store-backed instance cannot be stopped; CloudFormation replaces it. Re-deploy with cdkd deploy --replace.`));
19297
+ const state = instance?.State?.Name;
19298
+ if (!STOPPABLE_INSTANCE_STATES.has(state ?? "")) throw refusedBeforeApplying(new ProvisioningError(`${logicalId} (${resourceType}): instance ${physicalId} is ${state ?? "not returned by DescribeInstances"}, so its ${members} cannot be changed. The instance was not updated.`, resourceType, logicalId, physicalId));
19299
+ }
19300
+ if (needsDefaultGroup) plan.securityGroups = {
19301
+ kind: "set",
19302
+ ids: [await this.vpcDefaultSecurityGroupId(logicalId, physicalId, resourceType, instance?.VpcId)]
19303
+ };
19304
+ }
19305
+ async applyInstanceMemberUpdates(logicalId, physicalId, resourceType, plan) {
19306
+ const applied = [];
19307
+ if (plan.securityGroups?.kind === "set") {
19308
+ await this.ec2Client.send(new ModifyInstanceAttributeCommand({
19309
+ InstanceId: physicalId,
19310
+ Groups: plan.securityGroups.ids
19311
+ }));
19312
+ applied.push("the new SecurityGroupIds");
19313
+ }
19314
+ if (plan.profile !== void 0) {
19315
+ await this.applyInstanceProfileChange(logicalId, physicalId, plan.profile);
19316
+ applied.push(plan.profile.kind === "remove" ? "the IamInstanceProfile removed" : "the new IamInstanceProfile");
19317
+ }
19318
+ if (plan.deleteOnTermination !== void 0) {
19319
+ await this.ec2Client.send(new ModifyInstanceAttributeCommand({
19320
+ InstanceId: physicalId,
19321
+ BlockDeviceMappings: plan.deleteOnTermination.map((f) => ({
19322
+ DeviceName: f.DeviceName,
19323
+ Ebs: { DeleteOnTermination: f.DeleteOnTermination }
19324
+ }))
19325
+ }));
19326
+ applied.push("the new DeleteOnTermination");
19327
+ }
19328
+ if (plan.instanceType === void 0 && plan.userData === void 0) {
19329
+ if (instancesLeftStoppedByCdkd.has(physicalId)) {
19330
+ let state = await this.readInstanceState(physicalId);
19331
+ if (state === "stopping") state = await this.waitForInstanceState(logicalId, physicalId, resourceType, "stopped");
19332
+ if (state === "stopped") await this.ec2Client.send(new StartInstancesCommand({ InstanceIds: [physicalId] }));
19333
+ instancesLeftStoppedByCdkd.delete(physicalId);
19334
+ }
19335
+ return;
19336
+ }
19337
+ let state = await this.readInstanceState(physicalId);
19338
+ if (state === "pending") state = await this.waitForInstanceState(logicalId, physicalId, resourceType, "running");
19339
+ else if (state === "stopping") state = await this.waitForInstanceState(logicalId, physicalId, resourceType, "stopped");
19340
+ if (state !== "stopped" && state !== "running") throw markNonRetryable(new ProvisioningError(`${logicalId} (${resourceType}): instance ${physicalId} became ${state ?? "not returned by DescribeInstances"} during the update, so its InstanceType / UserData were not changed`, resourceType, logicalId, physicalId));
19341
+ const startAfter = state === "running" || instancesLeftStoppedByCdkd.has(physicalId);
19342
+ let phase = "stop";
19343
+ let stopIssued = false;
19344
+ try {
19345
+ if (state === "running") {
19346
+ this.logger.info(safeMsg`Stopping EC2 Instance ${logicalId} (${physicalId}) to change its ${plan.instanceType !== void 0 ? "InstanceType" : ""}${plan.instanceType !== void 0 && plan.userData !== void 0 ? " and " : ""}${plan.userData !== void 0 ? "UserData" : ""}; it is started again afterwards`);
19347
+ await this.ec2Client.send(new StopInstancesCommand({ InstanceIds: [physicalId] }));
19348
+ stopIssued = true;
19349
+ await this.waitForInstanceState(logicalId, physicalId, resourceType, "stopped");
19350
+ }
19351
+ phase = "modify";
19352
+ if (plan.instanceType !== void 0) {
19353
+ await this.ec2Client.send(new ModifyInstanceAttributeCommand({
19354
+ InstanceId: physicalId,
19355
+ InstanceType: { Value: plan.instanceType }
19356
+ }));
19357
+ applied.push("the new InstanceType");
19358
+ }
19359
+ if (plan.userData !== void 0) {
19360
+ await this.ec2Client.send(new ModifyInstanceAttributeCommand({
19361
+ InstanceId: physicalId,
19362
+ UserData: { Value: plan.userData }
19363
+ }));
19364
+ applied.push("the new UserData");
19365
+ }
19366
+ if (!startAfter) return;
19367
+ phase = "start";
19368
+ await this.ec2Client.send(new StartInstancesCommand({ InstanceIds: [physicalId] }));
19369
+ instancesLeftStoppedByCdkd.delete(physicalId);
19370
+ if (process.env["CDKD_NO_WAIT"] === "true") {
19371
+ this.logger.debug(safeMsg`Instance ${physicalId} started (skipping running-state wait per --no-wait)`);
19372
+ return;
19373
+ }
19374
+ phase = "wait";
19375
+ await this.waitForInstanceState(logicalId, physicalId, resourceType, "running", { stoppedIsTerminal: true });
19376
+ } catch (error) {
19377
+ const carries = applied.length === 0 ? "its previous settings" : `${applied.join(" and ")} already applied`;
19378
+ const startCommand = `aws ec2 start-instances --instance-ids ${physicalId}`;
19379
+ if (phase === "stop" && !stopIssued) throw error;
19380
+ if (phase === "wait") {
19381
+ if (instanceStoppedAfterStart.has(error)) {
19382
+ instancesLeftStoppedByCdkd.add(physicalId);
19383
+ this.logger.warn(safeMsg`${logicalId}: instance ${physicalId} stopped again right after cdkd started it, with ${carries}. Start it with: ${startCommand}`);
19384
+ }
19385
+ throw error;
19386
+ }
19387
+ if (!stopIssued && !startAfter) throw error;
19388
+ if (phase !== "start" && !isInterruptedWaitError(error) && !enclosingDeadlineExpired()) {
19389
+ let startFailure;
19390
+ try {
19391
+ await this.ec2Client.send(new StartInstancesCommand({ InstanceIds: [physicalId] }));
19392
+ } catch (e) {
19393
+ startFailure = e ?? /* @__PURE__ */ new Error("StartInstances failed");
19394
+ }
19395
+ if (startFailure === void 0) {
19396
+ instancesLeftStoppedByCdkd.delete(physicalId);
19397
+ this.logger.warn(safeMsg`${logicalId}: the InstanceType / UserData change failed after instance ${physicalId} was stopped; cdkd started it again, with ${carries}.`);
19398
+ } else {
19399
+ instancesLeftStoppedByCdkd.add(physicalId);
19400
+ this.logger.warn(safeMsg`${logicalId}: instance ${physicalId} was stopped for an InstanceType / UserData change that failed, and starting it again failed too (${describeAwsFailure(startFailure).summary}); it is stopped, with ${carries}. Start it with: ${startCommand}`);
19401
+ }
19402
+ throw error;
19403
+ }
19404
+ instancesLeftStoppedByCdkd.add(physicalId);
19405
+ this.logger.warn(phase === "stop" ? safeMsg`${logicalId}: instance ${physicalId} was being stopped for an InstanceType / UserData change that did not finish. Once it is stopped, start it with: ${startCommand}` : safeMsg`${logicalId}: instance ${physicalId} is stopped, with ${carries}; cdkd did not start it again. Start it with: ${startCommand}`);
19406
+ throw error;
19407
+ }
19408
+ }
19409
+ async readInstanceState(physicalId) {
19410
+ return (await this.ec2Client.send(new DescribeInstancesCommand({ InstanceIds: [physicalId] }))).Reservations?.[0]?.Instances?.[0]?.State?.Name;
19411
+ }
19412
+ async waitForInstanceState(logicalId, physicalId, resourceType, target, opts = {}) {
19413
+ const watch = startInterruptWatch(`EC2 Instance ${logicalId} ${target} wait`);
19414
+ const capAt = Date.now() + INSTANCE_STATE_WAIT_CAP_MS;
19415
+ let last;
19416
+ let stoppedReads = 0;
19417
+ try {
19418
+ for (;;) {
19419
+ if (watch.isInterrupted()) throw watch.onInterrupted();
19420
+ if (Math.min(enclosingDeadlineRemainingMs(), capAt - Date.now()) <= 0) throw markNonRetryable(new ProvisioningError(`${logicalId} (${resourceType}): timed out waiting for instance ${physicalId} to be ${target} (last state: ${last ?? "none reported"})`, resourceType, logicalId, physicalId));
19421
+ const previous = last;
19422
+ last = await this.readInstanceState(physicalId);
19423
+ if (last === target) return last;
19424
+ stoppedReads = last === "stopped" ? stoppedReads + 1 : 0;
19425
+ if (opts.stoppedIsTerminal === true && last === "stopped" && (previous === "pending" || stoppedReads >= STOPPED_AFTER_START_READS)) {
19426
+ const stopped = markNonRetryable(new ProvisioningError(`${logicalId} (${resourceType}): instance ${physicalId} stopped again right after it was started; run \`aws ec2 describe-instances --instance-ids ${physicalId}\` and read StateReason for AWS's cause`, resourceType, logicalId, physicalId));
19427
+ instanceStoppedAfterStart.add(stopped);
19428
+ throw stopped;
19429
+ }
19430
+ if (last === "terminated" || last === "shutting-down" || last === void 0) throw markNonRetryable(new ProvisioningError(`${logicalId} (${resourceType}): instance ${physicalId} is ${last ?? "no longer returned by DescribeInstances"} while cdkd waited for it to be ${target}`, resourceType, logicalId, physicalId));
19431
+ if (watch.isInterrupted()) throw watch.onInterrupted();
19432
+ await this.sleep(Math.max(0, Math.min(INSTANCE_STATE_POLL_MS, enclosingDeadlineRemainingMs())));
19433
+ }
19434
+ } finally {
19435
+ watch.dispose();
19436
+ }
19437
+ }
19438
+ async vpcDefaultSecurityGroupId(logicalId, physicalId, resourceType, vpcId) {
19439
+ const id = (vpcId === void 0 ? void 0 : await this.ec2Client.send(new DescribeSecurityGroupsCommand({ Filters: [{
19440
+ Name: "vpc-id",
19441
+ Values: [vpcId]
19442
+ }, {
19443
+ Name: "group-name",
19444
+ Values: ["default"]
19445
+ }] })))?.SecurityGroups?.[0]?.GroupId;
19446
+ if (id === void 0) throw refusedBeforeApplying(new ProvisioningError(`${logicalId} (${resourceType}): SecurityGroupIds was removed, and the default security group of instance ${physicalId}'s VPC (${vpcId ?? "unknown"}) could not be found to put in its place. The instance was not updated.`, resourceType, logicalId, physicalId));
19447
+ return id;
19448
+ }
19449
+ async applyInstanceProfileChange(logicalId, physicalId, change) {
19450
+ let assoc = await this.ec2Client.send(new DescribeIamInstanceProfileAssociationsCommand({ Filters: [{
19451
+ Name: "instance-id",
19452
+ Values: [physicalId]
19453
+ }] }));
19454
+ for (let poll = 1; poll <= PROFILE_DISASSOCIATING_POLLS && (assoc.IamInstanceProfileAssociations ?? []).some((a) => a.State === "disassociating"); poll++) {
19455
+ await this.sleep(PROFILE_DISASSOCIATING_DELAY_MS);
19456
+ assoc = await this.ec2Client.send(new DescribeIamInstanceProfileAssociationsCommand({ Filters: [{
19457
+ Name: "instance-id",
19458
+ Values: [physicalId]
19459
+ }] }));
19460
+ }
19461
+ const current = assoc.IamInstanceProfileAssociations?.find((a) => a.State === "associated" || a.State === "associating");
19462
+ if (change.kind === "remove") {
19463
+ if (current?.AssociationId === void 0) return;
19464
+ await this.ec2Client.send(new DisassociateIamInstanceProfileCommand({ AssociationId: current.AssociationId }));
19465
+ return;
19466
+ }
19467
+ const spec = change.arn !== void 0 ? { Arn: change.arn } : { Name: change.name };
19468
+ const maxAttempts = 10;
19469
+ for (let attempt = 1;; attempt++) try {
19470
+ if (current?.AssociationId !== void 0) await this.ec2Client.send(new ReplaceIamInstanceProfileAssociationCommand({
19471
+ AssociationId: current.AssociationId,
19472
+ IamInstanceProfile: spec
19473
+ }));
19474
+ else await this.ec2Client.send(new AssociateIamInstanceProfileCommand({
19475
+ InstanceId: physicalId,
19476
+ IamInstanceProfile: spec
19477
+ }));
19478
+ return;
19479
+ } catch (error) {
19480
+ const msg = describeAwsFailure(error).detail;
19481
+ if (this.isInstanceProfilePropagationError(msg) && attempt < maxAttempts) {
19482
+ this.logger.debug(safeMsg`IAM instance profile not yet propagated for ${logicalId} (${physicalId}) (attempt ${attempt}/${maxAttempts}: ${msg}), retrying in ${attempt}s...`);
19483
+ await this.sleep(attempt * 1e3);
19484
+ continue;
19485
+ }
19486
+ throw error;
19487
+ }
19488
+ }
18953
19489
  shallowEqual(a, b) {
18954
19490
  if (b === void 0) return false;
18955
19491
  const ra = a;
@@ -19140,7 +19676,7 @@ var EC2Provider = class {
19140
19676
  const raw = obj["CPUCredits"] ?? obj["CpuCredits"];
19141
19677
  return typeof raw === "string" ? raw : void 0;
19142
19678
  }
19143
- buildIpPermission(properties, direction = "ingress") {
19679
+ buildIpPermission(properties, direction = "ingress", inline = false) {
19144
19680
  const ipProtocol = properties["IpProtocol"] ?? "-1";
19145
19681
  const fromPort = properties["FromPort"];
19146
19682
  const toPort = properties["ToPort"];
@@ -19161,8 +19697,9 @@ var EC2Provider = class {
19161
19697
  permission.Ipv6Ranges = [ipv6Range];
19162
19698
  }
19163
19699
  const peerGroupId = direction === "egress" ? properties["DestinationSecurityGroupId"] : properties["SourceSecurityGroupId"];
19164
- if (peerGroupId) {
19165
- const groupPair = { GroupId: peerGroupId };
19700
+ const peerGroupName = inline && direction === "ingress" ? properties["SourceSecurityGroupName"] : void 0;
19701
+ if (peerGroupId || peerGroupName) {
19702
+ const groupPair = peerGroupId ? { GroupId: peerGroupId } : { GroupName: peerGroupName };
19166
19703
  if (direction === "ingress") {
19167
19704
  const peerOwnerId = properties["SourceSecurityGroupOwnerId"];
19168
19705
  if (peerOwnerId) groupPair.UserId = peerOwnerId;
@@ -19183,6 +19720,7 @@ var EC2Provider = class {
19183
19720
  const peerKey = direction === "egress" ? rule["DestinationSecurityGroupId"] : rule["SourceSecurityGroupId"];
19184
19721
  const prefixKey = direction === "egress" ? rule["DestinationPrefixListId"] : rule["SourcePrefixListId"];
19185
19722
  const peerOwner = direction === "ingress" ? rule["SourceSecurityGroupOwnerId"] : void 0;
19723
+ const peerName = direction === "ingress" ? rule["SourceSecurityGroupName"] : void 0;
19186
19724
  return JSON.stringify({
19187
19725
  p: sgProtocolKey(rule["IpProtocol"]),
19188
19726
  f: rule["FromPort"] ?? null,
@@ -19190,6 +19728,7 @@ var EC2Provider = class {
19190
19728
  c4: rule["CidrIp"] ?? null,
19191
19729
  c6: rule["CidrIpv6"] ?? null,
19192
19730
  peer: peerKey ?? null,
19731
+ peerName: peerKey ? null : peerName ?? null,
19193
19732
  peerOwner: peerOwner ?? null,
19194
19733
  pl: prefixKey ?? null,
19195
19734
  d: rule["Description"] ?? null
@@ -19218,7 +19757,7 @@ var EC2Provider = class {
19218
19757
  }));
19219
19758
  else await this.ec2Client.send(new RevokeSecurityGroupIngressCommand({
19220
19759
  GroupId: groupId,
19221
- IpPermissions: [this.buildIpPermission(rule, "ingress")]
19760
+ IpPermissions: [this.buildIpPermission(rule, "ingress", true)]
19222
19761
  }));
19223
19762
  } catch (error) {
19224
19763
  if (!this.isNotFoundError(error)) throw error;
@@ -19231,7 +19770,7 @@ var EC2Provider = class {
19231
19770
  }));
19232
19771
  else await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
19233
19772
  GroupId: groupId,
19234
- IpPermissions: [this.buildIpPermission(rule, "ingress")]
19773
+ IpPermissions: [this.buildIpPermission(rule, "ingress", true)]
19235
19774
  }));
19236
19775
  } catch (error) {
19237
19776
  if (!(error instanceof Error && error.message.includes("already exists"))) throw error;
@@ -19364,7 +19903,7 @@ var EC2Provider = class {
19364
19903
  const egress = properties["Egress"] ?? false;
19365
19904
  const parts = physicalId.split("|");
19366
19905
  if (parts.length !== 3) throw new ProvisioningError(compositeIdFormatMessage(EC2_NETWORK_ACL_ENTRY_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
19367
- if (typeof networkAclId !== "string" || ruleNumber === void 0 || parts[0] !== networkAclId || parts[1] !== String(ruleNumber) || parts[2] !== String(egress)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "NetworkAclId, RuleNumber and Egress identify the entry and are createOnly; a change to any of them requires a new entry. Re-deploy with cdkd deploy --replace, or destroy + redeploy.");
19906
+ if (typeof networkAclId !== "string" || ruleNumber === void 0 || parts[0] !== networkAclId || parts[1] !== String(ruleNumber) || parts[2] !== String(egress)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "NetworkAclId, RuleNumber and Egress identify the entry and are createOnly; a change to any of them requires a new entry. Re-deploy with cdkd deploy --replace, or destroy + redeploy");
19368
19907
  if (properties["Protocol"] == null || !properties["RuleAction"]) throw new ProvisioningError(`Protocol and RuleAction are required for NetworkAclEntry ${logicalId}`, resourceType, logicalId, physicalId);
19369
19908
  const ruleParams = networkAclEntryRuleParams(properties, properties["Icmp"] !== void 0 && properties["IcmpTypeCode"] !== void 0 && JSON.stringify(properties["Icmp"]) === JSON.stringify(previousProperties["Icmp"]) && JSON.stringify(properties["IcmpTypeCode"]) !== JSON.stringify(previousProperties["IcmpTypeCode"]));
19370
19909
  if (context?.desiredFromAwsReadback === true && ruleParams.CidrBlock !== void 0 && ruleParams.Ipv6CidrBlock !== void 0) {
@@ -24558,6 +25097,8 @@ function camelToPascalCaseKeys(value, preserveKeys) {
24558
25097
 
24559
25098
  //#endregion
24560
25099
  //#region src/provisioning/providers/agentcore-runtime-provider.ts
25100
+ const CFN_MAP_KEYS = /* @__PURE__ */ new Set(["Tags", "AdvertisedScopeMapping"]);
25101
+ const SDK_MAP_KEYS = /* @__PURE__ */ new Set(["tags", "advertisedScopeMapping"]);
24561
25102
  var AgentCoreRuntimeProvider = class {
24562
25103
  client;
24563
25104
  logger = getLogger().child("AgentCoreRuntimeProvider");
@@ -24595,16 +25136,16 @@ var AgentCoreRuntimeProvider = class {
24595
25136
  agentRuntimeName,
24596
25137
  roleArn
24597
25138
  };
24598
- if (properties["AgentRuntimeArtifact"] !== void 0) input["agentRuntimeArtifact"] = pascalToCamelCaseKeys(properties["AgentRuntimeArtifact"]);
24599
- if (properties["NetworkConfiguration"] !== void 0) input["networkConfiguration"] = pascalToCamelCaseKeys(properties["NetworkConfiguration"]);
25139
+ if (properties["AgentRuntimeArtifact"] !== void 0) input["agentRuntimeArtifact"] = pascalToCamelCaseKeys(properties["AgentRuntimeArtifact"], CFN_MAP_KEYS);
25140
+ if (properties["NetworkConfiguration"] !== void 0) input["networkConfiguration"] = pascalToCamelCaseKeys(properties["NetworkConfiguration"], CFN_MAP_KEYS);
24600
25141
  if (properties["Description"] !== void 0) input["description"] = properties["Description"];
24601
- if (properties["AuthorizerConfiguration"] !== void 0) input["authorizerConfiguration"] = pascalToCamelCaseKeys(properties["AuthorizerConfiguration"]);
25142
+ if (properties["AuthorizerConfiguration"] !== void 0) input["authorizerConfiguration"] = pascalToCamelCaseKeys(properties["AuthorizerConfiguration"], CFN_MAP_KEYS);
24602
25143
  if (properties["ProtocolConfiguration"] !== void 0) {
24603
25144
  const proto = properties["ProtocolConfiguration"];
24604
25145
  if (typeof proto === "string") input["protocolConfiguration"] = { serverProtocol: proto };
24605
- else input["protocolConfiguration"] = pascalToCamelCaseKeys(proto);
25146
+ else input["protocolConfiguration"] = pascalToCamelCaseKeys(proto, CFN_MAP_KEYS);
24606
25147
  }
24607
- if (properties["LifecycleConfiguration"] !== void 0 && typeof properties["LifecycleConfiguration"] === "object" && properties["LifecycleConfiguration"] !== null && Object.keys(properties["LifecycleConfiguration"]).length > 0) input["lifecycleConfiguration"] = pascalToCamelCaseKeys(properties["LifecycleConfiguration"]);
25148
+ if (properties["LifecycleConfiguration"] !== void 0 && typeof properties["LifecycleConfiguration"] === "object" && properties["LifecycleConfiguration"] !== null && Object.keys(properties["LifecycleConfiguration"]).length > 0) input["lifecycleConfiguration"] = pascalToCamelCaseKeys(properties["LifecycleConfiguration"], CFN_MAP_KEYS);
24608
25149
  if (properties["EnvironmentVariables"] !== void 0) input["environmentVariables"] = properties["EnvironmentVariables"];
24609
25150
  if (properties["ClientToken"] !== void 0) input["clientToken"] = properties["ClientToken"];
24610
25151
  const response = await this.client.send(new CreateAgentRuntimeCommand(input));
@@ -24630,16 +25171,16 @@ var AgentCoreRuntimeProvider = class {
24630
25171
  agentRuntimeId: physicalId,
24631
25172
  roleArn
24632
25173
  };
24633
- if (properties["AgentRuntimeArtifact"] !== void 0) input["agentRuntimeArtifact"] = pascalToCamelCaseKeys(properties["AgentRuntimeArtifact"]);
24634
- if (properties["NetworkConfiguration"] !== void 0) input["networkConfiguration"] = pascalToCamelCaseKeys(properties["NetworkConfiguration"]);
25174
+ if (properties["AgentRuntimeArtifact"] !== void 0) input["agentRuntimeArtifact"] = pascalToCamelCaseKeys(properties["AgentRuntimeArtifact"], CFN_MAP_KEYS);
25175
+ if (properties["NetworkConfiguration"] !== void 0) input["networkConfiguration"] = pascalToCamelCaseKeys(properties["NetworkConfiguration"], CFN_MAP_KEYS);
24635
25176
  if (properties["Description"] !== void 0) input["description"] = properties["Description"];
24636
- if (properties["AuthorizerConfiguration"] !== void 0) input["authorizerConfiguration"] = pascalToCamelCaseKeys(properties["AuthorizerConfiguration"]);
25177
+ if (properties["AuthorizerConfiguration"] !== void 0) input["authorizerConfiguration"] = pascalToCamelCaseKeys(properties["AuthorizerConfiguration"], CFN_MAP_KEYS);
24637
25178
  if (properties["ProtocolConfiguration"] !== void 0) {
24638
25179
  const proto = properties["ProtocolConfiguration"];
24639
25180
  if (typeof proto === "string") input["protocolConfiguration"] = { serverProtocol: proto };
24640
- else input["protocolConfiguration"] = pascalToCamelCaseKeys(proto);
25181
+ else input["protocolConfiguration"] = pascalToCamelCaseKeys(proto, CFN_MAP_KEYS);
24641
25182
  }
24642
- if (properties["LifecycleConfiguration"] !== void 0 && typeof properties["LifecycleConfiguration"] === "object" && properties["LifecycleConfiguration"] !== null && Object.keys(properties["LifecycleConfiguration"]).length > 0) input["lifecycleConfiguration"] = pascalToCamelCaseKeys(properties["LifecycleConfiguration"]);
25183
+ if (properties["LifecycleConfiguration"] !== void 0 && typeof properties["LifecycleConfiguration"] === "object" && properties["LifecycleConfiguration"] !== null && Object.keys(properties["LifecycleConfiguration"]).length > 0) input["lifecycleConfiguration"] = pascalToCamelCaseKeys(properties["LifecycleConfiguration"], CFN_MAP_KEYS);
24643
25184
  if (properties["EnvironmentVariables"] !== void 0) input["environmentVariables"] = properties["EnvironmentVariables"];
24644
25185
  if (properties["ClientToken"] !== void 0) input["clientToken"] = properties["ClientToken"];
24645
25186
  const response = await this.client.send(new UpdateAgentRuntimeCommand(input));
@@ -24681,7 +25222,7 @@ var AgentCoreRuntimeProvider = class {
24681
25222
  if (response.agentRuntimeVersion !== void 0) attributes["AgentRuntimeVersion"] = response.agentRuntimeVersion;
24682
25223
  if (response.status !== void 0) attributes["Status"] = response.status;
24683
25224
  if (response.createdAt !== void 0) attributes["CreatedAt"] = response.createdAt.toISOString();
24684
- if (response.workloadIdentityDetails !== void 0) attributes["WorkloadIdentityDetails"] = camelToPascalCaseKeys(response.workloadIdentityDetails);
25225
+ if (response.workloadIdentityDetails !== void 0) attributes["WorkloadIdentityDetails"] = camelToPascalCaseKeys(response.workloadIdentityDetails, SDK_MAP_KEYS);
24685
25226
  return attributes;
24686
25227
  }
24687
25228
  async getAttribute(physicalId, _resourceType, attributeName) {
@@ -24710,17 +25251,17 @@ var AgentCoreRuntimeProvider = class {
24710
25251
  const result = {};
24711
25252
  if (resp.agentRuntimeName !== void 0) result["AgentRuntimeName"] = resp.agentRuntimeName;
24712
25253
  if (resp.roleArn !== void 0) result["RoleArn"] = resp.roleArn;
24713
- if (resp.agentRuntimeArtifact !== void 0) result["AgentRuntimeArtifact"] = camelToPascalCaseKeys(resp.agentRuntimeArtifact);
24714
- if (resp.networkConfiguration !== void 0) result["NetworkConfiguration"] = camelToPascalCaseKeys(resp.networkConfiguration);
25254
+ if (resp.agentRuntimeArtifact !== void 0) result["AgentRuntimeArtifact"] = camelToPascalCaseKeys(resp.agentRuntimeArtifact, SDK_MAP_KEYS);
25255
+ if (resp.networkConfiguration !== void 0) result["NetworkConfiguration"] = camelToPascalCaseKeys(resp.networkConfiguration, SDK_MAP_KEYS);
24715
25256
  result["Description"] = resp.description ?? "";
24716
- if (resp.authorizerConfiguration !== void 0) result["AuthorizerConfiguration"] = camelToPascalCaseKeys(resp.authorizerConfiguration);
25257
+ if (resp.authorizerConfiguration !== void 0) result["AuthorizerConfiguration"] = camelToPascalCaseKeys(resp.authorizerConfiguration, SDK_MAP_KEYS);
24717
25258
  if (resp.protocolConfiguration !== void 0) {
24718
25259
  const proto = resp.protocolConfiguration;
24719
25260
  const keys = Object.keys(proto);
24720
25261
  if (keys.length === 1 && keys[0] === "serverProtocol" && typeof proto["serverProtocol"] === "string") result["ProtocolConfiguration"] = proto["serverProtocol"];
24721
- else result["ProtocolConfiguration"] = camelToPascalCaseKeys(resp.protocolConfiguration);
25262
+ else result["ProtocolConfiguration"] = camelToPascalCaseKeys(resp.protocolConfiguration, SDK_MAP_KEYS);
24722
25263
  }
24723
- if (resp.lifecycleConfiguration !== void 0) result["LifecycleConfiguration"] = camelToPascalCaseKeys(resp.lifecycleConfiguration);
25264
+ if (resp.lifecycleConfiguration !== void 0) result["LifecycleConfiguration"] = camelToPascalCaseKeys(resp.lifecycleConfiguration, SDK_MAP_KEYS);
24724
25265
  if (resp.environmentVariables !== void 0) result["EnvironmentVariables"] = resp.environmentVariables;
24725
25266
  return result;
24726
25267
  }
@@ -29871,7 +30412,7 @@ var DocDBProvider = class {
29871
30412
  createdResourceId = response.DBCluster?.DbClusterResourceId;
29872
30413
  if (!response.DBCluster) throw new Error("CreateDBCluster did not return DBCluster");
29873
30414
  this.logger.debug(`Successfully created DocDB DBCluster ${logicalId}: ${dbClusterIdentifier}`);
29874
- if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(dbClusterIdentifier);
30415
+ if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(logicalId, dbClusterIdentifier);
29875
30416
  return {
29876
30417
  physicalId: dbClusterIdentifier,
29877
30418
  attributes: clusterAttributes(await this.describeDBCluster(dbClusterIdentifier))
@@ -30026,7 +30567,7 @@ var DocDBProvider = class {
30026
30567
  createdResourceId = response.DBInstance?.DbiResourceId;
30027
30568
  if (!response.DBInstance) throw new Error("CreateDBInstance did not return DBInstance");
30028
30569
  this.logger.debug(`Successfully created DocDB DBInstance ${logicalId}: ${dbInstanceIdentifier}`);
30029
- if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForInstanceAvailable(dbInstanceIdentifier);
30570
+ if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForInstanceAvailable(logicalId, dbInstanceIdentifier);
30030
30571
  return {
30031
30572
  physicalId: dbInstanceIdentifier,
30032
30573
  attributes: instanceAttributes(await this.describeDBInstance(dbInstanceIdentifier))
@@ -30161,7 +30702,7 @@ var DocDBProvider = class {
30161
30702
  }
30162
30703
  requireClusterWhileWaiting(cluster, logicalId, change) {
30163
30704
  if (cluster === void 0) throw new Error(safeMsg`DescribeDBClusters returned no cluster for DocDB DBCluster ${logicalId} while waiting for its new ${change}`);
30164
- if (isTerminalDbStatus(cluster.Status)) throw new Error(safeMsg`DocDB DBCluster ${logicalId} entered status ${cluster.Status ?? ""} while applying its new ${change}`);
30705
+ if (isTerminalDbWaitStatus(cluster.Status)) throw new Error(safeMsg`DocDB DBCluster ${logicalId} entered status ${cluster.Status ?? ""} while applying its new ${change}`);
30165
30706
  return cluster;
30166
30707
  }
30167
30708
  async waitForClusterPortApplied(dbClusterIdentifier, logicalId, port, maxWaitMs = 18e5) {
@@ -30210,13 +30751,14 @@ var DocDBProvider = class {
30210
30751
  remainingMs: enclosingDeadlineRemainingMs()
30211
30752
  });
30212
30753
  }
30213
- async waitForClusterAvailable(dbClusterIdentifier, maxWaitMs = 18e5) {
30754
+ async waitForClusterAvailable(logicalId, dbClusterIdentifier, maxWaitMs = 18e5) {
30214
30755
  const startTime = Date.now();
30215
30756
  let delay = 5e3;
30216
30757
  while (Date.now() - startTime < maxWaitMs) {
30217
30758
  const status = (await this.describeDBCluster(dbClusterIdentifier))?.Status;
30218
30759
  this.logger.debug(`DocDB DBCluster ${dbClusterIdentifier} status: ${status}`);
30219
30760
  if (status === "available") return;
30761
+ if (isTerminalDbWaitStatus(status)) throw new Error(safeMsg`DocDB DBCluster ${logicalId} entered status ${status ?? ""} while being created, which it does not leave on its own`);
30220
30762
  await this.sleep(delay);
30221
30763
  delay = Math.min(delay * 2, 1e4);
30222
30764
  }
@@ -30240,13 +30782,14 @@ var DocDBProvider = class {
30240
30782
  }
30241
30783
  throw new Error(`Timed out waiting for DocDB DBCluster ${logicalId} to be deleted`);
30242
30784
  }
30243
- async waitForInstanceAvailable(dbInstanceIdentifier, maxWaitMs = 18e5) {
30785
+ async waitForInstanceAvailable(logicalId, dbInstanceIdentifier, maxWaitMs = 18e5) {
30244
30786
  const startTime = Date.now();
30245
30787
  let delay = 1e4;
30246
30788
  while (Date.now() - startTime < maxWaitMs) {
30247
30789
  const status = (await this.describeDBInstance(dbInstanceIdentifier))?.DBInstanceStatus;
30248
30790
  this.logger.debug(`DocDB DBInstance ${dbInstanceIdentifier} status: ${status}`);
30249
30791
  if (status === "available") return;
30792
+ if (isTerminalDbWaitStatus(status)) throw new Error(safeMsg`DocDB DBInstance ${logicalId} entered status ${status ?? ""} while being created, which it does not leave on its own`);
30250
30793
  await this.sleep(delay);
30251
30794
  delay = Math.min(delay * 2, 1e4);
30252
30795
  }
@@ -30511,6 +31054,18 @@ var DocDBSubnetGroupProvider = class {
30511
31054
  function isZoneDisabledForMutationError(error) {
30512
31055
  return error instanceof Error && error.message.includes("is marked disabled for mutation");
30513
31056
  }
31057
+ function declaresAcceleratedRecovery(properties, acceptReadSideKey = false) {
31058
+ const features = properties["HostedZoneFeatures"];
31059
+ if (isTruthyCfnBoolean(features?.["EnableAcceleratedRecovery"])) return true;
31060
+ const legacyStatus = features?.["AcceleratedRecoveryStatus"];
31061
+ return acceptReadSideKey && features?.["EnableAcceleratedRecovery"] === void 0 && typeof legacyStatus === "string" && ACCELERATED_RECOVERY_ON_STATUSES.has(legacyStatus);
31062
+ }
31063
+ const ACCELERATED_RECOVERY_ON_STATUSES = /* @__PURE__ */ new Set([
31064
+ "ENABLED",
31065
+ "ENABLING",
31066
+ "ENABLING_HOSTED_ZONE_LOCKED",
31067
+ "DISABLE_FAILED"
31068
+ ]);
30514
31069
  var HostedZoneNameNotFoundError = class HostedZoneNameNotFoundError extends ProvisioningError {
30515
31070
  constructor(message, resourceType, logicalId, physicalId, cause) {
30516
31071
  super(message, resourceType, logicalId, physicalId, cause);
@@ -30726,26 +31281,29 @@ var Route53Provider = class {
30726
31281
  }
30727
31282
  await this.applyHostedZoneTags(zoneId, properties, logicalId, void 0, mask);
30728
31283
  await this.applyQueryLoggingConfig(zoneId, properties, logicalId);
30729
- if (properties["HostedZoneFeatures"]?.["AcceleratedRecoveryStatus"] === "ENABLED") try {
30730
- await this.getClient().send(new UpdateHostedZoneFeaturesCommand({
30731
- HostedZoneId: zoneId,
30732
- EnableAcceleratedRecovery: true
30733
- }));
30734
- } catch (uhfError) {
30735
- this.logger.error(`UpdateHostedZoneFeatures failed for ${zoneId} after CreateHostedZone — rolling back the hosted zone to keep deploy retry idempotent`);
31284
+ if (declaresAcceleratedRecovery(properties)) {
30736
31285
  try {
30737
- await this.deleteQueryLoggingConfigForZone(zoneId, logicalId);
30738
- await this.getClient().send(new DeleteHostedZoneCommand({ Id: zoneId }));
30739
- zoneRolledBack = true;
30740
- callerReferenceToken.release();
30741
- } catch (deleteError) {
30742
- const head = `Best-effort rollback DeleteHostedZone for ${zoneId} also failed: `;
30743
- const m = maskerOrIdentity(mask);
30744
- this.logger.warn(m(`${head}${this.warnFailureText(head, deleteError, mask)} The operator may need to clean up the orphan zone.`));
31286
+ await this.getClient().send(new UpdateHostedZoneFeaturesCommand({
31287
+ HostedZoneId: zoneId,
31288
+ EnableAcceleratedRecovery: true
31289
+ }));
31290
+ } catch (uhfError) {
31291
+ this.logger.error(`UpdateHostedZoneFeatures failed for ${zoneId} after CreateHostedZone — rolling back the hosted zone to keep deploy retry idempotent`);
31292
+ try {
31293
+ await this.deleteQueryLoggingConfigForZone(zoneId, logicalId);
31294
+ await this.getClient().send(new DeleteHostedZoneCommand({ Id: zoneId }));
31295
+ zoneRolledBack = true;
31296
+ callerReferenceToken.release();
31297
+ } catch (deleteError) {
31298
+ const head = `Best-effort rollback DeleteHostedZone for ${zoneId} also failed: `;
31299
+ const m = maskerOrIdentity(mask);
31300
+ this.logger.warn(m(`${head}${this.warnFailureText(head, deleteError, mask)} The operator may need to clean up the orphan zone.`));
31301
+ }
31302
+ const cause = uhfError instanceof Error ? uhfError : void 0;
31303
+ markAuxiliaryFailure(uhfError, logicalId);
31304
+ throw this.wrapCaughtError(mask, uhfError, (text) => new ProvisioningError(`Failed to enable Accelerated Recovery on hosted zone ${logicalId} (${zoneId}): ${text}`, resourceType, logicalId, zoneId, cause));
30745
31305
  }
30746
- const cause = uhfError instanceof Error ? uhfError : void 0;
30747
- markAuxiliaryFailure(uhfError, logicalId);
30748
- throw this.wrapCaughtError(mask, uhfError, (text) => new ProvisioningError(`Failed to enable Accelerated Recovery on hosted zone ${logicalId} (${zoneId}): ${text}`, resourceType, logicalId, zoneId, cause));
31306
+ await this.waitForAcceleratedRecoveryFeatureSettled(zoneId, logicalId, resourceType, true, true);
30749
31307
  }
30750
31308
  const nameServers = response.DelegationSet?.NameServers ?? [];
30751
31309
  this.logger.debug(`Successfully created hosted zone ${logicalId}: ${zoneId}`);
@@ -30823,12 +31381,15 @@ var Route53Provider = class {
30823
31381
  await this.applyHostedZoneTags(physicalId, properties, logicalId, previousProperties, mask);
30824
31382
  await this.applyQueryLoggingConfig(physicalId, properties, logicalId, previousProperties);
30825
31383
  await this.syncVPCAssociations(physicalId, properties, logicalId);
30826
- const prevEnabled = previousProperties["HostedZoneFeatures"]?.["AcceleratedRecoveryStatus"] === "ENABLED";
30827
- const nextEnabled = properties["HostedZoneFeatures"]?.["AcceleratedRecoveryStatus"] === "ENABLED";
30828
- if (prevEnabled !== nextEnabled) await this.getClient().send(new UpdateHostedZoneFeaturesCommand({
30829
- HostedZoneId: physicalId,
30830
- EnableAcceleratedRecovery: nextEnabled
30831
- }));
31384
+ const prevEnabled = declaresAcceleratedRecovery(previousProperties, true);
31385
+ const nextEnabled = declaresAcceleratedRecovery(properties, context?.desiredFromAwsReadback === true);
31386
+ if (prevEnabled !== nextEnabled) {
31387
+ await this.getClient().send(new UpdateHostedZoneFeaturesCommand({
31388
+ HostedZoneId: physicalId,
31389
+ EnableAcceleratedRecovery: nextEnabled
31390
+ }));
31391
+ await this.waitForAcceleratedRecoveryFeatureSettled(physicalId, logicalId, resourceType, nextEnabled);
31392
+ }
30832
31393
  const nameServers = (await this.getClient().send(new GetHostedZoneCommand({ Id: physicalId }))).DelegationSet?.NameServers ?? [];
30833
31394
  this.logger.debug(`Successfully updated hosted zone ${logicalId}`);
30834
31395
  if (context?.desiredFromAwsReadback !== true && declaresNoVpcs(properties["VPCs"]) && Array.isArray(previousProperties["VPCs"]) && previousProperties["VPCs"].length > 0) this.logger.warn(context?.replayingState ? safeMsg`${logicalId} (AWS::Route53::HostedZone): VPCs is absent from the state being restored, so the rollback leaves the zone's VPC associations in place. ${VPCS_KEPT_ON_REMOVAL_NOTE}` : safeMsg`${logicalId} (AWS::Route53::HostedZone): VPCs was removed from the template, and the zone keeps its current VPC associations, as CloudFormation leaves them. ${VPCS_KEPT_ON_REMOVAL_NOTE}`);
@@ -31083,25 +31644,43 @@ var Route53Provider = class {
31083
31644
  throw this.wrapDeleteError(context?.maskSecrets, error, (text) => new ProvisioningError(`Failed to delete record set ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
31084
31645
  }
31085
31646
  }
31086
- async waitForAcceleratedRecoveryMutable(zoneId, logicalId, resourceType) {
31647
+ async waitForAcceleratedRecoveryFeatureSettled(zoneId, logicalId, resourceType, enabled, freshZone = false) {
31648
+ const verb = enabled ? "enable" : "disable";
31649
+ await this.waitForAcceleratedRecoveryMutable(zoneId, logicalId, resourceType, {
31650
+ blocked: `the hosted zone ${logicalId} can be deployed`,
31651
+ retry: `after the ${verb} of Accelerated Recovery (hosted zone ${logicalId}); re-run the deploy once the transition settles`,
31652
+ settledAt: enabled ? "ENABLED" : "DISABLED",
31653
+ freshZone
31654
+ });
31655
+ }
31656
+ async waitForAcceleratedRecoveryMutable(zoneId, logicalId, resourceType, purpose = {
31657
+ blocked: `the record set ${logicalId} can be deleted`,
31658
+ retry: `(record set ${logicalId}); re-run the destroy once the transition settles`
31659
+ }) {
31087
31660
  const client = this.getClient();
31088
31661
  const parsedInterval = Number.parseInt(process.env["CDKD_R53_ACCEL_RECOVERY_POLL_INTERVAL_MS"] ?? "15000", 10);
31089
31662
  const pollIntervalMs = Number.isFinite(parsedInterval) && parsedInterval > 0 ? parsedInterval : 15e3;
31090
31663
  const parsedTimeout = Number.parseInt(process.env["CDKD_R53_ACCEL_RECOVERY_POLL_TIMEOUT_MS"] ?? "600000", 10);
31091
31664
  const timeoutMs = Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : 6e5;
31092
31665
  const deadline = Date.now() + timeoutMs;
31093
- const TERMINAL_FAILED = /* @__PURE__ */ new Set(["ENABLE_FAILED", "DISABLE_FAILED"]);
31094
- const MUTABLE = /* @__PURE__ */ new Set(["ENABLED", "DISABLED"]);
31666
+ const TERMINAL_FAILED = new Set(purpose.settledAt === "ENABLED" ? ["ENABLE_FAILED"] : purpose.settledAt === "DISABLED" ? ["DISABLE_FAILED"] : ["ENABLE_FAILED", "DISABLE_FAILED"]);
31667
+ const MUTABLE = new Set(purpose.settledAt !== void 0 ? [purpose.settledAt] : ["ENABLED", "DISABLED"]);
31668
+ let sawTransition = purpose.settledAt === void 0 || purpose.freshZone === true;
31669
+ let lastStatus;
31095
31670
  while (Date.now() < deadline) {
31096
31671
  let status;
31097
31672
  try {
31098
31673
  status = (await client.send(new GetHostedZoneCommand({ Id: zoneId }))).HostedZone?.Features?.AcceleratedRecoveryStatus;
31099
31674
  } catch (err) {
31100
- if (err instanceof Error && err.name === "NoSuchHostedZone") return;
31675
+ if (err instanceof Error && err.name === "NoSuchHostedZone" && purpose.settledAt === void 0) return;
31101
31676
  throw err;
31102
31677
  }
31103
- if (status === void 0 || MUTABLE.has(status)) return;
31104
- if (TERMINAL_FAILED.has(status)) throw new ProvisioningError(`Cannot mutate hosted zone ${zoneId}: AcceleratedRecoveryStatus is '${status}' — operator must resolve before the record set ${logicalId} can be deleted`, resourceType, logicalId, zoneId);
31678
+ if (status === void 0) {
31679
+ if (purpose.settledAt !== "ENABLED") return;
31680
+ } else if (MUTABLE.has(status)) return;
31681
+ lastStatus = status;
31682
+ if (status?.includes("ING") === true) sawTransition = true;
31683
+ if (status !== void 0 && sawTransition && TERMINAL_FAILED.has(status)) throw new ProvisioningError(`Cannot mutate hosted zone ${zoneId}: AcceleratedRecoveryStatus is '${status}' — operator must resolve before ${purpose.blocked}`, resourceType, logicalId, zoneId);
31105
31684
  this.logger.debug(`Polling AcceleratedRecoveryStatus for ${zoneId}: ${status} (waiting for a mutable state, will re-poll in ~${pollIntervalMs}ms)`);
31106
31685
  const sliceEnd = Math.min(Date.now() + pollIntervalMs, deadline);
31107
31686
  while (Date.now() < sliceEnd) {
@@ -31110,7 +31689,7 @@ var Route53Provider = class {
31110
31689
  await new Promise((resolve) => setTimeout(resolve, tick));
31111
31690
  }
31112
31691
  }
31113
- throw new ProvisioningError(`Timed out after ${timeoutMs}ms waiting for AcceleratedRecoveryStatus to reach a mutable state on hosted zone ${zoneId} (record set ${logicalId}); re-run the destroy once the transition settles`, resourceType, logicalId, zoneId);
31692
+ throw new ProvisioningError(`Timed out after ${timeoutMs}ms waiting for AcceleratedRecoveryStatus to reach a mutable state on hosted zone ${zoneId} ${purpose.retry}${purpose.settledAt !== void 0 ? ` (last status: ${lastStatus ?? "none"})` : ""}`, resourceType, logicalId, zoneId);
31114
31693
  }
31115
31694
  buildResourceRecordSet(properties) {
31116
31695
  const name = properties["Name"];
@@ -31418,7 +31997,8 @@ var Route53Provider = class {
31418
31997
  const bare = resp.DelegationSet.Id.replace(/^\/delegationset\//, "");
31419
31998
  result["DelegationSetId"] = properties?.["DelegationSetId"] === `/delegationset/${bare}` ? `/delegationset/${bare}` : bare;
31420
31999
  }
31421
- if (resp.HostedZone.Features?.AcceleratedRecoveryStatus !== void 0) result["HostedZoneFeatures"] = { AcceleratedRecoveryStatus: resp.HostedZone.Features.AcceleratedRecoveryStatus };
32000
+ const acceleratedRecoveryStatus = resp.HostedZone.Features?.AcceleratedRecoveryStatus;
32001
+ if (acceleratedRecoveryStatus !== void 0) result["HostedZoneFeatures"] = { EnableAcceleratedRecovery: ACCELERATED_RECOVERY_ON_STATUSES.has(acceleratedRecoveryStatus) };
31422
32002
  if (resp.HostedZone.Config?.PrivateZone === true) result["VPCs"] = (resp.VPCs ?? []).map((v) => {
31423
32003
  const out = {};
31424
32004
  if (v.VPCId !== void 0) out["VPCId"] = v.VPCId;
@@ -33179,7 +33759,7 @@ var CognitoUserPoolProvider = class {
33179
33759
  //#region src/provisioning/providers/elasticache-provider.ts
33180
33760
  const CACHE_DELETE_WAIT_MS = Math.max(6e5, slowCcOperationTimeoutMs("AWS::ElastiCache::CacheCluster", "DELETE"));
33181
33761
  const NODE_TYPE_MODIFY_WAIT_MS = Math.max(36e5, slowCcOperationTimeoutMs("AWS::ElastiCache::CacheCluster", "UPDATE"));
33182
- const NODE_TYPE_DROPPED_AFTER_MS = 12e4;
33762
+ const MODIFY_DROPPED_AFTER_MS = 12e4;
33183
33763
  function availabilityZonesNotCovered(a, b) {
33184
33764
  const remaining = [...b];
33185
33765
  const out = [];
@@ -33190,6 +33770,27 @@ function availabilityZonesNotCovered(a, b) {
33190
33770
  }
33191
33771
  return out;
33192
33772
  }
33773
+ function nodesToRemove(nodes, desiredCount, desiredZones) {
33774
+ const remainingZones = desiredZones === void 0 ? void 0 : [...desiredZones];
33775
+ const named = nodes.filter((n) => n.id !== void 0);
33776
+ const keep = /* @__PURE__ */ new Set();
33777
+ const unnamed = nodes.length - named.length;
33778
+ const byIdDesc = [...named].sort((a, b) => a.id < b.id ? 1 : a.id > b.id ? -1 : 0);
33779
+ for (const node of byIdDesc) {
33780
+ if (keep.size + unnamed >= desiredCount) break;
33781
+ if (remainingZones === void 0) {
33782
+ keep.add(node.id);
33783
+ continue;
33784
+ }
33785
+ const i = node.zone === void 0 ? -1 : remainingZones.indexOf(node.zone);
33786
+ if (i >= 0) {
33787
+ remainingZones.splice(i, 1);
33788
+ keep.add(node.id);
33789
+ }
33790
+ }
33791
+ if (keep.size + unnamed < desiredCount) return void 0;
33792
+ return named.map((n) => n.id).filter((id) => !keep.has(id)).sort();
33793
+ }
33193
33794
  var ElastiCacheProvider = class {
33194
33795
  client;
33195
33796
  createClient;
@@ -33407,7 +34008,8 @@ var ElastiCacheProvider = class {
33407
34008
  const sgIds = rawSgIds && rawSgIds.length > 0 ? rawSgIds : void 0;
33408
34009
  const notificationTopicArn = properties["NotificationTopicArn"];
33409
34010
  const desiredNodeType = properties["CacheNodeType"];
33410
- const placement = await this.newNodePlacement(physicalId, properties, previousProperties);
34011
+ const nodeChanges = await this.planNodeChanges(logicalId, physicalId, resourceType, properties, previousProperties);
34012
+ const placement = nodeChanges.placement;
33411
34013
  this.warnNotAppliedInPlace(logicalId, properties, previousProperties, placement);
33412
34014
  await this.getClient().send(new ModifyCacheClusterCommand({
33413
34015
  CacheClusterId: physicalId,
@@ -33417,6 +34019,7 @@ var ElastiCacheProvider = class {
33417
34019
  NewAvailabilityZones: placement.newAvailabilityZones,
33418
34020
  ...placement.azMode !== void 0 && { AZMode: placement.azMode }
33419
34021
  },
34022
+ ...nodeChanges.removeIds.length > 0 && { CacheNodeIdsToRemove: nodeChanges.removeIds },
33420
34023
  SecurityGroupIds: sgIds,
33421
34024
  CacheParameterGroupName: properties["CacheParameterGroupName"],
33422
34025
  EngineVersion: properties["EngineVersion"],
@@ -33431,7 +34034,7 @@ var ElastiCacheProvider = class {
33431
34034
  ApplyImmediately: true
33432
34035
  }));
33433
34036
  this.logger.debug(`Successfully updated CacheCluster ${logicalId}`);
33434
- if (desiredNodeType !== void 0) await this.waitForClusterAvailable(physicalId, NODE_TYPE_MODIFY_WAIT_MS, desiredNodeType);
34037
+ if (desiredNodeType !== void 0 || nodeChanges.removeIds.length > 0) await this.waitForClusterAvailable(physicalId, NODE_TYPE_MODIFY_WAIT_MS, desiredNodeType, nodeChanges.removeIds);
33435
34038
  else await this.waitForClusterAvailable(physicalId);
33436
34039
  const described = await this.describeCacheCluster(physicalId);
33437
34040
  this.warnTransitEncryptionNotApplied(logicalId, properties, previousProperties, described?.TransitEncryptionEnabled);
@@ -33495,15 +34098,37 @@ var ElastiCacheProvider = class {
33495
34098
  const state = current === true ? "ENABLED" : "DISABLED";
33496
34099
  this.logger.warn(safeMsg`CacheCluster ${logicalId}: the cluster still has encryption in transit ${state} ` + safeMsg`(the template now says TransitEncryptionEnabled: ${String(next)}). ` + "ModifyCacheCluster cannot change it, and CloudFormation leaves it unchanged too; to change it, replace the cluster, for example by changing its logical ID.");
33497
34100
  }
33498
- async newNodePlacement(physicalId, properties, previousProperties) {
33499
- const desired = properties["PreferredAvailabilityZones"];
33500
- if (!Array.isArray(desired) || desired.length === 0) return void 0;
33501
- if (isDeepStrictEqual(desired, previousProperties["PreferredAvailabilityZones"]) && String(properties["NumCacheNodes"]) === String(previousProperties["NumCacheNodes"])) return;
33502
- const newAvailabilityZones = availabilityZonesNotCovered(desired, ((await this.describeCacheCluster(physicalId))?.CacheNodes ?? []).map((n) => n.CustomerAvailabilityZone).filter((az) => typeof az === "string"));
33503
- if (newAvailabilityZones.length === 0) return void 0;
34101
+ async planNodeChanges(logicalId, physicalId, resourceType, properties, previousProperties) {
34102
+ const none = {
34103
+ placement: void 0,
34104
+ removeIds: []
34105
+ };
34106
+ const desiredZones = properties["PreferredAvailabilityZones"];
34107
+ const zones = Array.isArray(desiredZones) && desiredZones.length > 0 ? desiredZones : void 0;
34108
+ if (!(String(properties["NumCacheNodes"]) !== String(previousProperties["NumCacheNodes"])) && (zones === void 0 || isDeepStrictEqual(zones, previousProperties["PreferredAvailabilityZones"]))) return none;
34109
+ if (zones === void 0 && !(Number(properties["NumCacheNodes"]) < Number(previousProperties["NumCacheNodes"]))) return none;
34110
+ const liveNodes = ((await this.describeCacheCluster(physicalId))?.CacheNodes ?? []).map((n) => ({
34111
+ id: n.CacheNodeId,
34112
+ zone: n.CustomerAvailabilityZone
34113
+ }));
34114
+ const desiredCount = properties["NumCacheNodes"] != null ? Number(properties["NumCacheNodes"]) : void 0;
34115
+ const previousCount = Number(previousProperties["NumCacheNodes"]);
34116
+ const templateLowered = Number(properties["NumCacheNodes"]) < previousCount;
34117
+ let removeIds = [];
34118
+ if (templateLowered && desiredCount !== void 0 && desiredCount < liveNodes.length) {
34119
+ if (liveNodes.length !== previousCount) throw new ProvisioningError(safeMsg`Cannot update CacheCluster ${logicalId}: the template lowers NumCacheNodes from ${String(previousCount)} ` + safeMsg`to ${String(desiredCount)}, but the cluster has ${String(liveNodes.length)} nodes, so ` + "the removal would delete nodes the template change does not account for. Deploy NumCacheNodes set to the live count first, then lower it.", resourceType, logicalId, physicalId);
34120
+ const plan = nodesToRemove(liveNodes, desiredCount, zones);
34121
+ if (plan === void 0) throw new ProvisioningError(safeMsg`Cannot update CacheCluster ${logicalId}: lowering NumCacheNodes to ${String(desiredCount)} ` + "with this PreferredAvailabilityZones list needs a node removed and a node added in a zone no live node occupies, in one update, which ModifyCacheCluster cannot do. Replace the cluster (for example by changing its logical ID), or list only zones the kept nodes occupy.", resourceType, logicalId, physicalId);
34122
+ removeIds = plan;
34123
+ this.logger.info(safeMsg`CacheCluster ${logicalId}: NumCacheNodes ${String(previousProperties["NumCacheNodes"])} -> ${String(desiredCount)}, removing cache nodes ${removeIds.join(", ")}`);
34124
+ }
34125
+ const newAvailabilityZones = zones === void 0 || removeIds.length > 0 ? [] : availabilityZonesNotCovered(zones, liveNodes.filter((n) => n.id === void 0 || !removeIds.includes(n.id)).map((n) => n.zone).filter((z) => typeof z === "string"));
33504
34126
  return {
33505
- newAvailabilityZones,
33506
- azMode: properties["AZMode"]
34127
+ placement: newAvailabilityZones.length > 0 ? {
34128
+ newAvailabilityZones,
34129
+ azMode: properties["AZMode"]
34130
+ } : void 0,
34131
+ removeIds
33507
34132
  };
33508
34133
  }
33509
34134
  warnNotAppliedInPlace(logicalId, properties, previousProperties, placement) {
@@ -33577,7 +34202,7 @@ var ElastiCacheProvider = class {
33577
34202
  ShowCacheNodeInfo: true
33578
34203
  }))).CacheClusters?.[0];
33579
34204
  }
33580
- async waitForClusterAvailable(cacheClusterId, maxWaitMs = 6e5, expectedNodeType) {
34205
+ async waitForClusterAvailable(cacheClusterId, maxWaitMs = 6e5, expectedNodeType, removedNodeIds = []) {
33581
34206
  const startTime = Date.now();
33582
34207
  let delay = 1e4;
33583
34208
  while (Date.now() - startTime < maxWaitMs) {
@@ -33585,12 +34210,14 @@ var ElastiCacheProvider = class {
33585
34210
  const status = cluster?.CacheClusterStatus;
33586
34211
  this.logger.debug(`CacheCluster ${cacheClusterId} status: ${status}`);
33587
34212
  const liveNodeType = cluster?.CacheNodeType;
33588
- if (status === "available" && (expectedNodeType === void 0 || liveNodeType === expectedNodeType)) return;
33589
- if (expectedNodeType !== void 0 && status === "available" && cluster?.PendingModifiedValues?.CacheNodeType === void 0 && Date.now() - startTime > NODE_TYPE_DROPPED_AFTER_MS) throw new Error(safeMsg`CacheCluster ${cacheClusterId} is available with node type ${liveNodeType} and no node-type change pending; the change to ${expectedNodeType} was not applied. Check the cluster's events (aws elasticache describe-events) for the reason, then deploy again.`);
34213
+ const removalDone = removedNodeIds.length === 0 || cluster?.CacheNodes !== void 0 && !cluster.CacheNodes.some((n) => n.CacheNodeId !== void 0 && removedNodeIds.includes(n.CacheNodeId));
34214
+ if (status === "available" && removalDone && (expectedNodeType === void 0 || liveNodeType === expectedNodeType)) return;
34215
+ if (!removalDone && status === "available" && (cluster?.PendingModifiedValues?.CacheNodeIdsToRemove ?? []).length === 0 && Date.now() - startTime > MODIFY_DROPPED_AFTER_MS) throw new Error(safeMsg`CacheCluster ${cacheClusterId} is available and still lists cache nodes ${removedNodeIds.join(", ")} with no removal pending; the removal was not applied. Check the cluster's events (aws elasticache describe-events) for the reason, then deploy again.`);
34216
+ if (expectedNodeType !== void 0 && liveNodeType !== expectedNodeType && status === "available" && cluster?.PendingModifiedValues?.CacheNodeType === void 0 && Date.now() - startTime > MODIFY_DROPPED_AFTER_MS) throw new Error(safeMsg`CacheCluster ${cacheClusterId} is available with node type ${liveNodeType} and no node-type change pending; the change to ${expectedNodeType} was not applied. Check the cluster's events (aws elasticache describe-events) for the reason, then deploy again.`);
33590
34217
  await this.sleep(delay);
33591
34218
  delay = Math.min(delay * 2, 1e4);
33592
34219
  }
33593
- throw new Error(expectedNodeType === void 0 ? `Timed out waiting for CacheCluster ${cacheClusterId} to become available` : safeMsg`Timed out waiting for CacheCluster ${cacheClusterId} to become available with node type ${expectedNodeType}`);
34220
+ throw new Error(expectedNodeType === void 0 && removedNodeIds.length === 0 ? `Timed out waiting for CacheCluster ${cacheClusterId} to become available` : safeMsg`Timed out waiting for CacheCluster ${cacheClusterId} to become available` + (expectedNodeType === void 0 ? "" : safeMsg` with node type ${expectedNodeType}`) + (removedNodeIds.length === 0 ? "" : safeMsg` without cache nodes ${removedNodeIds.join(", ")}`));
33594
34221
  }
33595
34222
  async waitForClusterDeleted(logicalId, cacheClusterId, maxWaitMs = 6e5) {
33596
34223
  const startTime = Date.now();
@@ -37831,15 +38458,20 @@ function desiredGlueTags(raw, resourceType, logicalId, physicalId) {
37831
38458
  const tags = refuseMalformedDesiredTags(tagMapAsList(raw), resourceType, logicalId, physicalId, "Tags", GLUE_TAGS_WHAT);
37832
38459
  return tags.length > 0 ? Object.fromEntries(tags.map((t) => [t.Key, t.Value])) : void 0;
37833
38460
  }
37834
- async function applyGlueTagDiff(client, resourceArn, recorded, desired, resourceType, logicalId, warn) {
38461
+ async function applyGlueTagDiff(client, resourceArn, recorded, desired, resourceType, logicalId, warn, unsettled = {
38462
+ forceUpsert: false,
38463
+ beforeWrite: () => void 0
38464
+ }) {
37835
38465
  const plan = planTagDiff(tagMapAsList(recorded), tagMapAsList(desired));
37836
38466
  const warning = tagPlanWarning(plan, resourceType, logicalId);
37837
38467
  if (warning !== void 0) warn(warning);
37838
- if (plan.set.size === 0 && plan.remove.length === 0) return;
38468
+ const toSet = unsettled.forceUpsert ? planTagDiff([], tagMapAsList(desired)).set : plan.set;
38469
+ if (toSet.size === 0 && plan.remove.length === 0) return;
38470
+ unsettled.beforeWrite();
37839
38471
  const arn = await resourceArn();
37840
- if (plan.set.size > 0) await client.send(new TagResourceCommand$19({
38472
+ if (toSet.size > 0) await client.send(new TagResourceCommand$19({
37841
38473
  ResourceArn: arn,
37842
- TagsToAdd: Object.fromEntries(plan.set)
38474
+ TagsToAdd: Object.fromEntries(toSet)
37843
38475
  }));
37844
38476
  if (plan.remove.length > 0) await client.send(new UntagResourceCommand$20({
37845
38477
  ResourceArn: arn,
@@ -37874,6 +38506,28 @@ const crawlerLeftInPlaceWarning = (logicalId, names, caller) => {
37874
38506
  const cfnKeeps = names.includes("LineageConfiguration") ? "" : `, as CloudFormation leaves ${one ? "it" : "them"}`;
37875
38507
  return caller === "rollback" ? safeMsg`${logicalId} (AWS::Glue::Crawler): ${subject} ${names.join(", ")} ${one ? "is" : "are"} absent from the state being restored; UpdateCrawler keeps a setting it is not sent, so the rollback leaves the value the failed deploy applied in place.` : safeMsg`${logicalId} (AWS::Glue::Crawler): ${subject} ${names.join(", ")} ${one ? "was" : "were"} removed from the template and ${one ? "keeps its" : "keep their"} current AWS value${cfnKeeps}; UpdateCrawler keeps a setting it is not sent. Declare the intended value explicitly to change it.`;
37876
38508
  };
38509
+ const CRAWLER_UPDATE_KEYS = [
38510
+ "Role",
38511
+ "Targets",
38512
+ "Classifiers",
38513
+ "Configuration",
38514
+ "CrawlerSecurityConfiguration",
38515
+ "DatabaseName",
38516
+ "Description",
38517
+ "LakeFormationConfiguration",
38518
+ "LineageConfiguration",
38519
+ "RecrawlPolicy",
38520
+ "Schedule",
38521
+ "SchemaChangePolicy",
38522
+ "TablePrefix"
38523
+ ];
38524
+ const TRIGGER_UPDATE_KEYS = [
38525
+ "Description",
38526
+ "Schedule",
38527
+ "Actions",
38528
+ "Predicate",
38529
+ "EventBatchingCondition"
38530
+ ];
37877
38531
  var GlueCrawlerProvider = class {
37878
38532
  client;
37879
38533
  createClient;
@@ -37956,6 +38610,7 @@ var GlueCrawlerProvider = class {
37956
38610
  const { value: v } = log;
37957
38611
  log.debug(`Updating Glue Crawler ${logicalId}: ${v(physicalId)}`);
37958
38612
  desiredGlueTags(properties["Tags"], resourceType, logicalId, physicalId);
38613
+ const markUnsettled = (key) => context?.markUnsettled?.(key);
37959
38614
  try {
37960
38615
  const updateInput = {
37961
38616
  Name: physicalId,
@@ -37963,6 +38618,7 @@ var GlueCrawlerProvider = class {
37963
38618
  ...properties["Targets"] !== void 0 && { Targets: toSdkCrawlerTargets(properties["Targets"]) },
37964
38619
  ...buildCrawlerCommonFields(properties)
37965
38620
  };
38621
+ for (const key of CRAWLER_UPDATE_KEYS) if (JSON.stringify(properties[key]) !== JSON.stringify(previousProperties[key])) markUnsettled(key);
37966
38622
  try {
37967
38623
  await this.getClient().send(new UpdateCrawlerCommand(updateInput));
37968
38624
  } catch (err) {
@@ -37972,7 +38628,10 @@ var GlueCrawlerProvider = class {
37972
38628
  await this.getClient().send(new UpdateCrawlerCommand(updateInput));
37973
38629
  } else throw err;
37974
38630
  }
37975
- await applyGlueTagDiff(this.getClient(), () => this.resourceArn(physicalId), previousProperties["Tags"], properties["Tags"], resourceType, logicalId, log.warn);
38631
+ await applyGlueTagDiff(this.getClient(), () => this.resourceArn(physicalId), previousProperties["Tags"], properties["Tags"], resourceType, logicalId, log.warn, {
38632
+ forceUpsert: context?.unsettledProperties?.has("Tags") === true,
38633
+ beforeWrite: () => markUnsettled("Tags")
38634
+ });
37976
38635
  if (!context?.desiredFromAwsReadback) {
37977
38636
  const removed = context?.removedProperties;
37978
38637
  const leftInPlace = [];
@@ -38448,6 +39107,7 @@ var GlueTriggerProvider = class {
38448
39107
  const { value: v } = log;
38449
39108
  log.debug(`Updating Glue Trigger ${logicalId}: ${v(physicalId)}`);
38450
39109
  desiredGlueTags(properties["Tags"], resourceType, logicalId, physicalId);
39110
+ const markUnsettled = (key) => context?.markUnsettled?.(key);
38451
39111
  try {
38452
39112
  let restart = false;
38453
39113
  try {
@@ -38467,6 +39127,7 @@ var GlueTriggerProvider = class {
38467
39127
  ...properties["EventBatchingCondition"] !== void 0 && { EventBatchingCondition: properties["EventBatchingCondition"] }
38468
39128
  };
38469
39129
  let updateError;
39130
+ for (const key of TRIGGER_UPDATE_KEYS) if (JSON.stringify(properties[key]) !== JSON.stringify(previousProperties[key])) markUnsettled(key);
38470
39131
  try {
38471
39132
  await this.getClient().send(new UpdateTriggerCommand({
38472
39133
  Name: physicalId,
@@ -38484,7 +39145,10 @@ var GlueTriggerProvider = class {
38484
39145
  if (restartFailure.redacted) log.debug(`Failed to re-activate Glue Trigger ${v(physicalId)} after a failed update: ${restartFailure.detail}`);
38485
39146
  }
38486
39147
  if (updateError !== void 0) throw updateError;
38487
- await applyGlueTagDiff(this.getClient(), () => this.resourceArn(physicalId), previousProperties["Tags"], properties["Tags"], resourceType, logicalId, log.warn);
39148
+ await applyGlueTagDiff(this.getClient(), () => this.resourceArn(physicalId), previousProperties["Tags"], properties["Tags"], resourceType, logicalId, log.warn, {
39149
+ forceUpsert: context?.unsettledProperties?.has("Tags") === true,
39150
+ beforeWrite: () => markUnsettled("Tags")
39151
+ });
38488
39152
  return {
38489
39153
  physicalId,
38490
39154
  wasReplaced: false
@@ -38494,6 +39158,14 @@ var GlueTriggerProvider = class {
38494
39158
  throw this.wrapMaskedError(log.mask, error, (text) => new ProvisioningError(`Failed to update Glue Trigger ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
38495
39159
  }
38496
39160
  }
39161
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names) {
39162
+ try {
39163
+ const state = (await this.getClient().send(new GetTriggerCommand({ Name: physicalId }))).Trigger?.State;
39164
+ return state === "ACTIVATING" || state === "DEACTIVATING" || state === "CREATING" || state === "UPDATING" ? new Set([...names].filter((name) => name !== "Tags")) : /* @__PURE__ */ new Set();
39165
+ } catch {
39166
+ return names;
39167
+ }
39168
+ }
38497
39169
  async waitForTriggerDeactivated(physicalId) {
38498
39170
  for (let attempt = 0; attempt < TRIGGER_DEACTIVATE_MAX_ATTEMPTS; attempt++) {
38499
39171
  try {
@@ -40052,6 +40724,7 @@ function previousMetricNames(value) {
40052
40724
  const expanded = expandShardLevelMetrics(value);
40053
40725
  return isUsableMetricsList(expanded) ? expanded : [];
40054
40726
  }
40727
+ const DEFAULT_MAX_RECORD_SIZE_KIB = 1024;
40055
40728
  function readMaxRecordSize(value, mask) {
40056
40729
  if (value == null) return { kind: "absent" };
40057
40730
  if (typeof value === "number" && Number.isFinite(value)) return {
@@ -40248,6 +40921,7 @@ var KinesisStreamProvider = class {
40248
40921
  const desiredRetention = readRetentionHours(properties["RetentionPeriodHours"], mask);
40249
40922
  if (desiredRetention.kind === "unusable" && context?.replayingState !== true) throw new ProvisioningError(desiredRetention.reason, resourceType, logicalId, physicalId);
40250
40923
  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`);
40924
+ const markUnsettled = (key) => context?.markUnsettled?.(key);
40251
40925
  const previousRetention = readRetentionHours(previousProperties["RetentionPeriodHours"], mask);
40252
40926
  try {
40253
40927
  const streamModeDetails = properties["StreamModeDetails"];
@@ -40257,6 +40931,7 @@ var KinesisStreamProvider = class {
40257
40931
  if (modeChanged) {
40258
40932
  const streamArn = await this.resolveStreamArn(physicalId);
40259
40933
  this.logger.debug(`Switching stream mode for ${physicalId}: from ${oldStreamMode} to ${streamMode}`);
40934
+ markUnsettled("StreamModeDetails");
40260
40935
  await this.getClient().send(new UpdateStreamModeCommand({
40261
40936
  StreamARN: streamArn,
40262
40937
  StreamModeDetails: { StreamMode: streamMode }
@@ -40265,9 +40940,10 @@ var KinesisStreamProvider = class {
40265
40940
  }
40266
40941
  if (streamMode === "PROVISIONED") {
40267
40942
  const newShardCount = Number(properties["ShardCount"] ?? 1);
40268
- const oldShardCount = modeChanged ? await this.getOpenShardCount(physicalId) : Number(previousProperties["ShardCount"] ?? 1);
40943
+ const oldShardCount = modeChanged || context?.unsettledProperties?.has("StreamModeDetails") === true ? await this.getOpenShardCount(physicalId) : Number(previousProperties["ShardCount"] ?? 1);
40269
40944
  if (newShardCount !== oldShardCount) {
40270
40945
  this.logger.debug(`Updating shard count for ${physicalId}: from ${oldShardCount} to ${newShardCount}`);
40946
+ markUnsettled("ShardCount");
40271
40947
  await this.getClient().send(new UpdateShardCountCommand({
40272
40948
  StreamName: physicalId,
40273
40949
  TargetShardCount: newShardCount,
@@ -40280,6 +40956,7 @@ var KinesisStreamProvider = class {
40280
40956
  const effectiveOldRetention = previousRetention.kind === "usable" ? previousRetention.hours ?? 24 : 24;
40281
40957
  if (desiredRetention.kind !== "unusable" && effectiveNewRetention !== effectiveOldRetention) {
40282
40958
  this.logger.debug(`Updating retention period for ${physicalId}: from ${effectiveOldRetention} to ${effectiveNewRetention}`);
40959
+ markUnsettled("RetentionPeriodHours");
40283
40960
  if (effectiveNewRetention > effectiveOldRetention) await this.getClient().send(new IncreaseStreamRetentionPeriodCommand({
40284
40961
  StreamName: physicalId,
40285
40962
  RetentionPeriodHours: effectiveNewRetention
@@ -40290,7 +40967,10 @@ var KinesisStreamProvider = class {
40290
40967
  }));
40291
40968
  await this.waitForStreamActive(physicalId);
40292
40969
  }
40293
- await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
40970
+ await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], {
40971
+ forceUpsert: context?.unsettledProperties?.has("Tags") === true,
40972
+ beforeWrite: () => markUnsettled("Tags")
40973
+ });
40294
40974
  const newEncryption = properties["StreamEncryption"];
40295
40975
  const oldEncryption = previousProperties["StreamEncryption"];
40296
40976
  const oldIsKms = isKmsEncryption(oldEncryption);
@@ -40298,6 +40978,7 @@ var KinesisStreamProvider = class {
40298
40978
  const oldKeyId = oldIsKms ? oldEncryption["KeyId"] : void 0;
40299
40979
  const newKeyId = newIsKms ? newEncryption["KeyId"] : void 0;
40300
40980
  if (oldIsKms !== newIsKms || oldIsKms && newIsKms && oldKeyId !== newKeyId) {
40981
+ markUnsettled("StreamEncryption");
40301
40982
  if (oldIsKms) {
40302
40983
  await this.getClient().send(new StopStreamEncryptionCommand({
40303
40984
  StreamName: physicalId,
@@ -40317,17 +40998,18 @@ var KinesisStreamProvider = class {
40317
40998
  }
40318
40999
  const desiredSizeRead = readMaxRecordSize(properties["MaxRecordSizeInKiB"], mask);
40319
41000
  if (desiredSizeRead.kind === "unusable") warn(`${desiredSizeRead.reason}. Leaving the maximum record size on ${physicalId} unchanged; the same value is REFUSED on a template-path create`);
40320
- const newMaxRecordSize = comparableRecordSize(properties["MaxRecordSizeInKiB"], mask);
40321
- const oldMaxRecordSize = comparableRecordSize(previousProperties["MaxRecordSizeInKiB"], mask);
40322
- if (newMaxRecordSize !== void 0 && newMaxRecordSize !== oldMaxRecordSize) {
40323
- this.logger.debug(`Updating max record size for ${physicalId}: from ${oldMaxRecordSize ?? "no value"} to ${newMaxRecordSize}`);
41001
+ const newMaxRecordSize = desiredSizeRead.kind === "absent" ? DEFAULT_MAX_RECORD_SIZE_KIB : comparableRecordSize(properties["MaxRecordSizeInKiB"], mask);
41002
+ const recordedMaxRecordSize = comparableRecordSize(previousProperties["MaxRecordSizeInKiB"], mask);
41003
+ if (newMaxRecordSize !== void 0 && newMaxRecordSize !== (recordedMaxRecordSize ?? DEFAULT_MAX_RECORD_SIZE_KIB)) {
41004
+ this.logger.debug(`Updating max record size for ${physicalId}: from ${recordedMaxRecordSize ?? "no value"} to ${newMaxRecordSize}`);
41005
+ markUnsettled("MaxRecordSizeInKiB");
40324
41006
  await this.getClient().send(new UpdateMaxRecordSizeCommand({
40325
41007
  StreamARN: await this.resolveStreamArn(physicalId),
40326
41008
  MaxRecordSizeInKiB: newMaxRecordSize
40327
41009
  }));
40328
41010
  await this.waitForStreamActive(physicalId);
40329
41011
  }
40330
- await this.reconcileDistributionStrategy(physicalId, streamMode, properties, previousProperties, mask, warn);
41012
+ await this.reconcileDistributionStrategy(physicalId, streamMode, properties, previousProperties, mask, warn, markUnsettled);
40331
41013
  const desiredRead = readShardLevelMetrics(properties["DesiredShardLevelMetrics"], mask);
40332
41014
  let desiredMetrics;
40333
41015
  if (desiredRead.kind === "unusable") warn(`${desiredRead.reason}. Leaving the shard-level metrics on ${physicalId} unchanged rather than disabling the live ones; the same value is REFUSED on a template-path create`);
@@ -40336,7 +41018,9 @@ var KinesisStreamProvider = class {
40336
41018
  const desiredSet = desiredRead.kind === "usable" ? desiredRead.metrics : [];
40337
41019
  const previousSet = previousMetricNames(previousProperties["DesiredShardLevelMetrics"]);
40338
41020
  const toDisable = previousSet.filter((m) => !desiredSet.includes(m));
41021
+ if (context?.unsettledProperties?.has("DesiredShardLevelMetrics") === true) await this.warnUndeclaredLiveMetrics(physicalId, [...previousSet, ...desiredSet], warn, mask);
40339
41022
  const toEnable = desiredSet.filter((m) => !previousSet.includes(m));
41023
+ if (toDisable.length > 0 || toEnable.length > 0) markUnsettled("DesiredShardLevelMetrics");
40340
41024
  if (toDisable.length > 0) {
40341
41025
  this.logger.debug(`Disabling ${toDisable.length} shard-level metric(s) on ${physicalId}`);
40342
41026
  await this.getClient().send(new DisableEnhancedMonitoringCommand({
@@ -40448,7 +41132,7 @@ var KinesisStreamProvider = class {
40448
41132
  throw error;
40449
41133
  }
40450
41134
  }
40451
- async reconcileDistributionStrategy(physicalId, streamMode, properties, previousProperties, mask, warn) {
41135
+ async reconcileDistributionStrategy(physicalId, streamMode, properties, previousProperties, mask, warn, markUnsettled) {
40452
41136
  const desired = readDistributionStrategy(properties["RecordDistributionStrategy"], mask);
40453
41137
  if (desired.kind === "unusable") {
40454
41138
  warn(`${desired.reason}. Leaving the record distribution strategy on ${physicalId} unchanged; the same value is REFUSED on a template-path create`);
@@ -40466,6 +41150,7 @@ var KinesisStreamProvider = class {
40466
41150
  target = DEFAULT_RECORD_DISTRIBUTION_STRATEGY;
40467
41151
  }
40468
41152
  this.logger.debug(mask(safeMsg`Updating record distribution strategy for ${physicalId}: from ${recorded ?? "no value"} to ${target}`));
41153
+ markUnsettled("RecordDistributionStrategy");
40469
41154
  await this.getClient().send(new UpdateStreamRecordDistributionStrategyCommand({
40470
41155
  StreamARN: await this.resolveStreamArn(physicalId),
40471
41156
  RecordDistributionStrategy: target
@@ -40494,12 +41179,16 @@ var KinesisStreamProvider = class {
40494
41179
  if (resourceType !== "AWS::Kinesis::Stream") return [];
40495
41180
  return ["DesiredShardLevelMetrics"];
40496
41181
  }
40497
- async applyTagDiff(streamName, resourceType, logicalId, oldTagsRaw, newTagsRaw) {
41182
+ async applyTagDiff(streamName, resourceType, logicalId, oldTagsRaw, newTagsRaw, unsettled = {
41183
+ forceUpsert: false,
41184
+ beforeWrite: () => void 0
41185
+ }) {
40498
41186
  const plan = planTagDiff(oldTagsRaw, newTagsRaw);
40499
41187
  const tagWarning = tagPlanWarning(plan, resourceType, logicalId);
40500
41188
  if (tagWarning !== void 0) this.logger.warn(tagWarning);
40501
- const tagsToAdd = Object.fromEntries(plan.set);
41189
+ const tagsToAdd = Object.fromEntries(unsettled.forceUpsert ? planTagDiff(void 0, newTagsRaw).set : plan.set);
40502
41190
  const tagsToRemove = plan.remove;
41191
+ if (tagsToRemove.length > 0 || Object.keys(tagsToAdd).length > 0) unsettled.beforeWrite();
40503
41192
  if (tagsToRemove.length > 0) {
40504
41193
  await this.getClient().send(new RemoveTagsFromStreamCommand({
40505
41194
  StreamName: streamName,
@@ -40515,6 +41204,46 @@ var KinesisStreamProvider = class {
40515
41204
  this.logger.debug(`Added/updated ${Object.keys(tagsToAdd).length} tag(s) on Kinesis stream ${streamName}`);
40516
41205
  }
40517
41206
  }
41207
+ async warnUndeclaredLiveMetrics(physicalId, named, warn, mask) {
41208
+ let live;
41209
+ try {
41210
+ live = ((await this.getClient().send(new DescribeStreamSummaryCommand({ StreamName: physicalId }))).StreamDescriptionSummary?.EnhancedMonitoring ?? []).flatMap((entry) => entry.ShardLevelMetrics ?? []);
41211
+ } catch {
41212
+ return;
41213
+ }
41214
+ const expandedLive = expandShardLevelMetrics(live);
41215
+ const undeclared = (Array.isArray(expandedLive) ? expandedLive : live).filter((metric) => metric !== "ALL" && !named.includes(metric));
41216
+ if (undeclared.length === 0) return;
41217
+ const secretDerived = mask(physicalId) !== physicalId;
41218
+ warn(`Kinesis stream ${physicalId} has shard-level metrics enabled that neither cdkd's record nor the template declares (${undeclared.join(", ")}), so cdkd left them enabled while converging the unsettled DesiredShardLevelMetrics (CloudFormation never disables a metric it did not record). Another tool may have enabled them, or an earlier failed cdkd update may have; cdkd cannot tell which. If they should be off, disable them ` + (secretDerived ? `with DisableEnhancedMonitoring for those metrics.` : `with: aws kinesis disable-enhanced-monitoring --stream-name ${physicalId} --shard-level-metrics ${undeclared.join(" ")} (in the stream's region)`));
41219
+ }
41220
+ async readUnsettled(physicalId, logicalId, resourceType, names, recordedProperties, desiredProperties) {
41221
+ const live = await this.readCurrentState(physicalId, logicalId, resourceType);
41222
+ if (typeof live !== "object" || live === null) return live;
41223
+ const out = { ...live };
41224
+ if (names.has("DesiredShardLevelMetrics") && Array.isArray(out["DesiredShardLevelMetrics"])) {
41225
+ const named = new Set([recordedProperties, desiredProperties].flatMap((bag) => {
41226
+ const expanded = expandShardLevelMetrics(bag?.["DesiredShardLevelMetrics"]);
41227
+ return isUsableMetricsList(expanded) ? expanded : [];
41228
+ }));
41229
+ const liveMetrics = expandShardLevelMetrics(out["DesiredShardLevelMetrics"]);
41230
+ out["DesiredShardLevelMetrics"] = (Array.isArray(liveMetrics) ? liveMetrics : []).filter((metric) => typeof metric === "string" && named.has(metric));
41231
+ }
41232
+ if (!names.has("ShardCount")) return out;
41233
+ return {
41234
+ ...out,
41235
+ ShardCount: await this.getOpenShardCount(physicalId)
41236
+ };
41237
+ }
41238
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names) {
41239
+ try {
41240
+ const status = (await this.getClient().send(new DescribeStreamSummaryCommand({ StreamName: physicalId }))).StreamDescriptionSummary?.StreamStatus;
41241
+ if (status !== "UPDATING" && status !== "CREATING") return /* @__PURE__ */ new Set();
41242
+ return new Set([...names].filter((name) => name !== "Tags"));
41243
+ } catch {
41244
+ return names;
41245
+ }
41246
+ }
40518
41247
  async readCurrentState(physicalId, _logicalId, resourceType, _properties) {
40519
41248
  if (resourceType !== "AWS::Kinesis::Stream") return void 0;
40520
41249
  let stream;
@@ -41280,6 +42009,13 @@ function pipeIdentityToken(name, arn, creationTime) {
41280
42009
  return `${arn}@${Math.floor(ms / 1e3)}`;
41281
42010
  }
41282
42011
  const SETTLED_STATES = /* @__PURE__ */ new Set(["RUNNING", "STOPPED"]);
42012
+ const MOVING_STATES = /* @__PURE__ */ new Set([
42013
+ "CREATING",
42014
+ "UPDATING",
42015
+ "STARTING",
42016
+ "STOPPING",
42017
+ "DELETING"
42018
+ ]);
41283
42019
  const PIPE_POLL_INTERVAL_MS = 5e3;
41284
42020
  const PIPE_SETTLE_MAX_WAIT_MS = 9e5;
41285
42021
  const PIPE_DELETE_MAX_WAIT_MS = 6e5;
@@ -41573,13 +42309,19 @@ var PipesPipeProvider = class {
41573
42309
  };
41574
42310
  const sourceParameters = toUpdateSourceParameters(desired["SourceParameters"], previousProperties["SourceParameters"]);
41575
42311
  if (sourceParameters !== void 0) input.SourceParameters = sourceParameters;
42312
+ for (const key of this.changedPipeFields(desired, previousProperties)) context?.markUnsettled?.(key);
41576
42313
  const accepted = await this.getClient().send(new UpdatePipeCommand(input));
41577
42314
  settled = await this.waitForSettled(physicalId, logicalId, resourceType, "update", mask, accepted.LastModifiedTime);
41578
42315
  }
41579
- const plan = planTagDiff(tagMapAsList(previousProperties["Tags"]), tagMapAsList(desiredTags));
42316
+ const recordedPlan = planTagDiff(tagMapAsList(previousProperties["Tags"]), tagMapAsList(desiredTags));
42317
+ const plan = context?.unsettledProperties?.has("Tags") === true ? {
42318
+ ...recordedPlan,
42319
+ set: planTagDiff([], tagMapAsList(desiredTags)).set
42320
+ } : recordedPlan;
41580
42321
  const warning = tagPlanWarning(plan, resourceType, logicalId);
41581
42322
  if (warning) this.logger.warn(warning);
41582
42323
  if (plan.set.size > 0 || plan.remove.length > 0) {
42324
+ context?.markUnsettled?.("Tags");
41583
42325
  settled ??= await this.describe(physicalId);
41584
42326
  const arn = settled.Arn;
41585
42327
  if (!arn) throw new Error("DescribePipe returned no Arn, which tagging needs");
@@ -41739,10 +42481,30 @@ var PipesPipeProvider = class {
41739
42481
  return value;
41740
42482
  }
41741
42483
  pipeFieldsChanged(desired, previous) {
42484
+ return this.changedPipeFields(desired, previous).length > 0;
42485
+ }
42486
+ changedPipeFields(desired, previous) {
41742
42487
  const keys = /* @__PURE__ */ new Set([...Object.keys(desired), ...Object.keys(previous)]);
41743
42488
  keys.delete("Tags");
41744
- for (const key of keys) if (JSON.stringify(desired[key]) !== JSON.stringify(previous[key])) return true;
41745
- return false;
42489
+ return [...keys].filter((key) => JSON.stringify(desired[key]) !== JSON.stringify(previous[key]));
42490
+ }
42491
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names) {
42492
+ try {
42493
+ const state = (await this.describe(physicalId)).CurrentState;
42494
+ if (state === void 0 || !MOVING_STATES.has(state)) return /* @__PURE__ */ new Set();
42495
+ return new Set([...names].filter((name) => name !== "Tags"));
42496
+ } catch {
42497
+ return names;
42498
+ }
42499
+ }
42500
+ async readUnsettled(physicalId, logicalId, resourceType, names) {
42501
+ const live = await this.readCurrentState(physicalId, logicalId, resourceType);
42502
+ if (typeof live !== "object" || live === null) return live;
42503
+ const state = (await this.describe(physicalId)).CurrentState;
42504
+ if (state === void 0 || !state.endsWith("_FAILED")) return live;
42505
+ const out = { ...live };
42506
+ for (const name of names) delete out[name];
42507
+ return out;
41746
42508
  }
41747
42509
  async readStateForDelete(name) {
41748
42510
  try {
@@ -41905,6 +42667,7 @@ var EFSProvider = class {
41905
42667
  providerRegion = ambientRegion();
41906
42668
  logger = getLogger().child("EFSProvider");
41907
42669
  unconfirmedCreationTokens = /* @__PURE__ */ new Map();
42670
+ removalDefaults = /* @__PURE__ */ new Map([["AWS::EFS::FileSystem", /* @__PURE__ */ new Map([["BackupPolicy", { Status: "DISABLED" }], ["FileSystemProtection", { ReplicationOverwriteProtection: "ENABLED" }]])]]);
41908
42671
  handledProperties = /* @__PURE__ */ new Map([
41909
42672
  ["AWS::EFS::FileSystem", /* @__PURE__ */ new Set([
41910
42673
  "FileSystemTags",
@@ -41976,13 +42739,56 @@ var EFSProvider = class {
41976
42739
  }
41977
42740
  update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
41978
42741
  const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
42742
+ properties = withRemovalDefaults(this.removalDefaults, resourceType, properties, previousProperties, context);
41979
42743
  switch (resourceType) {
41980
42744
  case "AWS::EFS::FileSystem": return this.updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties, log);
41981
42745
  case "AWS::EFS::MountTarget": return this.updateMountTarget(logicalId, physicalId, resourceType, properties, log);
41982
- case "AWS::EFS::AccessPoint": return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS EFS AccessPoint has no in-place update API — there is no UpdateAccessPoint command; every property change requires DeleteAccessPoint + CreateAccessPoint. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack."));
42746
+ case "AWS::EFS::AccessPoint": return this.updateAccessPoint(logicalId, physicalId, resourceType, properties, previousProperties, log);
41983
42747
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
41984
42748
  }
41985
42749
  }
42750
+ async applyTagDiff(resourceId, resourceType, logicalId, recorded, desired, log) {
42751
+ const plan = planTagDiff(recorded, desired);
42752
+ const warning = tagPlanWarning(plan, resourceType, logicalId);
42753
+ if (warning !== void 0) log.warn(warning);
42754
+ if (plan.remove.length > 0) await this.retryOnTransientControlPlane(() => this.getClient().send(new UntagResourceCommand$24({
42755
+ ResourceId: resourceId,
42756
+ TagKeys: plan.remove
42757
+ })), `UntagResource ${resourceId}`, log);
42758
+ if (plan.set.size > 0) await this.retryOnTransientControlPlane(() => this.getClient().send(new TagResourceCommand$23({
42759
+ ResourceId: resourceId,
42760
+ Tags: [...plan.set].map(([Key, Value]) => ({
42761
+ Key,
42762
+ Value
42763
+ }))
42764
+ })), `TagResource ${resourceId}`, log);
42765
+ }
42766
+ async updateAccessPoint(logicalId, physicalId, resourceType, properties, previousProperties, log) {
42767
+ const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
42768
+ const sameExceptSecretLeaves = (desired, recorded) => {
42769
+ if (desired !== void 0 && typeof recorded === "string" && holdsSecretDerivedEntry(recorded)) return true;
42770
+ if (Array.isArray(desired) && Array.isArray(recorded)) return desired.length === recorded.length && desired.every((d, i) => sameExceptSecretLeaves(d, recorded[i]));
42771
+ if (isPlainObject(desired) && isPlainObject(recorded)) return [.../* @__PURE__ */ new Set([...Object.keys(desired), ...Object.keys(recorded)])].every((k) => sameExceptSecretLeaves(desired[k], recorded[k]));
42772
+ return isDeepStrictEqual(desired, recorded);
42773
+ };
42774
+ const keys = /* @__PURE__ */ new Set([...Object.keys(properties), ...Object.keys(previousProperties)]);
42775
+ for (const key of keys) {
42776
+ if (key === "AccessPointTags") continue;
42777
+ if (!sameExceptSecretLeaves(properties[key], previousProperties[key])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS EFS AccessPoint ${key} changed or was removed, and it is immutable on AWS — there is no UpdateAccessPoint command, and only AccessPointTags can change in place; any other property change requires DeleteAccessPoint + CreateAccessPoint. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack.`);
42778
+ }
42779
+ refuseMalformedDesiredTags(properties["AccessPointTags"], resourceType, logicalId, physicalId, "AccessPointTags");
42780
+ try {
42781
+ await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["AccessPointTags"], properties["AccessPointTags"], log);
42782
+ return {
42783
+ physicalId,
42784
+ wasReplaced: false
42785
+ };
42786
+ } catch (error) {
42787
+ if (error instanceof ProvisioningError) throw error;
42788
+ const cause = error instanceof Error ? error : void 0;
42789
+ throw this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to update EFS AccessPoint ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
42790
+ }
42791
+ }
41986
42792
  async updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties, log) {
41987
42793
  for (const key of [
41988
42794
  "Encrypted",
@@ -42006,7 +42812,9 @@ var EFSProvider = class {
42006
42812
  const backupChanged = changed("BackupPolicy");
42007
42813
  const policyChanged = changed("FileSystemPolicy") || changed("BypassPolicyLockoutSafetyCheck");
42008
42814
  const protectionChanged = changed("FileSystemProtection");
42009
- if (!throughputModeChanged && !provisionedChanged && !lifecycleChanged && !backupChanged && !policyChanged && !protectionChanged) {
42815
+ const tagsChanged = changed("FileSystemTags");
42816
+ if (tagsChanged) refuseMalformedDesiredTags(properties["FileSystemTags"], resourceType, logicalId, physicalId, "FileSystemTags");
42817
+ if (!throughputModeChanged && !provisionedChanged && !lifecycleChanged && !backupChanged && !policyChanged && !protectionChanged && !tagsChanged) {
42010
42818
  log.debug(`No mutable diff for EFS FileSystem ${logicalId}, skipping update`);
42011
42819
  return {
42012
42820
  physicalId,
@@ -42025,8 +42833,12 @@ var EFSProvider = class {
42025
42833
  }
42026
42834
  if (lifecycleChanged) await this.applyLifecyclePolicies(physicalId, properties["LifecyclePolicies"], log, previousProperties["LifecyclePolicies"]);
42027
42835
  if (backupChanged) await this.applyBackupPolicy(physicalId, properties["BackupPolicy"], log);
42028
- if (policyChanged) await this.applyFileSystemPolicy(physicalId, properties["FileSystemPolicy"], properties["BypassPolicyLockoutSafetyCheck"], log);
42836
+ if (policyChanged) {
42837
+ if (properties["FileSystemPolicy"] === void 0 && previousProperties["FileSystemPolicy"] !== void 0) await this.deleteFileSystemPolicy(physicalId, log);
42838
+ else await this.applyFileSystemPolicy(physicalId, properties["FileSystemPolicy"], properties["BypassPolicyLockoutSafetyCheck"], log);
42839
+ }
42029
42840
  if (protectionChanged) await this.applyFileSystemProtection(physicalId, properties["FileSystemProtection"], log);
42841
+ if (tagsChanged) await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["FileSystemTags"], properties["FileSystemTags"], log);
42030
42842
  log.debug(`Successfully updated EFS FileSystem ${logicalId}`);
42031
42843
  return {
42032
42844
  physicalId,
@@ -42234,6 +43046,10 @@ var EFSProvider = class {
42234
43046
  })), `set FileSystemPolicy on ${fileSystemId}`, log);
42235
43047
  log.debug(`Set FileSystemPolicy on EFS FileSystem ${fileSystemId}`);
42236
43048
  }
43049
+ async deleteFileSystemPolicy(fileSystemId, log) {
43050
+ await this.retryOnTransientControlPlane(() => this.getClient().send(new DeleteFileSystemPolicyCommand({ FileSystemId: fileSystemId })), `delete FileSystemPolicy on ${fileSystemId}`, log);
43051
+ log.debug(`Deleted FileSystemPolicy on EFS FileSystem ${fileSystemId}`);
43052
+ }
42237
43053
  async applyFileSystemProtection(fileSystemId, spec, log) {
42238
43054
  if (spec === void 0 || spec === null) return;
42239
43055
  const protection = spec.ReplicationOverwriteProtection;
@@ -42742,6 +43558,23 @@ const routeTableDelta = (next, prev) => {
42742
43558
  ...remove.length > 0 && { remove }
42743
43559
  };
42744
43560
  };
43561
+ const STALE_FAILED_READS = 3;
43562
+ const ALIAS_NON_BLOCKING_ACTIONS = /* @__PURE__ */ new Set(["STORAGE_OPTIMIZATION", "IOPS_OPTIMIZATION"]);
43563
+ const runningAliasBlockingActions = (fs) => (fs.AdministrativeActions ?? []).filter((a) => (a.Status === "PENDING" || a.Status === "IN_PROGRESS") && !ALIAS_NON_BLOCKING_ACTIONS.has(a.AdministrativeActionType ?? "")).map((a) => a.AdministrativeActionType ?? "unknown");
43564
+ const isAdministrativeActionInProgress = (error) => error instanceof Error && error.name === "BadRequest" && /in progress/i.test(describeAwsFailure(error).detail);
43565
+ const isTransientFsxError = (error) => {
43566
+ const name = error instanceof Error ? error.name : "";
43567
+ return name === "ThrottlingException" || name === "ServiceLimitExceeded" || name === "InternalServerError" || name === "TimeoutError" || /rate exceeded|too many requests|timed? ?out|ECONNRESET|EPIPE|socket hang up/i.test(describeAwsFailure(error).detail);
43568
+ };
43569
+ const aliasDelta = (next, prev) => {
43570
+ const names = (value) => new Set((Array.isArray(value) ? value : []).filter((v) => typeof v === "string").map((v) => v.toLowerCase()));
43571
+ const nextNames = names(next);
43572
+ const prevNames = names(prev);
43573
+ return {
43574
+ add: [...nextNames].filter((n) => !prevNames.has(n)),
43575
+ remove: [...prevNames].filter((n) => !nextNames.has(n))
43576
+ };
43577
+ };
42745
43578
  const TOP_LEVEL_IMMUTABLE_PROPS = [
42746
43579
  "FileSystemType",
42747
43580
  "SubnetIds",
@@ -43144,11 +43977,16 @@ var FSxFileSystemProvider = class {
43144
43977
  if (JSON.stringify(next ?? null) !== JSON.stringify(prev ?? null)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS FSx FileSystem ${key} is immutable on AWS — UpdateFileSystem does not accept ${key}; the property is fixed at creation. Re-deploy with cdkd deploy --replace --force-stateful-recreation (the type is in cdkd's stateful-recreate guard set, so a bare --replace is refused a second time with STATEFUL_REPLACE_BLOCKED), or destroy + redeploy the stack.`);
43145
43978
  }
43146
43979
  const variantConfigKey = this.detectVariantConfigKey(properties, previousProperties);
43147
- const { diff: variantDiff, hasMutableDiff: variantHasMutableDiff, leftInPlace } = variantConfigKey ? this.computeVariantConfigDiff(resourceType, logicalId, variantConfigKey, properties[variantConfigKey] ?? {}, previousProperties[variantConfigKey] ?? {}) : {
43980
+ const { diff: variantDiff, hasMutableDiff: variantHasMutableDiff, leftInPlace, aliases } = variantConfigKey ? this.computeVariantConfigDiff(resourceType, logicalId, variantConfigKey, properties[variantConfigKey] ?? {}, previousProperties[variantConfigKey] ?? {}) : {
43148
43981
  diff: {},
43149
43982
  hasMutableDiff: false,
43150
- leftInPlace: []
43983
+ leftInPlace: [],
43984
+ aliases: {
43985
+ add: [],
43986
+ remove: []
43987
+ }
43151
43988
  };
43989
+ const aliasesChanged = aliases.add.length > 0 || aliases.remove.length > 0;
43152
43990
  const leftInPlaceNames = [...TOP_LEVEL_LEFT_IN_PLACE_PROPS.filter((key) => properties[key] === void 0 && previousProperties[key] !== void 0), ...leftInPlace.map((key) => `${variantConfigKey}.${key}`)];
43153
43991
  const warnLeftInPlace = () => {
43154
43992
  if (leftInPlaceNames.length === 0 || context?.desiredFromAwsReadback) return;
@@ -43169,7 +44007,7 @@ var FSxFileSystemProvider = class {
43169
44007
  const typeVersionChanged = sendsChange("FileSystemTypeVersion");
43170
44008
  const networkTypeChanged = sendsChange("NetworkType");
43171
44009
  const tagsChanged = changed("Tags");
43172
- if (!variantHasMutableDiff && !storageCapacityChanged && !storageTypeChanged && !typeVersionChanged && !networkTypeChanged && !tagsChanged) {
44010
+ if (!variantHasMutableDiff && !storageCapacityChanged && !storageTypeChanged && !typeVersionChanged && !networkTypeChanged && !aliasesChanged && !tagsChanged) {
43173
44011
  this.logger.debug(`No mutable diff for FSx FileSystem ${logicalId}, skipping update`);
43174
44012
  warnLeftInPlace();
43175
44013
  return {
@@ -43179,6 +44017,7 @@ var FSxFileSystemProvider = class {
43179
44017
  }
43180
44018
  this.logger.debug(`Updating FSx FileSystem ${logicalId}: ${physicalId}`);
43181
44019
  try {
44020
+ const deadline = Date.now() + this.maxWaitMs;
43182
44021
  const needsUpdateCall = variantHasMutableDiff || storageCapacityChanged || storageTypeChanged || typeVersionChanged || networkTypeChanged;
43183
44022
  let updatedFs;
43184
44023
  if (needsUpdateCall) {
@@ -43193,8 +44032,9 @@ var FSxFileSystemProvider = class {
43193
44032
  }))).FileSystem?.AdministrativeActions ?? []).filter((a) => a.AdministrativeActionType === "FILE_SYSTEM_UPDATE" && (a.Status === "PENDING" || a.Status === "IN_PROGRESS"));
43194
44033
  const requestTimes = respPendingActions.map((a) => a.RequestTime?.getTime()).filter((t) => t !== void 0);
43195
44034
  const actionThresholdMs = requestTimes.length > 0 ? Math.min(...requestTimes) : callTimeMs - 6e4;
43196
- updatedFs = await this.waitForUpdateActionComplete(physicalId, logicalId, resourceType, actionThresholdMs, respPendingActions.length > 0, context?.maskSecrets);
44035
+ updatedFs = await this.waitForUpdateActionComplete(physicalId, logicalId, resourceType, actionThresholdMs, respPendingActions.length > 0, deadline, context?.maskSecrets);
43197
44036
  }
44037
+ if (aliasesChanged) await this.applyAliasDelta(physicalId, logicalId, resourceType, aliases, deadline);
43198
44038
  if (tagsChanged) await this.applyTagDiff(physicalId, resourceType, logicalId, properties["Tags"], previousProperties["Tags"]);
43199
44039
  if (!updatedFs) try {
43200
44040
  updatedFs = (await this.getClient().send(new DescribeFileSystemsCommand$1({ FileSystemIds: [physicalId] }))).FileSystems?.[0];
@@ -43222,11 +44062,20 @@ var FSxFileSystemProvider = class {
43222
44062
  const keys = /* @__PURE__ */ new Set([...Object.keys(next), ...Object.keys(prev)]);
43223
44063
  const diff = {};
43224
44064
  const leftInPlace = [];
44065
+ let aliases = {
44066
+ add: [],
44067
+ remove: []
44068
+ };
43225
44069
  let hasMutableDiff = false;
43226
44070
  for (const key of keys) {
43227
44071
  const nextVal = next[key];
43228
44072
  const prevVal = prev[key];
43229
44073
  if (JSON.stringify(nextVal) === JSON.stringify(prevVal)) continue;
44074
+ if (configKey === "OpenZFSConfiguration" && key === "Options") continue;
44075
+ if (configKey === "WindowsConfiguration" && key === "Aliases") {
44076
+ aliases = aliasDelta(nextVal, prevVal);
44077
+ continue;
44078
+ }
43230
44079
  if (!mutable.has(key)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS FSx FileSystem ${configKey}.${key} is immutable on AWS — UpdateFileSystem cannot change it after creation. Re-deploy with cdkd deploy --replace --force-stateful-recreation (the type is in cdkd's stateful-recreate guard set, so a bare --replace is refused a second time with STATEFUL_REPLACE_BLOCKED), or destroy + redeploy the stack.`);
43231
44080
  if (nextVal === void 0 && key !== "RouteTableIds") {
43232
44081
  leftInPlace.push(key);
@@ -43242,7 +44091,8 @@ var FSxFileSystemProvider = class {
43242
44091
  return {
43243
44092
  diff,
43244
44093
  hasMutableDiff,
43245
- leftInPlace
44094
+ leftInPlace,
44095
+ aliases
43246
44096
  };
43247
44097
  }
43248
44098
  unmappedMutableSubprop(ctx, key) {
@@ -43374,6 +44224,104 @@ var FSxFileSystemProvider = class {
43374
44224
  }
43375
44225
  return true;
43376
44226
  }
44227
+ async applyAliasDelta(physicalId, logicalId, resourceType, delta, deadline) {
44228
+ await this.waitForNoAdministrativeAction(physicalId, logicalId, resourceType, deadline);
44229
+ const live = new Map((await this.describeAliases(physicalId)).map((a) => [a.Name?.toLowerCase(), a.Lifecycle]));
44230
+ const retryAdd = delta.add.filter((n) => {
44231
+ const lifecycle = live.get(n);
44232
+ return lifecycle === "CREATE_FAILED" || lifecycle === "DELETING" || lifecycle === "DELETE_FAILED";
44233
+ });
44234
+ const awaitGone = [...delta.remove, ...retryAdd];
44235
+ const sendRemove = awaitGone.filter((n) => live.has(n) && live.get(n) !== "DELETING");
44236
+ const sendAdd = delta.add.filter((n) => live.get(n) !== "AVAILABLE" && live.get(n) !== "CREATING");
44237
+ if (sendRemove.length > 0) await this.sendAliasCall(physicalId, logicalId, resourceType, () => new DisassociateFileSystemAliasesCommand({
44238
+ FileSystemId: physicalId,
44239
+ Aliases: sendRemove
44240
+ }), deadline);
44241
+ if (awaitGone.length > 0) await this.waitForAliases(physicalId, logicalId, resourceType, awaitGone, "gone", deadline);
44242
+ if (sendAdd.length > 0) await this.sendAliasCall(physicalId, logicalId, resourceType, () => new AssociateFileSystemAliasesCommand({
44243
+ FileSystemId: physicalId,
44244
+ Aliases: sendAdd
44245
+ }), deadline);
44246
+ if (delta.add.length > 0) await this.waitForAliases(physicalId, logicalId, resourceType, delta.add, "AVAILABLE", deadline);
44247
+ }
44248
+ async describeAliases(physicalId) {
44249
+ const aliases = [];
44250
+ let nextToken;
44251
+ do {
44252
+ const resp = await this.getClient().send(new DescribeFileSystemAliasesCommand({
44253
+ FileSystemId: physicalId,
44254
+ NextToken: nextToken
44255
+ }));
44256
+ aliases.push(...resp.Aliases ?? []);
44257
+ nextToken = resp.NextToken;
44258
+ } while (nextToken);
44259
+ return aliases;
44260
+ }
44261
+ async waitForNoAdministrativeAction(physicalId, logicalId, resourceType, deadline) {
44262
+ const transientState = { count: 0 };
44263
+ let running = [];
44264
+ while (Date.now() < deadline) {
44265
+ const fs = await this.describeForPoll(physicalId, transientState);
44266
+ if (fs) {
44267
+ running = runningAliasBlockingActions(fs);
44268
+ if (running.length === 0) return;
44269
+ this.logger.debug(safeMsg`FSx FileSystem ${physicalId}: waiting for ${running.join(", ")} to finish before changing DNS aliases...`);
44270
+ }
44271
+ await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
44272
+ }
44273
+ throw new ProvisioningError(safeMsg`Timed out waiting for FSx FileSystem ${physicalId} administrative actions (${running.join(", ") || "unknown"}) to finish before changing its DNS aliases (${Math.round(this.maxWaitMs / 6e4)} min). Re-deploy once they complete.`, resourceType, logicalId, physicalId);
44274
+ }
44275
+ async sendAliasCall(physicalId, logicalId, resourceType, command, deadline) {
44276
+ for (;;) try {
44277
+ const call = command();
44278
+ if (call instanceof AssociateFileSystemAliasesCommand) await this.getClient().send(call);
44279
+ else await this.getClient().send(call);
44280
+ return;
44281
+ } catch (error) {
44282
+ if (!isAdministrativeActionInProgress(error)) throw error;
44283
+ if (Date.now() >= deadline) {
44284
+ let running = [];
44285
+ try {
44286
+ running = ((await this.describeForPoll(physicalId, { count: 0 }))?.AdministrativeActions ?? []).filter((a) => a.Status === "PENDING" || a.Status === "IN_PROGRESS").map((a) => a.AdministrativeActionType ?? "unknown");
44287
+ } catch {}
44288
+ throw new ProvisioningError(safeMsg`Timed out changing FSx FileSystem ${physicalId} DNS aliases: FSx kept refusing the call while an administrative action (${running.join(", ") || "unknown"}) is in progress (${Math.round(this.maxWaitMs / 6e4)} min). Re-deploy once it completes.`, resourceType, logicalId, physicalId, error);
44289
+ }
44290
+ this.logger.debug(safeMsg`FSx FileSystem ${physicalId}: DNS alias call refused while an administrative action is in progress, re-sending...`);
44291
+ await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
44292
+ }
44293
+ }
44294
+ async waitForAliases(physicalId, logicalId, resourceType, names, target, deadline) {
44295
+ const failedLifecycle = target === "AVAILABLE" ? "CREATE_FAILED" : "DELETE_FAILED";
44296
+ const transitionLifecycle = target === "AVAILABLE" ? "CREATING" : "DELETING";
44297
+ const seenTransition = /* @__PURE__ */ new Set();
44298
+ const failedReads = /* @__PURE__ */ new Map();
44299
+ let transient = 0;
44300
+ while (Date.now() < deadline) {
44301
+ let aliases;
44302
+ try {
44303
+ aliases = await this.describeAliases(physicalId);
44304
+ transient = 0;
44305
+ } catch (error) {
44306
+ if (!isTransientFsxError(error) || transient >= 5) throw error;
44307
+ transient += 1;
44308
+ await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
44309
+ continue;
44310
+ }
44311
+ const byName = new Map(aliases.map((a) => [a.Name?.toLowerCase(), a.Lifecycle]));
44312
+ for (const n of names) {
44313
+ const lifecycle = byName.get(n);
44314
+ if (lifecycle === transitionLifecycle) seenTransition.add(n);
44315
+ failedReads.set(n, lifecycle === failedLifecycle ? (failedReads.get(n) ?? 0) + 1 : 0);
44316
+ }
44317
+ const failed = names.filter((n) => byName.get(n) === failedLifecycle && (seenTransition.has(n) || (failedReads.get(n) ?? 0) >= STALE_FAILED_READS));
44318
+ if (failed.length > 0) throw new ProvisioningError(safeMsg`FSx FileSystem ${physicalId}: DNS alias ${failed.join(", ")} entered ${failedLifecycle}. Check the alias name and the file system's Active Directory, then re-deploy.`, resourceType, logicalId, physicalId);
44319
+ if (names.every((n) => target === "AVAILABLE" ? byName.get(n) === "AVAILABLE" : !byName.has(n))) return;
44320
+ this.logger.debug(safeMsg`FSx FileSystem ${physicalId}: waiting for DNS aliases ${names.join(", ")} to be ${target}...`);
44321
+ await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
44322
+ }
44323
+ throw new ProvisioningError(safeMsg`Timed out waiting for FSx FileSystem ${physicalId} DNS aliases ${names.join(", ")} to be ${target} (${Math.round(this.maxWaitMs / 6e4)} min)`, resourceType, logicalId, physicalId);
44324
+ }
43377
44325
  async applyTagDiff(physicalId, resourceType, logicalId, nextTags, prevTags) {
43378
44326
  const plan = planTagDiff(prevTags, nextTags);
43379
44327
  const arn = (await this.getClient().send(new DescribeFileSystemsCommand$1({ FileSystemIds: [physicalId] }))).FileSystems?.[0]?.ResourceARN;
@@ -43385,19 +44333,23 @@ var FSxFileSystemProvider = class {
43385
44333
  Value
43386
44334
  }));
43387
44335
  const toRemove = plan.remove;
43388
- if (toSet.length > 0) await this.getClient().send(new TagResourceCommand$23({
44336
+ if (toSet.length > 0) await this.getClient().send(new TagResourceCommand$24({
43389
44337
  ResourceARN: arn,
43390
44338
  Tags: toSet
43391
44339
  }));
43392
- if (toRemove.length > 0) await this.getClient().send(new UntagResourceCommand$24({
44340
+ if (toRemove.length > 0) await this.getClient().send(new UntagResourceCommand$25({
43393
44341
  ResourceARN: arn,
43394
44342
  TagKeys: toRemove
43395
44343
  }));
43396
44344
  }
43397
- async delete(logicalId, physicalId, resourceType, _properties, context) {
44345
+ async delete(logicalId, physicalId, resourceType, properties, context) {
43398
44346
  this.logger.debug(`Deleting FSx FileSystem ${logicalId}: ${physicalId}`);
43399
44347
  try {
43400
- await this.getClient().send(new DeleteFileSystemCommand$1({ FileSystemId: physicalId }));
44348
+ const openZfsOptions = this.toDeleteOpenZFSOptions(properties);
44349
+ await this.getClient().send(new DeleteFileSystemCommand$1({
44350
+ FileSystemId: physicalId,
44351
+ ...openZfsOptions && { OpenZFSConfiguration: { Options: openZfsOptions } }
44352
+ }));
43401
44353
  } catch (error) {
43402
44354
  if (error instanceof FileSystemNotFound$1) {
43403
44355
  const clientRegion = await this.getClient().config.region();
@@ -43413,6 +44365,14 @@ var FSxFileSystemProvider = class {
43413
44365
  await this.waitForFileSystemDeleted(physicalId, logicalId, resourceType, context?.maskSecrets);
43414
44366
  this.logger.debug(`Successfully deleted FSx FileSystem ${logicalId}`);
43415
44367
  }
44368
+ toDeleteOpenZFSOptions(properties) {
44369
+ if (properties?.["FileSystemType"] !== "OPENZFS") return void 0;
44370
+ const config = properties["OpenZFSConfiguration"];
44371
+ if (typeof config !== "object" || config === null || Array.isArray(config)) return void 0;
44372
+ const options = config["Options"];
44373
+ if (!Array.isArray(options) || options.length === 0) return void 0;
44374
+ return options.map((o) => String(o));
44375
+ }
43416
44376
  async isSameResource(journaledPhysicalId, record, resourceType, context) {
43417
44377
  if (resourceType !== "AWS::FSx::FileSystem") return "unknown";
43418
44378
  if (!FILE_SYSTEM_ID_PATTERN.test(journaledPhysicalId) || !FILE_SYSTEM_ID_PATTERN.test(record.physicalId)) return "unknown";
@@ -43443,9 +44403,8 @@ var FSxFileSystemProvider = class {
43443
44403
  transientState.count = 0;
43444
44404
  return response.FileSystems?.[0];
43445
44405
  } catch (error) {
43446
- const name = error instanceof Error ? error.name : "";
43447
44406
  const msg = describeAwsFailure(error).detail;
43448
- if ((name === "ThrottlingException" || name === "ServiceLimitExceeded" || name === "InternalServerError" || name === "TimeoutError" || /rate exceeded|too many requests|timed? ?out|ECONNRESET|EPIPE|socket hang up/i.test(msg)) && transientState.count < maxConsecutiveTransient) {
44407
+ if (isTransientFsxError(error) && transientState.count < maxConsecutiveTransient) {
43449
44408
  transientState.count += 1;
43450
44409
  this.logger.debug(`Transient DescribeFileSystems error while polling ${fileSystemId} (${transientState.count}/${maxConsecutiveTransient}): ${msg} — retrying`);
43451
44410
  return;
@@ -43466,11 +44425,10 @@ var FSxFileSystemProvider = class {
43466
44425
  }
43467
44426
  throw new ProvisioningError(`Timed out waiting for FSx FileSystem ${fileSystemId} to become AVAILABLE (${Math.round(this.maxWaitMs / 6e4)} min)`, resourceType, logicalId, fileSystemId);
43468
44427
  }
43469
- async waitForUpdateActionComplete(fileSystemId, logicalId, resourceType, actionThresholdMs, requireActionObservation, mask) {
43470
- const startTime = Date.now();
44428
+ async waitForUpdateActionComplete(fileSystemId, logicalId, resourceType, actionThresholdMs, requireActionObservation, deadline, mask) {
43471
44429
  const transientState = { count: 0 };
43472
44430
  let seenTrackedAction = false;
43473
- while (Date.now() - startTime < this.maxWaitMs) {
44431
+ while (Date.now() < deadline) {
43474
44432
  const fs = await this.describeForPoll(fileSystemId, transientState);
43475
44433
  if (fs) {
43476
44434
  const updateActions = (fs.AdministrativeActions ?? []).filter((a) => a.AdministrativeActionType === "FILE_SYSTEM_UPDATE" && (a.RequestTime === void 0 || a.RequestTime.getTime() >= actionThresholdMs));
@@ -43659,13 +44617,26 @@ var FSxFileSystemProvider = class {
43659
44617
  canonicalizeDriftProperties(resourceType, properties) {
43660
44618
  if (resourceType !== "AWS::FSx::FileSystem") return properties;
43661
44619
  const lustre = properties["LustreConfiguration"];
43662
- if (typeof lustre !== "object" || lustre === null || Array.isArray(lustre)) return properties;
44620
+ if (typeof lustre !== "object" || lustre === null || Array.isArray(lustre)) return this.canonicalizeWindowsAliases(properties);
43663
44621
  const log = lustre["LogConfiguration"];
43664
- if (typeof log !== "object" || log === null || Array.isArray(log) || JSON.stringify(log) !== JSON.stringify({ Level: "DISABLED" })) return properties;
44622
+ if (typeof log !== "object" || log === null || Array.isArray(log) || JSON.stringify(log) !== JSON.stringify({ Level: "DISABLED" })) return this.canonicalizeWindowsAliases(properties);
43665
44623
  const { LogConfiguration: _disabled, ...rest } = lustre;
43666
- return {
44624
+ return this.canonicalizeWindowsAliases({
43667
44625
  ...properties,
43668
44626
  LustreConfiguration: rest
44627
+ });
44628
+ }
44629
+ canonicalizeWindowsAliases(properties) {
44630
+ const windows = properties["WindowsConfiguration"];
44631
+ if (typeof windows !== "object" || windows === null || Array.isArray(windows)) return properties;
44632
+ const aliases = windows["Aliases"];
44633
+ if (!Array.isArray(aliases) || aliases.every((a) => typeof a !== "string" || a === a.toLowerCase())) return properties;
44634
+ return {
44635
+ ...properties,
44636
+ WindowsConfiguration: {
44637
+ ...windows,
44638
+ Aliases: aliases.map((a) => typeof a === "string" ? a.toLowerCase() : a)
44639
+ }
43669
44640
  };
43670
44641
  }
43671
44642
  getDriftUnknownPaths(resourceType) {
@@ -43675,7 +44646,8 @@ var FSxFileSystemProvider = class {
43675
44646
  "BackupId",
43676
44647
  "WindowsConfiguration.SelfManagedActiveDirectoryConfiguration.Password",
43677
44648
  "OntapConfiguration.FsxAdminPassword",
43678
- "OpenZFSConfiguration.RootVolumeConfiguration"
44649
+ "OpenZFSConfiguration.RootVolumeConfiguration",
44650
+ "OpenZFSConfiguration.Options"
43679
44651
  ];
43680
44652
  }
43681
44653
  getDriftUnorderedPaths(resourceType) {
@@ -43805,6 +44777,12 @@ const toNumber$2 = (v) => {
43805
44777
  const jsonEqual$2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
43806
44778
  const addInstanceGroupsLatch = new AmbiguousCreateLatch("emr:AddInstanceGroups");
43807
44779
  const groupsCreatedByThisProcess = new RecentIdSet();
44780
+ const MOVING_GROUP_STATES = /* @__PURE__ */ new Set([
44781
+ "PROVISIONING",
44782
+ "BOOTSTRAPPING",
44783
+ "RESIZING",
44784
+ "RECONFIGURING"
44785
+ ]);
43808
44786
  var EMRInstanceGroupConfigProvider = class {
43809
44787
  disableCcApiFallback = true;
43810
44788
  client;
@@ -44005,6 +44983,7 @@ var EMRInstanceGroupConfigProvider = class {
44005
44983
  this.logger.debug(`Updating EMR instance group ${logicalId}: ${physicalId}`);
44006
44984
  try {
44007
44985
  if (instanceCountChanged) {
44986
+ context?.markUnsettled?.("InstanceCount");
44008
44987
  await this.getClient().send(new ModifyInstanceGroupsCommand({
44009
44988
  ...jobFlowId && { ClusterId: jobFlowId },
44010
44989
  InstanceGroups: [{
@@ -44017,6 +44996,7 @@ var EMRInstanceGroupConfigProvider = class {
44017
44996
  if (autoScalingChanged) {
44018
44997
  if (!jobFlowId) throw new ProvisioningError(`EMR InstanceGroupConfig ${logicalId} AutoScalingPolicy update needs JobFlowId (the cluster id), which is absent`, resourceType, logicalId, physicalId);
44019
44998
  const policy = properties["AutoScalingPolicy"];
44999
+ context?.markUnsettled?.("AutoScalingPolicy");
44020
45000
  if (policy) await this.getClient().send(new PutAutoScalingPolicyCommand({
44021
45001
  ClusterId: jobFlowId,
44022
45002
  InstanceGroupId: physicalId,
@@ -44100,6 +45080,27 @@ var EMRInstanceGroupConfigProvider = class {
44100
45080
  }
44101
45081
  throw new ProvisioningError(`Timed out waiting for EMR instance group ${groupId} to reach RUNNING with ${expectedCount} instance(s) (${Math.round(this.maxWaitMs / 6e4)} min)`, resourceType, logicalId, groupId);
44102
45082
  }
45083
+ async readUnsettled(physicalId, logicalId, resourceType, _names, recordedProperties) {
45084
+ const jobFlowId = recordedProperties?.["JobFlowId"];
45085
+ if (typeof jobFlowId !== "string" || jobFlowId === "") throw new ProvisioningError(`EMR InstanceGroupConfig ${logicalId} has no JobFlowId (the cluster id) recorded, so cdkd cannot read the group to converge what an earlier failed update left unsettled`, resourceType, logicalId, physicalId);
45086
+ const group = await this.findGroupForPoll(jobFlowId, physicalId, { count: 0 }, 0);
45087
+ if (group === void 0) return void 0;
45088
+ return {
45089
+ ...group.RunningInstanceCount !== void 0 && { InstanceCount: group.RunningInstanceCount },
45090
+ ...group.AutoScalingPolicy !== void 0 && { AutoScalingPolicy: group.AutoScalingPolicy }
45091
+ };
45092
+ }
45093
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names, recordedProperties) {
45094
+ if (!names.has("InstanceCount")) return /* @__PURE__ */ new Set();
45095
+ const jobFlowId = recordedProperties?.["JobFlowId"];
45096
+ if (typeof jobFlowId !== "string" || jobFlowId === "") return /* @__PURE__ */ new Set();
45097
+ try {
45098
+ const state = (await this.findGroupForPoll(jobFlowId, physicalId, { count: 0 }, 0))?.Status?.State;
45099
+ return state !== void 0 && MOVING_GROUP_STATES.has(state) ? /* @__PURE__ */ new Set(["InstanceCount"]) : /* @__PURE__ */ new Set();
45100
+ } catch {
45101
+ return /* @__PURE__ */ new Set(["InstanceCount"]);
45102
+ }
45103
+ }
44103
45104
  async findGroupForPoll(clusterId, groupId, transientState, maxConsecutiveTransient = 5) {
44104
45105
  try {
44105
45106
  let marker;
@@ -45761,7 +46762,7 @@ var FirehoseProvider = class {
45761
46762
  async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context) {
45762
46763
  this.logger.debug(`Updating Firehose delivery stream ${logicalId}: ${physicalId}`);
45763
46764
  const desiredEncryption = readEncryptionInput(properties[ENCRYPTION_KEY], context?.desiredFromAwsReadback === true);
45764
- const encryptionChanged = !sameEncryptionInput(properties[ENCRYPTION_KEY], desiredEncryption, previousProperties[ENCRYPTION_KEY]);
46765
+ const encryptionChanged = !sameEncryptionInput(properties[ENCRYPTION_KEY], desiredEncryption, previousProperties[ENCRYPTION_KEY]) || context?.unsettledProperties?.has(ENCRYPTION_KEY) === true && desiredEncryption.kind !== "unusable";
45765
46766
  let encryptionSkipped = false;
45766
46767
  if (encryptionChanged && desiredEncryption.kind === "unusable") {
45767
46768
  const message = `${ENCRYPTION_KEY} of Firehose delivery stream ${logicalId} is not an object with a string KeyType (and a string KeyARN, if any)`;
@@ -45830,7 +46831,7 @@ var FirehoseProvider = class {
45830
46831
  const prevDest = previousProperties[activeDest] ?? {};
45831
46832
  if (JSON.stringify(nextDest) !== JSON.stringify(prevDest)) await this.applySnowflakeDestinationUpdate(logicalId, physicalId, nextDest);
45832
46833
  }
45833
- if (encryptionChanged && desiredEncryption.kind !== "unusable") await this.applyEncryptionUpdate(logicalId, physicalId, resourceType, desiredEncryption, maskerOrIdentity(context?.maskSecrets));
46834
+ if (encryptionChanged && desiredEncryption.kind !== "unusable") await this.applyEncryptionUpdate(logicalId, physicalId, resourceType, desiredEncryption, maskerOrIdentity(context?.maskSecrets), context?.markUnsettled);
45834
46835
  const description = await this.getClient().send(new DescribeDeliveryStreamCommand({ DeliveryStreamName: physicalId }));
45835
46836
  this.warnUnappliedS3BackupMode(logicalId, properties, unappliedS3BackupModeDestination(properties, previousProperties, destKey, context?.changedProperties), description.DeliveryStreamDescription);
45836
46837
  const desc = description.DeliveryStreamDescription;
@@ -45911,7 +46912,7 @@ var FirehoseProvider = class {
45911
46912
  }))
45912
46913
  }));
45913
46914
  }
45914
- async applyEncryptionUpdate(logicalId, physicalId, resourceType, desired, mask) {
46915
+ async applyEncryptionUpdate(logicalId, physicalId, resourceType, desired, mask, markUnsettled) {
45915
46916
  const settled = await this.waitForEncryption(logicalId, physicalId, resourceType, mask, {
45916
46917
  what: "settle",
45917
46918
  done: (enc) => enc?.Status !== "ENABLING" && enc?.Status !== "DISABLING"
@@ -45919,8 +46920,9 @@ var FirehoseProvider = class {
45919
46920
  if (desired.kind === "absent") {
45920
46921
  if (settled?.Status !== "ENABLED" && settled?.Status !== "DISABLING_FAILED") return;
45921
46922
  this.refuseToStartTransition(logicalId, physicalId, resourceType, "disable");
46923
+ markUnsettled?.(ENCRYPTION_KEY);
45922
46924
  await this.getClient().send(new StopDeliveryStreamEncryptionCommand({ DeliveryStreamName: physicalId }));
45923
- await this.warnIfUnfinished(logicalId, "StopDeliveryStreamEncryption", () => this.waitForEncryption(logicalId, physicalId, resourceType, mask, {
46925
+ await this.warnIfUnfinished(logicalId, "StopDeliveryStreamEncryption", markUnsettled, () => this.waitForEncryption(logicalId, physicalId, resourceType, mask, {
45924
46926
  what: "disable",
45925
46927
  done: (enc) => enc === void 0 || enc.Status === "DISABLED",
45926
46928
  inProgress: "DISABLING",
@@ -45931,6 +46933,7 @@ var FirehoseProvider = class {
45931
46933
  }
45932
46934
  if (encryptionMatches(settled, desired)) return;
45933
46935
  this.refuseToStartTransition(logicalId, physicalId, resourceType, "enable");
46936
+ markUnsettled?.(ENCRYPTION_KEY);
45934
46937
  await this.getClient().send(new StartDeliveryStreamEncryptionCommand({
45935
46938
  DeliveryStreamName: physicalId,
45936
46939
  DeliveryStreamEncryptionConfigurationInput: {
@@ -45938,7 +46941,7 @@ var FirehoseProvider = class {
45938
46941
  ...desired.keyArn !== void 0 && { KeyARN: desired.keyArn }
45939
46942
  }
45940
46943
  }));
45941
- await this.warnIfUnfinished(logicalId, "StartDeliveryStreamEncryption", () => this.waitForEncryption(logicalId, physicalId, resourceType, mask, {
46944
+ await this.warnIfUnfinished(logicalId, "StartDeliveryStreamEncryption", markUnsettled, () => this.waitForEncryption(logicalId, physicalId, resourceType, mask, {
45942
46945
  what: "enable",
45943
46946
  done: (enc) => encryptionMatches(enc, desired),
45944
46947
  inProgress: "ENABLING",
@@ -45946,12 +46949,12 @@ var FirehoseProvider = class {
45946
46949
  staleFailure: settled?.Status === "ENABLING_FAILED"
45947
46950
  }));
45948
46951
  }
45949
- async warnIfUnfinished(logicalId, call, wait) {
46952
+ async warnIfUnfinished(logicalId, call, markUnsettled, wait) {
45950
46953
  try {
45951
46954
  return await wait();
45952
46955
  } catch (error) {
45953
46956
  const outcome = typeof error === "object" && error !== null && terminalEncryptionFailures.has(error) ? "AWS reported the change failed, so the stream may no longer match" : "the stream may still finish the change, so it may no longer match";
45954
- this.logger.warn(safeMsg`Firehose delivery stream ${logicalId}: ${call} was sent but the encryption change did not finish; ${outcome} the previous setting cdkd keeps recorded. Run 'cdkd drift' to compare, then 'cdkd drift --revert' to apply the recorded setting or 'cdkd drift --accept' to record the stream's.`);
46957
+ this.logger.warn(safeMsg`Firehose delivery stream ${logicalId}: ${call} was sent but the encryption change did not finish; ${outcome} the previous setting cdkd keeps recorded. `.concat(markUnsettled === void 0 ? "Run 'cdkd drift' to compare, then 'cdkd drift --revert' to apply the recorded setting or 'cdkd drift --accept' to record the stream's." : `cdkd records ${ENCRYPTION_KEY} as unsettled, so the next 'cdkd deploy' reads the stream and applies the template's setting. To keep the stream as it ends up instead, run 'cdkd drift --accept' once the stream no longer reads ENABLING or DISABLING (an accept during the transition keeps the key unsettled).`));
45955
46958
  throw error;
45956
46959
  }
45957
46960
  }
@@ -46369,6 +47372,15 @@ var FirehoseProvider = class {
46369
47372
  if (config["CloudWatchLoggingOptions"] !== void 0) result.CloudWatchLoggingOptions = config["CloudWatchLoggingOptions"];
46370
47373
  return result;
46371
47374
  }
47375
+ async unsettledInTransition(physicalId, _logicalId, _resourceType, names) {
47376
+ if (!names.has(ENCRYPTION_KEY)) return /* @__PURE__ */ new Set();
47377
+ try {
47378
+ const status = (await this.getClient().send(new DescribeDeliveryStreamCommand({ DeliveryStreamName: physicalId }))).DeliveryStreamDescription?.DeliveryStreamEncryptionConfiguration?.Status;
47379
+ return status === "ENABLING" || status === "DISABLING" ? /* @__PURE__ */ new Set([ENCRYPTION_KEY]) : /* @__PURE__ */ new Set();
47380
+ } catch {
47381
+ return /* @__PURE__ */ new Set([ENCRYPTION_KEY]);
47382
+ }
47383
+ }
46372
47384
  async readCurrentState(physicalId, _logicalId, _resourceType) {
46373
47385
  let desc;
46374
47386
  try {
@@ -46916,6 +47928,7 @@ var CloudTrailProvider = class {
46916
47928
  //#endregion
46917
47929
  //#region src/provisioning/providers/codebuild-provider.ts
46918
47930
  const DEFAULT_PROJECT_VISIBILITY = "PRIVATE";
47931
+ const DEFAULT_ENCRYPTION_KEY = "alias/aws/s3";
46919
47932
  function readVisibility(properties, mask) {
46920
47933
  const visibility = properties["Visibility"];
46921
47934
  const role = properties["ResourceAccessRole"];
@@ -47194,6 +48207,9 @@ var CodeBuildProvider = class {
47194
48207
  const input = this.mapProperties(() => physicalId, properties);
47195
48208
  input.name = physicalId;
47196
48209
  if (properties["BuildBatchConfig"] == null && previousProperties["BuildBatchConfig"] != null) input.buildBatchConfig = {};
48210
+ const declaredKey = (value) => value != null && value !== "";
48211
+ if (!declaredKey(properties["EncryptionKey"]) && declaredKey(previousProperties["EncryptionKey"])) input.encryptionKey = DEFAULT_ENCRYPTION_KEY;
48212
+ if (properties["Tags"] == null && previousProperties["Tags"] != null) input.tags = [];
47197
48213
  const updated = await this.getClient().send(new UpdateProjectCommand(input));
47198
48214
  const desiredVisibility = readVisibility(properties, mask);
47199
48215
  const recorded = readVisibility(previousProperties, mask);
@@ -47680,11 +48696,11 @@ var CodeCommitRepositoryProvider = class {
47680
48696
  const repoArn = metadata?.Arn;
47681
48697
  if (repoArn) {
47682
48698
  const removedKeys = Object.keys(oldTagMap).filter((k) => !Object.hasOwn(newTagMap, k));
47683
- if (removedKeys.length > 0) await this.getClient().send(new UntagResourceCommand$25({
48699
+ if (removedKeys.length > 0) await this.getClient().send(new UntagResourceCommand$26({
47684
48700
  resourceArn: repoArn,
47685
48701
  tagKeys: removedKeys
47686
48702
  }));
47687
- if (Object.keys(newTagMap).length > 0) await this.getClient().send(new TagResourceCommand$24({
48703
+ if (Object.keys(newTagMap).length > 0) await this.getClient().send(new TagResourceCommand$25({
47688
48704
  resourceArn: repoArn,
47689
48705
  tags: newTagMap
47690
48706
  }));
@@ -48127,7 +49143,7 @@ var DLMLifecyclePolicyProvider = class {
48127
49143
  if (tagsChanged && arn) {
48128
49144
  const removedKeys = plan.remove;
48129
49145
  if (removedKeys.length > 0) {
48130
- await this.getClient().send(new UntagResourceCommand$26({
49146
+ await this.getClient().send(new UntagResourceCommand$27({
48131
49147
  ResourceArn: arn,
48132
49148
  TagKeys: removedKeys
48133
49149
  }));
@@ -48135,7 +49151,7 @@ var DLMLifecyclePolicyProvider = class {
48135
49151
  }
48136
49152
  const tagsToAdd = Object.fromEntries(plan.set);
48137
49153
  if (Object.keys(tagsToAdd).length > 0) {
48138
- await this.getClient().send(new TagResourceCommand$25({
49154
+ await this.getClient().send(new TagResourceCommand$26({
48139
49155
  ResourceArn: arn,
48140
49156
  Tags: tagsToAdd
48141
49157
  }));
@@ -48299,11 +49315,11 @@ var S3VectorsProvider = class {
48299
49315
  }
48300
49316
  if (!resourceArn) throw new ProvisioningError(`Could not resolve ARN for S3 VectorBucket ${logicalId} (${mask(physicalId)}); cannot apply tag update.`, resourceType, logicalId, physicalId);
48301
49317
  try {
48302
- if (Object.keys(toSet).length > 0) await this.getClient().send(new TagResourceCommand$26({
49318
+ if (Object.keys(toSet).length > 0) await this.getClient().send(new TagResourceCommand$27({
48303
49319
  resourceArn,
48304
49320
  tags: toSet
48305
49321
  }));
48306
- if (toRemove.length > 0) await this.getClient().send(new UntagResourceCommand$27({
49322
+ if (toRemove.length > 0) await this.getClient().send(new UntagResourceCommand$28({
48307
49323
  resourceArn,
48308
49324
  tagKeys: toRemove
48309
49325
  }));
@@ -48586,12 +49602,12 @@ var S3DirectoryBucketProvider = class {
48586
49602
  if (removedKeys.length > 0 || tagsToApply.length > 0) {
48587
49603
  const accountId = await this.getAccountId();
48588
49604
  const resourceArn = await this.buildBucketArn(physicalId, accountId);
48589
- if (removedKeys.length > 0) await this.getS3ControlClient().send(new UntagResourceCommand$28({
49605
+ if (removedKeys.length > 0) await this.getS3ControlClient().send(new UntagResourceCommand$29({
48590
49606
  AccountId: accountId,
48591
49607
  ResourceArn: resourceArn,
48592
49608
  TagKeys: removedKeys
48593
49609
  }));
48594
- if (tagsToApply.length > 0) await this.getS3ControlClient().send(new TagResourceCommand$27({
49610
+ if (tagsToApply.length > 0) await this.getS3ControlClient().send(new TagResourceCommand$28({
48595
49611
  AccountId: accountId,
48596
49612
  ResourceArn: resourceArn,
48597
49613
  Tags: tagsToApply
@@ -49439,7 +50455,7 @@ var S3TablesProvider = class {
49439
50455
  const resourceArn = await this.lookupTableArn(tableBucketARN, namespace, name);
49440
50456
  if (!resourceArn) throw new ProvisioningError(safeMsg`applyTableTagsDiff: GetTable returned no tableARN for ${physicalId} — table is gone ` + `or state is out-of-sync. Refusing to silently drop the tag update. If the table was deleted out-of-band, drop just this resource's record with the command below, filling in the construct path the CDK app gave this resource — its aws:cdk:path Metadata entry in the synthesized template.\nClean up with: cdkd orphan ${commandHole("constructPath")}`, resourceType, logicalId, physicalId);
49441
50457
  if (removedKeys.length > 0) try {
49442
- await this.getClient().send(new UntagResourceCommand$29({
50458
+ await this.getClient().send(new UntagResourceCommand$30({
49443
50459
  resourceArn,
49444
50460
  tagKeys: removedKeys
49445
50461
  }));
@@ -49448,7 +50464,7 @@ var S3TablesProvider = class {
49448
50464
  throw this.wrapCaughtError(mask, err, (text) => new ProvisioningError(`applyTableTagsDiff: UntagResource failed for ${resourceArn} (keys: ${removedKeys.join(", ")}): ${sentence(text)} State has NOT been updated so the next deploy will retry the tag-diff.`, resourceType, logicalId, physicalId, cause));
49449
50465
  }
49450
50466
  if (Object.keys(upserts).length > 0) try {
49451
- await this.getClient().send(new TagResourceCommand$28({
50467
+ await this.getClient().send(new TagResourceCommand$29({
49452
50468
  resourceArn,
49453
50469
  tags: upserts
49454
50470
  }));
@@ -49466,7 +50482,7 @@ var S3TablesProvider = class {
49466
50482
  if (removedKeys.length === 0 && Object.keys(upserts).length === 0) return;
49467
50483
  const resourceArn = physicalId;
49468
50484
  if (removedKeys.length > 0) try {
49469
- await this.getClient().send(new UntagResourceCommand$29({
50485
+ await this.getClient().send(new UntagResourceCommand$30({
49470
50486
  resourceArn,
49471
50487
  tagKeys: removedKeys
49472
50488
  }));
@@ -49475,7 +50491,7 @@ var S3TablesProvider = class {
49475
50491
  throw this.wrapCaughtError(mask, err, (text) => new ProvisioningError(`applyTableBucketTagsDiff: UntagResource failed for ${resourceArn} (keys: ${removedKeys.join(", ")}): ${sentence(text)} State has NOT been updated so the next deploy will retry the tag-diff.`, resourceType, logicalId, physicalId, cause));
49476
50492
  }
49477
50493
  if (Object.keys(upserts).length > 0) try {
49478
- await this.getClient().send(new TagResourceCommand$28({
50494
+ await this.getClient().send(new TagResourceCommand$29({
49479
50495
  resourceArn,
49480
50496
  tags: upserts
49481
50497
  }));
@@ -50595,4 +51611,4 @@ var ACMCertificateProvider = class {
50595
51611
 
50596
51612
  //#endregion
50597
51613
  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, LambdaNetworkConnectorProvider, LambdaPermissionProvider, LambdaUrlProvider, LambdaWebFunctionEndpointProvider, LambdaWebFunctionRevisionProvider, 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 };
50598
- //# sourceMappingURL=provider-classes-d9K3vBVh.js.map
51614
+ //# sourceMappingURL=provider-classes-DYke4HKr.js.map