@go-to-k/cdkd 0.284.12 → 0.284.14

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as isTerminationProtectionPropagationError, $n as ResourceUpdateNotSupportedError, $t as validateAssetBucketName, A as renderStatefulReason, An as derivePartitionAndUrlSuffix, At as INTRINSIC_KEYS, B as exportAliasCollisionScrubWarning, Bn as CdkdError, Bt as stringifyValue, C as isFinalSnapshotError, Cn as CFN_TEMPLATE_URL_LIMIT, Ct as s3BucketArn, D as extractDeploymentEventError, Dn as expectedOwnerParam, Dt as s3BucketWebsiteUrl, E as makeCanonicalizePropertiesFn, En as uploadCfnTemplate, Et as s3BucketRegionalDomainName, F as green, Fn as AwsClients, Ft as LockManager, G as collectInlinePolicyNamesManagedBySiblings, Gn as LocalMigrateError, Gt as rewriteTemplateAssetReferences, H as secretBearingStateKeyWarning, Ht as buildAssetRedirectMap, I as red, In as getAwsClients, It as S3StateBackend, J as findActionableSilentDrops, Jn as MissingCdkCliError, Jt as AssetModeResolver, K as clearOnUpdateRemoval, Kn as LocalStartServiceError, Kt as escapeRegExp, L as yellow, Ln as resetAwsClients, Lt as rebuildClientForBucketRegion, M as bold, Mn as processStackMessages, Mt as withRetry, N as cyan, Nt as DagBuilder, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as PARTITION_TABLE, Ot as applyRoleArnIfSet, P as gray, Pn as resolveBucketRegion, Pt as TemplateParser, Q as disableInstanceApiTermination, Qn as ResourceTimeoutError, Qt as parseBootstrapMarker, R as collectDeclaredOutputNames, Rn as setAwsClients, Rt as shouldRetainResource, S as createPreDeleteFinalSnapshot, Sn as CFN_TEMPLATE_BODY_LIMIT, St as scrubResourceRecord, T as unsupportedFinalSnapshotError, Tn as findLargeInlineResources, Tt as s3BucketDualStackDomainName, U as stateKeySecretExposure, Un as DeployCancelledError, Ut as createAssetRedirectResolver, V as isExportAliasCollision, Vt as WorkGraph, W as IAMRoleProvider, Wn as LocalInvokeBuildError$1, Wt as loadPublishableAssetManifest, X as CloudControlProvider, Xn as PartialFailureError, Xt as ensureAssetStorage, Y as findSilentDropProperties, Yn as NestedStackChildDirectDestroyError, Yt as BOOTSTRAP_MARKER_PREFIX, Z as slowCcOperationTimeoutMs, Zn as ProvisioningError, Zt as getBootstrapMarkerKey, _ as computeImplicitDeleteEdges, _n as resolveStateBucketWithDefault, _t as STATE_SOURCED_READBACK_RULES, a as DeploymentEventsStore, an as runDockerForeground, at as normalizeAwsTagsToCfn, b as buildFinalSnapshotIdentifier, bn as stateBucketExistenceConfirmed, bt as maskSecretsInText, c as replayFailedOperations, cn as getDockerImageBySourceHash, cr as isMarkedNonRetryable, ct as coerceCfnBoolean, d as updatePartialReason, dn as getDefaultStateBucketName, dr as markNonRetryable, dt as readConfigString, en as validateContainerRepoName, er as StackHasActiveImportsError, et as IntrinsicFunctionResolver, f as UNSPECIFIED_SKIP_REASON, fn as getLegacyStateBucketName, ft as replayWarn, g as IMPLICIT_DELETE_DEPENDENCIES, gn as resolveSkipPrefix, gt as STATE_SOURCED_CROSS_GENERATION_RULES, h as maskingRetryLogger, hn as resolveCaptureObservedState, ht as requireConfigString, i as DeploymentEventsReader, in as getDockerCmd, it as WAFv2WebACLProvider, j as formatResourceLine, jn as AssemblyReader, jt as describeTypeWithThrottleRetry, k as isStatefulRecreateTargetSync, kn as canonicalizeRegion, kt as DiffCalculator, l as replayRollback, ln as Synthesizer, lr as isRetryableTransientError, lt as configBooleanRefusal, m as withResourceDeadline, mn as resolveAutoAssetStorage, mt as requireConfigObject, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as buildDockerImage, nt as getAccountInfo, o as planFailedOps, on as runDockerStreaming, or as normalizeAwsError, ot as resolveExplicitPhysicalId, p as deleteSkipReason, pn as resolveApp, pt as requireConfigArray, q as ProviderRegistry, qt as stripControlChars, r as DeployEngine, rn as formatDockerLoginError, rt as refStateLookupFromResource, s as planRollback, sn as AssetManifestLoader, sr as withErrorHandling, st as assertRegionMatch, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDenyExternalAccessPolicy, tr as StackTerminationProtectionError, tt as cfnRefValueFromPhysicalId, u as updatePartialMessage, un as synthesisStatusMessage, ur as isThrottlingError, ut as configStringRefusal, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as resolveStateBucketWithDefaultAndSource, vt as TEMPLATE_SOURCED_RULES, w as refusesFinalSnapshot, wn as MIGRATE_TMP_PREFIX, wt as s3BucketDomainName, x as ccRoutedFinalSnapshotError, xn as warnDeprecatedNoPrefixCliFlag, xt as redactSecretsForState, y as PRE_DELETE_SNAPSHOT_TYPES, yn as resolveUseCdkBootstrapAssets, yt as createSecretMasker, z as collectPublishedOutputNames, zt as AssetPublisher } from "./deploy-engine-nwoIJsLn.js";
2
+ import { $ as isTerminationProtectionPropagationError, $n as ResourceUpdateNotSupportedError, $t as validateAssetBucketName, A as renderStatefulReason, An as derivePartitionAndUrlSuffix, At as INTRINSIC_KEYS, B as exportAliasCollisionScrubWarning, Bn as CdkdError, Bt as stringifyValue, C as isFinalSnapshotError, Cn as CFN_TEMPLATE_URL_LIMIT, Ct as s3BucketArn, D as extractDeploymentEventError, Dn as expectedOwnerParam, Dt as s3BucketWebsiteUrl, E as makeCanonicalizePropertiesFn, En as uploadCfnTemplate, Et as s3BucketRegionalDomainName, F as green, Fn as AwsClients, Ft as LockManager, G as collectInlinePolicyNamesManagedBySiblings, Gn as LocalMigrateError, Gt as rewriteTemplateAssetReferences, H as secretBearingStateKeyWarning, Ht as buildAssetRedirectMap, I as red, In as getAwsClients, It as S3StateBackend, J as findActionableSilentDrops, Jn as MissingCdkCliError, Jt as AssetModeResolver, K as clearOnUpdateRemoval, Kn as LocalStartServiceError, Kt as escapeRegExp, L as yellow, Ln as resetAwsClients, Lt as rebuildClientForBucketRegion, M as bold, Mn as processStackMessages, Mt as withRetry, N as cyan, Nt as DagBuilder, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as PARTITION_TABLE, Ot as applyRoleArnIfSet, P as gray, Pn as resolveBucketRegion, Pt as TemplateParser, Q as disableInstanceApiTermination, Qn as ResourceTimeoutError, Qt as parseBootstrapMarker, R as collectDeclaredOutputNames, Rn as setAwsClients, Rt as shouldRetainResource, S as createPreDeleteFinalSnapshot, Sn as CFN_TEMPLATE_BODY_LIMIT, St as scrubResourceRecord, T as unsupportedFinalSnapshotError, Tn as findLargeInlineResources, Tt as s3BucketDualStackDomainName, U as stateKeySecretExposure, Un as DeployCancelledError, Ut as createAssetRedirectResolver, V as isExportAliasCollision, Vt as WorkGraph, W as IAMRoleProvider, Wn as LocalInvokeBuildError$1, Wt as loadPublishableAssetManifest, X as CloudControlProvider, Xn as PartialFailureError, Xt as ensureAssetStorage, Y as findSilentDropProperties, Yn as NestedStackChildDirectDestroyError, Yt as BOOTSTRAP_MARKER_PREFIX, Z as slowCcOperationTimeoutMs, Zn as ProvisioningError, Zt as getBootstrapMarkerKey, _ as computeImplicitDeleteEdges, _n as resolveStateBucketWithDefault, _t as STATE_SOURCED_READBACK_RULES, a as DeploymentEventsStore, an as runDockerForeground, at as normalizeAwsTagsToCfn, b as buildFinalSnapshotIdentifier, bn as stateBucketExistenceConfirmed, bt as maskSecretsInText, c as replayFailedOperations, cn as getDockerImageBySourceHash, cr as isMarkedNonRetryable, ct as coerceCfnBoolean, d as updatePartialReason, dn as getDefaultStateBucketName, dr as markNonRetryable, dt as readConfigString, en as validateContainerRepoName, er as StackHasActiveImportsError, et as IntrinsicFunctionResolver, f as UNSPECIFIED_SKIP_REASON, fn as getLegacyStateBucketName, ft as replayWarn, g as IMPLICIT_DELETE_DEPENDENCIES, gn as resolveSkipPrefix, gt as STATE_SOURCED_CROSS_GENERATION_RULES, h as maskingRetryLogger, hn as resolveCaptureObservedState, ht as requireConfigString, i as DeploymentEventsReader, in as getDockerCmd, it as WAFv2WebACLProvider, j as formatResourceLine, jn as AssemblyReader, jt as describeTypeWithThrottleRetry, k as isStatefulRecreateTargetSync, kn as canonicalizeRegion, kt as DiffCalculator, l as replayRollback, ln as Synthesizer, lr as isRetryableTransientError, lt as configBooleanRefusal, m as withResourceDeadline, mn as resolveAutoAssetStorage, mt as requireConfigObject, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as buildDockerImage, nt as getAccountInfo, o as planFailedOps, on as runDockerStreaming, or as normalizeAwsError, ot as resolveExplicitPhysicalId, p as deleteSkipReason, pn as resolveApp, pt as requireConfigArray, q as ProviderRegistry, qt as stripControlChars, r as DeployEngine, rn as formatDockerLoginError, rt as refStateLookupFromResource, s as planRollback, sn as AssetManifestLoader, sr as withErrorHandling, st as assertRegionMatch, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDenyExternalAccessPolicy, tr as StackTerminationProtectionError, tt as cfnRefValueFromPhysicalId, u as updatePartialMessage, un as synthesisStatusMessage, ur as isThrottlingError, ut as configStringRefusal, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as resolveStateBucketWithDefaultAndSource, vt as TEMPLATE_SOURCED_RULES, w as refusesFinalSnapshot, wn as MIGRATE_TMP_PREFIX, wt as s3BucketDomainName, x as ccRoutedFinalSnapshotError, xn as warnDeprecatedNoPrefixCliFlag, xt as redactSecretsForState, y as PRE_DELETE_SNAPSHOT_TYPES, yn as resolveUseCdkBootstrapAssets, yt as createSecretMasker, z as collectPublishedOutputNames, zt as AssetPublisher } from "./deploy-engine-C2OBi4mp.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-CkIFzX7p.js";
4
+ import { t as ASGProvider } from "./asg-provider-DZAeGqBC.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";
@@ -33995,6 +33995,44 @@ var ECSProvider = class {
33995
33995
  }
