@composurecdk/ec2 0.6.0 → 0.8.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.
Files changed (150) hide show
  1. package/README.md +121 -0
  2. package/dist/commonjs/index.d.ts +14 -0
  3. package/dist/commonjs/index.d.ts.map +1 -0
  4. package/dist/commonjs/index.js +22 -0
  5. package/dist/commonjs/index.js.map +1 -0
  6. package/dist/commonjs/instance-alarm-config.d.ts.map +1 -0
  7. package/dist/commonjs/instance-alarm-config.js +3 -0
  8. package/dist/commonjs/instance-alarm-config.js.map +1 -0
  9. package/dist/commonjs/instance-alarm-defaults.d.ts.map +1 -0
  10. package/dist/commonjs/instance-alarm-defaults.js +65 -0
  11. package/dist/commonjs/instance-alarm-defaults.js.map +1 -0
  12. package/dist/commonjs/instance-alarms.d.ts.map +1 -0
  13. package/dist/commonjs/instance-alarms.js +132 -0
  14. package/dist/commonjs/instance-alarms.js.map +1 -0
  15. package/dist/{instance-builder.d.ts → commonjs/instance-builder.d.ts} +44 -6
  16. package/dist/commonjs/instance-builder.d.ts.map +1 -0
  17. package/dist/commonjs/instance-builder.js +135 -0
  18. package/dist/commonjs/instance-builder.js.map +1 -0
  19. package/dist/commonjs/instance-defaults.d.ts.map +1 -0
  20. package/dist/commonjs/instance-defaults.js +62 -0
  21. package/dist/commonjs/instance-defaults.js.map +1 -0
  22. package/dist/commonjs/instance-volume-attachment-config.d.ts +34 -0
  23. package/dist/commonjs/instance-volume-attachment-config.d.ts.map +1 -0
  24. package/dist/commonjs/instance-volume-attachment-config.js +3 -0
  25. package/dist/commonjs/instance-volume-attachment-config.js.map +1 -0
  26. package/dist/commonjs/instance-volume-attachment-defaults.d.ts +14 -0
  27. package/dist/commonjs/instance-volume-attachment-defaults.d.ts.map +1 -0
  28. package/dist/commonjs/instance-volume-attachment-defaults.js +27 -0
  29. package/dist/commonjs/instance-volume-attachment-defaults.js.map +1 -0
  30. package/dist/commonjs/instance-volume-attachments.d.ts +59 -0
  31. package/dist/commonjs/instance-volume-attachments.d.ts.map +1 -0
  32. package/dist/commonjs/instance-volume-attachments.js +107 -0
  33. package/dist/commonjs/instance-volume-attachments.js.map +1 -0
  34. package/dist/commonjs/package.json +3 -0
  35. package/dist/commonjs/volume-alarm-config.d.ts +35 -0
  36. package/dist/commonjs/volume-alarm-config.d.ts.map +1 -0
  37. package/dist/commonjs/volume-alarm-config.js +3 -0
  38. package/dist/commonjs/volume-alarm-config.js.map +1 -0
  39. package/dist/commonjs/volume-alarm-defaults.d.ts +17 -0
  40. package/dist/commonjs/volume-alarm-defaults.d.ts.map +1 -0
  41. package/dist/commonjs/volume-alarm-defaults.js +30 -0
  42. package/dist/commonjs/volume-alarm-defaults.js.map +1 -0
  43. package/dist/commonjs/volume-alarms.d.ts +29 -0
  44. package/dist/commonjs/volume-alarms.d.ts.map +1 -0
  45. package/dist/commonjs/volume-alarms.js +92 -0
  46. package/dist/commonjs/volume-alarms.js.map +1 -0
  47. package/dist/commonjs/volume-builder.d.ts +171 -0
  48. package/dist/commonjs/volume-builder.d.ts.map +1 -0
  49. package/dist/commonjs/volume-builder.js +98 -0
  50. package/dist/commonjs/volume-builder.js.map +1 -0
  51. package/dist/commonjs/volume-defaults.d.ts +15 -0
  52. package/dist/commonjs/volume-defaults.d.ts.map +1 -0
  53. package/dist/commonjs/volume-defaults.js +50 -0
  54. package/dist/commonjs/volume-defaults.js.map +1 -0
  55. package/dist/{vpc-builder.d.ts → commonjs/vpc-builder.d.ts} +3 -2
  56. package/dist/commonjs/vpc-builder.d.ts.map +1 -0
  57. package/dist/commonjs/vpc-builder.js +82 -0
  58. package/dist/commonjs/vpc-builder.js.map +1 -0
  59. package/dist/commonjs/vpc-defaults.d.ts.map +1 -0
  60. package/dist/commonjs/vpc-defaults.js +58 -0
  61. package/dist/commonjs/vpc-defaults.js.map +1 -0
  62. package/dist/esm/index.d.ts +14 -0
  63. package/dist/esm/index.d.ts.map +1 -0
  64. package/dist/esm/index.js +10 -0
  65. package/dist/esm/index.js.map +1 -0
  66. package/dist/esm/instance-alarm-config.d.ts +62 -0
  67. package/dist/esm/instance-alarm-config.d.ts.map +1 -0
  68. package/dist/esm/instance-alarm-config.js.map +1 -0
  69. package/dist/esm/instance-alarm-defaults.d.ts +20 -0
  70. package/dist/esm/instance-alarm-defaults.d.ts.map +1 -0
  71. package/dist/esm/instance-alarm-defaults.js.map +1 -0
  72. package/dist/esm/instance-alarms.d.ts +28 -0
  73. package/dist/esm/instance-alarms.d.ts.map +1 -0
  74. package/dist/esm/instance-alarms.js.map +1 -0
  75. package/dist/esm/instance-builder.d.ts +223 -0
  76. package/dist/esm/instance-builder.d.ts.map +1 -0
  77. package/dist/{instance-builder.js → esm/instance-builder.js} +49 -4
  78. package/dist/esm/instance-builder.js.map +1 -0
  79. package/dist/esm/instance-defaults.d.ts +14 -0
  80. package/dist/esm/instance-defaults.d.ts.map +1 -0
  81. package/dist/esm/instance-defaults.js.map +1 -0
  82. package/dist/esm/instance-volume-attachment-config.d.ts +34 -0
  83. package/dist/esm/instance-volume-attachment-config.d.ts.map +1 -0
  84. package/dist/esm/instance-volume-attachment-config.js +2 -0
  85. package/dist/esm/instance-volume-attachment-config.js.map +1 -0
  86. package/dist/esm/instance-volume-attachment-defaults.d.ts +14 -0
  87. package/dist/esm/instance-volume-attachment-defaults.d.ts.map +1 -0
  88. package/dist/esm/instance-volume-attachment-defaults.js +24 -0
  89. package/dist/esm/instance-volume-attachment-defaults.js.map +1 -0
  90. package/dist/esm/instance-volume-attachments.d.ts +59 -0
  91. package/dist/esm/instance-volume-attachments.d.ts.map +1 -0
  92. package/dist/esm/instance-volume-attachments.js +104 -0
  93. package/dist/esm/instance-volume-attachments.js.map +1 -0
  94. package/dist/esm/package.json +3 -0
  95. package/dist/esm/volume-alarm-config.d.ts +35 -0
  96. package/dist/esm/volume-alarm-config.d.ts.map +1 -0
  97. package/dist/esm/volume-alarm-config.js +2 -0
  98. package/dist/esm/volume-alarm-config.js.map +1 -0
  99. package/dist/esm/volume-alarm-defaults.d.ts +17 -0
  100. package/dist/esm/volume-alarm-defaults.d.ts.map +1 -0
  101. package/dist/esm/volume-alarm-defaults.js +27 -0
  102. package/dist/esm/volume-alarm-defaults.js.map +1 -0
  103. package/dist/esm/volume-alarms.d.ts +29 -0
  104. package/dist/esm/volume-alarms.d.ts.map +1 -0
  105. package/dist/esm/volume-alarms.js +88 -0
  106. package/dist/esm/volume-alarms.js.map +1 -0
  107. package/dist/esm/volume-builder.d.ts +171 -0
  108. package/dist/esm/volume-builder.d.ts.map +1 -0
  109. package/dist/esm/volume-builder.js +95 -0
  110. package/dist/esm/volume-builder.js.map +1 -0
  111. package/dist/esm/volume-defaults.d.ts +15 -0
  112. package/dist/esm/volume-defaults.d.ts.map +1 -0
  113. package/dist/esm/volume-defaults.js +47 -0
  114. package/dist/esm/volume-defaults.js.map +1 -0
  115. package/dist/esm/vpc-builder.d.ts +110 -0
  116. package/dist/esm/vpc-builder.d.ts.map +1 -0
  117. package/dist/{vpc-builder.js → esm/vpc-builder.js} +2 -2
  118. package/dist/esm/vpc-builder.js.map +1 -0
  119. package/dist/esm/vpc-defaults.d.ts +15 -0
  120. package/dist/esm/vpc-defaults.d.ts.map +1 -0
  121. package/dist/esm/vpc-defaults.js.map +1 -0
  122. package/package.json +36 -17
  123. package/dist/index.d.ts +0 -7
  124. package/dist/index.d.ts.map +0 -1
  125. package/dist/index.js +0 -6
  126. package/dist/index.js.map +0 -1
  127. package/dist/instance-alarm-config.d.ts.map +0 -1
  128. package/dist/instance-alarm-config.js.map +0 -1
  129. package/dist/instance-alarm-defaults.d.ts.map +0 -1
  130. package/dist/instance-alarm-defaults.js.map +0 -1
  131. package/dist/instance-alarms.d.ts.map +0 -1
  132. package/dist/instance-alarms.js.map +0 -1
  133. package/dist/instance-builder.d.ts.map +0 -1
  134. package/dist/instance-builder.js.map +0 -1
  135. package/dist/instance-defaults.d.ts.map +0 -1
  136. package/dist/instance-defaults.js.map +0 -1
  137. package/dist/vpc-builder.d.ts.map +0 -1
  138. package/dist/vpc-builder.js.map +0 -1
  139. package/dist/vpc-defaults.d.ts.map +0 -1
  140. package/dist/vpc-defaults.js.map +0 -1
  141. /package/dist/{instance-alarm-config.d.ts → commonjs/instance-alarm-config.d.ts} +0 -0
  142. /package/dist/{instance-alarm-defaults.d.ts → commonjs/instance-alarm-defaults.d.ts} +0 -0
  143. /package/dist/{instance-alarms.d.ts → commonjs/instance-alarms.d.ts} +0 -0
  144. /package/dist/{instance-defaults.d.ts → commonjs/instance-defaults.d.ts} +0 -0
  145. /package/dist/{vpc-defaults.d.ts → commonjs/vpc-defaults.d.ts} +0 -0
  146. /package/dist/{instance-alarm-config.js → esm/instance-alarm-config.js} +0 -0
  147. /package/dist/{instance-alarm-defaults.js → esm/instance-alarm-defaults.js} +0 -0
  148. /package/dist/{instance-alarms.js → esm/instance-alarms.js} +0 -0
  149. /package/dist/{instance-defaults.js → esm/instance-defaults.js} +0 -0
  150. /package/dist/{vpc-defaults.js → esm/vpc-defaults.js} +0 -0
