@go-to-k/cdkd 0.291.3 → 0.291.4
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-CLqajoRp.js → asg-provider-_vdcb9QG.js} +4 -4
- package/dist/asg-provider-_vdcb9QG.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-5fdODHji.js → deploy-engine-CpMT9Bmm.js} +1980 -132
- package/dist/deploy-engine-CpMT9Bmm.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-lTNPrurx.js → program-C5r-kmIQ.js} +456 -82
- package/dist/{program-lTNPrurx.js.map → program-C5r-kmIQ.js.map} +1 -1
- package/dist/{version-BKm47b9j.js → version-ChuDKy_E.js} +2 -2
- package/dist/{version-BKm47b9j.js.map → version-ChuDKy_E.js.map} +1 -1
- package/package.json +2 -2
- package/dist/asg-provider-CLqajoRp.js.map +0 -1
- package/dist/deploy-engine-5fdODHji.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as yellow, $i as
|
|
1
|
+
import { $ as yellow, $a as describeAwsFailure, $i as expectedOwnerParam, $n as repairMalformedResourcePropertiesForReadOnly, $r as rewriteTemplateAssetReferences, $t as requireConfigObject, A as createPreDeleteFinalSnapshot, Aa as LocalStartServiceError, Ai as getDefaultStateBucketName, An as malformedOutputsRefusalMessage, Ar as buildForceUnlockCommand, At as carriesDynamicReference, B as isStatefulRecreateTargetSync, Bi as warnDeprecatedNoPrefixCliFlag, Bn as refuseMalformedOrphansForDestroy, Br as hasUnverifiableParameterRefusal, Bt as isSettledInstanceState, C as namesAnyDeclaredParameter, Ci as runDockerStreaming, Cn as isReadableResourceEntry, Cr as recoverMaskedOutput, Ct as slowCcOperationTimeoutMs, D as PRE_DELETE_SNAPSHOT_TYPES, Da as DynamicReferenceRegionAmbiguousError, Di as getDockerImageBySourceHash, Dn as malformedOrphanRecordsWarning, Dr as S3StateBackend, Dt as disableInstanceApiTermination, E as ATOMIC_FINAL_SNAPSHOT_TYPES, Ea as DeployCancelledError, Ei as AssetManifestLoader, En as malformedLocalOutputsWarning, Er as LockManager, Et as deleteSkipReason, F as extractDeploymentEventError, Fa as ProvisioningError, Fi as resolveSkipPrefix, Fn as malformedResourcesWarning, Fr as CUSTOM_RESOURCE_RESPONSE_PREFIX, Ft as isImpossibleEmptyStoredAttribute, G as toCfnInteger, Ga as withErrorHandling, Gi as findLargeInlineResources, Gn as refuseMalformedResourceEntries, Gr as orphansCarriedFrom, Gt as coerceCfnBoolean, H as WARM_THROUGHPUT_MEMBERS, Hi as CFN_TEMPLATE_BODY_LIMIT, Hn as refuseMalformedOutputs, Hr as importableOutputs, Ht as normalizeAwsTagsToCfn, I as makeSiblingClaimReader, Ia as ResourceTimeoutError, Ii as resolveStateBucketWithDefault, In as malformedStateRefusalMessage, Ir as DEFAULT_STATE_PREFIX, It as isNamelessDynamicReferenceError, J as bold, Ja as isThrottlingError, Ji as injectiveKey, Jn as refuseMalformedResourcesForDestroy, Jr as stringifyValue, Jt as configIntegerRefusal, K as toFiniteNumber, Ka as isMarkedNonRetryable, Ki as uploadCfnTemplate, Kn as refuseMalformedResourceEntriesForOrphan, Kr as shouldRetainResource, Kt as coerceCfnInteger, L as planOrphanAdoption, La as ResourceUpdateNotSupportedError, Li as resolveStateBucketWithDefaultAndSource, Ln as refuseDivergentRecordRegionForDestroy, Lr as exportNamesCarriedFrom, Lt as isUnboundTemplateParameter, M as refusesFinalSnapshot, Ma as MalformedProducerRecordRefusalError, Mi as resolveApp, Mn as malformedRenderedContainersWarning, Mr as forceQuitRecoveryClause, Mt as cfnRefValueFromPhysicalId, N as unsupportedFinalSnapshotError, Na as NestedStackChildDirectDestroyError, Ni as resolveAutoAssetStorage, Nn as malformedResourceEntriesWarning, Nr as formatLockExpiry, Nt as coerceParameterTypedValue, O as buildFinalSnapshotIdentifier, Oa as IntrinsicResolutionRefusalError, Oi as Synthesizer, On as malformedOrphansRefusalMessage, Or as rebuildClientForBucketRegion, Ot as isTerminationProtectionPropagationError, P as makeCanonicalizePropertiesFn, Pa as PartialFailureError, Pi as resolveCaptureObservedState, Pn as malformedResourcePropertiesWarning, Pr as shellQuote, Pt as getAccountInfo, Q as red, Qa as retryClassificationText, Qi as decodeListingKey, Qn as repairMalformedResourceEntriesForReadOnly, Qr as loadPublishableAssetManifest, Qt as requireConfigArray, R as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ra as StackHasActiveImportsError, Ri as resolveUseCdkBootstrapAssets, Rn as refuseMalformedNestedChildOutputs, Rr as hasReadableExportSet, Rt as parameterTypeMayLoseSecretIdentity, S as computeParameterTaint, Sa as CdkdError, Si as runDockerForeground, Sn as hasReadableResources, Sr as recordMaskOnlyValue, St as isWaitAbandonedError, T as resourcesNamingDeclaredParameter, Ti as stripControlChars, Tn as malformedExportSourceWarning, Tr as scrubResourceRecord, Tt as deleteIndeterminateGuards, U as coerceWarmThroughput$1, Ui as CFN_TEMPLATE_URL_LIMIT, Un as refuseMalformedOutputsForDestroy, Ur as isReadableBag, Ut as resolveExplicitPhysicalId, V as renderStatefulReason, Va as SynthesisError, Vi as ssmResolvedValueType, Vn as refuseMalformedOrphansForOrphan, Vr as importableOutputKeys, Vt as WAFv2WebACLProvider, W as isWarmThroughputDecrease, Wa as normalizeAwsError, Wi as MIGRATE_TMP_PREFIX, Wn as refuseMalformedResourceAttributesForOrphan, Wr as orphansAfterRollback, Wt as assertRegionMatch, X as gray, Xa as markNonRetryable, Xi as producerCoordinateKey, Xn as repairMalformedOrphansForReadOnly, Xr as buildAssetRedirectMap, Xt as readConfigString, Y as cyan, Ya as isTransientServerError, Yi as injectiveKeyPrefix, Yn as refuseMalformedState, Yr as WorkGraph, Yt as configStringRefusal, Z as green, Za as markRedactedCause, Zi as producerRecordKey, Zn as repairMalformedOutputsForReadOnly, Zr as createAssetRedirectResolver, Zt as replayWarn, _ as bagHoldsSecretExpression, _a as AwsClients, _i as dockerSpawnEnvWithSensitive, _n as UNREADABLE_ORPHANS_CONTAINER_ROW, _r as identityKeyFor, _t as endCommandInterruptScope, a as DeploymentEventsStore, aa as globMatches, ai as isCrossRegionRedirect, an as s3BucketDomainName, ar as describeTypeWithThrottleRetry, at as secretBearingStateKeyWarning, b as mergeNoChangeOutputs, ba as setAwsClients, bi as partitionSensitiveEnv, bn as hasReadableOrphans, br as maskSecretsInError, bt as startInterruptWatch, c as replayFailedOperations, ca as hasOwnKey, ci as validateAssetBucketName, cn as s3BucketWebsiteUrl, cr as TemplateParser, ct as IAMRoleProvider, d as updatePartialMessage, da as ownValue, di as buildDockerImage, dn as applyCallerIdentityCredentials, dr as STATE_SOURCED_READBACK_RULES, dt as ProviderRegistry, ea as PARTITION_TABLE, ei as AssetModeResolver, en as requireConfigString, eo as safeStringify, er as repairMalformedResourcesForReadOnly, et as collectDeclaredOutputNames, f as updatePartialReason, fa as absoluteAssemblyPathEscape, fi as resolveDockerContextDirectory, fn as callerEnvCredentials, fr as TEMPLATE_SOURCED_RULES, ft as wouldReturnToSdkProvider, g as computeImplicitDeleteEdges, ga as resolveBucketRegion, gi as describeDockerFailure, gn as STATE_RESOURCES_MALFORMED, gr as errorCauseChain, gt as beginCommandInterruptScope, h as IMPLICIT_DELETE_DEPENDENCIES, hi as describeDockerExecFailure, hn as INTRINSIC_KEYS, hr as dynamicReferenceTokens, ht as maskerOrIdentity, i as DeploymentEventsReader, ia as failedStageNote, ii as getBootstrapMarkerKey, in as s3BucketArn, ir as findSilentDropProperties, it as secretBearing, j as isFinalSnapshotError, ji as getLegacyStateBucketName, jn as malformedOutputsWarning, jr as buildLockContentionMessage, jt as carriesFetchableDynamicReference, k as ccRoutedFinalSnapshotError, ka as LocalInvokeBuildError$1, ki as synthesisStatusMessage, kn as malformedOrphansWarning, kr as UNREPRODUCIBLE_LOCK_CLAUSE, kt as IntrinsicFunctionResolver, l as replayRollback, la as hasPlainPrototype, li as validateContainerRepoName, ln as applyRoleArnIfSet, lr as STATE_SOURCED_BASELINE_RULES, lt as collectInlinePolicyNamesManagedBySiblings, m as maskingRetryLogger, ma as resolveAssemblyPath, mi as describeDockerCapturedOutput, mn as DiffCalculator, mr as createSecretMasker, mt as maskDeep, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as derivePartitionAndUrlSuffix, ni as assertAssetBucketRegion, nn as producerRegionsFromState, nr as safeStackName, nt as exportAliasCollisionScrubWarning, o as planFailedOps, oa as processStackMessages, oi as parseBootstrapMarker, on as s3BucketDualStackDomainName, or as withRetry, ot as secretSafeKeyDisplay, p as withResourceDeadline, pa as renderAssemblyPathEscape, pi as warnManifestExecutable, pn as envCredentialsAreAssumedRole, pr as carriesSecretMask, pt as createMaskedRetryLogger, q as formatResourceLine, qa as isRetryableTransientError, qi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, qn as refuseMalformedResourcePropertiesForOrphan, qr as AssetPublisher, qt as configBooleanRefusal, r as DeployEngine, ra as AssemblyReader, ri as ensureAssetStorage, rn as withSharedDrainBudget, rr as findActionableSilentDrops, rt as isExportAliasCollision, s as planRollback, sa as defineOwnKey, si as readBootstrapMarkerBody, sn as s3BucketRegionalDomainName, sr as DagBuilder, st as getCurrentResourceSecrets, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as canonicalizeRegion, ti as BOOTSTRAP_MARKER_PREFIX, tn as classifyReplaySecretRegion, tr as safeRegion, tt as collectPublishedOutputNames, u as resolveReplacementOldType, ua as nullPrototypeRecord, ui as buildDenyExternalAccessPolicy, un as AWS_CREDENTIAL_ENV_KEYS, ur as STATE_SOURCED_CROSS_GENERATION_RULES, ut as clearOnUpdateRemoval, v as isSecretBearingReferenceString, va as getAwsClients, vi as formatDockerLoginError, vn as UNREADABLE_RESOURCES_MAP_ROW, vr as isSingleDynamicReferenceToken, vt as interruptWatchListenerCount, w as reachableDivergentParameters, wa as CrossAccountSecretRefusalError, wi as escapeRegExp, wn as malformedExportNamesWarning, wr as redactSecretsForState, wt as UNSPECIFIED_SKIP_REASON, x as bindingSkippedOutputs, xi as redactDockerArgvValues, xn as hasReadableOutputs, xr as maskSecretsInText, xt as CloudControlProvider, y as keptWholeReasonText, ya as resetAwsClients, yi as getDockerCmd, yn as displayLogicalId, yr as markSameGenerationBag, yt as isInterruptedWaitError, z as isStatefulRecreateTargetForReplace, za as StackTerminationProtectionError, zi as stateBucketExistenceConfirmed, zn as refuseMalformedOrphans, zr as hasReasonlessBaselineRefusal, zt as refStateLookupFromResource } from "./deploy-engine-CpMT9Bmm.js";
|
|
2
2
|
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-GASr9bAF.js";
|
|
3
3
|
import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.js";
|
|
4
|
-
import { t as getCdkdVersion } from "./version-
|
|
5
|
-
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as getCdkdVersion } from "./version-ChuDKy_E.js";
|
|
5
|
+
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-_vdcb9QG.js";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
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, NotFound, 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";
|
|
@@ -290,7 +290,7 @@ const STATE_KEY_MAX_CODE_POINTS = STACK_REF_MAX_CODE_POINTS + 64;
|
|
|
290
290
|
* Parse context key=value pairs from CLI arguments into a Record
|
|
291
291
|
*/
|
|
292
292
|
function parseContextOptions(contextArgs) {
|
|
293
|
-
const context =
|
|
293
|
+
const context = nullPrototypeRecord();
|
|
294
294
|
if (contextArgs) for (const arg of contextArgs) {
|
|
295
295
|
const eqIndex = arg.indexOf("=");
|
|
296
296
|
if (eqIndex > 0) context[arg.substring(0, eqIndex)] = arg.substring(eqIndex + 1);
|
|
@@ -2063,7 +2063,8 @@ function createBootstrapCommand() {
|
|
|
2063
2063
|
* appear in a CloudFormation stack name, so this is unambiguous)
|
|
2064
2064
|
* - Pattern contains no `/` → matched only against `stackName`
|
|
2065
2065
|
*
|
|
2066
|
-
* Wildcards (`*`) are supported in either case
|
|
2066
|
+
* Wildcards (`*`) are supported in either case; every other character is
|
|
2067
|
+
* literal. Results are de-duplicated by
|
|
2067
2068
|
* `stackName`, so a pattern that incidentally matches the same stack via both
|
|
2068
2069
|
* fields is returned only once.
|
|
2069
2070
|
*/
|
|
@@ -2133,10 +2134,14 @@ function describeStack(stack) {
|
|
|
2133
2134
|
function renderNoStackMatch(patterns, available, assembly) {
|
|
2134
2135
|
return (patterns.length > 0 ? `No stacks matching ${patterns.join(", ")} found in assembly. ` + (available.length > 0 ? `Available: ${available.map(describeStack).join(", ")}` : "The assembly has no stacks") : "No stacks found in assembly") + failedStageNote(patterns, assembly.failedStages);
|
|
2135
2136
|
}
|
|
2137
|
+
/**
|
|
2138
|
+
* `*` matches any run of characters and every other character is literal —
|
|
2139
|
+
* `globMatches` owns that rule for this matcher and for the failed-Stage
|
|
2140
|
+
* attribution alike ([#3508](https://github.com/go-to-k/cdkd/issues/3508)).
|
|
2141
|
+
*/
|
|
2136
2142
|
function stackMatchesPattern(stack, pattern) {
|
|
2137
2143
|
const target = pattern.includes("/") ? stack.displayName ?? stack.stackName : stack.stackName;
|
|
2138
|
-
|
|
2139
|
-
return target === pattern;
|
|
2144
|
+
return globMatches(pattern, target);
|
|
2140
2145
|
}
|
|
2141
2146
|
|
|
2142
2147
|
//#endregion
|
|
@@ -22354,17 +22359,21 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
22354
22359
|
} catch (error) {
|
|
22355
22360
|
throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
|
|
22356
22361
|
}
|
|
22362
|
+
let wiringProperties = properties;
|
|
22357
22363
|
if (indexesOmitted) {
|
|
22358
22364
|
effectiveProperties = { ...effectiveProperties ?? properties };
|
|
22359
22365
|
delete effectiveProperties["GlobalSecondaryIndexes"];
|
|
22360
|
-
const
|
|
22361
|
-
if (
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
|
|
22365
|
-
|
|
22366
|
-
|
|
22367
|
-
|
|
22366
|
+
const recorded = withdrawReplicaIndexBlocks(effectiveProperties["Replicas"]);
|
|
22367
|
+
if (recorded.changed) effectiveProperties["Replicas"] = recorded.replicas;
|
|
22368
|
+
const wired = withdrawReplicaIndexBlocks(properties["Replicas"]);
|
|
22369
|
+
if (wired.changed) {
|
|
22370
|
+
wiringProperties = {
|
|
22371
|
+
...properties,
|
|
22372
|
+
Replicas: wired.replicas
|
|
22373
|
+
};
|
|
22374
|
+
const crossRegion = wired.withdrawnRegions.filter((region) => region !== currentRegion);
|
|
22375
|
+
if (crossRegion.length > 0) warn(`DynamoDB GlobalTable ${logicalId}: omitting the GlobalSecondaryIndexes overrides of replica(s) ${crossRegion.join(", ")}: they name indexes this create does not build, so there is nothing for them to apply to`);
|
|
22376
|
+
}
|
|
22368
22377
|
const keptKeyAttributes = collectDesiredKeyAttributeNames(properties, { indexListKeys: ["LocalSecondaryIndexes"] });
|
|
22369
22378
|
if (keptKeyAttributes.size > 0 && Array.isArray(attributeDefinitions)) {
|
|
22370
22379
|
const prunedAttributeDefinitions = attributeDefinitions.filter((definition) => {
|
|
@@ -22397,7 +22406,8 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
22397
22406
|
if (billingMode !== "PROVISIONED" && Object.keys(createOnDemand).length > 0) createParams.OnDemandThroughput = createOnDemand;
|
|
22398
22407
|
const localReplicaTags = replicas.find((r) => r["Region"] === currentRegion)?.["Tags"];
|
|
22399
22408
|
if (localReplicaTags && localReplicaTags.length > 0) createParams.Tags = localReplicaTags;
|
|
22400
|
-
|
|
22409
|
+
const wiringReplicas = wiringProperties["Replicas"] ?? [];
|
|
22410
|
+
for (const replica of wiringReplicas) {
|
|
22401
22411
|
const replicaRegion = replica["Region"];
|
|
22402
22412
|
if (!replicaRegion || replicaRegion === currentRegion) continue;
|
|
22403
22413
|
toSdkReplicaGlobalSecondaryIndexes(replica["GlobalSecondaryIndexes"], billingMode, diagnostics, maskSecrets);
|
|
@@ -22413,7 +22423,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
22413
22423
|
}
|
|
22414
22424
|
try {
|
|
22415
22425
|
const tableInfo = await this.waitForTableActive(tableName, logicalId);
|
|
22416
|
-
for (const replica of
|
|
22426
|
+
for (const replica of wiringReplicas) {
|
|
22417
22427
|
const region = replica["Region"];
|
|
22418
22428
|
if (!region || region === currentRegion) continue;
|
|
22419
22429
|
await this.addReplica(tableName, replica, region, logicalId, billingMode, void 0, maskSecrets);
|
|
@@ -22438,7 +22448,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
22438
22448
|
}));
|
|
22439
22449
|
}
|
|
22440
22450
|
if (billingMode === "PROVISIONED") try {
|
|
22441
|
-
await this.reconcileAutoScalingTargets(tableName, [], collectAutoScalingTargets(
|
|
22451
|
+
await this.reconcileAutoScalingTargets(tableName, [], collectAutoScalingTargets(wiringProperties, currentRegion), currentRegion, void 0, maskSecrets);
|
|
22442
22452
|
} catch (autoScalingErr) {
|
|
22443
22453
|
warn(`Auto-scaling registration failed for ${tableName}: ${describeAwsFailure(autoScalingErr).detail}. The table was created successfully; re-run the deploy to register the scaling targets.`);
|
|
22444
22454
|
}
|
|
@@ -22560,13 +22570,55 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
22560
22570
|
if (tableOverrides.OnDemandThroughputOverride) create.OnDemandThroughputOverride = tableOverrides.OnDemandThroughputOverride;
|
|
22561
22571
|
if (replica["TableClassOverride"]) create.TableClassOverride = replica["TableClassOverride"];
|
|
22562
22572
|
const replicaUpdates = [{ Create: create }];
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22573
|
+
for (let attempt = 1;; attempt++) try {
|
|
22574
|
+
await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
22575
|
+
TableName: tableName,
|
|
22576
|
+
ReplicaUpdates: replicaUpdates
|
|
22577
|
+
}));
|
|
22578
|
+
break;
|
|
22579
|
+
} catch (error) {
|
|
22580
|
+
if (attempt >= 3 || !isReplicaCopyExistsRefusal(error)) throw error;
|
|
22581
|
+
if (!await this.waitForRegionalCopyReleased(tableName, region, maskSecrets)) throw error;
|
|
22582
|
+
await new Promise((resolve) => setTimeout(resolve, REPLICA_COPY_RELEASE_POLL_MS));
|
|
22583
|
+
}
|
|
22567
22584
|
await this.waitForReplicaActive(tableName, region, logicalId);
|
|
22568
22585
|
}
|
|
22569
22586
|
/**
|
|
22587
|
+
* Poll the replica REGION for its same-named table until it is gone,
|
|
22588
|
+
* answering `true` once it is (so the replica-add may be retried) and
|
|
22589
|
+
* `false` for anything that is not a copy on its way out: a table in any
|
|
22590
|
+
* status but `DELETING`, a probe error, or the cap running out. Only ever
|
|
22591
|
+
* READS the region — the caller re-throws AWS's own refusal on `false`.
|
|
22592
|
+
* Interruptible, like every other bounded wait on this provider's paths.
|
|
22593
|
+
*/
|
|
22594
|
+
async waitForRegionalCopyReleased(tableName, region, maskSecrets) {
|
|
22595
|
+
const client = this.getRegionalClient(region);
|
|
22596
|
+
const watch = startInterruptWatch(maskSecrets(`DynamoDB ${region} copy of ${tableName} to finish deleting`));
|
|
22597
|
+
let announced = false;
|
|
22598
|
+
try {
|
|
22599
|
+
for (let poll = 0; poll < 180; poll++) {
|
|
22600
|
+
if (watch.isInterrupted()) throw watch.onInterrupted();
|
|
22601
|
+
let status;
|
|
22602
|
+
try {
|
|
22603
|
+
status = (await client.send(new DescribeTableCommand({ TableName: tableName }))).Table?.TableStatus;
|
|
22604
|
+
} catch (probeError) {
|
|
22605
|
+
if (probeError instanceof ResourceNotFoundException$1) return true;
|
|
22606
|
+
this.logger.debug(maskSecrets(`Could not read the ${region} copy of ${tableName}: ${describeAwsFailure(probeError).detail}`));
|
|
22607
|
+
return false;
|
|
22608
|
+
}
|
|
22609
|
+
if (status !== "DELETING") return false;
|
|
22610
|
+
if (!announced) {
|
|
22611
|
+
this.logger.info(maskSecrets(`The ${region} copy of ${tableName} is still being deleted; waiting for it before adding the replica`));
|
|
22612
|
+
announced = true;
|
|
22613
|
+
}
|
|
22614
|
+
await new Promise((resolve) => setTimeout(resolve, REPLICA_COPY_RELEASE_POLL_MS));
|
|
22615
|
+
}
|
|
22616
|
+
return false;
|
|
22617
|
+
} finally {
|
|
22618
|
+
watch.dispose();
|
|
22619
|
+
}
|
|
22620
|
+
}
|
|
22621
|
+
/**
|
|
22570
22622
|
* Update a DynamoDB Global Table in place.
|
|
22571
22623
|
*
|
|
22572
22624
|
* AWS-side state-machine constraint: `UpdateTable` does not accept
|
|
@@ -23178,7 +23230,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
23178
23230
|
const oldEnabled = oldSettings !== void 0 && oldSettings !== null;
|
|
23179
23231
|
const newEnabled = newSettings !== void 0 && newSettings !== null;
|
|
23180
23232
|
if (!oldEnabled && !newEnabled) return;
|
|
23181
|
-
const watch = startInterruptWatch(`DynamoDB auto-scaling for ${tableName}`);
|
|
23233
|
+
const watch = startInterruptWatch(maskSecrets(`DynamoDB auto-scaling for ${tableName}`));
|
|
23182
23234
|
try {
|
|
23183
23235
|
if (newEnabled) {
|
|
23184
23236
|
const minCapacity = coerceCfnInteger(newSettings["MinCapacity"] ?? 0);
|
|
@@ -24531,6 +24583,65 @@ function collectAutoScalingTargets(properties, localRegion) {
|
|
|
24531
24583
|
return specs;
|
|
24532
24584
|
}
|
|
24533
24585
|
/**
|
|
24586
|
+
* How long `addReplica` waits out a same-named regional copy that is still
|
|
24587
|
+
* DELETING (issue #3569): polls x interval, ~15 minutes. A regional copy has
|
|
24588
|
+
* been measured to linger for minutes after it left the source table's
|
|
24589
|
+
* `Replicas[]`.
|
|
24590
|
+
*/
|
|
24591
|
+
const REPLICA_COPY_RELEASE_POLLS = 180;
|
|
24592
|
+
const REPLICA_COPY_RELEASE_POLL_MS = 5e3;
|
|
24593
|
+
/** Replica-add attempts in total, the first included. */
|
|
24594
|
+
const REPLICA_COPY_RELEASE_ATTEMPTS = 3;
|
|
24595
|
+
/**
|
|
24596
|
+
* AWS's refusal of a `ReplicaUpdates[].Create` whose region already holds a
|
|
24597
|
+
* table of that name. Keyed on the MESSAGE: the error class is a generic
|
|
24598
|
+
* `ValidationException`, shared with every other malformed replica request.
|
|
24599
|
+
*/
|
|
24600
|
+
function isReplicaCopyExistsRefusal(error) {
|
|
24601
|
+
return error instanceof Error && /one or more replicas already existed as tables/i.test(error.message);
|
|
24602
|
+
}
|
|
24603
|
+
/**
|
|
24604
|
+
* `Replicas` with every entry's `GlobalSecondaryIndexes` key REMOVED — the
|
|
24605
|
+
* replay-CREATE GSI omit arm's prune of the per-replica index blocks (issue
|
|
24606
|
+
* #1741, second instance), which name indexes that create does not build.
|
|
24607
|
+
*
|
|
24608
|
+
* Copy-on-write: an entry that carries the key is copied without it, every
|
|
24609
|
+
* other entry (a non-object included — an unresolved intrinsic must reach AWS
|
|
24610
|
+
* as-is, not be rewritten) is passed through by reference, and the caller's
|
|
24611
|
+
* array is never mutated. The caller's bag IS `previousState.properties` on
|
|
24612
|
+
* this path. A non-array container is returned untouched with `changed: false`.
|
|
24613
|
+
*
|
|
24614
|
+
* `withdrawnRegions` lists the `Region` of each entry whose block was a
|
|
24615
|
+
* non-empty ARRAY, i.e. one `toSdkReplicaGlobalSecondaryIndexes` would have
|
|
24616
|
+
* turned into a real override on the wire; a malformed or empty block was
|
|
24617
|
+
* never sendable, so withdrawing it is not a loss worth announcing.
|
|
24618
|
+
*/
|
|
24619
|
+
function withdrawReplicaIndexBlocks(replicas) {
|
|
24620
|
+
if (!Array.isArray(replicas)) return {
|
|
24621
|
+
replicas,
|
|
24622
|
+
changed: false,
|
|
24623
|
+
withdrawnRegions: []
|
|
24624
|
+
};
|
|
24625
|
+
let changed = false;
|
|
24626
|
+
const withdrawnRegions = [];
|
|
24627
|
+
const out = replicas.map((entry) => {
|
|
24628
|
+
const replica = asRecord$1(entry);
|
|
24629
|
+
if (!replica || !Object.prototype.hasOwnProperty.call(replica, "GlobalSecondaryIndexes")) return entry;
|
|
24630
|
+
changed = true;
|
|
24631
|
+
const block = replica["GlobalSecondaryIndexes"];
|
|
24632
|
+
const region = replica["Region"];
|
|
24633
|
+
if (Array.isArray(block) && block.length > 0 && typeof region === "string") withdrawnRegions.push(region);
|
|
24634
|
+
const copy = { ...replica };
|
|
24635
|
+
delete copy["GlobalSecondaryIndexes"];
|
|
24636
|
+
return copy;
|
|
24637
|
+
});
|
|
24638
|
+
return {
|
|
24639
|
+
replicas: changed ? out : replicas,
|
|
24640
|
+
changed,
|
|
24641
|
+
withdrawnRegions
|
|
24642
|
+
};
|
|
24643
|
+
}
|
|
24644
|
+
/**
|
|
24534
24645
|
* Every attribute name some KEY SCHEMA in the desired template references:
|
|
24535
24646
|
* the table's own `KeySchema`, each GSI's, and each LSI's. This is the set
|
|
24536
24647
|
* the `AttributeDefinitions`-removal guard in `update()` tests membership
|
|
@@ -46201,11 +46312,29 @@ function isEmptyObjectPlaceholder(value) {
|
|
|
46201
46312
|
* - `EMAIL_OTP` -> `EmailMfaConfiguration` (carries the email-OTP
|
|
46202
46313
|
* message/subject template — i.e. CFn's
|
|
46203
46314
|
* `EmailAuthenticationMessage` / `Subject`)
|
|
46315
|
+
*
|
|
46316
|
+
* **The email-OTP model, stated once (issue #1924): the `EmailMfaConfiguration`
|
|
46317
|
+
* block's PRESENCE is the factor.** `EmailMfaConfigType` is exactly
|
|
46318
|
+
* `{Message?, Subject?}` -- unlike `SoftwareTokenMfaConfigType` it has no
|
|
46319
|
+
* `Enabled` member, so the API has no way to carry a message template without
|
|
46320
|
+
* enabling email OTP, nor to report one. That is read off the API's TYPE, not
|
|
46321
|
+
* measured: a live probe needs a DEVELOPER email sender (a COGNITO_DEFAULT pool
|
|
46322
|
+
* refuses the block outright), which the maintainer's account does not have.
|
|
46323
|
+
* Both sides honor that: `buildMfaConfigRequest` sends the block for `EMAIL_OTP` OR a bare
|
|
46324
|
+
* message/subject (announcing the second, since the template did not list the
|
|
46325
|
+
* factor), and `readCurrentState` reads a present block back as `EMAIL_OTP`.
|
|
46204
46326
|
*/
|
|
46205
46327
|
const MFA_FACTOR_SMS = "SMS_MFA";
|
|
46206
46328
|
const MFA_FACTOR_SOFTWARE_TOKEN = "SOFTWARE_TOKEN_MFA";
|
|
46207
46329
|
const MFA_FACTOR_EMAIL_OTP = "EMAIL_OTP";
|
|
46208
46330
|
/**
|
|
46331
|
+
* The one `WebAuthnConfiguration.FactorConfiguration` value AWS accepts beside
|
|
46332
|
+
* `MfaConfiguration: ON` when `WEB_AUTHN` is an allowed first auth factor
|
|
46333
|
+
* (issue #2064). Typed against the SDK enum so a rename upstream is a compile
|
|
46334
|
+
* error rather than a silently never-matching comparison.
|
|
46335
|
+
*/
|
|
46336
|
+
const WEB_AUTHN_MULTI_FACTOR = "MULTI_FACTOR_WITH_USER_VERIFICATION";
|
|
46337
|
+
/**
|
|
46209
46338
|
* CFn path every `MfaConfiguration` shape refusal in this provider reports.
|
|
46210
46339
|
* One constant so the three sites that READ the property -- `create`,
|
|
46211
46340
|
* `update`, and `narrowMfaConfiguration` (the diff / effectiveProperties
|
|
@@ -46295,7 +46424,7 @@ function readEnabledMfas(properties) {
|
|
|
46295
46424
|
* mirrors how CloudFormation/CDK sequence the two calls.
|
|
46296
46425
|
*/
|
|
46297
46426
|
function hasMfaConfigProps(properties) {
|
|
46298
|
-
return readEnabledMfas(properties).declaresFactor || !!properties["EmailAuthenticationMessage"] || !!properties["EmailAuthenticationSubject"] || !!properties["WebAuthnRelyingPartyID"] || !!properties["WebAuthnUserVerification"];
|
|
46427
|
+
return readEnabledMfas(properties).declaresFactor || !!properties["EmailAuthenticationMessage"] || !!properties["EmailAuthenticationSubject"] || !!properties["WebAuthnRelyingPartyID"] || !!properties["WebAuthnUserVerification"] || !!properties["WebAuthnFactorConfiguration"];
|
|
46299
46428
|
}
|
|
46300
46429
|
/**
|
|
46301
46430
|
* Build the `SetUserPoolMfaConfig` request from the CFn-level MFA properties,
|
|
@@ -46361,6 +46490,7 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
|
|
|
46361
46490
|
const emailSubject = properties["EmailAuthenticationSubject"] || void 0;
|
|
46362
46491
|
const webAuthnRpId = properties["WebAuthnRelyingPartyID"] || void 0;
|
|
46363
46492
|
const webAuthnUserVerification = properties["WebAuthnUserVerification"] || void 0;
|
|
46493
|
+
const webAuthnFactorConfiguration = properties["WebAuthnFactorConfiguration"] || void 0;
|
|
46364
46494
|
if (!hasMfaConfigProps(properties)) return void 0;
|
|
46365
46495
|
const request = { UserPoolId: physicalId };
|
|
46366
46496
|
const factors = new Set(enabledMfas ?? []);
|
|
@@ -46371,13 +46501,15 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
|
|
|
46371
46501
|
...emailMessage !== void 0 ? { Message: emailMessage } : {},
|
|
46372
46502
|
...emailSubject !== void 0 ? { Subject: emailSubject } : {}
|
|
46373
46503
|
};
|
|
46374
|
-
if (webAuthnRpId !== void 0 || webAuthnUserVerification !== void 0) request.WebAuthnConfiguration = {
|
|
46504
|
+
if (webAuthnRpId !== void 0 || webAuthnUserVerification !== void 0 || webAuthnFactorConfiguration !== void 0) request.WebAuthnConfiguration = {
|
|
46375
46505
|
...webAuthnRpId !== void 0 ? { RelyingPartyId: webAuthnRpId } : {},
|
|
46376
|
-
...webAuthnUserVerification !== void 0 ? { UserVerification: webAuthnUserVerification } : {}
|
|
46506
|
+
...webAuthnUserVerification !== void 0 ? { UserVerification: webAuthnUserVerification } : {},
|
|
46507
|
+
...webAuthnFactorConfiguration !== void 0 ? { FactorConfiguration: webAuthnFactorConfiguration } : {}
|
|
46377
46508
|
};
|
|
46378
46509
|
const enablesMfaFactor = declaresFactor || request.EmailMfaConfiguration !== void 0;
|
|
46379
46510
|
request.MfaConfiguration = declaredMfaConfiguration || (enablesMfaFactor ? "OPTIONAL" : "OFF");
|
|
46380
46511
|
if (enabledMfasBlank) 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.`);
|
|
46512
|
+
if (request.EmailMfaConfiguration !== void 0 && !factors.has(MFA_FACTOR_EMAIL_OTP) && !enabledMfasMalformed) warn(`UserPool ${physicalId}: EmailAuthenticationMessage / EmailAuthenticationSubject is sent in SetUserPoolMfaConfig's EmailMfaConfiguration block, which has no enable switch of its own -- sending it ENABLES the ${MFA_FACTOR_EMAIL_OTP} MFA factor although EnabledMfas does not list it, and a read-back reports EnabledMfas with ${MFA_FACTOR_EMAIL_OTP}. Add ${MFA_FACTOR_EMAIL_OTP} to EnabledMfas to declare it, or remove the message and subject if email OTP is not wanted.`);
|
|
46381
46513
|
const anyFactorBlock = hasAnyMfaFactorBlock(request);
|
|
46382
46514
|
const maskLeaves = (v) => maskDeep(v, maskSecrets);
|
|
46383
46515
|
const dropped = unrecognizedFactors.map((f) => JSON.stringify(maskLeaves(f)) ?? String(f));
|
|
@@ -46551,7 +46683,7 @@ const POLICIES_SUB_KEY_ANNOUNCEMENT = {
|
|
|
46551
46683
|
* The PRE-FLIGHT: the reason this user-pool configuration cannot be applied at
|
|
46552
46684
|
* all, or `undefined` when nothing is known to refuse it.
|
|
46553
46685
|
*
|
|
46554
|
-
*
|
|
46686
|
+
* Three rules, all for combinations AWS rejects 100% of the time, and all
|
|
46555
46687
|
* raised BEFORE the first AWS call on BOTH the create and the update path. The
|
|
46556
46688
|
* ordering is what makes them worth refusing rather than warning about: on the
|
|
46557
46689
|
* UPDATE path `UpdateUserPool` lands FIRST and `SetUserPoolMfaConfig` is
|
|
@@ -46606,20 +46738,33 @@ const POLICIES_SUB_KEY_ANNOUNCEMENT = {
|
|
|
46606
46738
|
*
|
|
46607
46739
|
* -- with NO WebAuthn block at all, with `UserVerification: preferred`, and with
|
|
46608
46740
|
* `required`. The only accepted shape is
|
|
46609
|
-
* `WebAuthnConfiguration.FactorConfiguration = MULTI_FACTOR_WITH_USER_VERIFICATION
|
|
46610
|
-
*
|
|
46611
|
-
*
|
|
46612
|
-
*
|
|
46613
|
-
*
|
|
46614
|
-
*
|
|
46615
|
-
*
|
|
46616
|
-
*
|
|
46617
|
-
*
|
|
46618
|
-
*
|
|
46619
|
-
*
|
|
46620
|
-
*
|
|
46621
|
-
*
|
|
46622
|
-
*
|
|
46741
|
+
* `WebAuthnConfiguration.FactorConfiguration = MULTI_FACTOR_WITH_USER_VERIFICATION`
|
|
46742
|
+
* (the CFn `WebAuthnFactorConfiguration`), so switching to `WEB_AUTHN` is a
|
|
46743
|
+
* two-property edit with its own rule -- rule 3 -- and the remedy names the
|
|
46744
|
+
* one-property fix instead.
|
|
46745
|
+
*
|
|
46746
|
+
* Rule 3 (issue #2064) -- the request resolves `MfaConfiguration` to ON while
|
|
46747
|
+
* the allowed first auth factors include `WEB_AUTHN` and the request does not
|
|
46748
|
+
* carry `FactorConfiguration: MULTI_FACTOR_WITH_USER_VERIFICATION`. It cannot
|
|
46749
|
+
* be a deny-list entry: the SAME factor is legal or illegal by that one field.
|
|
46750
|
+
* MEASURED us-east-1 2026-09-23, on a pool allowing `[PASSWORD, WEB_AUTHN]`:
|
|
46751
|
+
*
|
|
46752
|
+
* {ON, SoftwareToken, FactorConfiguration: MULTI} -> ACCEPTED
|
|
46753
|
+
* {ON, SoftwareToken, FactorConfiguration: MULTI, UV: preferred} -> ACCEPTED
|
|
46754
|
+
* {OFF, FactorConfiguration: MULTI} -> ACCEPTED
|
|
46755
|
+
* {ON, SoftwareToken, FactorConfiguration: SINGLE_FACTOR} -> rejected
|
|
46756
|
+
* {ON, SoftwareToken, no WebAuthn block}, live value MULTI -> rejected
|
|
46757
|
+
*
|
|
46758
|
+
* all rejections reading "Cannot set WebAuthn factor configuration to
|
|
46759
|
+
* SINGLE_FACTOR if MFA is required and WebAuthn is an allowed first auth
|
|
46760
|
+
* factor". The last line is what licenses reading the REQUEST rather than the
|
|
46761
|
+
* pool: `SetUserPoolMfaConfig` is a full replace here too, so an omitted block
|
|
46762
|
+
* is SINGLE_FACTOR whatever the pool held. It is the #1975 partial apply
|
|
46763
|
+
* exactly -- measured from both an OFF and an OPTIONAL pool, `UpdateUserPool`
|
|
46764
|
+
* adding `WEB_AUTHN` LANDED and the `SetUserPoolMfaConfig(ON)` after it was
|
|
46765
|
+
* rejected. (From a pool already at ON, `UpdateUserPool` itself refuses to add
|
|
46766
|
+
* `WEB_AUTHN` -- "WEB_AUTHN cannot be used as an auth factor when MFA is
|
|
46767
|
+
* enabled if not configured for MFA" -- atomically.)
|
|
46623
46768
|
*
|
|
46624
46769
|
* SCOPE LIMIT, deliberate and load-bearing: rule 2 fires only when a
|
|
46625
46770
|
* `SetUserPoolMfaConfig` request is actually BUILT (`hasMfaConfigProps`), i.e.
|
|
@@ -46656,13 +46801,27 @@ const POLICIES_SUB_KEY_ANNOUNCEMENT = {
|
|
|
46656
46801
|
* NO logger, so the pre-flight is silent and `applyMfaConfig`'s own warnings
|
|
46657
46802
|
* still fire exactly once.
|
|
46658
46803
|
*
|
|
46659
|
-
*
|
|
46660
|
-
*
|
|
46661
|
-
*
|
|
46662
|
-
*
|
|
46663
|
-
*
|
|
46664
|
-
*
|
|
46665
|
-
*
|
|
46804
|
+
* WHICH FACTOR LIST rules 2 and 3 read (issue #2051): the one the pool will
|
|
46805
|
+
* hold when `SetUserPoolMfaConfig` runs. When the template SENDS
|
|
46806
|
+
* `Policies.SignInPolicy.AllowedFirstAuthFactors` as a list that is the
|
|
46807
|
+
* template's list; when it does not, it is the LIVE list, because an omitted
|
|
46808
|
+
* sub-key -- or a sent `SignInPolicy` without the member -- is PRESERVED by
|
|
46809
|
+
* `UpdateUserPool` (the ledger at `readLiveMfaConfiguration`;
|
|
46810
|
+
* `sendsFirstAuthFactorList`). `update()` supplies that live
|
|
46811
|
+
* list as `liveFirstAuthFactors`, read only when a rule could fire on it; on
|
|
46812
|
+
* create, and when the read fails, it is absent and only the template is
|
|
46813
|
+
* judged -- AWS's own default `[PASSWORD]` cannot trip either rule. MEASURED
|
|
46814
|
+
* us-east-1 2026-09-23: a pool at `[PASSWORD, EMAIL_OTP]` + OPTIONAL took an
|
|
46815
|
+
* `UpdateUserPool` omitting `Policies` (its `AutoVerifiedAttributes` canary
|
|
46816
|
+
* landed, the factor list survived), then `SetUserPoolMfaConfig(ON)` was
|
|
46817
|
+
* rejected -- the partial apply of DELETING `SignInPolicy` while setting ON.
|
|
46818
|
+
*
|
|
46819
|
+
* The OTHER #2051 path -- omitting `MfaConfiguration` while adding `EMAIL_OTP`
|
|
46820
|
+
* to a pool whose live MFA is ON -- is NOT a partial apply and is not guarded:
|
|
46821
|
+
* MEASURED the same day, `UpdateUserPool` itself rejects the added factor
|
|
46822
|
+
* ("Only PASSWORD and WEB_AUTHN (if configured) can be enabled as an auth
|
|
46823
|
+
* factor if MFA is enabled") and its co-sent `AutoVerifiedAttributes` did NOT
|
|
46824
|
+
* land, so AWS answers it atomically before any second call.
|
|
46666
46825
|
*
|
|
46667
46826
|
* Deliberately UNCONDITIONAL -- no `CreateContext.replayingState` downgrade.
|
|
46668
46827
|
* `.claude/rules/providers.md` says a create-path pre-flight refusal MUST
|
|
@@ -46670,16 +46829,23 @@ const POLICIES_SUB_KEY_ANNOUNCEMENT = {
|
|
|
46670
46829
|
* oversight, and the rule's own reasoning is what licenses it: the downgrade
|
|
46671
46830
|
* exists because a refusal against a STATE record leaves a resource
|
|
46672
46831
|
* un-rollbackable with no template-side remedy. That presupposes the replay
|
|
46673
|
-
* could otherwise SUCCEED. Here it cannot --
|
|
46832
|
+
* could otherwise SUCCEED. Here it cannot -- every combination is rejected by
|
|
46674
46833
|
* AWS 100% of the time (measured above, and CloudFormation rolls back on the
|
|
46675
46834
|
* same templates), so downgrading would trade a clear cdkd-worded refusal for
|
|
46676
46835
|
* the identical failure arriving later from AWS, on the create path with a
|
|
46677
|
-
* pool to roll back and on the update path with a partial apply.
|
|
46678
|
-
*
|
|
46679
|
-
*
|
|
46680
|
-
*
|
|
46681
|
-
|
|
46682
|
-
|
|
46836
|
+
* pool to roll back and on the update path with a partial apply. Revisit this
|
|
46837
|
+
* if AWS ever starts accepting one of them.
|
|
46838
|
+
*
|
|
46839
|
+
* That argument is about the REPLAY failing either way, not about state being
|
|
46840
|
+
* unable to hold the combination -- the live-list arm can meet a perfectly
|
|
46841
|
+
* legitimate record. Example: a record at `ON` with no `SignInPolicy`, then a
|
|
46842
|
+
* failed deploy that lowered MFA and added `EMAIL_OTP`; the rollback replays
|
|
46843
|
+
* the `ON` record against a live `EMAIL_OTP` and is refused. It would have been
|
|
46844
|
+
* rejected by AWS all the same, and in both cases the pool keeps the MFA value
|
|
46845
|
+
* the failed deploy left -- a rollback cannot restore `ON` until the live
|
|
46846
|
+
* factor list allows it, which the refusal text names.
|
|
46847
|
+
*/
|
|
46848
|
+
function describeUnsupportedMfaCombination(properties, declaredMfaConfiguration, liveFirstAuthFactors) {
|
|
46683
46849
|
const request = buildMfaConfigRequest("", properties, void 0, declaredMfaConfiguration);
|
|
46684
46850
|
if (request && request.MfaConfiguration === "OFF" && hasAnyMfaFactorBlock(request)) {
|
|
46685
46851
|
const blocks = [
|
|
@@ -46689,11 +46855,74 @@ function describeUnsupportedMfaCombination(properties, declaredMfaConfiguration)
|
|
|
46689
46855
|
];
|
|
46690
46856
|
return `${MFA_CONFIGURATION_PATH} is OFF while an MFA factor is configured (SetUserPoolMfaConfig would carry ${blocks.join(", ")}, built from EnabledMfas / EmailAuthenticationMessage / EmailAuthenticationSubject). AWS rejects that combination ("Invalid MFA configuration given, can't turn off MFA and configure an MFA together"), so cdkd refuses it before sending anything rather than leaving a partly-applied update behind. Set MfaConfiguration to ON or OPTIONAL (or remove it) to enable the declared factor. (If MFA is genuinely not wanted, remove EnabledMfas / EmailAuthenticationMessage / EmailAuthenticationSubject instead.)`;
|
|
46691
46857
|
}
|
|
46692
|
-
if (request
|
|
46693
|
-
|
|
46694
|
-
|
|
46695
|
-
|
|
46696
|
-
|
|
46858
|
+
if (!request || request.MfaConfiguration !== "ON") return void 0;
|
|
46859
|
+
const fromTemplate = sendsFirstAuthFactorList(properties);
|
|
46860
|
+
const factors = fromTemplate ? readAllowedFirstAuthFactors(properties) : liveFirstAuthFactors ?? [];
|
|
46861
|
+
const where = fromTemplate ? `Policies.SignInPolicy.AllowedFirstAuthFactors allows` : `the pool's LIVE Policies.SignInPolicy.AllowedFirstAuthFactors allows`;
|
|
46862
|
+
const liveNote = fromTemplate ? "" : " (this template does not declare Policies.SignInPolicy.AllowedFirstAuthFactors, and UpdateUserPool PRESERVES an omitted sub-key, so the live list is what SetUserPoolMfaConfig meets)";
|
|
46863
|
+
const partialApply = "and on an update the rest of the change has already been applied by the time it does, so cdkd refuses it before sending anything rather than leaving a partly-applied update behind.";
|
|
46864
|
+
const denied = MFA_INCOMPATIBLE_FIRST_AUTH_FACTORS;
|
|
46865
|
+
const offending = factors.filter((factor) => denied.includes(factor));
|
|
46866
|
+
if (offending.length > 0) return fromTemplate ? `${MFA_CONFIGURATION_PATH} is ON while Policies.SignInPolicy.AllowedFirstAuthFactors allows ${offending.join(", ")}. AWS rejects that combination ("Only PASSWORD and WEB_AUTHN (if configured) can be enabled as an auth factor if MFA is enabled"), and on an update the sign-in policy has already been applied by the time it does, so cdkd refuses it before sending anything rather than leaving a partly-applied update behind. Remove ${offending.join(", ")} from Policies.SignInPolicy.AllowedFirstAuthFactors, leaving PASSWORD. (Setting MfaConfiguration to OPTIONAL or OFF also clears the conflict, but weakens MFA.)` : `${MFA_CONFIGURATION_PATH} is ON while ${where} ${offending.join(", ")}${liveNote}. AWS rejects that combination ("Only PASSWORD and WEB_AUTHN (if configured) can be enabled as an auth factor if MFA is enabled"), ${partialApply} Declare Policies.SignInPolicy with AllowedFirstAuthFactors that leave out ${offending.join(", ")} (for example [PASSWORD]) -- removing the block cannot remove a factor. (Setting MfaConfiguration to OPTIONAL or OFF also clears the conflict, but weakens MFA.)`;
|
|
46867
|
+
if (factors.includes("WEB_AUTHN") && request.WebAuthnConfiguration?.FactorConfiguration !== WEB_AUTHN_MULTI_FACTOR) return `${MFA_CONFIGURATION_PATH} is ON while ${where} WEB_AUTHN${liveNote} and WebAuthnFactorConfiguration is not ${WEB_AUTHN_MULTI_FACTOR}. AWS rejects that combination ("Cannot set WebAuthn factor configuration to SINGLE_FACTOR if MFA is required and WebAuthn is an allowed first auth factor"), ${partialApply} Set WebAuthnFactorConfiguration to ${WEB_AUTHN_MULTI_FACTOR}, which is the only value AWS accepts beside MFA ON, or ${fromTemplate ? "remove WEB_AUTHN from Policies.SignInPolicy.AllowedFirstAuthFactors" : "declare Policies.SignInPolicy with AllowedFirstAuthFactors that leave out WEB_AUTHN"}. (Setting MfaConfiguration to OPTIONAL also clears the conflict, but weakens MFA.)`;
|
|
46868
|
+
}
|
|
46869
|
+
/**
|
|
46870
|
+
* Does this bag put `Policies.SignInPolicy.AllowedFirstAuthFactors` on the
|
|
46871
|
+
* wire as a LIST? Only then does the pool's list change. MEASURED us-east-1
|
|
46872
|
+
* 2026-09-23: an `UpdateUserPool` carrying `SignInPolicy: {}` landed (its
|
|
46873
|
+
* `AutoVerifiedAttributes` canary applied) and left a live
|
|
46874
|
+
* `[PASSWORD, EMAIL_OTP]` intact -- so a SENT sub-key without the member is,
|
|
46875
|
+
* for the factor list, the same as an omitted one. The sub-key gate is the
|
|
46876
|
+
* wire's own (`sendsPoliciesSubKey`); the member gate mirrors
|
|
46877
|
+
* `readAllowedFirstAuthFactors`, which reads a non-list as nothing. A declared
|
|
46878
|
+
* non-list takes the live arm, the side that can refuse (what AWS does with
|
|
46879
|
+
* that shape is unmeasured). An EMPTY list counts as sent and judges nothing, which is safe:
|
|
46880
|
+
* MEASURED the same day, `UpdateUserPool` rejects `AllowedFirstAuthFactors: []`
|
|
46881
|
+
* ("Member must have length greater than or equal to 1") atomically -- its
|
|
46882
|
+
* co-sent `AutoVerifiedAttributes` did not land.
|
|
46883
|
+
*/
|
|
46884
|
+
function sendsFirstAuthFactorList(properties) {
|
|
46885
|
+
if (!sendsPoliciesSubKey(properties, "SignInPolicy")) return false;
|
|
46886
|
+
const signInPolicy = properties["Policies"]["SignInPolicy"];
|
|
46887
|
+
return Array.isArray(signInPolicy["AllowedFirstAuthFactors"]);
|
|
46888
|
+
}
|
|
46889
|
+
/**
|
|
46890
|
+
* Does the pre-flight need the pool's LIVE first-auth-factor list (issue
|
|
46891
|
+
* #2051)? Only when rules 2 / 3 can evaluate -- a `SetUserPoolMfaConfig`
|
|
46892
|
+
* request resolving to ON -- AND the template does not send
|
|
46893
|
+
* `Policies.SignInPolicy`, so the live list is what that call will meet. Every
|
|
46894
|
+
* other update spends no extra read.
|
|
46895
|
+
*/
|
|
46896
|
+
function mfaPreflightNeedsLiveSignInPolicy(properties, declaredMfaConfiguration) {
|
|
46897
|
+
return buildMfaConfigRequest("", properties, void 0, declaredMfaConfiguration)?.MfaConfiguration === "ON" && !sendsFirstAuthFactorList(properties);
|
|
46898
|
+
}
|
|
46899
|
+
/**
|
|
46900
|
+
* Could this update need `SetUserPoolMfaConfig` BEFORE `UpdateUserPool`
|
|
46901
|
+
* (issue #3562)? True only for the shape that FAILS in cdkd's usual order and
|
|
46902
|
+
* DEPLOYS under CloudFormation: the request keeps MFA `ON`, carries
|
|
46903
|
+
* `FactorConfiguration: MULTI_FACTOR_WITH_USER_VERIFICATION`, and the
|
|
46904
|
+
* template's factor list includes `WEB_AUTHN`. Whether the order actually
|
|
46905
|
+
* flips is then decided by the pool's LIVE MFA state
|
|
46906
|
+
* (`readMfaFirstPrior`) -- this only decides whether that read is worth making.
|
|
46907
|
+
*
|
|
46908
|
+
* MEASURED us-east-1 2026-09-23, from a pool at `ON` + SINGLE_FACTOR +
|
|
46909
|
+
* `[PASSWORD]`:
|
|
46910
|
+
*
|
|
46911
|
+
* UpdateUserPool adding WEB_AUTHN first -> REJECTED ("WEB_AUTHN cannot be
|
|
46912
|
+
* used as an auth factor when MFA is enabled if not configured for MFA")
|
|
46913
|
+
* SetUserPoolMfaConfig(ON, MULTI) first, then UpdateUserPool -> both ACCEPTED
|
|
46914
|
+
* CloudFormation, the same template edit -> UPDATE_COMPLETE
|
|
46915
|
+
*
|
|
46916
|
+
* The OTHER order-dependent edit the issue measured -- lowering MFA from `ON`
|
|
46917
|
+
* while ADDING `EMAIL_OTP` / `SMS_OTP` -- deliberately keeps cdkd's order:
|
|
46918
|
+
* CloudFormation ROLLS BACK that edit (same day, UPDATE_ROLLBACK_COMPLETE with
|
|
46919
|
+
* the same AWS message), so succeeding there would hide from a cdkd user a
|
|
46920
|
+
* failure their production CloudFormation deploy will hit. cdkd's own
|
|
46921
|
+
* rejection is atomic (`UpdateUserPool` refuses before anything lands).
|
|
46922
|
+
*/
|
|
46923
|
+
function mfaMayNeedToPrecedeUpdateUserPool(properties, declaredMfaConfiguration) {
|
|
46924
|
+
const request = buildMfaConfigRequest("", properties, void 0, declaredMfaConfiguration);
|
|
46925
|
+
return request?.MfaConfiguration === "ON" && request.WebAuthnConfiguration?.FactorConfiguration === WEB_AUTHN_MULTI_FACTOR && readAllowedFirstAuthFactors(properties).includes("WEB_AUTHN");
|
|
46697
46926
|
}
|
|
46698
46927
|
/**
|
|
46699
46928
|
* Replace a DECLARED-but-unusable `MfaConfiguration` with the value cdkd
|
|
@@ -46777,9 +47006,10 @@ var CognitoUserPoolProvider = class {
|
|
|
46777
47006
|
"EmailAuthenticationMessage",
|
|
46778
47007
|
"EmailAuthenticationSubject",
|
|
46779
47008
|
"WebAuthnRelyingPartyID",
|
|
46780
|
-
"WebAuthnUserVerification"
|
|
47009
|
+
"WebAuthnUserVerification",
|
|
47010
|
+
"WebAuthnFactorConfiguration"
|
|
46781
47011
|
])]]);
|
|
46782
|
-
unhandledByDesign = /* @__PURE__ */ new Map(
|
|
47012
|
+
unhandledByDesign = /* @__PURE__ */ new Map();
|
|
46783
47013
|
/**
|
|
46784
47014
|
* Warn when `MfaConfiguration` is declared as a BLANK string (issue #1925
|
|
46785
47015
|
* item 2, review round 2).
|
|
@@ -46822,8 +47052,8 @@ var CognitoUserPoolProvider = class {
|
|
|
46822
47052
|
* physical id on update, matching what each path passes to its other
|
|
46823
47053
|
* `ProvisioningError`s.
|
|
46824
47054
|
*/
|
|
46825
|
-
assertMfaCombinationApplicable(logicalId, resourceType, target, properties, declaredMfaConfiguration) {
|
|
46826
|
-
const reason = describeUnsupportedMfaCombination(properties, declaredMfaConfiguration);
|
|
47055
|
+
assertMfaCombinationApplicable(logicalId, resourceType, target, properties, declaredMfaConfiguration, liveFirstAuthFactors) {
|
|
47056
|
+
const reason = describeUnsupportedMfaCombination(properties, declaredMfaConfiguration, liveFirstAuthFactors);
|
|
46827
47057
|
if (reason === void 0) return;
|
|
46828
47058
|
throw new ProvisioningError(reason, resourceType, logicalId, target);
|
|
46829
47059
|
}
|
|
@@ -47105,6 +47335,14 @@ var CognitoUserPoolProvider = class {
|
|
|
47105
47335
|
*
|
|
47106
47336
|
* - `AutoVerifiedAttributes` — DOES reset when omitted (2026-08-18).
|
|
47107
47337
|
* - `MfaConfiguration` — does NOT reset when omitted (2026-08-18, above).
|
|
47338
|
+
* - `DeletionProtection` — does NOT reset when omitted (2026-09-23, issue
|
|
47339
|
+
* #2675): a pool created `ACTIVE` stayed `ACTIVE` through an
|
|
47340
|
+
* `UpdateUserPool` omitting it, whose co-sent `AutoVerifiedAttributes`
|
|
47341
|
+
* change landed (the call ran), and an explicit `INACTIVE` then applied
|
|
47342
|
+
* (the field is writable on this call rather than ignored).
|
|
47343
|
+
* - `WebAuthnConfiguration.FactorConfiguration` (a `SetUserPoolMfaConfig`
|
|
47344
|
+
* field) — does NOT reset through an `UpdateUserPool` that omits every MFA
|
|
47345
|
+
* member (2026-09-23, issue #2064).
|
|
47108
47346
|
* - `Policies` — neither sub-key resets, measured in BOTH directions
|
|
47109
47347
|
* (2026-08-19, issue #1968; details at `toSdkUserPoolPolicies`): a
|
|
47110
47348
|
* container sent without `SignInPolicy` left `SignInPolicy` intact, a
|
|
@@ -47125,6 +47363,9 @@ var CognitoUserPoolProvider = class {
|
|
|
47125
47363
|
* rejected the same way (2026-08-19, issue #1968; the transcript lives at
|
|
47126
47364
|
* `describeUnsupportedMfaCombination`, which since issue #1977 REFUSES that
|
|
47127
47365
|
* combination before any call rather than warning about it).
|
|
47366
|
+
* - `WebAuthnConfiguration.FactorConfiguration` — an omitted block is read as
|
|
47367
|
+
* SINGLE_FACTOR even on a pool holding MULTI_FACTOR_WITH_USER_VERIFICATION
|
|
47368
|
+
* (2026-09-23, issue #2064; transcript at `describeUnsupportedMfaCombination`).
|
|
47128
47369
|
*
|
|
47129
47370
|
* Nothing here licenses a claim about a field on neither list, or about one
|
|
47130
47371
|
* API from the other's section. Measure it.
|
|
@@ -47163,6 +47404,79 @@ var CognitoUserPoolProvider = class {
|
|
|
47163
47404
|
}
|
|
47164
47405
|
}
|
|
47165
47406
|
/**
|
|
47407
|
+
* The pool's live `Policies.SignInPolicy.AllowedFirstAuthFactors`, for the
|
|
47408
|
+
* pre-flight (issue #2051), or `undefined` when it cannot be read.
|
|
47409
|
+
*
|
|
47410
|
+
* FAILS OPEN: an unreadable pool is judged on the template alone, which is
|
|
47411
|
+
* the pre-#2051 behavior -- AWS then answers with its own rejection. Refusing
|
|
47412
|
+
* on a failed read would block deploys over a question cdkd never answered.
|
|
47413
|
+
* `update()` already needs `cognito-idp:DescribeUserPool` (its post-update
|
|
47414
|
+
* attribute read), so this adds no permission. An ABSENT list (a pool with
|
|
47415
|
+
* no sign-in policy) is AWS's default `[PASSWORD]`, which trips no rule, so
|
|
47416
|
+
* it reads as empty.
|
|
47417
|
+
*/
|
|
47418
|
+
async readLiveAllowedFirstAuthFactors(physicalId) {
|
|
47419
|
+
try {
|
|
47420
|
+
const factors = (await this.retryOnTransientControlPlane(() => this.getClient().send(new DescribeUserPoolCommand({ UserPoolId: physicalId })), `DescribeUserPool(${physicalId}) before the MFA pre-flight`)).UserPool?.Policies?.SignInPolicy?.AllowedFirstAuthFactors;
|
|
47421
|
+
return Array.isArray(factors) ? factors : [];
|
|
47422
|
+
} catch (error) {
|
|
47423
|
+
this.logger.debug(`DescribeUserPool failed for UserPool ${physicalId} before the MFA pre-flight (${error instanceof Error ? error.name : typeof error}): ${describeAwsFailure(error).detail}`);
|
|
47424
|
+
return;
|
|
47425
|
+
}
|
|
47426
|
+
}
|
|
47427
|
+
/**
|
|
47428
|
+
* The pool's FULL live MFA configuration when `SetUserPoolMfaConfig` has to
|
|
47429
|
+
* go FIRST (issue #3562), or `undefined` to keep the usual order.
|
|
47430
|
+
*
|
|
47431
|
+
* MFA-first is needed exactly when the live pool is at `ON` without
|
|
47432
|
+
* `MULTI_FACTOR_WITH_USER_VERIFICATION`: `UpdateUserPool` then refuses to add
|
|
47433
|
+
* `WEB_AUTHN` until the factor configuration has changed. Anything else --
|
|
47434
|
+
* live `OFF` / `OPTIONAL`, live `MULTI` already, or a read that FAILED --
|
|
47435
|
+
* keeps `UpdateUserPool` first, which is the pre-#3562 behaviour and at worst
|
|
47436
|
+
* the atomic rejection it always was. The returned response is also what
|
|
47437
|
+
* `restoreMfaConfig` replays if `UpdateUserPool` fails after the MFA call
|
|
47438
|
+
* landed.
|
|
47439
|
+
*/
|
|
47440
|
+
async readMfaFirstPrior(physicalId) {
|
|
47441
|
+
try {
|
|
47442
|
+
const live = await this.retryOnTransientControlPlane(() => this.getClient().send(new GetUserPoolMfaConfigCommand({ UserPoolId: physicalId })), `GetUserPoolMfaConfig(${physicalId}) for the call order`);
|
|
47443
|
+
return live.MfaConfiguration === "ON" && live.WebAuthnConfiguration?.FactorConfiguration !== WEB_AUTHN_MULTI_FACTOR ? live : void 0;
|
|
47444
|
+
} catch (error) {
|
|
47445
|
+
this.logger.debug(`GetUserPoolMfaConfig failed for UserPool ${physicalId} while choosing the call order (${error instanceof Error ? error.name : typeof error}): ${describeAwsFailure(error).detail}`);
|
|
47446
|
+
return;
|
|
47447
|
+
}
|
|
47448
|
+
}
|
|
47449
|
+
/**
|
|
47450
|
+
* Put back the MFA configuration `readMfaFirstPrior` read, after
|
|
47451
|
+
* `UpdateUserPool` failed behind an MFA-first `SetUserPoolMfaConfig` (issue
|
|
47452
|
+
* #3562). Without this the reversed order leaves the #1975 partial apply
|
|
47453
|
+
* mirrored: the MFA change landed, the rest did not. MEASURED us-east-1
|
|
47454
|
+
* 2026-09-23: replaying the earlier `GetUserPoolMfaConfig` members through
|
|
47455
|
+
* `SetUserPoolMfaConfig` restored the pool exactly, after a MULTI change had
|
|
47456
|
+
* landed and the `UpdateUserPool` behind it was rejected.
|
|
47457
|
+
*
|
|
47458
|
+
* Best effort: the caller re-throws the ORIGINAL error either way, and a
|
|
47459
|
+
* failed restore is announced (error CLASS at warn, detail at debug) rather
|
|
47460
|
+
* than replacing it.
|
|
47461
|
+
*/
|
|
47462
|
+
async restoreMfaConfig(physicalId, prior) {
|
|
47463
|
+
const request = {
|
|
47464
|
+
UserPoolId: physicalId,
|
|
47465
|
+
...prior.MfaConfiguration !== void 0 && { MfaConfiguration: prior.MfaConfiguration },
|
|
47466
|
+
...prior.SmsMfaConfiguration !== void 0 && { SmsMfaConfiguration: prior.SmsMfaConfiguration },
|
|
47467
|
+
...prior.SoftwareTokenMfaConfiguration !== void 0 && { SoftwareTokenMfaConfiguration: prior.SoftwareTokenMfaConfiguration },
|
|
47468
|
+
...prior.EmailMfaConfiguration !== void 0 && { EmailMfaConfiguration: prior.EmailMfaConfiguration },
|
|
47469
|
+
...prior.WebAuthnConfiguration !== void 0 && { WebAuthnConfiguration: prior.WebAuthnConfiguration }
|
|
47470
|
+
};
|
|
47471
|
+
try {
|
|
47472
|
+
await this.retryOnTransientControlPlane(() => this.getClient().send(new SetUserPoolMfaConfigCommand(request)), `SetUserPoolMfaConfig(${physicalId}) restore`);
|
|
47473
|
+
this.logger.debug(`Restored the previous MFA configuration on UserPool ${physicalId}`);
|
|
47474
|
+
} catch (restoreError) {
|
|
47475
|
+
this.logger.warn(`UserPool ${physicalId}: UpdateUserPool failed after SetUserPoolMfaConfig had already applied the new MFA configuration, and restoring the previous one also failed (${restoreError instanceof Error ? restoreError.name : typeof restoreError}). The pool may now carry the NEW MFA configuration with its OLD sign-in policy; re-run the deploy, or check it with aws cognito-idp get-user-pool-mfa-config --user-pool-id ${physicalId}.`);
|
|
47476
|
+
this.logger.debug(`MFA restore failure detail for UserPool ${physicalId}: ${describeAwsFailure(restoreError).detail}`);
|
|
47477
|
+
}
|
|
47478
|
+
}
|
|
47479
|
+
/**
|
|
47166
47480
|
* Retry a Cognito control-plane call on transient "settling" errors. A
|
|
47167
47481
|
* SetUserPoolMfaConfig issued immediately after CreateUserPool (or another
|
|
47168
47482
|
* control-plane write) can briefly hit `ConcurrentModificationException` /
|
|
@@ -47276,9 +47590,11 @@ var CognitoUserPoolProvider = class {
|
|
|
47276
47590
|
this.logger.debug(`Updating Cognito User Pool ${logicalId}: ${physicalId}`);
|
|
47277
47591
|
const mfaConfiguration = readDeclaredMfaConfiguration(properties["MfaConfiguration"], { onUnusable: (message) => this.logger.warn(message) });
|
|
47278
47592
|
this.warnOnBlankMfaConfiguration(properties["MfaConfiguration"]);
|
|
47279
|
-
|
|
47593
|
+
const liveFirstAuthFactors = mfaPreflightNeedsLiveSignInPolicy(properties, mfaConfiguration) ? await this.readLiveAllowedFirstAuthFactors(physicalId) : void 0;
|
|
47594
|
+
this.assertMfaCombinationApplicable(logicalId, resourceType, physicalId, properties, mfaConfiguration, liveFirstAuthFactors);
|
|
47280
47595
|
const liveMfa = mfaConfiguration === "" && hasMfaConfigProps(properties) ? await this.readLiveMfaConfiguration(physicalId) : void 0;
|
|
47281
47596
|
this.warnOnUnremovablePoliciesSubKeys(physicalId, properties, previousProperties);
|
|
47597
|
+
const mfaFirstPrior = mfaMayNeedToPrecedeUpdateUserPool(properties, mfaConfiguration) ? await this.readMfaFirstPrior(physicalId) : void 0;
|
|
47282
47598
|
try {
|
|
47283
47599
|
const updateParams = { UserPoolId: physicalId };
|
|
47284
47600
|
if (properties["Policies"]) {
|
|
@@ -47303,14 +47619,23 @@ var CognitoUserPoolProvider = class {
|
|
|
47303
47619
|
if (properties["SmsAuthenticationMessage"] !== void 0) updateParams.SmsAuthenticationMessage = properties["SmsAuthenticationMessage"];
|
|
47304
47620
|
if (properties["SmsVerificationMessage"] !== void 0) updateParams.SmsVerificationMessage = properties["SmsVerificationMessage"];
|
|
47305
47621
|
if (properties["UserPoolTier"]) updateParams.UserPoolTier = properties["UserPoolTier"];
|
|
47306
|
-
|
|
47307
|
-
await this.reconcileSchemaCustomAttributes(logicalId, physicalId, resourceType, properties["Schema"], previousProperties["Schema"], (properties["DeletionProtection"] ? properties["DeletionProtection"] : previousProperties["DeletionProtection"]) === "ACTIVE");
|
|
47308
|
-
await this.applyMfaConfig(physicalId, properties, mfaConfiguration, {
|
|
47622
|
+
const mfaOptions = {
|
|
47309
47623
|
...liveMfa?.value !== void 0 ? { liveMfaConfiguration: liveMfa.value } : {},
|
|
47310
47624
|
...liveMfa?.failed === true ? { liveReadFailed: true } : {},
|
|
47311
47625
|
declaredKind: classifyDeclaredMfaConfiguration(properties["MfaConfiguration"]),
|
|
47312
47626
|
...context?.maskSecrets && { maskSecrets: context.maskSecrets }
|
|
47313
|
-
}
|
|
47627
|
+
};
|
|
47628
|
+
if (mfaFirstPrior) {
|
|
47629
|
+
await this.applyMfaConfig(physicalId, properties, mfaConfiguration, mfaOptions);
|
|
47630
|
+
try {
|
|
47631
|
+
await this.getClient().send(new UpdateUserPoolCommand(updateParams));
|
|
47632
|
+
} catch (updateError) {
|
|
47633
|
+
await this.restoreMfaConfig(physicalId, mfaFirstPrior);
|
|
47634
|
+
throw updateError;
|
|
47635
|
+
}
|
|
47636
|
+
} else await this.getClient().send(new UpdateUserPoolCommand(updateParams));
|
|
47637
|
+
await this.reconcileSchemaCustomAttributes(logicalId, physicalId, resourceType, properties["Schema"], previousProperties["Schema"], (properties["DeletionProtection"] ? properties["DeletionProtection"] : previousProperties["DeletionProtection"]) === "ACTIVE");
|
|
47638
|
+
if (!mfaFirstPrior) await this.applyMfaConfig(physicalId, properties, mfaConfiguration, mfaOptions);
|
|
47314
47639
|
this.logger.debug(`Successfully updated Cognito User Pool ${logicalId}`);
|
|
47315
47640
|
const userPool = (await this.getClient().send(new DescribeUserPoolCommand({ UserPoolId: physicalId }))).UserPool;
|
|
47316
47641
|
const region = await this.getClient().config.region();
|
|
@@ -47380,7 +47705,7 @@ var CognitoUserPoolProvider = class {
|
|
|
47380
47705
|
before: "aws cognito-idp update-user-pool --user-pool-id",
|
|
47381
47706
|
identifier: physicalId,
|
|
47382
47707
|
after: "--deletion-protection INACTIVE",
|
|
47383
|
-
caveat: "Note UpdateUserPool
|
|
47708
|
+
caveat: "Note UpdateUserPool resets some members a call omits (AutoVerifiedAttributes among them), so send your complete pool configuration alongside that flag rather than the flag alone."
|
|
47384
47709
|
}
|
|
47385
47710
|
}) : `AWS::Cognito::UserPool is a stateful type, so re-run with ${replaceFlags} to recreate it (this deletes all users in the pool).`;
|
|
47386
47711
|
throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `the Schema change (${immutableChanges.join("; ")}) is immutable on AWS — AWS can only ADD custom attributes in place, so removing or modifying an attribute requires recreating the pool. ${remedy}`);
|
|
@@ -47520,6 +47845,7 @@ var CognitoUserPoolProvider = class {
|
|
|
47520
47845
|
result["EmailAuthenticationSubject"] = mfa.EmailMfaConfiguration?.Subject ?? "";
|
|
47521
47846
|
result["WebAuthnRelyingPartyID"] = mfa.WebAuthnConfiguration?.RelyingPartyId ?? "";
|
|
47522
47847
|
result["WebAuthnUserVerification"] = mfa.WebAuthnConfiguration?.UserVerification ?? "";
|
|
47848
|
+
result["WebAuthnFactorConfiguration"] = mfa.WebAuthnConfiguration?.FactorConfiguration ?? "";
|
|
47523
47849
|
} catch (mfaErr) {
|
|
47524
47850
|
this.logger.debug(`GetUserPoolMfaConfig failed for ${physicalId}, skipping MFA-derived drift keys: ${describeAwsFailure(mfaErr).detail}`);
|
|
47525
47851
|
}
|
|
@@ -85045,6 +85371,36 @@ function resolveCdkPathToLogicalIds(input, index) {
|
|
|
85045
85371
|
//#endregion
|
|
85046
85372
|
//#region src/analyzer/orphan-rewriter.ts
|
|
85047
85373
|
/**
|
|
85374
|
+
* Why a reference to an orphaned record that is NOT a readable resource record
|
|
85375
|
+
* is left in place rather than resolved (go-to-k/cdkd#3350).
|
|
85376
|
+
*
|
|
85377
|
+
* `cdkd orphan` may drop such a record — refusing to would close the way out of
|
|
85378
|
+
* it — but it must not resolve THROUGH one. Measured before this guard: a
|
|
85379
|
+
* string or number record made a sibling's `{"Ref": O}` resolve to `undefined`,
|
|
85380
|
+
* which the save then dropped from the JSON, and its `Fn::Sub` `${O}` became
|
|
85381
|
+
* the literal `x-undefined`. Reported as unresolvable instead, so a plain run
|
|
85382
|
+
* aborts naming the site and `--force` leaves the intrinsic untouched — there
|
|
85383
|
+
* is no cached value to fall back to either, since the cache lives inside the
|
|
85384
|
+
* record that cannot be read.
|
|
85385
|
+
*/
|
|
85386
|
+
const UNREADABLE_ORPHAN_RECORD_REASON = "the orphaned resource's state record is not a readable resource record (not an object, no resource type, or no physical id), so nothing in it can be substituted";
|
|
85387
|
+
/**
|
|
85388
|
+
* Whether the fetcher may read an orphaned record at all: the ENTRY class's
|
|
85389
|
+
* predicate PLUS a non-empty string `physicalId`. The entry predicate stops at
|
|
85390
|
+
* `resourceType` on purpose (its own note), but every LIVE value this fetcher
|
|
85391
|
+
* produces is derived from the physical id — `{"resourceType": "T"}` alone
|
|
85392
|
+
* made `{"Ref": O}` resolve to `undefined` and `Fn::Sub` to `x-undefined`
|
|
85393
|
+
* exactly as a string record did, and a type whose `Ref` recovery reads the id
|
|
85394
|
+
* threw a bare `TypeError` (review of go-to-k/cdkd#3568). The `--force` cache
|
|
85395
|
+
* is skipped for such a record too, deliberately: a record with no identity is
|
|
85396
|
+
* not one whose cached values can be trusted to describe a live resource.
|
|
85397
|
+
*/
|
|
85398
|
+
function isResolvableOrphanRecord(entry) {
|
|
85399
|
+
if (!isReadableResourceEntry(entry)) return false;
|
|
85400
|
+
const physicalId = entry.physicalId;
|
|
85401
|
+
return typeof physicalId === "string" && physicalId !== "";
|
|
85402
|
+
}
|
|
85403
|
+
/**
|
|
85048
85404
|
* Live-fetch helper. Wraps `provider.getAttribute(...)` for one orphan
|
|
85049
85405
|
* resource and memoizes results so multiple references to the same
|
|
85050
85406
|
* `(orphan, attr)` pair only hit AWS once.
|
|
@@ -85115,8 +85471,12 @@ var AttributeFetcher = class {
|
|
|
85115
85471
|
* already does with a masked cached attribute; the two arms now agree.
|
|
85116
85472
|
*/
|
|
85117
85473
|
ref(orphanLogicalId) {
|
|
85474
|
+
if (!Object.hasOwn(this.orphans, orphanLogicalId)) throw new Error(`Internal: Ref to '${orphanLogicalId}' has no orphan entry — should have been filtered out`);
|
|
85118
85475
|
const o = this.orphans[orphanLogicalId];
|
|
85119
|
-
if (!o)
|
|
85476
|
+
if (!isResolvableOrphanRecord(o)) return {
|
|
85477
|
+
ok: false,
|
|
85478
|
+
reason: UNREADABLE_ORPHAN_RECORD_REASON
|
|
85479
|
+
};
|
|
85120
85480
|
let maskedKey;
|
|
85121
85481
|
const value = cfnRefValueFromPhysicalId(o.resourceType, o.physicalId, refStateLookupFromResource(o, (key) => {
|
|
85122
85482
|
maskedKey = key;
|
|
@@ -85155,11 +85515,15 @@ var AttributeFetcher = class {
|
|
|
85155
85515
|
ok: true,
|
|
85156
85516
|
value: this.cache.get(cacheKey)
|
|
85157
85517
|
};
|
|
85158
|
-
|
|
85159
|
-
if (!orphan) return {
|
|
85518
|
+
if (!Object.hasOwn(this.orphans, orphanLogicalId)) return {
|
|
85160
85519
|
ok: false,
|
|
85161
85520
|
reason: `Internal: GetAtt to '${orphanLogicalId}' has no orphan entry`
|
|
85162
85521
|
};
|
|
85522
|
+
const orphan = this.orphans[orphanLogicalId];
|
|
85523
|
+
if (!isResolvableOrphanRecord(orphan)) return {
|
|
85524
|
+
ok: false,
|
|
85525
|
+
reason: UNREADABLE_ORPHAN_RECORD_REASON
|
|
85526
|
+
};
|
|
85163
85527
|
let provider;
|
|
85164
85528
|
try {
|
|
85165
85529
|
provider = this.providerRegistry.getProviderFor({
|
|
@@ -85196,7 +85560,15 @@ var AttributeFetcher = class {
|
|
|
85196
85560
|
reason
|
|
85197
85561
|
};
|
|
85198
85562
|
const orphan = this.orphans[orphanLogicalId];
|
|
85199
|
-
const
|
|
85563
|
+
const bag = orphan.attributes;
|
|
85564
|
+
if (bag !== void 0 && !isReadableBag(bag)) {
|
|
85565
|
+
this.logger.warn(`--force: state.attributes of '${displaySafe(orphanLogicalId, { asciiOnly: true })}' is not a readable map, so it is not consulted for '${displaySafe(attribute, { asciiOnly: true })}'; leaving the original intrinsic in place.`);
|
|
85566
|
+
return {
|
|
85567
|
+
ok: false,
|
|
85568
|
+
reason: `${reason}; the state.attributes cache is not a readable map`
|
|
85569
|
+
};
|
|
85570
|
+
}
|
|
85571
|
+
const stored = bag !== void 0 && Object.hasOwn(bag, attribute) ? bag[attribute] : void 0;
|
|
85200
85572
|
const cached = isImpossibleEmptyStoredAttribute(orphan.resourceType, attribute, stored) ? void 0 : stored;
|
|
85201
85573
|
if (cached === void 0) {
|
|
85202
85574
|
this.logger.warn(`--force: state.attributes also lacks '${orphanLogicalId}.${attribute}'; leaving the original intrinsic in place.`);
|
|
@@ -85256,11 +85628,10 @@ var AttributeFetcher = class {
|
|
|
85256
85628
|
*/
|
|
85257
85629
|
async function rewriteResourceReferences(state, orphanLogicalIds, providerRegistry, options = {}) {
|
|
85258
85630
|
const orphanSet = new Set(orphanLogicalIds);
|
|
85259
|
-
const orphans =
|
|
85631
|
+
const orphans = Object.create(null);
|
|
85260
85632
|
for (const id of orphanLogicalIds) {
|
|
85261
|
-
|
|
85262
|
-
|
|
85263
|
-
orphans[id] = r;
|
|
85633
|
+
if (!Object.hasOwn(state.resources, id)) throw new Error(`rewriteResourceReferences: orphan '${id}' not found in state.resources`);
|
|
85634
|
+
orphans[id] = state.resources[id];
|
|
85264
85635
|
}
|
|
85265
85636
|
const fetcher = new AttributeFetcher(orphans, providerRegistry, options);
|
|
85266
85637
|
const rewrites = [];
|
|
@@ -85704,10 +86075,13 @@ async function orphanCommand(pathArgs, options) {
|
|
|
85704
86075
|
const stateData = await stateBackend.getState(stackInfo.stackName, targetRegion);
|
|
85705
86076
|
if (!stateData) throw new Error(`No state found for stack '${displaySafe(stackInfo.stackName)}' (${displaySafe(targetRegion, { asciiOnly: true })}). Nothing to orphan. (Did the stack get deployed?)`);
|
|
85706
86077
|
const { state, etag, migrationPending } = stateData;
|
|
85707
|
-
refuseMalformedState(state, stackInfo.stackName,
|
|
85708
|
-
refuseMalformedOutputs(state, stackInfo.stackName,
|
|
86078
|
+
refuseMalformedState(state, stackInfo.stackName, recordRegion, recovery);
|
|
86079
|
+
refuseMalformedOutputs(state, stackInfo.stackName, recordRegion, recovery);
|
|
86080
|
+
refuseMalformedResourceEntriesForOrphan(state, orphanLogicalIds, stackInfo.stackName, recordRegion, recovery);
|
|
85709
86081
|
refuseMalformedResourcePropertiesForOrphan(state, orphanLogicalIds, stackInfo.stackName, recordRegion, recovery);
|
|
85710
|
-
|
|
86082
|
+
refuseMalformedResourceAttributesForOrphan(state, orphanLogicalIds, stackInfo.stackName, recordRegion, recovery);
|
|
86083
|
+
refuseMalformedOrphansForOrphan(state, stackInfo.stackName, recordRegion, recovery);
|
|
86084
|
+
const missing = orphanLogicalIds.filter((id) => !Object.hasOwn(state.resources, id));
|
|
85711
86085
|
if (missing.length > 0) {
|
|
85712
86086
|
const have = displayIdentList(Object.keys(state.resources), ", ");
|
|
85713
86087
|
throw new Error(`Resource(s) not in state for stack '${displaySafe(stackInfo.stackName)}' (${displaySafe(targetRegion, { asciiOnly: true })}): ${displayIdentList(missing, ", ")}.\nAvailable logical IDs: ${have}`);
|
|
@@ -97769,4 +98143,4 @@ function buildProgram() {
|
|
|
97769
98143
|
|
|
97770
98144
|
//#endregion
|
|
97771
98145
|
export { buildProgram };
|
|
97772
|
-
//# sourceMappingURL=program-
|
|
98146
|
+
//# sourceMappingURL=program-C5r-kmIQ.js.map
|