@go-to-k/cdkd 0.282.7 → 0.283.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-DulIbRCK.js → asg-provider-R3IdAR_3.js} +2 -2
- package/dist/{asg-provider-DulIbRCK.js.map → asg-provider-R3IdAR_3.js.map} +1 -1
- package/dist/cli.js +521 -26
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-NA36xDvk.js → deploy-engine-InwDI9o0.js} +3 -3
- package/dist/{deploy-engine-NA36xDvk.js.map → deploy-engine-InwDI9o0.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as configStringRefusal, $t as resolveStateBucketWithDefaultAndSource, A as green, An as NestedStackChildDirectDestroyError, At as BOOTSTRAP_MARKER_PREFIX, B as slowCcOperationTimeoutMs, Bt as runDockerForeground, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ct as stringifyValue, D as bold, Dn as LocalStartServiceError, Dt as loadPublishableAssetManifest, E as formatResourceLine, En as LocalMigrateError, Et as createAssetRedirectResolver, F as clearOnUpdateRemoval, Fn as StackHasActiveImportsError, Ft as validateContainerRepoName, G as getAccountInfo, Gt as synthesisStatusMessage, H as isTerminationProtectionPropagationError, Hn as withErrorHandling, Ht as AssetManifestLoader, I as ProviderRegistry, In as StackTerminationProtectionError, It as buildDenyExternalAccessPolicy, J as normalizeAwsTagsToCfn, Jt as resolveApp, K as refStateLookupFromResource, Kt as getDefaultStateBucketName, L as findActionableSilentDrops, Lt as buildDockerImage, M as yellow, Mn as ProvisioningError, Mt as getBootstrapMarkerKey, N as IAMRoleProvider, Nn as ResourceTimeoutError, Nt as parseBootstrapMarker, O as cyan, Ot as rewriteTemplateAssetReferences, P as collectInlinePolicyNamesManagedBySiblings, Pn as ResourceUpdateNotSupportedError, Pt as validateAssetBucketName, Q as configBooleanRefusal, Qt as resolveStateBucketWithDefault, R as findSilentDropProperties, Rt as formatDockerLoginError, S as extractDeploymentEventError, St as AssetPublisher, T as renderStatefulReason, Tn as LocalInvokeBuildError$1, Tt as buildAssetRedirectMap, U as IntrinsicFunctionResolver, Ut as getDockerImageBySourceHash, V as disableInstanceApiTermination, Vn as normalizeAwsError, Vt as runDockerStreaming, W as cfnRefValueFromPhysicalId, Wt as Synthesizer, X as assertRegionMatch, Xt as resolveCaptureObservedState, Y as resolveExplicitPhysicalId, Yt as resolveAutoAssetStorage, Z as coerceCfnBoolean, Zt as resolveSkipPrefix, _ as createPreDeleteFinalSnapshot, _n as getAwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as MIGRATE_TMP_PREFIX, at as s3BucketArn, b as unsupportedFinalSnapshotError, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as expectedOwnerParam, ct as s3BucketRegionalDomainName, d as IMPLICIT_DELETE_DEPENDENCIES, dn as derivePartitionAndUrlSuffix, dt as DiffCalculator, en as resolveUseCdkBootstrapAssets, et as readConfigString, f as computeImplicitDeleteEdges, fn as AssemblyReader, ft as describeTypeWithThrottleRetry, g as ccRoutedFinalSnapshotError, gn as AwsClients, gt as DagBuilder, h as buildFinalSnapshotIdentifier, hn as resolveBucketRegion, ht as isThrottlingError, i as DeploymentEventsReader, in as CFN_TEMPLATE_URL_LIMIT, it as requireConfigString, j as red, jn as PartialFailureError, jt as ensureAssetStorage, k as gray, kn as MissingCdkCliError, kt as AssetModeResolver, l as replayRollback, ln as PARTITION_TABLE, lt as s3BucketWebsiteUrl, m as PRE_DELETE_SNAPSHOT_TYPES, mt as isRetryableTransientError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as warnDeprecatedNoPrefixCliFlag, nt as requireConfigArray, o as planFailedOps, on as findLargeInlineResources, ot as s3BucketDomainName, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as processStackMessages, pt as withRetry, q as WAFv2WebACLProvider, qt as getLegacyStateBucketName, r as DeployEngine, rn as CFN_TEMPLATE_BODY_LIMIT, rt as requireConfigObject, s as planRollback, sn as uploadCfnTemplate, st as s3BucketDualStackDomainName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as stateBucketExistenceConfirmed, tt as replayWarn, u as withResourceDeadline, un as canonicalizeRegion, ut as applyRoleArnIfSet, v as isFinalSnapshotError, vn as resetAwsClients, vt as LockManager, w as isStatefulRecreateTargetSync, wn as DeployCancelledError, wt as WorkGraph, x as makeCanonicalizePropertiesFn, xn as CdkdError, xt as shouldRetainResource, y as refusesFinalSnapshot, yn as setAwsClients, yt as S3StateBackend, z as CloudControlProvider, zt as getDockerCmd } from "./deploy-engine-
|
|
2
|
+
import { $ as configStringRefusal, $t as resolveStateBucketWithDefaultAndSource, A as green, An as NestedStackChildDirectDestroyError, At as BOOTSTRAP_MARKER_PREFIX, B as slowCcOperationTimeoutMs, Bt as runDockerForeground, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ct as stringifyValue, D as bold, Dn as LocalStartServiceError, Dt as loadPublishableAssetManifest, E as formatResourceLine, En as LocalMigrateError, Et as createAssetRedirectResolver, F as clearOnUpdateRemoval, Fn as StackHasActiveImportsError, Ft as validateContainerRepoName, G as getAccountInfo, Gt as synthesisStatusMessage, H as isTerminationProtectionPropagationError, Hn as withErrorHandling, Ht as AssetManifestLoader, I as ProviderRegistry, In as StackTerminationProtectionError, It as buildDenyExternalAccessPolicy, J as normalizeAwsTagsToCfn, Jt as resolveApp, K as refStateLookupFromResource, Kt as getDefaultStateBucketName, L as findActionableSilentDrops, Lt as buildDockerImage, M as yellow, Mn as ProvisioningError, Mt as getBootstrapMarkerKey, N as IAMRoleProvider, Nn as ResourceTimeoutError, Nt as parseBootstrapMarker, O as cyan, Ot as rewriteTemplateAssetReferences, P as collectInlinePolicyNamesManagedBySiblings, Pn as ResourceUpdateNotSupportedError, Pt as validateAssetBucketName, Q as configBooleanRefusal, Qt as resolveStateBucketWithDefault, R as findSilentDropProperties, Rt as formatDockerLoginError, S as extractDeploymentEventError, St as AssetPublisher, T as renderStatefulReason, Tn as LocalInvokeBuildError$1, Tt as buildAssetRedirectMap, U as IntrinsicFunctionResolver, Ut as getDockerImageBySourceHash, V as disableInstanceApiTermination, Vn as normalizeAwsError, Vt as runDockerStreaming, W as cfnRefValueFromPhysicalId, Wt as Synthesizer, X as assertRegionMatch, Xt as resolveCaptureObservedState, Y as resolveExplicitPhysicalId, Yt as resolveAutoAssetStorage, Z as coerceCfnBoolean, Zt as resolveSkipPrefix, _ as createPreDeleteFinalSnapshot, _n as getAwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as MIGRATE_TMP_PREFIX, at as s3BucketArn, b as unsupportedFinalSnapshotError, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as expectedOwnerParam, ct as s3BucketRegionalDomainName, d as IMPLICIT_DELETE_DEPENDENCIES, dn as derivePartitionAndUrlSuffix, dt as DiffCalculator, en as resolveUseCdkBootstrapAssets, et as readConfigString, f as computeImplicitDeleteEdges, fn as AssemblyReader, ft as describeTypeWithThrottleRetry, g as ccRoutedFinalSnapshotError, gn as AwsClients, gt as DagBuilder, h as buildFinalSnapshotIdentifier, hn as resolveBucketRegion, ht as isThrottlingError, i as DeploymentEventsReader, in as CFN_TEMPLATE_URL_LIMIT, it as requireConfigString, j as red, jn as PartialFailureError, jt as ensureAssetStorage, k as gray, kn as MissingCdkCliError, kt as AssetModeResolver, l as replayRollback, ln as PARTITION_TABLE, lt as s3BucketWebsiteUrl, m as PRE_DELETE_SNAPSHOT_TYPES, mt as isRetryableTransientError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as warnDeprecatedNoPrefixCliFlag, nt as requireConfigArray, o as planFailedOps, on as findLargeInlineResources, ot as s3BucketDomainName, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as processStackMessages, pt as withRetry, q as WAFv2WebACLProvider, qt as getLegacyStateBucketName, r as DeployEngine, rn as CFN_TEMPLATE_BODY_LIMIT, rt as requireConfigObject, s as planRollback, sn as uploadCfnTemplate, st as s3BucketDualStackDomainName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as stateBucketExistenceConfirmed, tt as replayWarn, u as withResourceDeadline, un as canonicalizeRegion, ut as applyRoleArnIfSet, v as isFinalSnapshotError, vn as resetAwsClients, vt as LockManager, w as isStatefulRecreateTargetSync, wn as DeployCancelledError, wt as WorkGraph, x as makeCanonicalizePropertiesFn, xn as CdkdError, xt as shouldRetainResource, y as refusesFinalSnapshot, yn as setAwsClients, yt as S3StateBackend, z as CloudControlProvider, zt as getDockerCmd } from "./deploy-engine-InwDI9o0.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-R3IdAR_3.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";
|
|
@@ -10,7 +10,7 @@ import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, GetQ
|
|
|
10
10
|
import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesCommand, GetTopicAttributesCommand, InvalidParameterException, ListSubscriptionsByTopicCommand, ListTagsForResourceCommand, ListTopicsCommand, NotFoundException, SNSClient, SetTopicAttributesCommand, SubscribeCommand, TagResourceCommand, UnsubscribeCommand, UntagResourceCommand } from "@aws-sdk/client-sns";
|
|
11
11
|
import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCommand, CreateFunctionUrlConfigCommand, DeleteEventSourceMappingCommand, DeleteFunctionCodeSigningConfigCommand, DeleteFunctionCommand, DeleteFunctionConcurrencyCommand, DeleteFunctionEventInvokeConfigCommand, DeleteFunctionUrlConfigCommand, DeleteLayerVersionCommand, GetEventSourceMappingCommand, GetFunctionCodeSigningConfigCommand, GetFunctionCommand, GetFunctionConcurrencyCommand, GetFunctionEventInvokeConfigCommand, GetFunctionRecursionConfigCommand, GetFunctionUrlConfigCommand, GetLayerVersionByArnCommand, GetPolicyCommand as GetPolicyCommand$1, GetRuntimeManagementConfigCommand, LambdaClient, ListTagsCommand, PublishLayerVersionCommand, PutFunctionCodeSigningConfigCommand, PutFunctionConcurrencyCommand, PutFunctionEventInvokeConfigCommand, PutFunctionRecursionConfigCommand, PutRuntimeManagementConfigCommand, RemovePermissionCommand, ResourceNotFoundException, TagResourceCommand as TagResourceCommand$1, UntagResourceCommand as UntagResourceCommand$1, UpdateEventSourceMappingCommand, UpdateFunctionCodeCommand, UpdateFunctionConfigurationCommand, UpdateFunctionUrlConfigCommand, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
|
|
12
12
|
import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
|
|
13
|
-
import { AllocateAddressCommand, AssociateAddressCommand, AssociateIamInstanceProfileCommand, AssociateRouteTableCommand, AttachInternetGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAddressesCommand, DescribeAvailabilityZonesCommand, DescribeIamInstanceProfileAssociationsCommand, DescribeInstanceAttributeCommand, DescribeInstanceCreditSpecificationsCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DetachInternetGatewayCommand, DisassociateAddressCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifyInstanceCreditSpecificationCommand, ModifyInstanceMetadataOptionsCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, MonitorInstancesCommand, ReleaseAddressCommand, ReplaceNetworkAclAssociationCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, TerminateInstancesCommand, UnmonitorInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
|
|
13
|
+
import { AllocateAddressCommand, AssociateAddressCommand, AssociateIamInstanceProfileCommand, AssociateRouteTableCommand, AttachInternetGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAddressesCommand, DescribeAvailabilityZonesCommand, DescribeIamInstanceProfileAssociationsCommand, DescribeInstanceAttributeCommand, DescribeInstanceCreditSpecificationsCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupRulesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DetachInternetGatewayCommand, DisassociateAddressCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifyInstanceCreditSpecificationCommand, ModifyInstanceMetadataOptionsCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, MonitorInstancesCommand, ReleaseAddressCommand, ReplaceNetworkAclAssociationCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, TerminateInstancesCommand, UnmonitorInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
|
|
14
14
|
import { CreateTableCommand, DeleteResourcePolicyCommand, DeleteTableCommand, DescribeContinuousBackupsCommand, DescribeContributorInsightsCommand, DescribeKinesisStreamingDestinationCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DisableKinesisStreamingDestinationCommand, DynamoDBClient, EnableKinesisStreamingDestinationCommand, GetResourcePolicyCommand, ListTagsOfResourceCommand, PutResourcePolicyCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateContinuousBackupsCommand, UpdateContributorInsightsCommand, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
|
|
15
15
|
import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, ExecuteChangeSetCommand, GetTemplateCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
|
|
16
16
|
import { APIGatewayClient, CreateAuthorizerCommand, CreateDeploymentCommand, CreateResourceCommand, CreateStageCommand, DeleteAuthorizerCommand, DeleteDeploymentCommand, DeleteMethodCommand, DeleteResourceCommand, DeleteStageCommand, GetAccountCommand, GetAuthorizerCommand, GetDeploymentCommand, GetMethodCommand, GetResourceCommand, GetStageCommand, NotFoundException as NotFoundException$1, PutIntegrationCommand, PutIntegrationResponseCommand, PutMethodCommand, PutMethodResponseCommand, TagResourceCommand as TagResourceCommand$3, UntagResourceCommand as UntagResourceCommand$3, UpdateAccountCommand, UpdateAuthorizerCommand, UpdateMethodCommand, UpdateStageCommand } from "@aws-sdk/client-api-gateway";
|
|
@@ -15484,10 +15484,16 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
15484
15484
|
};
|
|
15485
15485
|
if (tableArn) await this.applyTagDiff(tableArn, extractLocalTags(previousProperties), extractLocalTags(properties));
|
|
15486
15486
|
const recordedOldBilling = previousProperties["BillingMode"];
|
|
15487
|
-
const
|
|
15488
|
-
const
|
|
15489
|
-
const
|
|
15490
|
-
|
|
15487
|
+
const recordedOldBillingAbsent = recordedOldBilling === void 0;
|
|
15488
|
+
const recordedOldBillingUsable = typeof recordedOldBilling === "string" && recordedOldBilling.trim() !== "";
|
|
15489
|
+
const rawLiveBillingMode = describeResp.Table?.BillingModeSummary?.BillingMode;
|
|
15490
|
+
const reportedBillingMode = rawLiveBillingMode === "PROVISIONED" || rawLiveBillingMode === "PAY_PER_REQUEST" ? rawLiveBillingMode : void 0;
|
|
15491
|
+
const liveBillingMode = reportedBillingMode ?? "PROVISIONED";
|
|
15492
|
+
const desiredDeclaresBillingMode = properties["BillingMode"] !== void 0;
|
|
15493
|
+
const seedAbsentFromLive = recordedOldBillingAbsent && desiredDeclaresBillingMode;
|
|
15494
|
+
const oldBilling = recordedOldBillingUsable ? recordedOldBilling : recordedOldBillingAbsent ? seedAbsentFromLive ? liveBillingMode : "PAY_PER_REQUEST" : liveBillingMode;
|
|
15495
|
+
if (!recordedOldBillingUsable && !recordedOldBillingAbsent) this.logger.warn(`AWS::DynamoDB::GlobalTable ${logicalId}: the recorded previous BillingMode is unusable (${JSON.stringify(recordedOldBilling)}) — using the table's actual billing mode (${oldBilling}) as the comparison baseline for this update so a corrected template does not issue a same-mode UpdateTable.`);
|
|
15496
|
+
if (seedAbsentFromLive && oldBilling !== "PAY_PER_REQUEST") this.logger.warn(`AWS::DynamoDB::GlobalTable ${logicalId}: the cdkd state record declares no BillingMode — using ${oldBilling} as the comparison baseline for this update (${reportedBillingMode !== void 0 ? "the mode DescribeTable reports" : "inferred: DescribeTable reports no BillingModeSummary, which means the table was created without an explicit mode"}), so a template declaring a different mode still applies the flip instead of comparing equal to the create-path default.`);
|
|
15491
15497
|
let billingUnusable = false;
|
|
15492
15498
|
const requestedBilling = requireConfigString(properties["BillingMode"], "PAY_PER_REQUEST", "AWS::DynamoDB::GlobalTable BillingMode", { onUnusable: (message) => {
|
|
15493
15499
|
billingUnusable = true;
|
|
@@ -15575,6 +15581,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
15575
15581
|
effectiveProperties = { ...effectiveProperties ?? properties };
|
|
15576
15582
|
if (recordedOldBilling === void 0) delete effectiveProperties["BillingMode"];
|
|
15577
15583
|
else effectiveProperties["BillingMode"] = oldBilling;
|
|
15584
|
+
effectiveProperties = retainUnsendableCapacityMembers(effectiveProperties, previousProperties, newBilling);
|
|
15578
15585
|
}
|
|
15579
15586
|
const awsOnDemand = describeResp.Table?.OnDemandThroughput;
|
|
15580
15587
|
const onDemandPayload = {};
|
|
@@ -17533,6 +17540,185 @@ function stripProvisionedCapacityKeys(properties) {
|
|
|
17533
17540
|
return result;
|
|
17534
17541
|
}
|
|
17535
17542
|
/**
|
|
17543
|
+
* The capacity member keys that can only reach AWS while the table is
|
|
17544
|
+
* PROVISIONED, per container level (issue #1738).
|
|
17545
|
+
*
|
|
17546
|
+
* Identical to the set {@link stripProvisionedCapacityKeys} removes, and that
|
|
17547
|
+
* is not a coincidence: both answer "which members are unsendable once the mode
|
|
17548
|
+
* is PAY_PER_REQUEST". The two spellings a pre-#1387 cdkd wrote into state
|
|
17549
|
+
* (`ProvisionedThroughput` / `ProvisionedThroughputOverride`) are included for
|
|
17550
|
+
* the same reason — a state replay is exactly that population.
|
|
17551
|
+
*/
|
|
17552
|
+
const PROVISIONED_ONLY_CAPACITY_KEYS = {
|
|
17553
|
+
table: ["WriteProvisionedThroughputSettings"],
|
|
17554
|
+
index: [
|
|
17555
|
+
"WriteProvisionedThroughputSettings",
|
|
17556
|
+
"ReadProvisionedThroughputSettings",
|
|
17557
|
+
"ProvisionedThroughput"
|
|
17558
|
+
],
|
|
17559
|
+
replica: ["ReadProvisionedThroughputSettings", "ProvisionedThroughputOverride"],
|
|
17560
|
+
replicaIndex: ["ReadProvisionedThroughputSettings", "ProvisionedThroughputOverride"]
|
|
17561
|
+
};
|
|
17562
|
+
/**
|
|
17563
|
+
* The mirror set: capacity members that can only reach AWS while the table is
|
|
17564
|
+
* PAY_PER_REQUEST (issue #1738).
|
|
17565
|
+
*
|
|
17566
|
+
* `stripProvisionedCapacityKeys` has no equivalent because the create arm it
|
|
17567
|
+
* serves always substitutes PAY_PER_REQUEST — the mode under which these ARE
|
|
17568
|
+
* sent, which is why its header says they are deliberately not stripped. On the
|
|
17569
|
+
* UPDATE path the kept mode can be PROVISIONED, and then it is this half that
|
|
17570
|
+
* never leaves the process.
|
|
17571
|
+
*/
|
|
17572
|
+
const ON_DEMAND_ONLY_CAPACITY_KEYS = {
|
|
17573
|
+
table: ["WriteOnDemandThroughputSettings"],
|
|
17574
|
+
index: [
|
|
17575
|
+
"WriteOnDemandThroughputSettings",
|
|
17576
|
+
"ReadOnDemandThroughputSettings",
|
|
17577
|
+
"OnDemandThroughput"
|
|
17578
|
+
],
|
|
17579
|
+
replica: ["ReadOnDemandThroughputSettings", "OnDemandThroughputOverride"],
|
|
17580
|
+
replicaIndex: ["ReadOnDemandThroughputSettings", "OnDemandThroughputOverride"]
|
|
17581
|
+
};
|
|
17582
|
+
/**
|
|
17583
|
+
* Replace every capacity member the KEPT billing mode cannot send with the
|
|
17584
|
+
* PREVIOUS record's value (issue #1738).
|
|
17585
|
+
*
|
|
17586
|
+
* Called from `update()`'s `BillingMode` warn-and-KEEP arm, where an unusable
|
|
17587
|
+
* desired mode suppresses the flip: AWS is left holding the mode it already
|
|
17588
|
+
* had, so every block belonging to the OTHER mode was silently dropped on the
|
|
17589
|
+
* way out while the engine recorded what the template declared.
|
|
17590
|
+
*
|
|
17591
|
+
* **The split is per MEMBER against the KEPT mode**, and each side was read off
|
|
17592
|
+
* the gates in this file rather than off the member's name-shape (the
|
|
17593
|
+
* name-shape trap #1726 records — an on-demand ceiling looks like capacity and
|
|
17594
|
+
* goes on the wire under precisely the on-demand mode):
|
|
17595
|
+
*
|
|
17596
|
+
* - KEPT `PROVISIONED` — the on-demand half is unsendable. Step 3's table-level
|
|
17597
|
+
* `OnDemandThroughput` is gated on `newBilling !== 'PROVISIONED'`;
|
|
17598
|
+
* `toSdkGlobalSecondaryIndexes` and `toSdkReplicaThroughputOverrides` both
|
|
17599
|
+
* drop their on-demand branch under PROVISIONED. The provisioned half is NOT
|
|
17600
|
+
* retained: step 4b and step 6b's auto-scaling reconcile are live whenever
|
|
17601
|
+
* either side is PROVISIONED, and step 6's GSI diff carries per-index
|
|
17602
|
+
* `ProvisionedThroughput` — those blocks really are applied.
|
|
17603
|
+
* - KEPT `PAY_PER_REQUEST` — the provisioned half is unsendable. The table-level
|
|
17604
|
+
* `ProvisionedThroughput` rides step 4's flip and nothing else, the two
|
|
17605
|
+
* auto-scaling reconciles are gated on either side being PROVISIONED, and both
|
|
17606
|
+
* translators drop their provisioned branch. The on-demand half is NOT
|
|
17607
|
+
* retained: step 3 sends the table-level ceilings and step 6 the per-index
|
|
17608
|
+
* ones under exactly this mode.
|
|
17609
|
+
*
|
|
17610
|
+
* One residual is deliberately NOT covered, because the suppression did not
|
|
17611
|
+
* create it: the TABLE-level provisioned capacity NUMBER rides step 4's flip
|
|
17612
|
+
* and nothing else, so on ANY non-flip PROVISIONED -> PROVISIONED update it is
|
|
17613
|
+
* unsent while its `*CapacityAutoScalingSettings` sibling in the same block IS
|
|
17614
|
+
* reconciled. Retaining the block wholesale under a kept PROVISIONED mode would
|
|
17615
|
+
* therefore erase a real auto-scaling update to hide a pre-existing gap — so
|
|
17616
|
+
* the block stays as declared and the gap stays where it already lives.
|
|
17617
|
+
*
|
|
17618
|
+
* Retaining rather than DROPPING is the UPDATE row of
|
|
17619
|
+
* `.claude/rules/providers.md`: the call never ran, so AWS still holds the
|
|
17620
|
+
* previously-applied block and that IS what state should describe. Dropping
|
|
17621
|
+
* would leave a later template that REMOVES the block deriving no removal, so
|
|
17622
|
+
* the live setting would survive forever.
|
|
17623
|
+
*
|
|
17624
|
+
* **The previous side is VALIDATED before it is retained** (the #1653-review
|
|
17625
|
+
* rule) through `asRecord` — the SAME predicate every wire read of these blocks
|
|
17626
|
+
* applies, so the two cannot disagree about what a bare string / an array / a
|
|
17627
|
+
* number means. Be honest about what that buys: it refuses exactly the
|
|
17628
|
+
* not-a-plain-object shapes, and a block whose NUMERIC member is an unresolved
|
|
17629
|
+
* intrinsic passes it, because the wire accepts that block too (and then sends
|
|
17630
|
+
* nothing for the member, which is the same "AWS keeps what it had" outcome).
|
|
17631
|
+
* When the previous side is unusable OR absent the key is DROPPED — there is no
|
|
17632
|
+
* value cdkd can vouch for either way.
|
|
17633
|
+
*
|
|
17634
|
+
* COPIES each retained block one level rather than aliasing it: the rollback
|
|
17635
|
+
* executor spreads the returned bag shallowly, so handing back the previous
|
|
17636
|
+
* record's own object would leave two state records sharing one mutable value.
|
|
17637
|
+
* Scope that claim honestly — it covers the retained blocks only; every other
|
|
17638
|
+
* key of the input bag stays aliased, exactly as the sibling arms leave it.
|
|
17639
|
+
*
|
|
17640
|
+
* Deliberately NOT paired with a `canonicalizeDesiredProperties` twin: this is
|
|
17641
|
+
* a SKIP, not a narrowing, so there is no pure function of the desired bag to
|
|
17642
|
+
* fold both comparison sides with, and folding one would derive a REMOVAL of a
|
|
17643
|
+
* live capacity block from a template whose only fault is `BillingMode`.
|
|
17644
|
+
*
|
|
17645
|
+
* Pure and non-mutating at every level.
|
|
17646
|
+
*/
|
|
17647
|
+
function retainUnsendableCapacityMembers(desired, previous, keptBillingMode) {
|
|
17648
|
+
const unsendable = keptBillingMode === "PROVISIONED" ? ON_DEMAND_ONLY_CAPACITY_KEYS : PROVISIONED_ONLY_CAPACITY_KEYS;
|
|
17649
|
+
const result = retainCapacityKeys({ ...desired }, previous, unsendable.table);
|
|
17650
|
+
const indexes = result["GlobalSecondaryIndexes"];
|
|
17651
|
+
if (Array.isArray(indexes)) {
|
|
17652
|
+
const previousIndexes = indexCapacityContainers(previous["GlobalSecondaryIndexes"], "IndexName");
|
|
17653
|
+
result["GlobalSecondaryIndexes"] = indexes.map((entry) => {
|
|
17654
|
+
const gsi = asRecord$1(entry);
|
|
17655
|
+
if (!gsi) return entry;
|
|
17656
|
+
const previousGsi = previousIndexes.get(gsi["IndexName"]);
|
|
17657
|
+
return retainCapacityKeys({ ...gsi }, previousGsi, unsendable.index);
|
|
17658
|
+
});
|
|
17659
|
+
}
|
|
17660
|
+
const replicas = result["Replicas"];
|
|
17661
|
+
if (Array.isArray(replicas)) {
|
|
17662
|
+
const previousReplicas = indexCapacityContainers(previous["Replicas"], "Region");
|
|
17663
|
+
result["Replicas"] = replicas.map((entry) => {
|
|
17664
|
+
const replica = asRecord$1(entry);
|
|
17665
|
+
if (!replica) return entry;
|
|
17666
|
+
const previousReplica = previousReplicas.get(replica["Region"]);
|
|
17667
|
+
const copy = retainCapacityKeys({ ...replica }, previousReplica, unsendable.replica);
|
|
17668
|
+
const replicaIndexes = copy["GlobalSecondaryIndexes"];
|
|
17669
|
+
if (Array.isArray(replicaIndexes)) {
|
|
17670
|
+
const previousReplicaIndexes = indexCapacityContainers(previousReplica?.["GlobalSecondaryIndexes"], "IndexName");
|
|
17671
|
+
const kept = replicaIndexes.map((indexEntry) => {
|
|
17672
|
+
const gsi = asRecord$1(indexEntry);
|
|
17673
|
+
if (!gsi) return indexEntry;
|
|
17674
|
+
const previousGsi = previousReplicaIndexes.get(gsi["IndexName"]);
|
|
17675
|
+
return retainCapacityKeys({ ...gsi }, previousGsi, unsendable.replicaIndex);
|
|
17676
|
+
}).filter((entry) => {
|
|
17677
|
+
const gsi = asRecord$1(entry);
|
|
17678
|
+
if (!gsi) return true;
|
|
17679
|
+
return Object.keys(gsi).some((k) => k !== "IndexName");
|
|
17680
|
+
});
|
|
17681
|
+
if (kept.length > 0) copy["GlobalSecondaryIndexes"] = kept;
|
|
17682
|
+
else delete copy["GlobalSecondaryIndexes"];
|
|
17683
|
+
}
|
|
17684
|
+
return copy;
|
|
17685
|
+
});
|
|
17686
|
+
}
|
|
17687
|
+
return result;
|
|
17688
|
+
}
|
|
17689
|
+
/**
|
|
17690
|
+
* Retain (or drop) one container's unsendable capacity members, in place on a
|
|
17691
|
+
* bag the caller already copied.
|
|
17692
|
+
*
|
|
17693
|
+
* The counterpart container being `undefined` — no matching index / replica in
|
|
17694
|
+
* the previous record — is the ABSENT case, not a special one: there is nothing
|
|
17695
|
+
* to vouch for, so the key is dropped exactly as it is for an unusable value.
|
|
17696
|
+
*/
|
|
17697
|
+
function retainCapacityKeys(target, previousContainer, keys) {
|
|
17698
|
+
for (const key of keys) {
|
|
17699
|
+
const previousValue = asRecord$1(previousContainer?.[key]);
|
|
17700
|
+
if (previousValue) target[key] = { ...previousValue };
|
|
17701
|
+
else delete target[key];
|
|
17702
|
+
}
|
|
17703
|
+
return target;
|
|
17704
|
+
}
|
|
17705
|
+
/**
|
|
17706
|
+
* Index a previous-side array by its identity member, so a per-index /
|
|
17707
|
+
* per-replica retain reads the counterpart entry rather than the positional
|
|
17708
|
+
* one — AWS does not guarantee readback order and a template edit can reorder
|
|
17709
|
+
* either list.
|
|
17710
|
+
*/
|
|
17711
|
+
function indexCapacityContainers(value, identityKey) {
|
|
17712
|
+
const byIdentity = /* @__PURE__ */ new Map();
|
|
17713
|
+
if (!Array.isArray(value)) return byIdentity;
|
|
17714
|
+
for (const entry of value) {
|
|
17715
|
+
const record = asRecord$1(entry);
|
|
17716
|
+
const identity = record?.[identityKey];
|
|
17717
|
+
if (record && typeof identity === "string") byIdentity.set(identity, record);
|
|
17718
|
+
}
|
|
17719
|
+
return byIdentity;
|
|
17720
|
+
}
|
|
17721
|
+
/**
|
|
17536
17722
|
* True when a CFn sub-object is nothing but an unresolved intrinsic
|
|
17537
17723
|
* (`{"Fn::If": […]}` / `{"Ref": …}`), i.e. the template DID declare the block
|
|
17538
17724
|
* but its value could not be resolved to a shape with members.
|
|
@@ -20380,6 +20566,43 @@ const SG_INGRESS_IP_PROTOCOL_DEFAULT = "-1";
|
|
|
20380
20566
|
/** The refusal path both the create guard and the canonicalizer report under. */
|
|
20381
20567
|
const SG_INGRESS_IP_PROTOCOL_PATH = "AWS::EC2::SecurityGroupIngress IpProtocol";
|
|
20382
20568
|
/**
|
|
20569
|
+
* Page ceiling for {@link EC2Provider.lookupIngressRuleId}'s
|
|
20570
|
+
* `DescribeSecurityGroupRules` walk (issue #1761).
|
|
20571
|
+
*
|
|
20572
|
+
* The walk asks for {@link SG_RULE_PAGE_SIZE} rules per page, so 20 pages cover
|
|
20573
|
+
* 20,000 rules on a single security group — far beyond any real one (AWS's
|
|
20574
|
+
* default quota is 60 rules per SG, 1000 at the maximum quota increase). That
|
|
20575
|
+
* arithmetic only holds because the request pins `MaxResults`; without it AWS
|
|
20576
|
+
* applies its own default page size and the bound would mean an unknown number
|
|
20577
|
+
* of rules. The bound exists only so a pathological / looping `NextToken` cannot
|
|
20578
|
+
* hang a deploy; reaching it records nothing rather than answering from a
|
|
20579
|
+
* partial view.
|
|
20580
|
+
*/
|
|
20581
|
+
const MAX_SG_RULE_PAGES = 20;
|
|
20582
|
+
/**
|
|
20583
|
+
* `MaxResults` for that walk. `DescribeSecurityGroupRules` accepts 5-1000; the
|
|
20584
|
+
* ceiling is what makes {@link MAX_SG_RULE_PAGES}'s "20,000 rules" true and
|
|
20585
|
+
* keeps the common case (a group with a handful of rules) to ONE round trip.
|
|
20586
|
+
*/
|
|
20587
|
+
const SG_RULE_PAGE_SIZE = 1e3;
|
|
20588
|
+
/**
|
|
20589
|
+
* The `sgr-…` security-group rule id, anchored.
|
|
20590
|
+
*
|
|
20591
|
+
* The twin of `SG_RULE_ID_PATTERN` in `src/cli/commands/export.ts`, restated
|
|
20592
|
+
* rather than imported for the same reason that file restates
|
|
20593
|
+
* `ROUTE_DESTINATION_KEYS` instead of importing it from here: neither module may
|
|
20594
|
+
* pull the other's graph (the export command would drag in this provider and its
|
|
20595
|
+
* SDK client).
|
|
20596
|
+
*
|
|
20597
|
+
* Anchoring is what discriminates. `startsWith('sgr-')` also accepts
|
|
20598
|
+
* `sgr-0abc|tcp|443|443` — a composite wearing the identifier's prefix — which
|
|
20599
|
+
* on the import path reaches AWS as a malformed id and raises
|
|
20600
|
+
* `InvalidSecurityGroupRuleId.Malformed`, an error `isNotFoundError` does not
|
|
20601
|
+
* match, so `verifyExplicit` would THROW and abort the whole `cdkd import` run
|
|
20602
|
+
* instead of declining that one row.
|
|
20603
|
+
*/
|
|
20604
|
+
const SG_RULE_ID_PATTERN$1 = /^sgr-[0-9a-f]+$/;
|
|
20605
|
+
/**
|
|
20383
20606
|
* Resolve the `IpProtocol` an `AWS::EC2::SecurityGroupIngress` rule actually
|
|
20384
20607
|
* SENDS, plus the bag that describes it (issue #1633).
|
|
20385
20608
|
*
|
|
@@ -21713,25 +21936,29 @@ var EC2Provider = class {
|
|
|
21713
21936
|
}
|
|
21714
21937
|
], onUnusableProtocol ? { onRefusal: onUnusableProtocol } : void 0);
|
|
21715
21938
|
try {
|
|
21716
|
-
await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
|
|
21939
|
+
const ruleId = singleSecurityGroupRuleId((await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
|
|
21717
21940
|
GroupId: groupId,
|
|
21718
21941
|
IpPermissions: [{
|
|
21719
21942
|
...this.buildIpPermission(properties),
|
|
21720
21943
|
IpProtocol: ipProtocol
|
|
21721
21944
|
}]
|
|
21722
|
-
}));
|
|
21945
|
+
}))).SecurityGroupRules);
|
|
21723
21946
|
this.logger.debug(`Successfully created SecurityGroupIngress ${logicalId}: ${physicalId}`);
|
|
21724
21947
|
return {
|
|
21725
21948
|
physicalId,
|
|
21726
|
-
attributes: {},
|
|
21949
|
+
attributes: ruleId ? { Id: ruleId } : {},
|
|
21727
21950
|
...effectiveProperties && { effectiveProperties }
|
|
21728
21951
|
};
|
|
21729
21952
|
} catch (error) {
|
|
21730
21953
|
if (error instanceof Error && error.message.includes("already exists")) {
|
|
21731
21954
|
this.logger.debug(`SecurityGroupIngress ${logicalId} already exists, treating as success`);
|
|
21955
|
+
const existingRuleId = await this.lookupIngressRuleId(logicalId, groupId, {
|
|
21956
|
+
...properties,
|
|
21957
|
+
IpProtocol: ipProtocol
|
|
21958
|
+
});
|
|
21732
21959
|
return {
|
|
21733
21960
|
physicalId,
|
|
21734
|
-
attributes: {},
|
|
21961
|
+
attributes: existingRuleId ? { Id: existingRuleId } : {},
|
|
21735
21962
|
...effectiveProperties && { effectiveProperties }
|
|
21736
21963
|
};
|
|
21737
21964
|
}
|
|
@@ -21739,6 +21966,70 @@ var EC2Provider = class {
|
|
|
21739
21966
|
throw new ProvisioningError(`Failed to create SecurityGroupIngress ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
|
|
21740
21967
|
}
|
|
21741
21968
|
}
|
|
21969
|
+
/**
|
|
21970
|
+
* Best-effort lookup of the `sgr-...` id of an ALREADY-EXISTING ingress rule
|
|
21971
|
+
* (issue #1761).
|
|
21972
|
+
*
|
|
21973
|
+
* Only the idempotent "already exists" arm of {@link createSecurityGroupIngress}
|
|
21974
|
+
* calls this: the successful arm reads the id straight off
|
|
21975
|
+
* `AuthorizeSecurityGroupIngress`'s own response, which is what keeps the
|
|
21976
|
+
* mutating path free of the issue #1710 orphan hazard.
|
|
21977
|
+
*
|
|
21978
|
+
* **Never throws.** A `DescribeSecurityGroupRules` failure (permissions,
|
|
21979
|
+
* throttle, a rule that no longer matches) degrades to `undefined`, which
|
|
21980
|
+
* records no attribute — `cdkd export` then refuses that one resource with
|
|
21981
|
+
* its own actionable message, which is strictly better than failing a deploy
|
|
21982
|
+
* that AWS already considers satisfied.
|
|
21983
|
+
*
|
|
21984
|
+
* Returns a value only when EXACTLY ONE non-egress rule on the group matches
|
|
21985
|
+
* the requested `(protocol, ports, source)` triple. Zero matches means the
|
|
21986
|
+
* "already exists" error came from a rule cdkd cannot pin down; two or more
|
|
21987
|
+
* means the identifier would be ambiguous, and adopting an ambiguous id is
|
|
21988
|
+
* the #1658 failure mode (a state row that looks adopted but names the wrong
|
|
21989
|
+
* AWS object).
|
|
21990
|
+
*
|
|
21991
|
+
* **Paginated, unlike every other `Describe*` in this file.** That is not an
|
|
21992
|
+
* inconsistency to tidy away: the "exactly one" verdict above is a statement
|
|
21993
|
+
* about the WHOLE group, so a truncated first page turns a genuine 2-match
|
|
21994
|
+
* into a false "exactly one" and records the wrong `sgr-` id — the precise
|
|
21995
|
+
* outcome the ambiguity fence exists to prevent. A group can hold thousands
|
|
21996
|
+
* of rules (the account-level quota is per-SG, not per-page), so truncation
|
|
21997
|
+
* is reachable. `MAX_SG_RULE_PAGES` bounds the walk; exhausting it is treated
|
|
21998
|
+
* as "could not see the whole group" and records nothing, same as a throw.
|
|
21999
|
+
*/
|
|
22000
|
+
async lookupIngressRuleId(logicalId, groupId, properties) {
|
|
22001
|
+
try {
|
|
22002
|
+
const rules = [];
|
|
22003
|
+
let nextToken;
|
|
22004
|
+
let pages = 0;
|
|
22005
|
+
do {
|
|
22006
|
+
if (pages >= MAX_SG_RULE_PAGES) {
|
|
22007
|
+
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`);
|
|
22008
|
+
return;
|
|
22009
|
+
}
|
|
22010
|
+
const resp = await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({
|
|
22011
|
+
Filters: [{
|
|
22012
|
+
Name: "group-id",
|
|
22013
|
+
Values: [groupId]
|
|
22014
|
+
}],
|
|
22015
|
+
MaxResults: SG_RULE_PAGE_SIZE,
|
|
22016
|
+
...nextToken && { NextToken: nextToken }
|
|
22017
|
+
}));
|
|
22018
|
+
rules.push(...resp.SecurityGroupRules ?? []);
|
|
22019
|
+
nextToken = resp.NextToken;
|
|
22020
|
+
pages += 1;
|
|
22021
|
+
} while (nextToken);
|
|
22022
|
+
const ids = rules.filter((rule) => rule.IsEgress !== true && securityGroupRuleMatchesCfnIngress(rule, properties)).map((rule) => rule.SecurityGroupRuleId).filter((id) => typeof id === "string" && id.trim() !== "");
|
|
22023
|
+
if (ids.length !== 1) {
|
|
22024
|
+
this.logger.debug(`SecurityGroupIngress ${logicalId}: ${ids.length} existing rule(s) on ${groupId} match the requested protocol/ports/source — not recording an Id attribute`);
|
|
22025
|
+
return;
|
|
22026
|
+
}
|
|
22027
|
+
return ids[0];
|
|
22028
|
+
} catch (error) {
|
|
22029
|
+
this.logger.debug(`SecurityGroupIngress ${logicalId}: could not look up the existing rule id on ${groupId} (${error instanceof Error ? error.message : String(error)}) — not recording an Id attribute`);
|
|
22030
|
+
return;
|
|
22031
|
+
}
|
|
22032
|
+
}
|
|
21742
22033
|
async updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
21743
22034
|
this.logger.debug(`Updating SecurityGroupIngress ${logicalId}: ${physicalId}`);
|
|
21744
22035
|
if (JSON.stringify(properties) === JSON.stringify(previousProperties)) return {
|
|
@@ -22681,13 +22972,28 @@ var EC2Provider = class {
|
|
|
22681
22972
|
/**
|
|
22682
22973
|
* Adopt an existing EC2 networking resource into cdkd state.
|
|
22683
22974
|
*
|
|
22684
|
-
*
|
|
22685
|
-
*
|
|
22686
|
-
*
|
|
22687
|
-
*
|
|
22688
|
-
*
|
|
22689
|
-
*
|
|
22690
|
-
*
|
|
22975
|
+
* Every arm here is OVERRIDE-ONLY: `import()` returns `null` unless the
|
|
22976
|
+
* caller passed `--resource <logicalId>=<physicalId>`, and `verifyExplicit`
|
|
22977
|
+
* then confirms that id against AWS. Supported types:
|
|
22978
|
+
*
|
|
22979
|
+
* - `AWS::EC2::VPC`, `AWS::EC2::Subnet`, `AWS::EC2::SecurityGroup` — verified
|
|
22980
|
+
* by id, recorded verbatim with no attributes.
|
|
22981
|
+
* - `AWS::EC2::NatGateway` — verified by id, ignoring a gateway already in
|
|
22982
|
+
* `deleted` / `deleting` state.
|
|
22983
|
+
* - `AWS::EC2::EIP` — accepts an allocation id, a public IP, or the composite
|
|
22984
|
+
* `<publicIp>|<allocationId>`, and records the composite plus the
|
|
22985
|
+
* `AllocationId` / `PublicIp` attributes.
|
|
22986
|
+
* - `AWS::EC2::SecurityGroupIngress` — accepts the `sgr-...` rule id
|
|
22987
|
+
* (CloudFormation's own identifier for the type), declines an EGRESS rule
|
|
22988
|
+
* id, and records cdkd's composite `<groupId>|<ipProtocol>|<fromPort>|<toPort>`
|
|
22989
|
+
* plus the rule id as the `Id` attribute (issue #1761).
|
|
22990
|
+
*
|
|
22991
|
+
* The remaining EC2 types this provider creates (RouteTable, Route,
|
|
22992
|
+
* InternetGateway, VPCGatewayAttachment, NetworkAcl, NetworkAclEntry,
|
|
22993
|
+
* SubnetRouteTableAssociation, SubnetNetworkAclAssociation, Instance) return
|
|
22994
|
+
* `null` — Routes and associations are derived from their parent, and the
|
|
22995
|
+
* typical adoption story is "find the VPC, cdkd reconstructs the rest at
|
|
22996
|
+
* deploy time".
|
|
22691
22997
|
*
|
|
22692
22998
|
* There is no `aws:cdk:path` tag lookup: AWS rejects `aws:`-prefixed tag
|
|
22693
22999
|
* writes, so that tag never exists on a real resource and a
|
|
@@ -23273,6 +23579,39 @@ var EC2Provider = class {
|
|
|
23273
23579
|
physicalId,
|
|
23274
23580
|
attributes: {}
|
|
23275
23581
|
} : null;
|
|
23582
|
+
case "AWS::EC2::SecurityGroupIngress": {
|
|
23583
|
+
if (!SG_RULE_ID_PATTERN$1.test(physicalId)) return null;
|
|
23584
|
+
const rule = (await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({ SecurityGroupRuleIds: [physicalId] }))).SecurityGroupRules?.[0];
|
|
23585
|
+
if (!rule || rule.IsEgress === true) return null;
|
|
23586
|
+
if (!rule.GroupId || !rule.IpProtocol || !rule.SecurityGroupRuleId) return null;
|
|
23587
|
+
const segments = [
|
|
23588
|
+
{
|
|
23589
|
+
name: "groupId",
|
|
23590
|
+
value: rule.GroupId
|
|
23591
|
+
},
|
|
23592
|
+
{
|
|
23593
|
+
name: "ipProtocol",
|
|
23594
|
+
value: rule.IpProtocol
|
|
23595
|
+
},
|
|
23596
|
+
{
|
|
23597
|
+
name: "fromPort",
|
|
23598
|
+
value: rule.FromPort ?? "-1"
|
|
23599
|
+
},
|
|
23600
|
+
{
|
|
23601
|
+
name: "toPort",
|
|
23602
|
+
value: rule.ToPort ?? "-1"
|
|
23603
|
+
}
|
|
23604
|
+
];
|
|
23605
|
+
const refusal = compositeIdSeparatorRefusal("AWS::EC2::SecurityGroupIngress", logicalId, segments);
|
|
23606
|
+
if (refusal !== void 0) {
|
|
23607
|
+
this.logger.warn(`${refusal} Skipping import.`);
|
|
23608
|
+
return null;
|
|
23609
|
+
}
|
|
23610
|
+
return {
|
|
23611
|
+
physicalId: packCompositeId("AWS::EC2::SecurityGroupIngress", logicalId, segments),
|
|
23612
|
+
attributes: { Id: rule.SecurityGroupRuleId }
|
|
23613
|
+
};
|
|
23614
|
+
}
|
|
23276
23615
|
case "AWS::EC2::NatGateway": return (await this.ec2Client.send(new DescribeNatGatewaysCommand({ NatGatewayIds: [physicalId] }))).NatGateways?.find((g) => g.State !== "deleted" && g.State !== "deleting") ? {
|
|
23277
23616
|
physicalId,
|
|
23278
23617
|
attributes: {}
|
|
@@ -23393,6 +23732,93 @@ function flattenIpPermissions(perms, direction) {
|
|
|
23393
23732
|
function sgProtocolKey(value) {
|
|
23394
23733
|
return canonicalizeIpProtocolValue(value ?? "-1");
|
|
23395
23734
|
}
|
|
23735
|
+
/**
|
|
23736
|
+
* The single `sgr-...` id an `AuthorizeSecurityGroupIngress` response reports
|
|
23737
|
+
* for a standalone `AWS::EC2::SecurityGroupIngress` (issue #1761).
|
|
23738
|
+
*
|
|
23739
|
+
* Pure by design: {@link EC2Provider.createSecurityGroupIngress} calls it
|
|
23740
|
+
* between the mutating Authorize and its return, where an `await` of anything
|
|
23741
|
+
* that can throw would re-create the issue #1710 orphan class.
|
|
23742
|
+
*
|
|
23743
|
+
* `undefined` unless the response carries EXACTLY ONE rule id. AWS mints one
|
|
23744
|
+
* rule per source entry, and cdkd sends one `IpPermission` per CFn resource —
|
|
23745
|
+
* but a template declaring both `CidrIp` and `CidrIpv6` on the same ingress
|
|
23746
|
+
* resource makes `buildIpPermission` emit two sources and AWS answers with two
|
|
23747
|
+
* rules. Neither is "the" CloudFormation identifier for the resource, so
|
|
23748
|
+
* recording nothing (and letting `cdkd export` refuse with its own message) is
|
|
23749
|
+
* the only answer that cannot silently name the wrong rule.
|
|
23750
|
+
*/
|
|
23751
|
+
function singleSecurityGroupRuleId(rules) {
|
|
23752
|
+
const ids = (rules ?? []).map((rule) => rule.SecurityGroupRuleId).filter((id) => typeof id === "string" && id.trim() !== "");
|
|
23753
|
+
return ids.length === 1 ? ids[0] : void 0;
|
|
23754
|
+
}
|
|
23755
|
+
/**
|
|
23756
|
+
* A CFn `FromPort` / `ToPort` in the NUMBER form `DescribeSecurityGroupRules`
|
|
23757
|
+
* reports, for {@link securityGroupRuleMatchesCfnIngress}'s compare.
|
|
23758
|
+
*
|
|
23759
|
+
* An identity compare against `properties['FromPort'] as number` was wrong for
|
|
23760
|
+
* the same reason issue #1513 documents one property over: a YAML / JSON
|
|
23761
|
+
* template may spell a port as the STRING `"443"`, and `buildIpPermission`
|
|
23762
|
+
* passes it through to AWS unchanged, so such a rule deploys fine and would
|
|
23763
|
+
* then never match its own AWS-side row.
|
|
23764
|
+
*
|
|
23765
|
+
* Absent, `null`, and blank (INCLUDING whitespace-only) are AWS's `-1`, "all
|
|
23766
|
+
* ports". Everything that is not a number or a numeric string is `NaN`, which
|
|
23767
|
+
* compares unequal to everything — so the lookup records nothing rather than
|
|
23768
|
+
* matching some unrelated rule.
|
|
23769
|
+
*
|
|
23770
|
+
* A bare `Number(value)` was NOT enough for that last sentence, which an earlier
|
|
23771
|
+
* cut of this function claimed anyway: `Number` maps `' '` and `[]` to `0`,
|
|
23772
|
+
* `true` to `1`, and `['443']` to `443`. A whitespace-only port would then have
|
|
23773
|
+
* matched an ICMP **type-0** rule — a different rule, silently recorded — which
|
|
23774
|
+
* is the exact outcome the claim promised it could not produce.
|
|
23775
|
+
*/
|
|
23776
|
+
function cfnIngressPortValue(value) {
|
|
23777
|
+
if (value === void 0 || value === null) return -1;
|
|
23778
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
|
|
23779
|
+
if (typeof value === "string") {
|
|
23780
|
+
const trimmed = value.trim();
|
|
23781
|
+
if (trimmed === "") return -1;
|
|
23782
|
+
const parsed = Number(trimmed);
|
|
23783
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
23784
|
+
}
|
|
23785
|
+
return NaN;
|
|
23786
|
+
}
|
|
23787
|
+
/**
|
|
23788
|
+
* Does a `DescribeSecurityGroupRules` rule describe the same ingress rule the
|
|
23789
|
+
* given CFn `AWS::EC2::SecurityGroupIngress` properties ask for?
|
|
23790
|
+
*
|
|
23791
|
+
* Deliberately NOT expressed via {@link sgRuleKey}: that key includes
|
|
23792
|
+
* `SourceSecurityGroupOwnerId` and `Description`, both of which AWS fills in on
|
|
23793
|
+
* the read side even when the template omits them, so a whole-key compare
|
|
23794
|
+
* would report "no match" for the ordinary same-account SG-to-SG rule this
|
|
23795
|
+
* lookup exists to find. Matching the fields the template actually pins —
|
|
23796
|
+
* protocol, port range, and whichever ONE source key it declares — is what
|
|
23797
|
+
* makes the comparison decidable from a partial template.
|
|
23798
|
+
*
|
|
23799
|
+
* The protocol goes through {@link sgProtocolKey} on BOTH sides, not a raw
|
|
23800
|
+
* compare: AWS substitutes the canonical NAME for the four protocols that have
|
|
23801
|
+
* one, so a template declaring `IpProtocol: 6` (or `'6'`) is read back as
|
|
23802
|
+
* `'tcp'` and would otherwise match nothing (the issue #1643 fold, one call site
|
|
23803
|
+
* further along).
|
|
23804
|
+
*
|
|
23805
|
+
* Ports are compared with AWS's own `-1` ("all") spelling on both sides, since
|
|
23806
|
+
* an omitted `FromPort` / `ToPort` reads back as `-1`, and the template side is
|
|
23807
|
+
* NUMERICALLY coerced — see {@link cfnIngressPortValue}.
|
|
23808
|
+
*/
|
|
23809
|
+
function securityGroupRuleMatchesCfnIngress(rule, properties) {
|
|
23810
|
+
if (sgProtocolKey(rule.IpProtocol) !== sgProtocolKey(properties["IpProtocol"])) return false;
|
|
23811
|
+
if ((rule.FromPort ?? -1) !== cfnIngressPortValue(properties["FromPort"])) return false;
|
|
23812
|
+
if ((rule.ToPort ?? -1) !== cfnIngressPortValue(properties["ToPort"])) return false;
|
|
23813
|
+
const sources = [
|
|
23814
|
+
[properties["CidrIp"], rule.CidrIpv4],
|
|
23815
|
+
[properties["CidrIpv6"], rule.CidrIpv6],
|
|
23816
|
+
[properties["SourceSecurityGroupId"], rule.ReferencedGroupInfo?.GroupId],
|
|
23817
|
+
[properties["SourcePrefixListId"], rule.PrefixListId]
|
|
23818
|
+
];
|
|
23819
|
+
for (const [declared, actual] of sources) if (declared !== void 0 && declared !== null && declared !== actual) return false;
|
|
23820
|
+
return true;
|
|
23821
|
+
}
|
|
23396
23822
|
function sgRuleKey(rule, direction) {
|
|
23397
23823
|
const peerKey = direction === "egress" ? rule["DestinationSecurityGroupId"] : rule["SourceSecurityGroupId"];
|
|
23398
23824
|
const prefixKey = direction === "egress" ? rule["DestinationPrefixListId"] : rule["SourcePrefixListId"];
|
|
@@ -59473,7 +59899,7 @@ function readStringProperty(properties, key, resourceType) {
|
|
|
59473
59899
|
* entry reads the value cdkd recorded in state (`attributes`), and refuses when
|
|
59474
59900
|
* it is absent rather than guessing.
|
|
59475
59901
|
*
|
|
59476
|
-
* ## Live measurement (us-east-1, 2026-08-13)
|
|
59902
|
+
* ## Live measurement (us-east-1, 2026-08-13; the SG-ingress row 2026-08-14)
|
|
59477
59903
|
*
|
|
59478
59904
|
* Every field below is BOTH the type's single-field `primaryIdentifier` AND
|
|
59479
59905
|
* `readOnlyProperties`, which is why each resolution returns an EMPTY
|
|
@@ -59485,7 +59911,19 @@ function readStringProperty(properties, key, resourceType) {
|
|
|
59485
59911
|
* | `AWS::AppSync::DataSource` | `DataSourceArn` | `attributes.DataSourceArn` |
|
|
59486
59912
|
* | `AWS::AppSync::Resolver` | `ResolverArn` | `attributes.ResolverArn` |
|
|
59487
59913
|
* | `AWS::S3Tables::Table` | `TableARN` | `attributes.TableARN` |
|
|
59488
|
-
* | `AWS::EC2::SecurityGroupIngress` | `Id` |
|
|
59914
|
+
* | `AWS::EC2::SecurityGroupIngress` | `Id` | `attributes.Id` (issue #1761) |
|
|
59915
|
+
*
|
|
59916
|
+
* The SG-ingress row is measured the same way as the rest, and separately
|
|
59917
|
+
* against a real CloudFormation stack because the three strings that name one
|
|
59918
|
+
* resource can disagree (issue #1771 found CFn reporting an EIP as the bare
|
|
59919
|
+
* public IP while Cloud Control reports the composite, and the two disagreeing
|
|
59920
|
+
* on field ORDER for `AWS::EC2::VPCGatewayAttachment`). For this type they all
|
|
59921
|
+
* AGREE: `describe-type` says `primaryIdentifier` / `readOnlyProperties` are
|
|
59922
|
+
* both exactly `["/properties/Id"]` with a full `create`/`delete`/`list`/
|
|
59923
|
+
* `read`/`update` handler set and `ProvisioningType: FULLY_MUTABLE`, and a live
|
|
59924
|
+
* stack carrying a standalone ingress rule reports `PhysicalResourceId`
|
|
59925
|
+
* `sgr-02345615af6d2db0d`, with `Ref` and `Fn::GetAtt .Id` both returning that
|
|
59926
|
+
* same value.
|
|
59489
59927
|
*
|
|
59490
59928
|
* All four declare a `read` handler, so CFn genuinely does accept them for
|
|
59491
59929
|
* IMPORT — unlike `AWS::Glue::Table`, the type issue #1659's title names, which
|
|
@@ -59510,13 +59948,70 @@ const COMPOSITE_PHYSICAL_ID_IDENTIFIERS = {
|
|
|
59510
59948
|
physicalIdShape: "<tableBucketARN>|<namespace>|<name>",
|
|
59511
59949
|
arnServiceSegment: "s3tables"
|
|
59512
59950
|
}),
|
|
59513
|
-
"AWS::EC2::SecurityGroupIngress":
|
|
59951
|
+
"AWS::EC2::SecurityGroupIngress": recordedRuleIdIdentifier()
|
|
59952
|
+
};
|
|
59953
|
+
/**
|
|
59954
|
+
* The `sgr-…` shape, anchored. Shape-only — the hex body's LENGTH is not
|
|
59955
|
+
* pinned, because this exists to tell the identifier apart from the two other
|
|
59956
|
+
* strings that can sit in the same state row, not to validate an id AWS itself
|
|
59957
|
+
* minted:
|
|
59958
|
+
*
|
|
59959
|
+
* - cdkd's own composite physicalId, which starts `sg-` (the parent group) and
|
|
59960
|
+
* carries `|` separators; and
|
|
59961
|
+
* - a bare `sg-…` group id, which a hand-edited row could put under
|
|
59962
|
+
* `attributes.Id`.
|
|
59963
|
+
*
|
|
59964
|
+
* Anchoring is what does the discriminating: `^sgr-[0-9a-f]+$` cannot match
|
|
59965
|
+
* either, because `|` and the composite's trailing segments have nowhere to go.
|
|
59966
|
+
* A bare `startsWith('sgr-')` would accept `sgr-abc|tcp|443|443`.
|
|
59967
|
+
*
|
|
59968
|
+
* Measured live (us-east-1, 2026-08-14) from a CloudFormation stack carrying a
|
|
59969
|
+
* standalone ingress rule: `PhysicalResourceId` = `sgr-02345615af6d2db0d`, and
|
|
59970
|
+
* both `Ref` and `Fn::GetAtt .Id` return that same value.
|
|
59971
|
+
*/
|
|
59972
|
+
const SG_RULE_ID_PATTERN = /^sgr-[0-9a-f]+$/;
|
|
59973
|
+
/**
|
|
59974
|
+
* The {@link CompositePhysicalIdIdentifier} for `AWS::EC2::SecurityGroupIngress`.
|
|
59975
|
+
*
|
|
59976
|
+
* The `recordedArnIdentifier` sibling of the family, differing only in that the
|
|
59977
|
+
* identifier is an `sgr-` rule id rather than an ARN — so it gets its own
|
|
59978
|
+
* builder rather than an `isUsable` callback threaded through that one, which
|
|
59979
|
+
* would make the ARN family's every message ("which is not a <service> ARN",
|
|
59980
|
+
* "so cdkd records <field>") conditional on a shape it no longer owns.
|
|
59981
|
+
*
|
|
59982
|
+
* Two accepted sources, validated by the SAME predicate — the point #1771
|
|
59983
|
+
* made about the EIP splitter's segment binding applies here verbatim:
|
|
59984
|
+
* validating one side is not a discriminator.
|
|
59985
|
+
*
|
|
59986
|
+
* 1. `attributes.Id`, which every writer produces today — a fresh deploy, a
|
|
59987
|
+
* `cdkd import --resource <logicalId>=sgr-…`, and the recursive
|
|
59988
|
+
* `--migrate-from-cloudformation` walk (which hands CFn's
|
|
59989
|
+
* `PhysicalResourceId` to `EC2Provider.import` as `knownPhysicalId`, and
|
|
59990
|
+
* that value IS the `sgr-` id — measured above).
|
|
59991
|
+
* 2. A physicalId that is ALREADY the bare rule id. **No cdkd path writes this
|
|
59992
|
+
* shape today** — unlike `AWS::S3Tables::Table`, whose Cloud-Control import
|
|
59993
|
+
* genuinely records the bare ARN — so it is accepted defensively rather than
|
|
59994
|
+
* because it is live: CFn's `PhysicalResourceId` for the type is exactly
|
|
59995
|
+
* this string, so a row hand-repaired from a CFn console listing, or written
|
|
59996
|
+
* by some future Cloud-Control routing of the type, carries an unambiguous
|
|
59997
|
+
* identifier that it would be perverse to refuse. Such a row is separately
|
|
59998
|
+
* BROKEN for `cdkd destroy` (`deleteSecurityGroupIngress` needs the 4-part
|
|
59999
|
+
* tuple), which is another reason to let the export hand it to CFn rather
|
|
60000
|
+
* than strand it.
|
|
60001
|
+
*/
|
|
60002
|
+
function recordedRuleIdIdentifier() {
|
|
60003
|
+
const isUsableIdentifier = (value) => SG_RULE_ID_PATTERN.test(value.trim());
|
|
60004
|
+
return {
|
|
59514
60005
|
field: "Id",
|
|
59515
|
-
resolve: ({ logicalId }) => {
|
|
59516
|
-
|
|
60006
|
+
resolve: ({ logicalId, physicalId, attributes }) => {
|
|
60007
|
+
const recorded = attributes["Id"];
|
|
60008
|
+
if (typeof recorded === "string" && isUsableIdentifier(recorded)) return recorded.trim();
|
|
60009
|
+
if (isUsableIdentifier(physicalId)) return physicalId.trim();
|
|
60010
|
+
const recordedNote = typeof recorded === "string" && recorded.trim() ? `attributes.Id is recorded as '${recorded.trim()}', which is not an 'sgr-…' security-group rule id` : `attributes.Id is missing or empty`;
|
|
60011
|
+
throw new Error(`cdkd's physical id for AWS::EC2::SecurityGroupIngress is the composite '<groupId>|<ipProtocol>|<fromPort>|<toPort>', but CloudFormation IMPORT identifies the rule by the 'sgr-...' security-group rule id AWS assigns, which cdkd state does not record usably for '${logicalId}' (${recordedNote}). Two causes, with different remedies. (1) The rule declares MORE THAN ONE source — e.g. both CidrIp and CidrIpv6 on the same resource — so AWS minted one rule per source and cdkd deliberately recorded neither id, because neither is 'the' identifier for this resource. Re-deploying does not help; split it into one AWS::EC2::SecurityGroupIngress resource per source, which is also the shape CloudFormation will manage after the export. (2) Otherwise the rule was deployed by a cdkd older than https://github.com/go-to-k/cdkd/issues/1761, which did not record the id at all. AWS returns it only from AuthorizeSecurityGroupIngress itself, so a no-op re-deploy will NOT heal the record either: change any property of the rule (cdkd revokes and re-authorizes, minting a fresh id, with a momentary traffic interruption) or destroy and re-deploy it, then re-run cdkd export. In both cases you can instead remove '${logicalId}' from the stack before exporting — the rule stays in AWS and can be re-declared in CloudFormation afterwards.`);
|
|
59517
60012
|
}
|
|
59518
|
-
}
|
|
59519
|
-
}
|
|
60013
|
+
};
|
|
60014
|
+
}
|
|
59520
60015
|
/**
|
|
59521
60016
|
* Build the {@link CompositePhysicalIdIdentifier} for a type whose CFn
|
|
59522
60017
|
* identifier is an ARN cdkd records in `attributes` under the SAME name as the
|
|
@@ -75943,7 +76438,7 @@ function createMigrateCommand() {
|
|
|
75943
76438
|
*/
|
|
75944
76439
|
function buildProgram() {
|
|
75945
76440
|
const program = new Command();
|
|
75946
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
76441
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.1");
|
|
75947
76442
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
75948
76443
|
const { profile } = actionCommand.optsWithGlobals();
|
|
75949
76444
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|