package/README.md CHANGED
@@ -120,6 +120,51 @@ const server = createInstanceBuilder()
120
120
  );
121
121
  ```
122
122
 
123
+ ### Attaching persistent volumes
124
+
125
+ `attachVolume(key, volumeRef, opts)` mirrors the call shape of `addAlarm` and produces an `AWS::EC2::VolumeAttachment` for an externally-managed EBS volume. The volume reference accepts either a `Resolvable<VolumeBuilderResult>` (drop a `ref<VolumeBuilderResult>("data")` straight in) or a `Resolvable<IVolume>`.
126
+
127
+ ```ts
128
+ import { compose, ref } from "@composurecdk/core";
129
+ import {
130
+ createInstanceBuilder,
131
+ createVolumeBuilder,
132
+ createVpcBuilder,
133
+ type VolumeBuilderResult,
134
+ type VpcBuilderResult,
135
+ } from "@composurecdk/ec2";
136
+ import { Size } from "aws-cdk-lib";
137
+ import { InstanceClass, InstanceSize, InstanceType, MachineImage } from "aws-cdk-lib/aws-ec2";
138
+
139
+ compose(
140
+ {
141
+ network: createVpcBuilder().maxAzs(2).natGateways(0),
142
+
143
+ data: createVolumeBuilder()
144
+ .availabilityZone(ref<VpcBuilderResult>("network").map((r) => r.vpc.availabilityZones[0]))
145
+ .size(Size.gibibytes(50)),
146
+
147
+ agent: createInstanceBuilder()
148
+ .vpc(ref<VpcBuilderResult>("network").map((r) => r.vpc))
149
+ .instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.MICRO))
150
+ .machineImage(MachineImage.latestAmazonLinux2023())
151
+ .attachVolume("AgentData", ref<VolumeBuilderResult>("data"), { device: "/dev/sdf" }),
152
+ },
153
+ { network: [], data: ["network"], agent: ["network", "data"] },
154
+ ).build(stack, "AgentApp");
155
+ ```
156
+
157
+ The result exposes the attachment under `result.agent.volumeAttachments.AgentData` and emits a per-attachment `volumeStalledIo` alarm under `result.agent.alarms["AgentData.volumeStalledIo"]`. When both AZs are concrete strings at synth, the builder asserts the instance and volume share an Availability Zone — synth-time failure beats boot-time failure.
158
+
159
+ `VolumeStalledIOCheck` is published only for Nitro-instance attachments. On non-Nitro instances the alarm sits at `INSUFFICIENT_DATA`, which the `treatMissingData: NOT_BREACHING` default makes harmless. To disable the alarm per attachment:
160
+
161
+ ```ts
162
+ .attachVolume("AgentData", ref<VolumeBuilderResult>("data"), {
163
+ device: "/dev/sdf",
164
+ recommendedAlarms: false,
165
+ })
166
+ ```
167
+
123
168
  ## VPC Builder
124
169
 
125
170
  ```ts