33996
33996
  };
33997
33997
 
33998
+ //#endregion
33999
+ //#region src/provisioning/masked-retry-logger.ts
34000
+ /**
34001
+ * The masker a caller supplied, or the identity function when it supplied
34002
+ * none.
34003
+ *
34004
+ * ABSENT MEANS UNMASKED, and that is the back-compatible default the contract
34005
+ * mandates — `create()` / `update()` are also reached from the import path,
34006
+ * from `cdkd drift --revert`, and from tests, and a provider must not care
34007
+ * which caller it got. Centralised here so no call site re-spells the `??`
34008
+ * and accidentally makes the capability required.
34009
+ */
34010
+ function maskerOrIdentity(maskSecrets) {
34011
+ return maskSecrets ?? ((text) => text);
34012
+ }
34013
+ /**
34014
+ * A {@link RetryLogger} whose every line is routed through `maskSecrets`.
34015
+ *
34016
+ * `warn` is ALWAYS provided, never omitted. It is optional on `RetryLogger`,
34017
+ * and dropping it would silence the give-up summary on the calling path only —
34018
+ * trading a disclosure for the reporting hole issue #2018 closed. It goes
34019
+ * through the SAME mask as `debug` precisely because it is the line that
34020
+ * survives a run without `--verbose`; forwarding it unmasked would defeat the
34021
+ * fence at a HIGHER log level than the one the fence was written for.
34022
+ *
34023
+ * `logger` is typed structurally rather than as the concrete `Logger` so this
34024
+ * module stays a leaf and a test can pass two spies. That matches the
34025
+ * precedent already in the tree (`buildMfaConfigRequest` in
34026
+ * `cognito-provider.ts` takes an injected `logger?: { warn }`).
34027
+ */
34028
+ function createMaskedRetryLogger(logger, maskSecrets) {
34029
+ const mask = maskerOrIdentity(maskSecrets);
34030
+ return {
34031
+ debug: (message) => logger.debug(mask(message)),
34032
+ warn: (message) => logger.warn(mask(message))
34033
+ };
34034
+ }
34035
+
33998
34036
  //#endregion
33999
34037
  //#region src/provisioning/providers/elbv2-provider.ts
34000
34038
  /**
@@ -34004,6 +34042,41 @@ var ECSProvider = class {
34004
34042
  */
34005
34043
  const capacityReservationDelays = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
