@go-to-k/cdkd 0.283.24 → 0.283.26

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 coerceCfnBoolean, $t as resolveCaptureObservedState, A as cyan, An as LocalStartServiceError, At as rewriteTemplateAssetReferences, B as findSilentDropProperties, Bt as buildDockerImage, C as makeCanonicalizePropertiesFn, Ct as shouldRetainResource, D as renderStatefulReason, Dn as DeployCancelledError, Dt as buildAssetRedirectMap, E as isStatefulRecreateTargetSync, Et as WorkGraph, F as IAMRoleProvider, Fn as ProvisioningError, Ft as getBootstrapMarkerKey, G as IntrinsicFunctionResolver, Gn as withErrorHandling, Gt as AssetManifestLoader, H as slowCcOperationTimeoutMs, Ht as getDockerCmd, I as collectInlinePolicyNamesManagedBySiblings, In as ResourceTimeoutError, It as parseBootstrapMarker, J as refStateLookupFromResource, Jn as isThrottlingError, Jt as synthesisStatusMessage, K as cfnRefValueFromPhysicalId, Kn as isMarkedNonRetryable, Kt as getDockerImageBySourceHash, L as clearOnUpdateRemoval, Ln as ResourceUpdateNotSupportedError, Lt as validateAssetBucketName, M as green, Mn as MissingCdkCliError, Mt as AssetModeResolver, N as red, Nn as NestedStackChildDirectDestroyError, Nt as BOOTSTRAP_MARKER_PREFIX, O as formatResourceLine, On as LocalInvokeBuildError$1, Ot as createAssetRedirectResolver, P as yellow, Pn as PartialFailureError, Pt as ensureAssetStorage, Q as assertRegionMatch, Qt as resolveAutoAssetStorage, R as ProviderRegistry, Rn as StackHasActiveImportsError, Rt as validateContainerRepoName, S as unsupportedFinalSnapshotError, Sn as setAwsClients, St as rebuildClientForBucketRegion, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tt as stringifyValue, U as disableInstanceApiTermination, Ut as runDockerForeground, V as CloudControlProvider, Vt as formatDockerLoginError, W as isTerminationProtectionPropagationError, Wn as normalizeAwsError, Wt as runDockerStreaming, X as normalizeAwsTagsToCfn, Xt as getLegacyStateBucketName, Y as WAFv2WebACLProvider, Yn as markNonRetryable, Yt as getDefaultStateBucketName, Z as resolveExplicitPhysicalId, Zt as resolveApp, _ as buildFinalSnapshotIdentifier, _t as withRetry, a as DeploymentEventsStore, an as warnDeprecatedNoPrefixCliFlag, at as requireConfigObject, b as isFinalSnapshotError, bn as getAwsClients, bt as LockManager, c as replayFailedOperations, cn as MIGRATE_TMP_PREFIX, ct as scrubResourceRecord, d as deleteSkipReason, dn as expectedOwnerParam, dt as s3BucketDualStackDomainName, en as resolveSkipPrefix, et as configBooleanRefusal, f as withResourceDeadline, fn as PARTITION_TABLE, ft as s3BucketRegionalDomainName, g as PRE_DELETE_SNAPSHOT_TYPES, gn as processStackMessages, gt as describeTypeWithThrottleRetry, h as ATOMIC_FINAL_SNAPSHOT_TYPES, hn as AssemblyReader, ht as DiffCalculator, i as DeploymentEventsReader, in as stateBucketExistenceConfirmed, it as requireConfigArray, j as gray, jt as escapeRegExp, k as bold, kn as LocalMigrateError, kt as loadPublishableAssetManifest, l as replayRollback, ln as findLargeInlineResources, lt as s3BucketArn, m as computeImplicitDeleteEdges, mn as derivePartitionAndUrlSuffix, mt as applyRoleArnIfSet, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveStateBucketWithDefaultAndSource, nt as readConfigString, o as planFailedOps, on as CFN_TEMPLATE_BODY_LIMIT, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as canonicalizeRegion, pt as s3BucketWebsiteUrl, q as getAccountInfo, qn as isRetryableTransientError, qt as Synthesizer, r as DeployEngine, rn as resolveUseCdkBootstrapAssets, rt as replayWarn, s as planRollback, sn as CFN_TEMPLATE_URL_LIMIT, st as redactSecretsForState, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveStateBucketWithDefault, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as uploadCfnTemplate, ut as s3BucketDomainName, v as ccRoutedFinalSnapshotError, vn as resolveBucketRegion, vt as DagBuilder, w as extractDeploymentEventError, wn as CdkdError, wt as AssetPublisher, x as refusesFinalSnapshot, xn as resetAwsClients, xt as S3StateBackend, y as createPreDeleteFinalSnapshot, yn as AwsClients, yt as TemplateParser, z as findActionableSilentDrops, zn as StackTerminationProtectionError, zt as buildDenyExternalAccessPolicy } from "./deploy-engine-Cu6OO-8t.js";
2
+ import { $ as coerceCfnBoolean, $t as resolveAutoAssetStorage, A as cyan, An as LocalMigrateError, At as loadPublishableAssetManifest, B as findSilentDropProperties, Bn as StackTerminationProtectionError, Bt as buildDenyExternalAccessPolicy, C as makeCanonicalizePropertiesFn, Cn as setAwsClients, Ct as rebuildClientForBucketRegion, D as renderStatefulReason, Dt as WorkGraph, E as isStatefulRecreateTargetSync, Et as stringifyValue, F as IAMRoleProvider, Fn as PartialFailureError, Ft as ensureAssetStorage, G as IntrinsicFunctionResolver, Gn as normalizeAwsError, Gt as runDockerStreaming, H as slowCcOperationTimeoutMs, Ht as formatDockerLoginError, I as collectInlinePolicyNamesManagedBySiblings, In as ProvisioningError, It as getBootstrapMarkerKey, J as refStateLookupFromResource, Jn as isRetryableTransientError, Jt as Synthesizer, K as cfnRefValueFromPhysicalId, Kn as withErrorHandling, Kt as AssetManifestLoader, L as clearOnUpdateRemoval, Ln as ResourceTimeoutError, Lt as parseBootstrapMarker, M as green, Mt as escapeRegExp, N as red, Nn as MissingCdkCliError, Nt as AssetModeResolver, O as formatResourceLine, On as DeployCancelledError, Ot as buildAssetRedirectMap, P as yellow, Pn as NestedStackChildDirectDestroyError, Pt as BOOTSTRAP_MARKER_PREFIX, Q as assertRegionMatch, Qt as resolveApp, R as ProviderRegistry, Rn as ResourceUpdateNotSupportedError, Rt as validateAssetBucketName, S as unsupportedFinalSnapshotError, Sn as resetAwsClients, St as S3StateBackend, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tn as CdkdError, Tt as AssetPublisher, U as disableInstanceApiTermination, Ut as getDockerCmd, V as CloudControlProvider, Vt as buildDockerImage, W as isTerminationProtectionPropagationError, Wt as runDockerForeground, X as normalizeAwsTagsToCfn, Xn as markNonRetryable, Xt as getDefaultStateBucketName, Y as WAFv2WebACLProvider, Yn as isThrottlingError, Yt as synthesisStatusMessage, Z as resolveExplicitPhysicalId, Zt as getLegacyStateBucketName, _ as buildFinalSnapshotIdentifier, _n as processStackMessages, _t as describeTypeWithThrottleRetry, a as DeploymentEventsStore, an as stateBucketExistenceConfirmed, at as requireConfigObject, b as isFinalSnapshotError, bn as AwsClients, bt as TemplateParser, c as replayFailedOperations, cn as CFN_TEMPLATE_URL_LIMIT, ct as redactSecretsForState, d as deleteSkipReason, dn as uploadCfnTemplate, dt as s3BucketDomainName, en as resolveCaptureObservedState, et as configBooleanRefusal, f as withResourceDeadline, fn as expectedOwnerParam, ft as s3BucketDualStackDomainName, g as PRE_DELETE_SNAPSHOT_TYPES, gn as AssemblyReader, gt as DiffCalculator, h as ATOMIC_FINAL_SNAPSHOT_TYPES, hn as derivePartitionAndUrlSuffix, ht as applyRoleArnIfSet, i as DeploymentEventsReader, in as resolveUseCdkBootstrapAssets, it as requireConfigArray, j as gray, jn as LocalStartServiceError, jt as rewriteTemplateAssetReferences, k as bold, kn as LocalInvokeBuildError$1, kt as createAssetRedirectResolver, l as replayRollback, ln as MIGRATE_TMP_PREFIX, lt as scrubResourceRecord, m as computeImplicitDeleteEdges, mn as canonicalizeRegion, mt as s3BucketWebsiteUrl, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveStateBucketWithDefault, nt as readConfigString, o as planFailedOps, on as warnDeprecatedNoPrefixCliFlag, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as PARTITION_TABLE, pt as s3BucketRegionalDomainName, q as getAccountInfo, qn as isMarkedNonRetryable, qt as getDockerImageBySourceHash, r as DeployEngine, rn as resolveStateBucketWithDefaultAndSource, rt as replayWarn, s as planRollback, sn as CFN_TEMPLATE_BODY_LIMIT, st as TEMPLATE_SOURCED_READBACK_RULES, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveSkipPrefix, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as findLargeInlineResources, ut as s3BucketArn, v as ccRoutedFinalSnapshotError, vt as withRetry, w as extractDeploymentEventError, wt as shouldRetainResource, x as refusesFinalSnapshot, xn as getAwsClients, xt as LockManager, y as createPreDeleteFinalSnapshot, yn as resolveBucketRegion, yt as DagBuilder, z as findActionableSilentDrops, zn as StackHasActiveImportsError, zt as validateContainerRepoName } from "./deploy-engine-B6z9CCAL.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-C9Mq493c.js";
4
+ import { t as ASGProvider } from "./asg-provider-CshsD-Pi.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";
@@ -21914,13 +21914,13 @@ const SG_INGRESS_IP_PROTOCOL_PATH = "AWS::EC2::SecurityGroupIngress IpProtocol";
21914
21914
  * hang a deploy; reaching it records nothing rather than answering from a