@@ -175,6 +220,82 @@ createVpcBuilder().flowLogs(false);
175
220
 
176
221
  For multiple flow logs against the same VPC, omit this config and create additional `FlowLog` constructs directly against the returned `vpc`.
177
222
 
223
+ ## Volume Builder
224
+
225
+ ```ts
226
+ import { createVolumeBuilder } from "@composurecdk/ec2";
227
+ import { Size } from "aws-cdk-lib";
228
+
229
+ const data = createVolumeBuilder()
230
+ .availabilityZone("us-east-1a")
231
+ .size(Size.gibibytes(50))
232
+ .build(stack, "AgentData");
233
+ ```
234
+
235
+ Every [VolumeProps](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.VolumeProps.html) property is available as a fluent setter on the builder, except `availabilityZone` which is set via the dedicated `.availabilityZone()` method (so it can be wired from a sibling `VpcBuilder` via `ref`) and `encryptionKey` which accepts a `Resolvable<IKey>` for cross-component KMS-key wiring.
236
+
237
+ ### Volume Defaults
238
+
239
+ | Property | Default | Rationale |
240
+ | --------------- | ---------------------- | --------------------------------------------------------------------------------------------------------- |
241
+ | `volumeType` | `GP3` | Current-generation general-purpose SSD — cheaper and faster than GP2 at equivalent sizes. |
242
+ | `encrypted` | `true` | Encryption at rest with the account's default EBS KMS key. Pass `encryptionKey` for a CMK. |
243
+ | `autoEnableIo` | `true` | Lets I/O resume on suspected inconsistency so the instance can come up unattended. |
244
+ | `removalPolicy` | `RemovalPolicy.RETAIN` | A destroyed volume is unrecoverable; an orphaned volume is a $/month nuisance. Mirrors `BUCKET_DEFAULTS`. |
245
+
246
+ Three properties intentionally have no default — they are application-specific and must be supplied explicitly:
247
+
248
+ - `availabilityZone` (via the `.availabilityZone()` method)
249
+ - `size`
250
+ - `iops` / `throughput` (only when opting into a volume type that requires them)
251
+
252
+ The defaults are exported as `VOLUME_DEFAULTS` for visibility and testing:
253
+
254
+ ```ts
255
+ import { VOLUME_DEFAULTS } from "@composurecdk/ec2";
256
+ ```
257
+
258
+ ### Recommended Volume Alarms
259
+
260
+ The builder creates [AWS-recommended CloudWatch alarms](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EBS) by default. No alarm actions are configured — wire actions via `alarmActionsPolicy` from `@composurecdk/cloudwatch`.
261
+
262
+ | Alarm | Metric | Default threshold | Created when |
263
+ | -------------- | ----------------------------- | -------------------- | ----------------------------------------------- |
264
+ | `burstBalance` | BurstBalance (Average, 5 min) | < 20% over 3 × 5 min | `volumeType` is burstable (`gp2`, `st1`, `sc1`) |
265
+
266
+ The defaults are exported as `VOLUME_ALARM_DEFAULTS` for visibility and testing:
267
+
268
+ ```ts
269
+ import { VOLUME_ALARM_DEFAULTS } from "@composurecdk/ec2";
270
+ ```
271
+
272
+ `VolumeQueueLength` and `VolumeIdleTime` are deferred — both need per-`volumeType`/per-workload tuning to be a defensible default. Wire them via `addAlarm` until first-class support lands:
273
+
274
+ ```ts
275
+ import { Metric, Stats } from "aws-cdk-lib/aws-cloudwatch";
276
+ import { Duration } from "aws-cdk-lib";
277
+
278
+ const data = createVolumeBuilder()
279
+ .availabilityZone("us-east-1a")
280
+ .size(Size.gibibytes(50))
281
+ .addAlarm("volumeQueueLength", (alarm) =>
282
+ alarm
283
+ .metric(
284
+ (volume) =>
285
+ new Metric({
286
+ namespace: "AWS/EBS",
287
+ metricName: "VolumeQueueLength",
288
+ dimensionsMap: { VolumeId: volume.volumeId },
289
+ statistic: Stats.AVERAGE,
290
+ period: Duration.minutes(5),
291
+ }),
292
+ )
293
+ .threshold(10)
294
+ .greaterThan()
295
+ .description("EBS volume queue length is high"),
296
+ );
297
+ ```
298
+
178
299
  ## Composing EC2 + VPC
179
300
 
180
301
  Compose the builders into a single system — the instance is wired to the VPC via `ref`:
@@ -0,0 +1,14 @@
1
+ export { createInstanceBuilder, type IInstanceBuilder, type InstanceBuilderProps, type InstanceBuilderResult, } from "./instance-builder.js";
2
+ export { INSTANCE_DEFAULTS } from "./instance-defaults.js";
3
+ export { type InstanceAlarmConfig } from "./instance-alarm-config.js";
4
+ export { INSTANCE_ALARM_DEFAULTS } from "./instance-alarm-defaults.js";
5
+ export { type AttachVolumeOptions } from "./instance-volume-attachments.js";
6
+ export { type VolumeAttachmentAlarmConfig } from "./instance-volume-attachment-config.js";
7
+ export { VOLUME_ATTACHMENT_ALARM_DEFAULTS } from "./instance-volume-attachment-defaults.js";
8
+ export { createVolumeBuilder, type IVolumeBuilder, type VolumeBuilderProps, type VolumeBuilderResult, } from "./volume-builder.js";
9
+ export { VOLUME_DEFAULTS } from "./volume-defaults.js";
10
+ export { type VolumeAlarmConfig } from "./volume-alarm-config.js";
11
+ export { VOLUME_ALARM_DEFAULTS } from "./volume-alarm-defaults.js";
12
+ export { createVpcBuilder, type FlowLogsConfig, type IVpcBuilder, type VpcBuilderProps, type VpcBuilderResult, } from "./vpc-builder.js";
13
+ export { VPC_DEFAULTS } from "./vpc-defaults.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,gCAAgC,EAAE,MAAM,0CAA0C,CAAC;AAE5F,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VPC_DEFAULTS = exports.createVpcBuilder = exports.VOLUME_ALARM_DEFAULTS = exports.VOLUME_DEFAULTS = exports.createVolumeBuilder = exports.VOLUME_ATTACHMENT_ALARM_DEFAULTS = exports.INSTANCE_ALARM_DEFAULTS = exports.INSTANCE_DEFAULTS = exports.createInstanceBuilder = void 0;
4
+ var instance_builder_js_1 = require("./instance-builder.js");
5
+ Object.defineProperty(exports, "createInstanceBuilder", { enumerable: true, get: function () { return instance_builder_js_1.createInstanceBuilder; } });
6
+ var instance_defaults_js_1 = require("./instance-defaults.js");
7
+ Object.defineProperty(exports, "INSTANCE_DEFAULTS", { enumerable: true, get: function () { return instance_defaults_js_1.INSTANCE_DEFAULTS; } });
8
+ var instance_alarm_defaults_js_1 = require("./instance-alarm-defaults.js");
9
+ Object.defineProperty(exports, "INSTANCE_ALARM_DEFAULTS", { enumerable: true, get: function () { return instance_alarm_defaults_js_1.INSTANCE_ALARM_DEFAULTS; } });
10
+ var instance_volume_attachment_defaults_js_1 = require("./instance-volume-attachment-defaults.js");
11
+ Object.defineProperty(exports, "VOLUME_ATTACHMENT_ALARM_DEFAULTS", { enumerable: true, get: function () { return instance_volume_attachment_defaults_js_1.VOLUME_ATTACHMENT_ALARM_DEFAULTS; } });
12
+ var volume_builder_js_1 = require("./volume-builder.js");
13
+ Object.defineProperty(exports, "createVolumeBuilder", { enumerable: true, get: function () { return volume_builder_js_1.createVolumeBuilder; } });
14
+ var volume_defaults_js_1 = require("./volume-defaults.js");
15
+ Object.defineProperty(exports, "VOLUME_DEFAULTS", { enumerable: true, get: function () { return volume_defaults_js_1.VOLUME_DEFAULTS; } });
16
+ var volume_alarm_defaults_js_1 = require("./volume-alarm-defaults.js");
17
+ Object.defineProperty(exports, "VOLUME_ALARM_DEFAULTS", { enumerable: true, get: function () { return volume_alarm_defaults_js_1.VOLUME_ALARM_DEFAULTS; } });
18
+ var vpc_builder_js_1 = require("./vpc-builder.js");
19
+ Object.defineProperty(exports, "createVpcBuilder", { enumerable: true, get: function () { return vpc_builder_js_1.createVpcBuilder; } });
20
+ var vpc_defaults_js_1 = require("./vpc-defaults.js");
21
+ Object.defineProperty(exports, "VPC_DEFAULTS", { enumerable: true, get: function () { return vpc_defaults_js_1.VPC_DEFAULTS; } });
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAK+B;AAJ7B,4HAAA,qBAAqB,OAAA;AAKvB,+DAA2D;AAAlD,yHAAA,iBAAiB,OAAA;AAE1B,2EAAuE;AAA9D,qIAAA,uBAAuB,OAAA;AAGhC,mGAA4F;AAAnF,0JAAA,gCAAgC,OAAA;AAEzC,yDAK6B;AAJ3B,wHAAA,mBAAmB,OAAA;AAKrB,2DAAuD;AAA9C,qHAAA,eAAe,OAAA;AAExB,uEAAmE;AAA1D,iIAAA,qBAAqB,OAAA;AAE9B,mDAM0B;AALxB,kHAAA,gBAAgB,OAAA;AAMlB,qDAAiD;AAAxC,+GAAA,YAAY,OAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-alarm-config.d.ts","sourceRoot":"","sources":["../../src/instance-alarm-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAErC;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAExC;;;;;;;;;;OAUG;IACH,4BAA4B,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAEnD;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CACxC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=instance-alarm-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-alarm-config.js","sourceRoot":"","sources":["../../src/instance-alarm-config.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-alarm-defaults.d.ts","sourceRoot":"","sources":["../../src/instance-alarm-defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,IAAI,CAAC;IACd,cAAc,EAAE,mBAAmB,CAAC;IACpC,iBAAiB,EAAE,mBAAmB,CAAC;IACvC,4BAA4B,EAAE,mBAAmB,CAAC;IAClD,gBAAgB,EAAE,mBAAmB,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,qBAsDrC,CAAC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.INSTANCE_ALARM_DEFAULTS = void 0;
4
+ const aws_cloudwatch_1 = require("aws-cdk-lib/aws-cloudwatch");
5
+ /**
6
+ * AWS-recommended default alarm configuration for EC2 instances.
7
+ *
8
+ * Thresholds are sourced from the CloudWatch Best Practice Recommended
9
+ * Alarms guide. Thresholds may reasonably be tuned per-workload; defaults
10
+ * bias toward catching obvious issues without excessive noise.
11
+ *
12
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EC2
13
+ */
14
+ exports.INSTANCE_ALARM_DEFAULTS = {
15
+ enabled: true,
16
+ /**
17
+ * Sustained high CPU indicates the instance is a bottleneck and may
18
+ * need to be scaled up. 80% over 5 consecutive minutes avoids
19
+ * alarming on brief workload spikes.
20
+ */
21
+ cpuUtilization: {
22
+ threshold: 80,
23
+ evaluationPeriods: 5,
24
+ datapointsToAlarm: 5,
25
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
26
+ },
27
+ /**
28
+ * Any status check failure is actionable — it indicates instance or
29
+ * host impairment. 2-of-2 evaluation filters transient single-minute
30
+ * noise while keeping time-to-detect low.
31
+ */
32
+ statusCheckFailed: {
33
+ threshold: 0,
34
+ evaluationPeriods: 2,
35
+ datapointsToAlarm: 2,
36
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
37
+ },
38
+ /**
39
+ * Attached EBS volumes are unreachable or unable to complete I/O —
40
+ * typically a host or storage-subsystem issue. The 10-of-10 evaluation
41
+ * window matches AWS guidance: EBS infrastructure usually self-heals
42
+ * within a few minutes, so a longer window avoids paging on transient
43
+ * issues that resolve without intervention.
44
+ */
45
+ attachedEbsStatusCheckFailed: {
46
+ threshold: 1,
47
+ evaluationPeriods: 10,
48
+ datapointsToAlarm: 10,
49
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
50
+ },
51
+ /**
52
+ * Low CPU credit balance on burstable instances means imminent
53
+ * throttling to baseline performance. Threshold < 50 at 5-minute
54
+ * minimum gives an early warning to investigate or switch instance
55
+ * family. Credit balance metrics are only emitted at 5-minute
56
+ * granularity regardless of detailed monitoring.
57
+ */
58
+ cpuCreditBalance: {
59
+ threshold: 50,
60
+ evaluationPeriods: 3,
61
+ datapointsToAlarm: 3,
62
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
63
+ },
64
+ };
65
+ //# sourceMappingURL=instance-alarm-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-alarm-defaults.js","sourceRoot":"","sources":["../../src/instance-alarm-defaults.ts"],"names":[],"mappings":";;;AAAA,+DAA8D;AAW9D;;;;;;;;GAQG;AACU,QAAA,uBAAuB,GAA0B;IAC5D,OAAO,EAAE,IAAI;IAEb;;;;OAIG;IACH,cAAc,EAAE;QACd,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;;OAIG;IACH,iBAAiB,EAAE;QACjB,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;;;;OAMG;IACH,4BAA4B,EAAE;QAC5B,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,EAAE;QACrB,iBAAiB,EAAE,EAAE;QACrB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;;;;OAMG;IACH,gBAAgB,EAAE;QAChB,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;CACF,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-alarms.d.ts","sourceRoot":"","sources":["../../src/instance-alarms.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAqC,MAAM,4BAA4B,CAAC;AAC3F,OAAO,KAAK,EAAa,QAAQ,EAAE,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAoC,MAAM,0BAA0B,CAAC;AACpG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AA6CtE;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,mBAAmB,GAAG,SAAS,EACvC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC,GACzC,eAAe,EAAE,CA2EnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,mBAAmB,GAAG,KAAK,GAAG,SAAS,EAC/C,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC,EAC1C,YAAY,GAAE,sBAAsB,CAAC,QAAQ,CAAC,EAAO,GACpD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAUvB"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveInstanceAlarmDefinitions = resolveInstanceAlarmDefinitions;
4
+ exports.createInstanceAlarms = createInstanceAlarms;
5
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
6
+ const aws_cloudwatch_1 = require("aws-cdk-lib/aws-cloudwatch");
7
+ const cloudwatch_1 = require("@composurecdk/cloudwatch");
8
+ const instance_alarm_defaults_js_1 = require("./instance-alarm-defaults.js");
9
+ const METRIC_PERIOD = aws_cdk_lib_1.Duration.minutes(1);
10
+ const METRIC_PERIOD_LABEL = `${String(METRIC_PERIOD.toMinutes())} minute`;
11
+ /**
12
+ * CPU credit metrics are emitted by EC2 at 5-minute granularity regardless
13
+ * of whether detailed monitoring is enabled. Using a shorter period yields
14
+ * missing data rather than higher resolution.
15
+ *
16
+ * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_credits_CPU.html
17
+ */
18
+ const CREDIT_METRIC_PERIOD = aws_cdk_lib_1.Duration.minutes(5);
19
+ const CREDIT_METRIC_PERIOD_LABEL = `${String(CREDIT_METRIC_PERIOD.toMinutes())} minute`;
20
+ /**
21
+ * Instance type family prefixes that accrue CPU credits (burstable).
22
+ * Used to decide whether to emit the contextual {@link InstanceAlarmConfig.cpuCreditBalance}
23
+ * alarm. Other families bill at a flat CPU rate and have no credit metric.
24
+ *
25
+ * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances.html
26
+ */
27
+ const BURSTABLE_FAMILY_PREFIXES = ["t2.", "t3.", "t3a.", "t4g."];
28
+ function isBurstableInstanceType(instanceType) {
29
+ const identifier = instanceType.toString();
30
+ return BURSTABLE_FAMILY_PREFIXES.some((prefix) => identifier.startsWith(prefix));
31
+ }
32
+ function instanceMetric(instance, metricName, statistic, period = METRIC_PERIOD) {
33
+ return new aws_cloudwatch_1.Metric({
34
+ namespace: "AWS/EC2",
35
+ metricName,
36
+ dimensionsMap: { InstanceId: instance.instanceId },
37
+ statistic,
38
+ period,
39
+ });
40
+ }
41
+ /**
42
+ * Resolves the recommended alarm configuration into fully-resolved
43
+ * {@link AlarmDefinition}s, applying contextual logic for the
44
+ * burstable-only CPU credit alarm.
45
+ */
46
+ function resolveInstanceAlarmDefinitions(instance, config, props) {
47
+ if (config?.enabled === false)
48
+ return [];
49
+ const definitions = [];
50
+ if (config?.cpuUtilization !== false) {
51
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(config?.cpuUtilization, instance_alarm_defaults_js_1.INSTANCE_ALARM_DEFAULTS.cpuUtilization);
52
+ definitions.push({
53
+ key: "cpuUtilization",
54
+ alarmName: cfg.alarmName,
55
+ metric: instanceMetric(instance, "CPUUtilization", aws_cloudwatch_1.Stats.AVERAGE),
56
+ threshold: cfg.threshold,
57
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
58
+ evaluationPeriods: cfg.evaluationPeriods,
59
+ datapointsToAlarm: cfg.datapointsToAlarm,
60
+ treatMissingData: cfg.treatMissingData,
61
+ description: `EC2 instance CPU utilization is sustained at a high level. Threshold: > ${String(cfg.threshold)}% average over ${String(cfg.evaluationPeriods)} x ${METRIC_PERIOD_LABEL}.`,
62
+ });
63
+ }
64
+ if (config?.statusCheckFailed !== false) {
65
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(config?.statusCheckFailed, instance_alarm_defaults_js_1.INSTANCE_ALARM_DEFAULTS.statusCheckFailed);
66
+ definitions.push({
67
+ key: "statusCheckFailed",
68
+ alarmName: cfg.alarmName,
69
+ metric: instanceMetric(instance, "StatusCheckFailed", aws_cloudwatch_1.Stats.SUM),
70
+ threshold: cfg.threshold,
71
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
72
+ evaluationPeriods: cfg.evaluationPeriods,
73
+ datapointsToAlarm: cfg.datapointsToAlarm,
74
+ treatMissingData: cfg.treatMissingData,
75
+ description: `EC2 instance is failing its status checks. Threshold: > ${String(cfg.threshold)} failed checks over ${String(cfg.evaluationPeriods)} x ${METRIC_PERIOD_LABEL}.`,
76
+ });
77
+ }
78
+ if (config?.attachedEbsStatusCheckFailed !== false) {
79
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(config?.attachedEbsStatusCheckFailed, instance_alarm_defaults_js_1.INSTANCE_ALARM_DEFAULTS.attachedEbsStatusCheckFailed);
80
+ definitions.push({
81
+ key: "attachedEbsStatusCheckFailed",
82
+ alarmName: cfg.alarmName,
83
+ metric: instanceMetric(instance, "StatusCheckFailed_AttachedEBS", aws_cloudwatch_1.Stats.MAXIMUM),
84
+ threshold: cfg.threshold,
85
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
86
+ evaluationPeriods: cfg.evaluationPeriods,
87
+ datapointsToAlarm: cfg.datapointsToAlarm,
88
+ treatMissingData: cfg.treatMissingData,
89
+ description: `EC2 instance attached EBS volume(s) are unreachable or unable to complete I/O. Threshold: >= ${String(cfg.threshold)} failed checks (max) over ${String(cfg.evaluationPeriods)} x ${METRIC_PERIOD_LABEL}.`,
90
+ });
91
+ }
92
+ if (config?.cpuCreditBalance !== false && isBurstableInstanceType(props.instanceType)) {
93
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(config?.cpuCreditBalance, instance_alarm_defaults_js_1.INSTANCE_ALARM_DEFAULTS.cpuCreditBalance);
94
+ definitions.push({
95
+ key: "cpuCreditBalance",
96
+ alarmName: cfg.alarmName,
97
+ metric: instanceMetric(instance, "CPUCreditBalance", aws_cloudwatch_1.Stats.MINIMUM, CREDIT_METRIC_PERIOD),
98
+ threshold: cfg.threshold,
99
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.LESS_THAN_THRESHOLD,
100
+ evaluationPeriods: cfg.evaluationPeriods,
101
+ datapointsToAlarm: cfg.datapointsToAlarm,
102
+ treatMissingData: cfg.treatMissingData,
103
+ description: `EC2 burstable instance CPU credit balance is low — baseline throttling is imminent. Threshold: < ${String(cfg.threshold)} credits (minimum) over ${String(cfg.evaluationPeriods)} x ${CREDIT_METRIC_PERIOD_LABEL}.`,
104
+ });
105
+ }
106
+ return definitions;
107
+ }
108
+ /**
109
+ * Creates AWS-recommended CloudWatch alarms for an EC2 instance,
110
+ * merging recommended definitions with any custom alarm builders.
111
+ *
112
+ * @param scope - CDK construct scope for creating alarm constructs.
113
+ * @param id - Base identifier for alarm construct ids.
114
+ * @param instance - The EC2 instance to create alarms for.
115
+ * @param config - User-provided alarm configuration, or `false` to disable all.
116
+ * @param props - The merged instance props, used for contextual alarm thresholds.
117
+ * @param customAlarms - Custom alarm builders added via `addAlarm()`.
118
+ * @returns A record mapping alarm keys to their created Alarm constructs.
119
+ *
120
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EC2
121
+ */
122
+ function createInstanceAlarms(scope, id, instance, config, props, customAlarms = []) {
123
+ if (config === false)
124
+ return {};
125
+ const enabled = config?.enabled ?? instance_alarm_defaults_js_1.INSTANCE_ALARM_DEFAULTS.enabled;
126
+ if (!enabled)
127
+ return {};
128
+ const recommended = resolveInstanceAlarmDefinitions(instance, config, props);
129
+ const custom = customAlarms.map((b) => b.resolve(instance));
130
+ return (0, cloudwatch_1.createAlarms)(scope, id, [...recommended, ...custom]);
131
+ }
132
+ //# sourceMappingURL=instance-alarms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-alarms.js","sourceRoot":"","sources":["../../src/instance-alarms.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,12 @@
1
1
  import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