34006
34044
  /**
34045
+ * Depth cap for {@link maskDeep}. Bounds a self-referential bag rather than
34046
+ * hanging; `JSON.stringify` would throw on one anyway, and a throw is the
34047
+ * better failure of the two.
34048
+ */
34049
+ const MASK_WALK_MAX_DEPTH$1 = 8;
34050
+ /**
34051
+ * Mask every string LEAF and KEY of an arbitrary value, returning a structure
34052
+ * safe to `JSON.stringify` into a log line (issue #2050).
34053
+ *
34054
+ * The ordering is the whole point. `maskSecretsInText` matches by literal
34055
+ * occurrence, and `JSON.stringify` escapes `"`, `\` and newlines — so a Secrets
34056
+ * Manager JSON document (the commonest real secret shape) does not occur in the
34057
+ * stringified text and survives a mask applied afterwards. Masking the leaves
34058
+ * first also hands the masker each RAW value, which reaches
34059
+ * `maskSecretsInText`'s whole-value arm at any length instead of only the
34060
+ * substring arm's 4-character floor. See the "three gaps" note in
34061
+ * `src/deployment/secret-redaction.ts`.
34062
+ *
34063
+ * Keys are masked as well as values: `JSON.stringify` renders them into the
34064
+ * same line, so a resolved secret used as a map key would otherwise escape.
34065
+ *
34066
+ * `cognito-provider.ts` carries an equivalent private walk for its own
34067
+ * stringified warnings. Two copies is the current cost of keeping this a leaf;
34068
+ * a THIRD site is the point at which this should move into
34069
+ * `../masked-retry-logger.ts` (already the shared provider-side masking module)
34070
+ * and all three converge on it.
34071
+ */
34072
+ function maskDeep(value, mask, depth = 0) {
34073
+ if (typeof value === "string") return mask(value);
34074
+ if (depth >= MASK_WALK_MAX_DEPTH$1) return value;
34075
+ if (Array.isArray(value)) return value.map((entry) => maskDeep(entry, mask, depth + 1));
34076
+ if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [mask(k), maskDeep(v, mask, depth + 1)]));
34077
+ return value;
34078
+ }
34079
+ /**
34007
34080
  * Documented AWS defaults for TargetGroup attributes, used to reset a
34008
34081
  * removed `TargetGroupAttributes` entry. Unlike ModifyLoadBalancerAttributes
34009
34082
  * / ModifyListenerAttributes, ModifyTargetGroupAttributes REJECTS an empty
@@ -34177,28 +34250,86 @@ var ELBv2Provider = class {
34177
34250
  if (!this.elbv2Client) this.elbv2Client = new ElasticLoadBalancingV2Client(this.providerRegion ? { region: this.providerRegion } : {});
34178
34251
  return this.elbv2Client;
34179
34252
  }
34180
- async create(logicalId, resourceType, properties) {
34253
+ /**
34254
+ * A {@link RetryLogger} for this provider's `withRetry` calls, bound to the
34255
+ * caller's secret masker (issue #2050).
34256
+ *
34257
+ * Thin wrapper over the shared {@link createMaskedRetryLogger} so the two
34258
+ * providers that need it cannot drift apart — see that module for why the
34259
+ * factory is shared rather than hand-rolled per file, and for the full
34260
+ * rationale on why `warn` is threaded rather than omitted.
34261
+ *
34262
+ * WHY NOT A `maskSecrets` OPTION ON `withRetry` (issue #2050 acceptance
34263
+ * item 3): `RetryLogger` is a structural type any caller can already satisfy
34264
+ * with a masking object — `drift.ts`'s revert has done exactly that since
34265
+ * issue #1914 — so the option would be a SECOND spelling of one intent
34266
+ * across ~50 existing call sites, and two spellings is how a later author
34267
+ * reaches for the unmasked one.
34268
+ */
34269
+ maskedRetryLogger(maskSecrets) {
34270
+ return createMaskedRetryLogger(this.logger, maskSecrets);
34271
+ }
34272
+ /**
34273
+ * Mask a caught AWS error's message before it is interpolated into the
34274
+ * `ProvisioningError` this provider throws (issue #2050, review round 2).
34275
+ *
34276
+ * NOT a duplicate of {@link maskedRetryLogger}, and strictly WIDER than it.
34277
+ * `withRetry` rethrows the RAW error, and `deploy-engine.ts` prints the
34278
+ * resulting message at ERROR — i.e. at DEFAULT verbosity — so a masked
34279
+ * give-up `warn` was being followed one line later by the identical text
34280
+ * unmasked. It is also the ONLY disclosure surface for a NON-RETRYABLE
34281
+ * rejection, where `withRetry` emits nothing at all: its give-up summary is
34282
+ * gated on `propagationRetries > 0 || serverErrorRetries > 0`, and a
34283
+ * validation error that fails on attempt 0 satisfies neither.
34284
+ *
34285
+ * Masks `error.message` rather than the assembled sentence ON PURPOSE. The
34286
+ * two are NOT equivalent: handing the masker the raw message can reach
34287
+ * `maskSecretsInText`'s WHOLE-VALUE arm, which matches at ANY length, while
34288
+ * a longer assembled sentence can only ever reach the SUBSTRING arm, which
34289
+ * ignores needles below 4 characters. See `SecretMaskingContext` in
34290
+ * `src/types/resource.ts`.
34291
+ *
34292
+ * The `cause` chain is deliberately left UNMASKED and unwrapped by every
34293
+ * caller of this helper: `isRetryableTransientError` walks `cause` for
34294
+ * `$metadata.httpStatusCode`, and rewriting or dropping it would silently
34295
+ * change retry classification. Only the human-readable message is rewritten.
34296
+ */
34297
+ maskErrorMessage(error, maskSecrets) {
34298
+ return maskerOrIdentity(maskSecrets)(error instanceof Error ? error.message : String(error));
34299
+ }
34300
+ /**
34301
+ * `context` is read for ONE thing today: `maskSecrets` (issue #2050). The
34302
+ * Listener create path runs a post-create `ModifyListenerAttributes` through
34303
+ * `withRetry`, whose per-attempt `debug` line and give-up `warn` summary
34304
+ * interpolate the AWS message verbatim — and that payload is built from
34305
+ * RESOLVED template properties. See {@link maskedRetryLogger}.
34306
+ */
34307
+ async create(logicalId, resourceType, properties, context) {
34181
34308
  switch (resourceType) {
34182
34309
  case "AWS::ElasticLoadBalancingV2::LoadBalancer": return this.createLoadBalancer(logicalId, resourceType, properties);
34183
- case "AWS::ElasticLoadBalancingV2::TargetGroup": return this.createTargetGroup(logicalId, resourceType, properties);
34184
- case "AWS::ElasticLoadBalancingV2::Listener": return this.createListener(logicalId, resourceType, properties);
34310
+ case "AWS::ElasticLoadBalancingV2::TargetGroup": return this.createTargetGroup(logicalId, resourceType, properties, context?.maskSecrets);
34311
+ case "AWS::ElasticLoadBalancingV2::Listener": return this.createListener(logicalId, resourceType, properties, context?.maskSecrets);
34185
34312
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
34186
34313
  }
34187
34314
  }
34188
- async update(logicalId, physicalId, resourceType, properties, previousProperties) {
34315
+ /**
34316
+ * `context` is read for ONE thing today: `maskSecrets` (issue #2050) — the
34317
+ * update-path twin of the `create()` note above.
34318
+ */
34319
+ async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
34189
34320
  try {
34190
- return await this.applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties);
34321
+ return await this.applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context);
34191
34322
  } catch (error) {
34192
34323
  if (error instanceof CdkdError) throw error;
34193
34324
  const cause = error instanceof Error ? error : void 0;
34194
34325
  throw new ProvisioningError(`Failed to update ELBv2 resource ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
34195
34326
  }
34196
34327
  }
34197
- async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties) {
34328
+ async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context) {
34198
34329
  switch (resourceType) {
34199
34330
  case "AWS::ElasticLoadBalancingV2::LoadBalancer": return this.updateLoadBalancer(logicalId, physicalId, resourceType, properties, previousProperties);
34200
- case "AWS::ElasticLoadBalancingV2::TargetGroup": return this.updateTargetGroup(logicalId, physicalId, resourceType, properties, previousProperties);
34201
- case "AWS::ElasticLoadBalancingV2::Listener": return this.updateListener(logicalId, physicalId, resourceType, properties, previousProperties);
34331
+ case "AWS::ElasticLoadBalancingV2::TargetGroup": return this.updateTargetGroup(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
34332
+ case "AWS::ElasticLoadBalancingV2::Listener": return this.updateListener(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
34202
34333
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
34203
34334
  }
34204
34335
  }
@@ -34443,7 +34574,7 @@ var ELBv2Provider = class {
34443
34574
  throw new ProvisioningError(`Failed to delete LoadBalancer ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
34444
34575
  }
34445
34576
  }
34446
- async createTargetGroup(logicalId, resourceType, properties) {
34577
+ async createTargetGroup(logicalId, resourceType, properties, maskSecrets) {
34447
34578
  this.logger.debug(`Creating TargetGroup ${logicalId}`);
34448
34579
  try {
34449
34580
  const tags = this.extractTags(properties);
@@ -34481,7 +34612,7 @@ var ELBv2Provider = class {
34481
34612
  }));
34482
34613
  this.logger.debug(`Applied ${tgAttributes.length} TargetGroup attribute(s) for ${logicalId}`);
34483
34614
  }