21915
21915
  * partial view.
21916
21916
  */
21917
- const MAX_SG_RULE_PAGES = 20;
21917
+ const MAX_SG_RULE_PAGES$1 = 20;
21918
21918
  /**
21919
21919
  * `MaxResults` for that walk. `DescribeSecurityGroupRules` accepts 5-1000; the
21920
21920
  * ceiling is what makes {@link MAX_SG_RULE_PAGES}'s "20,000 rules" true and
21921
21921
  * keeps the common case (a group with a handful of rules) to ONE round trip.
21922
21922
  */
21923
- const SG_RULE_PAGE_SIZE = 1e3;
21923
+ const SG_RULE_PAGE_SIZE$1 = 1e3;
21924
21924
  /**
21925
21925
  * The `sgr-…` security-group rule id, anchored.
21926
21926
  *
@@ -23339,8 +23339,8 @@ var EC2Provider = class {
23339
23339
  let nextToken;
23340
23340
  let pages = 0;
23341
23341
  do {
23342
- if (pages >= MAX_SG_RULE_PAGES) {
23343
- this.logger.debug(`SecurityGroupIngress ${logicalId}: DescribeSecurityGroupRules on ${groupId} still paginating after ${MAX_SG_RULE_PAGES} pages — cannot prove the match is unique, so not recording an Id attribute`);
23342
+ if (pages >= MAX_SG_RULE_PAGES$1) {
23343
+ this.logger.debug(`SecurityGroupIngress ${logicalId}: DescribeSecurityGroupRules on ${groupId} still paginating after ${MAX_SG_RULE_PAGES$1} pages — cannot prove the match is unique, so not recording an Id attribute`);
23344
23344
  return;
23345
23345
  }
23346
23346
  const resp = await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({
@@ -23348,7 +23348,7 @@ var EC2Provider = class {
23348
23348
  Name: "group-id",
23349
23349
  Values: [groupId]
23350
23350
  }],
23351
- MaxResults: SG_RULE_PAGE_SIZE,
23351
+ MaxResults: SG_RULE_PAGE_SIZE$1,
23352
23352
  ...nextToken && { NextToken: nextToken }
23353
23353
  }));
23354
23354
  rules.push(...resp.SecurityGroupRules ?? []);
@@ -58412,7 +58412,9 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
58412
58412
  }
58413
58413
  const state = loaded.state;
58414
58414
  const perResourceSecrets = /* @__PURE__ */ new Map();