2
- import { Instance, type IKeyPair, type ISecurityGroup, type IVpc, type InstanceProps } from "aws-cdk-lib/aws-ec2";
2
+ import { type CfnVolumeAttachment, Instance, type IKeyPair, type ISecurityGroup, type IVpc, type InstanceProps } from "aws-cdk-lib/aws-ec2";
3
3
  import { type IRole } from "aws-cdk-lib/aws-iam";
4
4
  import { type IConstruct } from "constructs";
5
- import { type IBuilder, type Lifecycle, type Resolvable } from "@composurecdk/core";
5
+ import { COPY_STATE, type Lifecycle, type Resolvable } from "@composurecdk/core";
6
+ import { type ITaggedBuilder } from "@composurecdk/cloudformation";
6
7
  import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
7
8
  import type { InstanceAlarmConfig } from "./instance-alarm-config.js";
9
+ import { type AttachVolumeOptions, type AttachVolumeRef } from "./instance-volume-attachments.js";
8
10
  /**
9
11
  * Configuration properties for the EC2 instance builder.
10
12
  *
@@ -82,9 +84,10 @@ export interface InstanceBuilderResult {
82
84
  /**
83
85
  * CloudWatch alarms created for the instance, keyed by alarm name.
84
86
  *
85
- * Includes both AWS-recommended alarms and any custom alarms added
86
- * via {@link IInstanceBuilder.addAlarm}. Access individual alarms by
87
- * key (e.g., `result.alarms.cpuUtilization`).
87
+ * Includes AWS-recommended instance alarms, any custom alarms added
88
+ * via {@link IInstanceBuilder.addAlarm}, and per-attachment alarms
89
+ * added by {@link IInstanceBuilder.attachVolume} (keyed
90
+ * `${attachmentKey}.${alarmKey}`, e.g. `AgentData.volumeStalledIo`).
88
91
  *
89
92
  * No alarm actions are configured — apply them via the result or an
90
93
  * `afterBuild` hook.
@@ -92,6 +95,14 @@ export interface InstanceBuilderResult {
92
95
  * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EC2
93
96
  */