34484
- const targets = this.convertTargets(properties["Targets"]);
34615
+ const targets = this.convertTargets(properties["Targets"], void 0, maskSecrets);
34485
34616
  if (targets.length > 0) {
34486
34617
  await this.getClient().send(new RegisterTargetsCommand({
34487
34618
  TargetGroupArn: tgArn,
@@ -34494,7 +34625,7 @@ var ELBv2Provider = class {
34494
34625
  await this.getClient().send(new DeleteTargetGroupCommand({ TargetGroupArn: tgArn }));
34495
34626
  this.logger.debug(`Cleaned up partially-created TargetGroup ${logicalId} (${tgArn}) after wiring failure`);
34496
34627
  } catch (cleanupError) {
34497
- this.logger.warn(`Failed to clean up partially-created TargetGroup ${logicalId} (${tgArn}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws elbv2 delete-target-group --target-group-arn ${tgArn}`);
34628
+ this.logger.warn(`Failed to clean up partially-created TargetGroup ${logicalId} (${tgArn}): ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: aws elbv2 delete-target-group --target-group-arn ${tgArn}`);
34498
34629
  }
34499
34630
  throw innerError;
34500
34631
  }
@@ -34508,10 +34639,10 @@ var ELBv2Provider = class {
34508
34639
  };
34509
34640
  } catch (error) {
34510
34641
  const cause = error instanceof Error ? error : void 0;
34511
- throw new ProvisioningError(`Failed to create TargetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
34642
+ throw new ProvisioningError(`Failed to create TargetGroup ${logicalId}: ${this.maskErrorMessage(error, maskSecrets)}`, resourceType, logicalId, void 0, cause);
34512
34643
  }
34513
34644
  }
34514
- async updateTargetGroup(logicalId, physicalId, resourceType, properties, previousProperties) {
34645
+ async updateTargetGroup(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
34515
34646
  this.logger.debug(`Updating TargetGroup ${logicalId}: ${physicalId}`);
34516
34647
  const immutableChanged = (key, coerce) => {
34517
34648
  const next = properties[key];
@@ -34539,7 +34670,7 @@ var ELBv2Provider = class {
34539
34670
  const tgAttrDiff = this.diffAttributes(this.normalizeAttributes(properties["TargetGroupAttributes"]), this.normalizeAttributes(previousProperties["TargetGroupAttributes"]), (key, currentValue) => {
34540
34671
  const fallback = Object.hasOwn(TARGET_GROUP_ATTRIBUTE_DEFAULTS, key) ? TARGET_GROUP_ATTRIBUTE_DEFAULTS[key] : void 0;
34541
34672
  if (fallback === void 0) {
34542
- this.logger.warn(`TargetGroup attribute ${key} was removed from the template but has no documented default cdkd can reset it to — the live value is retained. Set the attribute explicitly to change it.`);
34673
+ this.logger.warn(`TargetGroup attribute ${maskerOrIdentity(maskSecrets)(key)} was removed from the template but has no documented default cdkd can reset it to — the live value is retained. Set the attribute explicitly to change it.`);
34543
34674
  return;
34544
34675
  }
34545
34676
  return currentValue === fallback ? void 0 : fallback;
@@ -34551,8 +34682,8 @@ var ELBv2Provider = class {
34551
34682
  }));
34552
34683
  this.logger.debug(`Applied ${tgAttrDiff.length} TargetGroupAttributes change(s) for ${logicalId}`);
34553
34684
  }
34554
- const newTargets = this.convertTargets(properties["Targets"], properties["Port"]);
34555
- const oldTargets = this.convertTargets(previousProperties["Targets"], properties["Port"]);
34685
+ const newTargets = this.convertTargets(properties["Targets"], properties["Port"], maskSecrets);
34686
+ const oldTargets = this.convertTargets(previousProperties["Targets"], properties["Port"], maskSecrets);
34556
34687
  const targetKey = (t) => JSON.stringify([
34557
34688
  t.Id,
34558
34689
  t.Port ?? null,
@@ -34590,7 +34721,7 @@ var ELBv2Provider = class {
34590
34721
  };
34591
34722
  } catch (error) {
34592
34723
  const cause = error instanceof Error ? error : void 0;
34593
- throw new ProvisioningError(`Failed to update TargetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
34724
+ throw new ProvisioningError(`Failed to update TargetGroup ${logicalId}: ${this.maskErrorMessage(error, maskSecrets)}`, resourceType, logicalId, physicalId, cause);
34594
34725
  }
34595
34726
  }
34596
34727
  async deleteTargetGroup(logicalId, physicalId, resourceType, context) {
@@ -34608,7 +34739,7 @@ var ELBv2Provider = class {
34608
34739
  throw new ProvisioningError(`Failed to delete TargetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
34609
34740
  }
34610
34741
  }
34611
- async createListener(logicalId, resourceType, properties) {
34742
+ async createListener(logicalId, resourceType, properties, maskSecrets) {
34612
34743
  this.logger.debug(`Creating Listener ${logicalId}`);
34613
34744
  try {
34614
34745
  const tags = this.extractTags(properties);
@@ -34636,7 +34767,7 @@ var ELBv2Provider = class {
34636
34767
  await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
34637
34768
  ListenerArn: listenerArn,
34638
34769
  Attributes: listenerAttributes
34639
- })), logicalId, { logger: this.logger });
34770
+ })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
34640
34771
  this.logger.debug(`Applied ${listenerAttributes.length} ListenerAttribute(s) for ${logicalId}`);
34641
34772
  }
34642
34773
  } catch (innerError) {
@@ -34644,7 +34775,7 @@ var ELBv2Provider = class {
34644
34775
  await this.getClient().send(new DeleteListenerCommand({ ListenerArn: listenerArn }));
34645
34776
  this.logger.debug(`Cleaned up partially-created Listener ${logicalId} (${listenerArn}) after attributes-wiring failure`);
34646
34777
  } catch (cleanupError) {
34647
- this.logger.warn(`Failed to clean up partially-created Listener ${logicalId} (${listenerArn}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws elbv2 delete-listener --listener-arn ${listenerArn}`);
34778
+ this.logger.warn(`Failed to clean up partially-created Listener ${logicalId} (${listenerArn}): ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: aws elbv2 delete-listener --listener-arn ${listenerArn}`);
34648
34779
  }
34649
34780
  throw innerError;
34650
34781
  }
@@ -34654,10 +34785,10 @@ var ELBv2Provider = class {
34654
34785
  };
34655
34786
  } catch (error) {
34656
34787
  const cause = error instanceof Error ? error : void 0;
34657
- throw new ProvisioningError(`Failed to create Listener ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
34788
+ throw new ProvisioningError(`Failed to create Listener ${logicalId}: ${this.maskErrorMessage(error, maskSecrets)}`, resourceType, logicalId, void 0, cause);
34658
34789
  }
34659
34790
  }
34660
- async updateListener(logicalId, physicalId, resourceType, properties, previousProperties) {
34791
+ async updateListener(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
34661
34792
  this.logger.debug(`Updating Listener ${logicalId}: ${physicalId}`);
34662
34793
  try {
34663
34794
  const defaultActions = this.convertActions(properties["DefaultActions"]);
@@ -34679,7 +34810,7 @@ var ELBv2Provider = class {
34679
34810
  await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
34680
34811
  ListenerArn: physicalId,
34681
34812
  Attributes: submittedAttrs
34682
- })), logicalId, { logger: this.logger });
34813
+ })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
34683
34814
  this.logger.debug(`Applied ${submittedAttrs.length} ListenerAttributes change(s) for ${logicalId}`);
34684
34815
  }
34685
34816
  await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
@@ -34691,7 +34822,7 @@ var ELBv2Provider = class {
34691
34822
  };
34692
34823
  } catch (error) {
34693
34824
  const cause = error instanceof Error ? error : void 0;
34694
- throw new ProvisioningError(`Failed to update Listener ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
34825
+ throw new ProvisioningError(`Failed to update Listener ${logicalId}: ${this.maskErrorMessage(error, maskSecrets)}`, resourceType, logicalId, physicalId, cause);
34695
34826
  }
34696
34827
  }
34697
34828
  async deleteListener(logicalId, physicalId, resourceType, context) {
@@ -34844,13 +34975,29 @@ var ELBv2Provider = class {
34844
34975
  * Absent for a `lambda` target group (no port), where both sides stay
34845
34976
  * undefined and therefore still key identically.
34846
34977
  */
34847
- convertTargets(raw, groupPort) {
34978
+ /**
34979
+ * `maskSecrets` (issue #2050) exists for the drop-warning below, which
34980
+ * stringifies a whole REJECTED `Targets` element at DEFAULT verbosity. That
34981
+ * element came out of the resolved `properties` bag, so a
34982
+ * `{{resolve:secretsmanager:...}}` scalar anywhere inside it — an `Id` built
34983
+ * by `Fn::Sub` from a secret, say — is already plaintext by the time this
34984
+ * runs.
34985
+ *
34986
+ * OPTIONAL, but all three call sites supply it today (`createTargetGroup` and
34987
+ * both `updateTargetGroup` sides). It stays optional because that is the
34988
+ * `SecretMaskingContext` contract — absent means unmasked — not because some
34989
+ * caller is known to omit it. An earlier revision of this comment claimed it
34990
+ * protected "the `readCurrentState` / diff callers that have no masker";
34991
+ * there are no such callers, and describing a defence by a consumer that does
34992
+ * not exist is how the next author concludes the parameter is dead.
34993
+ */
34994
+ convertTargets(raw, groupPort, maskSecrets) {
34848
34995
  const defaultPort = groupPort === void 0 || groupPort === null || Number.isNaN(Number(groupPort)) ? void 0 : Number(groupPort);
34849
34996
  if (!Array.isArray(raw)) return [];
34850
34997
  const out = [];
34851
34998
  for (const entry of raw) {
34852
34999
  if (entry === null || typeof entry !== "object" || typeof entry.Id !== "string" || entry.Id.length === 0) {
34853
- this.logger.warn(`Dropping malformed TargetGroup Targets entry (missing string Id): ${JSON.stringify(entry)}`);
35000
+ this.logger.warn(`Dropping malformed TargetGroup Targets entry (missing string Id): ${JSON.stringify(maskDeep(entry, maskerOrIdentity(maskSecrets)))}`);
34854
35001
  continue;
34855
35002
  }
34856
35003
  const e = entry;
@@ -39887,8 +40034,7 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
39887
40034
  const enablesMfaFactor = declaresFactor || request.EmailMfaConfiguration !== void 0;
39888
40035
  request.MfaConfiguration = declaredMfaConfiguration || (enablesMfaFactor ? "OPTIONAL" : "OFF");
39889
40036
  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.`);
39890
- const anyFactorBlock = request.SmsMfaConfiguration !== void 0 || request.SoftwareTokenMfaConfiguration !== void 0 || request.EmailMfaConfiguration !== void 0;
39891
- if (request.MfaConfiguration === "OFF" && anyFactorBlock) 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.`);
40037
+ const anyFactorBlock = hasAnyMfaFactorBlock(request);
39892
40038
  const maskDeep = (v, depth = 0) => {
39893
40039
  if (typeof v === "string") return maskSecrets(v);
39894
40040
  if (depth >= MASK_WALK_MAX_DEPTH) return v;
@@ -39899,7 +40045,7 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
39899
40045
  const dropped = unrecognizedFactors.map((f) => JSON.stringify(maskDeep(f)) ?? String(f));
39900
40046
  if (enabledMfasMalformed) dropped.push(`${JSON.stringify(maskDeep(properties["EnabledMfas"]))} (not a list)`);
39901
40047
  if (dropped.length > 0) {
39902
- 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`;
40048
+ 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`;
39903
40049
  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}.`);
