@go-to-k/cdkd 0.283.36 → 0.284.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-BruE_3F2.js → asg-provider-DDEsD6uA.js} +2 -2
- package/dist/{asg-provider-BruE_3F2.js.map → asg-provider-DDEsD6uA.js.map} +1 -1
- package/dist/cli.js +157 -30
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-Bhq_JPCc.js → deploy-engine-CruC7Qxn.js} +113 -16
- package/dist/deploy-engine-CruC7Qxn.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-Bhq_JPCc.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as IntrinsicFunctionResolver, $n as StackTerminationProtectionError, $t as buildDenyExternalAccessPolicy, A as formatResourceLine, An as processStackMessages, At as withRetry, B as isExportAliasCollision, Bt as buildAssetRedirectMap, C as refusesFinalSnapshot, Cn as findLargeInlineResources, Ct as s3BucketDualStackDomainName, D as MULTI_REGION_RECREATE_BLOCKED_TYPES, Dn as canonicalizeRegion, Dt as DiffCalculator, E as extractDeploymentEventError, En as PARTITION_TABLE, Et as applyRoleArnIfSet, F as red, Fn as resetAwsClients, Ft as rebuildClientForBucketRegion, G as clearOnUpdateRemoval, Gt as stripControlChars, H as stateKeySecretExposure, Hn as LocalInvokeBuildError$1, Ht as loadPublishableAssetManifest, I as yellow, In as setAwsClients, It as shouldRetainResource, J as findSilentDropProperties, Jn as PartialFailureError, Jt as ensureAssetStorage, K as ProviderRegistry, Kn as MissingCdkCliError, Kt as AssetModeResolver, L as collectDeclaredOutputNames, Lt as AssetPublisher, M as cyan, Mn as resolveBucketRegion, Mt as TemplateParser, N as gray, Nn as AwsClients, Nt as LockManager, O as isStatefulRecreateTargetSync, On as derivePartitionAndUrlSuffix, Ot as INTRINSIC_KEYS, P as green, Pn as getAwsClients, Pt as S3StateBackend, Q as isTerminationProtectionPropagationError, Qn as StackHasActiveImportsError, Qt as validateContainerRepoName, R as collectPublishedOutputNames, Rn as CdkdError, Rt as stringifyValue, S as isFinalSnapshotError, Sn as MIGRATE_TMP_PREFIX, St as s3BucketDomainName, T as makeCanonicalizePropertiesFn, Tn as expectedOwnerParam, Tt as s3BucketWebsiteUrl, U as IAMRoleProvider, Un as LocalMigrateError, Ut as rewriteTemplateAssetReferences, V as secretBearingStateKeyWarning, Vn as DeployCancelledError, Vt as createAssetRedirectResolver, W as collectInlinePolicyNamesManagedBySiblings, Wn as LocalStartServiceError, Wt as escapeRegExp, X as slowCcOperationTimeoutMs, Xn as ResourceTimeoutError, Xt as parseBootstrapMarker, Y as CloudControlProvider, Yn as ProvisioningError, Yt as getBootstrapMarkerKey, Z as disableInstanceApiTermination, Zn as ResourceUpdateNotSupportedError, Zt as validateAssetBucketName, _ as ATOMIC_FINAL_SNAPSHOT_TYPES, _n as resolveUseCdkBootstrapAssets, _t as TEMPLATE_SOURCED_RULES, a as DeploymentEventsStore, an as AssetManifestLoader, ar as withErrorHandling, at as resolveExplicitPhysicalId, b as ccRoutedFinalSnapshotError, bn as CFN_TEMPLATE_BODY_LIMIT, bt as scrubResourceRecord, c as replayFailedOperations, cn as synthesisStatusMessage, cr as isThrottlingError, ct as configBooleanRefusal, d as updatePartialReason, dn as resolveApp, dt as replayWarn, en as buildDockerImage, et as cfnRefValueFromPhysicalId, f as UNSPECIFIED_SKIP_REASON, fn as resolveAutoAssetStorage, ft as requireConfigArray, g as computeImplicitDeleteEdges, gn as resolveStateBucketWithDefaultAndSource, gt as STATE_SOURCED_READBACK_RULES, h as IMPLICIT_DELETE_DEPENDENCIES, hn as resolveStateBucketWithDefault, ht as STATE_SOURCED_CROSS_GENERATION_RULES, i as DeploymentEventsReader, in as runDockerStreaming, ir as normalizeAwsError, it as normalizeAwsTagsToCfn, j as bold, jt as DagBuilder, k as renderStatefulReason, kn as AssemblyReader, kt as describeTypeWithThrottleRetry, l as replayRollback, ln as getDefaultStateBucketName, lr as markNonRetryable, lt as configStringRefusal, m as withResourceDeadline, mn as resolveSkipPrefix, mt as requireConfigString, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as getDockerCmd, nt as refStateLookupFromResource, o as planFailedOps, on as getDockerImageBySourceHash, or as isMarkedNonRetryable, ot as assertRegionMatch, p as deleteSkipReason, pn as resolveCaptureObservedState, pt as requireConfigObject, q as findActionableSilentDrops, qn as NestedStackChildDirectDestroyError, qt as BOOTSTRAP_MARKER_PREFIX, r as DeployEngine, rn as runDockerForeground, rt as WAFv2WebACLProvider, s as planRollback, sn as Synthesizer, sr as isRetryableTransientError, st as coerceCfnBoolean, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as formatDockerLoginError, tt as getAccountInfo, u as updatePartialMessage, un as getLegacyStateBucketName, ut as readConfigString, v as PRE_DELETE_SNAPSHOT_TYPES, vn as stateBucketExistenceConfirmed, vt as maskSecretsInText, w as unsupportedFinalSnapshotError, wn as uploadCfnTemplate, wt as s3BucketRegionalDomainName, x as createPreDeleteFinalSnapshot, xn as CFN_TEMPLATE_URL_LIMIT, xt as s3BucketArn, y as buildFinalSnapshotIdentifier, yn as warnDeprecatedNoPrefixCliFlag, yt as redactSecretsForState, z as exportAliasCollisionScrubWarning, zt as WorkGraph } from "./deploy-engine-CruC7Qxn.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-DDEsD6uA.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -3098,18 +3098,27 @@ var IAMManagedPolicyProvider = class {
|
|
|
3098
3098
|
const reason = newPolicyName !== oldPolicyName ? "ManagedPolicyName" : newPath !== oldPath ? "Path" : "Description";
|
|
3099
3099
|
this.logger.debug(`${reason} changed, replacing managed policy: ${physicalId} (${reason} mutation)`);
|
|
3100
3100
|
const createResult = await this.create(logicalId, resourceType, properties);
|
|
3101
|
+
let orphanReason;
|
|
3101
3102
|
try {
|
|
3102
3103
|
const deleteResult = await this.delete(logicalId, physicalId, resourceType, previousProperties);
|
|
3103
|
-
if (deleteResult?.outcome === "skipped")
|
|
3104
|
+
if (deleteResult?.outcome === "skipped") {
|
|
3105
|
+
orphanReason = `old managed policy ${physicalId} was not deleted: ${deleteResult.reason}`;
|
|
3106
|
+
this.logger.warn(`Skipped deleting old managed policy ${physicalId} during replacement: ${deleteResult.reason}. The old policy may be orphaned and require manual cleanup.`);
|
|
3107
|
+
}
|
|
3104
3108
|
} catch (error) {
|
|
3109
|
+
orphanReason = `old managed policy ${physicalId} could not be deleted: ${String(error)}`;
|
|
3105
3110
|
this.logger.warn(`Failed to delete old managed policy ${physicalId} during replacement: ${String(error)}. The old policy may be orphaned and require manual cleanup.`);
|
|
3106
3111
|
}
|
|
3107
|
-
const
|
|
3112
|
+
const base = {
|
|
3108
3113
|
physicalId: createResult.physicalId,
|
|
3109
|
-
wasReplaced: true
|
|
3114
|
+
wasReplaced: true,
|
|
3115
|
+
...createResult.attributes ? { attributes: createResult.attributes } : {}
|
|
3110
3116
|
};
|
|
3111
|
-
|
|
3112
|
-
|
|
3117
|
+
return orphanReason !== void 0 ? {
|
|
3118
|
+
...base,
|
|
3119
|
+
outcome: "partial",
|
|
3120
|
+
reason: orphanReason
|
|
3121
|
+
} : base;
|
|
3113
3122
|
}
|
|
3114
3123
|
try {
|
|
3115
3124
|
const newDocument = properties["PolicyDocument"];
|
|
@@ -26244,16 +26253,23 @@ var ApiGatewayProvider = class ApiGatewayProvider {
|
|
|
26244
26253
|
if (newPathPart !== oldPathPart) {
|
|
26245
26254
|
this.logger.debug(`PathPart changed from "${oldPathPart}" to "${newPathPart}", replacing resource`);
|
|
26246
26255
|
const createResult = await this.createResource(logicalId, resourceType, properties);
|
|
26256
|
+
let orphanReason;
|
|
26247
26257
|
try {
|
|
26248
26258
|
await this.deleteResource(logicalId, physicalId, resourceType, previousProperties);
|
|
26249
26259
|
} catch (error) {
|
|
26260
|
+
orphanReason = `old API Gateway Resource ${physicalId} could not be deleted: ${String(error)}`;
|
|
26250
26261
|
this.logger.warn(`Failed to delete old API Gateway Resource ${physicalId} during replacement: ${String(error)}. The old resource may be orphaned and require manual cleanup.`);
|
|
26251
26262
|
}
|
|
26252
|
-
|
|
26263
|
+
const base = {
|
|
26253
26264
|
physicalId: createResult.physicalId,
|
|
26254
26265
|
wasReplaced: true,
|
|
26255
26266
|
attributes: createResult.attributes ?? {}
|
|
26256
26267
|
};
|
|
26268
|
+
return orphanReason !== void 0 ? {
|
|
26269
|
+
...base,
|
|
26270
|
+
outcome: "partial",
|
|
26271
|
+
reason: orphanReason
|
|
26272
|
+
} : base;
|
|
26257
26273
|
}
|
|
26258
26274
|
return {
|
|
26259
26275
|
physicalId,
|
|
@@ -39034,10 +39050,17 @@ function hasMfaConfigProps(properties) {
|
|
|
39034
39050
|
* - `WebAuthnRelyingPartyID`/`WebAuthnUserVerification` -> `WebAuthnConfiguration`
|
|
39035
39051
|
*
|
|
39036
39052
|
* `MfaConfiguration` (ON/OFF/OPTIONAL) MUST be threaded into this request:
|
|
39037
|
-
* `SetUserPoolMfaConfig` is a full-replace of the pool's MFA state, and
|
|
39038
|
-
* omitted `MfaConfiguration`
|
|
39039
|
-
*
|
|
39040
|
-
*
|
|
39053
|
+
* `SetUserPoolMfaConfig` is a full-replace of the pool's MFA state, and AWS
|
|
39054
|
+
* reads an omitted `MfaConfiguration` as OFF. That splits into two MUTUALLY
|
|
39055
|
+
* EXCLUSIVE arms — both MEASURED against THIS API, not read off
|
|
39056
|
+
* `UpdateUserPool`'s blanket reset sentence, which holds only field by field:
|
|
39057
|
+
*
|
|
39058
|
+
* - WITH a per-factor sub-block in the request, AWS REJECTS the call: nothing
|
|
39059
|
+
* is reset, and the deploy FAILS.
|
|
39060
|
+
* - WITHOUT one, AWS accepts the call and the pool is silently reset to
|
|
39061
|
+
* MFA-disabled.
|
|
39062
|
+
*
|
|
39063
|
+
* Use the template's `MfaConfiguration`
|
|
39041
39064
|
* when present; when the template omitted it, default by whether this call
|
|
39042
39065
|
* enables an MFA FACTOR — `OPTIONAL` if it does, `OFF` (CloudFormation's own
|
|
39043
39066
|
* default) if it does not. The body comment on that decision explains why both
|
|
@@ -39075,11 +39098,11 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
|
|
|
39075
39098
|
request.MfaConfiguration = declaredMfaConfiguration || (enablesMfaFactor ? "OPTIONAL" : "OFF");
|
|
39076
39099
|
if (enabledMfasBlank) logger?.warn(`UserPool ${physicalId}: EnabledMfas is an empty string, which declares no MFA factor and is treated as absent — a mangled Fn::Join or an empty String parameter collapses to this shape. Nothing is enabled from it and MfaConfiguration is ${request.MfaConfiguration}; declare the factor names as a list (e.g. ["${MFA_FACTOR_SOFTWARE_TOKEN}"]) if a factor was intended.`);
|
|
39077
39100
|
const anyFactorBlock = request.SmsMfaConfiguration !== void 0 || request.SoftwareTokenMfaConfiguration !== void 0 || request.EmailMfaConfiguration !== void 0;
|
|
39078
|
-
if (request.MfaConfiguration === "OFF" && anyFactorBlock) logger?.warn(`UserPool ${physicalId}: MfaConfiguration is pinned to OFF while an MFA factor block is configured.
|
|
39101
|
+
if (request.MfaConfiguration === "OFF" && anyFactorBlock) logger?.warn(`UserPool ${physicalId}: MfaConfiguration is pinned to OFF while an MFA factor block is configured. AWS REJECTS that combination ("Invalid MFA configuration given, can't turn off MFA and configure an MFA together"), so this deploy FAILS rather than producing a pool with MFA disabled. Remove MfaConfiguration (or set it to ON / OPTIONAL) to enable the declared factor.`);
|
|
39079
39102
|
const dropped = unrecognizedFactors.map((f) => JSON.stringify(f) ?? String(f));
|
|
39080
39103
|
if (enabledMfasMalformed) dropped.push(`${JSON.stringify(properties["EnabledMfas"])} (not a list)`);
|
|
39081
39104
|
if (dropped.length > 0) {
|
|
39082
|
-
const consequence = request.MfaConfiguration === "OFF" ? `MfaConfiguration is OFF, so this pool deploys with MFA DISABLED` : anyFactorBlock ? `MfaConfiguration is ${request.MfaConfiguration} and another factor block IS sent, so these entries are silently ignored rather than failing the call on their own` : `MfaConfiguration is ${request.MfaConfiguration} with no factor enabled, so AWS rejects this call rather than deploying the pool with MFA disabled`;
|
|
39105
|
+
const consequence = request.MfaConfiguration === "OFF" && anyFactorBlock ? "MfaConfiguration is OFF while another factor block IS sent, so AWS REJECTS this call outright and the deploy fails" : request.MfaConfiguration === "OFF" ? `MfaConfiguration is OFF, so this pool deploys with MFA DISABLED` : anyFactorBlock ? `MfaConfiguration is ${request.MfaConfiguration} and another factor block IS sent, so these entries are silently ignored rather than failing the call on their own` : `MfaConfiguration is ${request.MfaConfiguration} with no factor enabled, so AWS rejects this call rather than deploying the pool with MFA disabled`;
|
|
39083
39106
|
logger?.warn(`UserPool ${physicalId}: EnabledMfas entries ${dropped.join(", ")} do not map to an MFA factor block (known: ${MFA_FACTOR_SMS}, ${MFA_FACTOR_SOFTWARE_TOKEN}, ${MFA_FACTOR_EMAIL_OTP}); no factor is enabled from them. ${consequence}.`);
|
|
39084
39107
|
}
|
|
39085
39108
|
return request;
|
|
@@ -39243,9 +39266,52 @@ var CognitoUserPoolProvider = class {
|
|
|
39243
39266
|
/**
|
|
39244
39267
|
* Build the SDK `Policies` input from the CFn `Policies` blob. Both
|
|
39245
39268
|
* sub-keys must be forwarded: `SignInPolicy` (passwordless first-auth
|
|
39246
|
-
* factors) was silently dropped before issue #1380,
|
|
39247
|
-
*
|
|
39248
|
-
*
|
|
39269
|
+
* factors) was silently dropped before issue #1380, so a template that
|
|
39270
|
+
* declared -- or later CHANGED -- the allowed first-auth factors never
|
|
39271
|
+
* reached AWS at all.
|
|
39272
|
+
*
|
|
39273
|
+
* **Forwarding is what APPLIES a declared value; it is NOT what keeps an
|
|
39274
|
+
* existing one alive.** The original rationale here was AWS's blanket "if
|
|
39275
|
+
* you don't provide a value for an attribute, Amazon Cognito sets it to its
|
|
39276
|
+
* default value", i.e. that omitting `SignInPolicy` would WIPE it. MEASURED
|
|
39277
|
+
* us-east-1 2026-08-19 (issue #1968), on a pool created with
|
|
39278
|
+
* `SignInPolicy.AllowedFirstAuthFactors = [PASSWORD, EMAIL_OTP]` on the
|
|
39279
|
+
* default `ESSENTIALS` tier -- no explicit `UserPoolTier` was needed for the
|
|
39280
|
+
* field to be settable:
|
|
39281
|
+
*
|
|
39282
|
+
* - `UpdateUserPool` sending `Policies` with ONLY `PasswordPolicy` left
|
|
39283
|
+
* `SignInPolicy` at `[PASSWORD, EMAIL_OTP]`. Control: that same call's
|
|
39284
|
+
* `--auto-verified-attributes email` applied, so the update really ran.
|
|
39285
|
+
* - `UpdateUserPool` omitting `Policies` entirely left BOTH sub-keys intact
|
|
39286
|
+
* (a non-default `RequireSymbols: false` survived too), while
|
|
39287
|
+
* `AutoVerifiedAttributes` reset from `[email]` to none in that very call
|
|
39288
|
+
* -- the control proving the omission was processed, not ignored.
|
|
39289
|
+
* - An explicit `SignInPolicy` write landed in both directions
|
|
39290
|
+
* (`[PASSWORD]`, then back to `[PASSWORD, EMAIL_OTP]`), so the field is
|
|
39291
|
+
* reachable and writable here rather than silently dropped.
|
|
39292
|
+
* - The MIRROR image holds too, so the ledger entry is measured rather than
|
|
39293
|
+
* generalised from one sub-key: a container carrying ONLY `SignInPolicy`
|
|
39294
|
+
* updated it to `[PASSWORD, WEB_AUTHN]` while leaving a non-default
|
|
39295
|
+
* `PasswordPolicy` (`MinimumLength: 12`, `RequireSymbols: false`) intact,
|
|
39296
|
+
* with the same `--auto-verified-attributes` control.
|
|
39297
|
+
*
|
|
39298
|
+
* So the forwarding stays -- it is the only way a CHANGED sign-in policy is
|
|
39299
|
+
* applied -- but on the measured reason, not the blanket sentence. Do not
|
|
39300
|
+
* generalise that sentence to another field from this site: see
|
|
39301
|
+
* `readLiveMfaConfiguration`'s docstring, which carries the one ledger of
|
|
39302
|
+
* which fields were measured to reset and which were not.
|
|
39303
|
+
*
|
|
39304
|
+
* **Consequence of that measurement, NOT fixed here: there is no removal
|
|
39305
|
+
* path.** This builder forwards only what the template DECLARES, so deleting
|
|
39306
|
+
* `SignInPolicy` from a template sends nothing for it -- and nothing sent
|
|
39307
|
+
* means nothing changed. A user tightening their auth by removing a
|
|
39308
|
+
* passwordless first-auth factor therefore leaves it live on the pool
|
|
39309
|
+
* indefinitely, and `readCurrentState` keeps reporting a drift that
|
|
39310
|
+
* `cdkd drift --revert` cannot clear, since the revert routes through this
|
|
39311
|
+
* same builder. Clearing it needs an explicit reset value on the wire, which
|
|
39312
|
+
* is a behavior change with its own integ and is out of this lane's scope.
|
|
39313
|
+
* Whether CloudFormation removes it on the same template edit is UNMEASURED
|
|
39314
|
+
* -- do not assume parity in either direction.
|
|
39249
39315
|
*/
|
|
39250
39316
|
toSdkUserPoolPolicies(policies) {
|
|
39251
39317
|
const result = {};
|
|
@@ -39402,10 +39468,37 @@ var CognitoUserPoolProvider = class {
|
|
|
39402
39468
|
* `--auto-verified-attributes` change in the same request applied) and that
|
|
39403
39469
|
* the field is writable here rather than ignored (an explicit
|
|
39404
39470
|
* `--mfa-configuration OFF` did reset it). So AWS's blanket "unspecified
|
|
39405
|
-
* parameters are set to their default value" holds field by field —
|
|
39406
|
-
* `AutoVerifiedAttributes` DOES reset, `MfaConfiguration` does not — and
|
|
39471
|
+
* parameters are set to their default value" holds field by field — and
|
|
39407
39472
|
* there is no MFA-OFF window to design around.
|
|
39408
39473
|
*
|
|
39474
|
+
* **This paragraph is the single ledger of that field-by-field result; a
|
|
39475
|
+
* per-field verdict belongs at that field's own site, pointing here.** Every
|
|
39476
|
+
* line below was taken with a control proving the omitting call really ran.
|
|
39477
|
+
*
|
|
39478
|
+
* `UpdateUserPool`, on omission:
|
|
39479
|
+
*
|
|
39480
|
+
* - `AutoVerifiedAttributes` — DOES reset when omitted (2026-08-18).
|
|
39481
|
+
* - `MfaConfiguration` — does NOT reset when omitted (2026-08-18, above).
|
|
39482
|
+
* - `Policies` — neither sub-key resets, measured in BOTH directions
|
|
39483
|
+
* (2026-08-19, issue #1968; details at `toSdkUserPoolPolicies`): a
|
|
39484
|
+
* container sent without `SignInPolicy` left `SignInPolicy` intact, a
|
|
39485
|
+
* container sent without `PasswordPolicy` left `PasswordPolicy` intact,
|
|
39486
|
+
* and omitting the container outright left both. Forwarding is still
|
|
39487
|
+
* REQUIRED to APPLY a changed sub-key — preservation is not application —
|
|
39488
|
+
* and no omission can express a REMOVAL.
|
|
39489
|
+
*
|
|
39490
|
+
* `SetUserPoolMfaConfig` — a DIFFERENT API with a DIFFERENT rule, kept in
|
|
39491
|
+
* its own section so neither list is read as evidence for the other:
|
|
39492
|
+
*
|
|
39493
|
+
* - `MfaConfiguration` — an omitted value is read as OFF. With a factor
|
|
39494
|
+
* sub-block in the request AWS REJECTS the call; with none it accepts and
|
|
39495
|
+
* resets the pool to MFA-disabled. An EXPLICIT OFF beside a block is
|
|
39496
|
+
* rejected the same way (2026-08-19, issue #1968; the transcript lives at
|
|
39497
|
+
* the pinned-OFF warning in `buildMfaConfigRequest`).
|
|
39498
|
+
*
|
|
39499
|
+
* Nothing here licenses a claim about a field on neither list, or about one
|
|
39500
|
+
* API from the other's section. Measure it.
|
|
39501
|
+
*
|
|
39409
39502
|
* The earlier ordering was reached from that doc sentence alone and is kept
|
|
39410
39503
|
* only because it is strictly safer and already tested: reading first cannot
|
|
39411
39504
|
* report a value this same call clobbered, whatever AWS does later. Do not
|
|
@@ -56621,6 +56714,26 @@ var WaitConditionHandleProvider = class {
|
|
|
56621
56714
|
//#endregion
|
|
56622
56715
|
//#region src/provisioning/providers/acm-certificate-provider.ts
|
|
56623
56716
|
/**
|
|
56717
|
+
* True for ACM's refusal to delete a certificate a consumer still references
|
|
56718
|
+
* (issue [#1922](https://github.com/go-to-k/cdkd/issues/1922)).
|
|
56719
|
+
*
|
|
56720
|
+
* Matched by error NAME first, with a message fallback: the SDK raises
|
|
56721
|
+
* `ResourceInUseException`, but a wrapped / re-thrown error can lose the class
|
|
56722
|
+
* while keeping the text. Deliberately narrow — this only picks the
|
|
56723
|
+
* remediation wording and the reason phrasing, and every other failure still
|
|
56724
|
+
* reaches the same `'partial'` outcome, so a miss degrades to a less specific
|
|
56725
|
+
* message rather than to the pre-#1922 silence.
|
|
56726
|
+
*/
|
|
56727
|
+
function isCertificateInUseError(error) {
|
|
56728
|
+
let current = error;
|
|
56729
|
+
for (let depth = 0; depth < 5 && current instanceof Error; depth++) {
|
|
56730
|
+
if (current.name === "ResourceInUseException") return true;
|
|
56731
|
+
current = current.cause;
|
|
56732
|
+
}
|
|
56733
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56734
|
+
return /ResourceInUseException|in use/i.test(message);
|
|
56735
|
+
}
|
|
56736
|
+
/**
|
|
56624
56737
|
* AWS ACM Certificate Provider
|
|
56625
56738
|
*
|
|
56626
56739
|
* Implements `AWS::CertificateManager::Certificate` using the ACM SDK.
|
|
@@ -56718,18 +56831,28 @@ var ACMCertificateProvider = class {
|
|
|
56718
56831
|
if (changedImmutable) {
|
|
56719
56832
|
this.logger.debug(`${changedImmutable} changed, replacing ACM certificate: ${physicalId}`);
|
|
56720
56833
|
const createResult = await this.create(logicalId, resourceType, properties);
|
|
56834
|
+
let orphanReason;
|
|
56721
56835
|
try {
|
|
56722
56836
|
const deleteResult = await this.delete(logicalId, physicalId, resourceType, previousProperties);
|
|
56723
|
-
if (deleteResult?.outcome === "skipped")
|
|
56837
|
+
if (deleteResult?.outcome === "skipped") {
|
|
56838
|
+
orphanReason = `old certificate ${physicalId} was not deleted: ${deleteResult.reason}`;
|
|
56839
|
+
this.logger.warn(`Skipped deleting old ACM certificate ${physicalId} during replacement: ${deleteResult.reason}. The old certificate may be orphaned and require manual cleanup.`);
|
|
56840
|
+
}
|
|
56724
56841
|
} catch (error) {
|
|
56725
|
-
|
|
56842
|
+
const inUse = isCertificateInUseError(error);
|
|
56843
|
+
orphanReason = inUse ? `old certificate ${physicalId} is still in use by another resource and was not deleted` : `old certificate ${physicalId} could not be deleted: ${String(error)}`;
|
|
56844
|
+
this.logger.warn(`Failed to delete old ACM certificate ${physicalId} during replacement: ${String(error)}. ` + (inUse ? "This usually means a consumer (e.g. a CloudFront distribution) still references it; it can be deleted once DescribeCertificate.InUseBy is empty. " : "") + `The old certificate may be orphaned and require manual cleanup.`);
|
|
56726
56845
|
}
|
|
56727
|
-
const
|
|
56846
|
+
const base = {
|
|
56728
56847
|
physicalId: createResult.physicalId,
|
|
56729
|
-
wasReplaced: true
|
|
56848
|
+
wasReplaced: true,
|
|
56849
|
+
...createResult.attributes ? { attributes: createResult.attributes } : {}
|
|
56730
56850
|
};
|
|
56731
|
-
|
|
56732
|
-
|
|
56851
|
+
return orphanReason !== void 0 ? {
|
|
56852
|
+
...base,
|
|
56853
|
+
outcome: "partial",
|
|
56854
|
+
reason: orphanReason
|
|
56855
|
+
} : base;
|
|
56733
56856
|
}
|
|
56734
56857
|
try {
|
|
56735
56858
|
const newCt = properties["CertificateTransparencyLoggingPreference"];
|
|
@@ -57590,9 +57713,11 @@ async function deployCommand(stacks, options) {
|
|
|
57590
57713
|
logger.info(`\n${bold("Deployment Summary:")}`);
|
|
57591
57714
|
logger.info(` Stack: ${bold(cyan(deployResult.stackName))}`);
|
|
57592
57715
|
logger.info(` Created: ${deployResult.created > 0 ? green(deployResult.created) : gray(deployResult.created)}`);
|
|
57593
|
-
|
|
57716
|
+
const updatedTotal = deployResult.updated + deployResult.updatePartial;
|
|
57717
|
+
logger.info(` Updated: ${updatedTotal > 0 ? yellow(updatedTotal) : gray(updatedTotal)}`);
|
|
57594
57718
|
logger.info(` Deleted: ${deployResult.deleted > 0 ? red(deployResult.deleted) : gray(deployResult.deleted)}`);
|
|
57595
57719
|
if (deployResult.deleteSkipped > 0) logger.info(` Skipped (not deleted): ${yellow(deployResult.deleteSkipped)}`);
|
|
57720
|
+
if (deployResult.updatePartial > 0) logger.info(` of which left an orphaned predecessor: ${yellow(deployResult.updatePartial)}`);
|
|
57596
57721
|
logger.info(` Unchanged: ${gray(deployResult.unchanged)}`);
|
|
57597
57722
|
logger.info(` Duration: ${cyan((deployResult.durationMs / 1e3).toFixed(2) + "s")}`);
|
|
57598
57723
|
if (deployResult.attributeFallbackCount > 0) logger.warn(`${deployResult.attributeFallbackCount} attribute resolution(s) fell back to the physical ID (potentially wrong values); re-run with --strict-getatt to fail on these`);
|
|
@@ -57604,9 +57729,9 @@ async function deployCommand(stacks, options) {
|
|
|
57604
57729
|
else logger.info(`\n${green("✓")} ${bold("Deployment completed successfully")}`);
|
|
57605
57730
|
recordRunSucceeded(eventRecorder, stackInfo.stackName, {
|
|
57606
57731
|
created: deployResult.created,
|
|
57607
|
-
updated: deployResult.updated,
|
|
57732
|
+
updated: deployResult.updated + deployResult.updatePartial,
|
|
57608
57733
|
deleted: deployResult.deleted,
|
|
57609
|
-
...deployResult.deleteSkipped > 0 && { skipped: deployResult.deleteSkipped }
|
|
57734
|
+
...deployResult.deleteSkipped + deployResult.updatePartial > 0 && { skipped: deployResult.deleteSkipped + deployResult.updatePartial }
|
|
57610
57735
|
}, deployResult.durationMs);
|
|
57611
57736
|
} catch (deployError) {
|
|
57612
57737
|
if (deployError instanceof DeployCancelledError) return;
|
|
@@ -62214,7 +62339,9 @@ async function runRevert(reports, providerRegistry, stateBackend, stateConfig, a
|
|
|
62214
62339
|
try {
|
|
62215
62340
|
const updateResult = await withRetry(() => provider.update(outcome.logicalId, stateResource.physicalId, outcome.resourceType, newProperties, outcome.awsProperties, { desiredFromAwsReadback: true }), outcome.logicalId, { logger: { debug: (msg) => logger.debug(maskSecretsInText(msg, secrets)) } });
|
|
62216
62341
|
totalSucceeded++;
|
|
62217
|
-
|
|
62342
|
+
const revertPartial = updatePartialReason(updateResult);
|
|
62343
|
+
if (revertPartial !== void 0) logger.warn(` ✓ ${report.stackName}/${outcome.logicalId} (${outcome.resourceType}): reverted, ${maskSecretsInText(updatePartialMessage(revertPartial), secrets)}`);
|
|
62344
|
+
else logger.info(` ✓ ${report.stackName}/${outcome.logicalId} (${outcome.resourceType}): reverted.`);
|
|
62218
62345
|
try {
|
|
62219
62346
|
if (updateResult?.effectiveProperties) {
|
|
62220
62347
|
const delta = collectNarrowedTopLevelKeys(newProperties, updateResult.effectiveProperties);
|
|
@@ -81246,7 +81373,7 @@ function createMigrateCommand() {
|
|
|
81246
81373
|
*/
|
|
81247
81374
|
function buildProgram() {
|
|
81248
81375
|
const program = new Command();
|
|
81249
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
81376
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.1");
|
|
81250
81377
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
81251
81378
|
const { profile } = actionCommand.optsWithGlobals();
|
|
81252
81379
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|