94
97
  alarms: Record<string, Alarm>;
98
+ /**
99
+ * `AWS::EC2::VolumeAttachment` constructs created via
100
+ * {@link IInstanceBuilder.attachVolume}, keyed by the attachment key
101
+ * supplied in that call.
102
+ *
103
+ * Empty when no volumes were attached.
104
+ */
105
+ volumeAttachments: Record<string, CfnVolumeAttachment>;
95
106
  }
96
107
  /**
97
108
  * A fluent builder for configuring and creating an AWS EC2 instance.
@@ -125,7 +136,7 @@ export interface InstanceBuilderResult {
125
136
  * .machineImage(MachineImage.latestAmazonLinux2023());
126
137
  * ```
127
138
  */
128
- export type IInstanceBuilder = IBuilder<InstanceBuilderProps, InstanceBuilder>;
139
+ export type IInstanceBuilder = ITaggedBuilder<InstanceBuilderProps, InstanceBuilder>;
129
140
  declare class InstanceBuilder implements Lifecycle<InstanceBuilderResult> {
130
141
  #private;
131
142
  props: Partial<InstanceBuilderProps>;
@@ -150,6 +161,33 @@ declare class InstanceBuilder implements Lifecycle<InstanceBuilderResult> {
150
161
  * @returns This builder for chaining.
151
162
  */
152
163
  addAlarm(key: string, configure: (alarm: AlarmDefinitionBuilder<Instance>) => AlarmDefinitionBuilder<Instance>): this;
164
+ /**
165
+ * Attaches an externally-managed EBS volume to the instance via an
166
+ * `AWS::EC2::VolumeAttachment` resource, mirroring the call shape of
167
+ * {@link addAlarm}.
168
+ *
169
+ * The `volumeRef` accepts either a `Resolvable<VolumeBuilderResult>`
170
+ * (drop a `ref<VolumeBuilderResult>("data")` straight in) or a
171
+ * `Resolvable<IVolume>` for an externally-managed volume — the builder
172
+ * unwraps either at build time.
173
+ *
174
+ * When both AZs are concrete at synth time, the builder asserts the
175
+ * instance and the volume are in the same Availability Zone — synth-
176
+ * time failure beats boot-time failure for AZ mismatches.
177
+ *
178
+ * Per-attachment AWS-recommended alarms (e.g. `volumeStalledIo`) are
179
+ * created by default and merged into the result's `alarms` record
180
+ * under prefixed keys (`${attachmentKey}.${alarmKey}`).
181
+ *
182
+ * @param key - Unique key for the attachment (used as the result-map
183
+ * field name and as the construct id suffix).
184
+ * @param volumeRef - Resolvable to the volume to attach.
185
+ * @param options - Attachment options (`device`, `recommendedAlarms`).
186
+ * @returns This builder for chaining.
187
+ */
188
+ attachVolume(key: string, volumeRef: AttachVolumeRef, options: AttachVolumeOptions): this;
189
+ /** @internal — see ADR-0005. */
190
+ [COPY_STATE](target: InstanceBuilder): void;
153
191
  build(scope: IConstruct, id: string, context?: Record<string, object>): InstanceBuilderResult;
154
192
  }
155
193
  /**
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-builder.d.ts","sourceRoot":"","sources":["../../src/instance-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EACL,KAAK,mBAAmB,EACxB,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAGrB,MAAM,kCAAkC,CAAC;AAE1C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,aAAa,EACb,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,eAAe,CAC7C;IACC;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAEzB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE/B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;IAE3C;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,QAAQ,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAE9B;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;AAErF,cAAM,eAAgB,YAAW,SAAS,CAAC,qBAAqB,CAAC;;IAC/D,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAM;IAK1C;;;;;;;;;OASG;IACH,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI;IAKhC;;;;;;;;OAQG;IACH,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,QAAQ,CAAC,KAAK,sBAAsB,CAAC,QAAQ,CAAC,GACvF,IAAI;IAKP;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAQzF,gCAAgC;IAChC,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAM3C,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,qBAAqB;CAoD9F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,CAExD"}
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInstanceBuilder = createInstanceBuilder;
4
+ const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
5
+ const core_1 = require("@composurecdk/core");
6
+ const cloudformation_1 = require("@composurecdk/cloudformation");
7
+ const cloudwatch_1 = require("@composurecdk/cloudwatch");
8
+ const instance_alarms_js_1 = require("./instance-alarms.js");
9
+ const instance_defaults_js_1 = require("./instance-defaults.js");
10
+ const instance_volume_attachments_js_1 = require("./instance-volume-attachments.js");
11
+ class InstanceBuilder {
12
+ props = {};
13
+ #customAlarms = [];
14
+ #volumeAttachments = [];
15
+ #vpc;
16
+ /**
17
+ * Sets the VPC the instance will be launched into.
18
+ *
19
+ * Accepts a concrete {@link IVpc} or a {@link Ref} that resolves to one
20
+ * at build time. This is how cross-component wiring works — e.g., to a
21
+ * sibling {@link IVpcBuilder} in the same composed system.
22
+ *
23
+ * @param vpc - The VPC or a Ref to one.
24
+ * @returns This builder for chaining.
25
+ */
26
+ vpc(vpc) {
27
+ this.#vpc = vpc;
28
+ return this;
29
+ }
30
+ /**
31
+ * Adds a custom CloudWatch alarm to be created alongside the recommended
32
+ * alarms. The provided callback receives an {@link AlarmDefinitionBuilder}
33
+ * scoped to the built {@link Instance}; configure it fluently and return it.
34
+ *
35
+ * @param key - A unique key for the alarm (used to generate the alarm id).
36
+ * @param configure - Callback that configures the alarm definition.
37
+ * @returns This builder for chaining.
38
+ */
39
+ addAlarm(key, configure) {
40
+ this.#customAlarms.push(configure(new cloudwatch_1.AlarmDefinitionBuilder(key)));
41
+ return this;
42
+ }
43
+ /**
44
+ * Attaches an externally-managed EBS volume to the instance via an
45
+ * `AWS::EC2::VolumeAttachment` resource, mirroring the call shape of
46
+ * {@link addAlarm}.
47
+ *
48
+ * The `volumeRef` accepts either a `Resolvable<VolumeBuilderResult>`
49
+ * (drop a `ref<VolumeBuilderResult>("data")` straight in) or a
50
+ * `Resolvable<IVolume>` for an externally-managed volume — the builder
51
+ * unwraps either at build time.
52
+ *
53
+ * When both AZs are concrete at synth time, the builder asserts the
54
+ * instance and the volume are in the same Availability Zone — synth-
55
+ * time failure beats boot-time failure for AZ mismatches.
56
+ *
57
+ * Per-attachment AWS-recommended alarms (e.g. `volumeStalledIo`) are
58
+ * created by default and merged into the result's `alarms` record
59
+ * under prefixed keys (`${attachmentKey}.${alarmKey}`).
60
+ *
61
+ * @param key - Unique key for the attachment (used as the result-map
62
+ * field name and as the construct id suffix).
63
+ * @param volumeRef - Resolvable to the volume to attach.
64
+ * @param options - Attachment options (`device`, `recommendedAlarms`).
65
+ * @returns This builder for chaining.
66
+ */
67
+ attachVolume(key, volumeRef, options) {
68
+ if (this.#volumeAttachments.some((a) => a.key === key)) {
69
+ throw new Error(`attachVolume: duplicate attachment key "${key}".`);
70
+ }
71
+ this.#volumeAttachments.push({ key, volumeRef, options });
72
+ return this;
73
+ }
74
+ /** @internal — see ADR-0005. */
75
+ [core_1.COPY_STATE](target) {
76
+ target.#vpc = this.#vpc;
77
+ target.#customAlarms.push(...this.#customAlarms);
78
+ target.#volumeAttachments.push(...this.#volumeAttachments);
79
+ }
80
+ build(scope, id, context) {
81
+ const resolvedVpc = this.#vpc ? (0, core_1.resolve)(this.#vpc, context) : undefined;
82
+ if (!resolvedVpc) {
83
+ throw new Error(`InstanceBuilder "${id}" requires a VPC. Call .vpc() with an IVpc or a Ref to one.`);
84
+ }
85
+ const { recommendedAlarms: alarmConfig, role, keyPair, securityGroup, ...instanceProps } = this.props;
86
+ const mergedProps = {
87
+ ...instance_defaults_js_1.INSTANCE_DEFAULTS,
88
+ ...instanceProps,
89
+ vpc: resolvedVpc,
90
+ ...(role !== undefined ? { role: (0, core_1.resolve)(role, context) } : {}),
91
+ ...(keyPair !== undefined ? { keyPair: (0, core_1.resolve)(keyPair, context) } : {}),
92
+ ...(securityGroup !== undefined ? { securityGroup: (0, core_1.resolve)(securityGroup, context) } : {}),
93
+ };
94
+ const instance = new aws_ec2_1.Instance(scope, id, mergedProps);
95
+ const instanceAlarms = (0, instance_alarms_js_1.createInstanceAlarms)(scope, id, instance, alarmConfig, mergedProps, this.#customAlarms);
96
+ const { attachments, alarms: attachmentAlarms } = (0, instance_volume_attachments_js_1.createVolumeAttachments)(scope, id, instance, mergedProps, this.#volumeAttachments, context);
97
+ return {
98
+ instance,
99
+ alarms: { ...instanceAlarms, ...attachmentAlarms },
100
+ volumeAttachments: attachments,
101
+ };
102
+ }
103
+ }
104
+ /**
105
+ * Creates a new {@link IInstanceBuilder} for configuring an AWS EC2 instance.
106
+ *
107
+ * This is the entry point for defining an EC2 instance component. The
108
+ * returned builder exposes every {@link InstanceBuilderProps} property as a
109
+ * fluent setter/getter, plus {@link IInstanceBuilder.vpc | .vpc()} for
110
+ * cross-component VPC wiring with Ref support. It implements
111
+ * {@link Lifecycle} for use with {@link compose}.
112
+ *
113
+ * @returns A fluent builder for an AWS EC2 instance.
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * const server = createInstanceBuilder()
118
+ * .vpc(ref<VpcBuilderResult>("network").get("vpc"))
119
+ * .instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.MICRO))
120
+ * .machineImage(MachineImage.latestAmazonLinux2023());
121
+ *
122
+ * // Use standalone:
123
+ * const result = server.build(stack, "MyInstance");
124
+ *
125
+ * // Or compose into a system:
126
+ * const system = compose(
127
+ * { network: createVpcBuilder(), server },
128
+ * { network: [], server: ["network"] },
129
+ * );
130
+ * ```
131
+ */
132
+ function createInstanceBuilder() {
133
+ return (0, cloudformation_1.taggedBuilder)(InstanceBuilder);
134
+ }
135
+ //# sourceMappingURL=instance-builder.js.map