39904
40050
  }
39905
40051
  return request;
@@ -39928,6 +40074,248 @@ function resolveSentMfaConfiguration(properties, declaredMfaConfiguration) {
39928
40074
  return declaredMfaConfiguration || void 0;
39929
40075
  }
39930
40076
  /**
40077
+ * "This `SetUserPoolMfaConfig` request carries an MFA FACTOR sub-block."
40078
+ *
40079
+ * ONE definition, shared by `buildMfaConfigRequest`'s dropped-entry warning and
40080
+ * by the pre-flight refusal below, so the message and the refusal cannot
40081
+ * disagree about the same request. It is precisely
40082
+ * `{Sms, SoftwareToken, Email}MfaConfiguration` -- `WebAuthnConfiguration` is
40083
+ * deliberately NOT a member, because WebAuthn is not an MFA factor and rides
40084
+ * alongside `MfaConfiguration: OFF` on every passkey-only pool (issue #1920).
40085
+ */
40086
+ function hasAnyMfaFactorBlock(request) {
40087
+ return request.SmsMfaConfiguration !== void 0 || request.SoftwareTokenMfaConfiguration !== void 0 || request.EmailMfaConfiguration !== void 0;
40088
+ }
40089
+ /**
40090
+ * The `Policies.SignInPolicy.AllowedFirstAuthFactors` members AWS refuses to
40091
+ * combine with MFA (issue #1975).
40092
+ *
40093
+ * A DENY-list, deliberately, even though the AWS message states an ALLOW-list
40094
+ * ("Only PASSWORD and WEB_AUTHN (if configured) can be enabled as an auth
40095
+ * factor if MFA is enabled"). Written from the allow-list, a member AWS adds
40096
+ * LATER would be refused by cdkd on the day it starts working -- the same
40097
+ * reason the unrecognized-`EnabledMfas` case in `buildMfaConfigRequest` warns
40098
+ * rather than throws, since that set is a hardcoded mirror of an AWS enum. The
40099
+ * cost of the deny-list is the reverse and is the cheaper one: a future
40100
+ * incompatible factor is simply not pre-flighted, which is the pre-#1975
40101
+ * behavior (a loud AWS rejection), not a regression.
40102
+ *
40103
+ * The two members ARE the whole of today's `AuthFactorType` enum outside the
40104
+ * allowed pair (`EMAIL_OTP`, `PASSWORD`, `SMS_OTP`, `WEB_AUTHN` --
40105
+ * `@aws-sdk/client-cognito-identity-provider`), so the deny-list and the
40106
+ * allow-list agree on every value that exists right now.
40107
+ *
40108
+ * BOTH members are MEASURED (us-east-1, 2026-08-19, issue #1975) -- neither is
40109
+ * extrapolated from the other, which is the failure mode this file's own
40110
+ * unrecognized-factor comment warns about:
40111
+ *
40112
+ * [PASSWORD, EMAIL_OTP] + SetUserPoolMfaConfig(ON) -> REJECTED
40113
+ * [PASSWORD, SMS_OTP] + SetUserPoolMfaConfig(ON) -> REJECTED, same message
40114
+ *
40115
+ * The `SMS_OTP` probe needed a precondition worth recording, because without it
40116
+ * the interesting call is never reached: on a pool with NO `SmsConfiguration`,
40117
+ * `CreateUserPool` refuses the factor outright ("SMS_OTP can not be configured
40118
+ * as AuthFactor as user pool is missing valid SMS configuration"), so the probe
40119
+ * had to stand up an SNS-caller IAM role + ExternalId first. That earlier,
40120
+ * different refusal is AWS's own and is NOT something this pre-flight
40121
+ * duplicates.
40122
+ *
40123
+ * Typed `satisfies AuthFactorType[]` rather than left as `string[]`: the values
40124
+ * mirror an SDK enum, so a rename upstream should be a COMPILE error here
40125
+ * rather than a silently-never-matching deny-list.
40126
+ */
40127
+ const MFA_INCOMPATIBLE_FIRST_AUTH_FACTORS = ["EMAIL_OTP", "SMS_OTP"];
40128
+ /**
40129
+ * `Policies.SignInPolicy.AllowedFirstAuthFactors` as a list of STRINGS, or an
40130
+ * empty list when the template declares nothing usable there.
40131
+ *
40132
+ * Every mis-shape reads as ABSENT rather than as a violation: this feeds a
40133
+ * REFUSAL, so guessing at a container cdkd could not read would block a deploy
40134
+ * over a value it never understood. A genuinely broken `Policies` blob is AWS's
40135
+ * to reject, with its own message.
40136
+ *
40137
+ * The two containers are traversed with `?.` rather than with the
40138
+ * `typeof x === 'object' && !Array.isArray(x)` guard `config-shape.ts` uses,
40139
+ * and that is deliberate rather than sloppy: those guards are load-bearing
40140
+ * THERE because the helper REFUSES a non-object, while here the only outcome is
40141
+ * "no factors", which is exactly what indexing a string / number / array
40142
+ * already yields. The version carrying them was written first and MEASURED
40143
+ * inert -- deleting either clause failed no test and could not be made to,
40144
+ * since no template value indexes to anything at those keys. `?.` keeps the one
40145
+ * behaviour that is real (a declared `null` throws on a bare index) and leaves
40146
+ * nothing unfalsifiable behind.
40147
+ *
40148
+ * The two filters below are NOT the same kind of thing, and saying so matters
40149
+ * because an earlier revision of this comment claimed both were load-bearing and
40150
+ * "each fenced by a test" -- which was false for one of them, and a false fence
40151
+ * claim is what stops the next reader re-checking.
40152
+ *
40153
+ * - `Array.isArray` is a RUNTIME guard and IS fenced: without it a scalar
40154
+ * `AllowedFirstAuthFactors: 'EMAIL_OTP'` is read as a declaration and
40155
+ * refused, and the test for that shape reds when it is dropped.
40156
+ * - The string filter is a TYPE NARROWING and is INERT at runtime. `includes`
40157
+ * compares by SameValueZero, so a non-string member can never equal a
40158
+ * deny-list entry however it is spelled; deleting the filter fails NO test,
40159
+ * and no test could be written that it would fail. It earns its place by
40160
+ * giving the caller a `string[]`, and by keeping this function honest if a
40161
+ * future caller ever COERCES a member instead of comparing it. The
40162
+ * nested-list case in the suite fences that hypothetical `String(member)`
40163
+ * implementation -- i.e. the mistake, not this code.
40164
+ */
40165
+ function readAllowedFirstAuthFactors(properties) {
40166
+ const factors = (properties["Policies"]?.["SignInPolicy"])?.["AllowedFirstAuthFactors"];
40167
+ if (!Array.isArray(factors)) return [];
40168
+ return factors.filter((factor) => typeof factor === "string");
40169
+ }
40170
+ /**
40171
+ * The PRE-FLIGHT: the reason this user-pool configuration cannot be applied at
40172
+ * all, or `undefined` when nothing is known to refuse it.
40173
+ *
40174
+ * Two rules, both for combinations AWS rejects 100% of the time, and both
40175
+ * raised BEFORE the first AWS call on BOTH the create and the update path. The
40176
+ * ordering is what makes them worth refusing rather than warning about: on the
40177
+ * UPDATE path `UpdateUserPool` lands FIRST and `SetUserPoolMfaConfig` is
40178
+ * refused afterwards, so sending the request leaves a PARTIAL APPLY behind --
40179
+ * with the security-relevant half being the half that did NOT apply (the MFA
40180
+ * configuration), while the half that did is the one loosening authentication.
40181
+ * Nothing unwinds it, and a retry cannot succeed until the user reverse-engineers
40182
+ * an AWS-worded error about a field they did not think they were changing.
40183
+ *
40184
+ * Rule 1 (issue #1977) -- `MfaConfiguration` resolves to OFF while the request
40185
+ * carries an MFA factor block. MEASURED us-east-1 2026-08-19 (issue #1968):
40186
+ *
40187
+ * {ON, SoftwareTokenMfaConfiguration: {Enabled: true}} -> ACCEPTED (control)
40188
+ * {OFF, SoftwareTokenMfaConfiguration: {Enabled: true}} -> rejected
40189
+ * {OFF, SoftwareTokenMfaConfiguration: {Enabled: false}} -> rejected
40190
+ * {OFF, EmailMfaConfiguration: {Message, Subject}} -> rejected
40191
+ *
40192
+ * all with `InvalidParameterException: Invalid MFA configuration given, can't
40193
+ * turn off MFA and configure an MFA together`. The `Enabled: false` arm settles
40194
+ * the KEY: a block that enables NOTHING is still refused, so the rule is about
40195
+ * the block's PRESENCE -- exactly what `hasAnyMfaFactorBlock` tests -- and
40196
+ * since that predicate is precisely the three factor sub-blocks, no accepted
40197
+ * case survives. (Bound on the Email arm: an OPTIONAL control with that block
40198
+ * failed with a DIFFERENT error, "Cannot set EmailMfaConfiguration when user
40199
+ * pool EmailConfiguration contains an EmailSendingAccount of COGNITO_DEFAULT",
40200
+ * so it has no isolated control of its own -- informative in itself, since it
40201
+ * shows the exclusion is evaluated BEFORE the email-sending-account check.)
40202
+ *
40203
+ * Rule 2 (issue #1975) -- the `SetUserPoolMfaConfig` request resolves
40204
+ * `MfaConfiguration` to ON while `Policies.SignInPolicy.AllowedFirstAuthFactors`
40205
+ * allows `EMAIL_OTP` or `SMS_OTP`. AWS refuses with `Only PASSWORD and WEB_AUTHN
40206
+ * (if configured) can be enabled as an auth factor if MFA is enabled`. MEASURED
40207
+ * us-east-1 2026-08-19, both members and both MFA modes:
40208
+ *
40209
+ * {[PASSWORD, EMAIL_OTP], ON} -> rejected
40210
+ * {[PASSWORD, EMAIL_OTP], OPTIONAL} -> ACCEPTED
40211
+ * {[PASSWORD, SMS_OTP], ON} -> rejected (pool WITH a valid SmsConfiguration)
40212
+ * {[PASSWORD, SMS_OTP], OPTIONAL} -> ACCEPTED
40213
+ *
40214
+ * So `=== 'ON'` is MEASURED-CORRECT rather than merely conservative: OPTIONAL is
40215
+ * ACCEPTED, and widening this to `!== 'OFF'` would REFUSE A WORKING DEPLOY. An
40216
+ * earlier revision of this comment called OPTIONAL unmeasured; it is not, and
40217
+ * the measurement is what settles the narrowing rather than a cost argument.
40218
+ *
40219
+ * WHY THE MESSAGE DOES NOT OFFER `WEB_AUTHN` AS THE ESCAPE HATCH, even though
40220
+ * AWS's own error text names it as accepted. MEASURED us-east-1 2026-08-20: a
40221
+ * pool allowing `WEB_AUTHN` as a first auth factor is REJECTED by
40222
+ * `SetUserPoolMfaConfig(ON, SoftwareTokenMfa)` --
40223
+ *
40224
+ * InvalidParameterException: Cannot set WebAuthn factor configuration to
40225
+ * SINGLE_FACTOR if MFA is required and WebAuthn is an allowed first auth factor
40226
+ *
40227
+ * -- with NO WebAuthn block at all, with `UserVerification: preferred`, and with
40228
+ * `required`. The only accepted shape is
40229
+ * `WebAuthnConfiguration.FactorConfiguration = MULTI_FACTOR_WITH_USER_VERIFICATION`,
40230
+ * and the PINNED SDK (`@aws-sdk/client-cognito-identity-provider` 3.1018.0)
40231
+ * does not carry that field -- `WebAuthnConfigurationType` is exactly
40232
+ * `{RelyingPartyId?, UserVerification?}` -- so cdkd cannot reach it today.
40233
+ * Telling the user to switch to `WEB_AUTHN` would therefore be advice that
40234
+ * cannot be followed, which is why the remedy names removing the factor or
40235
+ * lowering `MfaConfiguration` instead.
40236
+ *
40237
+ * That same measurement means `WEB_AUTHN` + ON reproduces this very
40238
+ * partial-apply shape while NOT being on the deny-list. Deliberately left
40239
+ * uncovered: the correct rule there is CONDITIONAL on
40240
+ * `WebAuthnConfiguration.FactorConfiguration`, which the pinned SDK cannot
40241
+ * express, so a flat deny-list entry would be an OVER-REFUSAL of the shape that
40242
+ * becomes valid the moment the SDK gains the field. Tracked as issue #2064.
40243
+ *
40244
+ * SCOPE LIMIT, deliberate and load-bearing: rule 2 fires only when a
40245
+ * `SetUserPoolMfaConfig` request is actually BUILT (`hasMfaConfigProps`), i.e.
40246
+ * the template declares `EnabledMfas` / an email-OTP message or subject /
40247
+ * WebAuthn. With NONE of those, `MfaConfiguration` rides on the SINGLE
40248
+ * `CreateUserPool` / `UpdateUserPool` call and no second call follows -- so
40249
+ * there is no two-call window and structurally NOTHING to partly apply, which
40250
+ * is the entire harm this pre-flight exists to prevent. Refusing there would
40251
+ * ALSO be an over-refusal on a shape AWS accepts: a pool created with
40252
+ * `AllowedFirstAuthFactors: [PASSWORD, EMAIL_OTP]` and no MFA configuration
40253
+ * call at all is ACCEPTED (measured, same session -- the pool came up fine and
40254
+ * only the later `SetUserPoolMfaConfig(ON)` failed). Such a template is left to
40255
+ * AWS, which answers it atomically.
40256
+ *
40257
+ * CLOUDFORMATION PARITY, settled by A/B rather than assumed -- both issues made
40258
+ * this an explicit precondition. CloudFormation hits the SAME rejections and
40259
+ * rolls the stack back:
40260
+ *
40261
+ * {MfaConfiguration: OFF, EnabledMfas: [SOFTWARE_TOKEN_MFA]}
40262
+ * -> ROLLBACK_COMPLETE, "Invalid MFA configuration given, can't turn off
40263
+ * MFA and configure an MFA together"
40264
+ * {MfaConfiguration: ON, EnabledMfas: [SOFTWARE_TOKEN_MFA],
40265
+ * AllowedFirstAuthFactors: [PASSWORD, EMAIL_OTP]} (ESSENTIALS tier)
40266
+ * -> ROLLBACK_COMPLETE, "Only PASSWORD and WEB_AUTHN..."
40267
+ *
40268
+ * So refusing is PARITY-PRESERVING, not a divergence: cdkd refuses the same SET
40269
+ * of templates CloudFormation refuses, only EARLIER (before the first API call
40270
+ * instead of after a partial apply) and in cdkd's own wording. Nothing that
40271
+ * deploys under CloudFormation is blocked here.
40272
+ *
40273
+ * Both rules read the value cdkd would SEND rather than the raw template value,
40274
+ * from ONE `buildMfaConfigRequest` call, so the refusal and the request can
40275
+ * never disagree about what OFF / ON means for a given bag. The build runs with
40276
+ * NO logger, so the pre-flight is silent and `applyMfaConfig`'s own warnings
40277
+ * still fire exactly once.
40278
+ *
40279
+ * WHAT THIS CANNOT SEE, stated because the message says "refuses it before
40280
+ * sending anything" and that reads as a completeness claim: the pre-flight
40281
+ * inspects the TEMPLATE, never the live pool, so two template edits reach the
40282
+ * same partial apply without tripping it -- DELETING the `SignInPolicy` block
40283
+ * while setting `MfaConfiguration: ON`, and OMITTING `MfaConfiguration` while
40284
+ * adding `EMAIL_OTP` to a pool whose live MFA is already ON. Both need a
40285
+ * live-state guard and are tracked in #2051.
40286
+ *
40287
+ * Deliberately UNCONDITIONAL -- no `CreateContext.replayingState` downgrade.
40288
+ * `.claude/rules/providers.md` says a create-path pre-flight refusal MUST
40289
+ * downgrade on a replay, so this is a STATED EXCEPTION rather than an
40290
+ * oversight, and the rule's own reasoning is what licenses it: the downgrade
40291
+ * exists because a refusal against a STATE record leaves a resource
40292
+ * un-rollbackable with no template-side remedy. That presupposes the replay
40293
+ * could otherwise SUCCEED. Here it cannot -- both combinations are rejected by
40294
+ * AWS 100% of the time (measured above, and CloudFormation rolls back on the
40295
+ * same templates), so downgrading would trade a clear cdkd-worded refusal for
40296
+ * the identical failure arriving later from AWS, on the create path with a
40297
+ * pool to roll back and on the update path with a partial apply. A state record
40298
+ * additionally cannot legitimately HOLD either combination: state is written
40299
+ * only after a SUCCESSFUL apply, and neither can be applied. Revisit this if
40300
+ * AWS ever starts accepting one of them.
40301
+ */
40302
+ function describeUnsupportedMfaCombination(properties, declaredMfaConfiguration) {
40303
+ const request = buildMfaConfigRequest("", properties, void 0, declaredMfaConfiguration);
40304
+ if (request && request.MfaConfiguration === "OFF" && hasAnyMfaFactorBlock(request)) {
40305
+ const blocks = [
40306
+ ...request.SmsMfaConfiguration !== void 0 ? ["SmsMfaConfiguration"] : [],
40307
+ ...request.SoftwareTokenMfaConfiguration !== void 0 ? ["SoftwareTokenMfaConfiguration"] : [],
40308
+ ...request.EmailMfaConfiguration !== void 0 ? ["EmailMfaConfiguration"] : []
40309
+ ];
40310
+ 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.)`;
40311
+ }
40312
+ if (request && request.MfaConfiguration === "ON") {
40313
+ const denied = MFA_INCOMPATIBLE_FIRST_AUTH_FACTORS;
40314
+ const offending = readAllowedFirstAuthFactors(properties).filter((factor) => denied.includes(factor));
40315
+ if (offending.length > 0) return `${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.)`;
40316
+ }
40317
+ }
40318
+ /**
39931
40319
  * Replace a DECLARED-but-unusable `MfaConfiguration` with the value cdkd
39932
40320
  * actually sends, or drop the key when nothing is sent. Returns the input
39933
40321
  * object unchanged when nothing applies, so the common case compares
@@ -40011,7 +40399,7 @@ var CognitoUserPoolProvider = class {
40011
40399
  "WebAuthnRelyingPartyID",
40012
40400
  "WebAuthnUserVerification"
40013
40401
  ])]]);
40014
- unhandledByDesign = /* @__PURE__ */ new Map([["AWS::Cognito::UserPool", /* @__PURE__ */ new Map([["WebAuthnFactorConfiguration", "No SDK wire path: @aws-sdk/client-cognito-identity-provider has no field accepting SINGLE_FACTOR | MULTI_FACTOR_WITH_USER_VERIFICATION (not on CreateUserPool/UpdateUserPool, nor SetUserPoolMfaConfig.WebAuthnConfiguration which only carries RelyingPartyId/UserVerification); CC-API-registry-only property"]])]]);
40402
+ unhandledByDesign = /* @__PURE__ */ new Map([["AWS::Cognito::UserPool", /* @__PURE__ */ new Map([["WebAuthnFactorConfiguration", "No wire path in the PINNED SDK (@aws-sdk/client-cognito-identity-provider 3.1018.0): WebAuthnConfigurationType is {RelyingPartyId?, UserVerification?} and no CreateUserPool/UpdateUserPool field accepts SINGLE_FACTOR | MULTI_FACTOR_WITH_USER_VERIFICATION. This is an SDK-VERSION limit, NOT an API one -- the live API does honour FactorConfiguration (measured us-east-1 2026-08-20: SetUserPoolMfaConfig(ON) on a pool allowing WEB_AUTHN as a first auth factor is rejected with \"Cannot set WebAuthn factor configuration to SINGLE_FACTOR if MFA is required and WebAuthn is an allowed first auth factor\", i.e. the service reads a field the SDK cannot send). RE-EVALUATE THIS ENTRY ON AN SDK BUMP: if WebAuthnConfigurationType gains FactorConfiguration, the property becomes handleable and this entry must go"]])]]);
40015
40403
  /**
40016
40404
  * Warn when `MfaConfiguration` is declared as a BLANK string (issue #1925
40017
40405
  * item 2, review round 2).
@@ -40040,6 +40428,26 @@ var CognitoUserPoolProvider = class {
40040
40428
  this.logger.warn("AWS::Cognito::UserPool MfaConfiguration is an empty string, which declares no MFA mode and is treated as absent -- a collapsed Fn::Join or an empty String parameter produces this shape. The pool deploys with the default MFA configuration instead; declare ON / OPTIONAL / OFF explicitly, or omit the property, to say which was meant.");
40041
40429
  }
40042
40430
  /**
40431
+ * Raise {@link describeUnsupportedMfaCombination}'s verdict as a typed
40432
+ * refusal, or return silently when there is nothing to refuse.
40433
+ *
40434
+ * A `ProvisioningError` so both call sites behave the way each already does
40435
+ * with the sibling `MfaConfiguration` shape refusal: on the create path it
40436
+ * surfaces as the provider's own error rather than an AWS one, and on the
40437
+ * update path `update()`'s catch re-throws an already-wrapped
40438
+ * `ProvisioningError` unchanged, so it is not double-wrapped. Both call sites
40439
+ * are OUTSIDE their method's try, so the throw travels untouched either way.
40440
+ *
40441
+ * `target` is the pool NAME on create (there is no physical id yet) and the
40442
+ * physical id on update, matching what each path passes to its other
40443
+ * `ProvisioningError`s.
40444
+ */
40445
+ assertMfaCombinationApplicable(logicalId, resourceType, target, properties, declaredMfaConfiguration) {
40446
+ const reason = describeUnsupportedMfaCombination(properties, declaredMfaConfiguration);
40447
+ if (reason === void 0) return;
40448
+ throw new ProvisioningError(reason, resourceType, logicalId, target);
40449
+ }
40450
+ /**
40043
40451
  * The DIFF-side half of the `MfaConfiguration` substitution record.
40044
40452
  *
40045
40453
  * Applied by `DiffCalculator` to BOTH comparison sides, so a template that
@@ -40129,6 +40537,7 @@ var CognitoUserPoolProvider = class {
40129
40537
  } catch (error) {
40130
40538
  throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, poolName, error instanceof Error ? error : void 0);
40131
40539
  }
40540
+ this.assertMfaCombinationApplicable(logicalId, resourceType, poolName, properties, mfaConfiguration);
40132
40541
  let createdUserPoolId;
40133
40542
  try {
40134
40543
  const createParams = { PoolName: poolName };
@@ -40298,7 +40707,8 @@ var CognitoUserPoolProvider = class {
40298
40707
  * sub-block in the request AWS REJECTS the call; with none it accepts and
40299
40708
  * resets the pool to MFA-disabled. An EXPLICIT OFF beside a block is
40300
40709
  * rejected the same way (2026-08-19, issue #1968; the transcript lives at
40301
- * the pinned-OFF warning in `buildMfaConfigRequest`).
40710
+ * `describeUnsupportedMfaCombination`, which since issue #1977 REFUSES that
40711
+ * combination before any call rather than warning about it).
40302
40712
  *
40303
40713
  * Nothing here licenses a claim about a field on neither list, or about one
40304
40714
  * API from the other's section. Measure it.
@@ -40376,6 +40786,7 @@ var CognitoUserPoolProvider = class {
40376
40786
  this.logger.debug(`Updating Cognito User Pool ${logicalId}: ${physicalId}`);
40377
40787
  const mfaConfiguration = readDeclaredMfaConfiguration(properties["MfaConfiguration"], { onUnusable: (message) => this.logger.warn(message) });
40378
40788
  this.warnOnBlankMfaConfiguration(properties["MfaConfiguration"]);
40789
+ this.assertMfaCombinationApplicable(logicalId, resourceType, physicalId, properties, mfaConfiguration);
40379
40790
  const liveMfa = mfaConfiguration === "" && hasMfaConfigProps(properties) ? await this.readLiveMfaConfiguration(physicalId) : void 0;
40380
40791
  try {
40381
40792
  const updateParams = { UserPoolId: physicalId };
@@ -41238,21 +41649,63 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41238
41649
  if (!this.stsClient) this.stsClient = new STSClient(this.providerRegion ? { region: this.providerRegion } : {});
41239
41650
  return this.stsClient;
41240
41651
  }
41241
- async create(logicalId, resourceType, properties) {
41652
+ /**
41653
+ * A {@link RetryLogger} for this provider's `withRetry` calls, bound to the
41654
+ * caller's secret masker (issue #2050).
41655
+ *
41656
+ * Thin wrapper over the shared {@link createMaskedRetryLogger} — see that
41657
+ * module for why the factory is shared with `elbv2-provider.ts` rather than
41658
+ * hand-rolled per file, and for why `warn` is threaded rather than omitted.
41659
+ *
41660
+ * NOT applied to `deleteNamespace`'s retry: that one sends a
41661
+ * `DeleteNamespaceCommand` carrying a physical id only, and `delete()` has no
41662
+ * `maskSecrets` to thread anyway (`DeleteContext` deliberately carries none).
41663
+ */
41664
+ maskedRetryLogger(maskSecrets) {
41665
+ return createMaskedRetryLogger(this.logger, maskSecrets);
41666
+ }
41667
+ /**
41668
+ * Mask a caught AWS error's message before it is interpolated into the
41669
+ * `ProvisioningError` this provider throws (issue #2050, review round 2).
41670
+ *
41671
+ * The twin of `ELBv2Provider.maskErrorMessage` — see that method for the full
41672
+ * rationale. In short: `withRetry` rethrows the RAW error and the deploy
41673
+ * engine prints the resulting message at ERROR (DEFAULT verbosity), so this
41674
+ * is a wider disclosure surface than the retry logger, and the ONLY one for a
41675
+ * NON-RETRYABLE rejection, where `withRetry` emits nothing at all.
41676
+ *
41677
+ * Masks `error.message` rather than the assembled sentence so the masker can
41678
+ * reach `maskSecretsInText`'s WHOLE-VALUE arm (any length) instead of only
41679
+ * the SUBSTRING arm (needles of 4+ characters). The `cause` chain is left
41680
+ * untouched so `isRetryableTransientError`'s `$metadata` walk is unaffected.
41681
+ */
41682
+ maskErrorMessage(error, maskSecrets) {
41683
+ return maskerOrIdentity(maskSecrets)(error instanceof Error ? error.message : String(error));
41684
+ }
41685
+ /**
41686
+ * `context` is read for ONE thing today: `maskSecrets` (issue #2050). The
41687
+ * Service create path runs a post-create `UpdateServiceAttributes` through
41688
+ * `withRetry` — see {@link maskedRetryLogger}.
41689
+ */
41690
+ async create(logicalId, resourceType, properties, context) {
41242
41691
  switch (resourceType) {
41243
41692
  case "AWS::ServiceDiscovery::PrivateDnsNamespace": return this.createNamespace(logicalId, resourceType, properties);
41244
41693
  case "AWS::ServiceDiscovery::HttpNamespace": return this.createHttpNamespace(logicalId, resourceType, properties);
41245
41694
  case "AWS::ServiceDiscovery::PublicDnsNamespace": return this.createPublicDnsNamespace(logicalId, resourceType, properties);
41246
- case "AWS::ServiceDiscovery::Service": return this.createService(logicalId, resourceType, properties);
41695
+ case "AWS::ServiceDiscovery::Service": return this.createService(logicalId, resourceType, properties, context?.maskSecrets);
41247
41696
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
41248
41697
  }
41249
41698
  }
41250
- update(logicalId, physicalId, resourceType, properties, previousProperties) {
41699
+ /**
41700
+ * `context` is read for ONE thing today: `maskSecrets` (issue #2050) — the
41701
+ * update-path twin of the `create()` note above.
41702
+ */
41703
+ update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
41251
41704
  switch (resourceType) {
41252
41705
  case "AWS::ServiceDiscovery::PrivateDnsNamespace": return this.updateNamespace(logicalId, physicalId, resourceType, properties, previousProperties);
41253
41706
  case "AWS::ServiceDiscovery::HttpNamespace": return this.updateHttpNamespace(logicalId, physicalId, resourceType, properties, previousProperties);
41254
41707
  case "AWS::ServiceDiscovery::PublicDnsNamespace": return this.updatePublicDnsNamespace(logicalId, physicalId, resourceType, properties, previousProperties);
41255
- case "AWS::ServiceDiscovery::Service": return this.updateService(logicalId, physicalId, resourceType, properties, previousProperties);
41708
+ case "AWS::ServiceDiscovery::Service": return this.updateService(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
41256
41709
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
41257
41710
  }
41258
41711
  }
@@ -41540,7 +41993,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41540
41993
  throw new ProvisioningError(`Failed to update public DNS namespace ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
41541
41994
  }
