@go-to-k/cdkd 0.51.2 → 0.51.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -21733,10 +21733,21 @@ var EC2Provider = class {
21733
21733
  * `AllocationId`, `ConnectivityType`, `PrivateIpAddress`.
21734
21734
  * - **AWS::EC2::RouteTable**: `DescribeRouteTables` for `VpcId`.
21735
21735
  * - **AWS::EC2::SecurityGroup**: `DescribeSecurityGroups` for
21736
- * `GroupName`, `GroupDescription`, `VpcId`. Ingress / egress rules
21737
- * are NOT surfaced — the CFn shape is rule-list-style, while AWS
21738
- * returns IpPermissions in a different normalized form, and a
21739
- * faithful reverse-mapping is out of scope for v1.
21736
+ * `GroupName`, `GroupDescription`, `VpcId`, plus `SecurityGroupIngress`
21737
+ * and `SecurityGroupEgress` reverse-mapped from AWS's normalized
21738
+ * `IpPermissions[]` / `IpPermissionsEgress[]` form. Each AWS
21739
+ * `IpPermission` is flattened into one CFn rule per `IpRanges` /
21740
+ * `Ipv6Ranges` / `UserIdGroupPairs` / `PrefixListIds` entry; field
21741
+ * names follow CFn direction conventions (`Source*` for ingress,
21742
+ * `Destination*` for egress). When state templates ingress/egress
21743
+ * rules, AWS's response is reordered to match state's positional
21744
+ * order via `reconcileSgRules` so the comparator's array compare
21745
+ * doesn't fire false drift on AWS's normalized ordering. The
21746
+ * AWS-auto-attached "allow-all 0.0.0.0/0" egress rule is filtered
21747
+ * out of `SecurityGroupEgress` when state did not template egress
21748
+ * (the auto-default is invisible to drift). Both arrays are
21749
+ * always emitted (even as `[]`) so the v3 `observedProperties`
21750
+ * baseline catches console-side rule ADDs to a templated SG.
21740
21751
  * - **AWS::EC2::Instance**: `DescribeInstances` for `ImageId`,
21741
21752
  * `InstanceType`, `KeyName`, `SubnetId`. SecurityGroupIds /
21742
21753
  * BlockDeviceMappings shape-match is out of scope for v1.
@@ -21761,7 +21772,7 @@ var EC2Provider = class {
21761
21772
  * Returns `undefined` when the resource is gone (any `*NotFound` /
21762
21773
  * `Invalid*` error from the EC2 SDK matches `isNotFoundError`).
21763
21774
  */
21764
- async readCurrentState(physicalId, logicalId, resourceType) {
21775
+ async readCurrentState(physicalId, logicalId, resourceType, properties) {
21765
21776
  try {
21766
21777
  switch (resourceType) {
21767
21778
  case "AWS::EC2::VPC":
@@ -21775,7 +21786,7 @@ var EC2Provider = class {
21775
21786
  case "AWS::EC2::RouteTable":
21776
21787
  return await this.readRouteTableCurrentState(physicalId);
21777
21788
  case "AWS::EC2::SecurityGroup":
21778
- return await this.readSecurityGroupCurrentState(physicalId);
21789
+ return await this.readSecurityGroupCurrentState(physicalId, properties);
21779
21790
  case "AWS::EC2::Instance":
21780
21791
  return await this.readInstanceCurrentState(physicalId);
21781
21792
  case "AWS::EC2::NetworkAcl":
@@ -21884,7 +21895,7 @@ var EC2Provider = class {
21884
21895
  result["VpcId"] = rt.VpcId;
21885
21896
  return result;
21886
21897
  }
21887
- async readSecurityGroupCurrentState(physicalId) {
21898
+ async readSecurityGroupCurrentState(physicalId, properties) {
21888
21899
  const resp = await this.ec2Client.send(
21889
21900
  new DescribeSecurityGroupsCommand2({ GroupIds: [physicalId] })
21890
21901
  );
@@ -21898,6 +21909,15 @@ var EC2Provider = class {
21898
21909
  result["GroupDescription"] = sg.Description;
21899
21910
  if (sg.VpcId !== void 0)
21900
21911
  result["VpcId"] = sg.VpcId;
21912
+ const stateIngress = Array.isArray(properties?.["SecurityGroupIngress"]) ? properties["SecurityGroupIngress"] : void 0;
21913
+ const ingressRules = flattenIpPermissions(sg.IpPermissions ?? [], "ingress");
21914
+ result["SecurityGroupIngress"] = reconcileSgRules(ingressRules, stateIngress, "ingress");
21915
+ const stateEgress = Array.isArray(properties?.["SecurityGroupEgress"]) ? properties["SecurityGroupEgress"] : void 0;
21916
+ let egressRules = flattenIpPermissions(sg.IpPermissionsEgress ?? [], "egress");
21917
+ if (stateEgress === void 0) {
21918
+ egressRules = egressRules.filter((r) => !isDefaultEgressRule(r));
21919
+ }
21920
+ result["SecurityGroupEgress"] = reconcileSgRules(egressRules, stateEgress, "egress");
21901
21921
  return result;
21902
21922
  }
21903
21923
  async readInstanceCurrentState(physicalId) {
@@ -21967,6 +21987,115 @@ var EC2Provider = class {
21967
21987
  }
21968
21988
  }
21969
21989
  };
21990
+ function flattenIpPermissions(perms, direction) {
21991
+ const out = [];
21992
+ for (const p of perms) {
21993
+ const base = {};
21994
+ if (p.IpProtocol !== void 0)
21995
+ base["IpProtocol"] = p.IpProtocol;
21996
+ if (p.FromPort !== void 0)
21997
+ base["FromPort"] = p.FromPort;
21998
+ if (p.ToPort !== void 0)
21999
+ base["ToPort"] = p.ToPort;
22000
+ for (const ip of p.IpRanges ?? []) {
22001
+ const rule = { ...base };
22002
+ if (ip.CidrIp !== void 0)
22003
+ rule["CidrIp"] = ip.CidrIp;
22004
+ if (ip.Description !== void 0)
22005
+ rule["Description"] = ip.Description;
22006
+ out.push(rule);
22007
+ }
22008
+ for (const ipv6 of p.Ipv6Ranges ?? []) {
22009
+ const rule = { ...base };
22010
+ if (ipv6.CidrIpv6 !== void 0)
22011
+ rule["CidrIpv6"] = ipv6.CidrIpv6;
22012
+ if (ipv6.Description !== void 0)
22013
+ rule["Description"] = ipv6.Description;
22014
+ out.push(rule);
22015
+ }
22016
+ for (const grp of p.UserIdGroupPairs ?? []) {
22017
+ const rule = { ...base };
22018
+ if (direction === "ingress") {
22019
+ if (grp.GroupId !== void 0)
22020
+ rule["SourceSecurityGroupId"] = grp.GroupId;
22021
+ if (grp.UserId !== void 0)
22022
+ rule["SourceSecurityGroupOwnerId"] = grp.UserId;
22023
+ } else {
22024
+ if (grp.GroupId !== void 0)
22025
+ rule["DestinationSecurityGroupId"] = grp.GroupId;
22026
+ }
22027
+ if (grp.Description !== void 0)
22028
+ rule["Description"] = grp.Description;
22029
+ out.push(rule);
22030
+ }
22031
+ for (const pl of p.PrefixListIds ?? []) {
22032
+ const rule = { ...base };
22033
+ if (direction === "ingress") {
22034
+ if (pl.PrefixListId !== void 0)
22035
+ rule["SourcePrefixListId"] = pl.PrefixListId;
22036
+ } else {
22037
+ if (pl.PrefixListId !== void 0)
22038
+ rule["DestinationPrefixListId"] = pl.PrefixListId;
22039
+ }
22040
+ if (pl.Description !== void 0)
22041
+ rule["Description"] = pl.Description;
22042
+ out.push(rule);
22043
+ }
22044
+ if ((p.IpRanges?.length ?? 0) === 0 && (p.Ipv6Ranges?.length ?? 0) === 0 && (p.UserIdGroupPairs?.length ?? 0) === 0 && (p.PrefixListIds?.length ?? 0) === 0) {
22045
+ out.push({ ...base });
22046
+ }
22047
+ }
22048
+ return out;
22049
+ }
22050
+ function sgRuleKey(rule, direction) {
22051
+ const peerKey = direction === "egress" ? rule["DestinationSecurityGroupId"] : rule["SourceSecurityGroupId"];
22052
+ const prefixKey = direction === "egress" ? rule["DestinationPrefixListId"] : rule["SourcePrefixListId"];
22053
+ const peerOwner = direction === "ingress" ? rule["SourceSecurityGroupOwnerId"] : void 0;
22054
+ return JSON.stringify({
22055
+ p: rule["IpProtocol"] ?? "-1",
22056
+ f: rule["FromPort"] ?? null,
22057
+ t: rule["ToPort"] ?? null,
22058
+ c4: rule["CidrIp"] ?? null,
22059
+ c6: rule["CidrIpv6"] ?? null,
22060
+ peer: peerKey ?? null,
22061
+ peerOwner: peerOwner ?? null,
22062
+ pl: prefixKey ?? null,
22063
+ d: rule["Description"] ?? null
22064
+ });
22065
+ }
22066
+ function reconcileSgRules(awsRules, stateRules, direction) {
22067
+ if (!stateRules || stateRules.length === 0)
22068
+ return awsRules;
22069
+ const remaining = [...awsRules];
22070
+ const reordered = [];
22071
+ for (const sr of stateRules) {
22072
+ const key = sgRuleKey(sr, direction);
22073
+ const idx = remaining.findIndex((ar) => sgRuleKey(ar, direction) === key);
22074
+ if (idx >= 0) {
22075
+ reordered.push(remaining.splice(idx, 1)[0]);
22076
+ }
22077
+ }
22078
+ return [...reordered, ...remaining];
22079
+ }
22080
+ function isDefaultEgressRule(rule) {
22081
+ if (rule["IpProtocol"] !== "-1")
22082
+ return false;
22083
+ if (rule["CidrIp"] !== "0.0.0.0/0")
22084
+ return false;
22085
+ if (rule["CidrIpv6"] !== void 0)
22086
+ return false;
22087
+ if (rule["DestinationSecurityGroupId"] !== void 0)
22088
+ return false;
22089
+ if (rule["DestinationPrefixListId"] !== void 0)
22090
+ return false;
22091
+ if (rule["Description"] !== void 0)
22092
+ return false;
22093
+ if (rule["FromPort"] !== void 0 && rule["FromPort"] !== -1)
22094
+ return false;
22095
+ if (rule["ToPort"] !== void 0 && rule["ToPort"] !== -1)
22096
+ return false;
22097
+ return true;
22098
+ }
21970
22099
 
21971
22100
  // src/provisioning/providers/apigateway-provider.ts
21972
22101
  import {
@@ -35218,20 +35347,26 @@ var FirehoseProvider = class {
35218
35347
  * `KinesisStreamSourceConfiguration` parent fields when present (the
35219
35348
  * `DescribeDeliveryStream` response splits source under `Source.KinesisStreamSourceDescription`).
35220
35349
  *
35221
- * Destination configurations (`*DestinationConfiguration` in CFn vs.
35222
- * `*DestinationDescription` in `DescribeDeliveryStream`) are intentionally
35223
- * not re-shaped here. Their nested fields are large and the description
35224
- * vs. configuration shape divergence (extra metadata, write-only fields
35225
- * like `Password` redacted) makes a clean comparator surface impossible
35226
- * for v1. We do surface the destination *kind* under a stable key so
35227
- * users at least see destination drift across types, but not the inner
35228
- * fields. Drift on destination contents is best chased manually via
35229
- * `aws firehose describe-delivery-stream` for now.
35350
+ * **Destination configurations**: partial coverage. AWS returns destination
35351
+ * config under `Destinations[0].*DestinationDescription` (note:
35352
+ * `Description`, not `Configuration`). For S3 / ExtendedS3 destinations
35353
+ * the top-level fields with a clean reverse-mapping are surfaced —
35354
+ * `BucketARN`, `RoleARN`, `Prefix`, `ErrorOutputPrefix`, `BufferingHints`,
35355
+ * `CompressionFormat`, plus `S3BackupMode` for Extended. Inner nested
35356
+ * fields (`EncryptionConfiguration`, `CloudWatchLoggingOptions`,
35357
+ * `ProcessingConfiguration`, `DataFormatConversionConfiguration`,
35358
+ * `DynamicPartitioningConfiguration`, `S3BackupConfiguration`) are not
35359
+ * re-shaped — AWS auto-defaults / extra-metadata / write-only redaction
35360
+ * (`Password`) make the round-trip unsafe; they're declared via
35361
+ * `getDriftUnknownPaths()` so the comparator skips them instead of firing
35362
+ * false drift. Non-S3 destination types
35363
+ * (`Redshift`/`Elasticsearch`/`Amazonopensearchservice`/`Splunk`/`HttpEndpoint`/`AmazonOpenSearchServerless`)
35364
+ * stay drift-unknown for v1 — same shape-divergence problem at scale.
35365
+ * `DeliveryStreamEncryptionConfigurationInput` also drift-unknown.
35230
35366
  *
35231
35367
  * Tags are surfaced via a follow-up `ListTagsForDeliveryStream` call
35232
- * with `aws:*` filtered out and the result key omitted when empty.
35233
- * `DeliveryStreamEncryptionConfigurationInput` is still skipped (shape
35234
- * decision deferred).
35368
+ * with `aws:*` filtered out and always emitted as `[]` placeholder when
35369
+ * no user tags remain.
35235
35370
  *
35236
35371
  * Returns `undefined` when the stream is gone (`ResourceNotFoundException`).
35237
35372
  */
@@ -35267,6 +35402,60 @@ var FirehoseProvider = class {
35267
35402
  result["KinesisStreamSourceConfiguration"] = srcOut;
35268
35403
  }
35269
35404
  }
35405
+ const dest = desc.Destinations?.[0];
35406
+ if (dest?.ExtendedS3DestinationDescription) {
35407
+ const ext = dest.ExtendedS3DestinationDescription;
35408
+ const out = {};
35409
+ if (ext.BucketARN !== void 0)
35410
+ out["BucketARN"] = ext.BucketARN;
35411
+ if (ext.RoleARN !== void 0)
35412
+ out["RoleARN"] = ext.RoleARN;
35413
+ if (ext.Prefix !== void 0)
35414
+ out["Prefix"] = ext.Prefix;
35415
+ if (ext.ErrorOutputPrefix !== void 0)
35416
+ out["ErrorOutputPrefix"] = ext.ErrorOutputPrefix;
35417
+ if (ext.CompressionFormat !== void 0)
35418
+ out["CompressionFormat"] = ext.CompressionFormat;
35419
+ if (ext.BufferingHints) {
35420
+ const hints = {};
35421
+ if (ext.BufferingHints.SizeInMBs !== void 0)
35422
+ hints["SizeInMBs"] = ext.BufferingHints.SizeInMBs;
35423
+ if (ext.BufferingHints.IntervalInSeconds !== void 0)
35424
+ hints["IntervalInSeconds"] = ext.BufferingHints.IntervalInSeconds;
35425
+ if (Object.keys(hints).length > 0)
35426
+ out["BufferingHints"] = hints;
35427
+ }
35428
+ if (ext.S3BackupMode !== void 0)
35429
+ out["S3BackupMode"] = ext.S3BackupMode;
35430
+ if (Object.keys(out).length > 0) {
35431
+ result["ExtendedS3DestinationConfiguration"] = out;
35432
+ }
35433
+ } else if (dest?.S3DestinationDescription) {
35434
+ const s3 = dest.S3DestinationDescription;
35435
+ const out = {};
35436
+ if (s3.BucketARN !== void 0)
35437
+ out["BucketARN"] = s3.BucketARN;
35438
+ if (s3.RoleARN !== void 0)
35439
+ out["RoleARN"] = s3.RoleARN;
35440
+ if (s3.Prefix !== void 0)
35441
+ out["Prefix"] = s3.Prefix;
35442
+ if (s3.ErrorOutputPrefix !== void 0)
35443
+ out["ErrorOutputPrefix"] = s3.ErrorOutputPrefix;
35444
+ if (s3.CompressionFormat !== void 0)
35445
+ out["CompressionFormat"] = s3.CompressionFormat;
35446
+ if (s3.BufferingHints) {
35447
+ const hints = {};
35448
+ if (s3.BufferingHints.SizeInMBs !== void 0)
35449
+ hints["SizeInMBs"] = s3.BufferingHints.SizeInMBs;
35450
+ if (s3.BufferingHints.IntervalInSeconds !== void 0)
35451
+ hints["IntervalInSeconds"] = s3.BufferingHints.IntervalInSeconds;
35452
+ if (Object.keys(hints).length > 0)
35453
+ out["BufferingHints"] = hints;
35454
+ }
35455
+ if (Object.keys(out).length > 0) {
35456
+ result["S3DestinationConfiguration"] = out;
35457
+ }
35458
+ }
35270
35459
  try {
35271
35460
  const tagsResp = await this.getClient().send(
35272
35461
  new ListTagsForDeliveryStreamCommand({ DeliveryStreamName: physicalId })
@@ -35282,6 +35471,45 @@ var FirehoseProvider = class {
35282
35471
  }
35283
35472
  return result;
35284
35473
  }
35474
+ /**
35475
+ * Drift-unknown paths for `AWS::KinesisFirehose::DeliveryStream`.
35476
+ *
35477
+ * The drift comparator skips these state property paths so they never
35478
+ * fire false-positive drift on every run. See the `readCurrentState`
35479
+ * docstring for the full rationale per category.
35480
+ *
35481
+ * Categories:
35482
+ * - Inner nested fields under S3 / ExtendedS3 destinations: shape
35483
+ * divergence between `Configuration` (CFn input) and `Description`
35484
+ * (AWS read), AWS auto-defaults, write-only fields.
35485
+ * - Non-S3 destination types: same shape-divergence problem at scale,
35486
+ * deferred to a follow-up.
35487
+ * - `DeliveryStreamEncryptionConfigurationInput`: input-only shape
35488
+ * (`KeyARN` + `KeyType`) vs. read-side `DeliveryStreamEncryptionConfiguration`
35489
+ * (extra status / failure fields); not yet round-tripped.
35490
+ */
35491
+ getDriftUnknownPaths() {
35492
+ return [
35493
+ // S3 / ExtendedS3 nested fields with shape divergence
35494
+ "S3DestinationConfiguration.EncryptionConfiguration",
35495
+ "S3DestinationConfiguration.CloudWatchLoggingOptions",
35496
+ "ExtendedS3DestinationConfiguration.EncryptionConfiguration",
35497
+ "ExtendedS3DestinationConfiguration.CloudWatchLoggingOptions",
35498
+ "ExtendedS3DestinationConfiguration.ProcessingConfiguration",
35499
+ "ExtendedS3DestinationConfiguration.DataFormatConversionConfiguration",
35500
+ "ExtendedS3DestinationConfiguration.DynamicPartitioningConfiguration",
35501
+ "ExtendedS3DestinationConfiguration.S3BackupConfiguration",
35502
+ // Non-S3 destinations (drift-unknown for v1)
35503
+ "RedshiftDestinationConfiguration",
35504
+ "ElasticsearchDestinationConfiguration",
35505
+ "AmazonopensearchserviceDestinationConfiguration",
35506
+ "SplunkDestinationConfiguration",
35507
+ "HttpEndpointDestinationConfiguration",
35508
+ "AmazonOpenSearchServerlessDestinationConfiguration",
35509
+ // Encryption input shape (deferred)
35510
+ "DeliveryStreamEncryptionConfigurationInput"
35511
+ ];
35512
+ }
35285
35513
  async import(input) {
35286
35514
  const explicit = resolveExplicitPhysicalId(input, "DeliveryStreamName");
35287
35515
  if (explicit) {
@@ -44482,7 +44710,7 @@ function reorderArgs(argv) {
44482
44710
  }
44483
44711
  async function main() {
44484
44712
  const program = new Command14();
44485
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.51.2");
44713
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.51.4");
44486
44714
  program.addCommand(createBootstrapCommand());
44487
44715
  program.addCommand(createSynthCommand());
44488
44716
  program.addCommand(createListCommand());