@go-to-k/cdkd 0.51.3 → 0.51.5

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
@@ -19470,7 +19470,8 @@ import {
19470
19470
  ReplaceNetworkAclAssociationCommand,
19471
19471
  DescribeNetworkAclsCommand,
19472
19472
  DescribeNetworkInterfacesCommand as DescribeNetworkInterfacesCommand2,
19473
- DeleteNetworkInterfaceCommand as DeleteNetworkInterfaceCommand2
19473
+ DeleteNetworkInterfaceCommand as DeleteNetworkInterfaceCommand2,
19474
+ DescribeVolumesCommand
19474
19475
  } from "@aws-sdk/client-ec2";
19475
19476
  init_aws_clients();
19476
19477
  var EC2Provider = class {
@@ -21733,13 +21734,39 @@ var EC2Provider = class {
21733
21734
  * `AllocationId`, `ConnectivityType`, `PrivateIpAddress`.
21734
21735
  * - **AWS::EC2::RouteTable**: `DescribeRouteTables` for `VpcId`.
21735
21736
  * - **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.
21737
+ * `GroupName`, `GroupDescription`, `VpcId`, plus `SecurityGroupIngress`
21738
+ * and `SecurityGroupEgress` reverse-mapped from AWS's normalized
21739
+ * `IpPermissions[]` / `IpPermissionsEgress[]` form. Each AWS
21740
+ * `IpPermission` is flattened into one CFn rule per `IpRanges` /
21741
+ * `Ipv6Ranges` / `UserIdGroupPairs` / `PrefixListIds` entry; field
21742
+ * names follow CFn direction conventions (`Source*` for ingress,
21743
+ * `Destination*` for egress). When state templates ingress/egress
21744
+ * rules, AWS's response is reordered to match state's positional
21745
+ * order via `reconcileSgRules` so the comparator's array compare
21746
+ * doesn't fire false drift on AWS's normalized ordering. The
21747
+ * AWS-auto-attached "allow-all 0.0.0.0/0" egress rule is filtered
21748
+ * out of `SecurityGroupEgress` when state did not template egress
21749
+ * (the auto-default is invisible to drift). Both arrays are
21750
+ * always emitted (even as `[]`) so the v3 `observedProperties`
21751
+ * baseline catches console-side rule ADDs to a templated SG.
21740
21752
  * - **AWS::EC2::Instance**: `DescribeInstances` for `ImageId`,
21741
- * `InstanceType`, `KeyName`, `SubnetId`. SecurityGroupIds /
21742
- * BlockDeviceMappings shape-match is out of scope for v1.
21753
+ * `InstanceType`, `KeyName`, `SubnetId`, `SecurityGroupIds` (sorted
21754
+ * list of `SecurityGroups[].GroupId` for stable positional compare),
21755
+ * `PrivateIpAddress`, `SourceDestCheck`, `Monitoring` (mapped from
21756
+ * AWS `Monitoring.State` to CFn boolean), `Tenancy` (from
21757
+ * `Placement.Tenancy`), `IamInstanceProfile` (ARN form — v2 fallback
21758
+ * state holding a name will fire one-time drift, resolved via
21759
+ * `cdkd state refresh-observed`), `Tags` (filtered `aws:*`). For
21760
+ * `BlockDeviceMappings`, `DescribeInstances` only returns
21761
+ * `(DeviceName, Ebs.VolumeId, Ebs.DeleteOnTermination)`; cdkd
21762
+ * additionally calls `DescribeVolumes` on the attached volume ids to
21763
+ * surface `VolumeType` / `VolumeSize` / `Iops` / `Throughput` /
21764
+ * `Encrypted` / `KmsKeyId` / `SnapshotId`. The DescribeVolumes call
21765
+ * is best-effort — a permissions gap or other failure falls back to
21766
+ * the partial shape (DeleteOnTermination only). All arrays / scalars
21767
+ * that map to user-controllable CFn properties are always emitted
21768
+ * (even as `[]` or default scalar) so the v3 `observedProperties`
21769
+ * baseline catches console-side ADDs.
21743
21770
  * - **AWS::EC2::NetworkAcl**: `DescribeNetworkAcls` for `VpcId`.
21744
21771
  *
21745
21772
  * Skipped (return `undefined`, falls through to the comparator's
@@ -21761,7 +21788,7 @@ var EC2Provider = class {
21761
21788
  * Returns `undefined` when the resource is gone (any `*NotFound` /
21762
21789
  * `Invalid*` error from the EC2 SDK matches `isNotFoundError`).
21763
21790
  */
21764
- async readCurrentState(physicalId, logicalId, resourceType) {
21791
+ async readCurrentState(physicalId, logicalId, resourceType, properties) {
21765
21792
  try {
21766
21793
  switch (resourceType) {
21767
21794
  case "AWS::EC2::VPC":
@@ -21775,7 +21802,7 @@ var EC2Provider = class {
21775
21802
  case "AWS::EC2::RouteTable":
21776
21803
  return await this.readRouteTableCurrentState(physicalId);
21777
21804
  case "AWS::EC2::SecurityGroup":
21778
- return await this.readSecurityGroupCurrentState(physicalId);
21805
+ return await this.readSecurityGroupCurrentState(physicalId, properties);
21779
21806
  case "AWS::EC2::Instance":
21780
21807
  return await this.readInstanceCurrentState(physicalId);
21781
21808
  case "AWS::EC2::NetworkAcl":
@@ -21884,7 +21911,7 @@ var EC2Provider = class {
21884
21911
  result["VpcId"] = rt.VpcId;
21885
21912
  return result;
21886
21913
  }
21887
- async readSecurityGroupCurrentState(physicalId) {
21914
+ async readSecurityGroupCurrentState(physicalId, properties) {
21888
21915
  const resp = await this.ec2Client.send(
21889
21916
  new DescribeSecurityGroupsCommand2({ GroupIds: [physicalId] })
21890
21917
  );
@@ -21898,6 +21925,15 @@ var EC2Provider = class {
21898
21925
  result["GroupDescription"] = sg.Description;
21899
21926
  if (sg.VpcId !== void 0)
21900
21927
  result["VpcId"] = sg.VpcId;
21928
+ const stateIngress = Array.isArray(properties?.["SecurityGroupIngress"]) ? properties["SecurityGroupIngress"] : void 0;
21929
+ const ingressRules = flattenIpPermissions(sg.IpPermissions ?? [], "ingress");
21930
+ result["SecurityGroupIngress"] = reconcileSgRules(ingressRules, stateIngress, "ingress");
21931
+ const stateEgress = Array.isArray(properties?.["SecurityGroupEgress"]) ? properties["SecurityGroupEgress"] : void 0;
21932
+ let egressRules = flattenIpPermissions(sg.IpPermissionsEgress ?? [], "egress");
21933
+ if (stateEgress === void 0) {
21934
+ egressRules = egressRules.filter((r) => !isDefaultEgressRule(r));
21935
+ }
21936
+ result["SecurityGroupEgress"] = reconcileSgRules(egressRules, stateEgress, "egress");
21901
21937
  return result;
21902
21938
  }
21903
21939
  async readInstanceCurrentState(physicalId) {
@@ -21917,6 +21953,75 @@ var EC2Provider = class {
21917
21953
  result["KeyName"] = instance.KeyName;
21918
21954
  if (instance.SubnetId !== void 0)
21919
21955
  result["SubnetId"] = instance.SubnetId;
21956
+ result["SecurityGroupIds"] = (instance.SecurityGroups ?? []).map((g) => g.GroupId).filter((id) => typeof id === "string").sort();
21957
+ if (instance.PrivateIpAddress !== void 0) {
21958
+ result["PrivateIpAddress"] = instance.PrivateIpAddress;
21959
+ }
21960
+ if (instance.SourceDestCheck !== void 0) {
21961
+ result["SourceDestCheck"] = instance.SourceDestCheck;
21962
+ }
21963
+ const monitoringState = instance.Monitoring?.State;
21964
+ result["Monitoring"] = monitoringState === "enabled" || monitoringState === "pending";
21965
+ if (instance.Placement?.Tenancy !== void 0) {
21966
+ result["Tenancy"] = instance.Placement.Tenancy;
21967
+ }
21968
+ if (instance.IamInstanceProfile?.Arn !== void 0) {
21969
+ result["IamInstanceProfile"] = instance.IamInstanceProfile.Arn;
21970
+ }
21971
+ const ebsMappings = (instance.BlockDeviceMappings ?? []).filter(
21972
+ (m) => m.Ebs?.VolumeId !== void 0
21973
+ );
21974
+ const volumeIds = ebsMappings.map((m) => m.Ebs.VolumeId);
21975
+ let volumesById = /* @__PURE__ */ new Map();
21976
+ if (volumeIds.length > 0) {
21977
+ try {
21978
+ const volResp = await this.ec2Client.send(
21979
+ new DescribeVolumesCommand({ VolumeIds: volumeIds })
21980
+ );
21981
+ for (const v of volResp.Volumes ?? []) {
21982
+ if (v.VolumeId !== void 0)
21983
+ volumesById.set(v.VolumeId, v);
21984
+ }
21985
+ } catch (err) {
21986
+ this.logger.debug(
21987
+ `DescribeVolumes(${volumeIds.join(",")}) failed: ${err instanceof Error ? err.message : String(err)}`
21988
+ );
21989
+ volumesById = /* @__PURE__ */ new Map();
21990
+ }
21991
+ }
21992
+ const blockMappings = [];
21993
+ for (const m of instance.BlockDeviceMappings ?? []) {
21994
+ const out = {};
21995
+ if (m.DeviceName !== void 0)
21996
+ out["DeviceName"] = m.DeviceName;
21997
+ if (m.Ebs?.VolumeId !== void 0) {
21998
+ const ebs = {};
21999
+ if (m.Ebs.DeleteOnTermination !== void 0) {
22000
+ ebs["DeleteOnTermination"] = m.Ebs.DeleteOnTermination;
22001
+ }
22002
+ const vol = volumesById.get(m.Ebs.VolumeId);
22003
+ if (vol !== void 0) {
22004
+ if (vol.VolumeType !== void 0)
22005
+ ebs["VolumeType"] = vol.VolumeType;
22006
+ if (vol.Size !== void 0)
22007
+ ebs["VolumeSize"] = vol.Size;
22008
+ if (vol.Iops !== void 0)
22009
+ ebs["Iops"] = vol.Iops;
22010
+ if (vol.Throughput !== void 0)
22011
+ ebs["Throughput"] = vol.Throughput;
22012
+ if (vol.Encrypted !== void 0)
22013
+ ebs["Encrypted"] = vol.Encrypted;
22014
+ if (vol.KmsKeyId !== void 0)
22015
+ ebs["KmsKeyId"] = vol.KmsKeyId;
22016
+ if (vol.SnapshotId !== void 0)
22017
+ ebs["SnapshotId"] = vol.SnapshotId;
22018
+ }
22019
+ out["Ebs"] = ebs;
22020
+ }
22021
+ blockMappings.push(out);
22022
+ }
22023
+ result["BlockDeviceMappings"] = blockMappings;
22024
+ result["Tags"] = normalizeAwsTagsToCfn(instance.Tags);
21920
22025
  return result;
21921
22026
  }
21922
22027
  async readNetworkAclCurrentState(physicalId) {
@@ -21967,6 +22072,115 @@ var EC2Provider = class {
21967
22072
  }
21968
22073
  }
21969
22074
  };
22075
+ function flattenIpPermissions(perms, direction) {
22076
+ const out = [];
22077
+ for (const p of perms) {
22078
+ const base = {};
22079
+ if (p.IpProtocol !== void 0)
22080
+ base["IpProtocol"] = p.IpProtocol;
22081
+ if (p.FromPort !== void 0)
22082
+ base["FromPort"] = p.FromPort;
22083
+ if (p.ToPort !== void 0)
22084
+ base["ToPort"] = p.ToPort;
22085
+ for (const ip of p.IpRanges ?? []) {
22086
+ const rule = { ...base };
22087
+ if (ip.CidrIp !== void 0)
22088
+ rule["CidrIp"] = ip.CidrIp;
22089
+ if (ip.Description !== void 0)
22090
+ rule["Description"] = ip.Description;
22091
+ out.push(rule);
22092
+ }
22093
+ for (const ipv6 of p.Ipv6Ranges ?? []) {
22094
+ const rule = { ...base };
22095
+ if (ipv6.CidrIpv6 !== void 0)
22096
+ rule["CidrIpv6"] = ipv6.CidrIpv6;
22097
+ if (ipv6.Description !== void 0)
22098
+ rule["Description"] = ipv6.Description;
22099
+ out.push(rule);
22100
+ }
22101
+ for (const grp of p.UserIdGroupPairs ?? []) {
22102
+ const rule = { ...base };
22103
+ if (direction === "ingress") {
22104
+ if (grp.GroupId !== void 0)
22105
+ rule["SourceSecurityGroupId"] = grp.GroupId;
22106
+ if (grp.UserId !== void 0)
22107
+ rule["SourceSecurityGroupOwnerId"] = grp.UserId;
22108
+ } else {
22109
+ if (grp.GroupId !== void 0)
22110
+ rule["DestinationSecurityGroupId"] = grp.GroupId;
22111
+ }
22112
+ if (grp.Description !== void 0)
22113
+ rule["Description"] = grp.Description;
22114
+ out.push(rule);
22115
+ }
22116
+ for (const pl of p.PrefixListIds ?? []) {
22117
+ const rule = { ...base };
22118
+ if (direction === "ingress") {
22119
+ if (pl.PrefixListId !== void 0)
22120
+ rule["SourcePrefixListId"] = pl.PrefixListId;
22121
+ } else {
22122
+ if (pl.PrefixListId !== void 0)
22123
+ rule["DestinationPrefixListId"] = pl.PrefixListId;
22124
+ }
22125
+ if (pl.Description !== void 0)
22126
+ rule["Description"] = pl.Description;
22127
+ out.push(rule);
22128
+ }
22129
+ if ((p.IpRanges?.length ?? 0) === 0 && (p.Ipv6Ranges?.length ?? 0) === 0 && (p.UserIdGroupPairs?.length ?? 0) === 0 && (p.PrefixListIds?.length ?? 0) === 0) {
22130
+ out.push({ ...base });
22131
+ }
22132
+ }
22133
+ return out;
22134
+ }
22135
+ function sgRuleKey(rule, direction) {
22136
+ const peerKey = direction === "egress" ? rule["DestinationSecurityGroupId"] : rule["SourceSecurityGroupId"];
22137
+ const prefixKey = direction === "egress" ? rule["DestinationPrefixListId"] : rule["SourcePrefixListId"];
22138
+ const peerOwner = direction === "ingress" ? rule["SourceSecurityGroupOwnerId"] : void 0;
22139
+ return JSON.stringify({
22140
+ p: rule["IpProtocol"] ?? "-1",
22141
+ f: rule["FromPort"] ?? null,
22142
+ t: rule["ToPort"] ?? null,
22143
+ c4: rule["CidrIp"] ?? null,
22144
+ c6: rule["CidrIpv6"] ?? null,
22145
+ peer: peerKey ?? null,
22146
+ peerOwner: peerOwner ?? null,
22147
+ pl: prefixKey ?? null,
22148
+ d: rule["Description"] ?? null
22149
+ });
22150
+ }
22151
+ function reconcileSgRules(awsRules, stateRules, direction) {
22152
+ if (!stateRules || stateRules.length === 0)
22153
+ return awsRules;
22154
+ const remaining = [...awsRules];
22155
+ const reordered = [];
22156
+ for (const sr of stateRules) {
22157
+ const key = sgRuleKey(sr, direction);
22158
+ const idx = remaining.findIndex((ar) => sgRuleKey(ar, direction) === key);
22159
+ if (idx >= 0) {
22160
+ reordered.push(remaining.splice(idx, 1)[0]);
22161
+ }
22162
+ }
22163
+ return [...reordered, ...remaining];
22164
+ }
22165
+ function isDefaultEgressRule(rule) {
22166
+ if (rule["IpProtocol"] !== "-1")
22167
+ return false;
22168
+ if (rule["CidrIp"] !== "0.0.0.0/0")
22169
+ return false;
22170
+ if (rule["CidrIpv6"] !== void 0)
22171
+ return false;
22172
+ if (rule["DestinationSecurityGroupId"] !== void 0)
22173
+ return false;
22174
+ if (rule["DestinationPrefixListId"] !== void 0)
22175
+ return false;
22176
+ if (rule["Description"] !== void 0)
22177
+ return false;
22178
+ if (rule["FromPort"] !== void 0 && rule["FromPort"] !== -1)
22179
+ return false;
22180
+ if (rule["ToPort"] !== void 0 && rule["ToPort"] !== -1)
22181
+ return false;
22182
+ return true;
22183
+ }
21970
22184
 
21971
22185
  // src/provisioning/providers/apigateway-provider.ts
21972
22186
  import {
@@ -44581,7 +44795,7 @@ function reorderArgs(argv) {
44581
44795
  }
44582
44796
  async function main() {
44583
44797
  const program = new Command14();
44584
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.51.3");
44798
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.51.5");
44585
44799
  program.addCommand(createBootstrapCommand());
44586
44800
  program.addCommand(createSynthCommand());
44587
44801
  program.addCommand(createListCommand());