41542
41995
  }
41543
- async createService(logicalId, resourceType, properties) {
41996
+ async createService(logicalId, resourceType, properties, maskSecrets) {
41544
41997
  this.logger.debug(`Creating service discovery service ${logicalId}`);
41545
41998
  const client = this.getClient();
41546
41999
  const name = properties["Name"];
@@ -41572,7 +42025,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41572
42025
  await withRetry(() => client.send(new UpdateServiceAttributesCommand({
41573
42026
  ServiceId: serviceId,
41574
42027
  Attributes: attrs
41575
- })), logicalId, { logger: this.logger });
42028
+ })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
41576
42029
  this.logger.debug(`Applied ${Object.keys(attrs).length} ServiceAttribute(s) for ${logicalId}`);
41577
42030
  }
41578
42031
  } catch (innerError) {
@@ -41580,7 +42033,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41580
42033
  await client.send(new DeleteServiceCommand$1({ Id: serviceId }));
41581
42034
  this.logger.debug(`Cleaned up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure`);
41582
42035
  } catch (cleanupError) {
41583
- this.logger.warn(`Failed to clean up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws servicediscovery delete-service --id ${serviceId}`);
42036
+ this.logger.warn(`Failed to clean up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure: ${this.maskErrorMessage(cleanupError, maskSecrets)}. Manual deletion may be required before the next deploy: aws servicediscovery delete-service --id ${serviceId}`);
41584
42037
  }
