@go-to-k/cdkd 0.282.7 → 0.283.0
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-BNuwJ11q.js} +2 -2
- package/dist/{asg-provider-DulIbRCK.js.map → asg-provider-BNuwJ11q.js.map} +1 -1
- package/dist/cli.js +331 -22
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-NA36xDvk.js → deploy-engine-CEzMNZTA.js} +3 -3
- package/dist/{deploy-engine-NA36xDvk.js.map → deploy-engine-CEzMNZTA.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-CEzMNZTA.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-BNuwJ11q.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";
|
|
@@ -20380,6 +20380,43 @@ const SG_INGRESS_IP_PROTOCOL_DEFAULT = "-1";
|
|
|
20380
20380
|
/** The refusal path both the create guard and the canonicalizer report under. */
|
|
20381
20381
|
const SG_INGRESS_IP_PROTOCOL_PATH = "AWS::EC2::SecurityGroupIngress IpProtocol";
|
|
20382
20382
|
/**
|
|
20383
|
+
* Page ceiling for {@link EC2Provider.lookupIngressRuleId}'s
|
|
20384
|
+
* `DescribeSecurityGroupRules` walk (issue #1761).
|
|
20385
|
+
*
|
|
20386
|
+
* The walk asks for {@link SG_RULE_PAGE_SIZE} rules per page, so 20 pages cover
|
|
20387
|
+
* 20,000 rules on a single security group — far beyond any real one (AWS's
|
|
20388
|
+
* default quota is 60 rules per SG, 1000 at the maximum quota increase). That
|
|
20389
|
+
* arithmetic only holds because the request pins `MaxResults`; without it AWS
|
|
20390
|
+
* applies its own default page size and the bound would mean an unknown number
|
|
20391
|
+
* of rules. The bound exists only so a pathological / looping `NextToken` cannot
|
|
20392
|
+
* hang a deploy; reaching it records nothing rather than answering from a
|
|
20393
|
+
* partial view.
|
|
20394
|
+
*/
|
|
20395
|
+
const MAX_SG_RULE_PAGES = 20;
|
|
20396
|
+
/**
|
|
20397
|
+
* `MaxResults` for that walk. `DescribeSecurityGroupRules` accepts 5-1000; the
|
|
20398
|
+
* ceiling is what makes {@link MAX_SG_RULE_PAGES}'s "20,000 rules" true and
|
|
20399
|
+
* keeps the common case (a group with a handful of rules) to ONE round trip.
|
|
20400
|
+
*/
|
|
20401
|
+
const SG_RULE_PAGE_SIZE = 1e3;
|
|
20402
|
+
/**
|
|
20403
|
+
* The `sgr-…` security-group rule id, anchored.
|
|
20404
|
+
*
|
|
20405
|
+
* The twin of `SG_RULE_ID_PATTERN` in `src/cli/commands/export.ts`, restated
|
|
20406
|
+
* rather than imported for the same reason that file restates
|
|
20407
|
+
* `ROUTE_DESTINATION_KEYS` instead of importing it from here: neither module may
|
|
20408
|
+
* pull the other's graph (the export command would drag in this provider and its
|
|
20409
|
+
* SDK client).
|
|
20410
|
+
*
|
|
20411
|
+
* Anchoring is what discriminates. `startsWith('sgr-')` also accepts
|
|
20412
|
+
* `sgr-0abc|tcp|443|443` — a composite wearing the identifier's prefix — which
|
|
20413
|
+
* on the import path reaches AWS as a malformed id and raises
|
|
20414
|
+
* `InvalidSecurityGroupRuleId.Malformed`, an error `isNotFoundError` does not
|
|
20415
|
+
* match, so `verifyExplicit` would THROW and abort the whole `cdkd import` run
|
|
20416
|
+
* instead of declining that one row.
|
|
20417
|
+
*/
|
|
20418
|
+
const SG_RULE_ID_PATTERN$1 = /^sgr-[0-9a-f]+$/;
|
|
20419
|
+
/**
|
|
20383
20420
|
* Resolve the `IpProtocol` an `AWS::EC2::SecurityGroupIngress` rule actually
|
|
20384
20421
|
* SENDS, plus the bag that describes it (issue #1633).
|
|
20385
20422
|
*
|
|
@@ -21713,25 +21750,29 @@ var EC2Provider = class {
|
|
|
21713
21750
|
}
|
|
21714
21751
|
], onUnusableProtocol ? { onRefusal: onUnusableProtocol } : void 0);
|
|
21715
21752
|
try {
|
|
21716
|
-
await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
|
|
21753
|
+
const ruleId = singleSecurityGroupRuleId((await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
|
|
21717
21754
|
GroupId: groupId,
|
|
21718
21755
|
IpPermissions: [{
|
|
21719
21756
|
...this.buildIpPermission(properties),
|
|
21720
21757
|
IpProtocol: ipProtocol
|
|
21721
21758
|
}]
|
|
21722
|
-
}));
|
|
21759
|
+
}))).SecurityGroupRules);
|
|
21723
21760
|
this.logger.debug(`Successfully created SecurityGroupIngress ${logicalId}: ${physicalId}`);
|
|
21724
21761
|
return {
|
|
21725
21762
|
physicalId,
|
|
21726
|
-
attributes: {},
|
|
21763
|
+
attributes: ruleId ? { Id: ruleId } : {},
|
|
21727
21764
|
...effectiveProperties && { effectiveProperties }
|
|
21728
21765
|
};
|
|
21729
21766
|
} catch (error) {
|
|
21730
21767
|
if (error instanceof Error && error.message.includes("already exists")) {
|
|
21731
21768
|
this.logger.debug(`SecurityGroupIngress ${logicalId} already exists, treating as success`);
|
|
21769
|
+
const existingRuleId = await this.lookupIngressRuleId(logicalId, groupId, {
|
|
21770
|
+
...properties,
|
|
21771
|
+
IpProtocol: ipProtocol
|
|
21772
|
+
});
|
|
21732
21773
|
return {
|
|
21733
21774
|
physicalId,
|
|
21734
|
-
attributes: {},
|
|
21775
|
+
attributes: existingRuleId ? { Id: existingRuleId } : {},
|
|
21735
21776
|
...effectiveProperties && { effectiveProperties }
|
|
21736
21777
|
};
|
|
21737
21778
|
}
|
|
@@ -21739,6 +21780,70 @@ var EC2Provider = class {
|
|
|
21739
21780
|
throw new ProvisioningError(`Failed to create SecurityGroupIngress ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
|
|
21740
21781
|
}
|
|
21741
21782
|
}
|
|
21783
|
+
/**
|
|
21784
|
+
* Best-effort lookup of the `sgr-...` id of an ALREADY-EXISTING ingress rule
|
|
21785
|
+
* (issue #1761).
|
|
21786
|
+
*
|
|
21787
|
+
* Only the idempotent "already exists" arm of {@link createSecurityGroupIngress}
|
|
21788
|
+
* calls this: the successful arm reads the id straight off
|
|
21789
|
+
* `AuthorizeSecurityGroupIngress`'s own response, which is what keeps the
|
|
21790
|
+
* mutating path free of the issue #1710 orphan hazard.
|
|
21791
|
+
*
|
|
21792
|
+
* **Never throws.** A `DescribeSecurityGroupRules` failure (permissions,
|
|
21793
|
+
* throttle, a rule that no longer matches) degrades to `undefined`, which
|
|
21794
|
+
* records no attribute — `cdkd export` then refuses that one resource with
|
|
21795
|
+
* its own actionable message, which is strictly better than failing a deploy
|
|
21796
|
+
* that AWS already considers satisfied.
|
|
21797
|
+
*
|
|
21798
|
+
* Returns a value only when EXACTLY ONE non-egress rule on the group matches
|
|
21799
|
+
* the requested `(protocol, ports, source)` triple. Zero matches means the
|
|
21800
|
+
* "already exists" error came from a rule cdkd cannot pin down; two or more
|
|
21801
|
+
* means the identifier would be ambiguous, and adopting an ambiguous id is
|
|
21802
|
+
* the #1658 failure mode (a state row that looks adopted but names the wrong
|
|
21803
|
+
* AWS object).
|
|
21804
|
+
*
|
|
21805
|
+
* **Paginated, unlike every other `Describe*` in this file.** That is not an
|
|
21806
|
+
* inconsistency to tidy away: the "exactly one" verdict above is a statement
|
|
21807
|
+
* about the WHOLE group, so a truncated first page turns a genuine 2-match
|
|
21808
|
+
* into a false "exactly one" and records the wrong `sgr-` id — the precise
|
|
21809
|
+
* outcome the ambiguity fence exists to prevent. A group can hold thousands
|
|
21810
|
+
* of rules (the account-level quota is per-SG, not per-page), so truncation
|
|
21811
|
+
* is reachable. `MAX_SG_RULE_PAGES` bounds the walk; exhausting it is treated
|
|
21812
|
+
* as "could not see the whole group" and records nothing, same as a throw.
|
|
21813
|
+
*/
|
|
21814
|
+
async lookupIngressRuleId(logicalId, groupId, properties) {
|
|
21815
|
+
try {
|
|
21816
|
+
const rules = [];
|
|
21817
|
+
let nextToken;
|
|
21818
|
+
let pages = 0;
|
|
21819
|
+
do {
|
|
21820
|
+
if (pages >= MAX_SG_RULE_PAGES) {
|
|
21821
|
+
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`);
|
|
21822
|
+
return;
|
|
21823
|
+
}
|
|
21824
|
+
const resp = await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({
|
|
21825
|
+
Filters: [{
|
|
21826
|
+
Name: "group-id",
|
|
21827
|
+
Values: [groupId]
|
|
21828
|
+
}],
|
|
21829
|
+
MaxResults: SG_RULE_PAGE_SIZE,
|
|
21830
|
+
...nextToken && { NextToken: nextToken }
|
|
21831
|
+
}));
|
|
21832
|
+
rules.push(...resp.SecurityGroupRules ?? []);
|
|
21833
|
+
nextToken = resp.NextToken;
|
|
21834
|
+
pages += 1;
|
|
21835
|
+
} while (nextToken);
|
|
21836
|
+
const ids = rules.filter((rule) => rule.IsEgress !== true && securityGroupRuleMatchesCfnIngress(rule, properties)).map((rule) => rule.SecurityGroupRuleId).filter((id) => typeof id === "string" && id.trim() !== "");
|
|
21837
|
+
if (ids.length !== 1) {
|
|
21838
|
+
this.logger.debug(`SecurityGroupIngress ${logicalId}: ${ids.length} existing rule(s) on ${groupId} match the requested protocol/ports/source — not recording an Id attribute`);
|
|
21839
|
+
return;
|
|
21840
|
+
}
|
|
21841
|
+
return ids[0];
|
|
21842
|
+
} catch (error) {
|
|
21843
|
+
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`);
|
|
21844
|
+
return;
|
|
21845
|
+
}
|
|
21846
|
+
}
|
|
21742
21847
|
async updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
21743
21848
|
this.logger.debug(`Updating SecurityGroupIngress ${logicalId}: ${physicalId}`);
|
|
21744
21849
|
if (JSON.stringify(properties) === JSON.stringify(previousProperties)) return {
|
|
@@ -22681,13 +22786,28 @@ var EC2Provider = class {
|
|
|
22681
22786
|
/**
|
|
22682
22787
|
* Adopt an existing EC2 networking resource into cdkd state.
|
|
22683
22788
|
*
|
|
22684
|
-
*
|
|
22685
|
-
*
|
|
22686
|
-
*
|
|
22687
|
-
*
|
|
22688
|
-
*
|
|
22689
|
-
*
|
|
22690
|
-
*
|
|
22789
|
+
* Every arm here is OVERRIDE-ONLY: `import()` returns `null` unless the
|
|
22790
|
+
* caller passed `--resource <logicalId>=<physicalId>`, and `verifyExplicit`
|
|
22791
|
+
* then confirms that id against AWS. Supported types:
|
|
22792
|
+
*
|
|
22793
|
+
* - `AWS::EC2::VPC`, `AWS::EC2::Subnet`, `AWS::EC2::SecurityGroup` — verified
|
|
22794
|
+
* by id, recorded verbatim with no attributes.
|
|
22795
|
+
* - `AWS::EC2::NatGateway` — verified by id, ignoring a gateway already in
|
|
22796
|
+
* `deleted` / `deleting` state.
|
|
22797
|
+
* - `AWS::EC2::EIP` — accepts an allocation id, a public IP, or the composite
|
|
22798
|
+
* `<publicIp>|<allocationId>`, and records the composite plus the
|
|
22799
|
+
* `AllocationId` / `PublicIp` attributes.
|
|
22800
|
+
* - `AWS::EC2::SecurityGroupIngress` — accepts the `sgr-...` rule id
|
|
22801
|
+
* (CloudFormation's own identifier for the type), declines an EGRESS rule
|
|
22802
|
+
* id, and records cdkd's composite `<groupId>|<ipProtocol>|<fromPort>|<toPort>`
|
|
22803
|
+
* plus the rule id as the `Id` attribute (issue #1761).
|
|
22804
|
+
*
|
|
22805
|
+
* The remaining EC2 types this provider creates (RouteTable, Route,
|
|
22806
|
+
* InternetGateway, VPCGatewayAttachment, NetworkAcl, NetworkAclEntry,
|
|
22807
|
+
* SubnetRouteTableAssociation, SubnetNetworkAclAssociation, Instance) return
|
|
22808
|
+
* `null` — Routes and associations are derived from their parent, and the
|
|
22809
|
+
* typical adoption story is "find the VPC, cdkd reconstructs the rest at
|
|
22810
|
+
* deploy time".
|
|
22691
22811
|
*
|
|
22692
22812
|
* There is no `aws:cdk:path` tag lookup: AWS rejects `aws:`-prefixed tag
|
|
22693
22813
|
* writes, so that tag never exists on a real resource and a
|
|
@@ -23273,6 +23393,39 @@ var EC2Provider = class {
|
|
|
23273
23393
|
physicalId,
|
|
23274
23394
|
attributes: {}
|
|
23275
23395
|
} : null;
|
|
23396
|
+
case "AWS::EC2::SecurityGroupIngress": {
|
|
23397
|
+
if (!SG_RULE_ID_PATTERN$1.test(physicalId)) return null;
|
|
23398
|
+
const rule = (await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({ SecurityGroupRuleIds: [physicalId] }))).SecurityGroupRules?.[0];
|
|
23399
|
+
if (!rule || rule.IsEgress === true) return null;
|
|
23400
|
+
if (!rule.GroupId || !rule.IpProtocol || !rule.SecurityGroupRuleId) return null;
|
|
23401
|
+
const segments = [
|
|
23402
|
+
{
|
|
23403
|
+
name: "groupId",
|
|
23404
|
+
value: rule.GroupId
|
|
23405
|
+
},
|
|
23406
|
+
{
|
|
23407
|
+
name: "ipProtocol",
|
|
23408
|
+
value: rule.IpProtocol
|
|
23409
|
+
},
|
|
23410
|
+
{
|
|
23411
|
+
name: "fromPort",
|
|
23412
|
+
value: rule.FromPort ?? "-1"
|
|
23413
|
+
},
|
|
23414
|
+
{
|
|
23415
|
+
name: "toPort",
|
|
23416
|
+
value: rule.ToPort ?? "-1"
|
|
23417
|
+
}
|
|
23418
|
+
];
|
|
23419
|
+
const refusal = compositeIdSeparatorRefusal("AWS::EC2::SecurityGroupIngress", logicalId, segments);
|
|
23420
|
+
if (refusal !== void 0) {
|
|
23421
|
+
this.logger.warn(`${refusal} Skipping import.`);
|
|
23422
|
+
return null;
|
|
23423
|
+
}
|
|
23424
|
+
return {
|
|
23425
|
+
physicalId: packCompositeId("AWS::EC2::SecurityGroupIngress", logicalId, segments),
|
|
23426
|
+
attributes: { Id: rule.SecurityGroupRuleId }
|
|
23427
|
+
};
|
|
23428
|
+
}
|
|
23276
23429
|
case "AWS::EC2::NatGateway": return (await this.ec2Client.send(new DescribeNatGatewaysCommand({ NatGatewayIds: [physicalId] }))).NatGateways?.find((g) => g.State !== "deleted" && g.State !== "deleting") ? {
|
|
23277
23430
|
physicalId,
|
|
23278
23431
|
attributes: {}
|
|
@@ -23393,6 +23546,93 @@ function flattenIpPermissions(perms, direction) {
|
|
|
23393
23546
|
function sgProtocolKey(value) {
|
|
23394
23547
|
return canonicalizeIpProtocolValue(value ?? "-1");
|
|
23395
23548
|
}
|
|
23549
|
+
/**
|
|
23550
|
+
* The single `sgr-...` id an `AuthorizeSecurityGroupIngress` response reports
|
|
23551
|
+
* for a standalone `AWS::EC2::SecurityGroupIngress` (issue #1761).
|
|
23552
|
+
*
|
|
23553
|
+
* Pure by design: {@link EC2Provider.createSecurityGroupIngress} calls it
|
|
23554
|
+
* between the mutating Authorize and its return, where an `await` of anything
|
|
23555
|
+
* that can throw would re-create the issue #1710 orphan class.
|
|
23556
|
+
*
|
|
23557
|
+
* `undefined` unless the response carries EXACTLY ONE rule id. AWS mints one
|
|
23558
|
+
* rule per source entry, and cdkd sends one `IpPermission` per CFn resource —
|
|
23559
|
+
* but a template declaring both `CidrIp` and `CidrIpv6` on the same ingress
|
|
23560
|
+
* resource makes `buildIpPermission` emit two sources and AWS answers with two
|
|
23561
|
+
* rules. Neither is "the" CloudFormation identifier for the resource, so
|
|
23562
|
+
* recording nothing (and letting `cdkd export` refuse with its own message) is
|
|
23563
|
+
* the only answer that cannot silently name the wrong rule.
|
|
23564
|
+
*/
|
|
23565
|
+
function singleSecurityGroupRuleId(rules) {
|
|
23566
|
+
const ids = (rules ?? []).map((rule) => rule.SecurityGroupRuleId).filter((id) => typeof id === "string" && id.trim() !== "");
|
|
23567
|
+
return ids.length === 1 ? ids[0] : void 0;
|
|
23568
|
+
}
|
|
23569
|
+
/**
|
|
23570
|
+
* A CFn `FromPort` / `ToPort` in the NUMBER form `DescribeSecurityGroupRules`
|
|
23571
|
+
* reports, for {@link securityGroupRuleMatchesCfnIngress}'s compare.
|
|
23572
|
+
*
|
|
23573
|
+
* An identity compare against `properties['FromPort'] as number` was wrong for
|
|
23574
|
+
* the same reason issue #1513 documents one property over: a YAML / JSON
|
|
23575
|
+
* template may spell a port as the STRING `"443"`, and `buildIpPermission`
|
|
23576
|
+
* passes it through to AWS unchanged, so such a rule deploys fine and would
|
|
23577
|
+
* then never match its own AWS-side row.
|
|
23578
|
+
*
|
|
23579
|
+
* Absent, `null`, and blank (INCLUDING whitespace-only) are AWS's `-1`, "all
|
|
23580
|
+
* ports". Everything that is not a number or a numeric string is `NaN`, which
|
|
23581
|
+
* compares unequal to everything — so the lookup records nothing rather than
|
|
23582
|
+
* matching some unrelated rule.
|
|
23583
|
+
*
|
|
23584
|
+
* A bare `Number(value)` was NOT enough for that last sentence, which an earlier
|
|
23585
|
+
* cut of this function claimed anyway: `Number` maps `' '` and `[]` to `0`,
|
|
23586
|
+
* `true` to `1`, and `['443']` to `443`. A whitespace-only port would then have
|
|
23587
|
+
* matched an ICMP **type-0** rule — a different rule, silently recorded — which
|
|
23588
|
+
* is the exact outcome the claim promised it could not produce.
|
|
23589
|
+
*/
|
|
23590
|
+
function cfnIngressPortValue(value) {
|
|
23591
|
+
if (value === void 0 || value === null) return -1;
|
|
23592
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
|
|
23593
|
+
if (typeof value === "string") {
|
|
23594
|
+
const trimmed = value.trim();
|
|
23595
|
+
if (trimmed === "") return -1;
|
|
23596
|
+
const parsed = Number(trimmed);
|
|
23597
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
23598
|
+
}
|
|
23599
|
+
return NaN;
|
|
23600
|
+
}
|
|
23601
|
+
/**
|
|
23602
|
+
* Does a `DescribeSecurityGroupRules` rule describe the same ingress rule the
|
|
23603
|
+
* given CFn `AWS::EC2::SecurityGroupIngress` properties ask for?
|
|
23604
|
+
*
|
|
23605
|
+
* Deliberately NOT expressed via {@link sgRuleKey}: that key includes
|
|
23606
|
+
* `SourceSecurityGroupOwnerId` and `Description`, both of which AWS fills in on
|
|
23607
|
+
* the read side even when the template omits them, so a whole-key compare
|
|
23608
|
+
* would report "no match" for the ordinary same-account SG-to-SG rule this
|
|
23609
|
+
* lookup exists to find. Matching the fields the template actually pins —
|
|
23610
|
+
* protocol, port range, and whichever ONE source key it declares — is what
|
|
23611
|
+
* makes the comparison decidable from a partial template.
|
|
23612
|
+
*
|
|
23613
|
+
* The protocol goes through {@link sgProtocolKey} on BOTH sides, not a raw
|
|
23614
|
+
* compare: AWS substitutes the canonical NAME for the four protocols that have
|
|
23615
|
+
* one, so a template declaring `IpProtocol: 6` (or `'6'`) is read back as
|
|
23616
|
+
* `'tcp'` and would otherwise match nothing (the issue #1643 fold, one call site
|
|
23617
|
+
* further along).
|
|
23618
|
+
*
|
|
23619
|
+
* Ports are compared with AWS's own `-1` ("all") spelling on both sides, since
|
|
23620
|
+
* an omitted `FromPort` / `ToPort` reads back as `-1`, and the template side is
|
|
23621
|
+
* NUMERICALLY coerced — see {@link cfnIngressPortValue}.
|
|
23622
|
+
*/
|
|
23623
|
+
function securityGroupRuleMatchesCfnIngress(rule, properties) {
|
|
23624
|
+
if (sgProtocolKey(rule.IpProtocol) !== sgProtocolKey(properties["IpProtocol"])) return false;
|
|
23625
|
+
if ((rule.FromPort ?? -1) !== cfnIngressPortValue(properties["FromPort"])) return false;
|
|
23626
|
+
if ((rule.ToPort ?? -1) !== cfnIngressPortValue(properties["ToPort"])) return false;
|
|
23627
|
+
const sources = [
|
|
23628
|
+
[properties["CidrIp"], rule.CidrIpv4],
|
|
23629
|
+
[properties["CidrIpv6"], rule.CidrIpv6],
|
|
23630
|
+
[properties["SourceSecurityGroupId"], rule.ReferencedGroupInfo?.GroupId],
|
|
23631
|
+
[properties["SourcePrefixListId"], rule.PrefixListId]
|
|
23632
|
+
];
|
|
23633
|
+
for (const [declared, actual] of sources) if (declared !== void 0 && declared !== null && declared !== actual) return false;
|
|
23634
|
+
return true;
|
|
23635
|
+
}
|
|
23396
23636
|
function sgRuleKey(rule, direction) {
|
|
23397
23637
|
const peerKey = direction === "egress" ? rule["DestinationSecurityGroupId"] : rule["SourceSecurityGroupId"];
|
|
23398
23638
|
const prefixKey = direction === "egress" ? rule["DestinationPrefixListId"] : rule["SourcePrefixListId"];
|
|
@@ -59473,7 +59713,7 @@ function readStringProperty(properties, key, resourceType) {
|
|
|
59473
59713
|
* entry reads the value cdkd recorded in state (`attributes`), and refuses when
|
|
59474
59714
|
* it is absent rather than guessing.
|
|
59475
59715
|
*
|
|
59476
|
-
* ## Live measurement (us-east-1, 2026-08-13)
|
|
59716
|
+
* ## Live measurement (us-east-1, 2026-08-13; the SG-ingress row 2026-08-14)
|
|
59477
59717
|
*
|
|
59478
59718
|
* Every field below is BOTH the type's single-field `primaryIdentifier` AND
|
|
59479
59719
|
* `readOnlyProperties`, which is why each resolution returns an EMPTY
|
|
@@ -59485,7 +59725,19 @@ function readStringProperty(properties, key, resourceType) {
|
|
|
59485
59725
|
* | `AWS::AppSync::DataSource` | `DataSourceArn` | `attributes.DataSourceArn` |
|
|
59486
59726
|
* | `AWS::AppSync::Resolver` | `ResolverArn` | `attributes.ResolverArn` |
|
|
59487
59727
|
* | `AWS::S3Tables::Table` | `TableARN` | `attributes.TableARN` |
|
|
59488
|
-
* | `AWS::EC2::SecurityGroupIngress` | `Id` |
|
|
59728
|
+
* | `AWS::EC2::SecurityGroupIngress` | `Id` | `attributes.Id` (issue #1761) |
|
|
59729
|
+
*
|
|
59730
|
+
* The SG-ingress row is measured the same way as the rest, and separately
|
|
59731
|
+
* against a real CloudFormation stack because the three strings that name one
|
|
59732
|
+
* resource can disagree (issue #1771 found CFn reporting an EIP as the bare
|
|
59733
|
+
* public IP while Cloud Control reports the composite, and the two disagreeing
|
|
59734
|
+
* on field ORDER for `AWS::EC2::VPCGatewayAttachment`). For this type they all
|
|
59735
|
+
* AGREE: `describe-type` says `primaryIdentifier` / `readOnlyProperties` are
|
|
59736
|
+
* both exactly `["/properties/Id"]` with a full `create`/`delete`/`list`/
|
|
59737
|
+
* `read`/`update` handler set and `ProvisioningType: FULLY_MUTABLE`, and a live
|
|
59738
|
+
* stack carrying a standalone ingress rule reports `PhysicalResourceId`
|
|
59739
|
+
* `sgr-02345615af6d2db0d`, with `Ref` and `Fn::GetAtt .Id` both returning that
|
|
59740
|
+
* same value.
|
|
59489
59741
|
*
|
|
59490
59742
|
* All four declare a `read` handler, so CFn genuinely does accept them for
|
|
59491
59743
|
* IMPORT — unlike `AWS::Glue::Table`, the type issue #1659's title names, which
|
|
@@ -59510,13 +59762,70 @@ const COMPOSITE_PHYSICAL_ID_IDENTIFIERS = {
|
|
|
59510
59762
|
physicalIdShape: "<tableBucketARN>|<namespace>|<name>",
|
|
59511
59763
|
arnServiceSegment: "s3tables"
|
|
59512
59764
|
}),
|
|
59513
|
-
"AWS::EC2::SecurityGroupIngress":
|
|
59765
|
+
"AWS::EC2::SecurityGroupIngress": recordedRuleIdIdentifier()
|
|
59766
|
+
};
|
|
59767
|
+
/**
|
|
59768
|
+
* The `sgr-…` shape, anchored. Shape-only — the hex body's LENGTH is not
|
|
59769
|
+
* pinned, because this exists to tell the identifier apart from the two other
|
|
59770
|
+
* strings that can sit in the same state row, not to validate an id AWS itself
|
|
59771
|
+
* minted:
|
|
59772
|
+
*
|
|
59773
|
+
* - cdkd's own composite physicalId, which starts `sg-` (the parent group) and
|
|
59774
|
+
* carries `|` separators; and
|
|
59775
|
+
* - a bare `sg-…` group id, which a hand-edited row could put under
|
|
59776
|
+
* `attributes.Id`.
|
|
59777
|
+
*
|
|
59778
|
+
* Anchoring is what does the discriminating: `^sgr-[0-9a-f]+$` cannot match
|
|
59779
|
+
* either, because `|` and the composite's trailing segments have nowhere to go.
|
|
59780
|
+
* A bare `startsWith('sgr-')` would accept `sgr-abc|tcp|443|443`.
|
|
59781
|
+
*
|
|
59782
|
+
* Measured live (us-east-1, 2026-08-14) from a CloudFormation stack carrying a
|
|
59783
|
+
* standalone ingress rule: `PhysicalResourceId` = `sgr-02345615af6d2db0d`, and
|
|
59784
|
+
* both `Ref` and `Fn::GetAtt .Id` return that same value.
|
|
59785
|
+
*/
|
|
59786
|
+
const SG_RULE_ID_PATTERN = /^sgr-[0-9a-f]+$/;
|
|
59787
|
+
/**
|
|
59788
|
+
* The {@link CompositePhysicalIdIdentifier} for `AWS::EC2::SecurityGroupIngress`.
|
|
59789
|
+
*
|
|
59790
|
+
* The `recordedArnIdentifier` sibling of the family, differing only in that the
|
|
59791
|
+
* identifier is an `sgr-` rule id rather than an ARN — so it gets its own
|
|
59792
|
+
* builder rather than an `isUsable` callback threaded through that one, which
|
|
59793
|
+
* would make the ARN family's every message ("which is not a <service> ARN",
|
|
59794
|
+
* "so cdkd records <field>") conditional on a shape it no longer owns.
|
|
59795
|
+
*
|
|
59796
|
+
* Two accepted sources, validated by the SAME predicate — the point #1771
|
|
59797
|
+
* made about the EIP splitter's segment binding applies here verbatim:
|
|
59798
|
+
* validating one side is not a discriminator.
|
|
59799
|
+
*
|
|
59800
|
+
* 1. `attributes.Id`, which every writer produces today — a fresh deploy, a
|
|
59801
|
+
* `cdkd import --resource <logicalId>=sgr-…`, and the recursive
|
|
59802
|
+
* `--migrate-from-cloudformation` walk (which hands CFn's
|
|
59803
|
+
* `PhysicalResourceId` to `EC2Provider.import` as `knownPhysicalId`, and
|
|
59804
|
+
* that value IS the `sgr-` id — measured above).
|
|
59805
|
+
* 2. A physicalId that is ALREADY the bare rule id. **No cdkd path writes this
|
|
59806
|
+
* shape today** — unlike `AWS::S3Tables::Table`, whose Cloud-Control import
|
|
59807
|
+
* genuinely records the bare ARN — so it is accepted defensively rather than
|
|
59808
|
+
* because it is live: CFn's `PhysicalResourceId` for the type is exactly
|
|
59809
|
+
* this string, so a row hand-repaired from a CFn console listing, or written
|
|
59810
|
+
* by some future Cloud-Control routing of the type, carries an unambiguous
|
|
59811
|
+
* identifier that it would be perverse to refuse. Such a row is separately
|
|
59812
|
+
* BROKEN for `cdkd destroy` (`deleteSecurityGroupIngress` needs the 4-part
|
|
59813
|
+
* tuple), which is another reason to let the export hand it to CFn rather
|
|
59814
|
+
* than strand it.
|
|
59815
|
+
*/
|
|
59816
|
+
function recordedRuleIdIdentifier() {
|
|
59817
|
+
const isUsableIdentifier = (value) => SG_RULE_ID_PATTERN.test(value.trim());
|
|
59818
|
+
return {
|
|
59514
59819
|
field: "Id",
|
|
59515
|
-
resolve: ({ logicalId }) => {
|
|
59516
|
-
|
|
59820
|
+
resolve: ({ logicalId, physicalId, attributes }) => {
|
|
59821
|
+
const recorded = attributes["Id"];
|
|
59822
|
+
if (typeof recorded === "string" && isUsableIdentifier(recorded)) return recorded.trim();
|
|
59823
|
+
if (isUsableIdentifier(physicalId)) return physicalId.trim();
|
|
59824
|
+
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`;
|
|
59825
|
+
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
59826
|
}
|
|
59518
|
-
}
|
|
59519
|
-
}
|
|
59827
|
+
};
|
|
59828
|
+
}
|
|
59520
59829
|
/**
|
|
59521
59830
|
* Build the {@link CompositePhysicalIdIdentifier} for a type whose CFn
|
|
59522
59831
|
* identifier is an ARN cdkd records in `attributes` under the SAME name as the
|
|
@@ -75943,7 +76252,7 @@ function createMigrateCommand() {
|
|
|
75943
76252
|
*/
|
|
75944
76253
|
function buildProgram() {
|
|
75945
76254
|
const program = new Command();
|
|
75946
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
76255
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.0");
|
|
75947
76256
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
75948
76257
|
const { profile } = actionCommand.optsWithGlobals();
|
|
75949
76258
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|