58415
+ const perResourceTemplateProps = /* @__PURE__ */ new Map();
58415
58416
  const outputSecrets = /* @__PURE__ */ new Map();
58417
+ const outputsTemplateSource = {};
58416
58418
  const resolver = new IntrinsicFunctionResolver(region);
58417
58419
  let parameters = {};
58418
58420
  let conditions = {};
@@ -58450,11 +58452,15 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
58450
58452
  logger.debug(`Resolution of ${logicalId} during scrub was partial: ${err instanceof Error ? err.message : String(err)}`);
58451
58453
  }
58452
58454
  if (recordedSecretValues.size > 0) perResourceSecrets.set(logicalId, recordedSecretValues);
58455
+ perResourceTemplateProps.set(logicalId, templateResource.Properties);
58453
58456
  }
58454
58457
  const templateOutputs = stack.template.Outputs ?? {};
58455
58458
  for (const [name, output] of Object.entries(templateOutputs)) {
58456
58459
  const value = output.Value;
58457
58460
  if (value === void 0) continue;
58461
+ outputsTemplateSource[name] = value;
58462
+ const exportName = output.Export?.Name;
58463
+ if (typeof exportName === "string") outputsTemplateSource[exportName] = value;
58458
58464
  try {
58459
58465
  await resolver.resolve(value, {
58460
58466
  template: stack.template,
@@ -58478,11 +58484,17 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
58478
58484
  const newResources = {};
58479
58485
  for (const [logicalId, record] of Object.entries(state.resources)) {
58480
58486
  const secrets = perResourceSecrets.get(logicalId);
58481
- const scrubbed = secrets ? scrubResourceRecord(record, secrets) : record;
58487
+ const templateProps = perResourceTemplateProps.get(logicalId);
58488
+ const ownSecrets = secrets ?? /* @__PURE__ */ new Map();
58489
+ const positioned = templateProps ? {
58490
+ ...record,
58491
+ properties: redactSecretsForState(record.properties, ownSecrets, templateProps, TEMPLATE_SOURCED_READBACK_RULES)
58492
+ } : record;
58493
+ const scrubbed = secrets || templateProps ? scrubResourceRecord(positioned, ownSecrets) : record;
58482
58494
  if (JSON.stringify(scrubbed) !== JSON.stringify(record)) recordsChanged++;
58483
58495
  newResources[logicalId] = scrubbed;
58484
58496
  }
58485
- const newOutputs = outputSecrets.size > 0 ? redactSecretsForState(state.outputs, outputSecrets) : state.outputs;
58497
+ const newOutputs = outputSecrets.size > 0 ? redactSecretsForState(state.outputs, outputSecrets, outputsTemplateSource) : state.outputs;
58486
58498
  if (JSON.stringify(newOutputs) !== JSON.stringify(state.outputs)) recordsChanged++;
58487
58499
  if (recordsChanged > 0 && !opts.dryRun) {
58488
58500
  const nextState = {
@@ -61843,6 +61855,18 @@ function readStringProperty(properties, key, resourceType) {
61843
61855
  return v;
61844
61856
  }
61845
61857
  /**
61858
+ * Build the live-read seam for ONE {@link buildImportPlan} call, with a fresh
61859
+ * memo. A factory rather than an inline object literal so the cache cannot be
61860
+ * forgotten at a future call site (the field is required, so omitting it is a
61861
+ * type error, and sharing one across calls has to be written deliberately).
61862
+ */
61863
+ function createIdentifierBackfillDeps(ec2Client) {
61864
+ return {
61865
+ ec2Client,
61866
+ securityGroupRuleCache: /* @__PURE__ */ new Map()
61867
+ };
61868
+ }
61869
+ /**
61846
61870
  * Types whose cdkd physicalId is a COMPOSITE (`|`-joined) string while
61847
61871
  * CloudFormation's `primaryIdentifier` is a SINGLE field — so the physicalId is
61848
61872
  * NOT the identifier value, and it is not a SPLIT of the composite either
@@ -61975,6 +61999,7 @@ function recordedRuleIdIdentifier() {
61975
61999
  const isUsableIdentifier = (value) => SG_RULE_ID_PATTERN.test(value.trim());
61976
62000
  return {
61977
62001
  field: "Id",
62002
+ backfill: backfillSecurityGroupIngressRuleId,
61978
62003
  resolve: ({ logicalId, physicalId, attributes }) => {
61979
62004
  const recorded = attributes["Id"];
61980
62005
  if (typeof recorded === "string" && isUsableIdentifier(recorded)) return recorded.trim();
@@ -61985,6 +62010,292 @@ function recordedRuleIdIdentifier() {
61985
62010
  };
61986
62011
  }
61987
62012
  /**
62013
+ * Page ceiling for the {@link backfillSecurityGroupIngressRuleId} walk, and its
62014
+ * `MaxResults`. The twins of `MAX_SG_RULE_PAGES` / `SG_RULE_PAGE_SIZE` in
62015
+ * `src/provisioning/providers/ec2-provider.ts`, restated here for the same
62016
+ * reason {@link SG_RULE_ID_PATTERN} is: neither module may pull the other's
62017
+ * graph.
62018
+ *
62019
+ * 20 pages x 1000 rules covers 20,000 rules on ONE security group, far beyond
62020
+ * any real one (AWS's default quota is 60 rules per SG, 1000 at the maximum
62021
+ * increase). That arithmetic only holds because the request pins `MaxResults` —
62022
+ * without it AWS applies its own page size and the bound would cap an unknown
62023
+ * number of rules. Exhausting it is treated as "could not see the whole group",
62024
+ * which is a REFUSAL here rather than the provider's silent `undefined`: the
62025
+ * uniqueness verdict below is a statement about the whole group, so answering
62026
+ * from a partial view is exactly how a genuine 2-match becomes a false
62027
+ * "exactly one".
62028
+ */
62029
+ const MAX_SG_RULE_PAGES = 20;
62030
+ const SG_RULE_PAGE_SIZE = 1e3;
62031
+ /**
62032
+ * Parse cdkd's `<groupId>|<ipProtocol>|<fromPort>|<toPort>` physical id, or
62033
+ * `undefined` when it is not that shape.
62034
+ *
62035
+ * EXACTLY four segments, all non-blank. A longer id is the issue
62036
+ * [#1672](https://github.com/go-to-k/cdkd/issues/1672) ambiguity — a segment
62037
+ * carrying the separator — and decoding its first four parts would silently
62038
+ * look up a DIFFERENT rule's tuple, so it is refused like any other unparseable
62039
+ * shape.
62040
+ *
62041
+ * The ports are the `'-1'` AWS itself uses for "all ports", which is what
62042
+ * `EC2Provider` packs when the template declares no `FromPort` / `ToPort`; a
62043
+ * segment that is not a finite number yields `undefined` rather than `NaN`, so
62044
+ * a malformed id refuses instead of matching nothing and blaming AWS.
62045
+ *
62046
+ * A BLANK port segment is refused EXPLICITLY, before `Number()` ever sees it,
62047
+ * because `Number('')` is `0` rather than `NaN` — so `sg-x|tcp||443` would
62048
+ * otherwise parse to `fromPort: 0` and go and look up a REAL tuple (ICMP type
62049
+ * 0 is echo reply, a live rule shape), adopting an id for a rule the record
62050
+ * does not name. That is also why the blank is not simply folded to `-1`: this
62051
+ * module and `EC2Provider.cfnIngressPortValue` disagree about what a blank
62052
+ * MEANS — the provider reads it as `-1` (all ports) because its input is a CFn
62053
+ * property the template legitimately omits, while here it is a SEGMENT of an
62054
+ * id cdkd itself wrote, and cdkd never writes an empty one. A blank segment is
62055
+ * therefore evidence the id is damaged, not evidence of an absent port.
62056
+ */
62057
+ function parseSecurityGroupIngressComposite(physicalId) {
62058
+ const parts = physicalId.trim().split("|");
62059
+ if (parts.length !== 4) return void 0;
62060
+ const [groupId, ipProtocol, fromPort, toPort] = parts;
62061
+ if (!groupId.trim() || !ipProtocol.trim()) return void 0;
62062
+ const fromRaw = fromPort.trim();
62063
+ const toRaw = toPort.trim();
62064
+ if (!fromRaw || !toRaw) return void 0;
62065
+ const from = Number(fromRaw);
62066
+ const to = Number(toRaw);
62067
+ if (!Number.isFinite(from) || !Number.isFinite(to)) return void 0;
62068
+ return {
62069
+ groupId: groupId.trim(),
62070
+ ipProtocol: ipProtocol.trim(),
62071
+ fromPort: from,
62072
+ toPort: to
62073
+ };
62074
+ }
62075
+ /**
62076
+ * Does this `DescribeSecurityGroupRules` row describe the rule cdkd's composite
62077
+ * physical id names?
62078
+ *
62079
+ * The protocol goes through {@link canonicalizeIpProtocolValue} on BOTH sides
62080
+ * rather than a raw compare: EC2 rewrites the four protocol numbers it has a
62081
+ * name for before storing the rule, so a record packed from `IpProtocol: 6`
62082
+ * carries `'6'` while the readback says `'tcp'` and a raw compare would match
62083
+ * nothing (the issue #1643 fold, at a fourth call site).
62084
+ *
62085
+ * An omitted port reads back as `-1`, which is the same spelling the composite
62086
+ * carries for an absent one — so the two sides are directly comparable.
62087
+ */
62088
+ function ruleMatchesIngressTuple(rule, tuple) {
62089
+ if (rule.IsEgress === true) return false;
62090
+ if (canonicalizeIpProtocolValue(rule.IpProtocol) !== canonicalizeIpProtocolValue(tuple.ipProtocol)) return false;
62091
+ if ((rule.FromPort ?? -1) !== tuple.fromPort) return false;
62092
+ if ((rule.ToPort ?? -1) !== tuple.toPort) return false;
62093
+ return true;
62094
+ }
62095
+ /**
62096
+ * Test seam: overriding `sleep` lets unit tests drive the throttle backoff
62097
+ * without real waits. Mirrors `describeTypeRetryDelays` in
62098
+ * `src/provisioning/describe-type.ts`, which this retry is modeled on.
62099
+ */
62100
+ const securityGroupRuleLookupRetryDelays = {};
62101
+ /**
62102
+ * Retries after the first attempt, THROTTLE-shaped failures only. At the
62103
+ * default backoff (1s -> 2s -> 4s -> 8s) this adds at most ~15s of sleep,
62104
+ * which is small next to what it prevents: `cdkd export` is all-or-nothing, so
62105
+ * one `RequestLimitExceeded` — the commonest EC2 Describe failure there is —
62106
+ * would otherwise abort the WHOLE run. A non-throttle failure (a missing
62107
+ * `ec2:DescribeSecurityGroupRules` being the important one) is surfaced
62108
+ * immediately, since it cannot heal by waiting.
62109
+ */
62110
+ const MAX_SG_RULE_THROTTLE_RETRIES = 4;
62111
+ /**
62112
+ * Does this failure say the CALLER lacks permission, as opposed to anything
62113
+ * else AWS can fail with?
62114
+ *
62115
+ * Only these get the "Grant ec2:DescribeSecurityGroupRules" sentence. Sending
62116
+ * every failure to that remedy is how a throttle got reported as a permissions
62117
+ * problem: the user checks a policy that was correct all along, while the
62118
+ * actual advice ("re-run it") goes unsaid.
62119
+ */
62120
+ function isAuthorizationShapedError(err) {
62121
+ const name = err instanceof Error ? err.name : "";
62122
+ const message = err instanceof Error ? err.message : String(err);
62123
+ return /AccessDenied|UnauthorizedOperation|AuthFailure|not authorized to perform/i.test(`${name} ${message}`);
62124
+ }
62125
+ /**
62126
+ * Walk EVERY ingress/egress rule AWS holds on one security group, retrying a
62127
+ * throttled page (issue [#1791](https://github.com/go-to-k/cdkd/issues/1791)
62128
+ * review).
62129
+ *
62130
+ * Returns the failure rather than throwing it, because the caller owns the
62131
+ * message: every refusal here names the cdkd ROW that could not be resolved,
62132
+ * and this function does not know which row (possibly rows, via the memo)
62133
+ * asked.
62134
+ */
62135
+ async function describeAllSecurityGroupRules(groupId, ec2Client) {
62136
+ const rules = [];
62137
+ let nextToken;
62138
+ let pages = 0;
62139
+ do {
62140
+ if (pages >= MAX_SG_RULE_PAGES) return {
62141
+ ok: false,
62142
+ kind: "truncated"
62143
+ };
62144
+ let resp;
62145
+ try {
62146
+ resp = await withRetry(() => ec2Client.send(new DescribeSecurityGroupRulesCommand({
62147
+ Filters: [{
62148
+ Name: "group-id",
62149
+ Values: [groupId]
62150
+ }],
62151
+ MaxResults: SG_RULE_PAGE_SIZE,
62152
+ ...nextToken && { NextToken: nextToken }
62153
+ })), `DescribeSecurityGroupRules(${groupId})`, {
62154
+ maxRetries: MAX_SG_RULE_THROTTLE_RETRIES,
62155
+ isRetryable: (_message, error) => isThrottlingError(error),
62156
+ logger: getLogger().child("Export"),
62157
+ ...securityGroupRuleLookupRetryDelays.sleep ? { sleep: securityGroupRuleLookupRetryDelays.sleep } : {}
62158
+ });
62159
+ } catch (err) {
62160
+ const detail = err instanceof Error ? err.message : String(err);
62161
+ return {
62162
+ ok: false,
62163
+ kind: isAuthorizationShapedError(err) ? "auth" : isThrottlingError(err) ? "throttled" : "failed",
62164
+ detail
62165
+ };
62166
+ }
62167
+ rules.push(...resp.SecurityGroupRules ?? []);
62168
+ nextToken = resp.NextToken;
62169
+ pages += 1;
62170
+ } while (nextToken);
62171
+ return {
62172
+ ok: true,
62173
+ rules
62174
+ };
62175
+ }
62176
+ /**
62177
+ * {@link describeAllSecurityGroupRules}, memoized per GROUP for the lifetime of
62178
+ * one {@link buildImportPlan} call — see
62179
+ * {@link IdentifierBackfillDeps.securityGroupRuleCache}.
62180
+ *
62181
+ * The PROMISE is cached, not its resolution, so rows resolved concurrently on
62182
+ * one group also share a single walk rather than racing to start their own.
62183
+ */
62184
+ function cachedSecurityGroupRules(groupId, { ec2Client, securityGroupRuleCache }) {
62185
+ const cached = securityGroupRuleCache.get(groupId);
62186
+ if (cached) return cached;
62187
+ const pending = describeAllSecurityGroupRules(groupId, ec2Client);
62188
+ securityGroupRuleCache.set(groupId, pending);
62189
+ return pending;
62190
+ }
62191
+ /**
62192
+ * What to DO about a row more than one AWS rule matches — the part of the
62193
+ * refusal that is about the ambiguity itself rather than about how cdkd
62194
+ * noticed it.
62195
+ *
62196
+ * Written once because TWO arms below report a matched count above one: the
62197
+ * plain `> 1` refusal, and the unusable-id refusal (whose own remedy is "open
62198
+ * a cdkd issue" — right about the unreadable id, useless about the ambiguity).
62199
+ * A row that hits the second arm has BOTH problems, and before this was shared
62200
+ * it was the one row whose user never saw these two causes at all.
62201
+ *
62202
+ * Carries no row identity, so it can be appended to either message.
62203
+ */
62204
+ const SG_INGRESS_AMBIGUITY_REMEDY = "Two causes, with different remedies. (1) ONE resource declaring MORE THAN ONE source (e.g. both CidrIp and CidrIpv6 on the same resource), for which AWS mints one rule per source: split it into one AWS::EC2::SecurityGroupIngress resource per source, which is also the shape CloudFormation will manage after the export. (2) TWO DISTINCT AWS::EC2::SecurityGroupIngress resources that differ only by SOURCE — port 443 from a CIDR and port 443 from a peer security group, say. cdkd's composite physical id carries no source, so those two rows carry byte-identical ids and splitting is not their remedy: set the row's attributes.Id to the 'sgr-...' id that belongs to it (cdkd then resolves from state and issues no lookup at all), or remove the row from the stack before exporting. Then re-run cdkd export.";
62205
+ /** The remedy every refusal below ends with — one place, so they cannot drift. */
62206
+ function sgIngressExportEscapeHatch(logicalId) {
62207
+ return `You can instead remove '${logicalId}' from the stack before exporting — the rule stays in AWS and can be re-declared in CloudFormation afterwards.`;
62208
+ }
62209
+ /**
62210
+ * Live-read backfill of the `sgr-...` rule id for an
62211
+ * `AWS::EC2::SecurityGroupIngress` row that state does not carry one for
62212
+ * (issue [#1791](https://github.com/go-to-k/cdkd/issues/1791)).
62213
+ *
62214
+ * ## Why a live read at all
62215
+ *
62216
+ * Every row written by a cdkd older than issue #1761 has `attributes: {}`, and
62217
+ * `cdkd export` is ALL-OR-NOTHING — so ONE such row makes a whole stack
62218
+ * un-exportable. The obvious workaround does not exist: AWS returns the id only
62219
+ * from `AuthorizeSecurityGroupIngress`'s own response, so a no-op `cdkd deploy`
62220
+ * records nothing and the row is exactly as unexportable afterwards. Only a
62221
+ * re-authorization (a property change, or a destroy + deploy) heals it, which
62222
+ * is a traffic interruption to pay for a read cdkd can perform itself.
62223
+ *
62224
+ * ## Why the match is on the composite's tuple, and why EXACTLY ONE
62225
+ *
62226
+ * The row's identity, as far as cdkd state is concerned, IS
62227
+ * `<groupId>|<ipProtocol>|<fromPort>|<toPort>` — the tuple its own revoke call
62228
+ * needs. So the tuple is the most cdkd can filter on, and two AWS rules sharing
62229
+ * it are two rules cdkd's own physical id cannot tell apart either. Adopting
62230
+ * one of them would record an identifier that names the wrong AWS object, which
62231
+ * is the failure mode the #1761 already-exists arm's "exactly one" rule exists
62232
+ * to prevent — so ambiguity REFUSES, naming the candidates.
62233
+ *
62234
+ * The commonest ambiguity is the multi-source rule the state-only message
62235
+ * already describes: a resource declaring both `CidrIp` and `CidrIpv6` makes
62236
+ * AWS mint one rule PER SOURCE, both with this exact tuple. Splitting the
62237
+ * resource is the remedy in that case, and it is the same remedy CloudFormation
62238
+ * will want after the export. It is NOT the only cause, which is why the
62239
+ * refusal names two: TWO DISTINCT `AWS::EC2::SecurityGroupIngress` resources
62240
+ * differing only by source share one tuple as well — cdkd's composite carries
62241
+ * no source — and those are already one resource per source, so the remedy
62242
+ * there is to repair the row's `attributes.Id` (or drop the row) instead.
62243
+ *
62244
+ * Deliberately NOT narrowed by the recorded SOURCE (`CidrIp` and friends) even
62245
+ * though state carries it: {@link CompositePhysicalIdContext} does not carry
62246
+ * `properties`, and widening it to make ONE ambiguous case resolvable would
62247
+ * also make the export adopt a rule the physical id in state does not uniquely
62248
+ * name — a row that looks adopted while `cdkd destroy` would revoke a different
62249
+ * one. Refusing is the answer that keeps state and AWS agreeing.
62250
+ */
62251
+ async function backfillSecurityGroupIngressRuleId({ logicalId, physicalId }, deps) {
62252
+ const tuple = parseSecurityGroupIngressComposite(physicalId);
62253
+ if (!tuple) throw new Error(`cdkd state records no 'sgr-...' security-group rule id for '${logicalId}', and its physical id '${physicalId}' is not cdkd's '<groupId>|<ipProtocol>|<fromPort>|<toPort>' composite either, so cdkd cannot look the rule up in AWS to recover the id CloudFormation IMPORT needs. Repair the record's physicalId (or its attributes.Id, which may be the 'sgr-...' id verbatim) and re-run cdkd export. ${sgIngressExportEscapeHatch(logicalId)}`);
62254
+ const portRange = tuple.fromPort === tuple.toPort ? `${tuple.fromPort}` : `${tuple.fromPort}-${tuple.toPort}`;
62255
+ const tupleNote = `protocol '${tuple.ipProtocol}', ports ${portRange} on security group '${tuple.groupId}'`;
62256
+ const lookup = await cachedSecurityGroupRules(tuple.groupId, deps);
62257
+ if (!lookup.ok) {
62258
+ if (lookup.kind === "truncated") throw new Error(`cdkd state records no 'sgr-...' security-group rule id for '${logicalId}', and the DescribeSecurityGroupRules lookup on '${tuple.groupId}' was still paginating after ${MAX_SG_RULE_PAGES} pages — cdkd cannot prove a match is unique without seeing the whole group, and adopting an unproven id would name the wrong rule. ${sgIngressExportEscapeHatch(logicalId)}`);
62259
+ const remedy = lookup.kind === "auth" ? `Grant ec2:DescribeSecurityGroupRules and re-run cdkd export.` : lookup.kind === "throttled" ? `AWS throttled the lookup and cdkd retried it ${MAX_SG_RULE_THROTTLE_RETRIES} times before giving up — no permission is missing. Re-run cdkd export once the account's EC2 describe rate has recovered.` : `Re-run cdkd export once that cause is resolved.`;
62260
+ throw new Error(`cdkd state records no 'sgr-...' security-group rule id for '${logicalId}' (the rule was deployed by a cdkd older than https://github.com/go-to-k/cdkd/issues/1761), and the DescribeSecurityGroupRules lookup that would have recovered it failed: ${lookup.detail}. ${remedy} ${sgIngressExportEscapeHatch(logicalId)}`);
62261
+ }
62262
+ const matched = lookup.rules.filter((rule) => ruleMatchesIngressTuple(rule, tuple));
62263
+ const ids = matched.map((rule) => rule.SecurityGroupRuleId).filter((id) => typeof id === "string" && SG_RULE_ID_PATTERN.test(id.trim())).map((id) => id.trim());
62264
+ if (ids.length < matched.length) {
62265
+ const unusable = matched.filter((rule) => !(typeof rule.SecurityGroupRuleId === "string" && SG_RULE_ID_PATTERN.test(rule.SecurityGroupRuleId.trim()))).map((rule) => typeof rule.SecurityGroupRuleId === "string" ? `'${rule.SecurityGroupRuleId}'` : "<absent>");
62266
+ const readableNote = ids.length > 0 ? ` The readable candidates cdkd did see: ${ids.map((id) => `'${id}'`).join(", ")}.` : "";
62267
+ const ambiguityNote = matched.length > 1 ? ` Note that ${matched.length} rules matching one row is an ambiguity in its own right, which cdkd could not resolve even if every id were readable: ${SG_INGRESS_AMBIGUITY_REMEDY}${readableNote}` : "";
62268
+ throw new Error(`cdkd state records no 'sgr-...' security-group rule id for '${logicalId}', and a live DescribeSecurityGroupRules lookup found ${matched.length} ingress rule(s) matching ${tupleNote}, but ${unusable.length} of them carry no usable 'sgr-...' rule id (AWS reported ${unusable.join(", ")}). cdkd adopts an id only on an EXACTLY ONE match, and that is a count of MATCHING RULES — so discarding the unreadable ones first would let a genuine two-match pass as "exactly one" and record an identifier naming the wrong rule. cdkd refuses instead. This is a DescribeSecurityGroupRules response shape cdkd does not expect; please open a cdkd issue if it persists.${ambiguityNote} ${sgIngressExportEscapeHatch(logicalId)}`);
62269
+ }
62270
+ if (ids.length === 1) return ids[0];
62271
+ if (ids.length === 0) throw new Error(`cdkd state records no 'sgr-...' security-group rule id for '${logicalId}' (the rule was deployed by a cdkd older than https://github.com/go-to-k/cdkd/issues/1761), and a live DescribeSecurityGroupRules lookup found NO ingress rule matching ${tupleNote} — so cdkd has no id to hand CloudFormation IMPORT. Either the rule was revoked outside cdkd, or the group lives in a region other than the one this export is running against. Run cdkd deploy to reconcile the stack, then re-run cdkd export. ${sgIngressExportEscapeHatch(logicalId)}`);
62272
+ throw new Error(`cdkd state records no 'sgr-...' security-group rule id for '${logicalId}', and a live DescribeSecurityGroupRules lookup found ${ids.length} ingress rules matching ${tupleNote} (${ids.join(", ")}). cdkd's physical id identifies a rule only by group, protocol and port range, so it cannot say which of them '${logicalId}' is, and CloudFormation IMPORT needs exactly one — adopting either would record the wrong rule. ${SG_INGRESS_AMBIGUITY_REMEDY} ${sgIngressExportEscapeHatch(logicalId)}`);
62273
+ }
62274
+ /**
62275
+ * Resolve a registered type's CFn identifier VALUE, consulting AWS only when
62276
+ * cdkd state could not answer (issue
62277
+ * [#1791](https://github.com/go-to-k/cdkd/issues/1791)).
62278
+ *
62279
+ * The ordering is the whole design: `resolve` is tried FIRST and its throw is
62280
+ * the only trigger for the live read, so a row whose state already carries the
62281
+ * identifier — every row written by a current cdkd — issues no AWS call at all,
62282
+ * and a caller with no {@link IdentifierBackfillDeps} keeps exactly the
62283
+ * pre-#1791 behavior (the state-only refusal, verbatim).
62284
+ *
62285
+ * The backfill's own error REPLACES the state-only one when it fires: by then
62286
+ * cdkd has asked AWS, so the state-only message's central remedy ("re-deploy so
62287
+ * cdkd records the id") is not merely unhelpful but has been disproven for this
62288
+ * row — the live read already found zero, or too many.
62289
+ */
62290
+ async function resolveIdentifierValue(entry, ctx, deps) {
62291
+ try {
62292
+ return entry.resolve(ctx);
62293
+ } catch (stateOnlyError) {
62294
+ if (!entry.backfill || !deps) throw stateOnlyError;
62295
+ return await entry.backfill(ctx, deps);
62296
+ }
62297
+ }
62298
+ /**
61988
62299
  * Build the {@link CompositePhysicalIdIdentifier} for a type whose CFn
61989
62300
  * identifier is an ARN cdkd records in `attributes` under the SAME name as the
61990
62301
  * `primaryIdentifier` field.
@@ -62205,7 +62516,8 @@ async function exportCommand(stackArg, options) {
62205
62516
  }
62206
62517
  const { phase1Imports, phase2Creates, recreateBeforePhase2, nestedStackRows, blocked } = await buildImportPlan(state, template, awsClients.cloudFormation, resolvedStackName, {
62207
62518
  recreateImportUnsupported: options.recreateImportUnsupported,
62208
- skipImportSupportPreflight: options.skipImportSupportPreflight
62519
+ skipImportSupportPreflight: options.skipImportSupportPreflight,
62520
+ ec2Client: awsClients.ec2
62209
62521
  });
62210
62522
  if (blocked.length > 0) {
62211
62523
  logger.error("The following resources block migration:");
@@ -62247,6 +62559,7 @@ async function exportCommand(stackArg, options) {
62247
62559
  rootParameters: rootParametersForNested,
62248
62560
  deps: {
62249
62561
  cfnClient: awsClients.cloudFormation,
62562
+ ec2Client: awsClients.ec2,
62250
62563
  stateBackend,
62251
62564
  lockManager,
62252
62565
  uploadOpts: {
@@ -62741,6 +63054,7 @@ async function buildImportPlan(state, template, cfnClient, parentStackName, opti
62741
63054
  const nestedStackRows = [];
62742
63055
  const blocked = [];
62743
63056
  const identifierCache = /* @__PURE__ */ new Map();
63057
+ const backfillDeps = options.ec2Client ? createIdentifierBackfillDeps(options.ec2Client) : void 0;
62744
63058
  for (const [logicalId, raw] of Object.entries(templateResources)) {
62745
63059
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
62746
63060
  const resourceType = raw.Type ?? "";
@@ -62808,7 +63122,7 @@ async function buildImportPlan(state, template, cfnClient, parentStackName, opti
62808
63122
  });
62809
63123
  continue;
62810
63124
  }
62811
- resolved = resolveResourceIdentifier(resourceType, logicalId, stateEntry.physicalId, stateEntry.properties ?? {}, stateEntry.attributes ?? {}, schemaInfo);
63125
+ resolved = await resolveResourceIdentifier(resourceType, logicalId, stateEntry.physicalId, stateEntry.properties ?? {}, stateEntry.attributes ?? {}, schemaInfo, backfillDeps);
62812
63126
  } catch (err) {
62813
63127
  blocked.push({
62814
63128
  logicalId,
@@ -62895,15 +63209,15 @@ function groupBlockedReasons(blocked) {
62895
63209
  * what keeps `buildImportPlan` to ONE DescribeType per type across both the
62896
63210
  * IMPORT pre-flight and this resolution.
62897
63211
  */
62898
- function resolveResourceIdentifier(resourceType, logicalId, physicalId, properties, attributes, entry) {
63212
+ async function resolveResourceIdentifier(resourceType, logicalId, physicalId, properties, attributes, entry, backfillDeps) {
62899
63213
  const compositeIdentifier = Object.hasOwn(COMPOSITE_PHYSICAL_ID_IDENTIFIERS, resourceType) ? COMPOSITE_PHYSICAL_ID_IDENTIFIERS[resourceType] : void 0;
62900
63214
  if (compositeIdentifier) {
62901
63215
  if (entry.fields.length !== 1 || entry.fields[0] !== compositeIdentifier.field) throw new Error(`${resourceType} is registered in COMPOSITE_PHYSICAL_ID_IDENTIFIERS as identified by '${compositeIdentifier.field}', but CloudFormation now reports primaryIdentifier [${entry.fields.join(", ")}]. The registry schema changed under cdkd — update the entry in src/cli/commands/export.ts (or move the type to COMPOSITE_ID_SPLITTERS if its identifier is now composite too)`);
62902
- const value = compositeIdentifier.resolve({
63216
+ const value = await resolveIdentifierValue(compositeIdentifier, {
62903
63217
  logicalId,
62904
63218
  physicalId,
62905
63219
  attributes
62906
- });
63220
+ }, backfillDeps);
62907
63221
  return {
62908
63222
  resourceIdentifier: { [compositeIdentifier.field]: value },
62909
63223
  propertiesOverlay: {}
@@ -64065,7 +64379,8 @@ async function runPerStackImportLoop(args) {
64065
64379
  const meta = nodesByCdkdName.get(node.stackName);
64066
64380
  const plan = await buildImportPlan(meta.state, meta.template, deps.cfnClient, meta.cdkdStackName, {
64067
64381
  recreateImportUnsupported: options.recreateImportUnsupported,
64068
- skipImportSupportPreflight: options.skipImportSupportPreflight === true
64382
+ skipImportSupportPreflight: options.skipImportSupportPreflight === true,
64383
+ ...deps.ec2Client && { ec2Client: deps.ec2Client }
64069
64384
  });
64070
64385
  if (plan.blocked.length > 0) {
64071
64386
  const lines = groupBlockedReasons(plan.blocked);
@@ -68911,11 +69226,12 @@ async function resolveImportedProperties(stackState, template, region, stateBack
68911
69226
  };
68912
69227
  for (const [logicalId, resource] of entries) try {
68913
69228
  const recordedSecretValues = /* @__PURE__ */ new Map();
68914
- const resolved = await resolver.resolve(resource.properties ?? {}, {
69229
+ const unresolvedProperties = resource.properties ?? {};
69230
+ const resolved = await resolver.resolve(unresolvedProperties, {
68915
69231
  ...baseContext,
68916
69232
  recordedSecretValues
68917
69233
  });
68918
- resource.properties = recordedSecretValues.size > 0 ? redactSecretsForState(resolved, recordedSecretValues) : resolved;
69234
+ resource.properties = recordedSecretValues.size > 0 ? redactSecretsForState(resolved, recordedSecretValues, unresolvedProperties) : resolved;
68919
69235
  } catch (err) {
68920
69236
  logger.warn(`Failed to resolve intrinsics in Properties for imported resource '${logicalId}' (${resource.resourceType}): ${err instanceof Error ? err.message : String(err)}. State will be written with the raw intrinsic shape, which may cause 'cdkd destroy' to fail on this resource — re-import once every referenced sibling is in state, or remove this resource via 'cdkd state orphan'.`);
68921
69237
  }
@@ -79074,7 +79390,7 @@ function createMigrateCommand() {
79074
79390
  */
79075
79391
  function buildProgram() {
79076
79392
  const program = new Command();
79077
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.24");
79393
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.26");
79078
79394
  program.hook("preAction", (_thisCommand, actionCommand) => {
79079
79395
  const { profile } = actionCommand.optsWithGlobals();
79080
79396
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;