41585
42038
  throw innerError;
41586
42039
  }
@@ -41594,7 +42047,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41594
42047
  };
41595
42048
  } catch (error) {
41596
42049
  const cause = error instanceof Error ? error : void 0;
41597
- throw new ProvisioningError(`Failed to create service discovery service ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
42050
+ throw new ProvisioningError(`Failed to create service discovery service ${logicalId}: ${this.maskErrorMessage(error, maskSecrets)}`, resourceType, logicalId, void 0, cause);
41598
42051
  }
41599
42052
  }
41600
42053
  /**
@@ -41614,7 +42067,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41614
42067
  * full AWS-current snapshot as `properties`, so the round-trip is
41615
42068
  * value-preserving.
41616
42069
  */
41617
- async updateService(logicalId, physicalId, resourceType, properties, previousProperties) {
42070
+ async updateService(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
41618
42071
  this.logger.debug(`Updating service discovery service ${logicalId}: ${physicalId}`);
41619
42072
  const client = this.getClient();
41620
42073
  const serviceChange = {};
@@ -41649,14 +42102,14 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41649
42102
  await withRetry(() => client.send(new UpdateServiceAttributesCommand({
41650
42103
  ServiceId: physicalId,
41651
42104
  Attributes: upsertAttrs
41652
- })), logicalId, { logger: this.logger });
42105
+ })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
41653
42106
  this.logger.debug(`Applied ${Object.keys(upsertAttrs).length} ServiceAttribute change(s) for ${logicalId}`);
41654
42107
  }
41655
42108
  if (hasAttrRemove) {
41656
42109
  await withRetry(() => client.send(new DeleteServiceAttributesCommand({
41657
42110
  ServiceId: physicalId,
41658
42111
  Attributes: removedAttrKeys
41659
- })), logicalId, { logger: this.logger });
42112
+ })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
41660
42113
  this.logger.debug(`Removed ${removedAttrKeys.length} ServiceAttribute(s) for ${logicalId}`);
41661
42114
  }
41662
42115
  this.logger.debug(`Successfully updated service discovery service ${logicalId}`);
@@ -41667,7 +42120,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
41667
42120
  } catch (error) {
41668
42121
  if (error instanceof ProvisioningError) throw error;
41669
42122
  const cause = error instanceof Error ? error : void 0;
41670
- throw new ProvisioningError(`Failed to update service discovery service ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
42123
+ throw new ProvisioningError(`Failed to update service discovery service ${logicalId}: ${this.maskErrorMessage(error, maskSecrets)}`, resourceType, logicalId, physicalId, cause);
41671
42124
  }
41672
42125
  }
41673
42126
  async deleteService(logicalId, physicalId, resourceType, context) {
@@ -82709,7 +83162,7 @@ function createMigrateCommand() {
82709
83162
  */
82710
83163
  function buildProgram() {
82711
83164
  const program = new Command();
82712
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.12");
83165
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.14");
82713
83166
  program.hook("preAction", (_thisCommand, actionCommand) => {
82714
83167
  const { profile } = actionCommand.optsWithGlobals();
82715
83168
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;